Skip to content

Commit 729c652

Browse files
authored
Merge pull request #200 from RGB-WG/v0.11
API sanation for state types
2 parents c627404 + 2b59903 commit 729c652

File tree

9 files changed

+173
-66
lines changed

9 files changed

+173
-66
lines changed

src/contract/attachment.rs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use std::str::FromStr;
2424

2525
use amplify::{ByteArray, Bytes32};
2626
use baid58::{Baid58ParseError, Chunking, FromBaid58, ToBaid58, CHUNKING_32};
27-
use bp::secp256k1::rand::{thread_rng, RngCore};
27+
use bp::secp256k1::rand::{random, Rng, RngCore};
2828
use commit_verify::{CommitVerify, Conceal, StrictEncodedProtocol};
2929
use strict_encoding::StrictEncode;
3030

@@ -83,13 +83,28 @@ pub struct RevealedAttach {
8383
}
8484

8585
impl RevealedAttach {
86-
/// Creates new revealed attachment for the attachment id and MIME type.
87-
/// Uses `thread_rng` to initialize [`RevealedAttach::salt`].
88-
pub fn new(id: AttachId, media_type: MediaType) -> Self {
86+
/// Constructs new state using the provided value using random blinding
87+
/// factor.
88+
pub fn new_random_salt(id: AttachId, media_type: impl Into<MediaType>) -> Self {
89+
Self::with_salt(id, media_type, random())
90+
}
91+
92+
/// Constructs new state using the provided value and random generator for
93+
/// creating blinding factor.
94+
pub fn with_rng<R: Rng + RngCore>(
95+
id: AttachId,
96+
media_type: impl Into<MediaType>,
97+
rng: &mut R,
98+
) -> Self {
99+
Self::with_salt(id, media_type, rng.next_u64())
100+
}
101+
102+
/// Convenience constructor.
103+
pub fn with_salt(id: AttachId, media_type: impl Into<MediaType>, salt: u64) -> Self {
89104
Self {
90105
id,
91-
media_type,
92-
salt: thread_rng().next_u64(),
106+
media_type: media_type.into(),
107+
salt,
93108
}
94109
}
95110
}

src/contract/contract.rs

Lines changed: 64 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ use amplify::hex;
3434
use strict_encoding::{StrictDecode, StrictDumb, StrictEncode};
3535

3636
use crate::{
37-
Assign, AssignmentType, Assignments, AssignmentsRef, ContractId, ExposedSeal, ExposedState,
38-
Extension, Genesis, GlobalStateType, OpId, Operation, RevealedAttach, RevealedData,
39-
RevealedValue, SchemaId, SubSchema, Transition, TypedAssigns, VoidState, WitnessAnchor,
40-
WitnessId, XChain, XOutputSeal, LIB_NAME_RGB,
37+
Assign, AssignmentType, Assignments, AssignmentsRef, ContractId, DataState, ExposedSeal,
38+
ExposedState, Extension, Genesis, GlobalStateType, OpId, Operation, RevealedAttach,
39+
RevealedData, RevealedValue, SchemaId, SubSchema, Transition, TypedAssigns, VoidState,
40+
WitnessAnchor, WitnessId, XChain, XOutputSeal, LIB_NAME_RGB,
4141
};
4242

4343
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)]
@@ -92,22 +92,59 @@ impl FromStr for Opout {
9292
}
9393
}
9494

95-
#[derive(Clone, Eq, Debug)]
95+
/// Trait used by contract state. Unlike [`ExposedState`] it doesn't allow
96+
/// concealment of the state, i.e. may contain incomplete data without blinding
97+
/// factors, asset tags etc.
98+
pub trait KnownState: Debug + StrictDumb + StrictEncode + StrictDecode + Eq + Clone {}
99+
impl<S: ExposedState> KnownState for S {}
100+
101+
impl KnownState for () {}
102+
impl KnownState for DataState {}
103+
104+
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Display, From)]
105+
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
106+
#[strict_type(lib = LIB_NAME_RGB, tags = custom)]
107+
#[cfg_attr(
108+
feature = "serde",
109+
derive(Serialize, Deserialize),
110+
serde(crate = "serde_crate", rename_all = "camelCase")
111+
)]
112+
pub enum AssignmentWitness {
113+
#[display("~")]
114+
#[strict_type(tag = 0, dumb)]
115+
Absent,
116+
117+
#[from]
118+
#[display(inner)]
119+
#[strict_type(tag = 1)]
120+
Present(WitnessId),
121+
}
122+
123+
impl From<Option<WitnessId>> for AssignmentWitness {
124+
fn from(value: Option<WitnessId>) -> Self {
125+
match value {
126+
None => AssignmentWitness::Absent,
127+
Some(id) => AssignmentWitness::Present(id),
128+
}
129+
}
130+
}
131+
132+
#[derive(Copy, Clone, Eq, Debug)]
96133
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
97134
#[strict_type(lib = LIB_NAME_RGB)]
98135
#[cfg_attr(
99136
feature = "serde",
100137
derive(Serialize, Deserialize),
101138
serde(crate = "serde_crate", rename_all = "camelCase")
102139
)]
103-
pub struct OutputAssignment<State: ExposedState> {
140+
pub struct OutputAssignment<State: KnownState> {
104141
pub opout: Opout,
105142
pub seal: XOutputSeal,
106143
pub state: State,
107-
pub witness: Option<WitnessId>,
144+
pub witness: AssignmentWitness,
108145
}
109146

110-
impl<State: ExposedState> PartialEq for OutputAssignment<State> {
147+
impl<State: KnownState> PartialEq for OutputAssignment<State> {
111148
fn eq(&self, other: &Self) -> bool {
112149
if self.opout == other.opout &&
113150
(self.seal != other.seal ||
@@ -127,11 +164,11 @@ impl<State: ExposedState> PartialEq for OutputAssignment<State> {
127164
}
128165
}
129166

130-
impl<State: ExposedState> PartialOrd for OutputAssignment<State> {
167+
impl<State: KnownState> PartialOrd for OutputAssignment<State> {
131168
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
132169
}
133170

134-
impl<State: ExposedState> Ord for OutputAssignment<State> {
171+
impl<State: KnownState> Ord for OutputAssignment<State> {
135172
fn cmp(&self, other: &Self) -> Ordering {
136173
if self == other {
137174
return Ordering::Equal;
@@ -140,7 +177,7 @@ impl<State: ExposedState> Ord for OutputAssignment<State> {
140177
}
141178
}
142179

143-
impl<State: ExposedState> OutputAssignment<State> {
180+
impl<State: KnownState> OutputAssignment<State> {
144181
/// # Panics
145182
///
146183
/// If the processing is done on invalid stash data, the seal is
@@ -160,7 +197,7 @@ impl<State: ExposedState> OutputAssignment<State> {
160197
match anchor's chain",
161198
),
162199
state,
163-
witness: Some(witness_id),
200+
witness: witness_id.into(),
164201
}
165202
}
166203

@@ -182,7 +219,17 @@ impl<State: ExposedState> OutputAssignment<State> {
182219
information since it comes from genesis or extension",
183220
),
184221
state,
185-
witness: None,
222+
witness: AssignmentWitness::Absent,
223+
}
224+
}
225+
226+
pub fn transmute<S: KnownState>(self) -> OutputAssignment<S>
227+
where S: From<State> {
228+
OutputAssignment {
229+
opout: self.opout,
230+
seal: self.seal,
231+
state: self.state.into(),
232+
witness: self.witness,
186233
}
187234
}
188235
}
@@ -234,11 +281,6 @@ impl GlobalOrd {
234281
}
235282
}
236283

237-
pub type RightsOutput = OutputAssignment<VoidState>;
238-
pub type FungibleOutput = OutputAssignment<RevealedValue>;
239-
pub type DataOutput = OutputAssignment<RevealedData>;
240-
pub type AttachOutput = OutputAssignment<RevealedAttach>;
241-
242284
/// Contract history accumulates raw data from the contract history, extracted
243285
/// from a series of consignments over the time. It does consensus ordering of
244286
/// the state data, but it doesn't interpret or validates the state against the
@@ -262,10 +304,10 @@ pub struct ContractHistory {
262304
contract_id: ContractId,
263305
#[getter(skip)]
264306
global: TinyOrdMap<GlobalStateType, LargeOrdMap<GlobalOrd, RevealedData>>,
265-
rights: LargeOrdSet<RightsOutput>,
266-
fungibles: LargeOrdSet<FungibleOutput>,
267-
data: LargeOrdSet<DataOutput>,
268-
attach: LargeOrdSet<AttachOutput>,
307+
rights: LargeOrdSet<OutputAssignment<VoidState>>,
308+
fungibles: LargeOrdSet<OutputAssignment<RevealedValue>>,
309+
data: LargeOrdSet<OutputAssignment<RevealedData>>,
310+
attach: LargeOrdSet<OutputAssignment<RevealedAttach>>,
269311
}
270312

271313
impl ContractHistory {

src/contract/data.rs

Lines changed: 71 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,15 @@
2020
// See the License for the specific language governing permissions and
2121
// limitations under the License.
2222

23-
use core::fmt::{self, Debug, Display, Formatter};
23+
use core::fmt::{self, Debug, Formatter};
24+
use std::cmp::Ordering;
25+
use std::io::Write;
2426

25-
use amplify::confinement::SmallVec;
27+
use amplify::confinement::SmallBlob;
2628
use amplify::hex::ToHex;
2729
use amplify::{Bytes32, Wrapper};
28-
use commit_verify::{CommitVerify, Conceal, StrictEncodedProtocol};
30+
use bp::secp256k1::rand::{random, Rng, RngCore};
31+
use commit_verify::{CommitEncode, CommitVerify, Conceal, StrictEncodedProtocol};
2932
use strict_encoding::{StrictSerialize, StrictType};
3033

3134
use super::{ConfidentialState, ExposedState};
@@ -57,13 +60,49 @@ impl Conceal for VoidState {
5760
fn conceal(&self) -> Self::Concealed { *self }
5861
}
5962

60-
#[derive(Wrapper, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, From)]
61-
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
63+
#[derive(Wrapper, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash, From, Display, Default)]
64+
#[display(LowerHex)]
65+
#[wrapper(Deref, AsSlice, BorrowSlice, Hex)]
66+
#[derive(StrictType, StrictEncode, StrictDecode)]
6267
#[strict_type(lib = LIB_NAME_RGB)]
6368
#[derive(CommitEncode)]
64-
#[commit_encode(conceal)]
69+
#[commit_encode(strategy = strict)]
6570
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))]
66-
pub struct RevealedData(SmallVec<u8>);
71+
pub struct DataState(SmallBlob);
72+
impl StrictSerialize for DataState {}
73+
74+
impl From<RevealedData> for DataState {
75+
fn from(data: RevealedData) -> Self { data.value }
76+
}
77+
78+
#[derive(Clone, Eq, PartialEq, Hash)]
79+
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
80+
#[strict_type(lib = LIB_NAME_RGB)]
81+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))]
82+
pub struct RevealedData {
83+
pub value: DataState,
84+
pub salt: u128,
85+
}
86+
87+
impl RevealedData {
88+
/// Constructs new state using the provided value using random blinding
89+
/// factor.
90+
pub fn new_random_salt(value: impl Into<DataState>) -> Self { Self::with_salt(value, random()) }
91+
92+
/// Constructs new state using the provided value and random generator for
93+
/// creating blinding factor.
94+
pub fn with_rng<R: Rng + RngCore>(value: impl Into<DataState>, rng: &mut R) -> Self {
95+
Self::with_salt(value, rng.gen())
96+
}
97+
98+
/// Convenience constructor.
99+
pub fn with_salt(value: impl Into<DataState>, salt: u128) -> Self {
100+
Self {
101+
value: value.into(),
102+
salt,
103+
}
104+
}
105+
}
67106

68107
impl ExposedState for RevealedData {
69108
type Confidential = ConcealedData;
@@ -73,24 +112,39 @@ impl ExposedState for RevealedData {
73112

74113
impl Conceal for RevealedData {
75114
type Concealed = ConcealedData;
115+
76116
fn conceal(&self) -> Self::Concealed { ConcealedData::commit(self) }
77117
}
78118

79-
impl StrictSerialize for RevealedData {}
119+
impl CommitEncode for RevealedData {
120+
fn commit_encode(&self, e: &mut impl Write) {
121+
e.write_all(&self.value).ok();
122+
e.write_all(&self.salt.to_le_bytes()).ok();
123+
}
124+
}
80125

81-
impl Debug for RevealedData {
82-
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
83-
let val = match String::from_utf8(self.0.to_inner()) {
84-
Ok(s) => s,
85-
Err(_) => self.0.to_hex(),
86-
};
126+
impl PartialOrd for RevealedData {
127+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
128+
}
87129

88-
f.debug_tuple("RevealedData").field(&val).finish()
130+
impl Ord for RevealedData {
131+
fn cmp(&self, other: &Self) -> Ordering {
132+
match self.value.cmp(&other.value) {
133+
Ordering::Equal => self.salt.cmp(&other.salt),
134+
other => other,
135+
}
89136
}
90137
}
91138

92-
impl Display for RevealedData {
93-
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.write_str(&self.as_ref().to_hex()) }
139+
impl Debug for RevealedData {
140+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
141+
let val = String::from_utf8(self.value.to_vec()).unwrap_or_else(|_| self.value.to_hex());
142+
143+
f.debug_struct("RevealedData")
144+
.field("value", &val)
145+
.field("salt", &self.salt)
146+
.finish()
147+
}
94148
}
95149

96150
/// Confidential version of an structured state data.

src/contract/fungible.rs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ impl RevealedValue {
290290

291291
/// Constructs new state using the provided value and random generator for
292292
/// creating blinding factor.
293-
pub fn with_random_blinding<R: Rng + RngCore>(
293+
pub fn with_rng<R: Rng + RngCore>(
294294
value: impl Into<FungibleState>,
295295
rng: &mut R,
296296
tag: AssetTag,
@@ -531,7 +531,7 @@ mod test {
531531
fn commitments_determinism() {
532532
let tag = AssetTag::from_byte_array([1u8; 32]);
533533

534-
let value = RevealedValue::with_random_blinding(15, &mut thread_rng(), tag);
534+
let value = RevealedValue::with_rng(15, &mut thread_rng(), tag);
535535

536536
let generators = (0..10)
537537
.map(|_| {
@@ -548,15 +548,11 @@ mod test {
548548
let mut r = thread_rng();
549549
let tag = AssetTag::from_byte_array([1u8; 32]);
550550

551-
let a = PedersenCommitment::commit(&RevealedValue::with_random_blinding(15, &mut r, tag))
552-
.into_inner();
553-
let b = PedersenCommitment::commit(&RevealedValue::with_random_blinding(7, &mut r, tag))
554-
.into_inner();
551+
let a = PedersenCommitment::commit(&RevealedValue::with_rng(15, &mut r, tag)).into_inner();
552+
let b = PedersenCommitment::commit(&RevealedValue::with_rng(7, &mut r, tag)).into_inner();
555553

556-
let c = PedersenCommitment::commit(&RevealedValue::with_random_blinding(13, &mut r, tag))
557-
.into_inner();
558-
let d = PedersenCommitment::commit(&RevealedValue::with_random_blinding(9, &mut r, tag))
559-
.into_inner();
554+
let c = PedersenCommitment::commit(&RevealedValue::with_rng(13, &mut r, tag)).into_inner();
555+
let d = PedersenCommitment::commit(&RevealedValue::with_rng(9, &mut r, tag)).into_inner();
560556

561557
assert!(!secp256k1_zkp::verify_commitments_sum_to_equal(SECP256K1, &[a, b], &[c, d]))
562558
}

src/contract/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ pub use assignments::{
4242
pub use attachment::{AttachId, ConcealedAttach, RevealedAttach};
4343
pub use bundle::{BundleId, TransitionBundle, Vin};
4444
pub use contract::{
45-
AttachOutput, ContractHistory, ContractState, DataOutput, FungibleOutput, GlobalOrd, Opout,
46-
OpoutParseError, OutputAssignment, RightsOutput,
45+
AssignmentWitness, ContractHistory, ContractState, GlobalOrd, KnownState, Opout,
46+
OpoutParseError, OutputAssignment,
4747
};
48-
pub use data::{ConcealedData, RevealedData, VoidState};
48+
pub use data::{ConcealedData, DataState, RevealedData, VoidState};
4949
pub use fungible::{
5050
AssetTag, BlindingFactor, BlindingParseError, ConcealedValue, FungibleState,
5151
InvalidFieldElement, NoiseDumb, PedersenCommitment, RangeProof, RangeProofError, RevealedValue,

src/stl.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use crate::{AnchoredBundle, ContractState, Extension, Genesis, SubSchema, LIB_NA
3232

3333
/// Strict types id for the library providing data types for RGB consensus.
3434
pub const LIB_ID_RGB: &str =
35-
"urn:ubideco:stl:2PcZtrPrfQCu27qw8b4Wz4cEqUn2PpgSkDHwF4qVyyrq#russian-child-member";
35+
"urn:ubideco:stl:141hHBYBr2mzKyskZbRuwazYC9ki5x9ZrrzQHLbgBzx#oscar-rufus-tractor";
3636

3737
fn _rgb_core_stl() -> Result<TypeLib, CompileError> {
3838
LibBuilder::new(libname!(LIB_NAME_RGB), tiny_bset! {

src/validation/logic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ impl<Root: SchemaRoot> Schema<Root> {
260260
for data in set {
261261
if self
262262
.type_system
263-
.strict_deserialize_type(*sem_id, data.as_ref())
263+
.strict_deserialize_type(*sem_id, data.value.as_ref())
264264
.is_err()
265265
{
266266
status.add_failure(validation::Failure::SchemaInvalidGlobalValue(

0 commit comments

Comments
 (0)