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 3, 2018
1 parent 2885d7a commit 15fce6f
Show file tree
Hide file tree
Showing 11 changed files with 374 additions and 461 deletions.
20 changes: 7 additions & 13 deletions lib/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ Cache.prototype.get = function (key) {
var account = this.lookup(key)
if (!account) {
account = new Account()
account.exists = false
}
return account
}
Expand All @@ -32,7 +31,6 @@ Cache.prototype.lookup = function (key) {
var it = this._cache.find(key)
if (it.node) {
var account = new Account(it.value.val)
account.exists = it.value.exists
return account
}
}
Expand All @@ -42,9 +40,7 @@ Cache.prototype._lookupAccount = function (address, cb) {
self._trie.get(address, function (err, raw) {
if (err) return cb(err)
var account = new Account(raw)
var exists = !!raw
account.exists = exists
cb(null, account, exists)
cb(null, account)
})
}

Expand All @@ -54,9 +50,9 @@ Cache.prototype.getOrLoad = function (key, cb) {
if (account) {
cb(null, account)
} else {
self._lookupAccount(key, function (err, account, exists) {
self._lookupAccount(key, function (err, account) {
if (err) return cb(err)
self._update(key, account, false, exists)
self._update(key, account, false)
cb(null, account)
})
}
Expand All @@ -74,7 +70,7 @@ Cache.prototype.warm = function (addresses, cb) {
var address = Buffer.from(addressHex, 'hex')
self._lookupAccount(address, function (err, account) {
if (err) return done(err)
self._update(address, account, false, account.exists)
self._update(address, account, false)
done()
})
}, cb)
Expand Down Expand Up @@ -133,20 +129,18 @@ Cache.prototype.del = function (key) {
this._cache = this._cache.remove(key)
}

Cache.prototype._update = function (key, val, modified, exists) {
Cache.prototype._update = function (key, val, modified) {
key = key.toString('hex')
var it = this._cache.find(key)
if (it.node) {
this._cache = it.update({
val: val,
modified: modified,
exists: true
modified: modified
})
} else {
this._cache = this._cache.insert(key, {
val: val,
modified: modified,
exists: exists
modified: modified
})
}
}
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)
}
114 changes: 52 additions & 62 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 @@ -549,44 +552,37 @@ module.exports = {
subGas(runState, new BN(runState._common.param('gasPrices', 'callValueTransfer')))
}

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

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

if (!exists || empty) {
if (!value.isZero()) {
try {
subGas(runState, new BN(runState._common.param('gasPrices', 'callNewAccount')))
} catch (e) {
done(e.error)
return
}
if (empty) {
if (!value.isZero()) {
try {
subGas(runState, new BN(runState._common.param('gasPrices', 'callNewAccount')))
} catch (e) {
done(e.error)
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
}

if (!value.isZero()) {
runState.gasLeft.iaddn(runState._common.param('gasPrices', 'callStipend'))
options.gasLimit.iaddn(runState._common.param('gasPrices', 'callStipend'))
}
if (!value.isZero()) {
runState.gasLeft.iaddn(runState._common.param('gasPrices', 'callStipend'))
options.gasLimit.iaddn(runState._common.param('gasPrices', 'callStipend'))
}

makeCall(runState, options, localOpts, done)
})
makeCall(runState, options, localOpts, done)
})
},
CALLCODE: function (gas, toAddress, value, inOffset, inLength, outOffset, outLength, runState, done) {
Expand Down Expand Up @@ -684,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 @@ -705,29 +700,15 @@ module.exports = {
outLength: outLength
}

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

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 @@ -761,7 +742,7 @@ module.exports = {
}

if ((new BN(contract.balance)).gtn(0)) {
if (!toAccount.exists || empty) {
if (empty) {
try {
subGas(runState, new BN(runState._common.param('gasPrices', 'callNewAccount')))
} catch (e) {
Expand All @@ -779,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 @@ -954,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 @@ -974,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
Loading

0 comments on commit 15fce6f

Please sign in to comment.