Skip to content

Commit

Permalink
State manager API changes
Browse files Browse the repository at this point in the history
  • Loading branch information
mattdean-digicatapult committed Oct 31, 2018
1 parent 4585cac commit d947314
Show file tree
Hide file tree
Showing 14 changed files with 259 additions and 255 deletions.
27 changes: 9 additions & 18 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const StateManager = require('./stateManager.js')
const Common = require('ethereumjs-common')
const Account = require('ethereumjs-account')
const AsyncEventEmitter = require('async-eventemitter')
const Trie = require('merkle-patricia-tree/secure.js')
const fakeBlockchain = require('./fakeBlockChain.js')
const BN = ethUtil.BN

Expand Down Expand Up @@ -52,11 +53,14 @@ function VM (opts = {}) {
if (opts.stateManager) {
this.stateManager = opts.stateManager
} else {
this.stateManager = new StateManager({
trie: opts.state,
blockchain: opts.blockchain,
common: this._common
})
var trie = opts.state || new Trie()
if (opts.activatePrecompiles) {
trie = new Trie()
for (var i = 1; i <= 8; i++) {
trie.put(new BN(i).toArrayLike(Buffer, 'be', 20), new Account().serialize())
}
}
this.stateManager = new StateManager({ trie, common: this._common })
}

this.blockchain = opts.blockchain || fakeBlockchain
Expand All @@ -74,12 +78,6 @@ function VM (opts = {}) {
this._precompiled['0000000000000000000000000000000000000007'] = num07
this._precompiled['0000000000000000000000000000000000000008'] = num08

if (this.opts.activatePrecompiles) {
for (var i = 1; i <= 7; i++) {
this.stateManager.trie.put(new BN(i).toArrayLike(Buffer, 'be', 20), new Account().serialize())
}
}

AsyncEventEmitter.call(this)
}

Expand All @@ -95,10 +93,3 @@ VM.prototype.runBlockchain = require('./runBlockchain.js')
VM.prototype.copy = function () {
return new VM({ stateManager: this.stateManager.copy(), blockchain: this.blockchain })
}

/**
* Loads precompiled contracts into the state
*/
VM.prototype.loadCompiled = function (address, src, cb) {
this.stateManager.trie.db.put(address, src, cb)
}
48 changes: 25 additions & 23 deletions lib/opFns.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,11 +219,11 @@ module.exports = {
}

// otherwise load account then return balance
stateManager.getAccountBalance(address, function (err, value) {
stateManager.getAccount(address, function (err, account) {
if (err) {
return cb(err)
}
cb(null, new BN(value))
cb(null, new BN(account.balance))
})
},
ORIGIN: function (runState) {
Expand Down Expand Up @@ -671,7 +671,6 @@ module.exports = {
})
},
STATICCALL: function (gasLimit, toAddress, inOffset, inLength, outOffset, outLength, runState, done) {
var stateManager = runState.stateManager
var value = new BN(0)
toAddress = addressToBuffer(toAddress)

Expand All @@ -692,22 +691,15 @@ module.exports = {
outLength: outLength
}

stateManager.accountIsEmpty(toAddress, function (err, empty) {
if (err) {
done(err)
return
}

try {
checkCallMemCost(runState, options, localOpts)
checkOutOfGas(runState, options)
} catch (e) {
done(e.error)
return
}
try {
checkCallMemCost(runState, options, localOpts)
checkOutOfGas(runState, options)
} catch (e) {
done(e.error)
return
}

makeCall(runState, options, localOpts, done)
})
makeCall(runState, options, localOpts, done)
},
RETURN: function (offset, length, runState) {
runState.returnValue = memLoad(runState, offset, length)
Expand Down Expand Up @@ -759,9 +751,17 @@ module.exports = {
runState.stopped = true

var newBalance = new BN(contract.balance).add(new BN(toAccount.balance))
async.series([
stateManager.putAccountBalance.bind(stateManager, selfdestructToAddress, newBalance),
stateManager.putAccountBalance.bind(stateManager, contractAddress, new BN(0))
async.waterfall([
cb => stateManager.getAccount(selfdestructToAddress, cb),
(account, cb) => {
account.balance = newBalance
stateManager.putAccount(selfdestructToAddress, account, cb)
},
cb => stateManager.getAccount(contractAddress, cb),
(account, cb) => {
account.balance = new BN(0)
stateManager.putAccount(contractAddress, account, cb)
}
], function (err) {
// The reason for this is to avoid sending an array of results
cb(err)
Expand Down Expand Up @@ -956,8 +956,10 @@ function makeCall (runState, callOptions, localOpts, cb) {
runState.contract.nonce = new BN(runState.contract.nonce).addn(1)
}

runState.stateManager.cache.put(runState.address, runState.contract)
runState._vm.runCall(callOptions, parseCallResults)
runState.stateManager.putAccount(runState.address, runState.contract, function (err) {
if (err) return cb(err)
runState._vm.runCall(callOptions, parseCallResults)
})
}

function parseCallResults (err, results) {
Expand Down
39 changes: 18 additions & 21 deletions lib/runBlock.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,7 @@ module.exports = function (opts, cb) {
var txResults = []
var result

if (opts.root) {
self.stateManager.trie.root = opts.root
}

self.stateManager.trie.checkpoint()
self.stateManager.checkpoint()

// run everything
async.series([
Expand Down Expand Up @@ -123,19 +119,19 @@ module.exports = function (opts, cb) {
}
}

// credit all block rewards
// credit all block rewards
function payOmmersAndMiner (cb) {
var ommers = block.uncleHeaders

// pay each ommer
// pay each ommer
async.series([
rewardOmmers,
rewardMiner
], cb)

function rewardOmmers (done) {
async.each(block.uncleHeaders, function (ommer, next) {
// calculate reward
// calculate reward
var minerReward = new BN(self._common.param('pow', 'minerReward'))
var heightDiff = new BN(block.header.number).sub(new BN(ommer.number))
var reward = ((new BN(8)).sub(heightDiff)).mul(minerReward.divn(8))
Expand All @@ -149,7 +145,7 @@ module.exports = function (opts, cb) {
}

function rewardMiner (done) {
// calculate nibling reward
// calculate nibling reward
var minerReward = new BN(self._common.param('pow', 'minerReward'))
var niblingReward = minerReward.divn(32)
var totalNiblingReward = niblingReward.muln(ommers.length)
Expand All @@ -160,7 +156,7 @@ module.exports = function (opts, cb) {
function rewardAccount (address, reward, done) {
self.stateManager.getAccount(address, function (err, account) {
if (err) return done(err)
// give miner the block reward
// give miner the block reward
account.balance = new BN(account.balance).add(reward)
self.stateManager.putAccount(address, account, done)
})
Expand All @@ -170,18 +166,21 @@ module.exports = function (opts, cb) {
// handle results or error from block run
function parseBlockResults (err) {
if (err) {
self.stateManager.trie.revert()
cb(err)
self.stateManager.revert(() => cb(err))
return
}

// credit all block rewards
if (generateStateRoot) {
block.header.stateRoot = self.stateManager.trie.root
}
self.stateManager.commit(function (err) {
if (err) return cb(err)

self.stateManager.getStateRoot(function (err, stateRoot) {
if (err) return cb(err)

// credit all block rewards
if (generateStateRoot) {
block.header.stateRoot = stateRoot
}

self.stateManager.trie.commit(function (err) {
self.stateManager.cache.flush(function () {
if (validateStateRoot) {
if (receiptTrie.root && receiptTrie.root.toString('hex') !== block.header.receiptTrie.toString('hex')) {
err = new Error((err || '') + 'invalid receiptTrie ')
Expand All @@ -192,13 +191,11 @@ module.exports = function (opts, cb) {
if (ethUtil.bufferToInt(block.header.gasUsed) !== Number(gasUsed)) {
err = new Error((err || '') + 'invalid gasUsed ')
}
if (self.stateManager.trie.root.toString('hex') !== block.header.stateRoot.toString('hex')) {
if (stateRoot.toString('hex') !== block.header.stateRoot.toString('hex')) {
err = new Error((err || '') + 'invalid block stateRoot ')
}
}

self.stateManager.cache.clear()

result = {
receipts: receipts,
results: txResults,
Expand Down
67 changes: 35 additions & 32 deletions lib/runBlockchain.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const async = require('async')
*/
module.exports = function (blockchain, cb) {
var self = this
var headBlock, parentState
var headBlock

// parse arguments
if (typeof blockchain === 'function') {
Expand All @@ -17,52 +17,55 @@ module.exports = function (blockchain, cb) {
}

blockchain = blockchain || self.blockchain
var stateManager = self.stateManager

// setup blockchain iterator
blockchain.iterator('vm', processBlock, cb)
function processBlock (block, reorg, cb) {
async.series([
getStartingState,
assertGenesisState,
processReorg,
runBlock
], cb)

// determine starting state for block run
function getStartingState (cb) {
// if we are just starting or if a chain re-org has happened
if (!headBlock || reorg) {
blockchain.getBlock(block.header.parentHash, function (err, parentBlock) {
parentState = parentBlock.header.stateRoot
// generate genesis state if we are at the genesis block
// we don't have the genesis state
if (!headBlock) {
return self.stateManager.generateCanonicalGenesis(cb)
} else {
cb(err)
}
], function (err) {
if (err) {
// remove invalid block
blockchain.delBlock(block.header.hash(), function () {
cb(err)
})
} else {
parentState = headBlock.header.stateRoot
// set as new head block
headBlock = block
cb()
}
})

// if we are just starting we need to make sure the genesis state is loaded into stateManager
function assertGenesisState (cb) {
if (headBlock) cb()
else {
stateManager.hasGenesisState(function (err, hasGenesis) {
if (err || hasGenesis) cb(err)
else stateManager.generateCanonicalGenesis(cb)
})
}
}

// if there's no headblock or a reorg has occurred we may need to shift the stateRoot
function processReorg (cb) {
if (headBlock && !reorg) cb()
else {
blockchain.getBlock(block.header.parentHash, function (err, parentBlock) {
if (err) return cb(err)
stateManager.setStateRoot(parentBlock.header.stateRoot, cb)
})
}
}

// run block, update head if valid
function runBlock (cb) {
self.runBlock({
block: block,
root: parentState
}, function (err, results) {
if (err) {
// remove invalid block
blockchain.delBlock(block.header.hash(), function () {
cb(err)
})
} else {
// set as new head block
headBlock = block
cb()
}
})
block: block
}, cb)
}
}
}
2 changes: 1 addition & 1 deletion lib/runCall.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ module.exports = function (opts, cb) {
}
var newBalance = new BN(account.balance).sub(txValue)
account.balance = newBalance
stateManager.putAccountBalance(ethUtil.toBuffer(caller), newBalance, cb)
stateManager.putAccount(ethUtil.toBuffer(caller), account, cb)
}

function addTxValue (cb) {
Expand Down
2 changes: 1 addition & 1 deletion lib/runCode.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ module.exports = function (opts, cb) {
depth: runState.depth,
address: runState.address,
account: runState.contract,
cache: runState.stateManager.cache,
stateManager: runState.stateManager,
memory: runState.memory
}
self.emit('step', eventObj, cb)
Expand Down
Loading

0 comments on commit d947314

Please sign in to comment.