-
Notifications
You must be signed in to change notification settings - Fork 4.1k
feat(iavlx): membership and nonmembership proofs #25579
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
Open
technicallyty
wants to merge
9
commits into
aaronc/iavlx-sim-bench
Choose a base branch
from
technicallyty/iavlx-proofs
base: aaronc/iavlx-sim-bench
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+472
−2
Open
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
a00ff91
wip..
technicallyty d4c11b6
forgot to commit htis
technicallyty 681df26
fix proof code
technicallyty 4474292
organize code, update tests
technicallyty 2fb6d79
Merge branch 'aaronc/iavlx-sim-bench' into technicallyty/iavlx-proofs
technicallyty 6ac2160
renames for clarity
technicallyty 17b3fbe
use getimmutable
technicallyty a0975e0
NextIndex -> nextIndex
technicallyty 68c9fed
convert tests to rapid tests
technicallyty File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,243 @@ | ||
| package iavlx | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "encoding/binary" | ||
| "errors" | ||
|
|
||
| ics23 "github.com/cosmos/ics23/go" | ||
| ) | ||
|
|
||
| type proofInnerNode struct { | ||
| Height int8 `json:"height"` | ||
| Size int64 `json:"size"` | ||
| Version int64 `json:"version"` | ||
| Left []byte `json:"left"` | ||
| Right []byte `json:"right"` | ||
| } | ||
|
|
||
| type leafPath []proofInnerNode | ||
|
|
||
| // nextIndex returns the index that would be assigned to the key. | ||
| // This method assumes the key does not exist. | ||
| // Callers are expected to check that t.Has(key) == false. | ||
| func nextIndex(node Node, key []byte) (int64, error) { | ||
| if node == nil { | ||
| return 0, nil | ||
| } | ||
| nKey, err := node.Key() | ||
| if err != nil { | ||
| return 0, err | ||
| } | ||
| if node.IsLeaf() { | ||
| switch bytes.Compare(nKey, key) { | ||
| case -1: | ||
| return 1, nil | ||
| case 1: | ||
| return 0, nil | ||
| default: | ||
| return 0, nil | ||
| } | ||
| } | ||
| if bytes.Compare(key, nKey) < 0 { | ||
| leftNode, err := node.Left().Resolve() | ||
| if err != nil { | ||
| return 0, err | ||
| } | ||
|
|
||
| return nextIndex(leftNode, key) | ||
| } | ||
|
|
||
| rightNode, err := node.Right().Resolve() | ||
| if err != nil { | ||
| return 0, err | ||
| } | ||
|
|
||
| index, err := nextIndex(rightNode, key) | ||
| if err != nil { | ||
| return 0, err | ||
| } | ||
|
|
||
| index += node.Size() - rightNode.Size() | ||
| return index, nil | ||
| } | ||
|
|
||
| func createExistenceProof(root Node, key []byte) (*ics23.ExistenceProof, error) { | ||
| path := new(leafPath) | ||
| leafVersion := root.Version() | ||
|
|
||
| leaf, err := pathToLeaf(root, key, uint64(leafVersion), path) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| leafVersion = leaf.Version() | ||
|
|
||
| leafKey, err := leaf.Key() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| leafValue, err := leaf.Value() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| return &ics23.ExistenceProof{ | ||
| Key: leafKey, | ||
| Value: leafValue, | ||
| Leaf: convertLeafOp(int64(leafVersion)), | ||
| Path: convertInnerOps(*path), | ||
| }, nil | ||
| } | ||
|
|
||
| func getByIndex(node Node, index int64) (Node, error) { | ||
| if node == nil { | ||
| return nil, nil | ||
| } | ||
| if node.IsLeaf() { | ||
| if index == 0 { | ||
| return node, nil | ||
| } | ||
| return nil, nil | ||
| } | ||
| leftNode, err := node.Left().Resolve() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| if index < leftNode.Size() { | ||
| return getByIndex(leftNode, index) | ||
| } | ||
|
|
||
| rightNode, err := node.Right().Resolve() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| return getByIndex(rightNode, index-leftNode.Size()) | ||
| } | ||
| func convertLeafOp(version int64) *ics23.LeafOp { | ||
| var varintBuf [binary.MaxVarintLen64]byte | ||
| // this is adapted from iavl/proof.go:proofLeafNode.Hash() | ||
| prefix := convertVarIntToBytes(0, varintBuf) | ||
| prefix = append(prefix, convertVarIntToBytes(1, varintBuf)...) | ||
| prefix = append(prefix, convertVarIntToBytes(version, varintBuf)...) | ||
|
|
||
| return &ics23.LeafOp{ | ||
| Hash: ics23.HashOp_SHA256, | ||
| PrehashValue: ics23.HashOp_SHA256, | ||
| Length: ics23.LengthOp_VAR_PROTO, | ||
| Prefix: prefix, | ||
| } | ||
| } | ||
|
|
||
| func convertVarIntToBytes(orig int64, buf [binary.MaxVarintLen64]byte) []byte { | ||
| n := binary.PutVarint(buf[:], orig) | ||
| return buf[:n] | ||
| } | ||
|
|
||
| func convertInnerOps(path leafPath) []*ics23.InnerOp { | ||
| steps := make([]*ics23.InnerOp, 0, len(path)) | ||
|
|
||
| // lengthByte is the length prefix prepended to each of the sha256 sub-hashes | ||
| var lengthByte byte = 0x20 | ||
|
|
||
| var varintBuf [binary.MaxVarintLen64]byte | ||
|
|
||
| // we need to go in reverse order, iavl starts from root to leaf, | ||
| // we want to go up from the leaf to the root | ||
| for i := len(path) - 1; i >= 0; i-- { | ||
| // this is adapted from iavl/proof.go:proofInnerNode.Hash() | ||
| // prefix = bytes of height-size-version ++ <length>-leftHash-<length> | ||
| // suffix = <length>-rightHash | ||
| prefix := convertVarIntToBytes(int64(path[i].Height), varintBuf) | ||
| prefix = append(prefix, convertVarIntToBytes(path[i].Size, varintBuf)...) | ||
| prefix = append(prefix, convertVarIntToBytes(path[i].Version, varintBuf)...) | ||
|
|
||
| var suffix []byte | ||
| if len(path[i].Left) > 0 { | ||
| // length prefixed left side | ||
| prefix = append(prefix, lengthByte) | ||
| prefix = append(prefix, path[i].Left...) | ||
| // prepend the length prefix for child | ||
| prefix = append(prefix, lengthByte) | ||
| } else { | ||
| // prepend the length prefix for child | ||
| prefix = append(prefix, lengthByte) | ||
| // length-prefixed right side | ||
| suffix = []byte{lengthByte} | ||
| suffix = append(suffix, path[i].Right...) | ||
| } | ||
|
|
||
| op := &ics23.InnerOp{ | ||
| Hash: ics23.HashOp_SHA256, | ||
| Prefix: prefix, | ||
| Suffix: suffix, | ||
| } | ||
| steps = append(steps, op) | ||
| } | ||
| return steps | ||
| } | ||
|
|
||
| func pathToLeaf(node Node, key []byte, version uint64, path *leafPath) (Node, error) { | ||
| nodeKey, err := node.Key() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| if node.IsLeaf() { | ||
| if bytes.Equal(nodeKey, key) { | ||
| return node, nil | ||
| } else { | ||
| return node, errors.New("key does not exist") | ||
| } | ||
| } | ||
| nodeVersion := version | ||
| if node.ID().Index() != 0 { | ||
| nodeVersion = node.ID().Version() | ||
| } | ||
| if bytes.Compare(key, nodeKey) < 0 { | ||
| // left side | ||
| rightNodePtr := node.Right() | ||
| rightNode, err := rightNodePtr.Resolve() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| pin := proofInnerNode{ | ||
| Height: int8(node.Height()), | ||
| Size: node.Size(), | ||
| Version: int64(nodeVersion), | ||
| Left: nil, | ||
| Right: rightNode.Hash(), | ||
| } | ||
| *path = append(*path, pin) | ||
|
|
||
| leftNodePtr := node.Left() | ||
|
|
||
| leftNode, err := leftNodePtr.Resolve() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| n, err := pathToLeaf(leftNode, key, version, path) | ||
| return n, err | ||
| } | ||
| // right side | ||
| leftNode, err := node.Left().Resolve() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| pin := proofInnerNode{ | ||
| Height: int8(node.Height()), | ||
| Size: node.Size(), | ||
| Version: int64(nodeVersion), | ||
| Left: leftNode.Hash(), | ||
| Right: nil, | ||
| } | ||
| *path = append(*path, pin) | ||
|
|
||
| rightNode, err := node.Right().Resolve() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| n, err := pathToLeaf(rightNode, key, version, path) | ||
| return n, err | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Check warning
Code scanning / CodeQL
Useless assignment to local variable Warning
Copilot Autofix
AI about 8 hours ago
The best way to fix this problem is to properly check and handle the error from the call to
rightNode.Key(). Specifically, after line 137, add an error check: iferris not nil, return the error, mirroring how errors are treated after similar lines above (like forleftNode.Key()and others). This ensures any error fromKey()does not go unnoticed, aligning with good Go error-handling practice.iavlx/immutable_tree.go, lines 137–139:rightKey, err := rightNode.Key(), check iferris non-nil, and return as appropriate.