diff --git a/index.js b/index.js index 561c761..3ed1828 100644 --- a/index.js +++ b/index.js @@ -100,7 +100,7 @@ class Transaction { }, { name: 'v', allowZero: true, - default: new Buffer([0x1c]) + default: new Buffer([opts.chain || opts.common ? this._common.chainId() : 0x1c]) }, { name: 'r', length: 32, @@ -184,10 +184,15 @@ class Transaction { // elements (i.e. nonce, gasprice, startgas, to, value, data), hash nine elements, with v replaced by // CHAIN_ID, r = 0 and s = 0. - const onEIP155BlockOrLater = this._common.gteHardfork('spuriousDragon') const v = ethUtil.bufferToInt(this.v) + const onEIP155BlockOrLater = this._common.gteHardfork('spuriousDragon') const vAndChainIdMeetEIP155Conditions = v === this._chainId * 2 + 35 || v === this._chainId * 2 + 36 - if (vAndChainIdMeetEIP155Conditions && onEIP155BlockOrLater) { + const meetsAllEIP155Conditions = vAndChainIdMeetEIP155Conditions && onEIP155BlockOrLater + + const unsigned = !this.r.length && !this.s.length + const seeksReplayProtection = this._chainId > 0 + + if (unsigned && seeksReplayProtection || !unsigned && meetsAllEIP155Conditions) { const raw = this.raw.slice() this.v = this._chainId this.r = 0 @@ -229,8 +234,8 @@ class Transaction { * @return {Buffer} */ getSenderPublicKey () { - if (!this._senderPubKey || !this._senderPubKey.length) { - if (!this.verifySignature()) throw new Error('Invalid Signature') + if (!this.verifySignature()) { + throw new Error('Invalid Signature') } return this._senderPubKey } diff --git a/test/api.js b/test/api.js index c1a68ad..353d223 100644 --- a/test/api.js +++ b/test/api.js @@ -8,7 +8,7 @@ tape('[Transaction]: Basic functions', function (t) { var transactions = [] t.test('should decode transactions', function (st) { - txFixtures.slice(0, 3).forEach(function (tx) { + txFixtures.slice(0, 4).forEach(function (tx) { var pt = new Transaction(tx.raw) st.equal('0x' + pt.nonce.toString('hex'), tx.raw[0]) st.equal('0x' + pt.gasPrice.toString('hex'), tx.raw[1]) @@ -32,7 +32,7 @@ tape('[Transaction]: Basic functions', function (t) { }) t.test('should hash', function (st) { - var tx = new Transaction(txFixtures[2].raw) + var tx = new Transaction(txFixtures[3].raw) st.deepEqual(tx.hash(), new Buffer('375a8983c9fc56d7cfd118254a80a8d7403d590a6c9e105532b67aca1efb97aa', 'hex')) st.deepEqual(tx.hash(false), new Buffer('61e1ec33764304dddb55348e7883d4437426f44ab3ef65e6da1e025734c03ff0', 'hex')) st.deepEqual(tx.hash(true), new Buffer('375a8983c9fc56d7cfd118254a80a8d7403d590a6c9e105532b67aca1efb97aa', 'hex')) @@ -40,7 +40,7 @@ tape('[Transaction]: Basic functions', function (t) { }) t.test('should hash with defined chainId', function (st) { - var tx = new Transaction(txFixtures[3].raw) + var tx = new Transaction(txFixtures[4].raw) st.equal(tx.hash().toString('hex'), '0f09dc98ea85b7872f4409131a790b91e7540953992886fc268b7ba5c96820e4') st.equal(tx.hash(true).toString('hex'), '0f09dc98ea85b7872f4409131a790b91e7540953992886fc268b7ba5c96820e4') st.equal(tx.hash(false).toString('hex'), 'f97c73fdca079da7652dbc61a46cd5aeef804008e057be3e712c43eac389aaf0') @@ -153,7 +153,7 @@ tape('[Transaction]: Basic functions', function (t) { var tx = new Transaction() st.equals(tx.getDataFee().toNumber(), 0) - tx = new Transaction(txFixtures[2].raw) + tx = new Transaction(txFixtures[3].raw) st.equals(tx.getDataFee().toNumber(), 2496) st.end() @@ -185,6 +185,42 @@ tape('[Transaction]: Basic functions', function (t) { st.end() }) + t.test('Verify EIP155 Signature before and after signing with private key', function (st) { + // Inputs and expected results for this test are taken directly from the example in https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md + var txRaw = [ + '0x09', + '0x4a817c800', + '0x5208', + '0x3535353535353535353535353535353535353535', + '0x0de0b6b3a7640000', + '0x' + ] + var privateKey = Buffer.from('4646464646464646464646464646464646464646464646464646464646464646', 'hex') + var pt = new Transaction(txRaw, { chain: 1 }) + st.equal(pt.serialize().toString('hex'), 'ec098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a764000080018080') + pt.sign(privateKey) + st.equal(pt.hash(false).toString('hex'), 'daf5a779ae972f972197303d7b574746c7ef83eadac0f2791ad23db92e4c8e53') + st.equal(pt.serialize().toString('hex'), 'f86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83') + st.end() + }) + + t.test('Serialize correctly after being signed with EIP155 Signature for tx created on ropsten', function (st) { + var txRaw = [ + '0x1', + '0x02540be400', + '0x5208', + '0xd7250824390ec5c8b71d856b5de895e271170d9d', + '0x0de0b6b3a7640000', + '0x' + ] + + var privateKey = Buffer.from('DE3128752F183E8930D7F00A2AAA302DCB5E700B2CBA2D8CA5795660F07DEFD5', 'hex') + var pt = new Transaction(txRaw, { chain: 3 }) + pt.sign(privateKey) + st.equal(pt.serialize().toString('hex'), 'f86c018502540be40082520894d7250824390ec5c8b71d856b5de895e271170d9d880de0b6b3a76400008029a0d3512c68099d184ccf54f44d9d6905bff303128574b663dcf10b4c726ddd8133a0628acc8f481dea593f13309dfc5f0340f83fdd40cf9fbe47f782668f6f3aec74') + st.end() + }) + t.test('sign tx with chainId specified in params', function (st) { var tx = new Transaction({ chainId: 42 }) st.equal(tx.getChainId(), 42) @@ -202,4 +238,21 @@ tape('[Transaction]: Basic functions', function (t) { st.equal(tx.getChainId(), 0x16b2) st.end() }) + + t.test('EIP155 hashing when singing', function (st) { + txFixtures.slice(0, 3).forEach(function (txData) { + const tx = new Transaction(txData.raw.slice(0, 6), {chain: 1}) + + var privKey = new Buffer(txData.privateKey, 'hex') + tx.sign(privKey) + + st.equal( + tx.getSenderAddress().toString('hex'), + txData.sendersAddress, + 'computed sender address should equal the fixture\'s one' + ) + }) + + st.end() + }) }) diff --git a/test/txs.json b/test/txs.json index feb159b..2415383 100644 --- a/test/txs.json +++ b/test/txs.json @@ -14,6 +14,22 @@ "0x5e1d3a76fbf824220eafc8c79ad578ad2b67d01b0c2425eb1f1347e8f50882ab", "0x5bd428537f05f9830e93792f90ea6a3e2d1ee84952dd96edbae9f658f831ab13" ] +}, { + "privateKey": "4646464646464646464646464646464646464646464646464646464646464646", + "sendersAddress": "9d8a62f656a8d1615c1294fd71e9cfb3e4855a4f", + "type": "message", + "cost": 500, + "raw": [ + "0x09", + "0x04a817c800", + "0x2710", + "0x3535353535353535353535353535353535353535", + "0x0de0b6b3a7640000", + "0x", + "0x25", + "0x28ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276", + "0x67cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83" + ] }, { "privateKey": "e0a462586887362a18a318b128dbc1e3a0cae6d4b0739f5d0419ec25114bc722", "sendersAddress": "d13d825eb15c87b247c4c26331d66f225a5f632e",