@@ -359,15 +359,16 @@ exports.importPublic = function (publicKey) {
359359 * ECDSA sign
360360 * @param {Buffer } msgHash
361361 * @param {Buffer } privateKey
362+ * @param {Number } [chainId]
362363 * @return {Object }
363364 */
364- exports . ecsign = function ( msgHash , privateKey ) {
365+ exports . ecsign = function ( msgHash , privateKey , chainId ) {
365366 const sig = secp256k1 . sign ( msgHash , privateKey )
366367
367368 const ret = { }
368369 ret . r = sig . signature . slice ( 0 , 32 )
369370 ret . s = sig . signature . slice ( 32 , 64 )
370- ret . v = sig . recovery + 27
371+ ret . v = chainId ? sig . recovery + ( chainId * 2 + 35 ) : sig . recovery + 27
371372 return ret
372373}
373374
@@ -390,12 +391,13 @@ exports.hashPersonalMessage = function (message) {
390391 * @param {Number } v
391392 * @param {Buffer } r
392393 * @param {Buffer } s
394+ * @param {Number } [chainId]
393395 * @return {Buffer } publicKey
394396 */
395- exports . ecrecover = function ( msgHash , v , r , s ) {
397+ exports . ecrecover = function ( msgHash , v , r , s , chainId ) {
396398 const signature = Buffer . concat ( [ exports . setLength ( r , 32 ) , exports . setLength ( s , 32 ) ] , 64 )
397- const recovery = v - 27
398- if ( recovery !== 0 && recovery !== 1 ) {
399+ const recovery = calculateSigRecovery ( v , chainId )
400+ if ( ! isValidSigRecovery ( recovery ) ) {
399401 throw new Error ( 'Invalid signature v value' )
400402 }
401403 const senderPubKey = secp256k1 . recover ( msgHash , signature , recovery )
@@ -407,12 +409,13 @@ exports.ecrecover = function (msgHash, v, r, s) {
407409 * @param {Number } v
408410 * @param {Buffer } r
409411 * @param {Buffer } s
412+ * @param {Number } [chainId]
410413 * @return {String } sig
411414 */
412- exports . toRpcSig = function ( v , r , s ) {
413- // NOTE: with potential introduction of chainId this might need to be updated
414- if ( v !== 27 && v !== 28 ) {
415- throw new Error ( 'Invalid recovery id ' )
415+ exports . toRpcSig = function ( v , r , s , chainId ) {
416+ let recovery = calculateSigRecovery ( v , chainId )
417+ if ( ! isValidSigRecovery ( recovery ) ) {
418+ throw new Error ( 'Invalid signature v value ' )
416419 }
417420
418421 // geth (and the RPC eth_sign method) uses the 65 byte format used by Bitcoin
@@ -561,18 +564,19 @@ exports.addHexPrefix = function (str) {
561564 * @param {Buffer } r
562565 * @param {Buffer } s
563566 * @param {Boolean } [homestead=true]
567+ * @param {Number } [chainId]
564568 * @return {Boolean }
565569 */
566570
567- exports . isValidSignature = function ( v , r , s , homestead ) {
571+ exports . isValidSignature = function ( v , r , s , homestead , chainId ) {
568572 const SECP256K1_N_DIV_2 = new BN ( '7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0' , 16 )
569573 const SECP256K1_N = new BN ( 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141' , 16 )
570574
571575 if ( r . length !== 32 || s . length !== 32 ) {
572576 return false
573577 }
574578
575- if ( v !== 27 && v !== 28 ) {
579+ if ( ! isValidSigRecovery ( calculateSigRecovery ( v , chainId ) ) ) {
576580 return false
577581 }
578582
@@ -711,3 +715,11 @@ exports.defineProperties = function (self, fields, data) {
711715 }
712716 }
713717}
718+
719+ function calculateSigRecovery ( v , chainId ) {
720+ return chainId ? v - ( 2 * chainId + 35 ) : v - 27
721+ }
722+
723+ function isValidSigRecovery ( recovery ) {
724+ return recovery === 0 || recovery === 1
725+ }
0 commit comments