1
- const { ecdsaSign, ecdsaRecover, publicKeyConvert } = require ( 'ethereum-cryptography/secp256k1' )
1
+ import { ecdsaSign , ecdsaRecover , publicKeyConvert } from 'ethereum-cryptography/secp256k1'
2
2
import BN from 'bn.js'
3
3
import { toBuffer , setLengthLeft , bufferToHex , bufferToInt } from './bytes'
4
4
import { keccak } from './hash'
5
5
import { assertIsBuffer } from './helpers'
6
+ import { BNLike , toType , TypeOutput } from './types'
6
7
7
8
export interface ECDSASignature {
8
9
v : number
9
10
r : Buffer
10
11
s : Buffer
11
12
}
12
13
14
+ export interface ECDSASignatureBuffer {
15
+ v : Buffer
16
+ r : Buffer
17
+ s : Buffer
18
+ }
19
+
13
20
/**
14
21
* Returns the ECDSA signature of a message hash.
15
22
*/
16
- export const ecsign = function (
17
- msgHash : Buffer ,
18
- privateKey : Buffer ,
19
- chainId ?: number
20
- ) : ECDSASignature {
21
- const sig = ecdsaSign ( msgHash , privateKey )
22
- const recovery : number = sig . recid
23
-
24
- const ret = {
25
- r : Buffer . from ( sig . signature . slice ( 0 , 32 ) ) ,
26
- s : Buffer . from ( sig . signature . slice ( 32 , 64 ) ) ,
27
- v : chainId ? recovery + ( chainId * 2 + 35 ) : recovery + 27
23
+ export function ecsign ( msgHash : Buffer , privateKey : Buffer , chainId ?: number ) : ECDSASignature
24
+ export function ecsign ( msgHash : Buffer , privateKey : Buffer , chainId : BNLike ) : ECDSASignatureBuffer
25
+ export function ecsign ( msgHash : Buffer , privateKey : Buffer , chainId : any ) : any {
26
+ const { signature, recid : recovery } = ecdsaSign ( msgHash , privateKey )
27
+
28
+ const r = Buffer . from ( signature . slice ( 0 , 32 ) )
29
+ const s = Buffer . from ( signature . slice ( 32 , 64 ) )
30
+
31
+ if ( ! chainId || typeof chainId === 'number' ) {
32
+ // return legacy type ECDSASignature (deprecated in favor of ECDSASignatureBuffer to handle large chainIds)
33
+ if ( chainId && ! Number . isSafeInteger ( chainId ) ) {
34
+ throw new Error (
35
+ 'The provided number is greater than MAX_SAFE_INTEGER (please use an alternative input type)'
36
+ )
37
+ }
38
+ const v = chainId ? recovery + ( chainId * 2 + 35 ) : recovery + 27
39
+ return { r, s, v }
28
40
}
29
41
30
- return ret
42
+ const chainIdBN = toType ( chainId , TypeOutput . BN )
43
+ const v = chainIdBN
44
+ . muln ( 2 )
45
+ . addn ( 35 )
46
+ . addn ( recovery )
47
+ . toArrayLike ( Buffer )
48
+ return { r, s, v }
31
49
}
32
50
33
- function calculateSigRecovery ( v : number , chainId ?: number ) : number {
34
- return chainId ? v - ( 2 * chainId + 35 ) : v - 27
51
+ function calculateSigRecovery ( v : BNLike , chainId ?: BNLike ) : BN {
52
+ const vBN = toType ( v , TypeOutput . BN )
53
+ if ( ! chainId ) {
54
+ return vBN . subn ( 27 )
55
+ }
56
+ const chainIdBN = toType ( chainId , TypeOutput . BN )
57
+ return vBN . sub ( chainIdBN . muln ( 2 ) . addn ( 35 ) )
35
58
}
36
59
37
- function isValidSigRecovery ( recovery : number ) : boolean {
38
- return recovery === 0 || recovery === 1
60
+ function isValidSigRecovery ( recovery : number | BN ) : boolean {
61
+ const rec = new BN ( recovery )
62
+ return rec . eqn ( 0 ) || rec . eqn ( 1 )
39
63
}
40
64
41
65
/**
@@ -44,25 +68,25 @@ function isValidSigRecovery(recovery: number): boolean {
44
68
*/
45
69
export const ecrecover = function (
46
70
msgHash : Buffer ,
47
- v : number ,
71
+ v : BNLike ,
48
72
r : Buffer ,
49
73
s : Buffer ,
50
- chainId ?: number
74
+ chainId ?: BNLike
51
75
) : Buffer {
52
76
const signature = Buffer . concat ( [ setLengthLeft ( r , 32 ) , setLengthLeft ( s , 32 ) ] , 64 )
53
77
const recovery = calculateSigRecovery ( v , chainId )
54
78
if ( ! isValidSigRecovery ( recovery ) ) {
55
79
throw new Error ( 'Invalid signature v value' )
56
80
}
57
- const senderPubKey = ecdsaRecover ( signature , recovery , msgHash )
81
+ const senderPubKey = ecdsaRecover ( signature , recovery . toNumber ( ) , msgHash )
58
82
return Buffer . from ( publicKeyConvert ( senderPubKey , false ) . slice ( 1 ) )
59
83
}
60
84
61
85
/**
62
86
* Convert signature parameters into the format of `eth_sign` RPC method.
63
87
* @returns Signature
64
88
*/
65
- export const toRpcSig = function ( v : number , r : Buffer , s : Buffer , chainId ?: number ) : string {
89
+ export const toRpcSig = function ( v : BNLike , r : Buffer , s : Buffer , chainId ?: BNLike ) : string {
66
90
const recovery = calculateSigRecovery ( v , chainId )
67
91
if ( ! isValidSigRecovery ( recovery ) ) {
68
92
throw new Error ( 'Invalid signature v value' )
@@ -101,11 +125,11 @@ export const fromRpcSig = function(sig: string): ECDSASignature {
101
125
* @param homesteadOrLater Indicates whether this is being used on either the homestead hardfork or a later one
102
126
*/
103
127
export const isValidSignature = function (
104
- v : number ,
128
+ v : BNLike ,
105
129
r : Buffer ,
106
130
s : Buffer ,
107
131
homesteadOrLater : boolean = true ,
108
- chainId ?: number
132
+ chainId ?: BNLike
109
133
) : boolean {
110
134
const SECP256K1_N_DIV_2 = new BN (
111
135
'7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0' ,
@@ -121,8 +145,8 @@ export const isValidSignature = function(
121
145
return false
122
146
}
123
147
124
- const rBN : BN = new BN ( r )
125
- const sBN : BN = new BN ( s )
148
+ const rBN = new BN ( r )
149
+ const sBN = new BN ( s )
126
150
127
151
if ( rBN . isZero ( ) || rBN . gt ( SECP256K1_N ) || sBN . isZero ( ) || sBN . gt ( SECP256K1_N ) ) {
128
152
return false
0 commit comments