From 9e8031d5e3cf94cabe07685be510397367e90413 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Tue, 9 Oct 2018 18:26:13 +1100 Subject: Throw and handle errors from Providers. In web3 wrapper when a response contains an error field we throw this rather than return response.result which is often undefined. In Signature Utils we handle the error thrown when a user rejects the signing dialogue to prevent double signing. Exposed the ZeroExTransaction JSON schema. In Website only use the MetamaskSubprovider if we can detect the provider is Metamask --- packages/order-utils/test/eip712_utils_test.ts | 2 +- packages/order-utils/test/signature_utils_test.ts | 149 +++++++++++++--------- 2 files changed, 88 insertions(+), 63 deletions(-) (limited to 'packages/order-utils/test') diff --git a/packages/order-utils/test/eip712_utils_test.ts b/packages/order-utils/test/eip712_utils_test.ts index dc76595db..d65cabe9c 100644 --- a/packages/order-utils/test/eip712_utils_test.ts +++ b/packages/order-utils/test/eip712_utils_test.ts @@ -33,7 +33,7 @@ describe('EIP712 Utils', () => { { salt: new BigNumber('0'), data: constants.NULL_BYTES, - signerAddress: constants.NULL_BYTES, + signerAddress: constants.NULL_ADDRESS, }, constants.NULL_ADDRESS, ); diff --git a/packages/order-utils/test/signature_utils_test.ts b/packages/order-utils/test/signature_utils_test.ts index 7f6987f6a..f2d6790fb 100644 --- a/packages/order-utils/test/signature_utils_test.ts +++ b/packages/order-utils/test/signature_utils_test.ts @@ -17,6 +17,28 @@ chaiSetup.configure(); const expect = chai.expect; describe('Signature utils', () => { + let makerAddress: string; + const fakeExchangeContractAddress = '0x1dc4c1cefef38a777b15aa20260a54e584b16c48'; + let order: Order; + before(async () => { + const availableAddreses = await web3Wrapper.getAvailableAddressesAsync(); + makerAddress = availableAddreses[0]; + order = { + makerAddress, + takerAddress: constants.NULL_ADDRESS, + senderAddress: constants.NULL_ADDRESS, + feeRecipientAddress: constants.NULL_ADDRESS, + makerAssetData: constants.NULL_ADDRESS, + takerAssetData: constants.NULL_ADDRESS, + exchangeAddress: fakeExchangeContractAddress, + salt: new BigNumber(0), + makerFee: new BigNumber(0), + takerFee: new BigNumber(0), + makerAssetAmount: new BigNumber(0), + takerAssetAmount: new BigNumber(0), + expirationTimeSeconds: new BigNumber(0), + }; + }); describe('#isValidSignatureAsync', () => { let dataHex = '0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0'; const ethSignSignature = @@ -117,54 +139,54 @@ describe('Signature utils', () => { }); }); describe('#ecSignOrderAsync', () => { - let makerAddress: string; - const fakeExchangeContractAddress = '0x1dc4c1cefef38a777b15aa20260a54e584b16c48'; - let order: Order; - before(async () => { - const availableAddreses = await web3Wrapper.getAvailableAddressesAsync(); - makerAddress = availableAddreses[0]; - order = { - makerAddress, - takerAddress: constants.NULL_ADDRESS, - senderAddress: constants.NULL_ADDRESS, - feeRecipientAddress: constants.NULL_ADDRESS, - makerAssetData: constants.NULL_ADDRESS, - takerAssetData: constants.NULL_ADDRESS, - exchangeAddress: fakeExchangeContractAddress, - salt: new BigNumber(0), - makerFee: new BigNumber(0), - takerFee: new BigNumber(0), - makerAssetAmount: new BigNumber(0), - takerAssetAmount: new BigNumber(0), - expirationTimeSeconds: new BigNumber(0), + it('should default to eth_sign if eth_signTypedData is unavailable', async () => { + const expectedSignature = + '0x1c3582f06356a1314dbf1c0e534c4d8e92e59b056ee607a7ff5a825f5f2cc5e6151c5cc7fdd420f5608e4d5bef108e42ad90c7a4b408caef32e24374cf387b0d7603'; + + const fakeProvider = { + async sendAsync(payload: JSONRPCRequestPayload, callback: JSONRPCErrorCallback): Promise { + if (payload.method === 'eth_signTypedData') { + callback(new Error('Internal RPC Error')); + } else if (payload.method === 'eth_sign') { + const [address, message] = payload.params; + const signature = await web3Wrapper.signMessageAsync(address, message); + callback(null, { + id: 42, + jsonrpc: '2.0', + result: signature, + }); + } else { + callback(null, { id: 42, jsonrpc: '2.0', result: [makerAddress] }); + } + }, }; + const signedOrder = await signatureUtils.ecSignOrderAsync(fakeProvider, order, makerAddress); + expect(signedOrder.signature).to.equal(expectedSignature); }); - it('should result in the same signature as signing order hash without prefix', async () => { - const orderHashHex = orderHashUtils.getOrderHashHex(order); - const sig = ethUtil.ecsign( - ethUtil.toBuffer(orderHashHex), - Buffer.from('F2F48EE19680706196E2E339E5DA3491186E0C4C5030670656B0E0164837257D', 'hex'), - ); - const signatureBuffer = Buffer.concat([ - ethUtil.toBuffer(sig.v), - ethUtil.toBuffer(sig.r), - ethUtil.toBuffer(sig.s), - ethUtil.toBuffer(SignatureType.EIP712), - ]); - const signatureHex = `0x${signatureBuffer.toString('hex')}`; - const signedOrder = await signatureUtils.ecSignOrderAsync(provider, order, makerAddress); - const isValidSignature = await signatureUtils.isValidSignatureAsync( - provider, - orderHashHex, - signedOrder.signature, - makerAddress, + it('should throw if the user denies the signing request', async () => { + const fakeProvider = { + async sendAsync(payload: JSONRPCRequestPayload, callback: JSONRPCErrorCallback): Promise { + if (payload.method === 'eth_signTypedData') { + callback(new Error('User denied message signature')); + } else if (payload.method === 'eth_sign') { + const [address, message] = payload.params; + const signature = await web3Wrapper.signMessageAsync(address, message); + callback(null, { + id: 42, + jsonrpc: '2.0', + result: signature, + }); + } else { + callback(null, { id: 42, jsonrpc: '2.0', result: [makerAddress] }); + } + }, + }; + expect(signatureUtils.ecSignOrderAsync(fakeProvider, order, makerAddress)).to.to.be.rejectedWith( + 'User denied message signature', ); - expect(signatureHex).to.eq(signedOrder.signature); - expect(isValidSignature).to.eq(true); }); }); describe('#ecSignHashAsync', () => { - let makerAddress: string; before(async () => { const availableAddreses = await web3Wrapper.getAvailableAddressesAsync(); makerAddress = availableAddreses[0]; @@ -239,27 +261,30 @@ describe('Signature utils', () => { }); }); describe('#ecSignTypedDataOrderAsync', () => { - let makerAddress: string; - const fakeExchangeContractAddress = '0x1dc4c1cefef38a777b15aa20260a54e584b16c48'; - let order: Order; - before(async () => { - const availableAddreses = await web3Wrapper.getAvailableAddressesAsync(); - makerAddress = availableAddreses[0]; - order = { + it('should result in the same signature as signing the order hash without an ethereum message prefix', async () => { + // Note: Since order hash is an EIP712 hash the result of a valid EIP712 signature + // of order hash is the same as signing the order without the Ethereum Message prefix. + const orderHashHex = orderHashUtils.getOrderHashHex(order); + const sig = ethUtil.ecsign( + ethUtil.toBuffer(orderHashHex), + Buffer.from('F2F48EE19680706196E2E339E5DA3491186E0C4C5030670656B0E0164837257D', 'hex'), + ); + const signatureBuffer = Buffer.concat([ + ethUtil.toBuffer(sig.v), + ethUtil.toBuffer(sig.r), + ethUtil.toBuffer(sig.s), + ethUtil.toBuffer(SignatureType.EIP712), + ]); + const signatureHex = `0x${signatureBuffer.toString('hex')}`; + const signedOrder = await signatureUtils.ecSignTypedDataOrderAsync(provider, order, makerAddress); + const isValidSignature = await signatureUtils.isValidSignatureAsync( + provider, + orderHashHex, + signedOrder.signature, makerAddress, - takerAddress: constants.NULL_ADDRESS, - senderAddress: constants.NULL_ADDRESS, - feeRecipientAddress: constants.NULL_ADDRESS, - makerAssetData: constants.NULL_ADDRESS, - takerAssetData: constants.NULL_ADDRESS, - exchangeAddress: fakeExchangeContractAddress, - salt: new BigNumber(0), - makerFee: new BigNumber(0), - takerFee: new BigNumber(0), - makerAssetAmount: new BigNumber(0), - takerAssetAmount: new BigNumber(0), - expirationTimeSeconds: new BigNumber(0), - }; + ); + expect(signatureHex).to.eq(signedOrder.signature); + expect(isValidSignature).to.eq(true); }); it('should return the correct Signature for signatureHex concatenated as R + S + V', async () => { const expectedSignature = -- cgit v1.2.3