diff --git a/Cargo.lock b/Cargo.lock index 40e2a9a..4661c9f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -388,8 +388,7 @@ dependencies = [ [[package]] name = "bp-std" version = "0.11.0-beta.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "846c1beb631a1768371dd3f1afb6d6742588ee01d2bf61b5b368d1d5f19bbec7" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#72a734ed66b5ef4cb36d99b2b08bca457151a37e" dependencies = [ "amplify", "bech32", @@ -404,8 +403,7 @@ dependencies = [ [[package]] name = "bp-util" version = "0.11.0-beta.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdba42c645c3c6e2dba3ab44226c9c31d4dc701197e573c8013dd561e55e9a3b" +source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#e7996268b37fff67363c021f775a4bcb9421de52" dependencies = [ "amplify", "base64", @@ -427,8 +425,7 @@ dependencies = [ [[package]] name = "bp-wallet" version = "0.11.0-beta.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4e7274441a48cdf19b25ec9643dcc4a76f72fee14d3fda4a46131590304da" +source = "git+https://github.com/BP-WG/bp-wallet?branch=v0.11#e7996268b37fff67363c021f775a4bcb9421de52" dependencies = [ "amplify", "bp-esplora", @@ -667,8 +664,7 @@ dependencies = [ [[package]] name = "descriptors" version = "0.11.0-beta.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1538d1e2b6a64a815951b95db9cd8c5275bbd6a29fa96448005ad5755b1c033c" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#72a734ed66b5ef4cb36d99b2b08bca457151a37e" dependencies = [ "amplify", "bp-std", @@ -1331,8 +1327,7 @@ dependencies = [ [[package]] name = "psbt" version = "0.11.0-beta.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc077510950b86b180f5a2bd4f309ab6728f9b8ca1fdf3e84898f400bd7429f" +source = "git+https://github.com/BP-WG/bp-std?branch=v0.11#72a734ed66b5ef4cb36d99b2b08bca457151a37e" dependencies = [ "amplify", "base64", @@ -1519,7 +1514,8 @@ name = "rgb-runtime" version = "0.11.0-alpha.1" dependencies = [ "amplify", - "bp-dbc", + "baid58", + "bp-core", "bp-esplora", "bp-std", "bp-wallet", @@ -1536,7 +1532,7 @@ dependencies = [ [[package]] name = "rgb-std" version = "0.11.0-beta.1" -source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#5025eed9c1f25a80f7759f0a4b07265aa4e666ce" +source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#4dd76629a0fa23d067cc742eb67d84b9fc2d505b" dependencies = [ "amplify", "baid58", diff --git a/Cargo.toml b/Cargo.toml index 1be151d..2b42604 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ amplify = "4.5.0" baid58 = "0.4.4" strict_encoding = "2.6.1" strict_types = "1.6.3" -bp-dbc = "0.11.0-beta.1" +bp-core = "0.11.0-beta.1" bp-seals = "0.11.0-beta.1" bp-std = "0.11.0-beta.1" bp-wallet = "0.11.0-beta.1" @@ -59,8 +59,9 @@ name = "rgb_rt" [dependencies] amplify = { workspace = true } +baid58 = { workspace = true } strict_types = { workspace = true } -bp-dbc = { workspace = true } +bp-core = { workspace = true } bp-std = { workspace = true } bp-wallet = { workspace = true, features = ["fs"] } bp-esplora = { workspace = true, optional = true } @@ -85,3 +86,9 @@ features = [ "all" ] aluvm = { git = "https://github.com/AluVM/rust-aluvm", branch = "v0.11" } rgb-core = { git = "https://github.com/RGB-WG/rgb-core", branch = "v0.11" } rgb-std = { git = "https://github.com/RGB-WG/rgb-std", branch = "v0.11" } + +descriptors = { git = "https://github.com/BP-WG/bp-std", branch = "v0.11" } +psbt = { git = "https://github.com/BP-WG/bp-std", branch = "v0.11" } +bp-std = { git = "https://github.com/BP-WG/bp-std", branch = "v0.11" } +bp-wallet = { git = "https://github.com/BP-WG/bp-wallet", branch = "v0.11" } +bp-util = { git = "https://github.com/BP-WG/bp-wallet", branch = "v0.11" } diff --git a/cli/src/args.rs b/cli/src/args.rs index 59c89d8..1e841de 100644 --- a/cli/src/args.rs +++ b/cli/src/args.rs @@ -21,9 +21,9 @@ #![allow(clippy::needless_update)] // Caused by the From derivation macro -use bp_util::DescriptorOpts; +use bp_util::{Config, DescriptorOpts}; use bpstd::XpubDerivable; -use rgb_rt::{DescriptorRgb, Runtime, RuntimeError, TapretKey}; +use rgb_rt::{RgbDescr, Runtime, RuntimeError, TapretKey}; use crate::Command; @@ -35,7 +35,7 @@ pub struct DescrRgbOpts { } impl DescriptorOpts for DescrRgbOpts { - type Descr = DescriptorRgb; + type Descr = RgbDescr; fn is_some(&self) -> bool { self.tapret_key_only.is_some() } @@ -63,11 +63,13 @@ impl Default for RgbArgs { } impl RgbArgs { - pub fn rgb_runtime(&self) -> Result { + pub fn rgb_runtime(&self, config: &Config) -> Result { + let bprt = self.inner.bp_runtime::(config)?; eprint!("Loading stock ... "); - let runtime = Runtime::::load_pure_rgb( + let runtime = Runtime::::load_attach( self.general.data_dir.clone(), self.general.network, + bprt, )?; eprintln!("success"); diff --git a/cli/src/command.rs b/cli/src/command.rs index a315224..cf06f7d 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -25,16 +25,16 @@ use std::str::FromStr; use amplify::confinement::U16; use bp_util::{Config, Exec}; -use bpstd::Txid; -use rgb_rt::{DescriptorRgb, RuntimeError}; -use rgbinvoice::{InvoiceState, RgbInvoice, RgbTransport}; +use bpstd::{Sats, Txid}; +use rgb_rt::{DescriptorRgb, RgbDescr, RgbKeychain, RuntimeError}; +use rgbinvoice::{Beneficiary, InvoiceState, RgbInvoice, RgbTransport}; use rgbstd::containers::{Bindle, Transfer, UniversalBindle}; use rgbstd::contract::{ContractId, GenesisSeal, GraphSeal, StateType}; -use rgbstd::interface::{ContractBuilder, FilterExclude, SchemaIfaces}; +use rgbstd::interface::{ContractBuilder, FilterExclude, IfaceId, SchemaIfaces}; use rgbstd::persistence::{Inventory, Stash}; use rgbstd::schema::SchemaId; use rgbstd::SealDefinition; -use seals::txout::{CloseMethod, ExplicitSeal, TxPtr}; +use seals::txout::{CloseMethod, ExplicitSeal}; use strict_types::encoding::{FieldName, TypeName}; use strict_types::StrictVal; @@ -112,9 +112,6 @@ pub enum Command { /// Schema name to use for the contract. schema: SchemaId, //String, - /// Interface name to use for the contract. - iface: String, - /// File containing contract genesis description in YAML format. contract: PathBuf, }, @@ -122,6 +119,10 @@ pub enum Command { /// Create new invoice. #[display("invoice")] Invoice { + /// Force address-based invoice. + #[clap(short, long)] + address_based: bool, + /// Contract identifier. contract_id: ContractId, @@ -130,9 +131,6 @@ pub enum Command { /// Value to transfer. value: u64, - - /// Seal to get the transfer to. - seal: ExplicitSeal, }, /// Create new transfer. @@ -208,10 +206,14 @@ impl Exec for RgbArgs { fn exec(self, config: Config, _name: &'static str) -> Result<(), RuntimeError> { match &self.command { Command::Bp(cmd) => { - self.inner.translate(cmd).exec(config, "rgb")?; + return self + .inner + .translate(cmd) + .exec(config, "rgb") + .map_err(RuntimeError::from); } Command::Schemata => { - let runtime = self.rgb_runtime()?; + let runtime = self.rgb_runtime(&config)?; for id in runtime.schema_ids()? { print!("{id} "); for iimpl in runtime.schema(id)?.iimpls.values() { @@ -222,20 +224,20 @@ impl Exec for RgbArgs { } } Command::Interfaces => { - let runtime = self.rgb_runtime()?; + let runtime = self.rgb_runtime(&config)?; for (id, name) in runtime.ifaces()? { println!("{} {id}", name); } } Command::Contracts => { - let runtime = self.rgb_runtime()?; + let runtime = self.rgb_runtime(&config)?; for id in runtime.contract_ids()? { println!("{id}"); } } Command::Import { armored, file } => { - let mut runtime = self.rgb_runtime()?; + let mut runtime = self.rgb_runtime(&config)?; if *armored { todo!() } else { @@ -287,7 +289,7 @@ impl Exec for RgbArgs { contract, file, } => { - let mut runtime = self.rgb_runtime()?; + let mut runtime = self.rgb_runtime(&config)?; let bindle = runtime .export_contract(*contract) .map_err(|err| err.to_string())?; @@ -301,8 +303,8 @@ impl Exec for RgbArgs { } Command::State { contract_id, iface } => { - let mut runtime = self.rgb_runtime()?; - let bp_runtime = self.bp_runtime::(&config)?; + let mut runtime = self.rgb_runtime(&config)?; + let bp_runtime = self.bp_runtime::(&config)?; runtime.attach(bp_runtime.detach()); let iface = runtime.iface_by_name(&tn!(iface.to_owned()))?.clone(); @@ -341,18 +343,34 @@ impl Exec for RgbArgs { // TODO: Print out other types of state } } - Command::Issue { - schema, - iface: iface_name, - contract, - } => { - let mut runtime = self.rgb_runtime()?; + Command::Issue { schema, contract } => { + let mut runtime = self.rgb_runtime(&config)?; + + let file = fs::File::open(contract)?; + + let code = serde_yaml::from_reader::<_, serde_yaml::Value>(file)?; + + let code = code + .as_mapping() + .expect("invalid YAML root-level structure"); + + let iface_name = code + .get("interface") + .expect("contract must specify interface under which it is constructed") + .as_str() + .expect("interface name must be a string"); let SchemaIfaces { ref schema, ref iimpls, } = runtime.schema(*schema)?; let iface_name = tn!(iface_name.to_owned()); - let iface = runtime.iface_by_name(&iface_name)?.clone(); + let iface = runtime + .iface_by_name(&iface_name) + .or_else(|_| { + let id = IfaceId::from_str(iface_name.as_str())?; + runtime.iface_by_id(id).map_err(RuntimeError::from) + })? + .clone(); let iface_id = iface.iface_id(); let iface_impl = iimpls.get(&iface_id).ok_or_else(|| { RuntimeError::Custom(format!( @@ -361,8 +379,6 @@ impl Exec for RgbArgs { })?; let types = &schema.type_system; - let file = fs::File::open(contract)?; - let mut builder = ContractBuilder::with( iface.clone(), schema.clone(), @@ -370,12 +386,6 @@ impl Exec for RgbArgs { self.general.network.is_testnet(), )?; - let code = serde_yaml::from_reader::<_, serde_yaml::Value>(file)?; - - let code = code - .as_mapping() - .expect("invalid YAML root-level structure"); - if let Some(globals) = code.get("globals") { for (name, val) in globals .as_mapping() @@ -499,27 +509,52 @@ impl Exec for RgbArgs { ); } Command::Invoice { + address_based, contract_id, iface, value, - seal, } => { - let mut runtime = self.rgb_runtime()?; + let mut runtime = self.rgb_runtime(&config)?; let iface = TypeName::try_from(iface.to_owned()).expect("invalid interface name"); - let seal = GraphSeal::from(seal); + + let outpoint = runtime + .wallet() + .coinselect(Sats::ZERO, |utxo| { + RgbKeychain::contains_rgb(utxo.terminal.keychain) + }) + .next(); + let beneficiary = match (address_based, outpoint) { + (true, _) | (false, None) => { + let addr = runtime + .wallet() + .addresses(RgbKeychain::Rgb) + .next() + .expect("no addresses left") + .addr; + Beneficiary::WitnessUtxo(addr) + } + (_, Some(outpoint)) => { + let seal = GraphSeal::new( + runtime.wallet().seal_close_method(), + outpoint.txid, + outpoint.vout, + ); + runtime.store_seal_secret(SealDefinition::Bitcoin(seal))?; + Beneficiary::BlindedSeal(seal.to_concealed_seal()) + } + }; let invoice = RgbInvoice { transports: vec![RgbTransport::UnspecifiedMeans], contract: Some(*contract_id), iface: Some(iface), operation: None, assignment: None, - beneficiary: seal.to_concealed_seal().into(), + beneficiary, owned_state: InvoiceState::Amount(*value), network: None, expiry: None, unknown_query: none!(), }; - runtime.store_seal_secret(SealDefinition::Bitcoin(seal))?; println!("{invoice}"); } #[allow(unused_variables)] @@ -570,7 +605,7 @@ impl Exec for RgbArgs { println!("{s}"); } Command::Dump { root_dir } => { - let runtime = self.rgb_runtime()?; + let runtime = self.rgb_runtime(&config)?; fs::remove_dir_all(root_dir).ok(); fs::create_dir_all(format!("{root_dir}/stash/schemata"))?; @@ -673,7 +708,7 @@ impl Exec for RgbArgs { eprintln!("{status}"); } Command::Accept { force, file } => { - let mut runtime = self.rgb_runtime()?; + let mut runtime = self.rgb_runtime(&config)?; let mut resolver = self.resolver(); let bindle = Bindle::::load_file(file)?; let transfer = bindle @@ -731,6 +766,8 @@ impl Exec for RgbArgs { } } + println!(); + Ok(()) } } diff --git a/src/descriptor.rs b/src/descriptor.rs index 5238dcc..318714c 100644 --- a/src/descriptor.rs +++ b/src/descriptor.rs @@ -19,20 +19,25 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::BTreeMap; -use std::ops::Range; +use std::collections::{BTreeMap, BTreeSet}; use std::str::FromStr; use std::{iter, vec}; +use amplify::Wrapper; +use bp::dbc::tapret::TapretCommitment; +use bp::seals::txout::CloseMethod; use bpstd::{ - CompressedPk, Derive, DeriveCompr, DeriveSet, DeriveXOnly, DerivedScript, Idx, IndexError, - IndexParseError, KeyOrigin, NormalIndex, TapDerivation, Terminal, XOnlyPk, XpubDerivable, - XpubSpec, + CompressedPk, Derive, DeriveCompr, DeriveSet, DeriveXOnly, DerivedScript, Idx, IdxBase, + IndexError, IndexParseError, KeyOrigin, Keychain, NormalIndex, TapDerivation, Terminal, + XOnlyPk, XpubDerivable, XpubSpec, }; -use dbc::tapret::TapretCommitment; -use descriptors::{Descriptor, DescriptorStd, TrKey}; +use descriptors::{Descriptor, StdDescr, TrKey}; use indexmap::IndexMap; +pub trait DescriptorRgb: Descriptor { + fn seal_close_method(&self) -> CloseMethod; +} + #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)] #[derive(Serialize, Deserialize)] #[serde(crate = "serde_crate", rename_all = "camelCase")] @@ -52,6 +57,12 @@ pub enum RgbKeychain { } impl RgbKeychain { + pub const RGB_ALL: [RgbKeychain; 2] = [RgbKeychain::Rgb, RgbKeychain::Tapret]; + + pub fn contains_rgb(keychain: impl Into) -> bool { + let k = keychain.into().into_inner(); + k == Self::Rgb as u8 || k == Self::Tapret as u8 + } pub fn is_seal(self) -> bool { self == Self::Rgb || self == Self::Tapret } } @@ -73,6 +84,10 @@ impl FromStr for RgbKeychain { } } +impl From for Keychain { + fn from(keychain: RgbKeychain) -> Self { Keychain::from(keychain as u8) } +} + #[derive(Clone, Eq, PartialEq, Hash, Debug)] #[derive(Serialize, Deserialize)] #[serde(crate = "serde_crate", rename_all = "camelCase")] @@ -91,11 +106,19 @@ impl TapretKey { } impl Derive for TapretKey { - fn keychains(&self) -> Range { - 0..2 /* FIXME */ + #[inline] + fn default_keychain(&self) -> Keychain { RgbKeychain::Rgb.into() } + + fn keychains(&self) -> BTreeSet { + bset![ + RgbKeychain::External.into(), + RgbKeychain::Internal.into(), + RgbKeychain::Rgb.into(), + RgbKeychain::Tapret.into(), + ] } - fn derive(&self, change: u8, index: impl Into) -> DerivedScript { + fn derive(&self, change: impl Into, index: impl Into) -> DerivedScript { // TODO: Apply tweaks let internal_key = self.internal_key.derive(change, index); DerivedScript::TaprootKeyOnly(internal_key.into()) @@ -120,6 +143,37 @@ impl From> for TapretKey { } } +impl Descriptor for TapretKey { + type KeyIter<'k> = iter::Once<&'k K> where Self: 'k, K: 'k; + type VarIter<'v> = iter::Empty<&'v ()> where Self: 'v, (): 'v; + type XpubIter<'x> = iter::Once<&'x XpubSpec> where Self: 'x; + + fn keys(&self) -> Self::KeyIter<'_> { iter::once(&self.internal_key) } + fn vars(&self) -> Self::VarIter<'_> { iter::empty() } + fn xpubs(&self) -> Self::XpubIter<'_> { iter::once(self.internal_key.xpub_spec()) } + + fn compr_keyset(&self, _terminal: Terminal) -> IndexMap { + IndexMap::new() + } + + fn xonly_keyset(&self, terminal: Terminal) -> IndexMap { + let mut map = IndexMap::with_capacity(1); + let key = self.internal_key.derive(terminal.keychain, terminal.index); + map.insert( + key, + TapDerivation::with_internal_pk( + self.internal_key.xpub_spec().origin().clone(), + terminal, + ), + ); + map + } +} + +impl DescriptorRgb for TapretKey { + fn seal_close_method(&self) -> CloseMethod { CloseMethod::TapretFirst } +} + #[derive(Clone, Eq, PartialEq, Hash, Debug, From)] #[derive(Serialize, Deserialize)] #[serde( @@ -130,57 +184,87 @@ impl From> for TapretKey { deserialize = "S::XOnly: serde::Deserialize<'de>" ) )] -pub enum DescriptorRgb { - None, - +pub enum RgbDescr { #[from] TapretKey(TapretKey), } -impl Default for DescriptorRgb { - fn default() -> Self { Self::None } -} +impl Derive for RgbDescr { + fn default_keychain(&self) -> Keychain { + match self { + RgbDescr::TapretKey(d) => d.default_keychain(), + } + } -impl Derive for DescriptorRgb { - fn keychains(&self) -> Range { + fn keychains(&self) -> BTreeSet { match self { - DescriptorRgb::None => Range::default(), - DescriptorRgb::TapretKey(d) => d.keychains(), + RgbDescr::TapretKey(d) => d.keychains(), } } - fn derive(&self, change: u8, index: impl Into) -> DerivedScript { + fn derive(&self, change: impl Into, index: impl Into) -> DerivedScript { match self { - DescriptorRgb::None => todo!(), - DescriptorRgb::TapretKey(d) => d.derive(change, index), + RgbDescr::TapretKey(d) => d.derive(change, index), } } } -impl + DeriveCompr + DeriveXOnly> Descriptor - for DescriptorRgb +impl + DeriveCompr + DeriveXOnly> Descriptor for RgbDescr where Self: Derive { type KeyIter<'k> = vec::IntoIter<&'k K> where Self: 'k, K: 'k; type VarIter<'v> = iter::Empty<&'v ()> where Self: 'v, (): 'v; type XpubIter<'x> = vec::IntoIter<&'x XpubSpec> where Self: 'x; - fn keys(&self) -> Self::KeyIter<'_> { todo!() } + fn keys(&self) -> Self::KeyIter<'_> { + match self { + RgbDescr::TapretKey(d) => d.keys().collect::>(), + } + .into_iter() + } - fn vars(&self) -> Self::VarIter<'_> { todo!() } + fn vars(&self) -> Self::VarIter<'_> { + match self { + RgbDescr::TapretKey(d) => d.vars(), + } + } - fn xpubs(&self) -> Self::XpubIter<'_> { todo!() } + fn xpubs(&self) -> Self::XpubIter<'_> { + match self { + RgbDescr::TapretKey(d) => d.xpubs().collect::>(), + } + .into_iter() + } + + fn compr_keyset(&self, terminal: Terminal) -> IndexMap { + match self { + RgbDescr::TapretKey(d) => d.compr_keyset(terminal), + } + } - fn compr_keyset(&self, _terminal: Terminal) -> IndexMap { todo!() } + fn xonly_keyset(&self, terminal: Terminal) -> IndexMap { + match self { + RgbDescr::TapretKey(d) => d.xonly_keyset(terminal), + } + } +} - fn xonly_keyset(&self, _terminal: Terminal) -> IndexMap { todo!() } +impl + DeriveCompr + DeriveXOnly> DescriptorRgb + for RgbDescr +where Self: Derive +{ + fn seal_close_method(&self) -> CloseMethod { + match self { + RgbDescr::TapretKey(d) => d.seal_close_method(), + } + } } -impl From for DescriptorRgb { - fn from(descr: DescriptorStd) -> Self { +impl From for RgbDescr { + fn from(descr: StdDescr) -> Self { match descr { - DescriptorStd::Wpkh(_) => todo!(), - DescriptorStd::TrKey(tr) => DescriptorRgb::TapretKey(tr.into()), + StdDescr::Wpkh(_) => todo!(), + StdDescr::TrKey(tr) => RgbDescr::TapretKey(tr.into()), _ => todo!(), } } diff --git a/src/descriptor_old.rs b/src/descriptor_old.rs deleted file mode 100644 index 4a79b03..0000000 --- a/src/descriptor_old.rs +++ /dev/null @@ -1,165 +0,0 @@ -// RGB smart contract wallet runtime -// -// SPDX-License-Identifier: Apache-2.0 -// -// Written in 2019-2023 by -// Dr Maxim Orlovsky -// -// 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::collections::{BTreeMap, BTreeSet}; -use std::fmt::{self, Display, Formatter}; -use std::ops::Range; - -use bitcoin::bip32::{ChildNumber, ExtendedPubKey}; -use bitcoin::secp256k1::SECP256K1; -use bitcoin::ScriptBuf; -use bp::dbc::tapret::{TapretCommitment, TapretPathProof, TapretProof}; -use bp::{ScriptPubkey, TapNodeHash}; -use commit_verify::ConvolveCommit; - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] -#[display("*/{app}/{index}")] -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct TerminalPath { - pub app: u32, - pub index: u32, -} - -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -pub struct DeriveInfo { - pub terminal: TerminalPath, - pub tweak: Option, -} - -impl Display for DeriveInfo { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - Display::fmt(&self.terminal, f)?; - if let Some(tweak) = &self.tweak { - write!(f, "%{tweak}")?; - } - Ok(()) - } -} - -impl DeriveInfo { - pub fn with(app: u32, index: u32, tweak: Option) -> Self { - DeriveInfo { - terminal: TerminalPath { app, index }, - tweak, - } - } -} - -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Tapret { - pub xpub: ExtendedPubKey, - // pub script: Option<>, - pub taprets: BTreeMap>, -} - -impl Display for Tapret { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.write_str("tapret(")?; - Display::fmt(&self.xpub, f)?; - let mut first = true; - if f.alternate() { - for (terminal, taprets) in &self.taprets { - if first { - f.write_str(",")?; - first = false; - } else { - f.write_str(" ")?; - } - Display::fmt(terminal, f)?; - for tapret in taprets { - f.write_str("&")?; - Display::fmt(tapret, f)?; - } - } - } else { - f.write_str(", ...")?; - } - f.write_str(")") - } -} - -#[derive(Clone, PartialEq, Eq, Hash, Debug, Display)] -#[display(inner)] -#[derive(Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub enum RgbDescr { - Tapret(Tapret), -} - -pub trait SpkDescriptor { - // TODO: Replace index with UnhardenedIndex - fn derive(&self, app: u32, indexes: Range) -> BTreeMap; -} - -impl SpkDescriptor for Tapret { - fn derive(&self, app: u32, indexes: Range) -> BTreeMap { - let mut spks = BTreeMap::new(); - - for index in indexes { - let key = self - .xpub - .derive_pub(SECP256K1, &[ - ChildNumber::from_normal_idx(app) - .expect("application index must be unhardened"), - ChildNumber::from_normal_idx(index) - .expect("derivation index must be unhardened"), - ]) - .expect("unhardened derivation"); - - let xonly = key.to_x_only_pub(); - spks.insert( - DeriveInfo::with(app, index, None), - ScriptBuf::new_v1_p2tr(SECP256K1, xonly, None), - ); - for tweak in self - .taprets - .get(&TerminalPath { app, index }) - .into_iter() - .flatten() - { - let script = ScriptPubkey::p2tr(xonly.into(), None::); - let proof = TapretProof { - path_proof: TapretPathProof::root(tweak.nonce), - internal_pk: xonly.into(), - }; - let (spk, _) = script - .convolve_commit(&proof, &tweak.mpc) - .expect("malicious tapret value - an inverse of a key"); - spks.insert( - DeriveInfo::with(app, index, Some(tweak.clone())), - ScriptBuf::from_bytes(spk.to_inner()), - ); - } - } - spks - } -} - -impl SpkDescriptor for RgbDescr { - fn derive(&self, app: u32, indexes: Range) -> BTreeMap { - match self { - RgbDescr::Tapret(tapret) => tapret.derive(app, indexes), - } - } -} diff --git a/src/lib.rs b/src/lib.rs index 3d69c31..bc471d8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,5 +31,5 @@ extern crate serde_crate as serde; mod runtime; mod descriptor; -pub use descriptor::{DescriptorRgb, RgbKeychain, TapretKey}; +pub use descriptor::{DescriptorRgb, RgbDescr, RgbKeychain, TapretKey}; pub use runtime::{Runtime, RuntimeError}; diff --git a/src/runtime.rs b/src/runtime.rs index 662e915..350ad6c 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -28,7 +28,6 @@ use std::{fs, io}; use bpstd::{AddressNetwork, Network, XpubDerivable}; use bpwallet::Wallet; -use descriptors::Descriptor; use rgbfs::StockFs; use rgbstd::containers::{Contract, LoadError, Transfer}; use rgbstd::interface::{BuilderError, OutpointFilter}; @@ -38,7 +37,7 @@ use rgbstd::validation::{self, ResolveTx}; use rgbstd::Output; use strict_types::encoding::{DeserializeError, Ident, SerializeError}; -use crate::DescriptorRgb; +use crate::{DescriptorRgb, RgbDescr}; #[derive(Debug, Display, Error, From)] #[display(inner)] @@ -65,13 +64,18 @@ pub enum RuntimeError { #[from] Builder(BuilderError), - /// wallet with id '{0}' is not known to the system + /// wallet with id '{0}' is not known to the system. #[display(doc_comments)] WalletUnknown(Ident), #[from] InvalidConsignment(validation::Status), + /// invalid identifier. + #[from] + #[display(doc_comments)] + InvalidId(baid58::Baid58ParseError), + /// the contract source doesn't fit requirements imposed by the used schema. /// /// {0} @@ -94,26 +98,26 @@ impl From for RuntimeError { } #[derive(Getters)] -pub struct Runtime = DescriptorRgb, K = XpubDerivable> { +pub struct Runtime = RgbDescr, K = XpubDerivable> { stock_path: PathBuf, stock: Stock, #[getter(as_mut)] - wallet: Wallet, + wallet: Wallet, #[getter(as_copy)] network: Network, } -impl, K> Deref for Runtime { +impl, K> Deref for Runtime { type Target = Stock; fn deref(&self) -> &Self::Target { &self.stock } } -impl, K> DerefMut for Runtime { +impl, K> DerefMut for Runtime { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.stock } } -impl, K> OutpointFilter for Runtime { +impl, K> OutpointFilter for Runtime { fn include_output(&self, output: Output) -> bool { self.wallet .coins() @@ -122,23 +126,7 @@ impl, K> OutpointFilter for Runtime { } #[cfg(feature = "serde")] -impl, K> Runtime -where - D: Default, - for<'de> D: serde::Serialize + serde::Deserialize<'de>, - for<'de> bpwallet::WalletDescr: serde::Serialize + serde::Deserialize<'de>, -{ - pub fn load_pure_rgb(data_dir: PathBuf, network: Network) -> Result { - Self::load_attach( - data_dir, - network, - bpwallet::Runtime::new_standard(D::default(), network /* TODO: add layer 2 */), - ) - } -} - -#[cfg(feature = "serde")] -impl, K> Runtime +impl, K> Runtime where for<'de> D: serde::Serialize + serde::Deserialize<'de>, for<'de> bpwallet::WalletDescr: serde::Serialize + serde::Deserialize<'de>, @@ -220,7 +208,7 @@ where } } -impl, K> Runtime { +impl, K> Runtime { fn store(&mut self) { self.stock .store(&self.stock_path) @@ -306,6 +294,6 @@ impl, K> Runtime { } } -impl, K> Drop for Runtime { +impl, K> Drop for Runtime { fn drop(&mut self) { self.store() } }