From 30ccb1a55dc71b800fb03c461fdfac3ec49c4ea4 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sun, 7 Jan 2024 14:09:15 +0100 Subject: [PATCH] fungible operation history --- Cargo.lock | 6 ++--- cli/src/command.rs | 44 ++++++++++++++++++++++++------ src/runtime.rs | 67 +++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 102 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3974e63..75d115b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1529,7 +1529,7 @@ dependencies = [ [[package]] name = "rgb-core" version = "0.11.0-beta.4" -source = "git+https://github.com/RGB-WG/rgb-core?branch=v0.11#b1232f5f77ff253bbe79bdcee04a2cf0d4ef0c05" +source = "git+https://github.com/RGB-WG/rgb-core?branch=v0.11#2b59903039770df4766c9681bc74691382308ef5" dependencies = [ "aluvm", "amplify", @@ -1549,7 +1549,7 @@ dependencies = [ [[package]] name = "rgb-invoice" version = "0.11.0-beta.4" -source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#b71d58fd087506746347c68d676b8079e3b751d3" +source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#9db17e1a6b08d3817ffeebdfb85a4b405818fec4" dependencies = [ "amplify", "baid58", @@ -1618,7 +1618,7 @@ dependencies = [ [[package]] name = "rgb-std" version = "0.11.0-beta.4" -source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#b71d58fd087506746347c68d676b8079e3b751d3" +source = "git+https://github.com/RGB-WG/rgb-std?branch=v0.11#9db17e1a6b08d3817ffeebdfb85a4b405818fec4" dependencies = [ "amplify", "baid58", diff --git a/cli/src/command.rs b/cli/src/command.rs index 0064941..9ee4179 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -31,12 +31,12 @@ use psbt::{Psbt, PsbtVer}; use rgb_rt::{DescriptorRgb, RgbKeychain, RuntimeError, TransferParams}; use rgbstd::containers::{Bindle, BuilderSeal, Transfer, UniversalBindle}; use rgbstd::contract::{ContractId, GenesisSeal, GraphSeal, StateType}; -use rgbstd::interface::{ContractBuilder, FilterExclude, IfaceId, SchemaIfaces}; +use rgbstd::interface::{AmountChange, ContractBuilder, FilterExclude, IfaceId, SchemaIfaces}; use rgbstd::invoice::{Beneficiary, RgbInvoice, RgbInvoiceBuilder, XChainNet}; use rgbstd::persistence::{Inventory, Stash}; use rgbstd::schema::SchemaId; use rgbstd::validation::Validity; -use rgbstd::{OutputSeal, XChain}; +use rgbstd::{OutputSeal, XChain, XOutputSeal}; use seals::txout::CloseMethod; use strict_types::encoding::{FieldName, TypeName}; use strict_types::StrictVal; @@ -128,11 +128,15 @@ pub enum Command { iface: String, }, - /// Print operation history - #[display("history")] - History { + /// Print operation history for a default fungible token under a given + /// interface + #[display("history-fungible")] + HistoryFungible { /// Contract identifier - contract_id: Option, + contract_id: ContractId, + + /// Interface to interpret the state data + iface: String, }, /// Display all known UTXOs belonging to this wallet @@ -335,8 +339,32 @@ impl Exec for RgbArgs { None } - Command::History { contract_id: _ } => { - todo!(); + Command::HistoryFungible { contract_id, iface } => { + let runtime = self.rgb_runtime(&config)?; + let iface: TypeName = tn!(iface.clone()); + let history = runtime.fungible_history(*contract_id, iface)?; + println!("Amount\tCounterparty\tWitness Id"); + for (id, op) in history { + let (cparty, more) = match op.state_change { + AmountChange::Dec(_) => { + (op.beneficiaries.first(), op.beneficiaries.len().saturating_sub(1)) + } + AmountChange::Zero => continue, + AmountChange::Inc(_) => { + (op.payers.first(), op.payers.len().saturating_sub(1)) + } + }; + let more = if more > 0 { + format!(" (+{more})") + } else { + s!("") + }; + let cparty = cparty + .map(XOutputSeal::to_string) + .unwrap_or_else(|| s!("none")); + println!("{}\t{}{}\t{}", op.state_change, cparty, more, id); + } + None } Command::Import { armored, file } => { diff --git a/src/runtime.rs b/src/runtime.rs index 56b7e6c..11c1451 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -21,6 +21,7 @@ #![allow(clippy::result_large_err)] +use std::collections::HashMap; use std::convert::Infallible; use std::io; use std::io::ErrorKind; @@ -32,12 +33,16 @@ use bpstd::{Network, XpubDerivable}; use bpwallet::Wallet; use rgbfs::StockFs; use rgbstd::containers::{Contract, LoadError, Transfer}; -use rgbstd::interface::{BuilderError, OutpointFilter}; -use rgbstd::persistence::{Inventory, InventoryDataError, InventoryError, StashError, Stock}; +use rgbstd::interface::{ + AmountChange, BuilderError, ContractError, IfaceOp, OutpointFilter, WitnessFilter, +}; +use rgbstd::persistence::{ + Inventory, InventoryDataError, InventoryError, Stash, StashError, Stock, +}; use rgbstd::resolvers::ResolveHeight; use rgbstd::validation::{self, ResolveWitness}; -use rgbstd::{ContractId, XChain, XOutpoint}; -use strict_types::encoding::{DecodeError, DeserializeError, Ident, SerializeError}; +use rgbstd::{AssignmentWitness, ContractId, WitnessId, XChain, XOutpoint}; +use strict_types::encoding::{DecodeError, DeserializeError, Ident, SerializeError, TypeName}; use crate::{DescriptorRgb, RgbDescr}; @@ -67,6 +72,12 @@ pub enum RuntimeError { #[from] Builder(BuilderError), + #[from] + History(HistoryError), + + #[from] + Contract(ContractError), + #[from] PsbtDecode(psbt::DecodeError), @@ -111,6 +122,7 @@ impl From for RuntimeError { pub struct Runtime = RgbDescr, K = XpubDerivable> { stock_path: PathBuf, #[getter(as_mut)] + // TODO: Parametrize by the stock stock: Stock, bprt: bpwallet::Runtime, } @@ -134,6 +146,16 @@ impl, K> OutpointFilter for Runtime { } } +impl, K> WitnessFilter for Runtime { + fn include_witness(&self, witness: impl Into) -> bool { + let witness = witness.into(); + self.wallet() + .transactions() + .keys() + .any(|txid| AssignmentWitness::Present(WitnessId::Bitcoin(*txid)) == witness) + } +} + pub struct ContractOutpointsFilter<'runtime, D: DescriptorRgb, K> { pub contract_id: ContractId, pub filter: &'runtime Runtime, @@ -234,4 +256,41 @@ impl, K> Runtime { .accept_transfer(transfer, resolver, force) .map_err(RuntimeError::from) } + + // TODO: Integrate into BP Wallet `TxRow` as L2 and provide transactional info + pub fn fungible_history( + &self, + contract_id: ContractId, + iface_name: impl Into, + ) -> Result>, RuntimeError> { + let iface_name = iface_name.into(); + let iface = self.stock.iface_by_name(&iface_name)?; + let default_op = iface + .default_operation + .as_ref() + .ok_or(HistoryError::NoDefaultOp)?; + let state_name = iface + .transitions + .get(default_op) + .ok_or(HistoryError::DefaultOpNotTransition)? + .default_assignment + .as_ref() + .ok_or(HistoryError::NoDefaultAssignment)? + .clone(); + let contract = self.stock.contract_iface_named(contract_id, iface_name)?; + contract + .fungible_ops::(state_name, self, self) + .map_err(RuntimeError::from) + } +} + +#[derive(Debug, Display, Error, From)] +#[display(doc_comments)] +pub enum HistoryError { + /// interface doesn't define default operation + NoDefaultOp, + /// default operation defined by the interface is not a state transition + DefaultOpNotTransition, + /// interface doesn't define default fungible state + NoDefaultAssignment, }