Skip to content

Adds an unoptimized Merkle trie contract (MerkleTrieLib.sol)#154

Closed
smartcontracts wants to merge 6 commits intomasterfrom
YAS-380/MerkleTrie/verifyInclusionProof
Closed

Adds an unoptimized Merkle trie contract (MerkleTrieLib.sol)#154
smartcontracts wants to merge 6 commits intomasterfrom
YAS-380/MerkleTrie/verifyInclusionProof

Conversation

@smartcontracts
Copy link
Contributor

@smartcontracts smartcontracts commented Jun 12, 2020

Description

Hello! This PR introduces an unoptimized contract for dealing with Merkle tries. Currently supports a single method verifyInclusionProof(bytes _key, bytes _value, bytes32 _root, bytes memory _proof) returns (bool).

Contract is designed to handle both binary and hexary tries (modified via TREE_RADIX). Currently only tested with hexary tries until we're sure about the format of the binary trie proofs.

Current gas usage for 2048 nodes is ~300k gas, where ~100k of that is calldata and can't be reduced w/o modifying the underlying tree structure. Generally speaking, calldata size approximately follows the formula log_radix(n) * [(radix + 1) + (2 / n)] * 32, plus extra when accounting for RLP encoding. Gas cost is then calldata_bytes * 16 (calldata is 16 gas per byte). For a trie of height 40, this comes out to ~100-200k gas. Most of the excess gas stems from RLP encoding within the proof.

Metadata

Fixes

Contributing Agreement

Copy link

@willmeister willmeister 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! 🔥

I left a few suggestions, but I think all the logic should work, with the only caveat I see being guarantees about the length of the value of the first node.

/**
* @notice Library for dealing with Merkle tries.
*/
contract MerkleTrieLib {

Choose a reason for hiding this comment

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

Sort of nitpicky: Might suggest either making this a library instead of a contract or dropping/replacing Lib in the name.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah yeah, whoops. I meant to change the name. It's annoying to test libraries and @karlfloersch mentioned that Solidity is considered deprecating libraries anyway.

* @param _key Key of the node to verify.
* @param _value Value of the node to verify.
* @param _root Root of the trie.
* @param _proof Encoded proof.

Choose a reason for hiding this comment

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

Would add more info about the format & contents of _proof

Comment on lines 56 to 61
if (keyIndex == 0) {
// First proof element is always the root node.
require(
keccak256(node.encoded) == currentHash,
"Invalid root hash"
);

Choose a reason for hiding this comment

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

This gets checked every iteration but is only applicable for the first. 2 Questions:

  1. Can it be added as a check inside of the current else if and else only to be done if the node is invalid?
  2. Is it possible for the first node to be < 32 bytes? (this doesn't matter if the answer to 1 is yes)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So re: 2., the first node can be < 32 bytes in certain cases. Given that, I think the extra logic to move this into the else if or else statements would probably make this messier overall.

Choose a reason for hiding this comment

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

Yeah, I agree that we'll want to keep it DRY. Does it make sense to just completely remove this if? If I'm understanding correctly, we'll want to treat the root the same as other nodes in that currentHash should equal keccak256(node.encoded) IFF node.encoded is 32 bytes and node.encoded (not hashing it) otherwise. Sorry if it wasn't clear -- 2 above was meant to point out that in the case that the root node is < 32 bytes, I think the require(...) will fail when it shouldn't. I could definitely be misunderstanding something though!

Comment on lines 76 to 77
// Nodes with `TREE_RADIX + 1` elements are branches.
if (node.decoded.length == TREE_RADIX + 1) {

Choose a reason for hiding this comment

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

Is it worthwhile to define a BRANCH_NODE_LENGTH const above to be TREE_RADIX + 1? That would alleviate the need for the comment and addition in every iteration (though this is likely optimized by the compiler).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, will do.

} else {
// Find the next node within the branch node and repeat.
RLPReader.RLPItem memory next = node.decoded[uint8(key[keyIndex])];
currentHash = getCorrectBytes(next).toBytes32();

Choose a reason for hiding this comment

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

I might suggest renaming getCorrectBytes(...) to getNodeHash(...). If we're going to call it hash everywhere else even though it may not technically be a hash, we should lean into it. If not, maybe getNodeIDBytes(...) or something? 🤷

Copy link
Contributor Author

@smartcontracts smartcontracts Jun 12, 2020

Choose a reason for hiding this comment

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

Yeah, this one is a little weird. I think ID works better to be honest. I'll change that up.

if (node.decoded.length == 2) {
// Throw this step into a new function to avoid `STACK_TOO_DEEP`.
bool done;
(done, currentHash, keyIndex) = checkNonBranchNode(

Choose a reason for hiding this comment

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

Looks like keyIndex will only ever increase if !done, and always be 0 when done. Does it make sense to remove done and convey done that way, (maybe with a DONE_KEY_INDEX const?) or is that too cryptic?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmmmm. Returning 0 for keyIndex was mainly a way to deal with the fact that checkNonBranchNode must return all three values. It might be a little confusing to make this change, could make it seem like keyIndex == 0 is a necessary requirement for being done.

Choose a reason for hiding this comment

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

Sounds good! I thought it may be a stretch but thought I'd mention. I support leaving it in 👍

@karlfloersch
Copy link
Contributor

@kfichter I want to merge this once it passes CI as well! Lol

@karlfloersch
Copy link
Contributor

Seems this has been superseded by #160

@gakonst gakonst deleted the YAS-380/MerkleTrie/verifyInclusionProof branch March 18, 2021 15:08
snario pushed a commit that referenced this pull request Apr 14, 2021
* Separates storage from SCC and CTC (#151)

* First pass version

* More minor tweaks for tests to pass

* Add authentication

* Minor config updates

* Fix lint error

* Fix naming changes per review

* Enable Deployer Whitelist (#119)

* first pass, test runner updated

* add ability to only validate flag, test passes

* all tests passing

* clean up console.logs

* enforce gas refund preservation

* more cleanup/import removal

* whitelisted -> allowed

* first pass, test runner updated

* add ability to only validate flag, test passes

* all tests passing

* clean up console.logs

* enforce gas refund preservation

* more cleanup/import removal

* whitelisted -> allowed

* remove whitespace

* Restrict StateTransitionerFactory (#140)

* added msg sender check

* add create test

* cleanup

* add param

* add addressmanager.address param

* CTC Chain Monotonicity Fixes (#93)

* [wip] Fix block time logic

* some sad path and happy tests passing

* more progress

* first pass sad cases tested

* cleanup, adding empty tests

* more reversion tests

* rename shouldstartat}

* add final couple tests

* enable more tests

* cleanup

* remove .only

* textual cleanup

* make queue length public

* improve structure, comments

* update deploy config

* address nits

Co-authored-by: Karl Floersch <karl@karlfloersch.com>

* fix declarations, lint (#152)

* Adds river's new Merkle tree implementation, with some cleanup (#148)

* Reverts an accidental breaking merge

* Added new merkle tree impl

* add comments

* Final cleanups and merge

Co-authored-by: Ben Jones <ben@pseudonym.party>

* Fix run gas Lower Bound (#94)

* added the check

* add test

* lower OVM TX size for Kovan

* re-remove gas check

* update gas vals slightly

* lint

* lint

* Merge master into freeze integration branch  (#153)

* update solidity version to ^0.7.0 (#122)

* update solc version to ^0.7.0

* interfaces back to solidity >0.6.0 <0.8.0

* update solc to 0.7.6

* back to 0.7.4

* upgrade to 0.7.6, fix EXTCODESIZE check

* versions >0.5.0 <0.8.0 for xdomain msgers

* ctc: disable appendQueueBatch (#150)

* ctc: disable appendSequencerBatch

* typo: fix

* re-enable verifyQueueTransaction test:

* add explicit test for verifying queue elements against either append

Co-authored-by: Ben Jones <ben@pseudonym.party>

* fix up test

* remove .only

Co-authored-by: Alina <alina@optimism.io>
Co-authored-by: Mark Tyneway <mark.tyneway@gmail.com>

* add check, simple test, update deploy (#154)

* go back to first name (#155)

* lint

* fix js number error

* add error logging to help debug deploy

* [code freeze] Fix deploy script (#156)

* fix deploy script

* add block time config

* ensure value is integer

* lint

* remove console logs from deploy

* Moves gas check to applyTransaction (#161)

* move to OVM_ST, pass test

* remove old test because functionality moved

* linting

* remove leaf hasing

* use safe EXEMRG wrapper (#162)

* use safeREQUIRE

* add owner getter

* relayer: add to config (#160)

* relayer: add to config

* lint: fix

* Fix minor error in test config

Co-authored-by: Kelvin Fichter <kelvinfichter@gmail.com>
Co-authored-by: ben-chain <ben@pseudonym.party>
Co-authored-by: Alina <alina@optimism.io>
Co-authored-by: Mark Tyneway <mark.tyneway@gmail.com>
Co-authored-by: Kevin Ho <kevinjho1996@gmail.com>
protolambda pushed a commit to protolambda/optimism that referenced this pull request May 1, 2022
…e_test

Adds an end to end system test that runs an L1 node, a rollup node, and a L2 node.
It then submits a deposit and then verifies that the result of the deposit shows up on Layer 2.
ClaytonNorthey92 added a commit to hemilabs/optimism that referenced this pull request Jul 1, 2024
526787acd popm/wasm: improve js.Value creation, add JSMarshaler interface (ethereum-optimism#160)
be5de3b9a popm/wasm: add error codes (ethereum-optimism#154)

git-subtree-dir: heminetwork
git-subtree-split: 526787acd0454b26bd4f44927aadcdda4bbdac58
ClaytonNorthey92 added a commit to hemilabs/optimism that referenced this pull request Apr 4, 2025
526787acd popm/wasm: improve js.Value creation, add JSMarshaler interface (ethereum-optimism#160)
be5de3b9a popm/wasm: add error codes (ethereum-optimism#154)

git-subtree-dir: heminetwork
git-subtree-split: 526787acd0454b26bd4f44927aadcdda4bbdac58
ClaytonNorthey92 added a commit to hemilabs/optimism that referenced this pull request Apr 7, 2025
526787acd popm/wasm: improve js.Value creation, add JSMarshaler interface (ethereum-optimism#160)
be5de3b9a popm/wasm: add error codes (ethereum-optimism#154)

git-subtree-dir: heminetwork
git-subtree-split: 526787acd0454b26bd4f44927aadcdda4bbdac58
Zena-park added a commit to tokamak-network/optimism that referenced this pull request Dec 30, 2025
Zena-park added a commit to tokamak-network/optimism that referenced this pull request Dec 30, 2025
theochap pushed a commit that referenced this pull request Jan 15, 2026
### Description

Small PR to add an `is_system_transaction` method to the `OpTxEnvelope`
that applies to deposit transactions.

Upstreams [this bit of logic in
kona](https://github.com/anton-rs/kona/blob/main/crates/executor/src/util.rs#L73-L79).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants