Skip to content

Commit

Permalink
State manager API changes
Browse files Browse the repository at this point in the history
Squashed changes as discussed in #309
  • Loading branch information
mattdean-digicatapult committed Aug 6, 2018
1 parent d146c76 commit ab26c24
Show file tree
Hide file tree
Showing 10 changed files with 328 additions and 374 deletions.
31 changes: 9 additions & 22 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,14 +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)
}

VM.prototype.populateCache = function (addresses, cb) {
this.stateManager.warmCache(addresses, cb)
}
56 changes: 30 additions & 26 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 @@ -394,8 +394,11 @@ module.exports = {

stateManager.putContractStorage(address, key, value, function (err) {
if (err) return cb(err)
runState.contract = stateManager.cache.get(address)
cb(null)
stateManager.getAccount(address, function (err, account) {
if (err) return cb(err)
runState.contract = account
cb(null)
})
})
})
},
Expand Down Expand Up @@ -677,7 +680,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 @@ -698,22 +700,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 @@ -765,9 +760,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 @@ -940,7 +943,6 @@ function makeCall (runState, callOptions, localOpts, cb) {
callOptions.origin = runState.origin
callOptions.gasPrice = runState.gasPrice
callOptions.block = runState.block
callOptions.populateCache = false
callOptions.static = callOptions.static || false
callOptions.selfdestruct = runState.selfdestruct

Expand All @@ -960,8 +962,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
141 changes: 63 additions & 78 deletions lib/runBlock.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,17 @@ module.exports = function (opts, cb) {
const receiptTrie = new Trie()
// the total amount of gas used processing this block
var gasUsed = new BN(0)
// miner account
var minerAccount
var receipts = []
var txResults = []
var result

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

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

// run everything
async.series([
beforeBlock,
populateCache,
processTransactions
processTransactions,
payOmmersAndMiner
], parseBlockResults)

function beforeBlock (cb) {
Expand All @@ -51,22 +45,6 @@ module.exports = function (opts, cb) {
self.emit('afterBlock', result, cb)
}

// populates the cache with accounts that we know we will need
function populateCache (cb) {
var accounts = new Set()
accounts.add(block.header.coinbase.toString('hex'))
block.transactions.forEach(function (tx) {
accounts.add(tx.getSenderAddress().toString('hex'))
accounts.add(tx.to.toString('hex'))
})

block.uncleHeaders.forEach(function (uh) {
accounts.add(uh.coinbase.toString('hex'))
})

self.populateCache(accounts, cb)
}

/**
* Processes all of the transaction in the block
* @method processTransaction
Expand All @@ -87,8 +65,7 @@ module.exports = function (opts, cb) {
// run the tx through the VM
self.runTx({
tx: tx,
block: block,
populateCache: false
block: block
}, parseTxResult)

function parseTxResult (err, result) {
Expand Down Expand Up @@ -126,31 +103,76 @@ module.exports = function (opts, cb) {
}

receipts.push(txReceipt)
receiptTrie.put(rlp.encode(validReceiptCount), rlp.encode(rawTxReceipt))
validReceiptCount++
cb()
receiptTrie.put(rlp.encode(validReceiptCount), rlp.encode(rawTxReceipt), function () {
validReceiptCount++
cb()
})
}
}
}

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

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

function rewardOmmers (done) {
async.each(block.uncleHeaders, function (ommer, next) {
// 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))

if (reward.ltn(0)) {
reward = new BN(0)
}

rewardAccount(ommer.coinbase, reward, next)
}, done)
}

function rewardMiner (done) {
// calculate nibling reward
var minerReward = new BN(self._common.param('pow', 'minerReward'))
var niblingReward = minerReward.divn(32)
var totalNiblingReward = niblingReward.muln(ommers.length)
var reward = minerReward.add(totalNiblingReward)
rewardAccount(block.header.coinbase, reward, done)
}

function rewardAccount (address, reward, done) {
self.stateManager.getAccount(address, function (err, account) {
if (err) return done(err)
// give miner the block reward
account.balance = new BN(account.balance).add(reward)
self.stateManager.putAccount(address, account, done)
})
}
}

// 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
payOmmersAndMiner()
self.stateManager.commit(function (err) {
if (err) return cb(err)

// credit all block rewards
if (generateStateRoot) {
block.header.stateRoot = self.stateManager.trie.root
}
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 @@ -161,13 +183,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 All @@ -178,39 +198,4 @@ module.exports = function (opts, cb) {
})
})
}

// credit all block rewards
function payOmmersAndMiner () {
var ommers = block.uncleHeaders
// pay each ommer
ommers.forEach(rewardOmmer)

// calculate nibling reward
var minerReward = new BN(self._common.param('pow', 'minerReward'))
var niblingReward = minerReward.divn(32)
var totalNiblingReward = niblingReward.muln(ommers.length)
minerAccount = self.stateManager.cache.get(block.header.coinbase)
// give miner the block reward
minerAccount.balance = new BN(minerAccount.balance)
.add(minerReward)
.add(totalNiblingReward)
self.stateManager.cache.put(block.header.coinbase, minerAccount)
}

// credit ommer
function rewardOmmer (ommer) {
// 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))

if (reward.ltn(0)) {
reward = new BN(0)
}

// credit miners account
var ommerAccount = self.stateManager.cache.get(ommer.coinbase)
ommerAccount.balance = reward.add(new BN(ommerAccount.balance))
self.stateManager.cache.put(ommer.coinbase, ommerAccount)
}
}
Loading

0 comments on commit ab26c24

Please sign in to comment.