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

Make store.Query use ics23-proofs #6315

Closed
wants to merge 6 commits into from

Conversation

AdityaSripal
Copy link
Member

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:

  • Targeted PR against correct branch (see CONTRIBUTING.md)
  • Linked to Github issue with discussion and accepted design OR link to spec that describes this work.
  • Code follows the module structure standards.
  • Wrote unit and integration tests
  • Updated relevant documentation (docs/) or specification (x/<module>/spec/)
  • Added relevant godoc comments.
  • Added a relevant changelog entry to the Unreleased section in CHANGELOG.md
  • Re-reviewed Files changed in the Github PR explorer

}
}

func IAVLOpDecoder(pop merkle.ProofOp) (merkle.ProofOperator, error) {
Copy link
Contributor

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
Copy link
Contributor

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) {
Copy link
Contributor

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 👍

Copy link
Contributor

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.

Copy link
Contributor

Choose a reason for hiding this comment

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

store/iavl/proof.go Outdated Show resolved Hide resolved
store/iavl/proof.go Show resolved Hide resolved
// Proof == nil implies that the store is empty.
if value != nil {
panic("unexpected value for an empty proof")
var commitmentProof *ics23.CommitmentProof
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: group vars :)

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()))
Copy link
Contributor

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.

Copy link
Member Author

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

AdityaSripal and others added 2 commits June 2, 2020 09:50
Co-authored-by: Alexander Bezobchuk <alexanderbez@users.noreply.github.com>
Co-authored-by: Alexander Bezobchuk <alexanderbez@users.noreply.github.com>
var err error
if res.Value != nil {
// Only support query proof from MutableTree for now
mtree, ok := tree.(*iavl.MutableTree)
Copy link
Contributor

@alessio alessio Jun 2, 2020

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.

@@ -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 {
Copy link
Contributor

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:

Suggested change
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
Copy link
Member Author

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

Copy link
Contributor

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) {
Copy link
Member Author

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()
Copy link
Member Author

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

Copy link
Contributor

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.

Copy link
Contributor

@ethanfrey ethanfrey left a 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) {
Copy link
Contributor

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) {
Copy link
Contributor

Choose a reason for hiding this comment

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


const ProofOpIAVL = "iavlstore"

type IAVLOp struct {
Copy link
Contributor

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.

Copy link
Member Author

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])
Copy link
Contributor

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
Copy link
Contributor

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)
Copy link
Contributor

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) {
Copy link
Contributor

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.

@AdityaSripal
Copy link
Member Author

Closing and addressing outstanding comments in #6324

@AdityaSripal AdityaSripal deleted the aditya/ics-query branch June 15, 2020 18:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants