cln: reworking channel creation
Signed-off-by: Vincenzo Palazzo <vincenzopalazzodev@gmail.com>
This commit is contained in:
parent
38d3bc7e4e
commit
fcf46e17b7
|
@ -12,7 +12,7 @@ use lightning_signer::bitcoin as vlsbtc;
|
||||||
use lightning_signer::signer::derive::KeyDerive;
|
use lightning_signer::signer::derive::KeyDerive;
|
||||||
use lightning_signer::signer::derive::NativeKeyDerive;
|
use lightning_signer::signer::derive::NativeKeyDerive;
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::Serialize;
|
||||||
use serde_json as json;
|
use serde_json as json;
|
||||||
|
|
||||||
use clightningrpc_common::client::Client;
|
use clightningrpc_common::client::Client;
|
||||||
|
@ -21,13 +21,9 @@ use clightningrpc_plugin::errors::PluginError;
|
||||||
use clightningrpc_plugin::{commands::RPCCommand, plugin::Plugin};
|
use clightningrpc_plugin::{commands::RPCCommand, plugin::Plugin};
|
||||||
use clightningrpc_plugin_macros::{plugin, rpc_method};
|
use clightningrpc_plugin_macros::{plugin, rpc_method};
|
||||||
|
|
||||||
|
use rgb_common::anyhow;
|
||||||
use rgb_common::bitcoin::bip32::ExtendedPrivKey;
|
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::RGBManager;
|
||||||
use rgb_common::{anyhow, bitcoin};
|
|
||||||
|
|
||||||
mod walletrpc;
|
mod walletrpc;
|
||||||
|
|
||||||
|
@ -88,7 +84,8 @@ pub fn build_plugin() -> anyhow::Result<Plugin<State>> {
|
||||||
};
|
};
|
||||||
plugin.on_init(on_init);
|
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)
|
Ok(plugin)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,59 +139,3 @@ fn on_init(plugin: &mut Plugin<State>) -> json::Value {
|
||||||
plugin.state.rgb_manager = Some(Arc::new(manager));
|
plugin.state.rgb_manager = Some(Arc::new(manager));
|
||||||
json::json!({})
|
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<State> for OnFundingChannelTx {
|
|
||||||
fn call<'c>(
|
|
||||||
&self,
|
|
||||||
plugin: &mut Plugin<State>,
|
|
||||||
body: json::Value,
|
|
||||||
) -> Result<json::Value, clightningrpc_plugin::errors::PluginError> {
|
|
||||||
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.
|
|
||||||
|
|
|
@ -12,7 +12,10 @@ use clightningrpc_plugin::error;
|
||||||
use clightningrpc_plugin::errors::PluginError;
|
use clightningrpc_plugin::errors::PluginError;
|
||||||
use clightningrpc_plugin::plugin::Plugin;
|
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 rgb_common::types::RgbInfo;
|
||||||
|
|
||||||
use crate::plugin::State;
|
use crate::plugin::State;
|
||||||
|
@ -41,6 +44,14 @@ pub struct RGBFundChannelRequest {
|
||||||
asset_id: String,
|
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
|
/// Opening a RGB channel
|
||||||
pub fn fund_rgb_channel(plugin: &mut Plugin<State>, request: Value) -> Result<Value, PluginError> {
|
pub fn fund_rgb_channel(plugin: &mut Plugin<State>, request: Value) -> Result<Value, PluginError> {
|
||||||
let request: RGBFundChannelRequest = json::from_value(request)?;
|
let request: RGBFundChannelRequest = json::from_value(request)?;
|
||||||
|
@ -81,21 +92,31 @@ pub fn fund_rgb_channel(plugin: &mut Plugin<State>, request: Value) -> Result<Va
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let fundchannel: json::Value = plugin
|
let fundchannel: FundincStartResponse = plugin
|
||||||
.state
|
.state
|
||||||
.call(
|
.call(
|
||||||
"fundchannel",
|
"fundchannel_start",
|
||||||
json::json!({
|
json::json!({
|
||||||
"id": request.peer_id,
|
"id": request.peer_id,
|
||||||
"amount": balance.to_string(),
|
"amount": balance.to_string(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.map_err(|err| error!("{err}"))?;
|
.map_err(|err| error!("{err}"))?;
|
||||||
let channel_id = fundchannel["channel_id"].to_string();
|
let Ok(scriptpubkey) = bitcoin30::ScriptBuf::from_hex(&fundchannel.scriptpubkey) else {
|
||||||
log::info!("RGB channel id `{channel_id}` created");
|
let _: json::Value = plugin
|
||||||
|
.state
|
||||||
|
.call(
|
||||||
|
"fundchannel_cancel",
|
||||||
|
json::json!({
|
||||||
|
"id": request.peer_id,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.map_err(|err| error!("{err}"))?;
|
||||||
|
return Err(error!("Impossible parse `scriptpubkey`, failing funding"));
|
||||||
|
};
|
||||||
|
|
||||||
let info = RgbInfo {
|
let info = RgbInfo {
|
||||||
channel_id,
|
channel_id: "<channel_id>".to_owned(),
|
||||||
contract_id,
|
contract_id,
|
||||||
local_rgb_amount: balance,
|
local_rgb_amount: balance,
|
||||||
// FIXME: Check that we are not opening a dual funding channel with
|
// FIXME: Check that we are not opening a dual funding channel with
|
||||||
|
@ -108,6 +129,37 @@ pub fn fund_rgb_channel(plugin: &mut Plugin<State>, request: Value) -> Result<Va
|
||||||
.manager()
|
.manager()
|
||||||
.add_rgb_info(&info, true)
|
.add_rgb_info(&info, true)
|
||||||
.map_err(|err| error!("{err}"))?;
|
.map_err(|err| error!("{err}"))?;
|
||||||
|
let Ok(psbt) =
|
||||||
|
plugin
|
||||||
|
.state
|
||||||
|
.manager()
|
||||||
|
.build_rgb_funding_transaction(&info, scriptpubkey, 1.1, 6)
|
||||||
|
else {
|
||||||
|
let _: json::Value = plugin
|
||||||
|
.state
|
||||||
|
.call(
|
||||||
|
"fundchannel_cancel",
|
||||||
|
json::json!({
|
||||||
|
"id": request.peer_id,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.map_err(|err| error!("{err}"))?;
|
||||||
|
return Err(error!(
|
||||||
|
"Impossible .build_rgb_funding_transaction, failing funding"
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
let psbt = psbt.serialize_hex();
|
||||||
|
let fundchannel: json::Value = plugin
|
||||||
|
.state
|
||||||
|
.call(
|
||||||
|
"fundchannel_complete",
|
||||||
|
json::json!({
|
||||||
|
"id": request.peer_id,
|
||||||
|
"psbt": psbt,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.map_err(|err| error!("{err}"))?;
|
||||||
Ok(json::json!({
|
Ok(json::json!({
|
||||||
"info": fundchannel,
|
"info": fundchannel,
|
||||||
"rgb_info": info,
|
"rgb_info": info,
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
//! RGB Wallet mock
|
//! RGB Wallet mock
|
||||||
use std::path::{Path, PathBuf};
|
use std::collections::HashMap;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use bp::seals::txout::CloseMethod;
|
use bp::seals::txout::CloseMethod;
|
||||||
use rgb_lib::ScriptBuf;
|
use rgb_lib::wallet::{AssetNIA, BtcBalance, ReceiveData, Recipient};
|
||||||
use rgbstd::containers::BuilderSeal;
|
|
||||||
use rgbstd::interface::TypedState;
|
|
||||||
use rgbwallet::bitcoin::{TxOut, Txid};
|
|
||||||
use strict_encoding::{FieldName, TypeName};
|
use strict_encoding::{FieldName, TypeName};
|
||||||
|
|
||||||
use crate::bitcoin::bip32::ChildNumber;
|
use crate::bitcoin::bip32::ChildNumber;
|
||||||
|
@ -99,6 +97,107 @@ impl Wallet {
|
||||||
Ok(master_xprv.derive_priv(&Secp256k1::new(), &account_derivation_path)?)
|
Ok(master_xprv.derive_priv(&Secp256k1::new(), &account_derivation_path)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
pub fn issue_asset_nia(
|
||||||
|
&self,
|
||||||
|
ticker: String,
|
||||||
|
name: String,
|
||||||
|
precision: u8,
|
||||||
|
amounts: Vec<u64>,
|
||||||
|
) -> anyhow::Result<AssetNIA> {
|
||||||
|
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<String> {
|
||||||
|
let addr = self.wallet.lock().unwrap().get_address()?;
|
||||||
|
Ok(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_blind_receive(
|
||||||
|
&self,
|
||||||
|
asset_id: Option<String>,
|
||||||
|
transport_endpoints: Vec<String>,
|
||||||
|
min_confirmations: u8,
|
||||||
|
) -> anyhow::Result<ReceiveData> {
|
||||||
|
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<F>(&self, fee_rate: f32, sign_psbt: F) -> anyhow::Result<()>
|
||||||
|
where
|
||||||
|
F: FnOnce(String) -> anyhow::Result<String>,
|
||||||
|
{
|
||||||
|
// 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<BtcBalance> {
|
||||||
|
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<String, Vec<Recipient>>,
|
||||||
|
fee_rate: f32,
|
||||||
|
min_conf: u8,
|
||||||
|
) -> anyhow::Result<bitcoin::psbt::PartiallySignedTransaction> {
|
||||||
|
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
|
/// Given A PSBT we add the rgb information into it
|
||||||
pub fn colored_funding(
|
pub fn colored_funding(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -6,9 +6,7 @@ use bitcoin::bip32::ExtendedPrivKey;
|
||||||
use rgb_lib::wallet::Balance;
|
use rgb_lib::wallet::Balance;
|
||||||
use rgb_lib::wallet::Recipient;
|
use rgb_lib::wallet::Recipient;
|
||||||
use rgb_lib::wallet::RecipientData;
|
use rgb_lib::wallet::RecipientData;
|
||||||
use rgb_lib::ScriptBuf;
|
|
||||||
use rgbwallet::bitcoin;
|
use rgbwallet::bitcoin;
|
||||||
use rgbwallet::bitcoin::psbt::PartiallySignedTransaction;
|
|
||||||
|
|
||||||
use crate::internal_wallet::Wallet;
|
use crate::internal_wallet::Wallet;
|
||||||
use crate::lib::BitcoinNetwork;
|
use crate::lib::BitcoinNetwork;
|
||||||
|
@ -77,44 +75,36 @@ impl RGBManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Modify the funding transaction before sign it with the node signer.
|
/// Modify the funding transaction before sign it with the node signer.
|
||||||
pub fn handle_onfunding_tx(
|
pub fn build_rgb_funding_transaction(
|
||||||
&self,
|
&self,
|
||||||
tx: bitcoin::Transaction,
|
rgb_info: &RgbInfo,
|
||||||
txid: bitcoin::Txid,
|
scriptpubkey: bitcoin::ScriptBuf,
|
||||||
psbt: &mut PartiallySignedTransaction,
|
fee_rate: f32,
|
||||||
channel_id: String,
|
min_conf: u8,
|
||||||
) -> anyhow::Result<bitcoin::Transaction> {
|
) -> anyhow::Result<bitcoin::psbt::PartiallySignedTransaction> {
|
||||||
debug_assert!(tx.txid() == txid);
|
// Step 1: get the rgb info https://github.com/RGB-Tools/rgb-lightning-node/blob/master/src/ldk.rs#L328
|
||||||
debug_assert!(psbt.clone().extract_tx().txid() == txid);
|
//let mut info = self.storage.get_rgb_channel_info_pending(&channel_id)?;
|
||||||
// allow looup by channel and returnt the rgb info
|
//info.channel_id = channel_id;
|
||||||
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)?;
|
|
||||||
|
|
||||||
// Step 3: Make the cosignemtn and post it somewhere
|
// Step 2: Modify the psbt and start sending with the rgb wallet
|
||||||
let consignment_path = self
|
let psbt = self.prepare_rgb_tx(&rgb_info, scriptpubkey, fee_rate, min_conf)?;
|
||||||
.wallet()
|
// FIXME: avoid cloning
|
||||||
.path()
|
let txid = psbt.clone().extract_tx().txid();
|
||||||
.join("transfers")
|
// Step 3: Make the cosignemtn and post it somewhere
|
||||||
.join(txid.to_string().clone())
|
let consignment_path = self
|
||||||
.join(info.contract_id.to_string())
|
.wallet()
|
||||||
.join("consignment_out");
|
.path()
|
||||||
self.consignment_proxy().post_consignment(
|
.join("transfers")
|
||||||
&consignment_path,
|
.join(txid.to_string().clone())
|
||||||
txid.to_string(),
|
.join(rgb_info.contract_id.to_string())
|
||||||
txid.to_string(),
|
.join("consignment_out");
|
||||||
Some(0),
|
self.consignment_proxy().post_consignment(
|
||||||
)?;
|
&consignment_path,
|
||||||
return Ok(tx);
|
txid.to_string(),
|
||||||
}
|
txid.to_string(),
|
||||||
Ok(tx)
|
Some(0),
|
||||||
|
)?;
|
||||||
|
return Ok(psbt);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Missing parameters: amount_sat of the funding tx and
|
// Missing parameters: amount_sat of the funding tx and
|
||||||
|
@ -125,25 +115,25 @@ impl RGBManager {
|
||||||
fn prepare_rgb_tx(
|
fn prepare_rgb_tx(
|
||||||
&self,
|
&self,
|
||||||
info: &types::RgbInfo,
|
info: &types::RgbInfo,
|
||||||
funding_outpoint: types::OutPoint,
|
scriptpubkey: bitcoin::ScriptBuf,
|
||||||
tx: &bitcoin::Transaction,
|
fee_rate: f32,
|
||||||
psb: &mut PartiallySignedTransaction,
|
min_conf: u8,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<bitcoin::psbt::PartiallySignedTransaction> {
|
||||||
// TODO: this is still needed?
|
|
||||||
let recipient_map = amplify::map! {
|
let recipient_map = amplify::map! {
|
||||||
info.contract_id.to_string() => vec![Recipient {
|
info.contract_id.to_string() => vec![Recipient {
|
||||||
recipient_data: RecipientData::WitnessData {
|
recipient_data: RecipientData::WitnessData {
|
||||||
script_buf: ScriptBuf::new(), // TODO: get this from the transaction
|
script_buf: scriptpubkey,
|
||||||
amount_sat: 0, // TODO get this from the transaction
|
amount_sat: info.remote_rgb_amount,
|
||||||
blinding: Some(STATIC_BLINDING),
|
blinding: Some(STATIC_BLINDING),
|
||||||
},
|
},
|
||||||
amount: info.local_rgb_amount,
|
amount: info.local_rgb_amount,
|
||||||
transport_endpoints: vec![self.consignment_proxy.url.clone()]
|
transport_endpoints: vec![self.consignment_proxy.url.clone()]
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
// FIXME: find the position of the vout;
|
|
||||||
self.wallet
|
let psbt = self
|
||||||
.colored_funding(psb, funding_outpoint, info, 0)?;
|
.wallet
|
||||||
Ok(())
|
.rgb_funding_complete(recipient_map, fee_rate, min_conf)?;
|
||||||
|
Ok(psbt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue