Skip to content
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

Client/StateManager: storageRangeAt() RPC call / EVMStateManager Interface Extension #2922

Merged
merged 7 commits into from
Aug 9, 2023

Conversation

KoningR
Copy link
Contributor

@KoningR KoningR commented Jul 28, 2023

This PR implements debug_storageRangeAt() as discussed in ethereum/go-ethereum#14350. An implementation is only provided for DefaultStateManager, not for EthersStateManager. Additionally, hashed key preimages are always null because there does not seem to be a good way to retrieve them currently.

@codecov
Copy link

codecov bot commented Jul 29, 2023

Codecov Report

Merging #2922 (bc2bc69) into master (f704378) will decrease coverage by 2.90%.
The diff coverage is n/a.

Additional details and impacted files

Impacted file tree graph

Flag Coverage Δ
block 88.66% <ø> (ø)
blockchain 92.58% <ø> (ø)
client ?
common ?
ethash ?
evm 69.93% <ø> (ø)
rlp ∅ <ø> (∅)
statemanager ?
trie 89.98% <ø> (+0.03%) ⬆️
tx 95.96% <ø> (ø)
util 86.77% <ø> (ø)
vm 79.20% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

@@ -92,6 +101,7 @@ export class Debug {
[validators.transaction()],
[validators.blockOption],
])
this.storageRangeAt = middleware(this.storageRangeAt.bind(this), 5, [[validators.hex]])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the validators here will only validate the first arg, reading from comments need 5 validators

/**
* Returns a limited set of storage keys belonging to an account.
* @param params An array of 5 parameters:
* 1. The hash of the block that contains the requested account storage.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* 1. The hash of the block that contains the requested account storage.
* 1. The hash of the block at which to get storage from the state.

just making it less ambiguous

* Returns a limited set of storage keys belonging to an account.
* @param params An array of 5 parameters:
* 1. The hash of the block that contains the requested account storage.
* 2. The number index of the requested transaction within the block.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* 2. The number index of the requested transaction within the block.
* 2. The transaction index of the requested block post which to get the storage.

* 1. The hash of the block that contains the requested account storage.
* 2. The number index of the requested transaction within the block.
* 3. The address of the account.
* 4. The smallest (hashed) storage key that will be returned. To include the entire range, pass '0x00'.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* 4. The smallest (hashed) storage key that will be returned. To include the entire range, pass '0x00'.
* 4. The starting (hashed) key from which storage will be returned. To include the entire range, pass '0x00'.

* 2. The number index of the requested transaction within the block.
* 3. The address of the account.
* 4. The smallest (hashed) storage key that will be returned. To include the entire range, pass '0x00'.
* 5. The maximum number of storage values that will be returned.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* 5. The maximum number of storage values that will be returned.
* 5. The maximum number of storage values that could be returned.


let block: Block
try {
block = await this.service.chain.getBlock(blockHashBytes)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
block = await this.service.chain.getBlock(blockHashBytes)
block = await this.chain.getBlock(blockHashBytes)

this.chain is available

}
}

if (txIndex + 1 > block.transactions.length) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (txIndex + 1 > block.transactions.length) {
if (txIndex >= block.transactions.length) {

more comprehensible about the check

}

try {
const block = await this.service.chain.getBlock(hexToBytes(blockHash))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't the block already read above (in params check) also this.chain can be directly refereed

try {
address = Address.fromString(account)
blockHashBytes = hexToBytes(blockHash)
startKeyBytes = hexToBytes(startKey)
Copy link
Contributor

@g11tech g11tech Jul 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

startKey could be directly converted to bigint using a hexToBig... helper , and if limit is also coming as hex encoded it needs to be converted to number/bigint as well as per the requirement

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There does not appear to exist a hexToBigInt() helper. I have however moved the function calls to the same line.

Copy link
Contributor

@ScottyPoi ScottyPoi Jul 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the hex is '0x' prefixed, you can just use the BigInt constructor like: BigInt('0xabcde')

* @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]) {
Copy link
Contributor

@g11tech g11tech Jul 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
async storageRangeAt(params: [string, number, string, string, number]) {
async storageRangeAt(params: [string, string, string, string, string]) {

most like it will come as a hex encoded number ... not sure about that but do check and update/confirm

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@holgerd77
Copy link
Member

Thanks a lot for this PR! 🙏🙂❤️

@KoningR
Copy link
Contributor Author

KoningR commented Jul 31, 2023

It appears that parameters txIndex and limit are indeed passed as number instead of as hex strings. Currently, I have omitted validation for these parameters because I could not find a suitable validator in validation.ts. Is it suggested that I parse the input myself or is there a proper validator for the number type?

@holgerd77
Copy link
Member

It appears that parameters txIndex and limit are indeed passed as number instead of as hex strings. Currently, I have omitted validation for these parameters because I could not find a suitable validator in validation.ts. Is it suggested that I parse the input myself or is there a proper validator for the number type?

If you are open to do maybe add a very simple get int() validator to validation.ts, as simple as the bool one:

/**
   * bool validator to check if type is boolean
   * @param params parameters of method
   * @param index index of parameter
   */
  get bool() {
    return (params: any[], index: number) => {
      if (typeof params[index] !== 'boolean') {
        return {
          code: INVALID_PARAMS,
          message: `invalid argument ${index}: argument is not boolean`,
        }
      }
    }
  }

@@ -92,6 +101,14 @@ export class Debug {
[validators.transaction()],
[validators.blockOption],
])
// TODO: txIndex and limit are currently not input validated.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need new validator types for these?

context.createdAddressNoStorage = thirdResult.createdAddress!!
})

it<TestSetup>('Should return the correct (number of) key value pairs.', async ({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does the <TestSetup> type parameter do? is it just a label, or does it change the it function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a typesafe way of passing variables from beforeEach() to tests (see here). For legibility, I had already annotated the TestSetup interface.

throw new Error('Missing VM.')
}

if (txIndex < 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we don't already have a validator for index parameters, it could be worth adding one. especially if there are other methods are doing this themselves

return await vmCopy.stateManager.dumpStorageRange(
// Validator already verified that `account` and `startKey` are properly formatted.
Address.fromString(account),
bytesToBigInt(hexToBytes(startKey)),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
bytesToBigInt(hexToBytes(startKey)),
BigInt(startKey),

@ScottyPoi
Copy link
Contributor

It appears that parameters txIndex and limit are indeed passed as number instead of as hex strings. Currently, I have omitted validation for these parameters because I could not find a suitable validator in validation.ts. Is it suggested that I parse the input myself or is there a proper validator for the number type?

If you are open to do maybe add a very simple get int() validator to validation.ts, as simple as the bool one:

/**
   * bool validator to check if type is boolean
   * @param params parameters of method
   * @param index index of parameter
   */
  get bool() {
    return (params: any[], index: number) => {
      if (typeof params[index] !== 'boolean') {
        return {
          code: INVALID_PARAMS,
          message: `invalid argument ${index}: argument is not boolean`,
        }
      }
    }
  }

Are there methods that use signed integers (negative numbers)? "index" will always be unsigned, so it might be better to add getUint, which throws if the input is < 0

@KoningR KoningR requested a review from ScottyPoi August 4, 2023 13:35
Copy link
Contributor

@ScottyPoi ScottyPoi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Thanks for including all the tests, and for adding the validator! super helpful 👍

Copy link
Member

@jochem-brouwer jochem-brouwer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@holgerd77 holgerd77 changed the title Initial version of storageRangeAt(). Client/StateManager: storageRangeAt() RPC call / EVMStateManager Interface Extension Aug 9, 2023
Copy link
Contributor

@g11tech g11tech left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@holgerd77 holgerd77 merged commit fa8a518 into ethereumjs:master Aug 9, 2023
@gitpoap-bot
Copy link

gitpoap-bot bot commented Aug 9, 2023

Congrats, your important contribution to this open-source project has earned you a GitPOAP!

GitPOAP: 2023 EthereumJS Contributor:

GitPOAP: 2023 EthereumJS Contributor GitPOAP Badge

Head to gitpoap.io & connect your GitHub account to mint!

Learn more about GitPOAPs here.

@holgerd77
Copy link
Member

@KoningR Hi there, thanks again for this great work! 🤩

We'll directly that this into the upcoming releases (so: today)!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants