diff --git a/docs/spec/ibc/protobuf/.gitignore b/docs/spec/ibc/protobuf/.gitignore new file mode 100644 index 0000000000..c61a5e8b01 --- /dev/null +++ b/docs/spec/ibc/protobuf/.gitignore @@ -0,0 +1 @@ +*.pb.go diff --git a/docs/spec/ibc/protobuf/Makefile b/docs/spec/ibc/protobuf/Makefile new file mode 100644 index 0000000000..a1a2ef9e11 --- /dev/null +++ b/docs/spec/ibc/protobuf/Makefile @@ -0,0 +1,7 @@ +.PHONEY: proto test + +proto: + protoc --gogo_out=. *.proto + +test: proto + go install . diff --git a/docs/spec/ibc/protobuf/merkle.proto b/docs/spec/ibc/protobuf/merkle.proto new file mode 100644 index 0000000000..866e0a8d08 --- /dev/null +++ b/docs/spec/ibc/protobuf/merkle.proto @@ -0,0 +1,79 @@ +syntax = "proto3"; + +package protobuf; + + +// HashOp is the hashing algorithm we use at each level +enum HashOp { + RIPEMD160 = 0; + SHA224 = 1; + SHA256 = 2; + SHA384 = 3; + SHA512 = 4; + SHA3_224 = 5; + SHA3_256 = 6; + SHA3_384 = 7; + SHA3_512 = 8; + SHA256_X2 = 9; +}; + +// Op represents one hash in a chain of hashes. +// An operation takes the output of the last level and returns +// a hash for the next level: +// Op(last) => Operation(prefix + last + sufix) +// +// A simple left/right hash would simply set prefix=left or +// suffix=right and leave the other blank. However, one could +// also represent the a Patricia trie proof by setting +// prefix to the rlp encoding of all nodes before the branch +// we select, and suffix to all those after the one we select. +message Op { + bytes prefix = 1; + bytes suffix = 2; + HashOp op = 3; +} + +// Data is the end value stored, +// used to generate the initial hash store +message Data { + // optional prefix allows second preimage resistance + bytes prefix = 1; + bytes key = 2; + bytes value = 3; + HashOp op = 4; + // If it is KeyValue, this is the data we want + // If it is SubTree, key is name of the tree, + // value is root hash + enum DataType { + KeyValue = 0; + SubTree = 1; + } + DataType dataType = 5; +} + +// Branch will hash data and then pass it through operations +// from first to last in order to calculate the root node. +// +// Visualize Branch as representing the data closest to +// root as the first item, and the leaf as the last item. +message Branch { + // if either are non-empty, enforce this prefix on all + // leaf/inner nodes to provide second preimage resistence + bytes prefixLeaf = 1; + bytes prefixInner = 2; + // this is the data to get the original hash, + // and a set of operations to calculate the root hash + Data data = 3; + repeated Op operations = 4; +} + +message MerkleProof { + // TODO: root, height, chain_id, etc... + + // branches start from the value, and then may + // include multiple subtree branches to embed it + // + // The first branch must have dataType KeyValue + // Following branches must have dataType SubTree + repeated Branch branches = 1; +} diff --git a/docs/spec/ibc/protobuf/messages.proto b/docs/spec/ibc/protobuf/messages.proto new file mode 100644 index 0000000000..035c962e5b --- /dev/null +++ b/docs/spec/ibc/protobuf/messages.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; + +package protobuf; + +import "merkle.proto"; + + +// IBCPacket sends a proven key/value pair from an IBCQueue. +// Depending on the type of message, we require a certain type +// of key (MessageKey at a given height, or StateKey). +// +// Includes src_chain and src_height to look up the proper +// header to verify the merkle proof. +message IBCPacket { + // chain id it is coming from + string src_chain = 1; + // height for the header the proof belongs to + uint64 src_height = 2; + // the message type, which determines what key/value mean + enum MsgType { + RECEIVE = 0; + RECEIPT = 1; + TIMEOUT = 2; + CLEANUP = 3; + } + MsgType msgType = 3; + // the proof of the message, includes key and value + MerkleProof proof = 6; +} diff --git a/docs/spec/ibc/protobuf/queue.proto b/docs/spec/ibc/protobuf/queue.proto new file mode 100644 index 0000000000..5d1c0ad33c --- /dev/null +++ b/docs/spec/ibc/protobuf/queue.proto @@ -0,0 +1,57 @@ +syntax = "proto3"; + +package protobuf; + +import "google/protobuf/timestamp.proto"; + +message QueueName { + // chain_id is which chain this queue is + // associated with + string chain_id = 1; + enum Purpose { + SEND = 0; + RECEIPT = 1; + } + Purpose purpose = 2; +} + +// StateKey is a key for the head/tail of a given queue +message StateKey { + QueueName queue = 1; + // both encode into one byte with varint encoding + // never clash with 8 byte message indexes + enum State { + HEAD = 0; + TAIL = 0x7f; + } + State state = 2; +} + +// StateValue is the type stored under a StateKey +message StateValue { + fixed64 index = 1; +} + +// MessageKey is the key for message *index* in a given queue +message MessageKey { + QueueName queue = 1; + fixed64 index = 2; +} + +// SendValue is stored under a MessageKey in the SEND queue +message SendValue { + uint64 maxHeight = 1; + google.protobuf.Timestamp maxTime = 2; + // use kind instead of type to avoid keyword conflict + bytes kind = 3; + bytes data = 4; +} + +// ReceiptValue is stored under a MessageKey in the RECEIPT queue +message ReceiptValue { + // 0 is success, others are application-defined errors + int32 errorCode = 1; + // contains result on success, optional info on error + bytes data = 2; +} +