Skip to content

Commit

Permalink
Merge pull request #264 from RGB-WG/contract-state2
Browse files Browse the repository at this point in the history
Validation: use consensus order and compute contract state
  • Loading branch information
dr-orlovsky authored Aug 9, 2024
2 parents 8af3baf + 481132b commit f47d1a3
Show file tree
Hide file tree
Showing 20 changed files with 625 additions and 423 deletions.
4 changes: 4 additions & 0 deletions src/operation/commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ pub enum TypeCommitment {
#[commit_encode(strategy = strict, id = OpId)]
pub struct OpCommitment {
pub ffv: Ffv,
pub nonce: u8,
pub op_type: TypeCommitment,
pub metadata: StrictHash,
pub globals: MerkleHash,
Expand All @@ -286,6 +287,7 @@ impl Genesis {
};
OpCommitment {
ffv: self.ffv,
nonce: u8::MAX,
op_type: TypeCommitment::Genesis(base),
metadata: self.metadata.commit_id(),
globals: MerkleHash::merklize(&self.globals),
Expand All @@ -305,6 +307,7 @@ impl Transition {
pub fn commit(&self) -> OpCommitment {
OpCommitment {
ffv: self.ffv,
nonce: self.nonce,
op_type: TypeCommitment::Transition(self.contract_id, self.transition_type),
metadata: self.metadata.commit_id(),
globals: MerkleHash::merklize(&self.globals),
Expand All @@ -322,6 +325,7 @@ impl Extension {
pub fn commit(&self) -> OpCommitment {
OpCommitment {
ffv: self.ffv,
nonce: self.nonce,
op_type: TypeCommitment::Extension(self.contract_id, self.extension_type),
metadata: self.metadata.commit_id(),
globals: MerkleHash::merklize(&self.globals),
Expand Down
15 changes: 15 additions & 0 deletions src/operation/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,10 @@ pub trait Operation {
/// Returns [`ContractId`] this operation belongs to.
fn contract_id(&self) -> ContractId;

/// Returns nonce used in consensus ordering of state transitions and
/// extensions.
fn nonce(&self) -> u8;

/// Returns [`Option::Some`]`(`[`TransitionType`]`)` for transitions or
/// [`Option::None`] for genesis and extension operation types
fn transition_type(&self) -> Option<TransitionType>;
Expand Down Expand Up @@ -378,6 +382,7 @@ impl StrictDeserialize for Genesis {}
pub struct Extension {
pub ffv: Ffv,
pub contract_id: ContractId,
pub nonce: u8,
pub extension_type: ExtensionType,
pub metadata: Metadata,
pub globals: GlobalState,
Expand Down Expand Up @@ -410,6 +415,7 @@ impl PartialOrd for Extension {
pub struct Transition {
pub ffv: Ffv,
pub contract_id: ContractId,
pub nonce: u8,
pub transition_type: TransitionType,
pub metadata: Metadata,
pub globals: GlobalState,
Expand Down Expand Up @@ -511,6 +517,9 @@ impl Operation for Genesis {
#[inline]
fn contract_id(&self) -> ContractId { ContractId::from_inner(self.id().into_inner()) }

#[inline]
fn nonce(&self) -> u8 { u8::MAX }

#[inline]
fn transition_type(&self) -> Option<TransitionType> { None }

Expand Down Expand Up @@ -553,6 +562,9 @@ impl Operation for Extension {
#[inline]
fn contract_id(&self) -> ContractId { self.contract_id }

#[inline]
fn nonce(&self) -> u8 { self.nonce }

#[inline]
fn transition_type(&self) -> Option<TransitionType> { None }

Expand Down Expand Up @@ -595,6 +607,9 @@ impl Operation for Transition {
#[inline]
fn contract_id(&self) -> ContractId { self.contract_id }

#[inline]
fn nonce(&self) -> u8 { self.nonce }

#[inline]
fn transition_type(&self) -> Option<TransitionType> { Some(self.transition_type) }

Expand Down
4 changes: 2 additions & 2 deletions src/stl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ use crate::{

/// Strict types id for the library providing data types for RGB consensus.
pub const LIB_ID_RGB_COMMIT: &str =
"stl:tjFc6jD7-fe78CxG-WdJlH!l-uXlFfW0-XwG1!qV-MNdtNGE#orbit-airport-voice";
"stl:4WY0kCd1-qKjYuh5-4GgHKme-XIsmv98-W5Z9$8D-vMjFt!Y#miranda-blue-promise";
/// Strict types id for the library providing data types for RGB consensus.
pub const LIB_ID_RGB_LOGIC: &str =
"stl:pxDxFGo9-MbacU6J-Qug1G$1-6LsuROd-Um1H$hU-T6o2Lgk#lobster-dilemma-famous";
"stl:Yd7koRpf-hs7nsKX-TOLAnZl-hIfJ9wQ-M8J58hj-n60RcaA#pioneer-gong-smoke";

fn _rgb_commit_stl() -> Result<TypeLib, CompileError> {
LibBuilder::new(libname!(LIB_NAME_RGB_COMMIT), tiny_bset! {
Expand Down
124 changes: 122 additions & 2 deletions src/validation/consignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,133 @@ use amplify::confinement::Confined;
use strict_types::TypeSystem;

use super::EAnchor;
use crate::vm::{OpRef, XWitnessId};
use crate::{BundleId, Genesis, OpId, Operation, Schema, TransitionBundle};
use crate::vm::XWitnessId;
use crate::{
AssignmentType, AssignmentsRef, BundleId, ContractId, Extension, ExtensionType, Genesis,
GlobalState, GraphSeal, Inputs, Metadata, OpFullType, OpId, OpType, Operation, Schema,
Transition, TransitionBundle, TransitionType, TypedAssigns, Valencies,
};

pub const CONSIGNMENT_MAX_LIBS: usize = 1024;

pub type Scripts = Confined<BTreeMap<LibId, Lib>, 0, CONSIGNMENT_MAX_LIBS>;

#[derive(Copy, Clone, PartialEq, Eq, Debug, From)]
pub enum OpRef<'op> {
#[from]
Genesis(&'op Genesis),
#[from]
Transition(&'op Transition),
#[from]
Extension(&'op Extension),
}

impl<'op> Operation for OpRef<'op> {
fn op_type(&self) -> OpType {
match self {
Self::Genesis(op) => op.op_type(),
Self::Transition(op) => op.op_type(),
Self::Extension(op) => op.op_type(),
}
}

fn full_type(&self) -> OpFullType {
match self {
Self::Genesis(op) => op.full_type(),
Self::Transition(op) => op.full_type(),
Self::Extension(op) => op.full_type(),
}
}

fn id(&self) -> OpId {
match self {
Self::Genesis(op) => op.id(),
Self::Transition(op) => op.id(),
Self::Extension(op) => op.id(),
}
}

fn contract_id(&self) -> ContractId {
match self {
Self::Genesis(op) => op.contract_id(),
Self::Transition(op) => op.contract_id(),
Self::Extension(op) => op.contract_id(),
}
}

fn nonce(&self) -> u8 {
match self {
Self::Genesis(op) => op.nonce(),
Self::Transition(op) => op.nonce(),
Self::Extension(op) => op.nonce(),
}
}

fn transition_type(&self) -> Option<TransitionType> {
match self {
Self::Genesis(op) => op.transition_type(),
Self::Transition(op) => op.transition_type(),
Self::Extension(op) => op.transition_type(),
}
}

fn extension_type(&self) -> Option<ExtensionType> {
match self {
Self::Genesis(op) => op.extension_type(),
Self::Transition(op) => op.extension_type(),
Self::Extension(op) => op.extension_type(),
}
}

fn metadata(&self) -> &Metadata {
match self {
Self::Genesis(op) => op.metadata(),
Self::Transition(op) => op.metadata(),
Self::Extension(op) => op.metadata(),
}
}

fn globals(&self) -> &GlobalState {
match self {
Self::Genesis(op) => op.globals(),
Self::Transition(op) => op.globals(),
Self::Extension(op) => op.globals(),
}
}

fn valencies(&self) -> &Valencies {
match self {
Self::Genesis(op) => op.valencies(),
Self::Transition(op) => op.valencies(),
Self::Extension(op) => op.valencies(),
}
}

fn assignments(&self) -> AssignmentsRef<'op> {
match self {
Self::Genesis(op) => (&op.assignments).into(),
Self::Transition(op) => (&op.assignments).into(),
Self::Extension(op) => (&op.assignments).into(),
}
}

fn assignments_by_type(&self, t: AssignmentType) -> Option<TypedAssigns<GraphSeal>> {
match self {
Self::Genesis(op) => op.assignments_by_type(t),
Self::Transition(op) => op.assignments_by_type(t),
Self::Extension(op) => op.assignments_by_type(t),
}
}

fn inputs(&self) -> Inputs {
match self {
Self::Genesis(op) => op.inputs(),
Self::Transition(op) => op.inputs(),
Self::Extension(op) => op.inputs(),
}
}
}

pub struct CheckedConsignment<'consignment, C: ConsignmentApi>(&'consignment C);

impl<'consignment, C: ConsignmentApi> CheckedConsignment<'consignment, C> {
Expand Down
30 changes: 19 additions & 11 deletions src/validation/logic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use strict_types::TypeSystem;

use crate::schema::{AssignmentsSchema, GlobalSchema, ValencySchema};
use crate::validation::{CheckedConsignment, ConsignmentApi};
use crate::vm::{ContractStateAccess, ContractStateEvolve, OpInfo, OpRef, RgbIsa, VmContext};
use crate::vm::{ContractStateAccess, ContractStateEvolve, OpInfo, OrdOpRef, RgbIsa, VmContext};
use crate::{
validation, Assign, AssignmentType, Assignments, AssignmentsRef, ConcealedState,
ConfidentialState, ExposedSeal, ExposedState, Extension, GlobalState, GlobalStateSchema,
Expand All @@ -43,15 +43,14 @@ use crate::{
};

impl Schema {
// TODO: Instead of returning status fail immediately
pub fn validate_state<
'validator,
C: ConsignmentApi,
S: ContractStateAccess + ContractStateEvolve,
>(
&'validator self,
consignment: &'validator CheckedConsignment<'_, C>,
op: OpRef,
op: OrdOpRef,
contract_state: Rc<RefCell<S>>,
) -> validation::Status {
let opid = op.id();
Expand All @@ -70,7 +69,7 @@ impl Schema {
validator,
ty,
) = match op {
OpRef::Genesis(genesis) => {
OrdOpRef::Genesis(genesis) => {
for id in genesis.asset_tags.keys() {
if !matches!(self.owned_types.get(id), Some(OwnedStateSchema::Fungible(_))) {
status.add_failure(validation::Failure::AssetTagNoState(*id));
Expand All @@ -95,9 +94,12 @@ impl Schema {
None::<u16>,
)
}
OpRef::Transition(Transition {
transition_type, ..
}) => {
OrdOpRef::Transition(
Transition {
transition_type, ..
},
..,
) => {
// Right now we do not have actions to implement; but later
// we may have embedded procedures which must be verified
// here
Expand Down Expand Up @@ -131,7 +133,7 @@ impl Schema {
Some(transition_type.into_inner()),
)
}
OpRef::Extension(Extension { extension_type, .. }) => {
OrdOpRef::Extension(Extension { extension_type, .. }, ..) => {
// Right now we do not have actions to implement; but later
// we may have embedded procedures which must be verified
// here
Expand Down Expand Up @@ -168,15 +170,15 @@ impl Schema {
status += self.validate_metadata(opid, op.metadata(), metadata_schema, consignment.types());
status +=
self.validate_global_state(opid, op.globals(), global_schema, consignment.types());
let prev_state = if let OpRef::Transition(transition) = op {
let prev_state = if let OrdOpRef::Transition(transition, ..) = op {
let prev_state = extract_prev_state(consignment, opid, &transition.inputs, &mut status);
status += self.validate_prev_state(opid, &prev_state, owned_schema);
prev_state
} else {
Assignments::default()
};
let mut redeemed = Valencies::default();
if let OpRef::Extension(extension) = op {
if let OrdOpRef::Extension(extension, ..) = op {
for valency in extension.redeemed.keys() {
redeemed.push(*valency).expect("same size");
}
Expand Down Expand Up @@ -218,9 +220,15 @@ impl Schema {
error_code.map(u8::from),
None,
));
// We return here since all other validations will have no valid state to access
return status;
}
let contract_state = context.contract_state;
contract_state.borrow_mut().evolve_state(op);
if contract_state.borrow_mut().evolve_state(op).is_err() {
status.add_failure(validation::Failure::ContractStateFilled(opid));
// We return here since all other validations will have no valid state to access
return status;
}
}
status
}
Expand Down
2 changes: 1 addition & 1 deletion src/validation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ mod status;
mod commitments;

pub use commitments::{DbcError, DbcProof, EAnchor};
pub use consignment::{CheckedConsignment, ConsignmentApi, Scripts, CONSIGNMENT_MAX_LIBS};
pub use consignment::{CheckedConsignment, ConsignmentApi, OpRef, Scripts, CONSIGNMENT_MAX_LIBS};
pub use status::{Failure, Info, Status, Validity, Warning};
pub use validator::{ResolveWitness, Validator, WitnessResolverError};
2 changes: 2 additions & 0 deletions src/validation/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,8 @@ pub enum Failure {
/// evaluation of AluVM script for operation {0} has failed with the code
/// {1:?} and message {2:?}.
ScriptFailure(OpId, Option<u8>, Option<String>),
/// contract state can't fit more data (at operation id {0}).
ContractStateFilled(OpId),

/// Custom error by external services on top of RGB Core.
#[display(inner)]
Expand Down
Loading

0 comments on commit f47d1a3

Please sign in to comment.