Skip to content
This repository has been archived by the owner on Apr 6, 2020. It is now read-only.

Update official transaction tests #131

Merged
merged 10 commits into from
Apr 14, 2019
Merged
32 changes: 19 additions & 13 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict'
const ethUtil = require('ethereumjs-util')
const Common = require('ethereumjs-common')
const Common = require('ethereumjs-common').default
const BN = ethUtil.BN

// secp256k1n/2
Expand Down Expand Up @@ -151,7 +151,11 @@ class Transaction {
if (chainId < 0) chainId = 0

// set chainId
this._chainId = chainId || data.chainId || 0
if (opts.chain || opts.common) {
this._chainId = this._common.chainId()
} else {
this._chainId = chainId || data.chainId || 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The discussion on here is pretty useful - can we add an inline comment to summarize (or paste the link to comment)? This would help future devs to gather context quickly.

}
}

/**
Expand All @@ -170,16 +174,20 @@ class Transaction {
hash (includeSignature) {
if (includeSignature === undefined) includeSignature = true

// EIP155 spec:
// when computing the hash of a transaction for purposes of signing or recovering,
// instead of hashing only the first six elements (ie. nonce, gasprice, startgas, to, value, data),
// hash nine elements, with v replaced by CHAIN_ID, r = 0 and s = 0

let items
if (includeSignature) {
items = this.raw
} else {
if (this._chainId > 0) {
// EIP155 spec:
// If block.number >= 2,675,000 and v = CHAIN_ID * 2 + 35 or v = CHAIN_ID * 2 + 36, then when computing
// the hash of a transaction for purposes of signing or recovering, instead of hashing only the first six
// 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 vAndChainIdMeetEIP155Conditions = v === this._chainId * 2 + 35 || v === this._chainId * 2 + 36
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

if (vAndChainIdMeetEIP155Conditions && onEIP155BlockOrLater) {
const raw = this.raw.slice()
this.v = this._chainId
this.r = 0
Expand Down Expand Up @@ -239,11 +247,9 @@ class Transaction {
}

try {
let v = ethUtil.bufferToInt(this.v)
if (this._chainId > 0) {
v -= this._chainId * 2 + 8
}
this._senderPubKey = ethUtil.ecrecover(msgHash, v, this.r, this.s)
const v = ethUtil.bufferToInt(this.v)
const useChainIdWhileRecoveringPubKey = v >= this._chainId * 2 + 35 && this._common.gteHardfork('spuriousDragon')
this._senderPubKey = ethUtil.ecrecover(msgHash, v, this.r, this.s, useChainIdWhileRecoveringPubKey && this._chainId)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok.

} catch (e) {
return false
}
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
"author": "mjbecze <[email protected]>",
"license": "MPL-2.0",
"dependencies": {
"ethereumjs-common": "^0.6.1",
"ethereumjs-util": "^5.0.0"
"ethereumjs-common": "^1.0.0",
"ethereumjs-util": "^6.0.0"
},
"devDependencies": {
"async": "^2.0.0",
Expand All @@ -33,7 +33,7 @@
"contributor": "^0.1.25",
"coveralls": "^2.11.4",
"documentation": "^8.0.0",
"ethereumjs-testing": "0.0.1",
"ethereumjs-testing": "git+https://github.com/ethereumjs/ethereumjs-testing.git#v1.2.5",
"istanbul": "^0.4.1",
"karma": "^1.1.1",
"karma-browserify": "^5.1.0",
Expand Down
2 changes: 1 addition & 1 deletion test/fake.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const tape = require('tape')
const utils = require('ethereumjs-util')
const Common = require('ethereumjs-common')
const Common = require('ethereumjs-common').default
const FakeTransaction = require('../fake.js')

// Use private key 0x0000000000000000000000000000000000000000000000000000000000000001 as 'from' Account
Expand Down
108 changes: 56 additions & 52 deletions test/transactionRunner.js
Original file line number Diff line number Diff line change
@@ -1,64 +1,68 @@
const Tx = require('../index.js')
const tape = require('tape')
const ethUtil = require('ethereumjs-util')
const Common = require('ethereumjs-common')
const argv = require('minimist')(process.argv.slice(2))
const testing = require('ethereumjs-testing')

var txTests = testing.getTests('transaction', argv)
const forkNames = [
'Byzantium',
'Constantinople',
'EIP150',
'EIP158',
'Frontier',
'Homestead'
]

const bufferToHex = ethUtil.bufferToHex
const addHexPrefix = ethUtil.addHexPrefix
const stripHexPrefix = ethUtil.stripHexPrefix
const setLength = ethUtil.setLength

function addPad (v) {
if (v.length % 2 === 1) {
v = '0' + v
}
return v
const forkNameMap = {
Byzantium: 'byzantium',
Constantinople: 'constantinople',
EIP150: 'tangerineWhistle',
EIP158: 'spuriousDragon',
Frontier: 'chainstart',
Homestead: 'homestead'
}

function normalizeZero (v) {
if (!v || v === '0x') {
return '0x00'
} else {
return v
}
}
tape('TransactionTests', (t) => {
const fileFilterRegex = argv.file ? new RegExp(argv.file + '[^\\w]') : undefined

testing.runTests(function (testData, sst, cb) {
var tTx = testData.transaction
testing.getTests('TransactionTests', (filename, testName, testData) => {
t.test(testName, (st) => {
const rawTx = ethUtil.toBuffer(testData.rlp)

try {
var rawTx = ethUtil.toBuffer(testData.rlp)
var tx = new Tx(rawTx, {
hardfork: testData.blockNumber >= 1150000 ? 'homestead' : 'chainstart'
})
} catch (e) {
sst.equal(undefined, tTx, 'should not have any fields ')
cb()
return
}
let tx
forkNames.forEach(forkName => {
const forkTestData = testData[forkName]
const shouldBeInvalid = Object.keys(forkTestData).length === 0
try {
tx = new Tx(rawTx, {
hardfork: forkNameMap[forkName],
chain: 1
})

if (tTx && tx.validate()) {
try {
sst.equal(tx._common instanceof Common, true, '_common class attribute')
sst.equal(bufferToHex(tx.data), addHexPrefix(addPad(stripHexPrefix(tTx.data))), 'data')
sst.equal(normalizeZero(bufferToHex(tx.gasLimit)), tTx.gasLimit, 'gasLimit')
sst.equal(normalizeZero(bufferToHex(tx.gasPrice)), tTx.gasPrice, 'gasPrice')
sst.equal(normalizeZero(bufferToHex(tx.nonce)), tTx.nonce, 'nonce')
sst.equal(normalizeZero(bufferToHex(setLength(tx.r, 32))), normalizeZero(bufferToHex(setLength(tTx.r, 32))), 'r')
sst.equal(normalizeZero(bufferToHex(tx.s)), normalizeZero(bufferToHex(tTx.s)), 's')
sst.equal(normalizeZero(bufferToHex(tx.v)), normalizeZero(bufferToHex(tTx.v)), 'v')
sst.equal(bufferToHex(tx.to), addHexPrefix(tTx.to), 'to')
sst.equal(normalizeZero(bufferToHex(tx.value)), tTx.value, 'value')
sst.equal(normalizeZero(bufferToHex(tx.getSenderAddress())), addHexPrefix(testData.sender), "sender's address")
} catch (e) {
sst.fail(e)
}
} else {
sst.equal(undefined, tTx, 'no tx params in test')
}
cb()
}, txTests, tape)
const sender = tx.getSenderAddress().toString('hex')
const hash = tx.hash().toString('hex')

const validationErrors = tx.validate(true)
const transactionIsValid = validationErrors.length === 0
const hashAndSenderAreCorrect = forkTestData && (sender === forkTestData.sender && hash === forkTestData.hash)

if (shouldBeInvalid) {
st.assert(!transactionIsValid, `Transaction should be invalid on ${forkName}`)
} else {
st.assert(hashAndSenderAreCorrect && transactionIsValid, `Transaction should be valid on ${forkName}`)
}
} catch (e) {
if (shouldBeInvalid) {
st.assert(shouldBeInvalid, `Transaction should be invalid on ${forkName}`)
} else {
st.fail(`Transaction should be valid on ${forkName}`)
}
}
})
st.end()
})
}, fileFilterRegex)
.then(() => {
t.end()
})
})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, clean and nice and easy to read test setup! 👍