Skip to content

Commit

Permalink
Add cosmos/ics23 proto into local proto with conversion
Browse files Browse the repository at this point in the history
  • Loading branch information
h5law committed Jul 20, 2023
1 parent 3915107 commit 9f63e68
Show file tree
Hide file tree
Showing 3 changed files with 330 additions and 0 deletions.
107 changes: 107 additions & 0 deletions ibc/types/proofs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package types

import ics23 "github.com/cosmos/ics23/go"

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

func (p *ProofSpec) ConvertToIcs23ProofSpec() *ics23.ProofSpec {
ics := new(ics23.ProofSpec)
ics.LeafSpec = p.LeafSpec.ConvertToIcs23LeafOp()
ics.InnerSpec = p.InnerSpec.ConvertToIcs23InnerSpec()
ics.MaxDepth = p.MaxDepth
ics.MinDepth = p.MinDepth
ics.PrehashKeyBeforeComparison = p.PrehashKeyBeforeComparison
return ics
}

func (l *LeafOp) ConvertToIcs23LeafOp() *ics23.LeafOp {
ics := new(ics23.LeafOp)
ics.Hash = l.Hash.ConvertToIcs23HashOp()
ics.PrehashKey = l.PrehashKey.ConvertToIcs23HashOp()
ics.PrehashValue = l.PrehashValue.ConvertToIcs23HashOp()
ics.Length = l.Length.ConvertToIcs23LenthOp()
ics.Prefix = l.Prefix
return ics
}

func (i *InnerSpec) ConvertToIcs23InnerSpec() *ics23.InnerSpec {
ics := new(ics23.InnerSpec)
ics.ChildOrder = i.ChildOrder
ics.MinPrefixLength = i.MinPrefixLength
ics.MaxPrefixLength = i.MaxPrefixLength
ics.EmptyChild = i.EmptyChild
ics.Hash = i.Hash.ConvertToIcs23HashOp()
return ics
}

func (h HashOp) ConvertToIcs23HashOp() ics23.HashOp {
switch h {
case HashOp_NO_HASH:
return ics23.HashOp_NO_HASH
case HashOp_SHA256:
return ics23.HashOp_SHA256
case HashOp_SHA512:
return ics23.HashOp_SHA512
case HashOp_KECCAK:
return ics23.HashOp_KECCAK
case HashOp_RIPEMD160:
return ics23.HashOp_RIPEMD160
case HashOp_BITCOIN:
return ics23.HashOp_BITCOIN
case HashOp_SHA512_256:
return ics23.HashOp_SHA512_256
default:
panic("unknown hash op")
}
}

func (l LengthOp) ConvertToIcs23LenthOp() ics23.LengthOp {
switch l {
case LengthOp_NO_PREFIX:
return ics23.LengthOp_NO_PREFIX
case LengthOp_VAR_PROTO:
return ics23.LengthOp_VAR_PROTO
case LengthOp_VAR_RLP:
return ics23.LengthOp_VAR_RLP
case LengthOp_FIXED32_BIG:
return ics23.LengthOp_FIXED32_BIG
case LengthOp_FIXED32_LITTLE:
return ics23.LengthOp_FIXED32_LITTLE
case LengthOp_FIXED64_BIG:
return ics23.LengthOp_FIXED64_BIG
case LengthOp_FIXED64_LITTLE:
return ics23.LengthOp_FIXED64_LITTLE
case LengthOp_REQUIRE_32_BYTES:
return ics23.LengthOp_REQUIRE_32_BYTES
case LengthOp_REQUIRE_64_BYTES:
return ics23.LengthOp_REQUIRE_64_BYTES
default:
panic("unknown length op")
}
}

func (i *InnerOp) ConvertToIcs23InnerOp() *ics23.InnerOp {
ics := new(ics23.InnerOp)
ics.Hash = i.Hash.ConvertToIcs23HashOp()
ics.Prefix = i.Prefix
ics.Suffix = i.Suffix
return ics
}
222 changes: 222 additions & 0 deletions ibc/types/proto/proofs.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
syntax = "proto3";

// This file is a clone from the github.com/cosmos/ics23 repo, it has been
// cloned to be compiled with protoc generic. As such it will produce valid
// protobuf messages that can be serialised and cloned

option go_package = "github.com/pokt-network/pocket/ibc/types";

enum HashOp {
// NO_HASH is the default if no data passed. Note this is an illegal argument some places.
NO_HASH = 0;
SHA256 = 1;
SHA512 = 2;
KECCAK = 3;
RIPEMD160 = 4;
BITCOIN = 5; // ripemd160(sha256(x))
SHA512_256 = 6;
}

// LengthOp defines how to process the key and value of the LeafOp
// to include length information. After encoding the length with the given
// algorithm, the length will be prepended to the key and value bytes.
// (Each one with it's own encoded length)
enum LengthOp {
// NO_PREFIX don't include any length info
NO_PREFIX = 0;
// VAR_PROTO uses protobuf (and go-amino) varint encoding of the length
VAR_PROTO = 1;
// VAR_RLP uses rlp int encoding of the length
VAR_RLP = 2;
// FIXED32_BIG uses big-endian encoding of the length as a 32 bit integer
FIXED32_BIG = 3;
// FIXED32_LITTLE uses little-endian encoding of the length as a 32 bit integer
FIXED32_LITTLE = 4;
// FIXED64_BIG uses big-endian encoding of the length as a 64 bit integer
FIXED64_BIG = 5;
// FIXED64_LITTLE uses little-endian encoding of the length as a 64 bit integer
FIXED64_LITTLE = 6;
// REQUIRE_32_BYTES is like NONE, but will fail if the input is not exactly 32 bytes (sha256 output)
REQUIRE_32_BYTES = 7;
// REQUIRE_64_BYTES is like NONE, but will fail if the input is not exactly 64 bytes (sha512 output)
REQUIRE_64_BYTES = 8;
}

// ExistenceProof takes a key and a value and a set of steps to perform on it.
// The result of peforming all these steps will provide a "root hash", which can
// be compared to the value in a header.
//
// Since it is computationally infeasible to produce a hash collission for any of the used
// cryptographic hash functions, if someone can provide a series of operations to transform
// a given key and value into a root hash that matches some trusted root, these key and values
// must be in the referenced merkle tree.
//
// The only possible issue is maliablity in LeafOp, such as providing extra prefix data,
// which should be controlled by a spec. Eg. with lengthOp as NONE,
// prefix = FOO, key = BAR, value = CHOICE
// and
// prefix = F, key = OOBAR, value = CHOICE
// would produce the same value.
//
// With LengthOp this is tricker but not impossible. Which is why the "leafPrefixEqual" field
// in the ProofSpec is valuable to prevent this mutability. And why all trees should
// length-prefix the data before hashing it.
message ExistenceProof {
bytes key = 1;
bytes value = 2;
LeafOp leaf = 3;
repeated InnerOp path = 4;
}

// NonExistenceProof takes a proof of two neighbors, one left of the desired key,
// one right of the desired key. If both proofs are valid AND they are neighbors,
// then there is no valid proof for the given key.
message NonExistenceProof {
bytes key = 1; // TODO: remove this as unnecessary??? we prove a range
ExistenceProof left = 2;
ExistenceProof right = 3;
}

// CommitmentProof is either an ExistenceProof or a NonExistenceProof, or a Batch of such messages
message CommitmentProof {
oneof proof {
ExistenceProof exist = 1;
NonExistenceProof nonexist = 2;
BatchProof batch = 3;
CompressedBatchProof compressed = 4;
}
}

// LeafOp represents the raw key-value data we wish to prove, and
// must be flexible to represent the internal transformation from
// the original key-value pairs into the basis hash, for many existing
// merkle trees.
//
// key and value are passed in. So that the signature of this operation is:
// leafOp(key, value) -> output
//
// To process this, first prehash the keys and values if needed (ANY means no hash in this case):
// hkey = prehashKey(key)
// hvalue = prehashValue(value)
//
// Then combine the bytes, and hash it
// output = hash(prefix || length(hkey) || hkey || length(hvalue) || hvalue)
message LeafOp {
HashOp hash = 1;
HashOp prehash_key = 2;
HashOp prehash_value = 3;
LengthOp length = 4;
// prefix is a fixed bytes that may optionally be included at the beginning to differentiate
// a leaf node from an inner node.
bytes prefix = 5;
}

// InnerOp represents a merkle-proof step that is not a leaf.
// It represents concatenating two children and hashing them to provide the next result.
//
// The result of the previous step is passed in, so the signature of this op is:
// innerOp(child) -> output
//
// The result of applying InnerOp should be:
// output = op.hash(op.prefix || child || op.suffix)
//
// where the || operator is concatenation of binary data,
// and child is the result of hashing all the tree below this step.
//
// Any special data, like prepending child with the length, or prepending the entire operation with
// some value to differentiate from leaf nodes, should be included in prefix and suffix.
// If either of prefix or suffix is empty, we just treat it as an empty string
message InnerOp {
HashOp hash = 1;
bytes prefix = 2;
bytes suffix = 3;
}

// ProofSpec defines what the expected parameters are for a given proof type.
// This can be stored in the client and used to validate any incoming proofs.
//
// verify(ProofSpec, Proof) -> Proof | Error
//
// As demonstrated in tests, if we don't fix the algorithm used to calculate the
// LeafHash for a given tree, there are many possible key-value pairs that can
// generate a given hash (by interpretting the preimage differently).
// We need this for proper security, requires client knows a priori what
// tree format server uses. But not in code, rather a configuration object.
message ProofSpec {
// any field in the ExistenceProof must be the same as in this spec.
// except Prefix, which is just the first bytes of prefix (spec can be longer)
LeafOp leaf_spec = 1;
InnerSpec inner_spec = 2;
// max_depth (if > 0) is the maximum number of InnerOps allowed (mainly for fixed-depth tries)
int32 max_depth = 3;
// min_depth (if > 0) is the minimum number of InnerOps allowed (mainly for fixed-depth tries)
int32 min_depth = 4;
// prehash_key_before_comparison is a flag that indicates whether to use the
// prehash_key specified by LeafOp to compare lexical ordering of keys for
// non-existence proofs.
bool prehash_key_before_comparison = 5;
}

// InnerSpec contains all store-specific structure info to determine if two proofs from a
// given store are neighbors.
//
// This enables:
//
// isLeftMost(spec: InnerSpec, op: InnerOp)
// isRightMost(spec: InnerSpec, op: InnerOp)
// isLeftNeighbor(spec: InnerSpec, left: InnerOp, right: InnerOp)
message InnerSpec {
// Child order is the ordering of the children node, must count from 0
// iavl tree is [0, 1] (left then right)
// merk is [0, 2, 1] (left, right, here)
repeated int32 child_order = 1;
int32 child_size = 2;
int32 min_prefix_length = 3;
int32 max_prefix_length = 4;
// empty child is the prehash image that is used when one child is nil (eg. 20 bytes of 0)
bytes empty_child = 5;
// hash is the algorithm that must be used for each InnerOp
HashOp hash = 6;
}

// BatchProof is a group of multiple proof types than can be compressed
message BatchProof {
repeated BatchEntry entries = 1;
}

// Use BatchEntry not CommitmentProof, to avoid recursion
message BatchEntry {
oneof proof {
ExistenceProof exist = 1;
NonExistenceProof nonexist = 2;
}
}

// ====== all items here are compressed forms =======

message CompressedBatchProof {
repeated CompressedBatchEntry entries = 1;
repeated InnerOp lookup_inners = 2;
}

// Use BatchEntry not CommitmentProof, to avoid recursion
message CompressedBatchEntry {
oneof proof {
CompressedExistenceProof exist = 1;
CompressedNonExistenceProof nonexist = 2;
}
}

message CompressedExistenceProof {
bytes key = 1;
bytes value = 2;
LeafOp leaf = 3;
// these are indexes into the lookup_inners table in CompressedBatchProof
repeated int32 path = 4;
}

message CompressedNonExistenceProof {
bytes key = 1; // TODO: remove this as unnecessary??? we prove a range
CompressedExistenceProof left = 2;
CompressedExistenceProof right = 3;
}
1 change: 1 addition & 0 deletions internal/ics23
Submodule ics23 added at daa176

0 comments on commit 9f63e68

Please sign in to comment.