-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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
Make store.Query use ics23-proofs #6315
Conversation
} | ||
} | ||
|
||
func IAVLOpDecoder(pop merkle.ProofOp) (merkle.ProofOperator, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This guy definitely warrants a godoc 👍
} | ||
op.Proof = proof | ||
|
||
// Get Key from proof for now |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on the logic comparisons to ics23 types, are you sure the ok
semantics are correct? It reads as if it should be ; ok { .. }
and not ; !ok { ... }
return op.Key | ||
} | ||
|
||
func (op IAVLOp) Run(args [][]byte) ([][]byte, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does Run
do? Add a godoc here 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this is super confusing. Not from @AdityaSripal but from whoever designed this ProofOperator interface:
https://github.com/tendermint/tendermint/blob/master/crypto/merkle/proof.go#L12-L18
It is totally unclear if it is checking absence or existence. And why it returns multiple roots. I think this ProofOp interface needs an overhaul as well, but I think he is implementing this properly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can see how iavl existence proofs require exactly one entry: https://github.com/tendermint/iavl/blob/c4c6383ddd25f984467f78516aca7089bf110c97/proof_iavl_value.go
And absence proofs require exactly 0: https://github.com/tendermint/iavl/blob/c4c6383ddd25f984467f78516aca7089bf110c97/proof_iavl_absence.go#L62-L64
store/iavl/store.go
Outdated
// Proof == nil implies that the store is empty. | ||
if value != nil { | ||
panic("unexpected value for an empty proof") | ||
var commitmentProof *ics23.CommitmentProof |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: group vars :)
store/iavl/store.go
Outdated
res.Proof = &merkle.Proof{Ops: []merkle.ProofOp{iavl.NewValueOp(key, proof).ProofOp()}} | ||
commitmentProof, err = ics23iavl.CreateMembershipProof(mtree, req.Data) | ||
if err != nil { | ||
panic(fmt.Sprintf("unexpected value for empty proof: %s", err.Error())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand why we both panic and return and error. It seems like we should always return an error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should never happen, so I panic. Also believe this matches previous behavior
Co-authored-by: Alexander Bezobchuk <alexanderbez@users.noreply.github.com>
Co-authored-by: Alexander Bezobchuk <alexanderbez@users.noreply.github.com>
store/iavl/store.go
Outdated
var err error | ||
if res.Value != nil { | ||
// Only support query proof from MutableTree for now | ||
mtree, ok := tree.(*iavl.MutableTree) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd love to see mtree
declared outside of this block - mostly for readability's sake.
store/iavl/store.go
Outdated
@@ -275,31 +277,36 @@ func (st *Store) Query(req abci.RequestQuery) (res abci.ResponseQuery) { | |||
break | |||
} | |||
|
|||
_, res.Value = tree.GetVersioned(key, res.Height) | |||
if req.Prove { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd love to see this being implemented as follows:
if req.Prove { | |
if !req.Prove { | |
break | |
} |
This would reduce the indentation level and make the code more readable
} | ||
op.Proof = proof | ||
|
||
// Get Key from proof for now |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently op.Data just contains marshalled ics proof while the op has the key in a separate field. I extract key from proof here, though we may want to instead wrap the ics-proof in a proto struct containing proof and key and just directly unmarshal the proofoperator
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reading this again, I see that the marshalled version is just the commitment proof and the key is only cached in memory when decoding Yeah, please ignore my comment about the newtype, I was really worrying about the serialized version.
} | ||
|
||
func (op IAVLOp) Run(args [][]byte) ([][]byte, error) { | ||
switch len(args) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both Existence and Nonexistence proofs are being handled by the same IAVLop here
} | ||
|
||
func (op IAVLOp) ProofOp() merkle.ProofOp { | ||
bz, err := op.Proof.Marshal() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note data just contains marshalled proof for now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is quite nice.
A client (eg. js) can parse out the proof op type and data fields. type is just a string, and data is a protobuf message with mutli-langauge decoding support.
This is perfect to just store the protobuf data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work, simple and clean.
Left a number of comments on ways to polish it. Much of that would lead to PRs on other repos.
return op.Key | ||
} | ||
|
||
func (op IAVLOp) Run(args [][]byte) ([][]byte, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, this is super confusing. Not from @AdityaSripal but from whoever designed this ProofOperator interface:
https://github.com/tendermint/tendermint/blob/master/crypto/merkle/proof.go#L12-L18
It is totally unclear if it is checking absence or existence. And why it returns multiple roots. I think this ProofOp interface needs an overhaul as well, but I think he is implementing this properly.
return op.Key | ||
} | ||
|
||
func (op IAVLOp) Run(args [][]byte) ([][]byte, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can see how iavl existence proofs require exactly one entry: https://github.com/tendermint/iavl/blob/c4c6383ddd25f984467f78516aca7089bf110c97/proof_iavl_value.go
And absence proofs require exactly 0: https://github.com/tendermint/iavl/blob/c4c6383ddd25f984467f78516aca7089bf110c97/proof_iavl_absence.go#L62-L64
|
||
const ProofOpIAVL = "iavlstore" | ||
|
||
type IAVLOp struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, if it makes it easier, use a different proof type for existence and absence. So you don't have to type switch below. Makes more sense to this code, but not sure in how it is used externally.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm... if other people are in favor I may go for it, it's certainly doable. I agree it removes the type switch, but virtually everything else about how to proof is constructed is identical so I think having the switch statement here is fine
return nil, errors.New("could not calculate root from existence proof") | ||
} | ||
|
||
exists := ics23.VerifyMembership(ics23.IavlSpec, root, op.Proof, op.Key, args[0]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only iavl-specific code here is this ics23.IavlSpec
. It would be good to somehow abstract the code out so we can use 98% the same code for simple merkle proofs.
Although that is for a future pr. It is fine as is now.
var commitmentProof *ics23.CommitmentProof | ||
var err error | ||
|
||
// Must convert store.Tree to iavl.MutableTree to use in CreateProof |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we can use another type in ics23iavl, please let me know and make a pr to simplify this logic
Thank you for handling this case, but it really should go in that library (once you get it working here, let's pull it over there)
if value != nil { | ||
panic("unexpected value for an empty proof") | ||
} | ||
_, res.Value = tree.GetVersioned(key, res.Height) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note we get the value from res.Height, but below use the tree from the current height.
This will fail if the key changed between the querier and current heights.
|
||
// Must convert store.Tree to iavl.MutableTree to use in CreateProof | ||
var mtree *iavl.MutableTree | ||
switch t := tree.(type) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is what I am talking about. We need the tree from the requested height here to use for proofs.
Closing and addressing outstanding comments in #6324 |
Description
Currently changed iavl store queries to return ics23-proofs
closes: #XXXX
Before we can merge this PR, please make sure that all the following items have been
checked off. If any of the checklist items are not applicable, please leave them but
write a little note why.
For contributor use:
docs/
) or specification (x/<module>/spec/
)godoc
comments.Unreleased
section inCHANGELOG.md
Files changed
in the Github PR explorer