Skip to content

Commit d7e42b2

Browse files
authored
Merge pull request #204 from RGB-WG/vesper
Audit and refactoring of commitments workflows. Vesper support
2 parents 99c5318 + d4f96c9 commit d7e42b2

29 files changed

+2780
-1139
lines changed

Diff for: Cargo.lock

+122-121
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: Cargo.toml

+9-4
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ name = "rgbcore-stl"
2222
required-features = ["stl"]
2323

2424
[dependencies]
25-
amplify = { version = "~4.5.0", features = ["rand"] }
26-
strict_encoding = "~2.6.2"
27-
strict_types = "~1.6.3"
28-
aluvm = { version = "~0.11.0-beta.2", features = ["std"] }
25+
amplify = { version = "~4.6.0", features = ["rand"] }
26+
strict_encoding = "~2.7.0-beta.1"
27+
strict_types = "~2.7.0-beta.1"
28+
aluvm = { version = "~0.11.0-beta.3", features = ["std"] }
2929
commit_verify = { version = "~0.11.0-beta.3", features = ["rand", "derive"] }
3030
single_use_seals = "~0.11.0-beta.3"
3131
bp-core = { version = "~0.11.0-beta.3" }
@@ -62,4 +62,9 @@ wasm-bindgen-test = "0.3"
6262
features = [ "all" ]
6363

6464
[patch.crates-io]
65+
strict_types = { git = "https://github.com/strict-types/strict-types", branch = "vesper" }
66+
commit_verify = { git = "https://github.com/LNP-BP/client_side_validation", branch = "layouts" }
67+
bp-consensus = { git = "https://github.com/BP-WG/bp-core", branch = "v0.11" }
68+
bp-dbc = { git = "https://github.com/BP-WG/bp-core", branch = "v0.11" }
69+
bp-seals = { git = "https://github.com/BP-WG/bp-core", branch = "v0.11" }
6570
bp-core = { git = "https://github.com/BP-WG/bp-core", branch = "v0.11" }

Diff for: doc/Commitments.md

+275
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
# RGB consensus commitments
2+
3+
RGB commits to client-side validated data using dedicated serialization
4+
mechanism, implemented via `CommitEncode` trait. Depending on the specific data,
5+
the mechanism can be is partially or completely different from strict
6+
serialization, used for data storage. For instance, all data which may be
7+
confidential must be concealed, such that parties having no access to the
8+
original non-confidential values still be able to generate the same
9+
deterministic commitment value and verify single-use seals.
10+
11+
Any final consensus commitment is a SHA256 tagged hash. The tagging is performed
12+
according to BIP-340, when a commitment-specific fixed ASCII string value is
13+
first hashed with a single SHA256 hash, and the resulting 32 bytes are feed into
14+
a new SHA256 hasher twice before any actual data.
15+
16+
17+
## Generating commitment id
18+
19+
The commitment mechanism uses traits from [`commit_verify`] crate, specifically
20+
its `id.rs`and `merkle.rs` modules.
21+
22+
### `CommitEncode` trait
23+
24+
It is the main trait which must be implemented for each type requiring a
25+
dedicated commitment id.
26+
27+
The trait implementation can be done either with derive macro
28+
`#[derive(CommitEncode)]` or by providing a manual implementation.
29+
30+
The derive macro takes two arguments: `strategy` and `id`:
31+
- `id` must specify a resulting commitment id type, i.e. type wrapping 32-byte
32+
tagged SHA256 hash, implementing `CommitmentId` trait (the implementation
33+
provides a tag for the hasher - see trait details below). The type must also
34+
provide a converting constructor from `coommit_verify::Sha256` hasher.
35+
- `strategy` specifies a workflow used to feed the type data to the SHA256
36+
tagged hasher:
37+
* `strategy = strict`: the hasher receive strict-serialized type;
38+
* `strategy = conceal`: the type data are first concealed, and only then are
39+
strict-serialized into the hasher.
40+
41+
Manual `CommitEncode` implementation must be provided only when the commitment
42+
procedure is custom and can't be implemented using any of the strategies, for
43+
instance when a collection must be merklized (see on merklization below).
44+
45+
NB: you should never call methods of `CommitEncode` trait directly, and instead
46+
use `CommitId` trait, which is automatically implemented for
47+
48+
### `CommitmentId` trait
49+
50+
Each consensus commitment must have a dedicated Rust newtype, which wraps over
51+
inner `Bytes32` - a 32-byte resulting tagged hash value. The type is marked as
52+
a consensus commitment by implementing `CommitmentId` trait for it, which
53+
requires to provide a tag string values for the tagged hash.
54+
55+
The hash tags are defined using URN strings in form of
56+
`urn:<org>:<protocol>:<data>#<date>`, where `<org>` stands for the organization,
57+
`<protocol>` is the name of the protocol, `<data>` is the data type name
58+
producing the commitment, and `<date>` is a `YYYY-MM-DD` string for the latest
59+
revision of the commitment layout.
60+
61+
### `CommitId` trait
62+
63+
This trait is automatically implemented for all types which have `CommitEncode`
64+
implementation. You can't implement this trait manually and just need to call
65+
its `CommitId::commit_id()` method to produce the final commitment (i.e. bytes
66+
of the tagged hash in form of `CommitmentId`-implementing type).
67+
68+
The trait also provides `CommitId::commitment_layout()` method, which can be
69+
used for automatically generating the documentation on the commitment workflow.
70+
71+
## Merklization procedure
72+
73+
Merlization procedure uses traits and data types from `merkle.rs` module of
74+
`commit_verify` crate. It commits to the tree parameters, such as number of
75+
elements, depth of the tree, as well as depth of each node; and uses tagged
76+
SHA256, like the rest of the commitment procedures used here.
77+
78+
The main data type, related to the merklization, is `MerkleHash`: it is a tagged
79+
hash (using `urn:ubideco:merkle:node#2024-01-31` tag) representing node at any
80+
position of the tree: leaves, branch nodes and merkle tree root. `MerkleHash`
81+
can be produced in the following ways:
82+
- as a result of merklziation procedure, when it represents Merkle tree root;
83+
- as a root of empty Merkle tree (i.e. collection having 0 elements), by calling
84+
`MerkleHash::void(0u8, 0u32)`,
85+
- as a Merkle leaf, by implementing `CommitEncode` on some type and setting
86+
commitment id to be `MerkleHash`.
87+
88+
In all of the above cases the hash commits to the tree parameters, which makes
89+
it safe to use the same type for leaves, branches and root nodes. Specifically,
90+
it uses an intermediate structure `MerkleNode`, which is filled with information
91+
on (see [`MerkleNode.vesper`](../stl/MerkleNode.vesper) for details):
92+
- type of node branching (no branches, one branch or two branches),
93+
- depth of the node, as 8-bit unsigned integer,
94+
- width of the tree at its base, as a 256-bit LE unsigned integer,
95+
- node hashes of the branches; if one or both branches are absent, they are
96+
replaced with 32 bytes of repeated 0xFF value.
97+
98+
A collection in form of a list (Rust `Vec`) or an ordered set of unique
99+
non-repeating items (Rust `BTreeSet`), if wrapped into a confinement (i.e. has
100+
type-defined bounds on the minimum or maximum number of items) can be
101+
automatically merklized when passed as an argument to `MerkleHash::merklize()`
102+
call. The API puts the following requirements on the collection: either
103+
- maximum number of elements must be either 0xFF or 0xFFFF **and** each
104+
collection element must implement `CommitEncode` trait with target id set to
105+
`MerkleHash`,
106+
- or there is a manual implementation of `MerkleLeaves` trait.
107+
108+
```mermaid
109+
flowchart BT
110+
subgraph Merklization
111+
direction LR
112+
subgraph MerkleNode
113+
branching
114+
depth
115+
width
116+
node1
117+
node2
118+
end
119+
MerkleNode -- encode to\ntagged hasher --> MerkleHash
120+
end
121+
MerkleHash ---> MerkleNode
122+
MerkleHash === Root
123+
Leaf -- commit_id ----> MerkleHash
124+
```
125+
126+
127+
## Specific RGB consensus commitments
128+
129+
Currently, RGB has three consensus commitments: schema, operation and bundle.
130+
Operation commitment for genesis has a second representation, named contract id,
131+
which uses reversed-byte encoding and a special string serialization, but is
132+
generated with the same procedure as the operation commitment.
133+
134+
The commitment ids can be generated with either type-specific methods
135+
(`schema_id()` for schema, `bundle_id()` for state transition bundle and
136+
`id()` for any operation) or `CommitId::commit_id()` method, which must provide
137+
the equal result.
138+
139+
Here are more details on each type of the commitments:
140+
141+
| Commitment ID | Produced by | Procedure | Tag URN suffix(1) |
142+
|----------------------|--------------------------------------|------------------------------------------------------------------------------------------------|-------------------------------|
143+
| `SchemaID` | `RootSchema`, `SubSchema` | strict serialization | `rgb:schema#2024-02-03` |
144+
| `OpId`, `ContractId` | `Genesis`, `Transition`, `Extension` | nested commitments with concealing, merklization etc via intermediate `OpCommitment` structure | `rgb:operation#2024-02-03` |
145+
| `BundleId` | `TransitionBundle` | conceal and partial strict serialization | `rgb:bundle#2024-02-03` |
146+
| `SecretSeal` | `BlindSeal` | strict serialization | `seals:secret#2024-02-03` |
147+
| `ConcealedData` | `RevealedData` | strict serialization | `rgb:state-data#2024-02-12` |
148+
| `ConcealedAttach` | `RevealedAttach` | strict serialization | `rgb:state-attach#2024-02-12` |
149+
150+
(1): "URN suffix" is a part which follows "urn:lnp-bp:" prefix.
151+
152+
The last three commitments coincide to be a concealed form of BP seals and RGB state.
153+
These commitments produced by either calling `commit_id` or `conceal` methods of
154+
revealed seal (`BlindSeal`) and state types (`RevealedData` and `RevealedAttach`).
155+
156+
Additionally to these types there are two other commitment ids used internally
157+
by merklization and strict encoding procedures: `MerkleHash` (discussed in the
158+
Merklization section above) and `StrictHash` from `commit_verify` crate:
159+
160+
| Commitment ID | Tag URN suffix |
161+
|-------------------|--------------------------------------------------|
162+
| `MerkleHash` | `urn:ubideco:merkle:node#2024-01-31` |
163+
| `StrictHash` | `urn:ubideco:strict-types:value-hash#2024-02-10` |
164+
| `mpc::Commitment` | `urn:ubideco:mpc:commitment#2024-01-31` |
165+
166+
`StrictHash` can be produced as a result of serialization of any
167+
strict-encodable data; for instance, it is used in compactifying collections
168+
into a single hash field in the process of computing operation ids (described
169+
below).
170+
171+
Finally, in `commit_verify::mpc`, multi-protocol commitment protocol
172+
implementation, we have a type named `mpc::Commitment`, which is a commitment
173+
to a root of the MPC tree (i.e. the tree's root `MerkleHash` is tag-hashed once
174+
again to produce the final commitment value).
175+
176+
177+
### Schema ID
178+
179+
Schema id, represented by `SchemaId` data type, is produced from `Schema` type
180+
via strict serialization of all the schema data using
181+
`urn:lnp-bp:rgb:schema#2024-02-03` hash tag. No conceal or merklization
182+
procedures are applied; i.e. the commitment id is the same as hashing serialized
183+
schema with the given tag. The full description of how schema data are
184+
serialized into the hasher can be found in [`Schema.vesper`](
185+
../stl/Schema.vesper) file, which is automatically generated from the RGB rust
186+
code.
187+
188+
### Operation ID and Contract ID
189+
190+
Operation id is represented by a `OpId` type and produced for `Genesis`,
191+
`Transition` and `Extension` types via custom algorithm, which first creates a
192+
dedicated `OpCommitment` structure, and strict-serializes it to hasher,
193+
initialized with `urn:lnp-bp:rgb:operation#2024-02-03` hash tag.
194+
195+
The `OpCommitment` by itself consists of a sub-commitments to blocks of the
196+
operation data, where each sub-commitment is created with a custom procedure.
197+
For instance, operation global state, inputs and assignments are merklized,
198+
such that a succinct proofs of some specific state or input inclusion in RGB
199+
operation can be produced and used in smart contracts. Additionally to that,
200+
assignments are concealed before the merklization, and range proofs are
201+
removed from the commitment, such that an aggregation of the historical proofs
202+
can be applied without changing the operation ids.
203+
204+
To ensure succinctness, other types of collections, such as redeemed and
205+
defined valencies and list of alternate layer 1 in genesis are not merklized
206+
and strict-serialized producing `StrictHash`, which participates in the final
207+
`OpCommitment` structure.
208+
209+
```mermaid
210+
flowchart LR
211+
subgraph "Common data"
212+
Ffv --> OpCommitment
213+
TypeCommitment --> OpCommitment
214+
Metadata -- StrictHash --> OpCommitment
215+
Globals -- Merklize --> OpCommitment
216+
Inputs -- Merklize --> OpCommitment
217+
Assignments -- "Conceal\n + Merklize" --> OpCommitment
218+
Redeemed -- StrictHash --> OpCommitment
219+
Valencies -- StrictHash --> OpCommitment
220+
end
221+
222+
subgraph "Genesis"
223+
schemaId --> BaseCommitment
224+
testnet --> BaseCommitment
225+
altLayers1 -- StrictHash --> BaseCommitment
226+
end
227+
228+
subgraph "Transition"
229+
tcid[contractId] --> TypeCommitment
230+
transitionType --> TypeCommitment
231+
end
232+
233+
subgraph "Extension"
234+
ecid[contractId] --> TypeCommitment
235+
extensionType --> TypeCommitment
236+
end
237+
238+
BaseCommitment --> TypeCommitment
239+
240+
OpCommitment -- hash --> OpId
241+
OpId -- "reverse bytes\n(genesis only)" --> ContractId
242+
```
243+
244+
Additionally to `OpId`, genesis produces `ContractId`, which is made out of the
245+
genesis `OpId` by reversing byte order and using Base58 encoding.
246+
247+
### Bundle ID
248+
249+
Bundle id is a unique identifier of state transition bundle, directly used in
250+
constructing multi-protocol commitment tree. Bundle id commits to operation ids
251+
for the participating state transitions and maps of the witness transaction
252+
input to the operation ids. For this purpose, the commitment is created by
253+
strict-encoding `input_map` field of `TransitionBundle` into the hasher,
254+
initialized with tag `urn:lnp-bp:rgb:bundle#2024-02-03`. Input map is serialized
255+
first as a 16-bit little-endian integer specifying the number of the items in
256+
the map, followed by the sequence of pairs of input number (32-bit LE value)
257+
and `OpId` (32-bytes).
258+
259+
```mermaid
260+
flowchart TD
261+
subgraph Discarded
262+
id((" "))
263+
end
264+
265+
subgraph TransitionBundle
266+
inputMap
267+
knownTransitions
268+
end
269+
270+
inputMap -- encode \n hash --> BundleId
271+
knownTransitions --x Discarded
272+
```
273+
274+
275+
[`commit_verify`]: https://docs.rs/commit_verify

0 commit comments

Comments
 (0)