Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

feat: add support for debug_storageRangeAt RPC #755

Merged
merged 69 commits into from
Feb 26, 2021

Conversation

eshaben
Copy link
Contributor

@eshaben eshaben commented Jan 26, 2021

Resolves #682

@eshaben
Copy link
Contributor Author

eshaben commented Jan 26, 2021

Need to clean up the code (copy pasta from debug_traceTransaction), clean up/add typings, add annotations for documentation in api, and probably more... but the logic is there, this is close.

@eshaben
Copy link
Contributor Author

eshaben commented Feb 9, 2021

Hahaha... what my last comment said but hopefully for real this time

@eshaben eshaben marked this pull request as ready for review February 11, 2021 08:53
@eshaben
Copy link
Contributor Author

eshaben commented Feb 11, 2021

Not the prettiest way of implementing this rpc method but it gets the job done 🤷

Basically we are storing the raw, un-hashed version of storage keys along with their hashed keys so we can return both hashed and raw keys to the user for the debug_storageRangeAt method. Then after we replay the transaction, we use the trie to create a read stream and get all of our storage keys from the transaction. Then we sort the keys before we can filter through the ones we need, using the hashed key to get the raw key from the db, and finally return the desired range of storage data.

Findings: geth stores their hashed keys along with the raw, un-hashed keys. geth also sorts their keys after getting them out of the database (see here: https://gist.github.com/eshaben/ae99036dc15a3784ed3ea50856cd733d -- this is the ordering of keys returned from the trie read stream) 🤷

This is ready for initial review!

If anyone wants to test against geth, I used the following command to start up a geth console: geth --rpc --rpccorsdomain="https://remix.ethereum.org" --rpcapi web3,eth,debug,personal,net --vmdebug --datadir test-chain-dir --dev console
☝️ this command requires you create a directory called test-chain-dir for storing chaindata.

Then I went to remix and deployed my contracts and sent transactions. After sending transactions I went back to geth console and tried to use debug_storageRangeAt method, like so...

debug.storageRangeAt(
"0x29a4daa5e2a33843a5967e7f6014f95862772fa6077afeddeebc9b85e966dbda", // blockHash
 0, // txIndex
"0xe33983B0ab2E5987e6b88E0a860E859061F08C68", // contractAddress
"0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace", // startKey
 2 // maxResults
);

If you have any questions or want help configuring test environment for this, let me know!

@davidmurdoch davidmurdoch changed the base branch from next to develop February 17, 2021 04:06
Copy link
Member

@davidmurdoch davidmurdoch left a comment

Choose a reason for hiding this comment

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

Looking good and loving the new tests! Made some comments and suggestions. I'll do another review once you've done the clean up you mentioned.

* @param maxResult
* @returns returns a storage object with the keys being keccak-256 hashes of the storage keys,
* and the values being the raw, unhashed key and value for that specific storage slot. Also
* retuns a next key which is the keccak-256 hash of the next key in storage for continuous downloading.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
* retuns a next key which is the keccak-256 hash of the next key in storage for continuous downloading.
* returns a next key which is the keccak-256 hash of the next key in storage for continuous downloading.

@@ -365,6 +370,11 @@ export default class Blockchain extends Emittery.Typed<
);
});

// save storage keys to the database one at a time
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// save storage keys to the database one at a time
// save storage keys to the database

Because this loop happens within the batch callback, teeeecchhhhnically they'll all get saved at the same time :-)

Comment on lines 1141 to 1147
* Strategy:
*
* 1. Get transaction hash
* 2. Replay transaction
* 3. Create read stream to get the storage keys from the transaction
* 4. Sort storage keys
* 5. Filter, assemble, and send storage data for the given range back
Copy link
Member

Choose a reason for hiding this comment

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

❤️

Comment on lines 1171 to 1179
try {
await this.traceTransaction(transactionHash, {
disableMemory: true,
disableStack: false,
disableStorage: false
});
} catch (e) {
throw new Error(e);
}
Copy link
Member

Choose a reason for hiding this comment

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

traceTransaction shouldn't (:sweat_smile:) have any side effects, so this bit can probably be removed.

Comment on lines 1853 to 1857
* @param blockHash
* @param txIndex
* @param contractAddress
* @param startKey
* @param maxResult
Copy link
Member

Choose a reason for hiding this comment

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

Can you add descriptions for each of these parameters?

Comment on lines 305 to 313
const txReceipt1 = await provider.send("eth_getTransactionReceipt", [
tx1
]);
const txReceipt2 = await provider.send("eth_getTransactionReceipt", [
tx2
]);
const txReceipt3 = await provider.send("eth_getTransactionReceipt", [
tx3
]);
Copy link
Member

Choose a reason for hiding this comment

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

a slightly neater way:

Suggested change
const txReceipt1 = await provider.send("eth_getTransactionReceipt", [
tx1
]);
const txReceipt2 = await provider.send("eth_getTransactionReceipt", [
tx2
]);
const txReceipt3 = await provider.send("eth_getTransactionReceipt", [
tx3
]);
const [txReceipt1, txReceipt2, txReceipt3] = await Promise.all([
provider.send("eth_getTransactionReceipt", [tx1]),
provider.send("eth_getTransactionReceipt", [tx2]),
provider.send("eth_getTransactionReceipt", [tx3])
]);

});
});
};
await getStorageKeys();
Copy link
Member

Choose a reason for hiding this comment

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

Can you move the let keys: Buffer[] = []; declaration inside the getStorageKeys function and then:

Suggested change
await getStorageKeys();
const keys = await getStorageKeys();

return new Promise((resolve, reject) => {
trie
.createReadStream()
.on("data", async data => {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
.on("data", async data => {
.on("data", data => {

.on("data", async data => {
keys.push(data.key);
})
.on("end", async () => {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
.on("end", async () => {
.on("end", () => {

Comment on lines 227 to 237
type StepEvent = {
gasLeft: BN;
memory: Array<number>; // Not officially sure the type. Not a buffer or uint8array
stack: Array<BN>;
depth: number;
opcode: {
name: string;
};
pc: number;
address: Buffer;
};
Copy link
Member

Choose a reason for hiding this comment

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

Maybe we can move this type from here (and from the traceTransaction) to a common place, maybe @ganache/ethereum-utils can export this type?

@davidmurdoch davidmurdoch changed the title debug_storageRangeAt implementation feat: add support for debug_storageRangeAt RPC Feb 22, 2021
Copy link
Member

@davidmurdoch davidmurdoch left a comment

Choose a reason for hiding this comment

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

Only one more little quick fix and I think it's good to go! Awesome work @eshaben !

src/chains/ethereum/ethereum/src/miner/miner.ts Outdated Show resolved Hide resolved
Co-authored-by: David Murdoch <187813+davidmurdoch@users.noreply.github.com>
@davidmurdoch davidmurdoch merged commit be84335 into develop Feb 26, 2021
@davidmurdoch davidmurdoch deleted the debug_storageRangeAt branch February 26, 2021 02:17
sambacha pushed a commit to contractshark/ganache-core that referenced this pull request Apr 15, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

implement debug_storageRangeAt
2 participants