Skip to content

Commit c346a04

Browse files
mattdean-digicatapultholgerd77
authored andcommitted
StateManager interface simplification
Simplifies the stateManager interface used by the VM by removing dependencies on the following methods: * getAccountBalance * putAccountBalance * copy
1 parent 7de7a5b commit c346a04

File tree

7 files changed

+158
-53
lines changed

7 files changed

+158
-53
lines changed

lib/opFns.js

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -219,11 +219,11 @@ module.exports = {
219219
}
220220

221221
// otherwise load account then return balance
222-
stateManager.getAccountBalance(address, function (err, value) {
222+
stateManager.getAccount(address, function (err, account) {
223223
if (err) {
224224
return cb(err)
225225
}
226-
cb(null, new BN(value))
226+
cb(null, new BN(account.balance))
227227
})
228228
},
229229
ORIGIN: function (runState) {
@@ -702,7 +702,6 @@ module.exports = {
702702
})
703703
},
704704
STATICCALL: function (gasLimit, toAddress, inOffset, inLength, outOffset, outLength, runState, done) {
705-
var stateManager = runState.stateManager
706705
var value = new BN(0)
707706
toAddress = addressToBuffer(toAddress)
708707

@@ -723,22 +722,15 @@ module.exports = {
723722
outLength: outLength
724723
}
725724

726-
stateManager.accountIsEmpty(toAddress, function (err, empty) {
727-
if (err) {
728-
done(err)
729-
return
730-
}
731-
732-
try {
733-
checkCallMemCost(runState, options, localOpts)
734-
checkOutOfGas(runState, options)
735-
} catch (e) {
736-
done(e.error)
737-
return
738-
}
725+
try {
726+
checkCallMemCost(runState, options, localOpts)
727+
checkOutOfGas(runState, options)
728+
} catch (e) {
729+
done(e.error)
730+
return
731+
}
739732

740-
makeCall(runState, options, localOpts, done)
741-
})
733+
makeCall(runState, options, localOpts, done)
742734
},
743735
RETURN: function (offset, length, runState) {
744736
runState.returnValue = memLoad(runState, offset, length)
@@ -790,9 +782,17 @@ module.exports = {
790782
runState.stopped = true
791783

792784
var newBalance = new BN(contract.balance).add(new BN(toAccount.balance))
793-
async.series([
794-
stateManager.putAccountBalance.bind(stateManager, selfdestructToAddress, newBalance),
795-
stateManager.putAccountBalance.bind(stateManager, contractAddress, new BN(0))
785+
async.waterfall([
786+
stateManager.getAccount.bind(stateManager, selfdestructToAddress),
787+
function (account, cb) {
788+
account.balance = newBalance
789+
stateManager.putAccount(selfdestructToAddress, account, cb)
790+
},
791+
stateManager.getAccount.bind(stateManager, contractAddress),
792+
function (account, cb) {
793+
account.balance = new BN(0)
794+
stateManager.putAccount(contractAddress, account, cb)
795+
}
796796
], function (err) {
797797
// The reason for this is to avoid sending an array of results
798798
cb(err)
@@ -970,6 +970,7 @@ function makeCall (runState, callOptions, localOpts, cb) {
970970
callOptions.block = runState.block
971971
callOptions.static = callOptions.static || false
972972
callOptions.selfdestruct = runState.selfdestruct
973+
callOptions.storageReader = runState.storageReader
973974

974975
// increment the runState.depth
975976
callOptions.depth = runState.depth + 1
@@ -1057,10 +1058,7 @@ function isCreateOpCode (opName) {
10571058

10581059
function getContractStorage (runState, address, key, cb) {
10591060
if (runState._common.gteHardfork('constantinople')) {
1060-
async.parallel({
1061-
original: function (cb) { runState.originalState.getContractStorage(address, key, cb) },
1062-
current: function (cb) { runState.stateManager.getContractStorage(address, key, cb) }
1063-
}, cb)
1061+
runState.storageReader.getContractStorage(address, key, cb)
10641062
} else {
10651063
runState.stateManager.getContractStorage(address, key, cb)
10661064
}

lib/runCall.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const async = require('async')
33
const ethUtil = require('ethereumjs-util')
44
const BN = ethUtil.BN
55
const exceptions = require('./exceptions.js')
6+
const StorageReader = require('./storageReader')
67

78
const ERROR = exceptions.ERROR
89

@@ -48,6 +49,7 @@ module.exports = function (opts, cb) {
4849
var delegatecall = opts.delegatecall || false
4950
var isStatic = opts.static || false
5051
var salt = opts.salt || null
52+
var storageReader = opts.storageReader || new StorageReader(stateManager)
5153

5254
txValue = new BN(txValue)
5355

@@ -149,7 +151,7 @@ module.exports = function (opts, cb) {
149151
}
150152
var newBalance = new BN(account.balance).sub(txValue)
151153
account.balance = newBalance
152-
stateManager.putAccountBalance(ethUtil.toBuffer(caller), newBalance, cb)
154+
stateManager.putAccount(ethUtil.toBuffer(caller), account, cb)
153155
}
154156

155157
function addTxValue (cb) {
@@ -205,7 +207,8 @@ module.exports = function (opts, cb) {
205207
block: block,
206208
depth: depth,
207209
selfdestruct: selfdestruct,
208-
static: isStatic
210+
static: isStatic,
211+
storageReader: storageReader
209212
}
210213

211214
// run Code through vm

lib/runCode.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const Block = require('ethereumjs-block')
1818
const lookupOpInfo = require('./opcodes.js')
1919
const opFns = require('./opFns.js')
2020
const exceptions = require('./exceptions.js')
21+
const StorageReader = require('./storageReader')
2122
const setImmediate = require('timers').setImmediate
2223
const BN = utils.BN
2324

@@ -64,7 +65,7 @@ module.exports = function (opts, cb) {
6465
var runState = {
6566
blockchain: self.blockchain,
6667
stateManager: stateManager,
67-
originalState: stateManager.copy(),
68+
storageReader: opts.storageReader || new StorageReader(stateManager),
6869
returnValue: false,
6970
stopped: false,
7071
vmError: false,

lib/runTx.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const BN = utils.BN
55
const Bloom = require('./bloom.js')
66
const Block = require('ethereumjs-block')
77
const Account = require('ethereumjs-account')
8+
const StorageReader = require('./storageReader')
89

910
/**
1011
* Process a transaction. Run the vm. Transfers eth. Checks balances.
@@ -39,6 +40,7 @@ module.exports = function (opts, cb) {
3940
var gasLimit
4041
var results
4142
var basefee
43+
var storageReader = new StorageReader(self.stateManager)
4244

4345
// tx is required
4446
if (!tx) {
@@ -141,7 +143,8 @@ module.exports = function (opts, cb) {
141143
to: tx.to,
142144
value: tx.value,
143145
data: tx.data,
144-
block: block
146+
block: block,
147+
storageReader: storageReader
145148
}
146149

147150
if (tx.to.toString('hex') === '') {
@@ -196,7 +199,7 @@ module.exports = function (opts, cb) {
196199
.add(new BN(fromAccount.balance))
197200
fromAccount.balance = finalFromBalance
198201

199-
self.stateManager.putAccountBalance(utils.toBuffer(tx.from), finalFromBalance, next)
202+
self.stateManager.putAccount(utils.toBuffer(tx.from), fromAccount, next)
200203
}
201204

202205
var minerAccount

lib/stateManager.js

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -52,29 +52,6 @@ proto.putAccount = function (address, account, cb) {
5252
cb()
5353
}
5454

55-
proto.getAccountBalance = function (address, cb) {
56-
var self = this
57-
self.getAccount(address, function (err, account) {
58-
if (err) {
59-
return cb(err)
60-
}
61-
cb(null, account.balance)
62-
})
63-
}
64-
65-
proto.putAccountBalance = function (address, balance, cb) {
66-
var self = this
67-
68-
self.getAccount(address, function (err, account) {
69-
if (err) {
70-
return cb(err)
71-
}
72-
73-
account.balance = balance
74-
self.putAccount(address, account, cb)
75-
})
76-
}
77-
7855
// sets the contract code on the account
7956
proto.putContractCode = function (address, value, cb) {
8057
var self = this
@@ -307,6 +284,7 @@ proto.accountIsEmpty = function (address, cb) {
307284
return cb(err)
308285
}
309286

287+
// should be replaced by account.isEmpty() once updated
310288
cb(null, account.nonce.toString('hex') === '' && account.balance.toString('hex') === '' && account.codeHash.toString('hex') === utils.KECCAK256_NULL_S)
311289
})
312290
}

lib/storageReader.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
module.exports = StorageReader
2+
3+
function StorageReader (stateManager) {
4+
this._stateManager = stateManager
5+
this._storageCache = new Map()
6+
}
7+
8+
const proto = StorageReader.prototype
9+
10+
proto.getContractStorage = function getContractStorage (address, key, cb) {
11+
const self = this
12+
const addressHex = address.toString('hex')
13+
const keyHex = key.toString('hex')
14+
15+
self._stateManager.getContractStorage(address, key, function (err, current) {
16+
if (err) return cb(err)
17+
18+
let map = null
19+
if (!self._storageCache.has(addressHex)) {
20+
map = new Map()
21+
self._storageCache.set(addressHex, map)
22+
} else {
23+
map = self._storageCache.get(addressHex)
24+
}
25+
26+
let original = null
27+
28+
if (map.has(keyHex)) {
29+
original = map.get(keyHex)
30+
} else {
31+
map.set(keyHex, current)
32+
original = current
33+
}
34+
35+
cb(null, {
36+
original,
37+
current
38+
})
39+
})
40+
}

tests/api/storageReader.js

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
const { promisify } = require('util')
2+
const tape = require('tape')
3+
const StorageReader = require('../../lib/storageReader')
4+
5+
const mkStateManagerMock = () => {
6+
let i = 0
7+
return {
8+
getContractStorage: function (address, key, cb) {
9+
cb(null, i++)
10+
}
11+
}
12+
}
13+
14+
tape('StateManager', (t) => {
15+
t.test('should get value from stateManager', async (st) => {
16+
const storageReader = new StorageReader(mkStateManagerMock())
17+
const getContractStorage = promisify((...args) => storageReader.getContractStorage(...args))
18+
const {
19+
original,
20+
current
21+
} = await getContractStorage(Buffer.from([1]), Buffer.from([1, 1]))
22+
23+
st.equal(original, 0)
24+
st.equal(current, 0)
25+
st.end()
26+
})
27+
28+
t.test('should retrieve original from cache', async (st) => {
29+
const storageReader = new StorageReader(mkStateManagerMock())
30+
const getContractStorage = promisify((...args) => storageReader.getContractStorage(...args))
31+
await getContractStorage(Buffer.from([1]), Buffer.from([1, 1]))
32+
const {
33+
original,
34+
current
35+
} = await getContractStorage(Buffer.from([1]), Buffer.from([1, 1]))
36+
37+
st.equal(original, 0)
38+
st.equal(current, 1)
39+
st.end()
40+
})
41+
42+
t.test('should cache keys separately', async (st) => {
43+
const storageReader = new StorageReader(mkStateManagerMock())
44+
const getContractStorage = promisify((...args) => storageReader.getContractStorage(...args))
45+
await getContractStorage(Buffer.from([1]), Buffer.from([1, 1]))
46+
const {
47+
original,
48+
current
49+
} = await getContractStorage(Buffer.from([1]), Buffer.from([1, 2]))
50+
51+
st.equal(original, 1)
52+
st.equal(current, 1)
53+
st.end()
54+
})
55+
56+
t.test('should cache addresses separately', async (st) => {
57+
const storageReader = new StorageReader(mkStateManagerMock())
58+
const getContractStorage = promisify((...args) => storageReader.getContractStorage(...args))
59+
await getContractStorage(Buffer.from([1]), Buffer.from([1, 1]))
60+
const {
61+
original,
62+
current
63+
} = await getContractStorage(Buffer.from([2]), Buffer.from([1, 1]))
64+
65+
st.equal(original, 1)
66+
st.equal(current, 1)
67+
st.end()
68+
})
69+
70+
t.test('should forward errors from stateManager.getContractStorage', async (st) => {
71+
const storageReader = new StorageReader({
72+
getContractStorage: (address, key, cb) => cb(new Error('test'))
73+
})
74+
const getContractStorage = promisify((...args) => storageReader.getContractStorage(...args))
75+
76+
await getContractStorage(Buffer.from([2]), Buffer.from([1, 1]))
77+
.then(() => t.fail('should have returned error'))
78+
.catch((e) => t.equal(e.message, 'test'))
79+
80+
st.end()
81+
})
82+
})

0 commit comments

Comments
 (0)