Skip to content
This repository was archived by the owner on Jun 17, 2021. It is now read-only.

Commit cc7850d

Browse files
committed
Handle signatures for chainIds greater than 110
The signature module was assuming that the `v` recovery value fit in a single byte. With chainIds larger than 110, since `v(110) = 110 * 2 + 35 = 255`, this assumption breaks (see chainid.network for a list of chainIds with large values). This commit changes `fromRpcSig` so the recovery `v` value is not just the 65th byte of the signaure, but all bytes after the 64th.
1 parent e19262e commit cc7850d

File tree

2 files changed

+53
-6
lines changed

2 files changed

+53
-6
lines changed

src/signature.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const { ecdsaSign, ecdsaRecover, publicKeyConvert } = require('ethereum-cryptography/secp256k1')
22
import * as BN from 'bn.js'
3-
import { toBuffer, setLengthLeft, bufferToHex } from './bytes'
3+
import { toBuffer, setLengthLeft, bufferToHex, bufferToInt } from './bytes'
44
import { keccak } from './hash'
55
import { assertIsBuffer } from './helpers'
66

@@ -71,12 +71,11 @@ export const toRpcSig = function(v: number, r: Buffer, s: Buffer, chainId?: numb
7171
export const fromRpcSig = function(sig: string): ECDSASignature {
7272
const buf: Buffer = toBuffer(sig)
7373

74-
// NOTE: with potential introduction of chainId this might need to be updated
75-
if (buf.length !== 65) {
74+
if (buf.length < 65) {
7675
throw new Error('Invalid signature length')
7776
}
7877

79-
let v = buf[64]
78+
let v = bufferToInt(buf.slice(64))
8079
// support both versions of `eth_sign` responses
8180
if (v < 27) {
8281
v += 27

test/signature.spec.ts

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,20 @@ describe('ecsign', function() {
4646
)
4747
assert.equal(sig.v, 41)
4848
})
49+
50+
it('should produce a signature for chainId=150', function() {
51+
const chainId = 150
52+
const sig = ecsign(echash, ecprivkey, chainId)
53+
assert.deepEqual(
54+
sig.r,
55+
Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex'),
56+
)
57+
assert.deepEqual(
58+
sig.s,
59+
Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex'),
60+
)
61+
assert.equal(sig.v, chainId * 2 + 35)
62+
})
4963
})
5064

5165
describe('ecrecover', function() {
@@ -63,6 +77,14 @@ describe('ecrecover', function() {
6377
const pubkey = ecrecover(echash, v, r, s, chainId)
6478
assert.deepEqual(pubkey, privateToPublic(ecprivkey))
6579
})
80+
it('should recover a public key (chainId = 150)', function() {
81+
const chainId = 150
82+
const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex')
83+
const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex')
84+
const v = chainId * 2 + 35
85+
const pubkey = ecrecover(echash, v, r, s, chainId)
86+
assert.deepEqual(pubkey, privateToPublic(ecprivkey))
87+
})
6688
it('should fail on an invalid signature (v = 21)', function() {
6789
const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex')
6890
const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex')
@@ -160,6 +182,13 @@ describe('isValidSignature', function() {
160182
const v = 41
161183
assert.equal(isValidSignature(v, r, s, false, chainId), true)
162184
})
185+
it('should work otherwise(chainId=150)', function() {
186+
const chainId = 150
187+
const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex')
188+
const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex')
189+
const v = chainId * 2 + 35
190+
assert.equal(isValidSignature(v, r, s, false, chainId), true)
191+
})
163192
// FIXME: add homestead test
164193
})
165194

@@ -194,13 +223,32 @@ describe('message sig', function() {
194223
)
195224
})
196225

197-
it('should throw on invalid length', function() {
226+
it('should return hex strings that the RPC can use (chainId=150)', function() {
227+
const chainId = 150
228+
const v = chainId * 2 + 35
229+
assert.equal(
230+
toRpcSig(v, r, s, chainId),
231+
'0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66014f',
232+
)
233+
assert.deepEqual(
234+
fromRpcSig(
235+
'0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66014f',
236+
),
237+
{
238+
v,
239+
r: r,
240+
s: s,
241+
},
242+
)
243+
})
244+
245+
it('should throw on shorter length', function() {
198246
assert.throws(function() {
199247
fromRpcSig('')
200248
})
201249
assert.throws(function() {
202250
fromRpcSig(
203-
'0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca660042',
251+
'0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca',
204252
)
205253
})
206254
})

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy