Refactor waku messaging setup (#74)

This commit is contained in:
Szymon Szlachtowicz 2021-09-15 15:10:12 +02:00 committed by GitHub
parent d4c8a3c35e
commit e70c0801d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 111 additions and 72 deletions

View File

@ -6,6 +6,7 @@ import { Contract } from '@ethersproject/contracts'
import { Interface } from '@ethersproject/abi' import { Interface } from '@ethersproject/abi'
import { ERC20 } from '../abi' import { ERC20 } from '../abi'
import { createWaku } from '../utils/createWaku' import { createWaku } from '../utils/createWaku'
import { WakuMessagesSetup } from '../types/WakuMessagesSetup'
const ABI = [ const ABI = [
'function aggregate(tuple(address target, bytes callData)[] calls) view returns (uint256 blockNumber, bytes[] returnData)', 'function aggregate(tuple(address target, bytes callData)[] calls) view returns (uint256 blockNumber, bytes[] returnData)',
] ]
@ -39,6 +40,7 @@ export class WakuMessaging {
provider: Web3Provider, provider: Web3Provider,
chainId: number, chainId: number,
multicall: string, multicall: string,
wakuMessagesSetup: WakuMessagesSetup<any>[],
waku?: Waku waku?: Waku
) { ) {
this.appName = appName this.appName = appName
@ -47,6 +49,17 @@ export class WakuMessaging {
this.chainId = chainId this.chainId = chainId
this.token = new Contract(tokenAddress, ERC20, this.provider) this.token = new Contract(tokenAddress, ERC20, this.provider)
this.multicall = new Contract(multicall, ABI, this.provider) this.multicall = new Contract(multicall, ABI, this.provider)
wakuMessagesSetup.forEach((setupData) => {
this.wakuMessages[setupData.name] = {
topic: `/${this.appName}/0.1/${setupData.name}/proto/`,
tokenCheckArray: setupData.tokenCheckArray,
hashMap: {},
arr: [],
updateFunction: (msg) => this.decodeMsgAndSetArray(msg, setupData),
}
})
this.setObserver()
} }
public cleanUp() { public cleanUp() {
@ -61,9 +74,7 @@ export class WakuMessaging {
await Promise.all( await Promise.all(
Object.values(this.wakuMessages).map(async (msgObj) => { Object.values(this.wakuMessages).map(async (msgObj) => {
const storeMessages = await this.waku?.store.queryHistory([msgObj.topic]) const storeMessages = await this.waku?.store.queryHistory([msgObj.topic])
if (storeMessages) { msgObj.updateFunction(storeMessages ?? [])
msgObj.updateFunction(storeMessages)
}
this?.waku?.relay.addObserver((msg) => msgObj.updateFunction([msg]), [msgObj.topic]) this?.waku?.relay.addObserver((msg) => msgObj.updateFunction([msg]), [msgObj.topic])
this.observers.push({ callback: (msg) => msgObj.updateFunction([msg]), topics: [msgObj.topic] }) this.observers.push({ callback: (msg) => msgObj.updateFunction([msg]), topics: [msgObj.topic] })
}) })
@ -72,18 +83,18 @@ export class WakuMessaging {
protected decodeMsgAndSetArray<T extends { id: string; timestamp: number }>( protected decodeMsgAndSetArray<T extends { id: string; timestamp: number }>(
messages: WakuMessage[], messages: WakuMessage[],
decode: (payload: Uint8Array | undefined, timestamp: Date | undefined, chainId: number) => T | undefined, setupData: WakuMessagesSetup<T>
msgObj: WakuMessageStore,
filterFunction?: (e: T) => boolean
) { ) {
const { decodeFunction, filterFunction, name } = setupData
const { arr, hashMap } = this.wakuMessages[name]
messages messages
.map((msg) => decode(msg.payload, msg.timestamp, this.chainId)) .map(decodeFunction)
.sort((a, b) => ((a?.timestamp ?? new Date(0)) > (b?.timestamp ?? new Date(0)) ? 1 : -1)) .sort((a, b) => ((a?.timestamp ?? new Date(0)) > (b?.timestamp ?? new Date(0)) ? 1 : -1))
.forEach((e) => { .forEach((e) => {
if (e) { if (e) {
if (filterFunction ? filterFunction(e) : true && !msgObj.hashMap?.[e.id]) { if (filterFunction ? filterFunction(e) : true && !hashMap?.[e.id]) {
msgObj.arr.unshift(e) arr.unshift(e)
msgObj.hashMap[e.id] = true hashMap[e.id] = true
} }
} }
}) })

View File

@ -9,6 +9,7 @@ import { DetailedTimedPoll } from '../models/DetailedTimedPoll'
import { createWaku } from '../utils/createWaku' import { createWaku } from '../utils/createWaku'
import { WakuMessaging } from './WakuMessaging' import { WakuMessaging } from './WakuMessaging'
import { Web3Provider } from '@ethersproject/providers' import { Web3Provider } from '@ethersproject/providers'
import { WakuMessagesSetup } from '../types/WakuMessagesSetup'
export enum MESSEGAGE_SENDING_RESULT { export enum MESSEGAGE_SENDING_RESULT {
ok = 0, ok = 0,
@ -26,29 +27,27 @@ export class WakuPolling extends WakuMessaging {
multicall: string, multicall: string,
waku?: Waku waku?: Waku
) { ) {
super(appName, tokenAddress, provider, chainId, multicall, waku) super(
this.wakuMessages['pollInit'] = { appName,
topic: `/${this.appName}/waku-polling/timed-polls-init/proto/`, tokenAddress,
hashMap: {}, provider,
chainId,
multicall,
[
{
name: 'pollInit',
tokenCheckArray: ['owner'], tokenCheckArray: ['owner'],
arr: [], decodeFunction: (wakuMessage) => PollInitMsg.decode(wakuMessage, chainId),
updateFunction: (msg: WakuMessage[]) => filterFunction: (e: PollInitMsg) => e.endTime > Date.now(),
this.decodeMsgAndSetArray( },
msg, {
PollInitMsg.decode, name: 'pollVote',
this.wakuMessages['pollInit'],
(e) => e.endTime > Date.now()
),
}
this.wakuMessages['pollVote'] = {
topic: `/${this.appName}/waku-polling/votes/proto/`,
hashMap: {},
tokenCheckArray: ['voter'], tokenCheckArray: ['voter'],
arr: [], decodeFunction: (wakuMessage) => TimedPollVoteMsg.decode(wakuMessage, chainId),
updateFunction: (msg: WakuMessage[]) => },
this.decodeMsgAndSetArray(msg, TimedPollVoteMsg.decode, this.wakuMessages['pollVote']), ],
} waku
this.setObserver() )
} }
public static async create( public static async create(

View File

@ -23,21 +23,22 @@ export class WakuVoting extends WakuMessaging {
multicallAddress: string, multicallAddress: string,
waku?: Waku waku?: Waku
) { ) {
super(appName, token, provider, chainId, multicallAddress, waku) super(
this.votingContract = votingContract appName,
this.wakuMessages['vote'] = { token,
topic: `/${this.appName}/waku-voting/votes/proto/`, provider,
hashMap: {}, chainId,
arr: [], multicallAddress,
[
{
name: 'vote',
tokenCheckArray: ['voter'], tokenCheckArray: ['voter'],
updateFunction: (msg: WakuMessage[]) => decodeFunction: (wakuMessage) => VoteMsg.decode(wakuMessage, chainId, votingContract.address),
this.decodeMsgAndSetArray( },
msg, ],
(payload, timestamp, chainId) => VoteMsg.decode(payload, timestamp, chainId, this.votingContract.address), waku
this.wakuMessages['vote'] )
), this.votingContract = votingContract
}
this.setObserver()
} }
public static async create( public static async create(

View File

@ -4,6 +4,7 @@ import { JsonRpcSigner } from '@ethersproject/providers'
import protons, { PollInit } from 'protons' import protons, { PollInit } from 'protons'
import { createSignFunction } from '../utils/createSignFunction' import { createSignFunction } from '../utils/createSignFunction'
import { verifySignature } from '../utils/verifySignature' import { verifySignature } from '../utils/verifySignature'
import { WakuMessage } from 'js-waku'
const proto = protons(` const proto = protons(`
message PollInit { message PollInit {
@ -173,14 +174,10 @@ export class PollInitMsg {
} }
} }
static decode( static decode(wakuMessage: WakuMessage, chainId: number, verifyFunction?: (params: any, address: string) => boolean) {
rawPayload: Uint8Array | undefined,
timestamp: Date | undefined,
chainId: number,
verifyFunction?: (params: any, address: string) => boolean
) {
try { try {
const payload = proto.PollInit.decode(rawPayload) const timestamp = wakuMessage.timestamp
const payload = proto.PollInit.decode(wakuMessage.payload)
if (!timestamp || timestamp.getTime() != payload.timestamp) { if (!timestamp || timestamp.getTime() != payload.timestamp) {
return undefined return undefined
} }

View File

@ -4,6 +4,7 @@ import protons, { TimedPollVote } from 'protons'
import { Wallet } from 'ethers' import { Wallet } from 'ethers'
import { createSignFunction } from '../utils/createSignFunction' import { createSignFunction } from '../utils/createSignFunction'
import { verifySignature } from '../utils/verifySignature' import { verifySignature } from '../utils/verifySignature'
import { WakuMessage } from 'js-waku'
const proto = protons(` const proto = protons(`
message TimedPollVote { message TimedPollVote {
@ -122,14 +123,10 @@ export class TimedPollVoteMsg {
} }
} }
static decode( static decode(wakuMessage: WakuMessage, chainId: number, verifyFunction?: (params: any, address: string) => boolean) {
rawPayload: Uint8Array | undefined,
timestamp: Date | undefined,
chainId: number,
verifyFunction?: (params: any, address: string) => boolean
) {
try { try {
const payload = proto.TimedPollVote.decode(rawPayload) const timestamp = wakuMessage.timestamp
const payload = proto.TimedPollVote.decode(wakuMessage.payload)
if (!timestamp || !payload.timestamp || timestamp?.getTime() != payload.timestamp) { if (!timestamp || !payload.timestamp || timestamp?.getTime() != payload.timestamp) {
return undefined return undefined
} }

View File

@ -4,6 +4,7 @@ import { BigNumber, Wallet } from 'ethers'
import { JsonRpcSigner } from '@ethersproject/providers' import { JsonRpcSigner } from '@ethersproject/providers'
import { createSignFunction } from '../utils/createSignFunction' import { createSignFunction } from '../utils/createSignFunction'
import { verifySignature } from '../utils/verifySignature' import { verifySignature } from '../utils/verifySignature'
import { WakuMessage } from 'js-waku'
const proto = protons(` const proto = protons(`
message Vote { message Vote {
@ -121,14 +122,14 @@ export class VoteMsg {
} }
static decode( static decode(
rawPayload: Uint8Array | undefined, wakuMessage: WakuMessage,
timestamp: Date | undefined,
chainId: number, chainId: number,
contractAddress: string, contractAddress: string,
verifyFunction?: (params: any, address: string) => boolean verifyFunction?: (params: any, address: string) => boolean
) { ) {
try { try {
const payload = proto.Vote.decode(rawPayload) const timestamp = wakuMessage.timestamp
const payload = proto.Vote.decode(wakuMessage.payload)
if (!timestamp || !payload.timestamp || timestamp?.getTime() != payload.timestamp) { if (!timestamp || !payload.timestamp || timestamp?.getTime() != payload.timestamp) {
return undefined return undefined
} }

View File

@ -0,0 +1,8 @@
import { WakuMessage } from 'js-waku'
export type WakuMessagesSetup<T> = {
name: string
tokenCheckArray: string[]
decodeFunction: (wakuMessage: WakuMessage) => T | undefined
filterFunction?: (e: T) => boolean
}

View File

@ -3,6 +3,7 @@ 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 { BigNumber } from 'ethers' import { BigNumber } from 'ethers'
import { WakuMessage } from 'js-waku'
describe('PollInitMsg', () => { describe('PollInitMsg', () => {
const provider = new MockProvider() const provider = new MockProvider()
@ -95,13 +96,20 @@ describe('PollInitMsg', () => {
const payload = data.encode() const payload = data.encode()
expect(payload).to.not.be.undefined expect(payload).to.not.be.undefined
if (payload) { if (payload) {
expect(PollInitMsg.decode(payload, new Date(data.timestamp), 0, () => true)).to.deep.eq(data) expect(
PollInitMsg.decode({ payload, timestamp: new Date(data.timestamp) } as WakuMessage, 0, () => true)
).to.deep.eq(data)
} }
} }
}) })
it('random decode', async () => { it('random decode', async () => {
expect(PollInitMsg.decode(new Uint8Array([12, 12, 3, 32, 31, 212, 31, 32, 23]), new Date(10), 0)).to.be.undefined expect(
PollInitMsg.decode(
{ payload: new Uint8Array([12, 12, 3, 32, 31, 212, 31, 32, 23]), timestamp: new Date(10) } as WakuMessage,
0
)
).to.be.undefined
}) })
it('NON_WEIGHTED init', async () => { it('NON_WEIGHTED init', async () => {
@ -119,7 +127,9 @@ describe('PollInitMsg', () => {
const payload = data.encode() const payload = data.encode()
expect(payload).to.not.be.undefined expect(payload).to.not.be.undefined
if (payload) { if (payload) {
expect(PollInitMsg.decode(payload, new Date(data.timestamp), 0, () => true)).to.deep.eq(data) expect(
PollInitMsg.decode({ payload, timestamp: new Date(data.timestamp) } as WakuMessage, 0, () => true)
).to.deep.eq(data)
} }
} }
}) })
@ -139,7 +149,9 @@ describe('PollInitMsg', () => {
expect(payload).to.not.be.undefined expect(payload).to.not.be.undefined
if (payload) { if (payload) {
expect(PollInitMsg.decode(payload, new Date(data.timestamp), 0, () => true)).to.deep.eq({ expect(
PollInitMsg.decode({ payload, timestamp: new Date(data.timestamp) } as WakuMessage, 0, () => true)
).to.deep.eq({
...data, ...data,
minToken: BigNumber.from(1), minToken: BigNumber.from(1),
}) })

View File

@ -2,6 +2,7 @@ import { expect } from 'chai'
import { TimedPollVoteMsg } from '../../src/models/TimedPollVoteMsg' import { TimedPollVoteMsg } from '../../src/models/TimedPollVoteMsg'
import { MockProvider } from 'ethereum-waffle' import { MockProvider } from 'ethereum-waffle'
import { BigNumber } from 'ethers' import { BigNumber } from 'ethers'
import { WakuMessage } from 'js-waku'
describe('TimedPollVoteMsg', () => { describe('TimedPollVoteMsg', () => {
const provider = new MockProvider() const provider = new MockProvider()
@ -52,14 +53,24 @@ describe('TimedPollVoteMsg', () => {
expect(payload).to.not.be.undefined expect(payload).to.not.be.undefined
if (payload) { if (payload) {
expect(await TimedPollVoteMsg.decode(payload, new Date(data.timestamp), 0, () => true)).to.deep.eq(data) expect(
await TimedPollVoteMsg.decode(
{ payload, timestamp: new Date(data.timestamp) } as WakuMessage,
0,
() => true
)
).to.deep.eq(data)
} }
} }
}) })
it('random decode', async () => { it('random decode', async () => {
expect(TimedPollVoteMsg.decode(new Uint8Array([12, 12, 3, 32, 31, 212, 31, 32, 23]), new Date(10), 0)).to.be expect(
.undefined TimedPollVoteMsg.decode(
{ payload: new Uint8Array([12, 12, 3, 32, 31, 212, 31, 32, 23]), timestamp: new Date(10) } as WakuMessage,
0
)
).to.be.undefined
}) })
it('data with token', async () => { it('data with token', async () => {
@ -77,7 +88,9 @@ describe('TimedPollVoteMsg', () => {
expect(payload).to.not.be.undefined expect(payload).to.not.be.undefined
if (payload) { if (payload) {
expect(TimedPollVoteMsg.decode(payload, new Date(data.timestamp), 0, () => true)).to.deep.eq({ expect(
TimedPollVoteMsg.decode({ payload, timestamp: new Date(data.timestamp) } as WakuMessage, 0, () => true)
).to.deep.eq({
...data, ...data,
tokenAmount: BigNumber.from(120), tokenAmount: BigNumber.from(120),
}) })