Skip to content

Commit

Permalink
vm -> tangerineWhistle: added separation on EIP-161 SELFDESTRUCT gas …
Browse files Browse the repository at this point in the history
…costs, new accountExists StateManager function
  • Loading branch information
holgerd77 committed Jul 8, 2020
1 parent 2f9ed3c commit 6bb93d8
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 15 deletions.
2 changes: 1 addition & 1 deletion packages/vm/lib/evm/eei.ts
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,7 @@ export default class EEI {
}

/**
* Returns true if account is empty (according to EIP-161).
* Returns true if account is empty or non-existent (according to EIP-161).
* @param address - Address of account
*/
async isAccountEmpty(address: Buffer): Promise<boolean> {
Expand Down
26 changes: 21 additions & 5 deletions packages/vm/lib/evm/opFns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -789,13 +789,29 @@ export const handlers: { [k: string]: OpHandler } = {
}

const selfdestructToAddressBuf = addressToBuffer(selfdestructToAddress)
const balance = await runState.eei.getExternalBalance(runState.eei.getAddress())
if (balance.gtn(0)) {
const empty = await runState.eei.isAccountEmpty(selfdestructToAddressBuf)
if (empty) {
runState.eei.useGas(new BN(runState._common.param('gasPrices', 'callNewAccount')))
let deductGas = false
if (runState._common.gteHardfork('spuriousDragon')) {
// EIP-161: State Trie Clearing
const balance = await runState.eei.getExternalBalance(runState.eei.getAddress())
if (balance.gtn(0)) {
// This technically checks if account is empty or non-existent
// TODO: improve on the API here (EEI and StateManager)
const empty = await runState.eei.isAccountEmpty(selfdestructToAddressBuf)
if (empty) {
const account = await runState.stateManager.getAccount(selfdestructToAddressBuf)
deductGas = true
}
}
} else {
// Pre EIP-161 (Spurious Dragon) gas semantics
const exists = await runState.stateManager.accountExists(selfdestructToAddressBuf)
if (!exists) {
deductGas = true
}
}
if (deductGas) {
runState.eei.useGas(new BN(runState._common.param('gasPrices', 'callNewAccount')))
}

return runState.eei.selfDestruct(selfdestructToAddressBuf)
},
Expand Down
20 changes: 13 additions & 7 deletions packages/vm/lib/state/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,24 +54,30 @@ export default class Cache {
/**
* Looks up address in underlying trie.
* @param address - Address of account
* @param create - Create emtpy account if non-existent
*/
async _lookupAccount(address: Buffer): Promise<Account> {
async _lookupAccount(address: Buffer, create: boolean = true): Promise<Account | undefined> {
const raw = await this._trie.get(address)
const account = new Account(raw)
return account
if (raw || create) {
const account = new Account(raw)
return account
}
}

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

if (!account) {
account = await this._lookupAccount(key)
this._update(key, account, false, false)
account = await this._lookupAccount(key, create)
if (account) {
this._update(key, account as Account, false, false)
}
}

return account
Expand All @@ -87,7 +93,7 @@ export default class Cache {
if (addressHex) {
const address = Buffer.from(addressHex, 'hex')
const account = await this._lookupAccount(address)
this._update(address, account, false, false)
this._update(address, account as Account, false, false)
}
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/vm/lib/state/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ export interface StateManager {
generateCanonicalGenesis(): Promise<void>
generateGenesis(initState: any): Promise<void>
accountIsEmpty(address: Buffer): Promise<boolean>
accountExists(address: Buffer): Promise<boolean>
cleanupTouchedAccounts(): Promise<void>
}
15 changes: 13 additions & 2 deletions packages/vm/lib/state/stateManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export default class DefaultStateManager implements StateManager {
* @param address - Address of the `account` to get
*/
async getAccount(address: Buffer): Promise<Account> {
const account = await this._cache.getOrLoad(address)
const account = (await this._cache.getOrLoad(address)) as Account
return account
}

Expand Down Expand Up @@ -468,7 +468,8 @@ export default class DefaultStateManager implements StateManager {
}

/**
* Checks if the `account` corresponding to `address` is empty as defined in
* Checks if the `account` corresponding to `address`
* is empty or non-existent as defined in
* EIP-161 (https://eips.ethereum.org/EIPS/eip-161).
* @param address - Address to check
*/
Expand All @@ -477,6 +478,16 @@ export default class DefaultStateManager implements StateManager {
return account.isEmpty()
}

/**
* Checks if the `account` corresponding to `address`
* exists
* @param address - Address of the `account` to check
*/
async accountExists(address: Buffer): Promise<boolean> {
const account = await this._cache.getOrLoad(address, false)
return account ? true : false
}

/**
* Removes accounts form the state trie that have been touched,
* as defined in EIP-161 (https://eips.ethereum.org/EIPS/eip-161).
Expand Down
31 changes: 31 additions & 0 deletions packages/vm/tests/api/state/stateManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,37 @@ tape('StateManager', (t) => {
},
)

t.test(
'should return false for a non-existent account',
async (st) => {
const stateManager = new DefaultStateManager()
const address = Buffer.from('a94f5374fce5edbc8e2a8697c15331677e6ebf0b', 'hex')

let res = await stateManager.accountExists(address)

st.notOk(res)

st.end()
},
)

t.test(
'should return true for an existent account',
async (st) => {
const stateManager = new DefaultStateManager()
const account = createAccount('0x1', '0x1')
const address = Buffer.from('a94f5374fce5edbc8e2a8697c15331677e6ebf0b', 'hex')

await stateManager.putAccount(address, account)

let res = await stateManager.accountExists(address)

st.ok(res)

st.end()
},
)

t.test(
'should call the callback with a false boolean representing non-emptiness when the account is not empty',
async (st) => {
Expand Down

0 comments on commit 6bb93d8

Please sign in to comment.