-
Notifications
You must be signed in to change notification settings - Fork 765
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
TangerineWhistle HF Support #807
Changes from all commits
f15f9d2
a2a2354
2f9ed3c
6bb93d8
f09a36b
e2f5371
46d9cd6
04eaff3
a0bd87b
d43da03
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -660,9 +660,9 @@ export const handlers: { [k: string]: OpHandler } = { | |
if (runState.eei.isStatic() && !value.isZero()) { | ||
trap(ERROR.STATIC_STATE_CHANGE) | ||
} | ||
|
||
subMemUsage(runState, inOffset, inLength) | ||
subMemUsage(runState, outOffset, outLength) | ||
|
||
if (!value.isZero()) { | ||
runState.eei.useGas(new BN(runState._common.param('gasPrices', 'callValueTransfer'))) | ||
} | ||
|
@@ -673,9 +673,17 @@ export const handlers: { [k: string]: OpHandler } = { | |
data = runState.memory.read(inOffset.toNumber(), inLength.toNumber()) | ||
} | ||
|
||
const empty = await runState.eei.isAccountEmpty(toAddressBuf) | ||
if (empty) { | ||
if (!value.isZero()) { | ||
if (runState._common.gteHardfork('spuriousDragon')) { | ||
// We are at or after Spurious Dragon | ||
// Call new account gas: account is DEAD and we transfer nonzero value | ||
if ((await runState.stateManager.accountIsEmpty(toAddressBuf)) && !value.isZero()) { | ||
runState.eei.useGas(new BN(runState._common.param('gasPrices', 'callNewAccount'))) | ||
} | ||
} else if (!(await runState.stateManager.accountExists(toAddressBuf))) { | ||
// We are before Spurious Dragon | ||
// Call new account gas: account does not exist (it is not in the state trie, not even as an "empty" account) | ||
const accountDoesNotExist = !(await runState.stateManager.accountExists(toAddressBuf)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need to call |
||
if (accountDoesNotExist) { | ||
runState.eei.useGas(new BN(runState._common.param('gasPrices', 'callNewAccount'))) | ||
} | ||
} | ||
|
@@ -789,13 +797,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) | ||
}, | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -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 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
*/ | ||||||
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> { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see any invocation of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup you are right, I will remove this. |
||||||
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 | ||||||
|
@@ -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) | ||||||
} | ||||||
} | ||||||
} | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,7 +10,8 @@ export interface StorageDump { | |
export interface StateManager { | ||
copy(): StateManager | ||
getAccount(address: Buffer): Promise<Account> | ||
putAccount(address: Buffer, account: Account): Promise<void> | ||
putAccount(address: Buffer, account: Account | null): Promise<void> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see the interface of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup I should revert the changes to the interface, thanks! |
||
deleteAccount(address: Buffer): Promise<void> | ||
touchAccount(address: Buffer): void | ||
putContractCode(address: Buffer, value: Buffer): Promise<void> | ||
getContractCode(address: Buffer): Promise<Buffer> | ||
|
@@ -28,5 +29,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> | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is cast with |
||
return account | ||
} | ||
|
||
|
@@ -87,7 +87,11 @@ export default class DefaultStateManager implements StateManager { | |
// if they have money or a non-zero nonce or code, then write to tree | ||
this._cache.put(address, account) | ||
this.touchAccount(address) | ||
// this._trie.put(addressHex, account.serialize(), cb) | ||
} | ||
|
||
async deleteAccount(address: Buffer) { | ||
this._cache.del(address) | ||
this.touchAccount(address) | ||
} | ||
|
||
/** | ||
|
@@ -111,7 +115,7 @@ export default class DefaultStateManager implements StateManager { | |
const codeHash = keccak256(value) | ||
|
||
if (codeHash.equals(KECCAK256_NULL)) { | ||
return | ||
//return | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. a note that this is commented out There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jochem-brouwer can you clarify why this should be commented out? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It should not be commented out, this is a remainder from toying around in order to resolve those failing tests. It will not change anything though; the only call is from |
||
} | ||
|
||
const account = await this.getAccount(address) | ||
|
@@ -468,7 +472,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 | ||
*/ | ||
|
@@ -477,17 +482,35 @@ 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.lookup(address) | ||
if (account) { | ||
return true | ||
} | ||
if (await this._cache._trie.get(address)) { | ||
return true | ||
} | ||
return false | ||
} | ||
|
||
/** | ||
* Removes accounts form the state trie that have been touched, | ||
* as defined in EIP-161 (https://eips.ethereum.org/EIPS/eip-161). | ||
*/ | ||
async cleanupTouchedAccounts(): Promise<void> { | ||
const touchedArray = Array.from(this._touched) | ||
for (const addressHex of touchedArray) { | ||
const address = Buffer.from(addressHex, 'hex') | ||
const empty = await this.accountIsEmpty(address) | ||
if (empty) { | ||
this._cache.del(address) | ||
if (this._common.gteHardfork('spuriousDragon')) { | ||
const touchedArray = Array.from(this._touched) | ||
for (const addressHex of touchedArray) { | ||
const address = Buffer.from(addressHex, 'hex') | ||
const empty = await this.accountIsEmpty(address) | ||
if (empty) { | ||
this._cache.del(address) | ||
} | ||
} | ||
} | ||
this._touched.clear() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add these methods to
EEI
and use those here instead? We want to make sure the EVM has access only to what's exposed from theEEI
and nothing else.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure what you mean here. You mean the calls to the
stateManager
? I.e. putaccountIsEmpty
andaccountExists
inEEI
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah exactly.
isAccountEmtpy
is already in theEEI
and can be used.accountExists
needs to be added.To clarify the reasoning, we want to isolate the execution of EVM code as much as possible.
EEI
is supposed to be the only external interface evm code has access to.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, makes sense!