Skip to content

Commit

Permalink
Merge pull request #200 from RGB-WG/v0.11
Browse files Browse the repository at this point in the history
API sanation for state types
  • Loading branch information
dr-orlovsky authored Jan 19, 2024
2 parents c627404 + 2b59903 commit 729c652
Show file tree
Hide file tree
Showing 9 changed files with 173 additions and 66 deletions.
27 changes: 21 additions & 6 deletions src/contract/attachment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use std::str::FromStr;

use amplify::{ByteArray, Bytes32};
use baid58::{Baid58ParseError, Chunking, FromBaid58, ToBaid58, CHUNKING_32};
use bp::secp256k1::rand::{thread_rng, RngCore};
use bp::secp256k1::rand::{random, Rng, RngCore};
use commit_verify::{CommitVerify, Conceal, StrictEncodedProtocol};
use strict_encoding::StrictEncode;

Expand Down Expand Up @@ -83,13 +83,28 @@ pub struct RevealedAttach {
}

impl RevealedAttach {
/// Creates new revealed attachment for the attachment id and MIME type.
/// Uses `thread_rng` to initialize [`RevealedAttach::salt`].
pub fn new(id: AttachId, media_type: MediaType) -> Self {
/// Constructs new state using the provided value using random blinding
/// factor.
pub fn new_random_salt(id: AttachId, media_type: impl Into<MediaType>) -> Self {
Self::with_salt(id, media_type, random())
}

/// Constructs new state using the provided value and random generator for
/// creating blinding factor.
pub fn with_rng<R: Rng + RngCore>(
id: AttachId,
media_type: impl Into<MediaType>,
rng: &mut R,
) -> Self {
Self::with_salt(id, media_type, rng.next_u64())
}

/// Convenience constructor.
pub fn with_salt(id: AttachId, media_type: impl Into<MediaType>, salt: u64) -> Self {
Self {
id,
media_type,
salt: thread_rng().next_u64(),
media_type: media_type.into(),
salt,
}
}
}
Expand Down
86 changes: 64 additions & 22 deletions src/contract/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ use amplify::hex;
use strict_encoding::{StrictDecode, StrictDumb, StrictEncode};

use crate::{
Assign, AssignmentType, Assignments, AssignmentsRef, ContractId, ExposedSeal, ExposedState,
Extension, Genesis, GlobalStateType, OpId, Operation, RevealedAttach, RevealedData,
RevealedValue, SchemaId, SubSchema, Transition, TypedAssigns, VoidState, WitnessAnchor,
WitnessId, XChain, XOutputSeal, LIB_NAME_RGB,
Assign, AssignmentType, Assignments, AssignmentsRef, ContractId, DataState, ExposedSeal,
ExposedState, Extension, Genesis, GlobalStateType, OpId, Operation, RevealedAttach,
RevealedData, RevealedValue, SchemaId, SubSchema, Transition, TypedAssigns, VoidState,
WitnessAnchor, WitnessId, XChain, XOutputSeal, LIB_NAME_RGB,
};

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

#[derive(Clone, Eq, Debug)]
/// Trait used by contract state. Unlike [`ExposedState`] it doesn't allow
/// concealment of the state, i.e. may contain incomplete data without blinding
/// factors, asset tags etc.
pub trait KnownState: Debug + StrictDumb + StrictEncode + StrictDecode + Eq + Clone {}
impl<S: ExposedState> KnownState for S {}

impl KnownState for () {}
impl KnownState for DataState {}

#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Display, From)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB, tags = custom)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub enum AssignmentWitness {
#[display("~")]
#[strict_type(tag = 0, dumb)]
Absent,

#[from]
#[display(inner)]
#[strict_type(tag = 1)]
Present(WitnessId),
}

impl From<Option<WitnessId>> for AssignmentWitness {
fn from(value: Option<WitnessId>) -> Self {
match value {
None => AssignmentWitness::Absent,
Some(id) => AssignmentWitness::Present(id),
}
}
}

#[derive(Copy, Clone, Eq, Debug)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB)]
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub struct OutputAssignment<State: ExposedState> {
pub struct OutputAssignment<State: KnownState> {
pub opout: Opout,
pub seal: XOutputSeal,
pub state: State,
pub witness: Option<WitnessId>,
pub witness: AssignmentWitness,
}

impl<State: ExposedState> PartialEq for OutputAssignment<State> {
impl<State: KnownState> PartialEq for OutputAssignment<State> {
fn eq(&self, other: &Self) -> bool {
if self.opout == other.opout &&
(self.seal != other.seal ||
Expand All @@ -127,11 +164,11 @@ impl<State: ExposedState> PartialEq for OutputAssignment<State> {
}
}

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

impl<State: ExposedState> Ord for OutputAssignment<State> {
impl<State: KnownState> Ord for OutputAssignment<State> {
fn cmp(&self, other: &Self) -> Ordering {
if self == other {
return Ordering::Equal;
Expand All @@ -140,7 +177,7 @@ impl<State: ExposedState> Ord for OutputAssignment<State> {
}
}

impl<State: ExposedState> OutputAssignment<State> {
impl<State: KnownState> OutputAssignment<State> {
/// # Panics
///
/// If the processing is done on invalid stash data, the seal is
Expand All @@ -160,7 +197,7 @@ impl<State: ExposedState> OutputAssignment<State> {
match anchor's chain",
),
state,
witness: Some(witness_id),
witness: witness_id.into(),
}
}

Expand All @@ -182,7 +219,17 @@ impl<State: ExposedState> OutputAssignment<State> {
information since it comes from genesis or extension",
),
state,
witness: None,
witness: AssignmentWitness::Absent,
}
}

pub fn transmute<S: KnownState>(self) -> OutputAssignment<S>
where S: From<State> {
OutputAssignment {
opout: self.opout,
seal: self.seal,
state: self.state.into(),
witness: self.witness,
}
}
}
Expand Down Expand Up @@ -234,11 +281,6 @@ impl GlobalOrd {
}
}

pub type RightsOutput = OutputAssignment<VoidState>;
pub type FungibleOutput = OutputAssignment<RevealedValue>;
pub type DataOutput = OutputAssignment<RevealedData>;
pub type AttachOutput = OutputAssignment<RevealedAttach>;

/// Contract history accumulates raw data from the contract history, extracted
/// from a series of consignments over the time. It does consensus ordering of
/// the state data, but it doesn't interpret or validates the state against the
Expand All @@ -262,10 +304,10 @@ pub struct ContractHistory {
contract_id: ContractId,
#[getter(skip)]
global: TinyOrdMap<GlobalStateType, LargeOrdMap<GlobalOrd, RevealedData>>,
rights: LargeOrdSet<RightsOutput>,
fungibles: LargeOrdSet<FungibleOutput>,
data: LargeOrdSet<DataOutput>,
attach: LargeOrdSet<AttachOutput>,
rights: LargeOrdSet<OutputAssignment<VoidState>>,
fungibles: LargeOrdSet<OutputAssignment<RevealedValue>>,
data: LargeOrdSet<OutputAssignment<RevealedData>>,
attach: LargeOrdSet<OutputAssignment<RevealedAttach>>,
}

impl ContractHistory {
Expand Down
88 changes: 71 additions & 17 deletions src/contract/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use core::fmt::{self, Debug, Display, Formatter};
use core::fmt::{self, Debug, Formatter};
use std::cmp::Ordering;
use std::io::Write;

use amplify::confinement::SmallVec;
use amplify::confinement::SmallBlob;
use amplify::hex::ToHex;
use amplify::{Bytes32, Wrapper};
use commit_verify::{CommitVerify, Conceal, StrictEncodedProtocol};
use bp::secp256k1::rand::{random, Rng, RngCore};
use commit_verify::{CommitEncode, CommitVerify, Conceal, StrictEncodedProtocol};
use strict_encoding::{StrictSerialize, StrictType};

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

#[derive(Wrapper, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, From)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[derive(Wrapper, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash, From, Display, Default)]
#[display(LowerHex)]
#[wrapper(Deref, AsSlice, BorrowSlice, Hex)]
#[derive(StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB)]
#[derive(CommitEncode)]
#[commit_encode(conceal)]
#[commit_encode(strategy = strict)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))]
pub struct RevealedData(SmallVec<u8>);
pub struct DataState(SmallBlob);
impl StrictSerialize for DataState {}

impl From<RevealedData> for DataState {
fn from(data: RevealedData) -> Self { data.value }
}

#[derive(Clone, Eq, PartialEq, Hash)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_RGB)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))]
pub struct RevealedData {
pub value: DataState,
pub salt: u128,
}

impl RevealedData {
/// Constructs new state using the provided value using random blinding
/// factor.
pub fn new_random_salt(value: impl Into<DataState>) -> Self { Self::with_salt(value, random()) }

/// Constructs new state using the provided value and random generator for
/// creating blinding factor.
pub fn with_rng<R: Rng + RngCore>(value: impl Into<DataState>, rng: &mut R) -> Self {
Self::with_salt(value, rng.gen())
}

/// Convenience constructor.
pub fn with_salt(value: impl Into<DataState>, salt: u128) -> Self {
Self {
value: value.into(),
salt,
}
}
}

impl ExposedState for RevealedData {
type Confidential = ConcealedData;
Expand All @@ -73,24 +112,39 @@ impl ExposedState for RevealedData {

impl Conceal for RevealedData {
type Concealed = ConcealedData;

fn conceal(&self) -> Self::Concealed { ConcealedData::commit(self) }
}

impl StrictSerialize for RevealedData {}
impl CommitEncode for RevealedData {
fn commit_encode(&self, e: &mut impl Write) {
e.write_all(&self.value).ok();
e.write_all(&self.salt.to_le_bytes()).ok();
}
}

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

f.debug_tuple("RevealedData").field(&val).finish()
impl Ord for RevealedData {
fn cmp(&self, other: &Self) -> Ordering {
match self.value.cmp(&other.value) {
Ordering::Equal => self.salt.cmp(&other.salt),
other => other,
}
}
}

impl Display for RevealedData {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.write_str(&self.as_ref().to_hex()) }
impl Debug for RevealedData {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let val = String::from_utf8(self.value.to_vec()).unwrap_or_else(|_| self.value.to_hex());

f.debug_struct("RevealedData")
.field("value", &val)
.field("salt", &self.salt)
.finish()
}
}

/// Confidential version of an structured state data.
Expand Down
16 changes: 6 additions & 10 deletions src/contract/fungible.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ impl RevealedValue {

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

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

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

let a = PedersenCommitment::commit(&RevealedValue::with_random_blinding(15, &mut r, tag))
.into_inner();
let b = PedersenCommitment::commit(&RevealedValue::with_random_blinding(7, &mut r, tag))
.into_inner();
let a = PedersenCommitment::commit(&RevealedValue::with_rng(15, &mut r, tag)).into_inner();
let b = PedersenCommitment::commit(&RevealedValue::with_rng(7, &mut r, tag)).into_inner();

let c = PedersenCommitment::commit(&RevealedValue::with_random_blinding(13, &mut r, tag))
.into_inner();
let d = PedersenCommitment::commit(&RevealedValue::with_random_blinding(9, &mut r, tag))
.into_inner();
let c = PedersenCommitment::commit(&RevealedValue::with_rng(13, &mut r, tag)).into_inner();
let d = PedersenCommitment::commit(&RevealedValue::with_rng(9, &mut r, tag)).into_inner();

assert!(!secp256k1_zkp::verify_commitments_sum_to_equal(SECP256K1, &[a, b], &[c, d]))
}
Expand Down
6 changes: 3 additions & 3 deletions src/contract/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ pub use assignments::{
pub use attachment::{AttachId, ConcealedAttach, RevealedAttach};
pub use bundle::{BundleId, TransitionBundle, Vin};
pub use contract::{
AttachOutput, ContractHistory, ContractState, DataOutput, FungibleOutput, GlobalOrd, Opout,
OpoutParseError, OutputAssignment, RightsOutput,
AssignmentWitness, ContractHistory, ContractState, GlobalOrd, KnownState, Opout,
OpoutParseError, OutputAssignment,
};
pub use data::{ConcealedData, RevealedData, VoidState};
pub use data::{ConcealedData, DataState, RevealedData, VoidState};
pub use fungible::{
AssetTag, BlindingFactor, BlindingParseError, ConcealedValue, FungibleState,
InvalidFieldElement, NoiseDumb, PedersenCommitment, RangeProof, RangeProofError, RevealedValue,
Expand Down
2 changes: 1 addition & 1 deletion src/stl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use crate::{AnchoredBundle, ContractState, Extension, Genesis, SubSchema, LIB_NA

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

fn _rgb_core_stl() -> Result<TypeLib, CompileError> {
LibBuilder::new(libname!(LIB_NAME_RGB), tiny_bset! {
Expand Down
2 changes: 1 addition & 1 deletion src/validation/logic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ impl<Root: SchemaRoot> Schema<Root> {
for data in set {
if self
.type_system
.strict_deserialize_type(*sem_id, data.as_ref())
.strict_deserialize_type(*sem_id, data.value.as_ref())
.is_err()
{
status.add_failure(validation::Failure::SchemaInvalidGlobalValue(
Expand Down
Loading

0 comments on commit 729c652

Please sign in to comment.