Skip to content

Commit

Permalink
Update EthersStateManager (#2873)
Browse files Browse the repository at this point in the history
* Update ethersStateManager caching logic

* empty

---------

Co-authored-by: Jochem Brouwer <jochembrouwer96@gmail.com>
  • Loading branch information
acolytec3 and jochem-brouwer committed Jul 10, 2023
1 parent b3cb348 commit 786a244
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 90 deletions.
19 changes: 1 addition & 18 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/statemanager/examples/browser.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<script type="module">
import { Account, Address } from '@ethereumjs/util'
import { DefaultStateManager } from '@ethereumjs/statemanager'
import { hexStringToBytes } from '@ethereumjs/util'
import { hexToBytes } from '@ethereumjs/util'

const run = async () => {
const stateManager = new DefaultStateManager()
Expand Down
21 changes: 17 additions & 4 deletions packages/statemanager/src/ethersStateManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ export class EthersStateManager implements EVMStateManagerInterface {

/**
* Gets the code corresponding to the provided `address`.
* @param address - Address to get the `code` for
* @param address - Address to get the `account` for
* @returns {Promise<Uint8Array>} - Resolves with the code corresponding to the provided address.
* Returns an empty `Uint8Array` if the account has no associated code.
*/
Expand Down Expand Up @@ -261,11 +261,21 @@ export class EthersStateManager implements EVMStateManagerInterface {
* @param address - Address under which to store `account`
* @param account - The account to store
*/
async putAccount(address: Address, account: Account): Promise<void> {
async putAccount(address: Address, account: Account | undefined): Promise<void> {
if (this.DEBUG) {
this._debug(`putting account data for ${address.toString()}`)
this._debug(
`Save account address=${address} nonce=${account?.nonce} balance=${
account?.balance
} contract=${account && account.isContract() ? 'yes' : 'no'} empty=${
account && account.isEmpty() ? 'yes' : 'no'
}`
)
}
if (account !== undefined) {
this._accountCache!.put(address, account)
} else {
this._accountCache!.del(address)
}
this._accountCache.put(address, account)
}

/**
Expand Down Expand Up @@ -337,6 +347,7 @@ export class EthersStateManager implements EVMStateManagerInterface {
*/
async checkpoint(): Promise<void> {
this._accountCache.checkpoint()
this._storageCache.checkpoint()
}

/**
Expand All @@ -358,6 +369,8 @@ export class EthersStateManager implements EVMStateManagerInterface {
*/
async revert(): Promise<void> {
this._accountCache.revert()
this._storageCache.revert()
this._contractCache.clear()
}

async flush(): Promise<void> {
Expand Down
30 changes: 21 additions & 9 deletions packages/statemanager/test/ethersStateManager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ describe('Ethers State Manager API tests', () => {
state.getContractStorage(UNIerc20ContractAddress, setLengthLeft(bigIntToBytes(2n), 32)),
'should not call provider.getStorageAt'
)
await state.checkpoint()

await state.putContractStorage(
UNIerc20ContractAddress,
Expand Down Expand Up @@ -174,6 +175,23 @@ describe('Ethers State Manager API tests', () => {
'account should not exist after being deleted'
)

await state.revert()
assert.ok(
(await state.getAccount(vitalikDotEth)) !== undefined,
'account deleted since last checkpoint should exist after revert called'
)

const deletedSlotAfterRevert = await state.getContractStorage(
UNIerc20ContractAddress,
setLengthLeft(bigIntToBytes(2n), 32)
)

assert.equal(
deletedSlotAfterRevert.length,
4,
'slot deleted since last checkpoint should exist in storage cache after revert'
)

try {
await Block.fromJsonRpcProvider(provider, 'fakeBlockTag', {} as any)
assert.fail('should have thrown')
Expand All @@ -184,25 +202,19 @@ describe('Ethers State Manager API tests', () => {
)
}

const newState = state.shallowCopy()

assert.equal(
undefined,
(state as any)._contractCache.get(UNIerc20ContractAddress),
'should not have any code for contract after cache is cleared'
)

assert.notEqual(
undefined,
(newState as any)._contractCache.get(UNIerc20ContractAddress.toString()),
'state manager copy should have code for contract after cache is cleared on original state manager'
'should not have any code for contract after cache is reverted'
)

assert.equal((state as any)._blockTag, '0x1', 'blockTag defaults to 1')
state.setBlockTag(5n)
assert.equal((state as any)._blockTag, '0x5', 'blockTag set to 0x5')
state.setBlockTag('earliest')
assert.equal((state as any)._blockTag, 'earliest', 'blockTag set to earliest')

await state.checkpoint()
}
})
})
Expand Down
124 changes: 66 additions & 58 deletions packages/statemanager/test/testdata/providerData/mockProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,84 +14,92 @@ export class MockProvider extends JsonRpcProvider {
return fakeConnection as FetchRequest
}

private getAccountValue = async (method: string, params: Array<any>) => {
switch (method) {
case 'eth_getProof':
return this.getProofValues(params as any)
case 'eth_getBlockByNumber':
return this.getBlockValues(params as any)
case 'eth_chainId': // Always pretends to be mainnet
return 1
case 'eth_getTransactionByHash':
return this.getTransactionData(params as any)
case 'eth_getCode':
return 0
case 'eth_getStorageAt':
return '0xabcd'
default:
throw new Error(`method ${method} not implemented`)
_send = async (payload: JsonRpcPayload | JsonRpcPayload[]): Promise<JsonRpcResult[]> => {
let method
let params
let id

if (Array.isArray(payload)) {
const results = []
for (const el of payload) {
;({ method, params, id } = el)
results.push(await this.getValues(method, id, params))
}
return results
} else {
;({ method, params, id } = payload)
return [await this.getValues(method, id, params)]
}
}

_send = async (payload: JsonRpcPayload | JsonRpcPayload[]): Promise<JsonRpcResult[]> => {
const { method, params, id } = payload as JsonRpcPayload

private getValues = async (method: string, id: number, params: any): Promise<JsonRpcResult> => {
switch (method) {
case 'eth_getProof':
return [
{
id,
result: this.getProofValues(params as any),
},
]
return {
id,
result: this.getProofValues(params as any),
}

case 'eth_getBlockByNumber':
return [
{
id,
result: this.getBlockValues(params as any),
},
]
return {
id,
result: this.getBlockValues(params as any),
}

case 'eth_chainId': // Always pretends to be mainnet
return [
{
id,
result: 1,
},
]
return {
id,
result: 1,
}
case 'eth_getTransactionByHash':
return [
{
id,
result: this.getTransactionData(params as any),
},
]
return {
id,
result: this.getTransactionData(params as any),
}

case 'eth_getCode': {
let code = '0x'
if ((params as any[])[0] !== '0xd8da6bf26964af9d7eed9e03e53415d37aa96045') {
code = '0xab'
}
return [
{
id,
result: code,
},
]
return {
id,
result: code,
}
}
case 'eth_getStorageAt':
return [
{
id,
result: '0xabcd',
},
]
return {
id,
result: '0xabcd',
}

default:
throw new Error(`method ${method} not implemented`)
return {
id,
result: {
error: 'method not implemented',
},
}
}
}

private getProofValues = async (params: [address: string, _: [], blockTag: bigint | string]) => {
const [address, _slot, blockTag] = params
const account = (await import(`./accounts/${address}.json`)).default
return account[blockTag.toString() ?? 'latest']
try {
const account = (await import(`./accounts/${address}.json`)).default
return account[blockTag.toString() ?? 'latest']
} catch {
return {
'0x1': {
address,
balance: '0x0',
codeHash: '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470',
nonce: '0x0',
storageHash: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421',
storageProof: [],
},
}
}
}

private getBlockValues = async (params: [blockTag: string, _: boolean]) => {
Expand Down

0 comments on commit 786a244

Please sign in to comment.