Skip to content

Commit ea98b8a

Browse files
committed
fix: serialize bigints
1 parent 7274ae2 commit ea98b8a

File tree

3 files changed

+72
-128
lines changed

3 files changed

+72
-128
lines changed

yarn-project/end-to-end/scripts/web3signer/docker-compose.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ configs:
77
88
services:
99
web3signer:
10-
image: consensys/web3signer:25.3.0
10+
image: consensys/web3signer:25.6.0
1111
ports:
1212
- "9000:9000"
1313
configs:
@@ -17,7 +17,7 @@ services:
1717
- --http-listen-port=9000
1818
- --http-host-allowlist=*
1919
- --key-store-path=/keys
20-
- --logging=DEBUG
20+
- --logging=ALL
2121
- eth1
2222
- --chain-id=31337
2323

yarn-project/end-to-end/src/composed/web3signer/integration_remote_signer.test.ts

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ import { jest } from '@jest/globals';
66
import type { TransactionSerializable, TypedDataDefinition } from 'viem';
77
import { privateKeyToAddress } from 'viem/accounts';
88

9-
const { WEB3_SIGNER_URL, L1_CHAIN_ID = '31337', TEST_PRIVATE_KEY } = process.env;
9+
const {
10+
WEB3_SIGNER_URL = 'http://localhost:9000',
11+
L1_CHAIN_ID = '31337',
12+
TEST_PRIVATE_KEY = '0x1111111111111111111111111111111111111111111111111111111111111111',
13+
} = process.env;
1014

1115
describe('RemoteSigner integration: Web3Signer (compose)', () => {
1216
jest.setTimeout(180_000);
@@ -41,22 +45,24 @@ describe('RemoteSigner integration: Web3Signer (compose)', () => {
4145
remoteSigner = new RemoteSigner(address, web3SignerUrl);
4246
});
4347

44-
// the remote signer ends up calling a client? I'm not sure this is implemented in web3signer
45-
it.skip('signs EIP-712 typed data and matches r/s with local', async () => {
48+
it('signs EIP-712 typed data and matches r/s with local', async () => {
4649
const typedData: TypedDataDefinition = {
47-
domain: { name: 'Aztec', version: '1', chainId: 1 },
50+
domain: {
51+
name: 'TallySlashingProposer',
52+
version: '1',
53+
chainId,
54+
verifyingContract: EthAddress.random().toString(),
55+
},
4856
types: {
49-
Mail: [
50-
{ name: 'from', type: 'address' },
51-
{ name: 'to', type: 'address' },
52-
{ name: 'contents', type: 'string' },
57+
Vote: [
58+
{ name: 'votes', type: 'bytes' },
59+
{ name: 'slot', type: 'uint256' },
5360
],
5461
},
55-
primaryType: 'Mail',
62+
primaryType: 'Vote',
5663
message: {
57-
from: address.toString(),
58-
to: address.toString(),
59-
contents: 'hello',
64+
votes: '0x1234',
65+
slot: '0x42',
6066
},
6167
};
6268

yarn-project/node-keystore/src/signer.ts

Lines changed: 52 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
*/
66
import type { EthSigner } from '@aztec/ethereum';
77
import { Buffer32 } from '@aztec/foundation/buffer';
8-
import { Secp256k1Signer, toRecoveryBit } from '@aztec/foundation/crypto';
8+
import { Secp256k1Signer, randomBytes, toRecoveryBit } from '@aztec/foundation/crypto';
99
import type { EthAddress } from '@aztec/foundation/eth-address';
1010
import { Signature, type ViemTransactionSignature } from '@aztec/foundation/eth-signature';
11+
import { jsonStringify } from '@aztec/foundation/json-rpc';
1112
import { withHexPrefix } from '@aztec/foundation/string';
1213

1314
import {
@@ -27,7 +28,7 @@ import type { EthRemoteSignerConfig } from './types.js';
2728
export class SignerError extends Error {
2829
constructor(
2930
message: string,
30-
public method: 'eth_sign' | 'eth_signTransaction' | 'eth_signTypedData_v4',
31+
public method: string,
3132
public url: string,
3233
public statusCode?: number,
3334
public errorCode?: number,
@@ -128,50 +129,12 @@ export class RemoteSigner implements EthSigner {
128129
* Make a JSON-RPC eth_sign request.
129130
*/
130131
private async makeJsonRpcSignRequest(data: Buffer32): Promise<Signature> {
131-
const url = this.getSignerUrl();
132-
133-
const body = {
134-
jsonrpc: '2.0',
135-
method: 'eth_sign',
136-
params: [this.address.toString(), data.toString()],
137-
id: 1,
138-
};
139-
140-
const response = await this.fetch(url, {
141-
method: 'POST',
142-
headers: {
143-
'Content-Type': 'application/json',
144-
},
145-
body: JSON.stringify(body),
146-
});
147-
148-
if (!response.ok) {
149-
const errorText = await response.text();
150-
throw new SignerError(
151-
`Web3Signer request failed for eth_sign at ${url}: ${response.status} ${response.statusText} - ${errorText}`,
152-
'eth_sign',
153-
url,
154-
response.status,
155-
);
156-
}
157-
158-
const result = await response.json();
159-
160-
if (result.error) {
161-
throw new SignerError(
162-
`Web3Signer JSON-RPC error for eth_sign at ${url}: ${result.error.code} - ${result.error.message}`,
163-
'eth_sign',
164-
url,
165-
undefined,
166-
result.error.code,
167-
);
168-
}
132+
let signatureHex = await this.makeJsonRpcRequest('eth_sign', this.address.toString(), data.toString());
169133

170-
if (!result.result) {
171-
throw new Error('Invalid response from Web3Signer: no result found');
134+
if (typeof signatureHex !== 'string') {
135+
throw new Error('Invalid signature');
172136
}
173137

174-
let signatureHex = result.result;
175138
if (!signatureHex.startsWith('0x')) {
176139
signatureHex = '0x' + signatureHex;
177140
}
@@ -183,50 +146,12 @@ export class RemoteSigner implements EthSigner {
183146
* Make a JSON-RPC eth_signTypedData_v4 request.
184147
*/
185148
private async makeJsonRpcSignTypedDataRequest(typedData: TypedDataDefinition): Promise<Signature> {
186-
const url = this.getSignerUrl();
187-
188-
const body = {
189-
jsonrpc: '2.0',
190-
method: 'eth_signTypedData_v4',
191-
params: [this.address.toString(), typedData],
192-
id: 1,
193-
};
194-
195-
const response = await this.fetch(url, {
196-
method: 'POST',
197-
headers: {
198-
'Content-Type': 'application/json',
199-
},
200-
body: JSON.stringify(body),
201-
});
202-
203-
if (!response.ok) {
204-
const errorText = await response.text();
205-
throw new SignerError(
206-
`Web3Signer request failed for eth_signTypedData_v4 at ${url}: ${response.status} ${response.statusText} - ${errorText}`,
207-
'eth_signTypedData_v4',
208-
url,
209-
response.status,
210-
);
211-
}
212-
213-
const result = await response.json();
214-
215-
if (result.error) {
216-
throw new SignerError(
217-
`Web3Signer JSON-RPC error for eth_signTypedData_v4 at ${url}: ${result.error.code} - ${result.error.message}`,
218-
'eth_signTypedData_v4',
219-
url,
220-
undefined,
221-
result.error.code,
222-
);
223-
}
149+
let signatureHex = await this.makeJsonRpcRequest('eth_signTypedData', this.address.toString(), typedData);
224150

225-
if (!result.result) {
226-
throw new Error('Invalid response from Web3Signer: no result found');
151+
if (typeof signatureHex !== 'string') {
152+
throw new Error('Invalid signature');
227153
}
228154

229-
let signatureHex = result.result;
230155
if (!signatureHex.startsWith('0x')) {
231156
signatureHex = '0x' + signatureHex;
232157
}
@@ -242,8 +167,6 @@ export class RemoteSigner implements EthSigner {
242167
throw new Error('This signer does not support tx type: ' + tx.type);
243168
}
244169

245-
const url = this.getSignerUrl();
246-
247170
const txObject: RemoteSignerTxObject = {
248171
from: this.address.toString(),
249172
to: tx.to ?? null,
@@ -263,26 +186,49 @@ export class RemoteSigner implements EthSigner {
263186
// blobs: tx.blobs?.map(blob => (typeof blob === 'string' ? blob : bufferToHex(Buffer.from(blob)))),
264187
};
265188

266-
const body = {
267-
jsonrpc: '2.0',
268-
method: 'eth_signTransaction',
269-
params: [txObject],
270-
id: 1,
271-
};
189+
let rawTxHex = await this.makeJsonRpcRequest('eth_signTransaction', txObject);
190+
191+
if (typeof rawTxHex !== 'string') {
192+
throw new Error('Invalid signed tx');
193+
}
194+
195+
if (!rawTxHex.startsWith('0x')) {
196+
rawTxHex = '0x' + rawTxHex;
197+
}
198+
199+
// we get back to whole signed tx. Deserialize it in order to read the signature
200+
const parsedTxWithSignature = parseTransaction(rawTxHex);
201+
if (
202+
parsedTxWithSignature.r === undefined ||
203+
parsedTxWithSignature.s === undefined ||
204+
parsedTxWithSignature.v === undefined
205+
) {
206+
throw new Error('Tx not signed by Web3Signer');
207+
}
208+
209+
return Signature.fromViemTransactionSignature(parsedTxWithSignature as ViemTransactionSignature);
210+
}
211+
212+
/**
213+
* Sends a JSON-RPC request and returns its result
214+
*/
215+
private async makeJsonRpcRequest(method: string, ...params: any[]): Promise<any> {
216+
const url = this.getSignerUrl();
217+
const id = this.generateId();
272218

273219
const response = await this.fetch(url, {
274220
method: 'POST',
275221
headers: {
276222
'Content-Type': 'application/json',
277223
},
278-
body: JSON.stringify(body),
224+
body: jsonStringify({ jsonrpc: '2.0', method, params, id }),
279225
});
280226

281227
if (!response.ok) {
282228
const errorText = await response.text();
283229
throw new SignerError(
284-
`Web3Signer request failed for eth_signTransaction at ${url}: ${response.status} ${response.statusText} - ${errorText}`,
285-
'eth_signTransaction',
230+
`Web3Signer request failed for ${method} at ${url}: ${response.status} ${response.statusText} - ${errorText}`,
231+
method,
286232
url,
287233
response.status,
288234
);
@@ -292,10 +238,10 @@ export class RemoteSigner implements EthSigner {
292238

293239
if (result.error) {
294240
throw new SignerError(
295-
`Web3Signer JSON-RPC error for eth_signTransaction at ${url}: ${result.error.code} - ${result.error.message}`,
296-
'eth_signTransaction',
241+
`Web3Signer JSON-RPC error for ${method} at ${url}: ${result.error.code} - ${result.error.message}`,
242+
method,
297243
url,
298-
undefined,
244+
response.status,
299245
result.error.code,
300246
);
301247
}
@@ -304,22 +250,7 @@ export class RemoteSigner implements EthSigner {
304250
throw new Error('Invalid response from Web3Signer: no result found');
305251
}
306252

307-
let rawTxHex = result.result;
308-
if (!rawTxHex.startsWith('0x')) {
309-
rawTxHex = '0x' + rawTxHex;
310-
}
311-
312-
// we get back to whole signed tx. Deserialize it in order to read the signature
313-
const parsedTxWithSignature = parseTransaction(rawTxHex);
314-
if (
315-
parsedTxWithSignature.r === undefined ||
316-
parsedTxWithSignature.s === undefined ||
317-
parsedTxWithSignature.v === undefined
318-
) {
319-
throw new Error('Tx not signed by Web3Signer');
320-
}
321-
322-
return Signature.fromViemTransactionSignature(parsedTxWithSignature as ViemTransactionSignature);
253+
return result.result;
323254
}
324255

325256
/**
@@ -331,4 +262,11 @@ export class RemoteSigner implements EthSigner {
331262
}
332263
return this.config.remoteSignerUrl;
333264
}
265+
266+
/**
267+
* Generate an id to use for a JSON-RPC call
268+
*/
269+
private generateId(): string {
270+
return randomBytes(4).toString('hex');
271+
}
334272
}

0 commit comments

Comments
 (0)