From fcf46e17b7fe0e0f7507a2619b519265e1835c9e Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Wed, 10 Apr 2024 19:45:29 +0200 Subject: [PATCH] cln: reworking channel creation Signed-off-by: Vincenzo Palazzo --- rgb-cln/src/plugin.rs | 67 ++---------------- rgb-cln/src/plugin/walletrpc.rs | 64 ++++++++++++++++-- rgb-common/src/internal_wallet.rs | 109 ++++++++++++++++++++++++++++-- rgb-common/src/rgb_manager.rs | 88 +++++++++++------------- 4 files changed, 205 insertions(+), 123 deletions(-) diff --git a/rgb-cln/src/plugin.rs b/rgb-cln/src/plugin.rs index d588c8a..2d6f81f 100644 --- a/rgb-cln/src/plugin.rs +++ b/rgb-cln/src/plugin.rs @@ -12,7 +12,7 @@ use lightning_signer::bitcoin as vlsbtc; use lightning_signer::signer::derive::KeyDerive; use lightning_signer::signer::derive::NativeKeyDerive; use serde::de::DeserializeOwned; -use serde::{Deserialize, Serialize}; +use serde::Serialize; use serde_json as json; use clightningrpc_common::client::Client; @@ -21,13 +21,9 @@ use clightningrpc_plugin::errors::PluginError; use clightningrpc_plugin::{commands::RPCCommand, plugin::Plugin}; use clightningrpc_plugin_macros::{plugin, rpc_method}; +use rgb_common::anyhow; use rgb_common::bitcoin::bip32::ExtendedPrivKey; -use rgb_common::bitcoin::consensus::encode::serialize_hex; -use rgb_common::bitcoin::consensus::Decodable; -use rgb_common::bitcoin::hashes::hex::FromHex; -use rgb_common::bitcoin::psbt::PartiallySignedTransaction; use rgb_common::RGBManager; -use rgb_common::{anyhow, bitcoin}; mod walletrpc; @@ -88,7 +84,8 @@ pub fn build_plugin() -> anyhow::Result> { }; plugin.on_init(on_init); - plugin = plugin.register_hook("onfunding_channel_tx", None, None, OnFundingChannelTx); + // FIXME: we disable this because it will create loop + //plugin = plugin.register_hook("rpc_command", None, None, OnRpcCommand); Ok(plugin) } @@ -142,59 +139,3 @@ fn on_init(plugin: &mut Plugin) -> json::Value { plugin.state.rgb_manager = Some(Arc::new(manager)); json::json!({}) } - -#[derive(Clone, Debug)] -struct OnFundingChannelTx; - -#[derive(Clone, Debug, Deserialize)] -struct OnFundingChannelTxHook { - onfunding_channel_tx: OnFundingChannelTxBody, -} - -#[derive(Clone, Debug, Deserialize)] -struct OnFundingChannelTxBody { - tx: String, - txid: String, - psbt: String, - channel_id: String, -} - -#[derive(Clone, Debug, Serialize)] -struct OnFundingChannelTxResponse { - tx: String, - psbt: String, -} - -impl RPCCommand for OnFundingChannelTx { - fn call<'c>( - &self, - plugin: &mut Plugin, - body: json::Value, - ) -> Result { - log::info!("Calling hook `onfunding_channel_tx` with `{body}`",); - let body: OnFundingChannelTxHook = json::from_value(body)?; - let body = body.onfunding_channel_tx; - let raw_tx = Vec::from_hex(&body.tx).unwrap(); - let tx: bitcoin::Transaction = Decodable::consensus_decode(&mut raw_tx.as_slice()).unwrap(); - let txid = bitcoin::Txid::from_str(&body.txid).unwrap(); - assert_eq!(txid, tx.txid()); - - let psbt_from_base64 = - bitcoin::base64::decode(&body.psbt).map_err(|err| error!("{err}"))?; - let mut psbt = PartiallySignedTransaction::deserialize(&psbt_from_base64) - .map_err(|err| error!("{err}"))?; - - let tx = plugin - .state - .manager() - .handle_onfunding_tx(tx, txid, &mut psbt, body.channel_id) - .unwrap(); - let result = OnFundingChannelTxResponse { - tx: serialize_hex(&tx), - psbt: psbt.serialize_hex(), - }; - Ok(json::json!({ "result": json::to_value(&result)? })) - } -} - -// FIXME: add an hook that will add rgb onchain address to the wallet. diff --git a/rgb-cln/src/plugin/walletrpc.rs b/rgb-cln/src/plugin/walletrpc.rs index d36abef..3ac1731 100644 --- a/rgb-cln/src/plugin/walletrpc.rs +++ b/rgb-cln/src/plugin/walletrpc.rs @@ -12,7 +12,10 @@ use clightningrpc_plugin::error; use clightningrpc_plugin::errors::PluginError; use clightningrpc_plugin::plugin::Plugin; -// TODO this should be hidden inside the common crate +use rgb_common::bitcoin::psbt::Psbt; +use rgb_common::bitcoin30; +use rgb_common::core::ContractId; +use rgb_common::lib::wallet::Balance; use rgb_common::types::RgbInfo; use crate::plugin::State; @@ -41,6 +44,14 @@ pub struct RGBFundChannelRequest { asset_id: String, } +#[derive(Debug, Deserialize, Serialize)] +pub struct FundincStartResponse { + funding_address: String, + scriptpubkey: String, + close_to: String, + channel_type: json::Value, +} + /// Opening a RGB channel pub fn fund_rgb_channel(plugin: &mut Plugin, request: Value) -> Result { let request: RGBFundChannelRequest = json::from_value(request)?; @@ -81,21 +92,31 @@ pub fn fund_rgb_channel(plugin: &mut Plugin, request: Value) -> Result".to_owned(), contract_id, local_rgb_amount: balance, // FIXME: Check that we are not opening a dual funding channel with @@ -108,6 +129,37 @@ pub fn fund_rgb_channel(plugin: &mut Plugin, request: Value) -> Result, + ) -> anyhow::Result { + let Some(ref online) = self.online_wallet else { + anyhow::bail!("Wallet is not online"); + }; + let assert = self.wallet.lock().unwrap().issue_asset_nia( + online.clone(), + ticker, + name, + precision, + amounts, + )?; + Ok(assert) + } + + pub fn new_addr(&self) -> anyhow::Result { + let addr = self.wallet.lock().unwrap().get_address()?; + Ok(addr) + } + + pub fn new_blind_receive( + &self, + asset_id: Option, + transport_endpoints: Vec, + min_confirmations: u8, + ) -> anyhow::Result { + let blind_receive = self.wallet.lock().unwrap().blind_receive( + asset_id, + None, + None, + transport_endpoints, + min_confirmations, + )?; + Ok(blind_receive) + } + + /// Preallocate the UTXO assets on chain for RGB. + pub fn create_utxos(&self, fee_rate: f32, sign_psbt: F) -> anyhow::Result<()> + where + F: FnOnce(String) -> anyhow::Result, + { + // FIXME: Mh, I should know why for this? + const UTXO_SIZE_SAT: u32 = 32000; + + let wallet_online = self + .online_wallet + .clone() + .ok_or(anyhow::anyhow!("Wallet not online"))?; + + let wallet = self.wallet.lock().unwrap(); + + let unsigned_psbt = wallet.create_utxos_begin( + wallet_online.clone(), + false, + Some(1), + Some(UTXO_SIZE_SAT), + fee_rate, + )?; + + let psbt = sign_psbt(unsigned_psbt)?; + + wallet.create_utxos_end(wallet_online, psbt)?; + + Ok(()) + } + + pub fn get_btc_balance(&self) -> anyhow::Result { + let wallet = self.wallet.lock().unwrap(); + let balance = wallet.get_btc_balance( + self.online_wallet + .clone() + .ok_or(anyhow::anyhow!("wallet is not online"))?, + )?; + Ok(balance) + } + + pub fn rgb_funding_complete( + &self, + recipient_map: HashMap>, + fee_rate: f32, + min_conf: u8, + ) -> anyhow::Result { + let wallet = self.wallet.lock().unwrap(); + let online = self + .online_wallet + .as_ref() + .ok_or(anyhow::anyhow!("Wallet not online"))?; + let unsigned_psbt = + wallet.send_begin(online.clone(), recipient_map, true, fee_rate, min_conf)?; + // FIXME: sign the psbt with the main key wallet + Ok(bitcoin::psbt::PartiallySignedTransaction::from_str( + &unsigned_psbt, + )?) + } + /// Given A PSBT we add the rgb information into it pub fn colored_funding( &self, diff --git a/rgb-common/src/rgb_manager.rs b/rgb-common/src/rgb_manager.rs index 4a22901..81eb154 100644 --- a/rgb-common/src/rgb_manager.rs +++ b/rgb-common/src/rgb_manager.rs @@ -6,9 +6,7 @@ use bitcoin::bip32::ExtendedPrivKey; use rgb_lib::wallet::Balance; use rgb_lib::wallet::Recipient; use rgb_lib::wallet::RecipientData; -use rgb_lib::ScriptBuf; use rgbwallet::bitcoin; -use rgbwallet::bitcoin::psbt::PartiallySignedTransaction; use crate::internal_wallet::Wallet; use crate::lib::BitcoinNetwork; @@ -77,44 +75,36 @@ impl RGBManager { } /// Modify the funding transaction before sign it with the node signer. - pub fn handle_onfunding_tx( + pub fn build_rgb_funding_transaction( &self, - tx: bitcoin::Transaction, - txid: bitcoin::Txid, - psbt: &mut PartiallySignedTransaction, - channel_id: String, - ) -> anyhow::Result { - debug_assert!(tx.txid() == txid); - debug_assert!(psbt.clone().extract_tx().txid() == txid); - // allow looup by channel and returnt the rgb info - if self.storage.is_channel_rgb(&channel_id, false)? { - // Step 1: get the rgb info https://github.com/RGB-Tools/rgb-lightning-node/blob/master/src/ldk.rs#L328 - let mut info = self.storage.get_rgb_channel_info_pending(&channel_id)?; - info.channel_id = channel_id; - // Step 2: Modify the psbt and start sending with the rgb wallet - let funding_outpoint = types::OutPoint { - txid, - index: 0, /* FIXME: cln should tell this info to us */ - }; - self.prepare_rgb_tx(&info, funding_outpoint, &tx, psbt)?; + rgb_info: &RgbInfo, + scriptpubkey: bitcoin::ScriptBuf, + fee_rate: f32, + min_conf: u8, + ) -> anyhow::Result { + // Step 1: get the rgb info https://github.com/RGB-Tools/rgb-lightning-node/blob/master/src/ldk.rs#L328 + //let mut info = self.storage.get_rgb_channel_info_pending(&channel_id)?; + //info.channel_id = channel_id; - // Step 3: Make the cosignemtn and post it somewhere - let consignment_path = self - .wallet() - .path() - .join("transfers") - .join(txid.to_string().clone()) - .join(info.contract_id.to_string()) - .join("consignment_out"); - self.consignment_proxy().post_consignment( - &consignment_path, - txid.to_string(), - txid.to_string(), - Some(0), - )?; - return Ok(tx); - } - Ok(tx) + // Step 2: Modify the psbt and start sending with the rgb wallet + let psbt = self.prepare_rgb_tx(&rgb_info, scriptpubkey, fee_rate, min_conf)?; + // FIXME: avoid cloning + let txid = psbt.clone().extract_tx().txid(); + // Step 3: Make the cosignemtn and post it somewhere + let consignment_path = self + .wallet() + .path() + .join("transfers") + .join(txid.to_string().clone()) + .join(rgb_info.contract_id.to_string()) + .join("consignment_out"); + self.consignment_proxy().post_consignment( + &consignment_path, + txid.to_string(), + txid.to_string(), + Some(0), + )?; + return Ok(psbt); } // Missing parameters: amount_sat of the funding tx and @@ -125,25 +115,25 @@ impl RGBManager { fn prepare_rgb_tx( &self, info: &types::RgbInfo, - funding_outpoint: types::OutPoint, - tx: &bitcoin::Transaction, - psb: &mut PartiallySignedTransaction, - ) -> anyhow::Result<()> { - // TODO: this is still needed? + scriptpubkey: bitcoin::ScriptBuf, + fee_rate: f32, + min_conf: u8, + ) -> anyhow::Result { let recipient_map = amplify::map! { info.contract_id.to_string() => vec![Recipient { recipient_data: RecipientData::WitnessData { - script_buf: ScriptBuf::new(), // TODO: get this from the transaction - amount_sat: 0, // TODO get this from the transaction + script_buf: scriptpubkey, + amount_sat: info.remote_rgb_amount, blinding: Some(STATIC_BLINDING), }, amount: info.local_rgb_amount, transport_endpoints: vec![self.consignment_proxy.url.clone()] }] }; - // FIXME: find the position of the vout; - self.wallet - .colored_funding(psb, funding_outpoint, info, 0)?; - Ok(()) + + let psbt = self + .wallet + .rgb_funding_complete(recipient_map, fee_rate, min_conf)?; + Ok(psbt) } }