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))()

猜你喜欢

转载自www.cnblogs.com/wanghui-garcia/p/10087420.html
vm