Skip to content

Commit b29998e

Browse files
jrainvilleemizzle
authored andcommitted
fix(@embark/rpc-manager): fix eth_signTypedData method + tests
The signTypedData rpc method was broken, because it didn't check for the node accounts, meaning that if you wanted to sign with a node account that was unlocked, like in the tests, it would throw
1 parent 67581ce commit b29998e

File tree

5 files changed

+147
-30
lines changed

5 files changed

+147
-30
lines changed

dapps/tests/app/test/another_storage_spec.js

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*global artifacts, contract, config, it, web3*/
1+
/*global artifacts, contract, config, it, web3, evmMethod*/
22
const assert = require('assert');
33
const AnotherStorage = artifacts.require('AnotherStorage');
44
const SimpleStorage = artifacts.require('SimpleStorage');
@@ -63,4 +63,51 @@ contract("AnotherStorage", function() {
6363
let result = await AnotherStorage.simpleStorageAddress();
6464
assert.equal(result.toString(), SimpleStorage.options.address);
6565
});
66+
67+
it("can sign using eth_signTypedData with a custom account", async function() {
68+
const chainId = await web3.eth.net.getId();
69+
70+
const domain = [
71+
{name: "name", type: "string"},
72+
{name: "version", type: "string"},
73+
{name: "chainId", type: "uint256"},
74+
{name: "verifyingContract", type: "address"}
75+
];
76+
77+
const redeem = [
78+
{name: "keycard", type: "address"},
79+
{name: "receiver", type: "address"},
80+
{name: "code", type: "bytes32"}
81+
];
82+
83+
const domainData = {
84+
name: "KeycardGift",
85+
version: "1",
86+
chainId,
87+
verifyingContract: SimpleStorage.options.address
88+
};
89+
90+
const message = {
91+
keycard: accounts[1],
92+
receiver: accounts[2],
93+
code: web3.utils.sha3("hello world")
94+
};
95+
96+
const data = {
97+
types: {
98+
EIP712Domain: domain,
99+
Redeem: redeem
100+
},
101+
primaryType: "Redeem",
102+
domain: domainData,
103+
message
104+
};
105+
106+
const signature = await evmMethod("eth_signTypedData", [
107+
accounts[0],
108+
data
109+
]);
110+
assert.strictEqual(signature, '0x5dcbab53809985222a62807dd2f23551902fa4471377e319d5d682e1458646714cc71' +
111+
'faa76cf6de3e0d871edbfa85628db552619d681594d5af2f34be2c33cdd1b');
112+
});
66113
});

dapps/tests/app/test/array_references_spec.js

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
/*global artifacts, contract, config, it, web3*/
1+
/*global artifacts, contract, config, it, web3, evmMethod*/
22
const assert = require('assert');
33
const SomeContract = artifacts.require('SomeContract');
44
const MyToken2 = artifacts.require('MyToken2');
55

6+
let accounts;
67
config({
78
contracts: {
89
deploy: {
@@ -22,6 +23,8 @@ config({
2223
}
2324
}
2425
}
26+
}, (err, theAccounts) => {
27+
accounts = theAccounts;
2528
});
2629

2730
contract("SomeContract", function() {
@@ -37,4 +40,50 @@ contract("SomeContract", function() {
3740
assert.strictEqual(address, web3.eth.defaultAccount);
3841
});
3942

43+
it("can sign using eth_signTypedData with a node account", async function() {
44+
const chainId = await web3.eth.net.getId();
45+
46+
const domain = [
47+
{name: "name", type: "string"},
48+
{name: "version", type: "string"},
49+
{name: "chainId", type: "uint256"},
50+
{name: "verifyingContract", type: "address"}
51+
];
52+
53+
const redeem = [
54+
{name: "keycard", type: "address"},
55+
{name: "receiver", type: "address"},
56+
{name: "code", type: "bytes32"}
57+
];
58+
59+
const domainData = {
60+
name: "KeycardGift",
61+
version: "1",
62+
chainId,
63+
verifyingContract: SomeContract.options.address
64+
};
65+
66+
const message = {
67+
keycard: accounts[1],
68+
receiver: accounts[2],
69+
code: web3.utils.sha3("hello world")
70+
};
71+
72+
const data = {
73+
types: {
74+
EIP712Domain: domain,
75+
Redeem: redeem
76+
},
77+
primaryType: "Redeem",
78+
domain: domainData,
79+
message
80+
};
81+
82+
const signature = await evmMethod("eth_signTypedData", [
83+
accounts[0],
84+
data
85+
]);
86+
// Impossible to tell what the signature will be because the account is not deterministic
87+
assert.ok(signature);
88+
});
4089
});

packages/plugins/rpc-manager/src/lib/eth_signData.ts

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Callback, Embark, EmbarkEvents } from "embark-core";
22
import { __ } from "embark-i18n";
33
import Web3 from "web3";
44
import RpcModifier from "./rpcModifier";
5+
import {handleSignRequest} from './utils/signUtils';
56

67
export default class EthSignData extends RpcModifier {
78
constructor(embark: Embark, rpcModifierEvents: EmbarkEvents, public nodeAccounts: string[], public accounts: any[], protected web3: Web3) {
@@ -16,21 +17,7 @@ export default class EthSignData extends RpcModifier {
1617
return callback(null, params);
1718
}
1819

19-
try {
20-
const [fromAddr] = params.request.params;
21-
22-
const account = this.nodeAccounts.find(acc => (
23-
Web3.utils.toChecksumAddress(acc) ===
24-
Web3.utils.toChecksumAddress(fromAddr)
25-
));
26-
27-
if (!account) {
28-
params.sendToNode = false;
29-
}
30-
} catch (err) {
31-
return callback(err);
32-
}
33-
callback(null, params);
20+
handleSignRequest(this.nodeAccounts, params, callback);
3421
}
3522

3623
private async ethSignDataResponse(params: any, callback: Callback<any>) {

packages/plugins/rpc-manager/src/lib/eth_signTypedData.ts

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import { sign, transaction } from "@omisego/omg-js-util";
2-
import { Callback, Embark, EmbarkEvents } from "embark-core";
3-
import { __ } from "embark-i18n";
1+
import {sign, transaction} from "@omisego/omg-js-util";
2+
import {Callback, Embark, EmbarkEvents} from "embark-core";
3+
import {__} from "embark-i18n";
44
import Web3 from "web3";
55
import RpcModifier from "./rpcModifier";
6+
import {handleSignRequest, isNodeAccount} from './utils/signUtils';
67

78
export default class EthSignTypedData extends RpcModifier {
89
constructor(embark: Embark, rpcModifierEvents: EmbarkEvents, public nodeAccounts: string[], public accounts: any[], protected web3: Web3) {
@@ -18,15 +19,14 @@ export default class EthSignTypedData extends RpcModifier {
1819
// - eth_signTypedData_v3
1920
// - eth_signTypedData_v4
2021
// - personal_signTypedData (parity)
21-
if (params.request.method.includes("signTypedData")) {
22-
// indicate that we do not want this call to go to the node
23-
params.sendToNode = false;
22+
if (!params.request.method.includes("signTypedData")) {
2423
return callback(null, params);
2524
}
26-
callback(null, params);
25+
26+
handleSignRequest(this.nodeAccounts, params, callback);
2727
}
28-
private async ethSignTypedDataResponse(params: any, callback: Callback<any>) {
2928

29+
private async ethSignTypedDataResponse(params: any, callback: Callback<any>) {
3030
// check for:
3131
// - eth_signTypedData
3232
// - eth_signTypedData_v3
@@ -35,12 +35,20 @@ export default class EthSignTypedData extends RpcModifier {
3535
if (!params.request.method.includes("signTypedData")) {
3636
return callback(null, params);
3737
}
38-
39-
this.logger.trace(__(`Modifying blockchain '${params.request.method}' response:`));
40-
this.logger.trace(__(`Original request/response data: ${JSON.stringify({ request: params.request, response: params.response })}`));
41-
4238
try {
4339
const [fromAddr, typedData] = params.request.params;
40+
41+
if (isNodeAccount(this.nodeAccounts, fromAddr)) {
42+
// If it's a node account, we send the result because it should already be signed
43+
return callback(null, params);
44+
}
45+
46+
this.logger.trace(__(`Modifying blockchain '${params.request.method}' response:`));
47+
this.logger.trace(__(`Original request/response data: ${JSON.stringify({
48+
request: params.request,
49+
response: params.response
50+
})}`));
51+
4452
const account = this.accounts.find((acc) => Web3.utils.toChecksumAddress(acc.address) === Web3.utils.toChecksumAddress(fromAddr));
4553
if (!(account && account.privateKey)) {
4654
return callback(
@@ -51,7 +59,10 @@ export default class EthSignTypedData extends RpcModifier {
5159
const signature = sign(toSign, [account.privateKey]);
5260

5361
params.response.result = signature[0];
54-
this.logger.trace(__(`Modified request/response data: ${JSON.stringify({ request: params.request, response: params.response })}`));
62+
this.logger.trace(__(`Modified request/response data: ${JSON.stringify({
63+
request: params.request,
64+
response: params.response
65+
})}`));
5566
} catch (err) {
5667
return callback(err);
5768
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import Web3 from "web3";
2+
3+
export function isNodeAccount(nodeAccounts, fromAddr) {
4+
const account = nodeAccounts.find(acc => (
5+
Web3.utils.toChecksumAddress(acc) ===
6+
Web3.utils.toChecksumAddress(fromAddr)
7+
));
8+
return !!account;
9+
}
10+
11+
export function handleSignRequest(nodeAccounts, params, callback) {
12+
try {
13+
const [fromAddr] = params.request.params;
14+
15+
// If it's not a node account, we don't send it to the Node as it won't understand it
16+
if (!isNodeAccount(nodeAccounts, fromAddr)) {
17+
params.sendToNode = false;
18+
}
19+
} catch (err) {
20+
return callback(err);
21+
}
22+
callback(null, params);
23+
}

0 commit comments

Comments
 (0)