Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ module github.com/pokt-network/pocket

go 1.18

// TECHDEBT: remove once upstream PR is merged (see: https://github.com/cosmos/ics23/pull/153)
replace github.com/cosmos/ics23/go => github.com/h5law/ics23/go v0.0.0-20230619152251-56d948cafb83

// TECHDEBT: remove once upstream PR is merged (see: https://github.com/regen-network/gocuke/pull/12)
replace github.com/regen-network/gocuke => github.com/pokt-network/gocuke v0.0.1

Expand All @@ -21,6 +24,7 @@ require (

require (
github.com/benbjohnson/clock v1.3.0
github.com/cosmos/ics23/go v0.10.0
github.com/deepmap/oapi-codegen v1.12.4
github.com/dgraph-io/badger/v3 v3.2103.2
github.com/foxcpp/go-mockdns v1.0.0
Expand All @@ -37,7 +41,7 @@ require (
github.com/manifoldco/promptui v0.9.0
github.com/mitchellh/mapstructure v1.5.0
github.com/multiformats/go-multiaddr v0.8.0
github.com/pokt-network/smt v0.5.0
github.com/pokt-network/smt v0.6.1
github.com/quasilyte/go-ruleguard/dsl v0.3.21
github.com/regen-network/gocuke v0.6.2
github.com/rs/zerolog v1.27.0
Expand Down Expand Up @@ -87,6 +91,7 @@ require (
github.com/cockroachdb/apd/v3 v3.1.0 // indirect
github.com/containerd/cgroups v1.0.4 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/cosmos/gogoproto v1.4.3 // indirect
github.com/cucumber/common/messages/go/v19 v19.1.2 // indirect
github.com/cucumber/gherkin/go/v26 v26.0.3 // indirect
github.com/cucumber/messages/go/v21 v21.0.1 // indirect
Expand Down
8 changes: 6 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cosmos/gogoproto v1.4.3 h1:RP3yyVREh9snv/lsOvmsAPQt8f44LgL281X0IOIhhcI=
github.com/cosmos/gogoproto v1.4.3/go.mod h1:0hLIG5TR7IvV1fme1HCFKjfzW9X2x0Mo+RooWXCnOWU=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
Expand Down Expand Up @@ -356,6 +358,8 @@ github.com/gotestyourself/gotestyourself v2.2.0+incompatible h1:AQwinXlbQR2HvPjQ
github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/h5law/ics23/go v0.0.0-20230619152251-56d948cafb83 h1:uG97IfYQttG5iVt/jHK2wnGZgKUxHUjnzAlWY6EDso8=
github.com/h5law/ics23/go v0.0.0-20230619152251-56d948cafb83/go.mod h1:ZfJSmng/TBNTBkFemHHHj5YY7VAU/MBU980F4VU1NG0=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
Expand Down Expand Up @@ -698,8 +702,8 @@ github.com/pokt-network/go-mockdns v0.0.1 h1:1Kb/kIFH6bNtY9F1bFhJyMRMCc7WyiqfGg0
github.com/pokt-network/go-mockdns v0.0.1/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4=
github.com/pokt-network/gocuke v0.0.1 h1:qJ/Ryf+hi5L6T9lsOZDNbiAclHkLlDio5/eVKQEYhgE=
github.com/pokt-network/gocuke v0.0.1/go.mod h1:BowLKW4++696gTTU33teodtIhjjyaphEbhQT9D5Refw=
github.com/pokt-network/smt v0.5.0 h1:rNTW3FB6i0pNMnafDqsBySgs0zpbjs0spP3p7ltVjAE=
github.com/pokt-network/smt v0.5.0/go.mod h1:CWgC9UzDxXJNkL7TEADnJXutZVMYzK/+dmBb37RWkeQ=
github.com/pokt-network/smt v0.6.1 h1:u5yTGNNND6edXv3vMQrAcjku1Ig4osehdu+EMYSXHUU=
github.com/pokt-network/smt v0.6.1/go.mod h1:CWgC9UzDxXJNkL7TEADnJXutZVMYzK/+dmBb37RWkeQ=
github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4=
github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
Expand Down
8 changes: 8 additions & 0 deletions ibc/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- [Persistence](#persistence)
- [Components](#components)
- [ICS-24 Host Requirements](#ics-24-host-requirements)
- [ICS-23 Vector Commitments](#ics-23-vector-commitments)

## Definitions

Expand Down Expand Up @@ -106,6 +107,13 @@ The [IBC specification][ibc-spec] details numerous Interchain Standards (ICSs) t

See: [ICS-24](./ics24.md) for more details on the specifics of the ICS-24 implementation for Pocket.

### ICS-23 Vector Commitments

[ICS-23][ics23] defines the `CommitmentProof` type that is used to prove the membership/non-membership of a key-value pair in the IBC stores. As this type is serialisable the relayers can relay these proofs to a counterparty chain, as described above, for their ibc specific light client (of the source chain) to verify the proof and thus react accordingly. In order to implement ICS-23, the `cosmos/ics23` library was used, specifically its `CommitmentProof` type and its methods to verify the proofs in a way that does not require the tree itself.

See: [ICS-23](./ics23.md) for more details on the specifics of the ICS-23 implementation for Pocket.

[ibc-spec]: https://github.com/cosmos/ibc
[ics24]: https://github.com/cosmos/ibc/blob/main/spec/core/ics-024-host-requirements/README.md
[ics23]: https://github.com/cosmos/ibc/blob/main/spec/core/ics-023-vector-commitments/README.md
[smt]: https://github.com/pokt-network/smt
116 changes: 116 additions & 0 deletions ibc/docs/ics23.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# ICS-23 Vector Commitments <!-- omit in toc -->

- [Overview](#overview)
- [Implementation](#implementation)
- [Custom SMT `ProofSpec`](#custom-smt-proofspec)
- [Converting `SparseMerkleProof` to `CommitmentProof`](#converting-sparsemerkleproof-to-commitmentproof)
- [Proof Verification](#proof-verification)

## Overview

[ICS-23][ics23] defines the types and functions needed to verify membership of a key-value pair in a `CommitmentState`. As the Pocket IBC implementation uses the [SMT][smt] for its provable stores, this is referred to as the `CommitmentState` object. Cosmos has a library `cosmos/ics23` which is already SDK agnostic and defines many of the types necessary for ICS-23. This library was able to be used _mostly_ out of the box, with some minor adjustments detailed below.

## Implementation

The benefit of using `cosmos/ics23` over implementing similar types ourselves is twofold:

1. It is already SDK agnostic, so can be used by Pocket (a non-cosmos chain) without any issues or major changes.
2. The functions defined for proof verification are decoupled from the underlying tree structure, meaning proof verification is tree agnostic.

However, there were some changes made specifically for Pocket's implementation of ICS-23.

See: [`cosmos/ics23` #152](https://github.com/cosmos/ics23/issues/152) and [`cosmos/ics23` #153](https://github.com/cosmos/ics23/pull/153ß) for the details of the changes made to allow for `ExclusionProof` verification.

### Custom SMT `ProofSpec`

The `ProofSpec` type in `cosmos/ics23` is used to define:

1. The steps needed to verify a proof
2. The hash functions used
3. Node prefixes
4. Etc...

The `ProofSpec` is then passed into the verification functions in order to verify a proof instead of having to interact with the tree itself. This is useful as proofs must be verified via an (IBC) light client, and as such being able to verify a proof without reconstructing a tree is much more memory efficient.

As the SMT used by Pocket Network only stores hashed values by default, the IBC store uses the `WithValueHasher(nil)` option which stores the source value (as raw bytes) in the tree. The following `ProofSpec` was created to support this:

```go
smtSpec *ics23.ProofSpec = &ics23.ProofSpec{
LeafSpec: &ics23.LeafOp{
Hash: ics23.HashOp_SHA256,
PrehashKey: ics23.HashOp_SHA256,
PrehashValue: ics23.HashOp_NO_HASH,
Length: ics23.LengthOp_NO_PREFIX,
Prefix: []byte{0},
},
InnerSpec: &ics23.InnerSpec{
ChildOrder: []int32{0, 1},
ChildSize: 32,
MinPrefixLength: 1,
MaxPrefixLength: 1,
EmptyChild: make([]byte, 32),
Hash: ics23.HashOp_SHA256,
},
MaxDepth: 256,
PrehashKeyBeforeComparison: true,
}
```

The main difference from the `cosmos/ics23` `SmtSpec` object is that the `PrehashValue` field is set to not hash values before hashing the key-value pair.

### Converting `SparseMerkleProof` to `CommitmentProof`

In order to convert the proofs generated by the SMT into a serialisable proof used by `cosmos/ics23`, the `SideNodes` field of the `SparseMerkleProof` must be converted into a list of `InnerOp` types which define the order of the hashes. The order of the hashes is important as depending on whether the next hash is the left or right neighbour of the current hash, they will be hashed in a different order, ultimately creating a different root hash. This conversion allows the verification to produce the same root hash as the SMT would have produced when verifying the proof.

As `SparseMerkleProof` objects represent both inclusion and exclusion proofs as defined in the [JMT whitepaper][jmt]. The conversion step will convert the SMT proof into either an `ExistenceProof` or `ExclusionProof` as defined in `cosmos/ics23`.

### Proof Verification

Membership proofs are verified as follows:

1. Use the key-value pair to generate a leaf hash
2. Hash the leaf with the `SideNodes` found in the `path` field of the `ExistenceProof` to generate the root hash
3. Compare the root hash with the one provided and expect them to be identical

Non-membership proofs are verified as follows:

1. If the `ActualValueHash` field in the `ExclusionProof` is the SMT's placeholder value (`[32]byte`, i.e. the key is not set in the tree), then use the placeholder value as the leaf node hash and skip to step 3 below
2. If the `ActualValueHash` field is not the placeholder value, then use the `ActualPath` and `ActualValueHash` fields (provided via `NonMembershipLeafData`) to generate the leaf node hash.
- **IMPORTANT**: DO NOT hash these values before hashing the node as they are populated from the SMT proof's `NonMembershipLeafData` field and thus are already hashed
3. Hash the leaf node hash with the `SideNodes` found in the `Path` field of the `ExclusionProof` to generate the root hash
4. Compare the root hash with the one provided
- if `computedRootHash == providedRootHash`
- `key` not in tree -> `Proof` is valid -> exclusion QED
- if `computedRootHash != providedRootHash`
- `key` is in tree -> `Proof` is invalid -> exclusion QED

```mermaid
flowchart TD
Copy link
Collaborator

Choose a reason for hiding this comment

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

Appreciate the new diagram!

I["Proof,Key"]
NMD{"proof.NonMembershipLeafData == nil ?"}
KP1["actualPath = sha256(key) \n actualValue = placeholder\ncurrentHash = [32]byte"]
KP2["actualPath = ProvidedKeyHash \n actualValue = ProvidedValueHash\ncurrentHash = sha256([]byte{0}+actualPath+actualValueHash)"]
C["nextHash = sha256(currentHash+sideNodeHash)"]
Compare{"ComputedRootHash == ProvidedRootHash ?"}
EV["Exclusion Prove VALID"]
EI["Exclusion Prove INVALID"]

I --> NMD
NMD -- Yes --> KP1
NMD -- No --> KP2

KP1 -- CurrentHash --> C
KP2 -- CurrentHash --> C

C -- while NextSideNode != nil --> C

C -- ComputedRootHash --> Compare
Compare -- Yes --> EV
Compare -- No --> EI
```

The full implementation of this logic can be found [here](../store/proofs_ics23.go) as well as in the `cosmos/ics23` [library](https://github.com/h5law/ics23/blob/56d948cafb83ded78dc4b9de3c8b04582734851a/go/proof.go#L171).

[ics23]: https://github.com/cosmos/ibc/blob/main/spec/core/ics-023-vector-commitments/README.md
[smt]: https://github.com/pokt-network/smt
[jmt]: https://developers.diem.com/papers/jellyfish-merkle-tree/2021-01-14.pdf
159 changes: 159 additions & 0 deletions ibc/store/proofs_ics23.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package store

import (
"crypto/sha256"

ics23 "github.com/cosmos/ics23/go"
coreTypes "github.com/pokt-network/pocket/shared/core/types"
"github.com/pokt-network/smt"
)

// position refers to whether the node is either the left or right child of its parent
// for the binary SMT
// Ref: https://github.com/pokt-network/smt/blob/main/types.go
const (
left int = iota // 0
right // 1
hashSize = 32
)

var (
// Custom SMT spec as the store does not hash values
Copy link
Collaborator

Choose a reason for hiding this comment

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

I keep bringing this up because I'm still personally not 100% sure myself (but trust your judgment), but you're confident that we should keep hashing the other values even though we're not hashing these ones?

Lmk if this is better for an offline dicussion.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We dont hash any other values. The other side nodes are already hashed. When we insert into the tree there are 3 steps:

  1. hash the key - this gives us the path (we use the PathHasher)
  • The PathHasher can be customised with the option WithPathHasher() when creating the tree, this cannot be nil
  1. hash the value - this gives us the valueHash (we use the ValueHasher)
  • The ValueHasher can be customised with the option WithValueHasher() if the value hasher is nil we do a noop function when hashing the value leaving us with the raw bytes
  1. We then hash both of these using the TreeHasher this is required when we create the tree - this gives us the nodes digest
  • The TreeHasher is used as the PathHasher and ValueHasher if they are not provided
  • The TreeHasher cannot be nil

From this we can see that we dont want to prehash the value as we need the raw bytes but the node is still encoded and hashed to give the digest whether the value is or not. The side nodes are digests, they are already hashed. What we do with them is create a new inner node like this:

innerNode = []byte{[]byte{1}, leftChild.digest, rightChild.digest}

And then we hash that to give us the next digest.

Copy link
Collaborator

Choose a reason for hiding this comment

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

The hashing and nonhashing makes sense to me in the context of IBC and SMT in independence.

It's not a blocker for this PR, but my question/concern is moreso for the larger context of the blockchain to make sure that I'm personally not missing/misunderstanding something. For the rest of the persistence module, question is: Should we hash the values for the other trees or not?

@dylanlott do you have thoughts/opinion here?

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 in my ICS-24 PR I made the decision to not hash any values in all of the trees (except root tree as its created differently) this is just to make things easier during creation. But open to ideas here

Copy link
Collaborator

Choose a reason for hiding this comment

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

Interesting. Would like to discuss this more (as a team).

Moving the thread to #847 (comment)

smtSpec *ics23.ProofSpec = &ics23.ProofSpec{
LeafSpec: &ics23.LeafOp{
Hash: ics23.HashOp_SHA256,
PrehashKey: ics23.HashOp_SHA256,
PrehashValue: ics23.HashOp_NO_HASH,
Length: ics23.LengthOp_NO_PREFIX,
Prefix: []byte{0},
},
InnerSpec: &ics23.InnerSpec{
ChildOrder: []int32{0, 1},
ChildSize: hashSize,
MinPrefixLength: 1,
MaxPrefixLength: 1,
EmptyChild: make([]byte, hashSize),
Hash: ics23.HashOp_SHA256,
},
MaxDepth: 256,
PrehashKeyBeforeComparison: true,
}
innerPrefix = []byte{1}

// defaultValue is the default placeholder value in a SparseMerkleTree
defaultValue = make([]byte, hashSize)
)

// VerifyMembership verifies the CommitmentProof provided, checking whether it produces the same
// root as the one given. If it does, the key-value pair is a member of the tree
func VerifyMembership(root ics23.CommitmentRoot, proof *ics23.CommitmentProof, key, value []byte) bool {
// verify the proof
return ics23.VerifyMembership(smtSpec, root, proof, key, value)
}

// VerifyNonMembership verifies the CommitmentProof provided, checking whether it produces the same
// root as the one given. If it does, the key-value pair is not a member of the tree as the proof's
// value is either the default nil value for the SMT or an unrelated value at the path
func VerifyNonMembership(root ics23.CommitmentRoot, proof *ics23.CommitmentProof, key []byte) bool {
// verify the proof
return ics23.VerifyNonMembership(smtSpec, root, proof, key)
}

// createMembershipProof generates a CommitmentProof object verifying the membership of a key-value pair
// in the SMT provided
func createMembershipProof(tree *smt.SMT, key, value []byte) (*ics23.CommitmentProof, error) {
proof, err := tree.Prove(key)
if err != nil {
return nil, coreTypes.ErrCreatingProof(err)
}
return convertSMPToExistenceProof(proof, key, value), nil
}

// createNonMembershipProof generates a CommitmentProof object verifying the membership of an unrealted key at the given key in the SMT provided
func createNonMembershipProof(tree *smt.SMT, key []byte) (*ics23.CommitmentProof, error) {
proof, err := tree.Prove(key)
if err != nil {
return nil, coreTypes.ErrCreatingProof(err)
}

return convertSMPToExclusionProof(proof, key), nil
}

// convertSMPToExistenceProof converts a SparseMerkleProof to an ics23
// ExistenceProof to verify membership of an element
func convertSMPToExistenceProof(proof *smt.SparseMerkleProof, key, value []byte) *ics23.CommitmentProof {
path := sha256.Sum256(key)
steps := convertSideNodesToSteps(proof.SideNodes, path[:])
return &ics23.CommitmentProof{
Proof: &ics23.CommitmentProof_Exist{
Exist: &ics23.ExistenceProof{
Key: key,
Value: value,
Leaf: smtSpec.LeafSpec,
Path: steps,
},
},
}
}

// convertSMPToExclusionProof converts a SparseMerkleProof to an ics23
// ExclusionProof to verify non-membership of an element
func convertSMPToExclusionProof(proof *smt.SparseMerkleProof, key []byte) *ics23.CommitmentProof {
path := sha256.Sum256(key)
steps := convertSideNodesToSteps(proof.SideNodes, path[:])
leaf := &ics23.LeafOp{
Hash: ics23.HashOp_SHA256,
// Do not re-hash already hashed fields from NonMembershipLeafData
PrehashKey: ics23.HashOp_NO_HASH,
PrehashValue: ics23.HashOp_NO_HASH,
Length: ics23.LengthOp_NO_PREFIX,
Prefix: []byte{0},
}
actualPath := path[:]
actualValue := defaultValue
if proof.NonMembershipLeafData != nil {
actualPath = proof.NonMembershipLeafData[1 : 1+hashSize] // len(prefix): len(prefix) + hashSize
actualValue = proof.NonMembershipLeafData[1+hashSize:]
}
return &ics23.CommitmentProof{
Proof: &ics23.CommitmentProof_Exclusion{
Exclusion: &ics23.ExclusionProof{
Key: key,
ActualPath: actualPath,
ActualValueHash: actualValue,
Leaf: leaf,
Path: steps,
},
},
}
}

// convertSideNodesToSteps converts the SideNodes field in the SparseMerkleProof
// into a list of InnerOps for the ics23 CommitmentProof
func convertSideNodesToSteps(sideNodes [][]byte, path []byte) []*ics23.InnerOp {
steps := make([]*ics23.InnerOp, 0, len(sideNodes))
for i := 0; i < len(sideNodes); i++ {
var prefix, suffix []byte
prefix = append(prefix, innerPrefix...)
if isLeft(path, len(sideNodes)-1-i) {
// path is on the left so sidenode must be on the right
suffix = make([]byte, 0, len(sideNodes[i]))
suffix = append(suffix, sideNodes[i]...)
} else {
// path is on the right so sidenode must be on the left
prefix = append(prefix, sideNodes[i]...)
}
op := &ics23.InnerOp{
Hash: ics23.HashOp_SHA256,
Prefix: prefix,
Suffix: suffix,
}
steps = append(steps, op)
}
return steps
}

// isLeft returns true is the i-th bit of path is a left child in the SMT
func isLeft(path []byte, i int) bool {
return smt.GetPathBit(path, i) == left
}
Loading