Skip to content

Commit 214f56e

Browse files
authored
Merge pull request #814 from ethereumjs/cleanup-tangerine-whistle
[VM] cleanup some changes made in the TangerineWhistle PR
2 parents 51f8af0 + 05d28ab commit 214f56e

File tree

6 files changed

+84
-21
lines changed

6 files changed

+84
-21
lines changed

packages/vm/lib/evm/eei.ts

+8
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,14 @@ export default class EEI {
583583
return this._state.accountIsEmpty(address)
584584
}
585585

586+
/**
587+
* Returns true if account exists in the state trie (it can be empty). Returns false if the account is `null`.
588+
* @param address - Address of account
589+
*/
590+
async accountExists(address: Buffer): Promise<boolean> {
591+
return this._state.accountExists(address)
592+
}
593+
586594
private _getReturnCode(results: EVMResult) {
587595
// This preserves the previous logic, but seems to contradict the EEI spec
588596
// https://github.com/ewasm/design/blob/38eeded28765f3e193e12881ea72a6ab807a3371/eth_interface.md

packages/vm/lib/evm/opFns.ts

+4-7
Original file line numberDiff line numberDiff line change
@@ -676,16 +676,13 @@ export const handlers: { [k: string]: OpHandler } = {
676676
if (runState._common.gteHardfork('spuriousDragon')) {
677677
// We are at or after Spurious Dragon
678678
// Call new account gas: account is DEAD and we transfer nonzero value
679-
if ((await runState.stateManager.accountIsEmpty(toAddressBuf)) && !value.isZero()) {
679+
if ((await runState.eei.isAccountEmpty(toAddressBuf)) && !value.isZero()) {
680680
runState.eei.useGas(new BN(runState._common.param('gasPrices', 'callNewAccount')))
681681
}
682-
} else if (!(await runState.stateManager.accountExists(toAddressBuf))) {
683-
// We are before Spurious Dragon
682+
} else if (!(await runState.eei.accountExists(toAddressBuf))) {
683+
// We are before Spurious Dragon and the account does not exist.
684684
// Call new account gas: account does not exist (it is not in the state trie, not even as an "empty" account)
685-
const accountDoesNotExist = !(await runState.stateManager.accountExists(toAddressBuf))
686-
if (accountDoesNotExist) {
687-
runState.eei.useGas(new BN(runState._common.param('gasPrices', 'callNewAccount')))
688-
}
685+
runState.eei.useGas(new BN(runState._common.param('gasPrices', 'callNewAccount')))
689686
}
690687

691688
if (!value.isZero()) {

packages/vm/lib/state/cache.ts

+19-10
Original file line numberDiff line numberDiff line change
@@ -51,30 +51,39 @@ export default class Cache {
5151
}
5252
}
5353

54+
/**
55+
* Returns true if the key was deleted and thus existed in the cache earlier
56+
* @param key - trie key to lookup
57+
*/
58+
keyIsDeleted(key: Buffer): boolean {
59+
const keyStr = key.toString('hex')
60+
const it = this._cache.find(keyStr)
61+
if (it.node) {
62+
return it.value.deleted
63+
}
64+
return false
65+
}
66+
5467
/**
5568
* Looks up address in underlying trie.
5669
* @param address - Address of account
57-
* @param create - Create emtpy account if non-existent
5870
*/
59-
async _lookupAccount(address: Buffer, create: boolean = true): Promise<Account | undefined> {
71+
async _lookupAccount(address: Buffer): Promise<Account> {
6072
const raw = await this._trie.get(address)
61-
if (raw || create) {
62-
const account = new Account(raw)
63-
return account
64-
}
73+
const account = new Account(raw)
74+
return account
6575
}
6676

6777
/**
6878
* Looks up address in cache, if not found, looks it up
6979
* in the underlying trie.
7080
* @param key - Address of account
71-
* @param create - Create emtpy account if non-existent
7281
*/
73-
async getOrLoad(key: Buffer, create: boolean = true): Promise<Account | undefined> {
82+
async getOrLoad(key: Buffer): Promise<Account> {
7483
let account = this.lookup(key)
7584

7685
if (!account) {
77-
account = await this._lookupAccount(key, create)
86+
account = await this._lookupAccount(key)
7887
if (account) {
7988
this._update(key, account as Account, false, false)
8089
}
@@ -114,7 +123,7 @@ export default class Cache {
114123
it.next()
115124
} else if (it.value && it.value.deleted) {
116125
it.value.modified = false
117-
it.value.deleted = false
126+
it.value.deleted = true
118127
it.value.val = new Account().serialize()
119128
await this._trie.del(Buffer.from(it.key, 'hex'))
120129
next = it.hasNext

packages/vm/lib/state/interface.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export interface StorageDump {
1010
export interface StateManager {
1111
copy(): StateManager
1212
getAccount(address: Buffer): Promise<Account>
13-
putAccount(address: Buffer, account: Account | null): Promise<void>
13+
putAccount(address: Buffer, account: Account): Promise<void>
1414
deleteAccount(address: Buffer): Promise<void>
1515
touchAccount(address: Buffer): void
1616
putContractCode(address: Buffer, value: Buffer): Promise<void>

packages/vm/lib/state/stateManager.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export default class DefaultStateManager implements StateManager {
7171
* @param address - Address of the `account` to get
7272
*/
7373
async getAccount(address: Buffer): Promise<Account> {
74-
const account = (await this._cache.getOrLoad(address)) as Account
74+
const account = await this._cache.getOrLoad(address)
7575
return account
7676
}
7777

@@ -89,6 +89,11 @@ export default class DefaultStateManager implements StateManager {
8989
this.touchAccount(address)
9090
}
9191

92+
/**
93+
* Deletes an [`@ethereumjs/account`](https://github.com/ethereumjs/ethereumjs-vm/tree/master/packages/account)
94+
* from state under the provided `address`. The account will also be removed from the state trie.
95+
* @param address - Address of the account which should be deleted
96+
*/
9297
async deleteAccount(address: Buffer) {
9398
this._cache.del(address)
9499
this.touchAccount(address)
@@ -115,7 +120,7 @@ export default class DefaultStateManager implements StateManager {
115120
const codeHash = keccak256(value)
116121

117122
if (codeHash.equals(KECCAK256_NULL)) {
118-
//return
123+
return
119124
}
120125

121126
const account = await this.getAccount(address)
@@ -489,7 +494,7 @@ export default class DefaultStateManager implements StateManager {
489494
*/
490495
async accountExists(address: Buffer): Promise<boolean> {
491496
const account = await this._cache.lookup(address)
492-
if (account) {
497+
if (account && !this._cache.keyIsDeleted(address)) {
493498
return true
494499
}
495500
if (await this._cache._trie.get(address)) {

packages/vm/tests/api/evm/eei.js

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
const tape = require('tape')
2+
const { zeroAddress } = require('ethereumjs-util')
3+
const EEI = require('../../../dist/evm/eei').default
4+
const StateManager = require('../../../dist/state/stateManager').default
5+
const Account = require('@ethereumjs/account').default
6+
7+
const ZeroAddress = Buffer.from("0000000000000000000000000000000000000000", 'hex')
8+
9+
tape('EEI', t => {
10+
11+
t.test('should return false on non-existing accounts', async st => {
12+
const eei = new EEI(undefined, new StateManager()) // create a dummy EEI (no VM, no EVM, etc.)
13+
st.notOk(await eei.accountExists(ZeroAddress))
14+
st.ok(await eei.isAccountEmpty(ZeroAddress))
15+
st.end()
16+
})
17+
18+
19+
t.test('should return false on non-existing accounts which once existed in state but are now gone', async st => {
20+
const eei = new EEI(undefined, new StateManager()) // create a dummy EEI (no VM, no EVM, etc.)
21+
// create empty account
22+
await eei._state.putAccount(ZeroAddress, new Account())
23+
st.ok(await eei.accountExists(ZeroAddress))
24+
st.ok(await eei.isAccountEmpty(ZeroAddress))
25+
// now put a non-empty account
26+
await eei._state.putAccount(ZeroAddress, new Account({nonce: '0x01', balance: '0x01'}))
27+
st.ok(await eei.accountExists(ZeroAddress))
28+
st.notOk(await eei.isAccountEmpty(ZeroAddress))
29+
st.end()
30+
})
31+
32+
t.test('should return true on existing accounts', async st => {
33+
const eei = new EEI(undefined, new StateManager()) // create a dummy EEI (no VM, no EVM, etc.)
34+
// create empty account
35+
await eei._state.putAccount(ZeroAddress, new Account())
36+
st.ok(await eei.accountExists(ZeroAddress)) // sanity check: account exists before we delete it
37+
st.ok(await eei.isAccountEmpty(ZeroAddress)) // it is obviously empty
38+
await eei._state.deleteAccount(ZeroAddress) // delete the account
39+
st.notOk(await eei.accountExists(ZeroAddress)) // account should not exist
40+
st.ok(await eei.isAccountEmpty(ZeroAddress)) // account is empty
41+
st.end()
42+
})
43+
44+
})

0 commit comments

Comments
 (0)