From d7c6ca27039f3defcb72fe57a7cfb26387afc82b Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Mon, 15 Apr 2024 14:03:42 +0200 Subject: [PATCH 1/3] core: be able to send an asset onchain Signed-off-by: Vincenzo Palazzo --- rgb-cln/src/plugin.rs | 8 ++++- rgb-cln/src/plugin/macros.rs | 19 +++++++++++ rgb-cln/src/plugin/walletrpc.rs | 54 ++++++++++++++++++++++--------- rgb-common/src/internal_wallet.rs | 43 +++++++++++++++++++++++- rgb-common/src/types.rs | 9 ++++++ 5 files changed, 115 insertions(+), 18 deletions(-) create mode 100644 rgb-cln/src/plugin/macros.rs diff --git a/rgb-cln/src/plugin.rs b/rgb-cln/src/plugin.rs index bf4c407..69df29c 100644 --- a/rgb-cln/src/plugin.rs +++ b/rgb-cln/src/plugin.rs @@ -25,6 +25,7 @@ use rgb_common::anyhow; use rgb_common::bitcoin::bip32::ExtendedPrivKey; use rgb_common::RGBManager; +mod macros; mod walletrpc; #[derive(Clone, Debug)] @@ -100,11 +101,16 @@ fn rgb_issue_asset(plugin: &mut Plugin, request: Value) -> Result, request: Value) -> Result { walletrpc::rgb_receive(plugin, request) } +#[rpc_method(rpc_name = "rgbsendasset", description = "RGB Send a asset on chain")] +fn rgb_send(plugin: &mut Plugin, request: Value) -> Result { + walletrpc::rgb_send(plugin, request) +} + // FIXME: this is just a test, we should remove it at some point #[rpc_method(rpc_name = "rgbinfo", description = "RGB Information")] fn rgb_info(plugin: &mut Plugin, request: Value) -> Result { diff --git a/rgb-cln/src/plugin/macros.rs b/rgb-cln/src/plugin/macros.rs new file mode 100644 index 0000000..65caef4 --- /dev/null +++ b/rgb-cln/src/plugin/macros.rs @@ -0,0 +1,19 @@ +//! Create macros +//! +//! Author: Vincenzo Palazzo + +macro_rules! howmuchfees { + ($cln:expr) => {{ + // Estimate the fee + let fees: Value = $cln + .state + .call("estimatefees", json::json!({})) + .map_err(|err| error!("{err}"))?; + log::trace!("estimated fee: {fees}"); + let minimum = fees + .get("feerate_floor") + .ok_or(error!("not able to find the feerate_floor in: `{fees}`"))?; + minimum.as_i64().unwrap_or_default() + }}; +} +pub(super) use howmuchfees; diff --git a/rgb-cln/src/plugin/walletrpc.rs b/rgb-cln/src/plugin/walletrpc.rs index e7ce164..a336448 100644 --- a/rgb-cln/src/plugin/walletrpc.rs +++ b/rgb-cln/src/plugin/walletrpc.rs @@ -12,12 +12,12 @@ use clightningrpc_plugin::error; use clightningrpc_plugin::errors::PluginError; use clightningrpc_plugin::plugin::Plugin; - -use rgb_common::bitcoin30; use rgb_common::core::ContractId; +use rgb_common::{bitcoin30, types}; use rgb_common::types::RgbInfo; +use crate::plugin::macros::howmuchfees; use crate::plugin::State; #[derive(Deserialize, Serialize)] @@ -203,25 +203,47 @@ pub fn rgb_receive(plugin: &mut Plugin, request: Value) -> Result for RgbSendRequest { + fn into(self) -> types::RGBSendAssetData { + types::RGBSendAssetData { + asset_id: self.asset_id, + amount: self.amount, + blinded_utxo: self.blinded_utxo, + donation: self.donation, + } + } +} + +pub fn rgb_send(plugin: &mut Plugin, request: Value) -> Result { + log::info!("calling rgb send with body `{request}`"); + let request: RgbSendRequest = json::from_value(request).map_err(|err| error!("{err}"))?; + let wallet = plugin.state.manager().wallet(); + let fee = howmuchfees!(plugin); + let minconf = 6; + let send = wallet.send_asset(&request.into(), fee as f32, minconf, |psbt| { + wallet.sing_with_master_key(psbt) + }); + let send = send.map_err(|err| error!("{err}"))?; + Ok(json::json!(send)) +} diff --git a/rgb-common/src/internal_wallet.rs b/rgb-common/src/internal_wallet.rs index 4df8af0..e42a0c3 100644 --- a/rgb-common/src/internal_wallet.rs +++ b/rgb-common/src/internal_wallet.rs @@ -4,12 +4,13 @@ use std::path::PathBuf; use std::str::FromStr; use std::sync::{Arc, Mutex}; +use amplify::map; use bdk; use bdk::blockchain::ElectrumBlockchain; use bdk::electrum_client::Client; use bdk::SyncOptions; use bp::seals::txout::CloseMethod; -use rgb_lib::wallet::{AssetNIA, ReceiveData, Recipient}; +use rgb_lib::wallet::SendResult; use strict_encoding::{FieldName, TypeName}; use crate::bitcoin::bip32::ChildNumber; @@ -21,8 +22,11 @@ use crate::bitcoin::Network; use crate::bitcoin::{ScriptBuf, TxOut}; use crate::bitcoin30::psbt::PartiallySignedTransaction as RgbPsbt; use crate::core::contract::Operation; +use crate::core::SecretSeal; use crate::json; use crate::lib::utils::load_rgb_runtime; +use crate::lib::wallet::RecipientData; +use crate::lib::wallet::{AssetNIA, ReceiveData, Recipient}; use crate::lib::wallet::{DatabaseType, Online, Wallet as RgbWallet, WalletData}; use crate::lib::BitcoinNetwork; use crate::rgb::persistence::Inventory; @@ -157,6 +161,43 @@ impl Wallet { Ok(addr) } + pub fn send_asset( + &self, + data: &types::RGBSendAssetData, + feerate: f32, + minconf: u8, + sign_psbt: F, + ) -> anyhow::Result + where + F: FnOnce(&mut bitcoin::psbt::PartiallySignedTransaction) -> anyhow::Result<()>, + { + let online = self + .online_wallet + .clone() + .ok_or(anyhow::anyhow!("Wallet is offline"))?; + let wallet = self.wallet.lock().unwrap(); + let seal = SecretSeal::from_str(&data.blinded_utxo)?; + let recipient_map = map! { + data.asset_id.clone() => vec![Recipient { + recipient_data: RecipientData::BlindedUTXO(seal), + amount: data.amount, + transport_endpoints: vec![self.proxy_endpoint.clone()], + }] + }; + + let psbt = wallet.send_begin( + online.clone(), + recipient_map, + data.donation, + feerate, + minconf, + )?; + let mut psbt = bitcoin::psbt::PartiallySignedTransaction::from_str(&psbt)?; + sign_psbt(&mut psbt)?; + let sendresult = wallet.send_end(online, psbt.to_string())?; + Ok(sendresult) + } + pub fn new_blind_receive( &self, asset_id: Option, diff --git a/rgb-common/src/types.rs b/rgb-common/src/types.rs index 0473f20..39f37a0 100644 --- a/rgb-common/src/types.rs +++ b/rgb-common/src/types.rs @@ -8,6 +8,15 @@ use crate::bitcoin::Txid; use crate::core::{Anchor, TransitionBundle}; use crate::std::contract::ContractId; +/// RGB Send asset data +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct RGBSendAssetData { + pub asset_id: String, + pub amount: u64, + pub blinded_utxo: String, + pub donation: bool, +} + /// RGB channel info #[derive(Debug, Clone, Deserialize, Serialize)] pub struct RgbInfo { From 96abe086897688c35b94439de9b7f4d75ce1cbab Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Mon, 15 Apr 2024 14:35:35 +0200 Subject: [PATCH 2/3] meta: update rust compiler version Signed-off-by: Vincenzo Palazzo --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 2bf5ad0..369f996 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -stable +1.77.2 From 8074b0650a2a4ca6223ffcdf0c36d59cc9c18524 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Mon, 15 Apr 2024 15:09:50 +0200 Subject: [PATCH 3/3] meta: run cargo audit Signed-off-by: Vincenzo Palazzo --- Cargo.lock | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a092ae8..795bb20 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1685,9 +1685,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.22" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", @@ -1866,7 +1866,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.5", + "socket2 0.4.10", "tokio", "tower-service", "tracing", @@ -2272,9 +2272,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "wasi", @@ -4661,6 +4661,12 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + [[package]] name = "wasm-bindgen" version = "0.2.89" @@ -4758,9 +4764,13 @@ dependencies = [ [[package]] name = "whoami" -version = "1.4.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" +checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" +dependencies = [ + "redox_syscall 0.4.1", + "wasite", +] [[package]] name = "winapi"