-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: copy protobuf compiler tool from mono repo
This patch copies the cnidarium related parts of the penumbra protobuf compiler tool [1]. Having this tool as part of the cnidarium repo is necessary to update to newer versions of ibc-types, ibc-proto, prost, tonic, and ics23 because these are also targeted by the code generator. This patch contains no logic (such as CI workflows) to maintain parity with the penumbra mono repo. This work is left to a follow-up PR. 1: https://github.com/penumbra-zone/penumbra/tree/159f48489bc8deb654551402fdce61108409c524/tools/proto-compiler
- Loading branch information
1 parent
ac5ae7d
commit 6e33eb5
Showing
8 changed files
with
615 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
syntax = "proto3"; | ||
|
||
package penumbra.cnidarium.v1; | ||
|
||
import "ibc/core/commitment/v1/commitment.proto"; | ||
|
||
service QueryService { | ||
// General-purpose key-value state query API, that can be used to query | ||
// arbitrary keys in the JMT storage. | ||
rpc KeyValue(KeyValueRequest) returns (KeyValueResponse); | ||
|
||
// General-purpose key-value state query API, that can be used to query | ||
// arbitrary keys in the non-verifiable storage. | ||
rpc NonVerifiableKeyValue(NonVerifiableKeyValueRequest) returns (NonVerifiableKeyValueResponse); | ||
|
||
// General-purpose prefixed key-value state query API, that can be used to query | ||
// arbitrary prefixes in the JMT storage. | ||
rpc PrefixValue(PrefixValueRequest) returns (stream PrefixValueResponse); | ||
|
||
// Subscribes to a stream of key-value updates, with regex filtering on keys. | ||
rpc Watch(WatchRequest) returns (stream WatchResponse); | ||
} | ||
|
||
// Performs a key-value query against the nonverifiable storage, | ||
// using a byte-encoded key. | ||
message NonVerifiableKeyValueRequest { | ||
message Key { | ||
bytes inner = 1; | ||
} | ||
|
||
Key key = 1; | ||
} | ||
|
||
message NonVerifiableKeyValueResponse { | ||
message Value { | ||
bytes value = 1; | ||
} | ||
// The value corresponding to the specified key, if it was found. | ||
Value value = 1; | ||
} | ||
|
||
// Performs a key-value query against the JMT, either by key or by key hash. | ||
// | ||
// Proofs are only supported by key. | ||
message KeyValueRequest { | ||
// If set, the key to fetch from storage. | ||
string key = 2; | ||
// whether to return a proof | ||
bool proof = 3; | ||
} | ||
|
||
message KeyValueResponse { | ||
message Value { | ||
bytes value = 1; | ||
} | ||
// The value corresponding to the specified key, if it was found. | ||
Value value = 1; | ||
// A proof of existence or non-existence. | ||
.ibc.core.commitment.v1.MerkleProof proof = 2; | ||
} | ||
|
||
// Performs a prefixed key-value query, by string prefix. | ||
message PrefixValueRequest { | ||
// The prefix to fetch subkeys from storage. | ||
string prefix = 2; | ||
} | ||
|
||
message PrefixValueResponse { | ||
string key = 1; | ||
bytes value = 2; | ||
} | ||
|
||
// Requests a stream of new key-value pairs that have been committed to the state. | ||
message WatchRequest { | ||
// A regex for keys in the verifiable storage. | ||
// | ||
// Only key-value updates whose keys match this regex will be returned. | ||
// Note that the empty string matches all keys. | ||
// To exclude all keys, use the regex "$^", which matches no strings. | ||
string key_regex = 1; | ||
// A regex for keys in the nonverifiable storage. | ||
// | ||
// Only key-value updates whose keys match this regex will be returned. | ||
// Note that the empty string matches all keys. | ||
// To exclude all keys, use the regex "$^", which matches no strings. | ||
string nv_key_regex = 2; | ||
} | ||
|
||
// A key-value pair that has been committed to the state. | ||
message WatchResponse { | ||
// Elements of the verifiable storage have string keys. | ||
message KeyValue { | ||
string key = 1; | ||
bytes value = 2; | ||
// If set to true, the key-value pair was deleted. | ||
// This allows distinguishing a deleted key-value pair from a key-value pair whose value is empty. | ||
bool deleted = 3; | ||
} | ||
// Elements of the nonverifiable storage have byte keys. | ||
message NvKeyValue { | ||
bytes key = 1; | ||
bytes value = 2; | ||
// If set to true, the key-value pair was deleted. | ||
// This allows distinguishing a deleted key-value pair from a key-value pair whose value is empty. | ||
bool deleted = 3; | ||
} | ||
|
||
// The state version the key-value pair was committed at. | ||
uint64 version = 1; | ||
|
||
// The entry that was committed. | ||
oneof entry { | ||
KeyValue kv = 5; | ||
NvKeyValue nv_kv = 6; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,234 @@ | ||
syntax = "proto3"; | ||
|
||
package cosmos.ics23.v1; | ||
|
||
option go_package = "github.com/cosmos/ics23/go;ics23"; | ||
|
||
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; | ||
} | ||
|
||
/* | ||
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; | ||
} |
Oops, something went wrong.