Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Separate API Tests for the VM #315

Closed
wants to merge 13 commits into from
5 changes: 5 additions & 0 deletions lib/fakeBlockChain.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const Buffer = require('safe-buffer').Buffer
const utils = require('ethereumjs-util')

module.exports = {
fake: true,
getBlock: function (blockTag, cb) {
var hash

Expand All @@ -24,5 +25,9 @@ module.exports = {

delBlock: function (hash, cb) {
cb(null)
},

iterator: function (name, onBlock, cb) {
cb(null)
}
}
9 changes: 9 additions & 0 deletions lib/runBlock.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ const BN = ethUtil.BN
* @param cb {Function} the callback which is given an error string
*/
module.exports = function (opts, cb) {
if (typeof opts === 'function' && cb === undefined) {
cb = opts
return cb(new Error('invalid input, opts must be provided'))
}

if (!opts.block) {
return cb(new Error('invalid input, block must be provided'))
}

const self = this

// parse options
Expand Down
5 changes: 3 additions & 2 deletions lib/runBlockchain.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@ module.exports = function (blockchain, cb) {
}, function (err, results) {
if (err) {
// remove invalid block
console.log('Invalid block error:', err)
blockchain.delBlock(block.header.hash(), cb)
blockchain.delBlock(block.header.hash(), function () {
cb(err)
})
} else {
// set as new head block
headBlock = block
Expand Down
18 changes: 14 additions & 4 deletions lib/runTx.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,23 @@ const Block = require('ethereumjs-block')
* @param cb {Function} - the callback
*/
module.exports = function (opts, cb) {
if (typeof opts === 'function' && cb === undefined) {
cb = opts
return cb(new Error('invalid input, opts must be provided'))
}

var self = this
var block = opts.block
var tx = opts.tx
var gasLimit
var results
var basefee

// tx is required
if (!tx) {
return cb(new Error('invalid input, tx is required'))
}

// create a reasonable default if no block is given
if (!block) {
block = new Block()
Expand Down Expand Up @@ -63,6 +73,10 @@ module.exports = function (opts, cb) {
* populates the cache with the 'to' and 'from' of the tx
*/
function populateCache (cb) {
if (opts.populateCache === false) {
return cb()
}

var accounts = new Set()
accounts.add(tx.from.toString('hex'))
accounts.add(block.header.coinbase.toString('hex'))
Expand All @@ -71,10 +85,6 @@ module.exports = function (opts, cb) {
accounts.add(tx.to.toString('hex'))
}

if (opts.populateCache === false) {
return cb()
}

self.stateManager.warmCache(accounts, cb)
}

Expand Down
2 changes: 1 addition & 1 deletion lib/stateManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ proto.revert = function (cb) {
//
proto.getStateRoot = function (cb) {
var self = this
self.cacheFlush(function (err) {
self.cache.flush(function (err) {
if (err) {
return cb(err)
}
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"testBlockchainBlockGasLimit": "npm run build:dist && node --stack-size=1500 ./tests/tester -b --dist --dir='bcBlockGasLimitTest'",
"testBlockchainValid": "npm run build:dist && node --stack-size=1500 ./tests/tester -b --dist --dir='bcValidBlockTest'",
"testBlockchainTotalDifficulty": "npm run build:dist && node --stack-size=1500 ./tests/tester -b --dist --dir='bcTotalDifficultyTest'",
"testAPI": "node ./tests/api/runner",
"test": "node ./tests/tester -a",
"lint": "standard",
"prepublishOnly": "npm run lint && npm run build:dist && npm run testBuildIntegrity",
Expand Down
76 changes: 76 additions & 0 deletions tests/api/bloom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
const tape = require('tape')
const Bloom = require('../../lib/bloom')
const utils = require('ethereumjs-util')

const byteSize = 256

tape('bloom', (t) => {
t.test('should initialize without params', (st) => {
const b = new Bloom()
st.deepEqual(b.bitvector, utils.zeros(byteSize), 'should be empty')
st.end()
})

t.test('shouldnt initialize with invalid bitvector', (st) => {
st.throws(() => new Bloom('invalid'), /AssertionError/, 'should fail for invalid type')
st.throws(() => new Bloom(utils.zeros(byteSize / 2), /AssertionError/), 'should fail for invalid length')
st.end()
})

t.test('should contain values of hardcoded bitvector', (st) => {
const hex = '00000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000'
const vector = Buffer.from(hex, 'hex')

const b = new Bloom(vector)
st.true(b.check('value 1'), 'should contain value 1')
st.true(b.check('value 2'), 'should contain value 2')
st.end()
})

t.test('check shouldnt be tautology', (st) => {
const b = new Bloom()
st.false(b.check('random value'), 'should not contain random value')
st.end()
})

t.test('should correctly add value', (st) => {
const b = new Bloom()
b.add('value')
let found = b.check('value')
st.true(found, 'should contain added value')
st.end()
})

t.test('should check multiple values', (st) => {
const b = new Bloom()
b.add('value 1')
b.add('value 2')
let found = b.multiCheck(['value 1', 'value 2'])
st.true(found, 'should contain both values')
st.end()
})

t.test('should or two filters', (st) => {
const b1 = new Bloom()
b1.add('value 1')
const b2 = new Bloom()
b2.add('value 2')

b1.or(b2)
st.true(b1.check('value 2'), 'should contain value 2 after or')
st.end()
})

t.test('should generate the correct bloom filter value ', (st) => {
let bloom = new Bloom()
bloom.add(Buffer.from('1d7022f5b17d2f8b695918fb48fa1089c9f85401', 'hex'))
bloom.add(Buffer.from('8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925', 'hex'))
bloom.add(Buffer.from('0000000000000000000000005409ed021d9299bf6814279a6a1411a7e866a631', 'hex'))
bloom.add(Buffer.from('0000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48', 'hex'))
st.equal(
bloom.bitvector.toString('hex'),
'00000000000000000000080000800000000000000000000000000000000000000000000000000000000000000000000000000000000004000000080000200000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000010008000000000000000002000000000000000000000000008000000000000'
)
st.end()
})
})
36 changes: 36 additions & 0 deletions tests/api/fakeBlockChain.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const tape = require('tape')
const fakeBlockchain = require('../../lib/fakeBlockChain')

tape('fakeBlockChain', (t) => {
const blockchain = fakeBlockchain

t.test('should fail to get block by invalid type', (st) => {
blockchain.getBlock(null, (err, block) => {
st.ok(err, 'should return error')
st.notOk(block)
st.end()
})
})

t.test('should get block hash by number', (st) => {
blockchain.getBlock(1, isValidBlock(st))
})

t.test('should get block hash by buffer', (st) => {
blockchain.getBlock(Buffer.from('0x0'), isValidBlock(st))
})

t.test('should "del" block', (st) => {
blockchain.delBlock('0x0', (err) => {
st.error(err)
st.end()
})
})
})

const isValidBlock = (st) => (err, block) => {
st.notOk(err)
st.ok(block, 'should return non-empty value')
st.ok(Buffer.isBuffer(block.hash()), 'block hash should of type buffer')
st.end()
}
101 changes: 101 additions & 0 deletions tests/api/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
const { promisify } = require('util')
const tape = require('tape')
const Level = require('levelup')
const util = require('ethereumjs-util')
const Blockchain = require('ethereumjs-blockchain')
const Block = require('ethereumjs-block')
const VM = require('../../lib/index')
const testData = require('./testdata.json')

tape('VM with fake blockchain', (t) => {
t.test('should insantiate without params', (st) => {
const vm = new VM()
st.ok(vm.stateManager)
st.equal(vm.stateManager.trie.root, util.KECCAK256_RLP, 'it has default trie')
st.ok(vm.stateManager.blockchain.fake, 'it has fake blockchain by default')
st.end()
})

t.test('should be able to activate precompiles', (st) => {
let vm = new VM({ activatePrecompiles: true })
st.notEqual(vm.stateManager.trie.root, util.KECCAK256_RLP, 'it has different root')
st.end()
})

t.test('should only accept valid chain and fork', (st) => {
let vm = new VM({ chain: 'ropsten', hardfork: 'byzantium' })
st.equal(vm.stateManager._common.param('gasPrices', 'ecAdd'), 500)

try {
vm = new VM({ chain: 'mainchain', hardfork: 'homestead' })
st.fail('should have failed for invalid chain')
} catch (e) {
st.ok(e.message.includes('not supported'))
}

st.end()
})

t.test('should run blockchain without blocks', async (st) => {
const vm = new VM()
const run = promisify(vm.runBlockchain.bind(vm))
await run()
st.end()
})
})

tape('VM with blockchain', (t) => {
const db = new Level('', {
db: require('memdown')
})
var cacheDB = new Level('./.cachedb')
const blockchain = new Blockchain(db)
blockchain.ethash.cacheDB = cacheDB

const putGenesisP = promisify(blockchain.putGenesis.bind(blockchain))
const putBlockP = promisify(blockchain.putBlock.bind(blockchain))
const getHeadP = promisify(blockchain.getHead.bind(blockchain))

t.test('should instantiate', (st) => {
const vm = new VM({
blockchain
})
st.equal(vm.stateManager.trie.root, util.KECCAK256_RLP, 'it has default trie')
st.notOk(vm.stateManager.blockchain.fake, 'it doesn\'t have fake blockchain')
st.end()
})

t.test('should run blockchain without blocks', async (st) => {
const vm = new VM({ blockchain })
await runBlockchain(vm)
st.end()
})

t.test('should run blockchain with blocks', async (st) => {
const vm = new VM({ blockchain })
const genesis = new Block(Buffer.from(testData.genesisRLP.slice(2), 'hex'))
const block = new Block(Buffer.from(testData.blocks[0].rlp.slice(2), 'hex'))

await putGenesisP(genesis)
st.equal(blockchain.meta.genesis, 'ce1f26f798dd03c8782d63b3e42e79a64eaea5694ea686ac5d7ce3df5171d1ae')

await putBlockP(block)
const head = await getHeadP()
st.equal(
head.hash().toString('hex'),
'f53f268d23a71e85c7d6d83a9504298712b84c1a2ba220441c86eeda0bf0b6e3'
)

vm.runBlock = (block, cb) => cb(new Error('test'))
runBlockchain(vm)
.then(() => st.fail('it hasn\'t returned any errors'))
.catch((e) => {
st.equal(e.message, 'test', 'it has correctly propagated runBlock\'s error')
st.end()
})
})
})

const runBlockchain = (vm) => {
return promisify(vm.runBlockchain.bind(vm))()
}
Loading