Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve deterministic contract creation. Add convenient RGB20 contract builder #139

Merged
merged 20 commits into from
Feb 4, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
71e67ac
persistence: fix typo in PersistedState
dr-orlovsky Jan 28, 2024
fe8c8e6
iface: add ContractClass trait
dr-orlovsky Jan 28, 2024
54b25de
iface: allow reading asset tags from contract and transition builders
dr-orlovsky Jan 28, 2024
82ca1aa
iface: add methods for adding deterministic owned state with builders
dr-orlovsky Jan 28, 2024
dd86ee5
iface: add Rgb20 contract builder
dr-orlovsky Jan 28, 2024
1e70a74
iface: validate contract with builder
dr-orlovsky Jan 28, 2024
6e85afb
iface: improve ContractClass
dr-orlovsky Jan 28, 2024
077a043
iface: fix Rgb20 contract builder
dr-orlovsky Jan 29, 2024
f3fc977
iface: refactor RGB20 iface and stl from functions to Rgb20 methods
dr-orlovsky Jan 29, 2024
afcfd16
iface: export Rgb20Contract builder
dr-orlovsky Jan 29, 2024
b86f8c9
ifaces: better filter layer1 mismatches in builders
dr-orlovsky Jan 29, 2024
9eaac18
iface: auto-compute total supply with Rgb20Genesis builder
dr-orlovsky Jan 29, 2024
bd27a45
iface: export TxOutpoint used by builders
dr-orlovsky Jan 29, 2024
7136726
iface: improve RGB20 deterministic contract creation. Fix timestamp bug
dr-orlovsky Jan 29, 2024
83fab95
chore: update dependencies
dr-orlovsky Jan 29, 2024
5aea18e
iface: provide convenience constructors via Rgb20
dr-orlovsky Jan 29, 2024
6b9f122
containers: add convenience bindling function to Consignment
dr-orlovsky Jan 29, 2024
486ecb0
iface: allow Rgb20 builder to work with schemata not known compile-time
dr-orlovsky Jan 29, 2024
ad44987
chore: fix useless clippy lints
dr-orlovsky Feb 4, 2024
1e36eee
Merge remote-tracking branch 'origin/master' into v0.11
dr-orlovsky Feb 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 89 additions & 30 deletions src/interface/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,19 @@ use amplify::confinement::{Confined, TinyOrdMap, TinyOrdSet, U16};
use amplify::{confinement, Wrapper};
use invoice::{Allocation, Amount};
use rgb::{
AltLayer1, AltLayer1Set, AssetTag, Assign, AssignmentType, Assignments, BlindingFactor,
ContractId, DataState, ExposedSeal, FungibleType, Genesis, GenesisSeal, GlobalState, GraphSeal,
Input, Opout, RevealedAttach, RevealedData, RevealedValue, StateSchema, SubSchema, Transition,
TransitionType, TypedAssigns,
validation, AltLayer1, AltLayer1Set, AssetTag, Assign, AssignmentType, Assignments,
BlindingFactor, ContractId, DataState, ExposedSeal, FungibleType, Genesis, GenesisSeal,
GlobalState, GraphSeal, Input, Opout, RevealedAttach, RevealedData, RevealedValue, StateSchema,
SubSchema, Transition, TransitionType, TypedAssigns,
};
use strict_encoding::{FieldName, SerializeError, StrictSerialize, TypeName};
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::persistence::PresistedState;
use crate::persistence::PersistedState;

#[derive(Clone, Eq, PartialEq, Debug, Display, Error, From)]
#[display(doc_comments)]
Expand Down Expand Up @@ -97,6 +98,10 @@ pub enum BuilderError {
#[from]
#[display(inner)]
Confinement(confinement::Error),

#[from]
#[display(inner)]
ContractInconsistency(validation::Status),
}

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -151,6 +156,11 @@ impl ContractBuilder {
Ok(self)
}

#[inline]
pub fn asset_tag(&self, name: impl Into<FieldName>) -> Result<AssetTag, BuilderError> {
self.builder.asset_tag(name)
}

#[inline]
pub fn add_asset_tag(
mut self,
Expand All @@ -171,13 +181,13 @@ impl ContractBuilder {
Ok(self)
}

pub fn add_owned_state_raw(
pub fn add_owned_state_det(
mut self,
type_id: AssignmentType,
name: impl Into<FieldName>,
seal: impl Into<BuilderSeal<GenesisSeal>>,
state: PresistedState,
state: PersistedState,
) -> Result<Self, BuilderError> {
self.builder = self.builder.add_owned_state_raw(type_id, seal, state)?;
self.builder = self.builder.add_owned_state_det(name, seal, state)?;
Ok(self)
}

Expand Down Expand Up @@ -257,20 +267,27 @@ impl ContractBuilder {
valencies: none!(),
};

// TODO: Validate against schema

let mut contract = Contract::new(schema, genesis, asset_tags);
contract.ifaces = tiny_bmap! { iface_pair.iface_id() => iface_pair };

Ok(contract)
let verified_contract =
contract
.validate(&mut DumbResolver, self.testnet)
.map_err(|consignment| {
consignment
.into_validation_status()
.expect("status always present upon validation")
})?;

Ok(verified_contract)
}
}

#[derive(Clone, Debug)]
pub struct TransitionBuilder {
builder: OperationBuilder<GraphSeal>,
transition_type: TransitionType,
inputs: TinyOrdMap<Input, PresistedState>,
inputs: TinyOrdMap<Input, PersistedState>,
}

impl TransitionBuilder {
Expand Down Expand Up @@ -323,6 +340,11 @@ impl TransitionBuilder {

pub fn transition_type(&self) -> TransitionType { self.transition_type }

#[inline]
pub fn asset_tag(&self, name: impl Into<FieldName>) -> Result<AssetTag, BuilderError> {
self.builder.asset_tag(name)
}

#[inline]
pub fn add_asset_tag(
mut self,
Expand Down Expand Up @@ -355,7 +377,7 @@ impl TransitionBuilder {
Ok(self)
}

pub fn add_input(mut self, opout: Opout, state: PresistedState) -> Result<Self, BuilderError> {
pub fn add_input(mut self, opout: Opout, state: PersistedState) -> Result<Self, BuilderError> {
self.inputs.insert(Input::with(opout), state)?;
Ok(self)
}
Expand All @@ -374,13 +396,23 @@ impl TransitionBuilder {
.assignments_type(name, Some(self.transition_type))
}

pub fn add_owned_state_det(
mut self,
name: impl Into<FieldName>,
seal: impl Into<BuilderSeal<GraphSeal>>,
state: PersistedState,
) -> Result<Self, BuilderError> {
self.builder = self.builder.add_owned_state_det(name, seal, state)?;
Ok(self)
}

pub fn add_owned_state_raw(
mut self,
type_id: AssignmentType,
seal: impl Into<BuilderSeal<GraphSeal>>,
state: PresistedState,
state: PersistedState,
) -> Result<Self, BuilderError> {
if matches!(state, PresistedState::Amount(_, _, tag) if self.builder.asset_tag(type_id)? != tag)
if matches!(state, PersistedState::Amount(_, _, tag) if self.builder.asset_tag_raw(type_id)? != tag)
{
return Err(BuilderError::AssetTagInvalid(type_id));
}
Expand Down Expand Up @@ -415,7 +447,7 @@ impl TransitionBuilder {
value: impl Into<Amount>,
blinding: BlindingFactor,
) -> Result<Self, BuilderError> {
let tag = self.builder.asset_tag(type_id)?;
let tag = self.builder.asset_tag_raw(type_id)?;
let state = RevealedValue::with_blinding(value.into(), blinding, tag);
self.builder = self.builder.add_fungible_state_raw(type_id, seal, state)?;
Ok(self)
Expand All @@ -432,7 +464,7 @@ impl TransitionBuilder {
.builder
.assignments_type(&name, None)
.ok_or(BuilderError::AssignmentNotFound(name.clone()))?;
let tag = self.builder.asset_tag(type_id)?;
let tag = self.builder.asset_tag_raw(type_id)?;

self.builder =
self.builder
Expand Down Expand Up @@ -581,8 +613,16 @@ impl<Seal: ExposedSeal> OperationBuilder<Seal> {
.expect("schema should match interface: must be checked by the constructor")
}

pub fn asset_tag(&self, name: impl Into<FieldName>) -> Result<AssetTag, BuilderError> {
let name = name.into();
let type_id = self
.assignments_type(&name, None)
.ok_or(BuilderError::AssignmentNotFound(name.clone()))?;
self.asset_tag_raw(type_id)
}

#[inline]
pub fn asset_tag(&self, type_id: AssignmentType) -> Result<AssetTag, BuilderError> {
fn asset_tag_raw(&self, type_id: AssignmentType) -> Result<AssetTag, BuilderError> {
self.asset_tags
.get(&type_id)
.ok_or(BuilderError::AssetTagMissed(type_id))
Expand Down Expand Up @@ -652,23 +692,42 @@ impl<Seal: ExposedSeal> OperationBuilder<Seal> {
Ok(self)
}

fn add_owned_state_det(
self,
name: impl Into<FieldName>,
seal: impl Into<BuilderSeal<Seal>>,
state: PersistedState,
) -> Result<Self, BuilderError> {
let name = name.into();
let type_id = self
.assignments_type(&name, None)
.ok_or(BuilderError::AssignmentNotFound(name.clone()))?;
self.add_owned_state_raw(type_id, seal, state)
}

fn add_owned_state_raw(
self,
type_id: AssignmentType,
seal: impl Into<BuilderSeal<Seal>>,
state: PresistedState,
state: PersistedState,
) -> Result<Self, BuilderError> {
match state {
PresistedState::Void => self.add_rights_raw(type_id, seal),
PresistedState::Amount(value, blinding, tag) => self.add_fungible_state_raw(
type_id,
seal,
RevealedValue::with_blinding(value, blinding, tag),
),
PresistedState::Data(data, salt) => {
PersistedState::Void => self.add_rights_raw(type_id, seal),
PersistedState::Amount(value, blinding, tag) => {
if self.asset_tag_raw(type_id)? != tag {
return Err(BuilderError::AssetTagInvalid(type_id));
}

self.add_fungible_state_raw(
type_id,
seal,
RevealedValue::with_blinding(value, blinding, tag),
)
}
PersistedState::Data(data, salt) => {
self.add_data_raw(type_id, seal, RevealedData::with_salt(data, salt))
}
PresistedState::Attachment(attach, salt) => self.add_attachment_raw(
PersistedState::Attachment(attach, salt) => self.add_attachment_raw(
type_id,
seal,
RevealedAttach::with_salt(attach.id, attach.media_type, salt),
Expand Down Expand Up @@ -844,7 +903,7 @@ impl<Seal: ExposedSeal> OperationBuilder<Seal> {

fn complete(
self,
inputs: Option<&TinyOrdMap<Input, PresistedState>>,
inputs: Option<&TinyOrdMap<Input, PersistedState>>,
) -> (SubSchema, IfacePair, GlobalState, Assignments<Seal>, TinyOrdMap<AssignmentType, AssetTag>)
{
let owned_state = self.fungible.into_iter().map(|(id, vec)| {
Expand All @@ -871,7 +930,7 @@ impl<Seal: ExposedSeal> OperationBuilder<Seal> {
i.iter()
.filter(|(out, _)| out.prev_out.ty == id)
.map(|(_, ts)| match ts {
PresistedState::Amount(_, blinding, _) => *blinding,
PersistedState::Amount(_, blinding, _) => *blinding,
_ => panic!("previous state has invalid type"),
})
.collect::<Vec<_>>()
Expand Down
5 changes: 5 additions & 0 deletions src/interface/iimpl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,3 +300,8 @@ impl IfacePair {
self.iimpl.transition_type(name)
}
}

pub trait ContractClass {
fn schema() -> SubSchema;
fn main_iface_impl() -> IfaceImpl;
}
3 changes: 2 additions & 1 deletion src/interface/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub mod rgb21;
pub mod rgb25;
mod suppl;
mod filters;
pub(crate) mod resolver;

pub use builder::{BuilderError, ContractBuilder, TransitionBuilder};
pub use contract::{
Expand All @@ -43,7 +44,7 @@ pub use iface::{
ArgMap, ArgSpec, AssignIface, ExtensionIface, GenesisIface, GlobalIface, Iface, IfaceId,
OwnedIface, Req, TransitionIface, ValencyIface,
};
pub use iimpl::{IfaceImpl, IfacePair, ImplId, NamedField, NamedType, SchemaIfaces};
pub use iimpl::{ContractClass, IfaceImpl, IfacePair, ImplId, NamedField, NamedType, SchemaIfaces};
pub use rgb20::{rgb20, rgb20_stl, 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
43 changes: 43 additions & 0 deletions src/interface/resolver.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// RGB standard library for working with smart contracts on Bitcoin & Lightning
//
// SPDX-License-Identifier: Apache-2.0
//
// Written in 2019-2023 by
// Dr Maxim Orlovsky <[email protected]>
//
// Copyright (C) 2019-2023 LNP/BP Standards Association. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::convert::Infallible;

use strict_encoding::StrictDumb;

use crate::resolvers::ResolveHeight;
use crate::validation::{ResolveWitness, WitnessResolverError};
use crate::{WitnessAnchor, WitnessId, XAnchor, XPubWitness};

pub(crate) struct DumbResolver;

impl ResolveWitness for DumbResolver {
fn resolve_pub_witness(&self, _: WitnessId) -> Result<XPubWitness, WitnessResolverError> {
Ok(XPubWitness::strict_dumb())
}
}

impl ResolveHeight for DumbResolver {
type Error = Infallible;
fn resolve_anchor(&mut self, _: &XAnchor) -> Result<WitnessAnchor, Self::Error> {
Ok(WitnessAnchor::strict_dumb())
}
}
Loading
Loading