Add waku init poll mock (#5)

This commit is contained in:
Szymon Szlachtowicz 2021-08-10 14:55:47 +02:00 committed by GitHub
parent 624f126f72
commit c1b326ab9c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 118 additions and 86 deletions

View File

@ -1,30 +1,67 @@
import { Waku, getStatusFleetNodes } from 'js-waku' import { Waku, getStatusFleetNodes } from 'js-waku'
import { JsonRpcSigner } from '@ethersproject/providers'
import { PollInitMsg } from './models/PollInitMsg'
import { PollType } from './types/PollType'
import { BigNumber, Wallet } from 'ethers'
import PollInit from './utils/proto/PollInit'
import { WakuMessage } from 'js-waku'
class WakuVoting { class WakuVoting {
private appName: string private appName: string
private waku: Waku | undefined private waku: Waku | undefined
public tokenAddress: string public tokenAddress: string
private pollInitTopic: string
private async createWaku() { private static async createWaku() {
this.waku = await Waku.create() const waku = await Waku.create()
const nodes = await getStatusFleetNodes() const nodes = await getStatusFleetNodes()
await Promise.all( await Promise.all(
nodes.map((addr) => { nodes.map((addr) => {
if (this.waku) { if (waku) {
return this.waku.dial(addr) return waku.dial(addr)
} }
}) })
) )
return waku
} }
constructor(appName: string, tokenAddress: string, waku?: Waku) { private constructor(appName: string, tokenAddress: string, waku: Waku) {
this.appName = appName this.appName = appName
this.tokenAddress = tokenAddress this.tokenAddress = tokenAddress
if (waku) { this.pollInitTopic = `/${this.appName}/waku-polling/timed-polls-init/proto/`
this.waku = waku this.waku = waku
} else { }
this.createWaku()
public static async create(appName: string, tokenAddress: string, waku?: Waku) {
if (!waku) {
waku = await this.createWaku()
} }
return new WakuVoting(appName, tokenAddress, waku)
}
public async createTimedPoll(
signer: JsonRpcSigner | Wallet,
question: string,
answers: string[],
pollType: PollType,
minToken?: BigNumber,
endTime?: number
) {
const pollInit = await PollInitMsg.create(signer, question, answers, pollType, minToken, endTime)
const payload = PollInit.encode(pollInit)
if (payload && pollInit) {
const wakuMessage = await WakuMessage.fromBytes(payload, this.pollInitTopic, {
timestamp: new Date(pollInit.timestamp),
})
await this.waku?.relay.send(wakuMessage)
}
}
public async getTimedPolls() {
const messages = await this.waku?.store.queryHistory({ contentTopics: [this.pollInitTopic] })
return messages
?.filter((e): e is WakuMessage & { payload: Uint8Array } => !!e?.payload)
.map((msg) => PollInit.decode(msg.payload, msg.timestamp))
} }
} }

View File

@ -1,7 +1,8 @@
import { PollType } from '../types/PollType' import { PollType } from '../types/PollType'
import { BigNumber, utils } from 'ethers' import { BigNumber, utils } from 'ethers'
import { JsonRpcSigner } from '@ethersproject/providers' import { JsonRpcSigner } from '@ethersproject/providers'
import { PollInit } from 'protons'
import { Wallet } from 'ethers'
export class PollInitMsg { export class PollInitMsg {
public owner: string public owner: string
public timestamp: number public timestamp: number
@ -34,7 +35,7 @@ export class PollInitMsg {
} }
static async create( static async create(
signer: JsonRpcSigner, signer: JsonRpcSigner | Wallet,
question: string, question: string,
answers: string[], answers: string[],
pollType: PollType, pollType: PollType,
@ -72,4 +73,42 @@ export class PollInitMsg {
return new PollInitMsg(owner, signature, timestamp, question, answers, pollType, newEndTime, minToken) return new PollInitMsg(owner, signature, timestamp, question, answers, pollType, newEndTime, minToken)
} }
static fromProto(payload: PollInit) {
const owner = utils.getAddress(utils.hexlify(payload.owner))
const timestamp = payload.timestamp
const question = payload.question
const answers = payload.answers
const pollType = payload.pollType
const endTime = payload.endTime
const signature = utils.hexlify(payload.signature)
let minToken = payload.minToken ? BigNumber.from(payload.minToken) : undefined
const msg: (string | number | BigNumber | PollType)[] = [
owner,
timestamp,
question,
answers.join(),
pollType,
endTime,
]
const types = ['address', 'uint256', 'string', 'string', 'uint8', 'uint256']
if (pollType === PollType.NON_WEIGHTED) {
if (minToken) {
msg.push(minToken)
types.push('uint256')
} else {
return undefined
}
} else {
minToken = undefined
}
const packedData = utils.arrayify(utils.solidityPack(types, msg))
const verifiedAddress = utils.verifyMessage(packedData, signature)
if (verifiedAddress != owner) {
return undefined
}
return new PollInitMsg(owner, signature, timestamp, question, answers, pollType, endTime, minToken)
}
} }

View File

@ -1,6 +1,6 @@
import protons, { PollInit } from 'protons' import protons, { PollInit } from 'protons'
import { PollType } from '../../types/PollType' import { PollType } from '../../types/PollType'
import { utils, BigNumber } from 'ethers' import { utils } from 'ethers'
import { PollInitMsg } from '../../models/PollInitMsg' import { PollInitMsg } from '../../models/PollInitMsg'
const proto = protons(` const proto = protons(`
@ -46,9 +46,12 @@ export function encode(pollInit: PollInitMsg) {
} }
} }
export function decode(payload: Uint8Array) { export function decode(payload: Uint8Array, timestamp: Date | undefined) {
try { try {
const msg = proto.PollInit.decode(payload) const msg = proto.PollInit.decode(payload)
if (!timestamp || timestamp.getTime() != msg.timestamp) {
return undefined
}
if ( if (
msg.owner && msg.owner &&
msg.timestamp && msg.timestamp &&
@ -58,24 +61,7 @@ export function decode(payload: Uint8Array) {
msg.endTime && msg.endTime &&
msg.signature msg.signature
) { ) {
const pollInit: PollInitMsg = { return PollInitMsg.fromProto(msg)
owner: utils.hexlify(msg.owner),
timestamp: msg.timestamp,
question: msg.question,
answers: msg.answers,
pollType: msg.pollType,
endTime: msg.endTime,
signature: utils.hexlify(msg.signature),
}
if (msg.pollType === PollType.NON_WEIGHTED) {
if (msg.minToken) {
pollInit.minToken = BigNumber.from(msg.minToken)
return pollInit
}
} else {
return pollInit
}
} }
} catch { } catch {
return undefined return undefined

View File

@ -4,7 +4,7 @@ import WakuVoting from '../src'
describe('WakuVoting', () => { describe('WakuVoting', () => {
it('success', async () => { it('success', async () => {
const wakuVoting = new WakuVoting('test', '0x0', {} as unknown as Waku) const wakuVoting = await WakuVoting.create('test', '0x0', {} as unknown as Waku)
expect(wakuVoting).to.not.be.undefined expect(wakuVoting).to.not.be.undefined
}) })

View File

@ -2,7 +2,6 @@ import { expect } from 'chai'
import { PollInitMsg } from '../../src/models/PollInitMsg' import { PollInitMsg } from '../../src/models/PollInitMsg'
import { MockProvider } from 'ethereum-waffle' import { MockProvider } from 'ethereum-waffle'
import { PollType } from '../../src/types/PollType' import { PollType } from '../../src/types/PollType'
import { JsonRpcSigner } from '@ethersproject/providers'
import { BigNumber, utils } from 'ethers' import { BigNumber, utils } from 'ethers'
describe('PollInitMsg', () => { describe('PollInitMsg', () => {
@ -10,12 +9,7 @@ describe('PollInitMsg', () => {
const [alice] = provider.getWallets() const [alice] = provider.getWallets()
it('success', async () => { it('success', async () => {
const poll = await PollInitMsg.create( const poll = await PollInitMsg.create(alice, 'test', ['one', 'two', 'three'], PollType.WEIGHTED)
alice as unknown as JsonRpcSigner,
'test',
['one', 'two', 'three'],
PollType.WEIGHTED
)
expect(poll).to.not.be.undefined expect(poll).to.not.be.undefined
expect(poll.owner).to.eq(alice.address) expect(poll.owner).to.eq(alice.address)
@ -35,7 +29,7 @@ describe('PollInitMsg', () => {
it('success NON_WEIGHTED', async () => { it('success NON_WEIGHTED', async () => {
const poll = await PollInitMsg.create( const poll = await PollInitMsg.create(
alice as unknown as JsonRpcSigner, alice,
'test', 'test',
['one', 'two', 'three'], ['one', 'two', 'three'],
PollType.NON_WEIGHTED, PollType.NON_WEIGHTED,
@ -61,12 +55,7 @@ describe('PollInitMsg', () => {
}) })
it('NON_WEIGHTED no minToken', async () => { it('NON_WEIGHTED no minToken', async () => {
const poll = await PollInitMsg.create( const poll = await PollInitMsg.create(alice, 'test', ['one', 'two', 'three'], PollType.NON_WEIGHTED)
alice as unknown as JsonRpcSigner,
'test',
['one', 'two', 'three'],
PollType.NON_WEIGHTED
)
expect(poll?.minToken?.toNumber()).to.eq(1) expect(poll?.minToken?.toNumber()).to.eq(1)
@ -87,14 +76,7 @@ describe('PollInitMsg', () => {
}) })
it('specific end time', async () => { it('specific end time', async () => {
const poll = await PollInitMsg.create( const poll = await PollInitMsg.create(alice, 'test', ['one', 'two', 'three'], PollType.NON_WEIGHTED, undefined, 100)
alice as unknown as JsonRpcSigner,
'test',
['one', 'two', 'three'],
PollType.NON_WEIGHTED,
undefined,
100
)
expect(poll?.endTime).to.eq(100) expect(poll?.endTime).to.eq(100)
}) })

View File

@ -3,29 +3,24 @@ import { PollType } from '../../../src/types/PollType'
import PollInit from '../../../src/utils/proto/PollInit' import PollInit from '../../../src/utils/proto/PollInit'
import { BigNumber } from 'ethers' import { BigNumber } from 'ethers'
import { PollInitMsg } from '../../../src/models/PollInitMsg' import { PollInitMsg } from '../../../src/models/PollInitMsg'
import { MockProvider } from 'ethereum-waffle'
describe('PollInit', () => { describe('PollInit', () => {
const provider = new MockProvider()
const [alice] = provider.getWallets()
it('success', async () => { it('success', async () => {
const data: PollInitMsg = { const data = await PollInitMsg.create(alice, 'whats up', ['ab', 'cd', 'ef'], PollType.WEIGHTED)
owner: '0x02',
answers: ['ab', 'cd', 'ef'],
endTime: 10,
pollType: PollType.WEIGHTED,
question: 'whats up',
signature: '0x11',
timestamp: 10,
}
const payload = PollInit.encode(data) const payload = PollInit.encode(data)
expect(payload).to.not.be.undefined expect(payload).to.not.be.undefined
if (payload) { if (payload) {
expect(PollInit.decode(payload)).to.deep.eq(data) expect(PollInit.decode(payload, new Date(data.timestamp))).to.deep.eq(data)
} }
}) })
it('random decode', async () => { it('random decode', async () => {
expect(PollInit.decode(new Uint8Array([12, 12, 3, 32, 31, 212, 31, 32, 23]))).to.be.undefined expect(PollInit.decode(new Uint8Array([12, 12, 3, 32, 31, 212, 31, 32, 23]), new Date(10))).to.be.undefined
}) })
it('random data', async () => { it('random data', async () => {
@ -33,36 +28,29 @@ describe('PollInit', () => {
}) })
it('NON_WEIGHTED init', async () => { it('NON_WEIGHTED init', async () => {
const data: PollInitMsg = { const data = await PollInitMsg.create(
owner: '0x02', alice,
answers: ['ab', 'cd', 'ef'], 'whats up',
endTime: 10, ['ab', 'cd', 'ef'],
pollType: PollType.NON_WEIGHTED, PollType.NON_WEIGHTED,
minToken: BigNumber.from(10), BigNumber.from(10)
question: 'whats up', )
signature: '0x11',
timestamp: 10,
}
const payload = PollInit.encode(data) const payload = PollInit.encode(data)
expect(payload).to.not.be.undefined expect(payload).to.not.be.undefined
if (payload) { if (payload) {
expect(PollInit.decode(payload)).to.deep.eq(data) expect(PollInit.decode(payload, new Date(data.timestamp))).to.deep.eq(data)
} }
}) })
it('NON_WEIGHTED no min token', async () => { it('NON_WEIGHTED no min token', async () => {
const data: PollInitMsg = { const data = await PollInitMsg.create(alice, 'whats up', ['ab', 'cd', 'ef'], PollType.NON_WEIGHTED)
owner: '0x02',
answers: ['ab', 'cd', 'ef'],
endTime: 10,
pollType: PollType.NON_WEIGHTED,
question: 'whats up',
signature: '0x11',
timestamp: 10,
}
const payload = PollInit.encode(data) const payload = PollInit.encode(data)
expect(payload).to.be.undefined expect(payload).to.not.be.undefined
if (payload) {
expect(PollInit.decode(payload, new Date(data.timestamp))).to.deep.eq({ ...data, minToken: BigNumber.from(1) })
}
}) })
}) })