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: create snap sync fetcher to sync accounts #2107

Merged
merged 90 commits into from
Feb 14, 2023

Conversation

scorbajio
Copy link
Contributor

@scorbajio scorbajio commented Aug 6, 2022

AccountFetcher
Capture data now done/part of

My understanding is that there needs to be one fetcher per each get call defined in the snap specs. Open to suggestions on this design plan.

@scorbajio scorbajio force-pushed the snap-client-fetchers branch from 295c386 to 4b7480d Compare August 6, 2022 02:13
@codecov
Copy link

codecov bot commented Aug 6, 2022

Codecov Report

Merging #2107 (420d087) into master (0cf9654) will decrease coverage by 0.72%.
The diff coverage is 100.00%.

Additional details and impacted files

Impacted file tree graph

Flag Coverage Δ
block ?
blockchain 90.19% <ø> (ø)
client ?
common 95.82% <ø> (ø)
devp2p 91.62% <ø> (-0.09%) ⬇️
ethash ?
evm 83.36% <ø> (ø)
rlp ∅ <ø> (?)
statemanager ?
trie ?
tx ?
util 84.50% <100.00%> (+0.01%) ⬆️
vm ?

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

@scorbajio scorbajio mentioned this pull request Aug 6, 2022
19 tasks
@scorbajio
Copy link
Contributor Author

scorbajio commented Aug 6, 2022

When calling getAccountRange, the proof and accounts arrays contain data elements that look like this:

{
  account: {
    hash: <Buffer 59 41 33 56 5c 9e 83 0b 70 3d d8 a3 24 55 45 73 d2 16 e1 60 a0 83 28 db 62 f7 85 c9 ff 90 61 0d>,
    body: [
      <Buffer 05>,
      <Buffer 16 48 4e a6 bc cd 3f>,
      Uint8Array(0) [],
      Uint8Array(0) []
    ]
  },
  proof: <Buffer f8 66 9d 3b a0 c8 17 db 1f e9 ac 90 64 49 6c 76 75 81 3b 54 3d c9 ec 10 5c 1c 24 4e 43 3b c8 9c b8 46 f8 44 01 80 a0 56 e8 1f 17 1b cc 55 a6 ff 83 45 ... 54 more bytes>
}

Sometimes, the proof is undefined:

  account: {
    hash: <Buffer 59 41 34 ee 04 72 a6 7a b4 0e 94 e5 aa bb 00 74 02 a7 74 fb c9 d8 bd 2c 76 bc 36 36 9c b5 fa f1>,
    body: [
      <Buffer 01>,
      Uint8Array(0) [],
      <Buffer 8f b4 c3 c2 7f 69 21 c6 44 dd fc 94 24 97 5b 91 b4 58 82 47 ca 2a 1d ec 36 96 da 65 a3 97 bb f5>,
      <Buffer f1 b5 74 43 1f 38 38 d9 cd ff 6e 70 1a fd 5a 05 86 52 da b5 ae 55 23 28 8a 83 d5 fa d7 69 61 39>
    ]
  },
  proof: undefined

Will need to also finish implementing StorageRanges, ByteCodes, and TrieNodes and collecting data for them too.

@scorbajio
Copy link
Contributor Author

scorbajio commented Aug 6, 2022

Now that there is reference data for getAccountRange, next steps are to figure out how to divide the fetching process into smaller tasks, if necessary. When fetching accounts, we need to verify the integrity of the account data using the included proof.

@scorbajio scorbajio mentioned this pull request Aug 6, 2022
3 tasks
@g11tech
Copy link
Contributor

g11tech commented Aug 6, 2022

When calling getAccountRange, the proof and accounts arrays contain data elements that look like this:

{
  account: {
    hash: <Buffer 59 41 33 56 5c 9e 83 0b 70 3d d8 a3 24 55 45 73 d2 16 e1 60 a0 83 28 db 62 f7 85 c9 ff 90 61 0d>,
.....
Sometimes, the proof is `undefined`:

account: {
hash: <Buffer 59 41 34 ee 04 72 a6 7a b4 0e 94 e5 aa bb 00 74 02 a7 74 fb c9 d8 bd 2c 76 bc 36 36 9c b5 fa f1>,
....
proof: undefined


Will need to also finish implementing `StorageRanges`, `ByteCodes`, and `TrieNodes` and collecting data for them too.

awesome glad to see you got it working!

@g11tech
Copy link
Contributor

g11tech commented Aug 6, 2022

yes we need separate fetchers for fetching each of the ranges 👍

@scorbajio scorbajio changed the title client: create account fetcher base client: create snap sync fetchers Aug 6, 2022
@scorbajio
Copy link
Contributor Author

@g11tech I wasn't able to get forceSnapSync option to work. I would set it like this:

DEBUG=devp2p:* npm run client:start:dev2 -- --forceSnapSync=true ...

I ended up hardcoding it to true. Am I setting it incorrectly?

@g11tech
Copy link
Contributor

g11tech commented Aug 7, 2022

@g11tech I wasn't able to get forceSnapSync option to work. I would set it like this:

DEBUG=devp2p:* npm run client:start:dev2 -- --forceSnapSync=true ...

I ended up hardcoding it to true. Am I setting it incorrectly?

looks alright to me the way you set, i will check and add a fix in this PR

@g11tech g11tech force-pushed the snap-client-fetchers branch from a8d306b to 9a56af0 Compare August 9, 2022 12:05
@scorbajio scorbajio force-pushed the snap-client-fetchers branch from 430b925 to 877a5e1 Compare August 17, 2022 03:36
@g11tech g11tech force-pushed the snap-client-fetchers branch 2 times, most recently from 12272fb to d472cb2 Compare August 22, 2022 18:52
@g11tech g11tech force-pushed the snap-client-fetchers branch 2 times, most recently from f3a7314 to f49938a Compare September 7, 2022 13:53
@g11tech g11tech force-pushed the snap-client-fetchers branch from 8451520 to c0995bb Compare September 11, 2022 19:50
@g11tech
Copy link
Contributor

g11tech commented Sep 12, 2022

🎉 the fetcher syncs range on mainnet! (and then create new tasks and sync them further)

image

if (accounts.length > 0 && accounts[accounts.length - 1]?.hash.compare(limit) >= 0) {
return false
} else {
// TODO: Check if there is a proof of missing limit in state
Copy link
Contributor

Choose a reason for hiding this comment

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

@jochem-brouwer @scorbajio Is there an early check that can be done which can see there are no more accounts between the last and the limit

Copy link
Member

Choose a reason for hiding this comment

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

verifyRangeProof returns a boolean if there are more items or not. We could maybe early-check hasRightElement but I don't know if this really speeds it up?

Copy link
Contributor Author

@scorbajio scorbajio Sep 19, 2022

Choose a reason for hiding this comment

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

I was originally thinking that if there is an empty response to a request, it means there are no accounts in a given range. There might also be a cryptographic way to check this.

await trie.verifyRangeProof(stateRoot, origin, keys[keys.length - 1], keys, values, <any>proof)
}

private isMissingRightRange(
Copy link
Contributor

Choose a reason for hiding this comment

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

@jochem-brouwer @scorbajio pls review the correctness of this fn

Copy link
Member

Choose a reason for hiding this comment

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

Isn't this intended to be the same as trie's hasRightElement?

Copy link
Contributor Author

@scorbajio scorbajio Oct 9, 2022

Choose a reason for hiding this comment

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

It seems like the way it is being done is in line with the approach used in geth. If the returned account hash is past the limit requested, it is discarded since it is a part of the next range. This also guarantees there are no more accounts to the right of the last account received within the requested range.

} else {
// validate the proof
try {
// verifyRangeProof will also verify validate there are no missed states between origin and
Copy link
Contributor

@g11tech g11tech Sep 12, 2022

Choose a reason for hiding this comment

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

@jochem-brouwer @scorbajio pls review if my understanding of proof verification is correct here w.r.t origin that using origin in verifyRangeProof (see the definition of this private function) will validate that there are no missed accounts between origin and the first account recieved

Copy link
Member

Choose a reason for hiding this comment

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

It /should/ throw if there are missing nodes, but we should probably test this (or it is already tested in trie)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The way you are verifying makes sense to me. Could be useful to add some try-except blocks and log any errors in each function body.

* @param result fetch result
*/
async store(result: AccountData[]): Promise<void> {
this.debug(`Stored ${result.length} accounts in account trie`)
Copy link
Contributor

Choose a reason for hiding this comment

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

right now store doesn't do anything, as a separate helper class with be added in a followup PR to track and aid the fetcher

@g11tech g11tech force-pushed the snap-client-fetchers branch from 7ff31da to 9fef3e0 Compare September 12, 2022 10:14
@g11tech g11tech changed the title client: create snap sync fetchers client: create snap sync fetcher to sync accounts Sep 12, 2022
@g11tech g11tech force-pushed the snap-client-fetchers branch from 0e386c8 to 5a73cd3 Compare December 26, 2022 21:32
@scorbajio scorbajio force-pushed the snap-client-fetchers branch from 92b1c19 to 5b46855 Compare February 10, 2023 14:50
* @param job
* @param withIndex pass true to additionally output job.index
*/
jobStr(job: Job<JobTask, JobResult, StorageItem>, withIndex = false) {
Copy link
Contributor

Choose a reason for hiding this comment

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

is this something that relates to PR or you added for convenience factor?

Copy link
Contributor

Choose a reason for hiding this comment

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

ahh i see because you added jobstr as abstract method

@@ -107,7 +107,6 @@ export class BlockFetcher extends BlockFetcherBase<Block[], Block> {
} else if (result.length > 0 && result.length < job.task.count) {
// Save partial result to re-request missing items.
job.partialResult = result
this.debug(`Partial result received=${result.length} expected=${job.task.count}`)
Copy link
Contributor

Choose a reason for hiding this comment

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

why remove debug log?

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.

looks good though still needs to be extensively tested locally and on testnets for the fetcher start/end conditions and consistency, but any fixes regarding those can be done in followup PRs as this is still wip

@g11tech g11tech merged commit a1ca973 into ethereumjs:master Feb 14, 2023
@gitpoap-bot
Copy link

gitpoap-bot bot commented Feb 14, 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.

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.

3 participants