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/Monorepo: Use WASM Crypto (keccak256) for Hashing / Consistent Hash Function Overwrite #3192

Merged
merged 66 commits into from
Jan 18, 2024

Conversation

holgerd77
Copy link
Member

@holgerd77 holgerd77 commented Dec 14, 2023

This was actually the original triggger for #3187, hehe. 😁

So this PR tests the @polkadot/wasm-crypto library as a replacement for the trie key hashing for the VM state manager in the client. Had this long on my plate to analyse this a bit better, since theoretically (re-)using a more native hash function should have a significant performance imact.

So I tested with the following early mainnet block (now starting directly with the block to avoid state caches):

npm run client:start -- --vmProfileBlocks --executeBlocks=227715

Block has 79 simple value transfer txs, so not much else is happening and mainly the sender and receiver state changes are written and come into play here.

Following results (with just the first two commits, so only exchanging for trie in execution):

Before (Entire Block): 280ms 277ms 276ms 283ms. 285ms 283ms. 280ms. 279ms
After. (Entire Block): 264ms. 270ms. 265ms. 268ms. 266ms. 262ms. 263ms. 265ms

Results are significant enough that I would continue here, especially since these are results for only exchanging things for the execution state trie, I expect this to get even substantially further if we do this holistically and exchange for all other places where keccak256 hashing is applied (tx trie validations, keccak256 precompile + opcode (there are these two, right?), guess some other things as well).

I will work a bit on this since I would like to get this done in a clean way. If I am not mistaken our whole "replace the hash function" functionality/API is generally not complete (so pretty sure e.g. for VM this is neither possible in the precompile or anywhere + things (the changed hash function) would at the end also needed to passed down to the lower level libraries.

Copy link

codecov bot commented Dec 14, 2023

Codecov Report

Attention: 24 lines in your changes are missing coverage. Please review.

Comparison is base (a8f326a) 87.93% compared to head (1d90668) 87.94%.

Additional details and impacted files

Impacted file tree graph

Flag Coverage Δ
block 87.55% <100.00%> (+0.03%) ⬆️
blockchain 91.60% <ø> (ø)
client 84.68% <80.95%> (+0.05%) ⬆️
common 98.26% <100.00%> (+<0.01%) ⬆️
devp2p 82.05% <92.06%> (-0.11%) ⬇️
ethash ∅ <ø> (∅)
evm 76.92% <38.46%> (-0.04%) ⬇️
genesis 99.98% <ø> (ø)
rlp ?
statemanager 86.57% <86.48%> (+0.03%) ⬆️
trie 89.71% <100.00%> (+0.03%) ⬆️
tx 95.74% <100.00%> (+0.01%) ⬆️
util 89.13% <100.00%> (ø)
vm 80.81% <90.90%> (-0.02%) ⬇️
wallet 91.00% <ø> (ø)

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

@holgerd77 holgerd77 force-pushed the client-use-polkadot-wasm-lib-hashing branch from b9a8f0b to 159abd1 Compare December 18, 2023 11:44
@acolytec3
Copy link
Contributor

I'm liking this new approach. I wonder if we should do the same with KZG and move away from the global singleton

@@ -93,6 +96,8 @@ export class Trie {
throw new Error('`valueEncoding` can only be set if a `db` is provided')
}
this._opts = { ...this._opts, ...opts }
this._opts.useKeyHashingFunction =
opts.common?.customCrypto.keccak256 ?? opts.useKeyHashingFunction ?? keccak256
Copy link
Member Author

Choose a reason for hiding this comment

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

Just remember that recently in an exchange with @jochem-brouwer we stumbled upon a problem (unexpected evaluation) when using this kind of operator chaining, can't completely recall the constallation specifics though. 🤔

Did some basic tests and these went as expected:

grafik

So just dropping this here for awareness.

(maybe though it was something with the ? operator from TypeScript? So a problem might occur when - in the case above - common is not defined? Maybe things would then not evaluate properly? Not sure if Jochem can give more insight here (If I recall correctly a solution for our problem were adding some brackets)

Copy link
Member

Choose a reason for hiding this comment

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

I can't recall the specifics, do you maybe have some more context when we stumbled upon this? I do slightly remember a "gotcha" with these operators though, but I don't recall in which context 😅

if (Object.isFrozen(tx)) {
if (!tx.cache.hash) {
tx.cache.hash = keccak256(tx.serialize())
tx.cache.hash = keccackFunction(tx.serialize())
Copy link
Member Author

Choose a reason for hiding this comment

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

Really happy here that our capability consolidation in tx is already paying off. 🤩

//cc @gabrocheleau

@holgerd77
Copy link
Member Author

holgerd77 commented Dec 29, 2023

Just compiling a list here on what is still missing, in case its helpful (no matter who continues on this):

  • Client: pass common to various trie instantiations, particularly the new Snap fetchters, search for new Trie( (no additional Trie.create( instance apart from VM execution (already updated))
  • Client: bytecode/trienode fetcher hashing (if easily doable, so in sync/fetcher)
  • Block: pass common to withdrawals and tx trie (this static genTransactionsTrieRoot() needs a closer look here)
  • Block: use keccak for hashing (block/uncle hash)
  •  StateManager: pass common to internal _trie object
  • StateManager: keccak256 usages (various)
  • VM: receipt trie (in runBlock() and buildBlock()), also _genTxTrie() in runBlock()
  • EVM: AUTH opcode keccak256 (optional but very easy)
  • EVM: ecrecover in AUTH opcode and (more important) ecrecover precompile
  •  Trie: pass to new instantiation in verifyProof()
  •  Trie: I think shallowCopy() does not work yet, might need confirmation
  • Devp2p: various keccak256 usages (all over the place 🤯, totally wasn't aware actually)
  • All touched: generall give shallowCopy() a quick look (eventually, optimally: a test 😬)

And finally, tricky one but I guess important: Account in the Util package. Ugh. Likely again with an interface as in trie and then apply where used (mainly EVM?).

Ugh. This list went longer than I expected. 😆 🙂

But on the other hand: if WASM is really that much faster this also REALLY will have an impact if applied so broadly!

(and on the good/relieving side: most changes really shouldn't be invasive (at all), now with the alternatives to the original methods mostly at hand. We likely (sigh) should add some minimal level of additional testing though, also on the per-library level)

holgerd77 and others added 8 commits January 12, 2024 10:32
* Devp2p: add keccak function and Common passing structure to RLPx, DPT, DNS

* Client: pass in common along devp2p DPT instantiation

* Devp2p: expand to ECIES, first replacement test

* Devp2p: First full-round replacement (including concatBytes occurrences) for ECIES

* Devp2p: integrate into DPT server and message

* Devp2p: Add to DNS ENR

* Devp2p: remove util keccak256 helper function
@holgerd77
Copy link
Member Author

Updated this via UI.

@acolytec3 feel free to merge this in whenever you think it’s ready!

@holgerd77
Copy link
Member Author

(from my side additional commits look good)

packages/client/bin/cli.ts Show resolved Hide resolved
packages/client/bin/cli.ts Show resolved Hide resolved
@@ -828,6 +890,8 @@ async function run() {
chain: chainName,
mergeForkIdPostMerge: args.mergeForkIdPostMerge,
})
//@ts-ignore
common.customCrypto = cryptoFunctions
Copy link
Member

Choose a reason for hiding this comment

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

Nit: should likely add this customCrypto to the fromGethGenesis constructor also?

Copy link
Contributor

Choose a reason for hiding this comment

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

Good point. Maybe the next PR.

packages/client/test/util/wasmCrypto.spec.ts Show resolved Hide resolved
@@ -50,6 +51,8 @@ export class RLPx {
protected _refillIntervalId: NodeJS.Timeout
protected _refillIntervalSelectionCounter: number = 0

protected _keccakFunction: (msg: Uint8Array) => Uint8Array
Copy link
Member

Choose a reason for hiding this comment

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

Not sure, why is this added? It's used nowhere?

Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure, but I'll sure use it in future devp2p PRs.

packages/evm/test/customCrypto.spec.ts Show resolved Hide resolved
let key = ROOT_DB_KEY

const encoding =
opts?.valueEncoding === ValueEncoding.Bytes ? ValueEncoding.Bytes : ValueEncoding.String

if (opts?.useKeyHashing === true) {
key = (opts?.useKeyHashingFunction ?? keccak256)(ROOT_DB_KEY) as Uint8Array
key = keccakFunction.call(undefined, ROOT_DB_KEY) as Uint8Array
Copy link
Member

Choose a reason for hiding this comment

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

Not sure, why do we have to .call here?

Copy link
Contributor

Choose a reason for hiding this comment

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

🤷 I think this was left over from when @holgerd77 was trying to introduce a global crypto thing or something.

jochem-brouwer
jochem-brouwer previously approved these changes Jan 18, 2024
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.

Left a few nits, ready to merge once CI passes

Copy link
Contributor

@acolytec3 acolytec3 left a comment

Choose a reason for hiding this comment

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

Approving this. @jochem-brouwer or @holgerd77, this can be merged if everyone is satisfied. Plenty of work to add in future PRs but I think this one is fine as is.

@jochem-brouwer
Copy link
Member

I'm fine to merge this when CI passes :)

@jochem-brouwer jochem-brouwer merged commit 54446b1 into master Jan 18, 2024
46 checks passed
@holgerd77 holgerd77 deleted the client-use-polkadot-wasm-lib-hashing branch January 18, 2024 08:57
@holgerd77
Copy link
Member Author

👍

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.

5 participants