Skip to content

Commit 3619d7b

Browse files
authored
vm: reintroduce modifyAccountFields (#1763)
* add modifyAccountFields method and tests * use new method in eei * update usage in various tests
1 parent ff2ffea commit 3619d7b

File tree

8 files changed

+110
-27
lines changed

8 files changed

+110
-27
lines changed

packages/client/test/miner/miner.spec.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,8 @@ const B = {
3030
}
3131

3232
const setBalance = async (stateManager: StateManager, address: Address, balance: BN) => {
33-
// this fn can be replaced with modifyAccountFields() when #1369 is available
3433
await stateManager.checkpoint()
35-
await stateManager.putAccount(address, new Account(new BN(0), balance))
34+
await stateManager.modifyAccountFields(address, { balance })
3635
await stateManager.commit()
3736
}
3837

packages/client/test/miner/pendingBlock.spec.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,8 @@ const B = {
2727
}
2828

2929
const setBalance = async (stateManager: StateManager, address: Address, balance: BN) => {
30-
// this fn can be replaced with modifyAccountFields() when #1369 is available
3130
await stateManager.checkpoint()
32-
await stateManager.putAccount(address, new Account(new BN(0), balance))
31+
await stateManager.modifyAccountFields(address, { balance })
3332
await stateManager.commit()
3433
}
3534

packages/vm/src/evm/eei.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -425,9 +425,9 @@ export default class EEI {
425425
await this._state.putAccount(toAddress, toAccount)
426426

427427
// Subtract from contract balance
428-
const account = await this._state.getAccount(this._env.address)
429-
account.balance = new BN(0)
430-
await this._state.putAccount(this._env.address, account)
428+
await this._state.modifyAccountFields(this._env.address, {
429+
balance: new BN(0),
430+
})
431431

432432
trap(ERROR.STOP)
433433
}

packages/vm/src/state/interface.ts

+3
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@ export interface StorageDump {
99
[key: string]: string
1010
}
1111

12+
export type AccountFields = Partial<Pick<Account, 'nonce' | 'balance' | 'stateRoot' | 'codeHash'>>
13+
1214
export interface StateManager {
1315
copy(): StateManager
1416
getAccount(address: Address): Promise<Account>
1517
putAccount(address: Address, account: Account): Promise<void>
1618
deleteAccount(address: Address): Promise<void>
1719
touchAccount(address: Address): void
20+
modifyAccountFields(address: Address, accountFields: AccountFields): Promise<void>
1821
putContractCode(address: Address, value: Buffer): Promise<void>
1922
getContractCode(address: Address): Promise<Buffer>
2023
getContractStorage(address: Address, key: Buffer): Promise<Buffer>

packages/vm/src/state/stateManager.ts

+18-4
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
setLengthLeft,
1616
} from 'ethereumjs-util'
1717
import Common from '@ethereumjs/common'
18-
import { StateManager, StorageDump } from './interface'
18+
import { StateManager, StorageDump, AccountFields } from './interface'
1919
import Cache, { getCb, putCb } from './cache'
2020
import { BaseStateManager } from './'
2121
import { short } from '../evm/opcodes'
@@ -132,12 +132,10 @@ export default class DefaultStateManager extends BaseStateManager implements Sta
132132
const key = Buffer.concat([CODEHASH_PREFIX, codeHash])
133133
await this._trie.db.put(key, value)
134134

135-
const account = await this.getAccount(address)
136135
if (this.DEBUG) {
137136
this._debug(`Update codeHash (-> ${short(codeHash)}) for account ${address}`)
138137
}
139-
account.codeHash = codeHash
140-
await this.putAccount(address, account)
138+
await this.modifyAccountFields(address, { codeHash })
141139
}
142140

143141
/**
@@ -513,4 +511,20 @@ export default class DefaultStateManager extends BaseStateManager implements Sta
513511
}
514512
return false
515513
}
514+
515+
/**
516+
* Gets the account associated with `address`, modifies the given account
517+
* fields, then saves the account into state. Account fields can include
518+
* `nonce`, `balance`, `stateRoot`, and `codeHash`.
519+
* @param address - Address of the account to modify
520+
* @param accountFields - Object containing account fields and values to modify
521+
*/
522+
async modifyAccountFields(address: Address, accountFields: AccountFields): Promise<void> {
523+
const account = await this.getAccount(address)
524+
account.nonce = accountFields.nonce ?? account.nonce
525+
account.balance = accountFields.balance ?? account.balance
526+
account.stateRoot = accountFields.stateRoot ?? account.stateRoot
527+
account.codeHash = accountFields.codeHash ?? account.codeHash
528+
await this.putAccount(address, account)
529+
}
516530
}

packages/vm/tests/api/EIPs/eip-1559-FeeMarket.spec.ts

+5-13
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,8 @@ tape('EIP1559 tests', (t) => {
109109
{ common }
110110
)
111111
const block2 = makeBlock(GWEI, tx2, 1)
112-
account = await vm.stateManager.getAccount(sender)
113-
account.balance = balance
114-
await vm.stateManager.putAccount(sender, account)
115-
miner.balance = new BN(0)
116-
await vm.stateManager.putAccount(coinbase, miner)
112+
await vm.stateManager.modifyAccountFields(sender, { balance })
113+
await vm.stateManager.modifyAccountFields(coinbase, { balance: new BN(0) })
117114
const results2 = await vm.runTx({
118115
tx: block2.transactions[0],
119116
block: block2,
@@ -140,11 +137,8 @@ tape('EIP1559 tests', (t) => {
140137
{ common }
141138
)
142139
const block3 = makeBlock(GWEI, tx3, 0)
143-
account = await vm.stateManager.getAccount(sender)
144-
account.balance = balance
145-
await vm.stateManager.putAccount(sender, account)
146-
miner.balance = new BN(0)
147-
await vm.stateManager.putAccount(coinbase, miner)
140+
await vm.stateManager.modifyAccountFields(sender, { balance })
141+
await vm.stateManager.modifyAccountFields(coinbase, { balance: new BN(0) })
148142
const results3 = await vm.runTx({
149143
tx: block3.transactions[0],
150144
block: block3,
@@ -180,10 +174,8 @@ tape('EIP1559 tests', (t) => {
180174
)
181175
const block = makeBlock(GWEI, tx, 2)
182176
const vm = new VM({ common })
183-
const account = await vm.stateManager.getAccount(sender)
184177
const balance = GWEI.muln(210000).muln(10)
185-
account.balance = balance
186-
await vm.stateManager.putAccount(sender, account)
178+
await vm.stateManager.modifyAccountFields(sender, { balance })
187179

188180
/**
189181
* GASPRICE

packages/vm/tests/api/EIPs/eip-3198-BaseFee.spec.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,7 @@ tape('EIP3198 tests', (t) => {
7474
)
7575
const block = makeBlock(fee, tx, 2)
7676
const vm = new VM({ common })
77-
const account = await vm.stateManager.getAccount(sender)
78-
account.balance = ETHER
79-
await vm.stateManager.putAccount(sender, account)
77+
await vm.stateManager.modifyAccountFields(sender, { balance: ETHER })
8078

8179
// Track stack
8280

packages/vm/tests/api/state/stateManager.spec.ts

+78
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,84 @@ tape('StateManager', (t) => {
168168
st.end()
169169
}
170170
)
171+
172+
t.test('should modify account fields correctly', async (st) => {
173+
const stateManager = new DefaultStateManager()
174+
const account = createAccount()
175+
const address = new Address(Buffer.from('a94f5374fce5edbc8e2a8697c15331677e6ebf0b', 'hex'))
176+
await stateManager.putAccount(address, account)
177+
178+
await stateManager.modifyAccountFields(address, { balance: new BN(1234) })
179+
180+
const res1 = await stateManager.getAccount(address)
181+
182+
st.equal(res1.balance.toString('hex'), '4d2')
183+
184+
await stateManager.modifyAccountFields(address, { nonce: new BN(1) })
185+
186+
const res2 = await stateManager.getAccount(address)
187+
188+
st.equal(res2.nonce.toNumber(), 1)
189+
190+
await stateManager.modifyAccountFields(address, {
191+
codeHash: Buffer.from(
192+
'd748bf26ab37599c944babfdbeecf6690801bd61bf2670efb0a34adfc6dca10b',
193+
'hex'
194+
),
195+
stateRoot: Buffer.from(
196+
'cafd881ab193703b83816c49ff6c2bf6ba6f464a1be560c42106128c8dbc35e7',
197+
'hex'
198+
),
199+
})
200+
201+
const res3 = await stateManager.getAccount(address)
202+
203+
st.equal(
204+
res3.codeHash.toString('hex'),
205+
'd748bf26ab37599c944babfdbeecf6690801bd61bf2670efb0a34adfc6dca10b'
206+
)
207+
st.equal(
208+
res3.stateRoot.toString('hex'),
209+
'cafd881ab193703b83816c49ff6c2bf6ba6f464a1be560c42106128c8dbc35e7'
210+
)
211+
212+
st.end()
213+
})
214+
215+
t.test(
216+
'should modify account fields correctly on previously non-existent account',
217+
async (st) => {
218+
const stateManager = new DefaultStateManager()
219+
const address = new Address(Buffer.from('a94f5374fce5edbc8e2a8697c15331677e6ebf0b', 'hex'))
220+
221+
await stateManager.modifyAccountFields(address, { balance: new BN(1234) })
222+
const res1 = await stateManager.getAccount(address)
223+
st.equal(res1.balance.toString('hex'), '4d2')
224+
225+
await stateManager.modifyAccountFields(address, { nonce: new BN(1) })
226+
const res2 = await stateManager.getAccount(address)
227+
st.equal(res2.nonce.toNumber(), 1)
228+
229+
const newCodeHash = Buffer.from(
230+
'd748bf26ab37599c944babfdbeecf6690801bd61bf2670efb0a34adfc6dca10b',
231+
'hex'
232+
)
233+
const newStateRoot = Buffer.from(
234+
'cafd881ab193703b83816c49ff6c2bf6ba6f464a1be560c42106128c8dbc35e7',
235+
'hex'
236+
)
237+
await stateManager.modifyAccountFields(address, {
238+
codeHash: newCodeHash,
239+
stateRoot: newStateRoot,
240+
})
241+
242+
const res3 = await stateManager.getAccount(address)
243+
st.ok(res3.codeHash.equals(newCodeHash))
244+
st.ok(res3.stateRoot.equals(newStateRoot))
245+
st.end()
246+
}
247+
)
248+
171249
t.test(
172250
'should generate the genesis state root correctly for mainnet from ethereum/tests data',
173251
async (st) => {

0 commit comments

Comments
 (0)