Refactor initMsg typed signing (#11)

This commit is contained in:
Szymon Szlachtowicz 2021-08-13 13:41:08 +02:00 committed by GitHub
parent f6203b783f
commit ac0279fda3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 68 additions and 90 deletions

View File

@ -3,6 +3,16 @@ import { BigNumber, utils, Wallet } from 'ethers'
import { JsonRpcSigner } from '@ethersproject/providers' import { JsonRpcSigner } from '@ethersproject/providers'
import { PollInit } from 'protons' import { PollInit } from 'protons'
function signFunction(signer: JsonRpcSigner | Wallet) {
return async (params: string[]) => {
if ('send' in signer.provider) {
return signer.provider.send('eth_signTypedData_v4', params)
} else {
throw `No send function in provider`
}
}
}
type Message = { type Message = {
owner: string owner: string
timestamp: number timestamp: number
@ -10,7 +20,7 @@ type Message = {
answers: string[] answers: string[]
pollType: PollType pollType: PollType
endTime: number endTime: number
minToken: BigNumber | undefined minToken?: BigNumber
} }
export function createSignMsgParams(message: Message) { export function createSignMsgParams(message: Message) {
@ -20,10 +30,8 @@ export function createSignMsgParams(message: Message) {
version: '1', version: '1',
}, },
message: { message: {
owner: message.owner, ...message,
timestamp: new Date(message.timestamp).toLocaleDateString(), timestamp: new Date(message.timestamp).toLocaleDateString(),
question: message.question,
answers: message.answers,
pollType: message.pollType === PollType.WEIGHTED ? 'Weighted' : 'Non weighted', pollType: message.pollType === PollType.WEIGHTED ? 'Weighted' : 'Non weighted',
endTime: new Date(message.endTime).toLocaleDateString(), endTime: new Date(message.endTime).toLocaleDateString(),
}, },
@ -44,11 +52,9 @@ export function createSignMsgParams(message: Message) {
}, },
} }
if (message.pollType === PollType.NON_WEIGHTED) { if (message.pollType === PollType.NON_WEIGHTED && message.minToken) {
if (message.minToken) { msgParams.message = { ...msgParams.message, minToken: message.minToken.toString() }
msgParams.message = { ...msgParams.message, minToken: message.minToken.toString() } msgParams.types.Mail.push({ name: 'minToken', type: 'uint256' })
msgParams.types.Mail.push({ name: 'minToken', type: 'uint256' })
}
} }
return msgParams return msgParams
} }
@ -64,27 +70,16 @@ export class PollInitMsg {
public signature: string public signature: string
public id: string public id: string
private constructor( private constructor(signature: string, msg: Message) {
id: string, this.id = utils.id([msg.owner, msg.timestamp, signature].join())
owner: string,
signature: string,
timestamp: number,
question: string,
answers: string[],
pollType: PollType,
endTime: number,
minToken?: BigNumber
) {
this.id = id
this.owner = owner
this.timestamp = timestamp
this.question = question
this.answers = answers
this.pollType = pollType
this.minToken = minToken
this.endTime = endTime
this.signature = signature this.signature = signature
this.owner = msg.owner
this.timestamp = msg.timestamp
this.question = msg.question
this.answers = msg.answers
this.pollType = msg.pollType
this.minToken = msg.minToken
this.endTime = msg.endTime
} }
static async _createWithSignFunction( static async _createWithSignFunction(
@ -96,34 +91,31 @@ export class PollInitMsg {
minToken?: BigNumber, minToken?: BigNumber,
endTime?: number endTime?: number
): Promise<PollInitMsg | undefined> { ): Promise<PollInitMsg | undefined> {
const owner = await signer.getAddress()
const timestamp = Date.now() const timestamp = Date.now()
let newEndTime = timestamp + 10000000
if (endTime) {
newEndTime = endTime
}
if (pollType === PollType.NON_WEIGHTED && !minToken) { if (pollType === PollType.NON_WEIGHTED && !minToken) {
minToken = BigNumber.from(1) minToken = BigNumber.from(1)
} }
const msg = {
const params = createSignMsgParams({ owner: await signer.getAddress(),
owner,
timestamp, timestamp,
question, question,
answers, answers,
pollType, pollType,
endTime: newEndTime, endTime: endTime ? endTime : timestamp + 100000000,
minToken, minToken,
})
const signature = await signFunction([owner, JSON.stringify(params)])
if (!signature) {
return undefined
} }
const id = utils.solidityKeccak256(['address', 'uint256'], [owner, timestamp]) const params = createSignMsgParams(msg)
return new PollInitMsg(id, owner, signature, timestamp, question, answers, pollType, newEndTime, minToken)
try {
const signature = await signFunction([msg.owner, JSON.stringify(params)])
if (!signature) {
return undefined
}
return new PollInitMsg(signature, msg)
} catch {
return undefined
}
} }
static async create( static async create(
@ -134,43 +126,27 @@ export class PollInitMsg {
minToken?: BigNumber, minToken?: BigNumber,
endTime?: number endTime?: number
): Promise<PollInitMsg | undefined> { ): Promise<PollInitMsg | undefined> {
const signFunction = async (params: string[]) => { return this._createWithSignFunction(signFunction(signer), signer, question, answers, pollType, minToken, endTime)
if ('send' in signer.provider) {
return signer.provider.send('eth_signTypedData_v4', params)
} else {
return undefined
}
}
return this._createWithSignFunction(signFunction, signer, question, answers, pollType, minToken, endTime)
} }
static fromProto(payload: PollInit, recoverFunction: ({ data, sig }: { data: any; sig: string }) => string) { static fromProto(payload: PollInit, recoverFunction: ({ data, sig }: { data: any; sig: string }) => string) {
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) const signature = utils.hexlify(payload.signature)
const minToken = payload.minToken ? BigNumber.from(payload.minToken) : undefined
const params = createSignMsgParams({ const msg = {
owner, ...payload,
timestamp, owner: utils.getAddress(utils.hexlify(payload.owner)),
question, minToken: payload.minToken ? BigNumber.from(payload.minToken) : undefined,
answers, }
endTime,
minToken, const params = createSignMsgParams(msg)
pollType,
})
const verifiedAddress = recoverFunction({ const verifiedAddress = recoverFunction({
data: params, data: params,
sig: signature, sig: signature,
}) })
if (verifiedAddress != owner) { if (verifiedAddress != msg.owner) {
return undefined return undefined
} }
const id = utils.solidityKeccak256(['address', 'uint256'], [owner, timestamp])
return new PollInitMsg(id, owner, signature, timestamp, question, answers, pollType, endTime, minToken) return new PollInitMsg(signature, msg)
} }
} }

View File

@ -20,7 +20,7 @@ describe('PollInitMsg', () => {
expect(poll).to.not.be.undefined expect(poll).to.not.be.undefined
if (poll) { if (poll) {
expect(poll.owner).to.eq(alice.address) expect(poll.owner).to.eq(alice.address)
expect(poll.endTime).to.eq(poll.timestamp + 10000000) expect(poll.endTime).to.eq(poll.timestamp + 100000000)
expect(poll.answers).to.deep.eq(['one', 'two', 'three']) expect(poll.answers).to.deep.eq(['one', 'two', 'three'])
expect(poll.minToken).to.be.undefined expect(poll.minToken).to.be.undefined
expect(poll.pollType).to.eq(PollType.WEIGHTED) expect(poll.pollType).to.eq(PollType.WEIGHTED)
@ -32,12 +32,12 @@ describe('PollInitMsg', () => {
JSON.stringify( JSON.stringify(
createSignMsgParams({ createSignMsgParams({
owner: poll.owner, owner: poll.owner,
answers: poll.answers,
endTime: poll.endTime,
pollType: poll.pollType,
minToken: poll.minToken,
question: poll.question,
timestamp: poll.timestamp, timestamp: poll.timestamp,
question: poll.question,
answers: poll.answers,
pollType: poll.pollType,
endTime: poll.endTime,
minToken: poll.minToken,
}) })
), ),
].join() ].join()
@ -63,12 +63,12 @@ describe('PollInitMsg', () => {
JSON.stringify( JSON.stringify(
createSignMsgParams({ createSignMsgParams({
owner: poll.owner, owner: poll.owner,
answers: poll.answers,
endTime: poll.endTime,
pollType: poll.pollType,
minToken: poll.minToken,
question: poll.question,
timestamp: poll.timestamp, timestamp: poll.timestamp,
question: poll.question,
answers: poll.answers,
pollType: poll.pollType,
endTime: poll.endTime,
minToken: poll.minToken,
}) })
), ),
].join() ].join()
@ -90,12 +90,12 @@ describe('PollInitMsg', () => {
if (poll) { if (poll) {
const msg = createSignMsgParams({ const msg = createSignMsgParams({
owner: poll.owner, owner: poll.owner,
answers: poll.answers,
endTime: poll.endTime,
pollType: poll.pollType,
minToken: poll.minToken,
question: poll.question,
timestamp: poll.timestamp, timestamp: poll.timestamp,
question: poll.question,
answers: poll.answers,
pollType: poll.pollType,
endTime: poll.endTime,
minToken: poll.minToken,
}) })
expect(poll.signature).to.eq([poll.owner, JSON.stringify(msg)].join()) expect(poll.signature).to.eq([poll.owner, JSON.stringify(msg)].join())

View File

@ -16,9 +16,11 @@ export function Poll({ poll, wakuVoting, signer }: PollProps) {
const [selectedAnswer, setSelectedAnswer] = useState(0) const [selectedAnswer, setSelectedAnswer] = useState(0)
const [tokenAmount, setTokenAmount] = useState(0) const [tokenAmount, setTokenAmount] = useState(0)
const [address, setAddress] = useState('') const [address, setAddress] = useState('')
useEffect(() => { useEffect(() => {
signer.getAddress().then((e) => setAddress(e)) signer.getAddress().then((e) => setAddress(e))
}, [signer]) }, [signer])
return ( return (
<PollWrapper> <PollWrapper>
<PollTitle> <PollTitle>

View File

@ -22,7 +22,7 @@ function Example({ appName }: ExampleProps) {
const [selectedType, setSelectedType] = useState(PollType.WEIGHTED) const [selectedType, setSelectedType] = useState(PollType.WEIGHTED)
useEffect(() => { useEffect(() => {
provider.on('accountsChanged', async () => { ;(window as any).ethereum.on('accountsChanged', async () => {
provider.send('eth_requestAccounts', []) provider.send('eth_requestAccounts', [])
setSigner(provider.getSigner()) setSigner(provider.getSigner())
}) })