Skip to content

Commit

Permalink
iface: allow Rgb20 builder to work with schemata not known compile-time
Browse files Browse the repository at this point in the history
  • Loading branch information
dr-orlovsky committed Jan 29, 2024
1 parent 6b9f122 commit 75c71d3
Show file tree
Hide file tree
Showing 6 changed files with 185 additions and 46 deletions.
63 changes: 34 additions & 29 deletions src/interface/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,19 @@ use strict_types::decode;
use crate::containers::{BuilderSeal, Contract};
use crate::interface::contract::AttachedState;
use crate::interface::resolver::DumbResolver;
use crate::interface::{Iface, IfaceImpl, IfacePair, TransitionIface};
use crate::interface::{
Iface, IfaceClass, IfaceImpl, IfacePair, IssuerTriplet, SchemaIssuer, TransitionIface,
WrongImplementation,
};
use crate::persistence::PersistedState;
use crate::Outpoint;

#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)]
#[display(doc_comments)]
pub enum BuilderError {
/// interface implementation references different interface that the one
/// provided to the builder.
InterfaceMismatch,

/// interface implementation references different schema that the one
/// provided to the builder.
SchemaMismatch,
#[from]
#[display(inner)]
WrongImplementation(WrongImplementation),

/// contract already has too many layers1.
TooManyLayers1,
Expand Down Expand Up @@ -146,7 +145,7 @@ impl ContractBuilder {
schema: SubSchema,
iimpl: IfaceImpl,
testnet: bool,
) -> Result<Self, BuilderError> {
) -> Result<Self, WrongImplementation> {
Ok(Self {
builder: OperationBuilder::with(iface, schema, iimpl)?,
testnet,
Expand All @@ -158,7 +157,7 @@ impl ContractBuilder {
iface: Iface,
schema: SubSchema,
iimpl: IfaceImpl,
) -> Result<Self, BuilderError> {
) -> Result<Self, WrongImplementation> {
Ok(Self {
builder: OperationBuilder::with(iface, schema, iimpl)?,
testnet: false,
Expand All @@ -170,7 +169,7 @@ impl ContractBuilder {
iface: Iface,
schema: SubSchema,
iimpl: IfaceImpl,
) -> Result<Self, BuilderError> {
) -> Result<Self, WrongImplementation> {
Ok(Self {
builder: OperationBuilder::with(iface, schema, iimpl)?,
testnet: true,
Expand Down Expand Up @@ -347,7 +346,7 @@ impl TransitionBuilder {
iface: Iface,
schema: SubSchema,
iimpl: IfaceImpl,
) -> Result<Self, BuilderError> {
) -> Result<Self, WrongImplementation> {
Self::with(iface, schema, iimpl, TransitionType::BLANK)
}

Expand All @@ -361,7 +360,7 @@ impl TransitionBuilder {
.as_ref()
.and_then(|name| iimpl.transition_type(name))
.ok_or(BuilderError::NoOperationSubtype)?;
Self::with(iface, schema, iimpl, transition_type)
Ok(Self::with(iface, schema, iimpl, transition_type)?)
}

pub fn named_transition(
Expand All @@ -374,15 +373,15 @@ impl TransitionBuilder {
let transition_type = iimpl
.transition_type(&transition_name)
.ok_or(BuilderError::TransitionNotFound(transition_name))?;
Self::with(iface, schema, iimpl, transition_type)
Ok(Self::with(iface, schema, iimpl, transition_type)?)
}

fn with(
iface: Iface,
schema: SubSchema,
iimpl: IfaceImpl,
transition_type: TransitionType,
) -> Result<Self, BuilderError> {
) -> Result<Self, WrongImplementation> {
Ok(Self {
builder: OperationBuilder::with(iface, schema, iimpl)?,
transition_type,
Expand Down Expand Up @@ -609,20 +608,11 @@ pub struct OperationBuilder<Seal: ExposedSeal> {
// TODO: add valencies
}

impl<Seal: ExposedSeal> OperationBuilder<Seal> {
fn with(iface: Iface, schema: SubSchema, iimpl: IfaceImpl) -> Result<Self, BuilderError> {
if iimpl.iface_id != iface.iface_id() {
return Err(BuilderError::InterfaceMismatch);
}
if iimpl.schema_id != schema.schema_id() {
return Err(BuilderError::SchemaMismatch);
}

// TODO: check schema internal consistency
// TODO: check interface internal consistency
// TODO: check implementation internal consistency
impl<Seal: ExposedSeal> From<IssuerTriplet> for OperationBuilder<Seal> {
fn from(triplet: IssuerTriplet) -> Self {
let (iface, schema, iimpl) = triplet.into_split();

Ok(OperationBuilder {
OperationBuilder {
schema,
iface,
iimpl,
Expand All @@ -633,7 +623,22 @@ impl<Seal: ExposedSeal> OperationBuilder<Seal> {
fungible: none!(),
attachments: none!(),
data: none!(),
})
}
}
}

impl<Seal: ExposedSeal, I: IfaceClass> From<SchemaIssuer<I>> for OperationBuilder<Seal> {
fn from(issuer: SchemaIssuer<I>) -> Self { Self::from(issuer.into_triplet()) }
}

impl<Seal: ExposedSeal> OperationBuilder<Seal> {
fn with(
iface: Iface,
schema: SubSchema,
iimpl: IfaceImpl,
) -> Result<Self, WrongImplementation> {
let triplet = IssuerTriplet::new(iface, schema, iimpl)?;
Ok(Self::from(triplet))
}

fn transition_iface(&self, ty: TransitionType) -> &TransitionIface {
Expand Down
119 changes: 118 additions & 1 deletion src/interface/iimpl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
// limitations under the License.

use std::fmt::{self, Display, Formatter};
use std::marker::PhantomData;
use std::str::FromStr;

use amplify::confinement::{TinyOrdMap, TinyOrdSet};
Expand All @@ -34,9 +35,10 @@ use strict_encoding::{FieldName, StrictDumb, TypeName};
use strict_types::encoding::{
StrictDecode, StrictDeserialize, StrictEncode, StrictSerialize, StrictType,
};
use strict_types::TypeLib;

use crate::interface::iface::IfaceId;
use crate::interface::{Iface, VerNo};
use crate::interface::{Iface, IfaceWrapper, VerNo};
use crate::{ReservedBytes, LIB_NAME_RGB_STD};

pub trait SchemaTypeIndex:
Expand Down Expand Up @@ -302,6 +304,121 @@ impl IfacePair {
}

pub trait ContractClass {
type Iface: IfaceClass;
fn schema() -> SubSchema;
fn main_iface_impl() -> IfaceImpl;
fn issuer() -> SchemaIssuer<Self::Iface>;
}

pub trait IfaceClass: IfaceWrapper {
fn iface() -> Iface;
fn stl() -> TypeLib;
}

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Display, Error)]
#[display(doc_comments)]
pub enum WrongImplementation {
/// the provided implementation {impl_id} implements interface {actual}
/// instead of {expected} for the schema {schema_id}
InterfaceMismatch {
schema_id: SchemaId,
impl_id: ImplId,
expected: IfaceId,
actual: IfaceId,
},

/// the provided implementation {impl_id} uses schema {actual} instead of
/// {expected}
SchemaMismatch {
impl_id: ImplId,
expected: SchemaId,
actual: SchemaId,
},
}

#[derive(Getters, Clone, Eq, PartialEq, Debug)]
pub struct IssuerTriplet {
schema: SubSchema,
iface: Iface,
iimpl: IfaceImpl,
}

impl IssuerTriplet {
pub fn new(
iface: Iface,
schema: SubSchema,
iimpl: IfaceImpl,
) -> Result<Self, WrongImplementation> {
let expected = iface.iface_id();
let actual = iimpl.iface_id;

if actual != expected {
return Err(WrongImplementation::InterfaceMismatch {
schema_id: schema.schema_id(),
impl_id: iimpl.impl_id(),
expected,
actual,
});
}

let expected = schema.schema_id();
let actual = iimpl.schema_id;
if actual != expected {
return Err(WrongImplementation::SchemaMismatch {
impl_id: iimpl.impl_id(),
expected,
actual,
});
}

// TODO: check schema internal consistency
// TODO: check interface internal consistency
// TODO: check implementation internal consistency

Ok(Self {
iface,
schema,
iimpl,
})
}

#[inline]
pub fn into_split(self) -> (Iface, SubSchema, IfaceImpl) {
(self.iface, self.schema, self.iimpl)
}

#[inline]
pub fn into_issuer(self) -> (SubSchema, IfaceImpl) { (self.schema, self.iimpl) }
}

#[derive(Getters, Clone, Eq, PartialEq, Debug)]
pub struct SchemaIssuer<I: IfaceClass> {
schema: SubSchema,
iimpl: IfaceImpl,
phantom: PhantomData<I>,
}

impl<I: IfaceClass> SchemaIssuer<I> {
pub fn new(schema: SubSchema, iimpl: IfaceImpl) -> Result<Self, WrongImplementation> {
let triplet = IssuerTriplet::new(I::iface(), schema, iimpl)?;
let (_, schema, iimpl) = triplet.into_split();

Ok(Self {
schema,
iimpl,
phantom: default!(),
})
}

#[inline]
pub fn into_split(self) -> (SubSchema, IfaceImpl) { (self.schema, self.iimpl) }

pub fn into_triplet(self) -> IssuerTriplet {
let (schema, iimpl) = self.into_split();
IssuerTriplet {
schema,
iface: I::iface(),
iimpl,
}
}
}
5 changes: 4 additions & 1 deletion src/interface/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ pub use iface::{
ArgMap, ArgSpec, AssignIface, ExtensionIface, GenesisIface, GlobalIface, Iface, IfaceId,
OwnedIface, Req, TransitionIface, ValencyIface,
};
pub use iimpl::{ContractClass, IfaceImpl, IfacePair, ImplId, NamedField, NamedType, SchemaIfaces};
pub use iimpl::{
ContractClass, IfaceClass, IfaceImpl, IfacePair, ImplId, IssuerTriplet, NamedField, NamedType,
SchemaIfaces, SchemaIssuer, SchemaTypeIndex, WrongImplementation,
};
pub use rgb20::{AmountChange, Rgb20, LIB_ID_RGB20, LIB_NAME_RGB20};
pub use rgb21::{rgb21, rgb21_stl, Rgb21, LIB_ID_RGB21, LIB_NAME_RGB21};
pub use rgb25::{rgb25, rgb25_stl, Rgb25, LIB_ID_RGB25, LIB_NAME_RGB25};
Expand Down
40 changes: 27 additions & 13 deletions src/interface/rgb20.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ use strict_types::{CompileError, LibBuilder, TypeLib};

use super::{
AssignIface, BuilderError, ContractBuilder, ContractClass, GenesisIface, GlobalIface, Iface,
IfaceOp, OwnedIface, Req, StateChange, TransitionIface, VerNo, WitnessFilter,
IfaceClass, IfaceOp, OwnedIface, Req, SchemaIssuer, StateChange, TransitionIface, VerNo,
WitnessFilter,
};
use crate::containers::Contract;
use crate::interface::builder::TxOutpoint;
Expand Down Expand Up @@ -320,12 +321,13 @@ impl IfaceWrapper for Rgb20 {
]);
}

impl Rgb20 {
pub fn iface() -> Iface { rgb20() }

pub fn stl() -> TypeLib { rgb20_stl() }
impl IfaceClass for Rgb20 {
fn iface() -> Iface { rgb20() }
fn stl() -> TypeLib { rgb20_stl() }
}

pub fn testnet<C: ContractClass>(
impl Rgb20 {
pub fn testnet<C: ContractClass<Iface = Self>>(
ticker: &str,
name: &str,
details: Option<&str>,
Expand All @@ -334,7 +336,7 @@ impl Rgb20 {
PrimaryIssue::testnet::<C>(ticker, name, details, precision)
}

pub fn testnet_det<C: ContractClass>(
pub fn testnet_det<C: ContractClass<Iface = Self>>(
ticker: &str,
name: &str,
details: Option<&str>,
Expand Down Expand Up @@ -453,7 +455,8 @@ pub struct PrimaryIssue {
}

impl PrimaryIssue {
fn testnet_int<C: ContractClass>(
fn testnet_int(
issuer: SchemaIssuer<Rgb20>,
ticker: &str,
name: &str,
details: Option<&str>,
Expand All @@ -466,7 +469,8 @@ impl PrimaryIssue {
media: None,
};

let builder = ContractBuilder::testnet(rgb20(), C::schema(), C::main_iface_impl())
let (schema, main_iface_impl) = issuer.into_split();
let builder = ContractBuilder::testnet(rgb20(), schema, main_iface_impl)
.expect("schema interface mismatch")
.add_global_state("spec", spec)
.expect("invalid RGB20 schema (token specification mismatch)")
Expand All @@ -481,24 +485,34 @@ impl PrimaryIssue {
})
}

pub fn testnet<C: ContractClass>(
pub fn testnet<C: ContractClass<Iface = Rgb20>>(
ticker: &str,
name: &str,
details: Option<&str>,
precision: Precision,
) -> Result<Self, InvalidIdent> {
Self::testnet_int(C::issuer(), ticker, name, details, precision, Timestamp::now())
}

pub fn testnet_with(
issuer: SchemaIssuer<Rgb20>,
ticker: &str,
name: &str,
details: Option<&str>,
precision: Precision,
) -> Result<Self, InvalidIdent> {
Self::testnet_int::<C>(ticker, name, details, precision, Timestamp::now())
Self::testnet_int(issuer, ticker, name, details, precision, Timestamp::now())
}

pub fn testnet_det<C: ContractClass>(
pub fn testnet_det<C: ContractClass<Iface = Rgb20>>(
ticker: &str,
name: &str,
details: Option<&str>,
precision: Precision,
timestamp: Timestamp,
asset_tag: AssetTag,
) -> Result<Self, InvalidIdent> {
let mut me = Self::testnet_int::<C>(ticker, name, details, precision, timestamp)?;
let mut me = Self::testnet_int(C::issuer(), ticker, name, details, precision, timestamp)?;
me.builder = me
.builder
.add_asset_tag("assetOwner", asset_tag)
Expand Down
2 changes: 1 addition & 1 deletion src/persistence/hoard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use strict_encoding::TypeName;
use crate::accessors::{MergeReveal, MergeRevealError};
use crate::containers::{Cert, Consignment, ContentId, ContentSigs};
use crate::interface::{
rgb21, rgb25, ContractSuppl, Iface, IfaceId, IfacePair, Rgb20, SchemaIfaces,
rgb21, rgb25, ContractSuppl, Iface, IfaceClass, IfaceId, IfacePair, Rgb20, SchemaIfaces,
};
use crate::persistence::{InventoryError, Stash, StashError, StashInconsistency};
use crate::LIB_NAME_RGB_STD;
Expand Down
Loading

0 comments on commit 75c71d3

Please sign in to comment.