根据代码发现还要了解的模块有:

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(), '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(), '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(, ) === '0x') {
// fix for BlockchainTests/GeneralStateTests/stRandom/*
testData.lastblockhash = testData.lastblockhash.substr()//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(), '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(), 'hex'))
key = utils.setLength(Buffer.from(key.slice(), 'hex'), ) 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)
}, ) 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(), 'hex'), )).toString('hex')] = acctData.storage[key]
} if (storageKeys.length > ) {//即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'), ) 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(), 'hex'))
const block = new Block(Buffer.from(testData.blocks[].rlp.slice(), 'hex')) await putGenesisP(vm.blockchain, genesis) //将初始区块添加进区块链中
st.equal(vm.blockchain.meta.genesis.toString('hex'), testData.genesisBlockHeader.hash.slice()) await putBlockP(vm.blockchain, block)
const head = await getHeadP(vm.blockchain)
st.equal(
head.hash().toString('hex'),
testData.blocks[].blockHeader.hash.slice()
) 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(), 'hex'))
const block = new Block(Buffer.from(testData.blocks[].rlp.slice(), 'hex')) await putGenesisP(vm.blockchain, genesis)
st.equal(vm.blockchain.meta.genesis.toString('hex'), testData.genesisBlockHeader.hash.slice()) await putBlockP(vm.blockchain, block)
const head = await getHeadP(vm.blockchain)
st.equal(
head.hash().toString('hex'),
testData.blocks[].blockHeader.hash.slice()
) 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))()

最新文章

  1. ABP(现代ASP.NET样板开发框架)系列之9、ABP设置管理
  2. 从零开始学 Java - 我放弃了 .NET ?
  3. CentOS 6.3下配置LVM(逻辑卷管理)
  4. Java 重写(Overriding)和重载(Overloading)
  5. TabHost的用法(转)
  6. 检测iOS的APP性能的一些方法
  7. OD: DEP - Ret2Libc via VirtualProtect() & VirtualAlloc()
  8. 《实验数据的结构化程序设计》 2.4.4Calendar个人意见,寻求指引
  9. jQuery选择器的分类之过滤选择器
  10. JSONP不支持循环调用
  11. 入坑IT都快十年了
  12. node.js进阶话题
  13. Jmeter 相关资源
  14. ArcGis安装失败提示“需要Microsoft .NET Framework 3.5 sp1或等效环境”的解决方法
  15. 10_java基础——构造器里调用构造器
  16. C#调试心经
  17. learning ddr RTT
  18. 发送短信验证码及调用短信接口与C# 后台 post 发送
  19. CentOS日志列表
  20. synchronized修饰方法和对象的区别

热门文章

  1. msql查询指定日期
  2. 【SSH网上商城项目实战01】整合Struts2、Hibernate4.3和Spring4.2
  3. eclipse 查看源码 source not found
  4. spss C# 二次开发 学习笔记(六)——Spss统计结果的输出
  5. javascrip 实现简单的计算器功能
  6. drupal7 STMP邮件模块配置
  7. float浮动
  8. 关闭TCP中135、139、445、593、1025 等端口的操作方法 (转)(记录下)
  9. 常规操作系统Windows系统淋雨系统Unix系统netware等系统介绍分析
  10. Java笔记-添加自定义公共类库