Skip to content

Commit

Permalink
Client/StateManager: storageRangeAt() RPC call / EVMStateManager Inte…
Browse files Browse the repository at this point in the history
…rface Extension (#2922)

* Initial version of storageRangeAt().

* Update input validation of storageRangeAt().

* Add validator for unsigned integers.

* remove return await

---------

Co-authored-by: KoningR <KoningR@users.noreply.github.com>
Co-authored-by: Scotty <66335769+ScottyPoi@users.noreply.github.com>
Co-authored-by: Holger Drewes <Holger.Drewes@gmail.com>
  • Loading branch information
4 people authored Aug 9, 2023
1 parent f704378 commit fa8a518
Show file tree
Hide file tree
Showing 7 changed files with 698 additions and 0 deletions.
67 changes: 67 additions & 0 deletions packages/client/src/rpc/modules/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { EthereumClient } from '../..'
import type { Chain } from '../../blockchain'
import type { FullEthereumService } from '../../service'
import type { RpcTx } from '../types'
import type { Block } from '@ethereumjs/block'
import type { VM } from '@ethereumjs/vm'

export interface tracerOpts {
Expand Down Expand Up @@ -92,6 +93,13 @@ export class Debug {
[validators.transaction()],
[validators.blockOption],
])
this.storageRangeAt = middleware(this.storageRangeAt.bind(this), 5, [
[validators.blockHash],
[validators.unsignedInteger],
[validators.address],
[validators.uint256],
[validators.unsignedInteger],
])
}

/**
Expand Down Expand Up @@ -278,4 +286,63 @@ export class Debug {
message: err.message.toString(),
}
}

/**
* Returns a limited set of storage keys belonging to an account.
* @param params An array of 5 parameters:
* 1. The hash of the block at which to get storage from the state.
* 2. The transaction index of the requested block post which to get the storage.
* 3. The address of the account.
* 4. The starting (hashed) key from which storage will be returned. To include the entire range, pass '0x00'.
* 5. The maximum number of storage values that could be returned.
* @returns A {@link StorageRange} object that will contain at most `limit` entries in its `storage` field.
* The object will also contain `nextKey`, the next (hashed) storage key after the range included in `storage`.
*/
async storageRangeAt(params: [string, number, string, string, number]) {
const [blockHash, txIndex, account, startKey, limit] = params

if (this.vm === undefined) {
throw new Error('Missing VM.')
}

let block: Block
try {
// Validator already verified that `blockHash` is properly formatted.
block = await this.chain.getBlock(hexToBytes(blockHash))
} catch (err: any) {
throw {
code: INTERNAL_ERROR,
message: 'Could not get requested block hash.',
}
}

if (txIndex >= block.transactions.length) {
throw {
code: INTERNAL_ERROR,
message: 'txIndex cannot be larger than the number of transactions in the block.',
}
}

try {
const parentBlock = await this.chain.getBlock(block.header.parentHash)
// Copy the VM and run transactions including the relevant transaction.
const vmCopy = await this.vm.shallowCopy()
await vmCopy.stateManager.setStateRoot(parentBlock.header.stateRoot)
for (let i = 0; i <= txIndex; i++) {
await vmCopy.runTx({ tx: block.transactions[i], block })
}

return vmCopy.stateManager.dumpStorageRange(
// Validator already verified that `account` and `startKey` are properly formatted.
Address.fromString(account),
BigInt(startKey),
limit
)
} catch (err: any) {
throw {
code: INTERNAL_ERROR,
message: err.message.toString(),
}
}
}
}
27 changes: 27 additions & 0 deletions packages/client/src/rpc/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,33 @@ export const validators = {
return (params: any[], index: number) => bytes(131072, params, index)
},

/**
* Validator to ensure a valid integer [0, Number.MAX_SAFE_INTEGER], represented as a `number`.
* @returns A validator function with parameters:
* - @param params Parameters of the method.
* - @param index The index of the parameter.
*/
get unsignedInteger() {
return (params: any[], index: number) => {
// This check guards against non-number types, decimal numbers,
// numbers that are too large (or small) to be represented exactly,
// NaN, null, and undefined.
if (!Number.isSafeInteger(params[index])) {
return {
code: INVALID_PARAMS,
message: `invalid argument ${index}: argument must be an integer`,
}
}

if (params[index] < 0) {
return {
code: INVALID_PARAMS,
message: `invalid argument ${index}: argument must be larger than 0`,
}
}
}
},

/**
* hex validator to validate block hash
* @param params parameters of method
Expand Down
Loading

0 comments on commit fa8a518

Please sign in to comment.