1. 程式人生 > >ethereumjs/ethereumjs-vm-4-tests

ethereumjs/ethereumjs-vm-4-tests

根據程式碼發現還要了解的模組有:

ethereumjs/merkle-patricia-tree -對應資料儲存的資料結構
ethereumjs-blockchain —— 區塊鏈
ethereumjs-block ——區塊
levelup —— 資料庫
ethereumjs-account ——賬戶狀態

在本部落格的ethereumjs分類中可見他們的學習文件

其實這就是怎麼自己使用各個模組來生成一個類似geth客戶端的以太坊虛擬機器,然後進行各類區塊鏈操作。然後上面的每個模組對應的就是實現這個虛擬機器的各個部分

 

1.

ethereumjs-vm/tests/BlockchainTestsRunner.js

sssconst testUtil = require('./util.js')
const ethUtil = require('ethereumjs-util')
const Trie = require('merkle-patricia-tree/secure')
const Block = require('ethereumjs-block')
const Blockchain = require('ethereumjs-blockchain')
const BlockHeader = require('ethereumjs-block/header.js')
const Level = require('
levelup') var cacheDB = new Level('./.cachedb') //生成一個node.js輕量級資料庫 module.exports = function runBlockchainTest (options, testData, t, cb) { var blockchainDB = new Level('', { db: require('memdown') //如果沒有設定則預設使用'memdown' }) var state = new Trie() var validate = false //不對加入的區塊進行驗證 // Only run with block validation when sealEngine present in test file
// and being set to Ethash PoW validation ,只有在test檔案中出現sealEngine時,執行才帶著區塊驗證,並且將別設定為Ethash工作量證明驗證 if (testData.sealEngine && testData.sealEngine === 'Ethash') { validate = true } var blockchain = new Blockchain({ db: blockchainDB, hardfork: options.forkConfig.toLowerCase(), //硬分叉 validate: validate }) if (validate) { blockchain.ethash.cacheDB = cacheDB } var VM if (options.dist) { VM = require('../dist/index.js') } else { VM = require('../lib/index.js') } var vm = new VM({ state: state, //state字首樹,key = address,value = account state(賬戶狀態) blockchain: blockchain, //設定好的區塊鏈 hardfork: options.forkConfig.toLowerCase() //使用的硬分支規則 }) var genesisBlock = new Block({ hardfork: options.forkConfig.toLowerCase() })//初始區塊 testData.homestead = true //以太坊的版本 if (testData.homestead) { vm.on('beforeTx', function (tx) { tx._homestead = true }) vm.on('beforeBlock', function (block) { block.header.isHomestead = function () { return true } }) } async.series([ // set up pre-state,設定預編譯的狀態,就是使用預編譯中定義的賬戶的nonce\balance\code\storage,根據storage的值去構建一個字首樹,然後該字首樹的root值將作為賬戶的storageRoot
//然後將codeBuf設定到account上
//最後構建了整個區塊鏈上的state字首樹,記錄所有賬戶
function (done) { testUtil.setupPreConditions(state, testData, function () { //詳細看下面 done() }) }, function (done) { // create and add genesis block ,建立並新增初始區塊 genesisBlock.header = new BlockHeader(formatBlockHeader(testData.genesisBlockHeader), { //先建立區塊的區塊頭,並新增到區塊上 hardfork: options.forkConfig.toLowerCase() })
//因為state.root即state字首樹的root值,也就是區塊頭中記錄的stateRoot的值,所以他們相等 t.equal(state.root.toString(
'hex'), genesisBlock.header.stateRoot.toString('hex'), 'correct pre stateRoot') if (testData.genesisRLP) { t.equal(genesisBlock.serialize().toString('hex'), testData.genesisRLP.slice(2), 'correct genesis RLP') } blockchain.putGenesis(genesisBlock, function (err) {//新增初始區塊到區塊鏈上 done(err) }) }, function (done) {//然後再根據testData上的blocks的資訊生成一個區塊 async.eachSeries(testData.blocks, function (raw, cb) { try { var block = new Block(Buffer.from(raw.rlp.slice(2), 'hex'), { hardfork: options.forkConfig.toLowerCase() }) // forces the block into thinking they are homestead if (testData.homestead) { block.header.isHomestead = function () { return true } block.uncleHeaders.forEach(function (uncle) { uncle.isHomestead = function () { return true } }) } blockchain.putBlock(block, function (err) { //新增該普通區塊 cb(err) }) } catch (err) { cb() } }, function () { done() }) }, function runBlockchain (done) {//執行區塊鏈,處理上面新增的區塊,驗證不正確的將不會連上區塊鏈 vm.runBlockchain(function () { done() }) }, function getHead (done) { vm.blockchain.getHead(function (err, block) {//得到最新的區塊頭 if (testData.lastblockhash.substr(0, 2) === '0x') { // fix for BlockchainTests/GeneralStateTests/stRandom/* testData.lastblockhash = testData.lastblockhash.substr(2)//testData.lastblockhash為最後新增的普通區塊的hash值 } t.equal(block.hash().toString('hex'), testData.lastblockhash, 'last block hash')//檢查最新區塊的hash是否為最後新增的普通區塊的hash值,是則說明區塊鏈執行成功 //如果測試失敗,那麼block.header是preState,因為vm.runBlock有一個防止實際的postState在其不等於預期的postState時被匯入的檢查 //跳過這一點對於除錯很有用,這樣verifyPostConditions可以比較testData.postState與實際的postState,而不是與preState。 if (!options.debug) {//不進行state字首樹的除錯 // make sure the state is set before checking post conditions // 保證在檢視post條件前就設定了狀態 state.root = block.header.stateRoot //則直接賦值state字首樹的root } done(err) }) }, function (done) { if (options.debug) {//進行除錯,則testData.postState中是執行後正確的賬戶的資訊,state是當前區塊鏈上的字首樹,對比兩者的狀態是否相同,相同才除錯成功 testUtil.verifyPostConditions(state, testData.postState, t, done) } else { done() } } ], function () { t.equal(blockchain.meta.rawHead.toString('hex'), testData.lastblockhash, 'correct header block') cb() }) } function formatBlockHeader (data) { var r = {} var keys = Object.keys(data) keys.forEach(function (key) { r[key] = ethUtil.addHexPrefix(data[key]) }) return r }

 

setupPreConditions函式:
/**
 * setupPreConditions given JSON testData
 * @param {[type]}   state    - the state DB/trie
 * @param {[type]}   testData - JSON from tests repo
 * @param {Function} done     - callback when function is completed
 */
exports.setupPreConditions = function (state, testData, done) {
    // set up pre-state,設定預編譯的狀態,就是使用預編譯中定義的賬戶的nonce\balance\code\storage
  var keysOfPre = Object.keys(testData.pre)

  async.eachSeries(keysOfPre, function (key, callback) {
    var acctData = testData.pre[key]
    var account = new Account()

    account.nonce = format(acctData.nonce)
    account.balance = format(acctData.balance)

    var codeBuf = Buffer.from(acctData.code.slice(2), 'hex')
    var storageTrie = state.copy()
    storageTrie.root = null

    async.series([

      function (cb2) {
        var keys = Object.keys(acctData.storage)

        async.forEachSeries(keys, function (key, cb3) {//根據storage的值去構建一個字首樹
          var val = acctData.storage[key]
          val = rlp.encode(Buffer.from(val.slice(2), 'hex'))
          key = utils.setLength(Buffer.from(key.slice(2), 'hex'), 32)

          storageTrie.put(key, val, cb3)
        }, cb2)
      },
      function (cb2) {//然後將codeBuf設定到account上
        account.setCode(state, codeBuf, cb2)
      },
      function (cb2) {//然後該字首樹的root值將作為賬戶的storageRoot  
        account.stateRoot = storageTrie.root

        if (testData.exec && key === testData.exec.address) {
          testData.root = storageTrie.root
        }
     //最後構建了整個區塊鏈上的state字首樹,記錄所有賬戶 state.put(Buffer.
from(utils.stripHexPrefix(key), 'hex'), account.serialize(), function () { cb2() }) } ], callback) }, done) }

 

verifyPostConditions和verifyAccountPostConditions函式

exports.verifyPostConditions = function (state, testData, t, cb) {//testData = testData.postState(即testData.json檔案中的postState值)
  var hashedAccounts = {}
  var keyMap = {}

  for (var key in testData) {//key為testData的索引,即address
    var hash = utils.keccak256(Buffer.from(utils.stripHexPrefix(key), 'hex')).toString('hex')//得到address相應hash值
    hashedAccounts[hash] = testData[key] //將testData.postState中對應索引key的賬戶資訊儲存到hashedAccounts[hash]中
    keyMap[hash] = key //address存到KeyMap[hash]中
  }

  var q = async.queue(function (task, cb2) {
    exports.verifyAccountPostConditions(state, task.address, task.account, task.testData, t, cb2)
  }, 1)

  var stream = state.createReadStream()//生成state可讀流,state為區塊鏈上的state 字首樹

  stream.on('data', function (data) { //得到state 字首樹上的所有賬戶資訊
    var acnt = new Account(rlp.decode(data.value)) //對應下面的testData
    var key = data.key.toString('hex') //對應下面的address
    var testData = hashedAccounts[key]//這兩個是testData.postState中的資訊
    var address = keyMap[key]
    delete keyMap[key]

    if (testData) {
      q.push({ //然後呼叫verifyAccountPostConditions函式對他們兩個的值進行對比
        address: address,
        account: acnt,
        testData: testData
      })
    } else {
      t.fail('invalid account in the trie: ' + key)
    }
  })

  stream.on('end', function () {
    function onEnd () {
      for (hash in keyMap) {
        t.fail('Missing account!: ' + keyMap[hash])
      }
      cb()
    }

    if (q.length()) {
      q.drain = onEnd
    } else {
      onEnd()
    }
  })
}

/**
 * verifyAccountPostConditions using JSON from tests repo
 * @param {[type]}   state    DB/trie
 * @param {[type]}   string   Account Address
 * @param {[type]}   account  to verify
 * @param {[type]}   acctData postconditions JSON from tests repo
 * @param {Function} cb       completion callback
 */
exports.verifyAccountPostConditions = function (state, address, account, acctData, t, cb) {//account是區塊鏈上state字首樹的賬戶資訊,acctData是testData.postState上的
  t.comment('Account: ' + address)
  t.equal(format(account.balance, true).toString('hex'), format(acctData.balance, true).toString('hex'), 'correct balance')//檢查餘額值是否相同
  t.equal(format(account.nonce, true).toString('hex'), format(acctData.nonce, true).toString('hex'), 'correct nonce') //檢查nonce值是否相同

  // validate storage,下面都是用於檢查storage的值
  var origRoot = state.root
  var storageKeys = Object.keys(acctData.storage)

  var hashedStorage = {}
  for (var key in acctData.storage) {
    hashedStorage[utils.keccak256(utils.setLength(Buffer.from(key.slice(2), 'hex'), 32)).toString('hex')] = acctData.storage[key]
  }

  if (storageKeys.length > 0) {//即testData.postState中的storage中有值,那麼就要進行對比
    state.root = account.stateRoot
    var rs = state.createReadStream() 
    rs.on('data', function (data) {//得到區塊鏈上state字首樹的所有賬戶資訊
      var key = data.key.toString('hex')
      var val = '0x' + rlp.decode(data.value).toString('hex')

      if (key === '0x') {
        key = '0x00' //將state字首樹中的key從'0x'改為'0x00'
        //下面是相應地改testData.postState中的key
        acctData.storage['0x00'] = acctData.storage['0x00'] ? acctData.storage['0x00'] : acctData.storage['0x']//即acctData.storage['0x00']是否存在,存在則用它,否則就將acctData.storage['0x']的值賦值給acctData.storage['0x00']
        delete acctData.storage['0x'] //刪掉acctData.storage['0x'],就是讓testData.postState中只留key == '0x00'的情況
      }
      //然後再進行比較,直至state字首樹的所有賬戶資訊比較完
      t.equal(val, hashedStorage[key], 'correct storage value')
      delete hashedStorage[key]
    })

    rs.on('end', function () {//然後最後檢視hashedStorage是否有剩下的非空賬戶的賬戶資訊,空賬戶可不相同
      for (var key in hashedStorage) {
        if (hashedStorage[key] !== '0x00') {//有則說明testData.postState中有賬戶在區塊鏈的state字首樹上沒找到,失敗;否則成功
          t.fail('key: ' + key + ' not found in storage')
        }
      }

      state.root = origRoot //那麼區塊鏈的state字首樹是對的,不改變其root,除錯通過
      cb()
    })
  } else {
    cb()
  }
}

 

相應的testData資料為:

{
  "_info" : {
    "comment" : "Taken from ethereum/tests, SimpleTx3_Byzantium, on 27/09/2018",
    "filledwith" : "cpp-1.3.0+commit.6e0ce939.Linux.g++",
    "lllcversion" : "Version: 0.4.18-develop.2017.9.25+commit.a72237f2.Linux.g++",
    "source" : "src/BlockchainTestsFiller/bcValidBlockTest/SimpleTx3Filler.json",
    "sourceHash" : "373dcc1d6dd499c8b9c23b8b4bd87688e216bc8dea7061bdc26da711c33f59cb"
  },
  "blocks" : [
    {
      "blockHeader" : {
        "bloom" : "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
        "coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1",
        "difficulty" : "0x020000",
        "extraData" : "",
        "gasLimit" : "0x2fefba",
        "gasUsed" : "0x5208",
        "hash" : "0x9a843b51370ec98baabc087b27273b193cdeac52ff14314b2781b5eb562179b1",
        "mixHash" : "0xe8fbc818dfd6d2f606c65794513dde2e93c5828fda4fea138d1a3ecf49f67115",
        "nonce" : "0xdffdb73abb355e95",
        "number" : "0x01",
        "parentHash" : "0x701327478e9e63cff9633717a6bb288db43cd4027b5b3e9afe76bfec0eac1c8e",
        "receiptTrie" : "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
        "stateRoot" : "0xc70abbdbb7533542fc237ad6e7f6bdc4cd6c60f865f15bfe88c55ffc6d8c0a0a",
        "timestamp" : "0x59d776fa",
        "transactionsTrie" : "0x53d5b71a8fbb9590de82d69dfa4ac31923b0c8afce0d30d0d8d1e931f25030dc",
        "uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
      },
      "rlp" : "0xf90260f901f9a0701327478e9e63cff9633717a6bb288db43cd4027b5b3e9afe76bfec0eac1c8ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0c70abbdbb7533542fc237ad6e7f6bdc4cd6c60f865f15bfe88c55ffc6d8c0a0aa053d5b71a8fbb9590de82d69dfa4ac31923b0c8afce0d30d0d8d1e931f25030dca0056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefba8252088459d776fa80a0e8fbc818dfd6d2f606c65794513dde2e93c5828fda4fea138d1a3ecf49f6711588dffdb73abb355e95f861f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba0f3266921c93d600c43f6fa4724b7abae079b35b9e95df592f95f9f3445e94c88a012f977552ebdb7a492cf35f3106df16ccb4576ebad4113056ee1f52cbe4978c1c0",
      "transactions" : [
        {
          "data" : "",
          "gasLimit" : "0xc350",
          "gasPrice" : "0x0a",
          "nonce" : "0x00",
          "r" : "0xf3266921c93d600c43f6fa4724b7abae079b35b9e95df592f95f9f3445e94c88",
          "s" : "0x12f977552ebdb7a492cf35f3106df16ccb4576ebad4113056ee1f52cbe4978c1",
          "to" : "0x095e7baea6a6c7c4c2dfeb977efac326af552d87",
          "v" : "0x1b",
          "value" : "0x0a"
        }
      ],
      "uncleHeaders" : [
      ]
    }
  ],
  "genesisBlockHeader" : {
    "bloom" : "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
    "coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1",
    "difficulty" : "0x020000",
    "extraData" : "0x42",
    "gasLimit" : "0x2fefd8",
    "gasUsed" : "0x00",
    "hash" : "0x701327478e9e63cff9633717a6bb288db43cd4027b5b3e9afe76bfec0eac1c8e",
    "mixHash" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
    "nonce" : "0x0102030405060708",
    "number" : "0x00",
    "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
    "receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
    "stateRoot" : "0x7d883d38bc7a640dd66e5cda78cd01b52a7dc40e61f7c2ddbab7cb3ae3b8b9f2",
    "timestamp" : "0x54c98c81",
    "transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
    "uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
  },
  "genesisRLP" : "0xf901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a07d883d38bc7a640dd66e5cda78cd01b52a7dc40e61f7c2ddbab7cb3ae3b8b9f2a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000080832fefd8808454c98c8142a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421880102030405060708c0c0",
  "lastblockhash" : "0x9a843b51370ec98baabc087b27273b193cdeac52ff14314b2781b5eb562179b1",
  "network" : "Byzantium",
  "postState" : {
    "0x095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
      "balance" : "0x0a",
      "code" : "",
      "nonce" : "0x00",
      "storage" : {
      }
    },
    "0x49ec3a96efcc4f9e2e741ea2af622b91f74a2bcc" : {
      "balance" : "0x02540be400",
      "code" : "",
      "nonce" : "0x03",
      "storage" : {
      }
    },
    "0x8888f1f195afa192cfee860698584c030f4c9db1" : {
      "balance" : "0x29a2241af62f3450",
      "code" : "",
      "nonce" : "0x00",
      "storage" : {
      }
    },
    "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
      "balance" : "0x025408afa6",
      "code" : "",
      "nonce" : "0x01",
      "storage" : {
      }
    },
    "0xe4ccfc7cc4a70f5efd0b87867f20acbf68842ef8" : {
      "balance" : "0x02540be400",
      "code" : "",
      "nonce" : "0x00",
      "storage" : {
      }
    }
  }, 
  "pre" : { //這裡就是預編譯時用來生成賬戶的資料
    "0x49ec3a96efcc4f9e2e741ea2af622b91f74a2bcc" : {
      "balance" : "0x02540be400",
      "code" : "",
      "nonce" : "0x03",
      "storage" : {
      }
    },
    "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
      "balance" : "0x02540be400",
      "code" : "",
      "nonce" : "0x00",
      "storage" : {
      }
    },
    "0xe4ccfc7cc4a70f5efd0b87867f20acbf68842ef8" : {
      "balance" : "0x02540be400",
      "code" : "",
      "nonce" : "0x00",
      "storage" : {
      }
    }
  }
}

 

2.

ethereumjs-vm/tests/api/index.js

const { promisify } = require('util')
const tape = require('tape')
const util = require('ethereumjs-util')
const Block = require('ethereumjs-block')
const VM = require('../../lib/index')
const { setupVM } = require('./utils')
const { setupPreConditions } = require('../util')
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.deepEqual(vm.stateManager._trie.root, util.KECCAK256_RLP, 'it has default trie') //會得到一個預設的字首樹
    st.ok(vm.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 }) //啟用預編譯,就會建立一個新字首樹例項,trie = new Trie(),然後新增八個賬戶,之後還在this._precompiled字典中新增八個事前預編譯好的合約
    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' }) //根據定義好的chain和byzantium,其對應的一些值則是預設好的
    st.equal(vm.stateManager._common.param('gasPrices', 'ecAdd'), 500)

    try {
      vm = new VM({ chain: 'mainchain', hardfork: 'homestead' }) //沒有這個chain
      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) => {
  t.test('should instantiate', (st) => {
    const vm = setupVM()
    st.deepEqual(vm.stateManager._trie.root, util.KECCAK256_RLP, 'it has default trie')
    st.notOk(vm.stateManager.fake, 'it doesn\'t have fake blockchain') //這種生成vm的方法是沒有假區塊鏈生成的
    st.end()
  })

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

  t.test('should run blockchain with mocked runBlock', async (st) => {
    const vm = setupVM()
    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(vm.blockchain, genesis) //將初始區塊新增進區塊鏈中
    st.equal(vm.blockchain.meta.genesis.toString('hex'), testData.genesisBlockHeader.hash.slice(2))

    await putBlockP(vm.blockchain, block)
    const head = await getHeadP(vm.blockchain)
    st.equal(
      head.hash().toString('hex'),
      testData.blocks[0].blockHeader.hash.slice(2)
    )

    const setupPreP = promisify(setupPreConditions)
    await setupPreP(vm.stateManager._trie, testData)

    vm.runBlock = (block, cb) => cb(new Error('test'))
    runBlockchainP(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()
      })
  })

  t.test('should run blockchain with blocks', async (st) => {
    const vm = setupVM()
    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(vm.blockchain, genesis)
    st.equal(vm.blockchain.meta.genesis.toString('hex'), testData.genesisBlockHeader.hash.slice(2))

    await putBlockP(vm.blockchain, block)
    const head = await getHeadP(vm.blockchain)
    st.equal(
      head.hash().toString('hex'),
      testData.blocks[0].blockHeader.hash.slice(2)
    )

    const setupPreP = promisify(setupPreConditions)
    await setupPreP(vm.stateManager._trie, testData)

    await runBlockchainP(vm)

    st.end()
  })
})

const runBlockchainP = (vm) => promisify(vm.runBlockchain.bind(vm))()
const putGenesisP = (blockchain, genesis) => promisify(blockchain.putGenesis.bind(blockchain))(genesis)
const putBlockP = (blockchain, block) => promisify(blockchain.putBlock.bind(blockchain))(block)
const getHeadP = (blockchain) => promisify(blockchain.getHead.bind(blockchain))()