Merge pull request #14 from vincenzopalazzo/macros/cln-async
core: be able to send an asset onchain
This commit is contained in:
commit
05ddb866ef
|
@ -1685,9 +1685,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.3.22"
|
version = "0.3.26"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178"
|
checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"fnv",
|
"fnv",
|
||||||
|
@ -1866,7 +1866,7 @@ dependencies = [
|
||||||
"httpdate",
|
"httpdate",
|
||||||
"itoa",
|
"itoa",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"socket2 0.5.5",
|
"socket2 0.4.10",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
@ -2272,9 +2272,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "0.8.10"
|
version = "0.8.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
|
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi",
|
||||||
|
@ -4661,6 +4661,12 @@ version = "0.11.0+wasi-snapshot-preview1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasite"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.89"
|
version = "0.2.89"
|
||||||
|
@ -4758,9 +4764,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "whoami"
|
name = "whoami"
|
||||||
version = "1.4.1"
|
version = "1.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50"
|
checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9"
|
||||||
|
dependencies = [
|
||||||
|
"redox_syscall 0.4.1",
|
||||||
|
"wasite",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
|
|
|
@ -25,6 +25,7 @@ use rgb_common::anyhow;
|
||||||
use rgb_common::bitcoin::bip32::ExtendedPrivKey;
|
use rgb_common::bitcoin::bip32::ExtendedPrivKey;
|
||||||
use rgb_common::RGBManager;
|
use rgb_common::RGBManager;
|
||||||
|
|
||||||
|
mod macros;
|
||||||
mod walletrpc;
|
mod walletrpc;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -100,11 +101,16 @@ fn rgb_issue_asset(plugin: &mut Plugin<State>, request: Value) -> Result<Value,
|
||||||
walletrpc::rgb_issue_new_assert(plugin, request)
|
walletrpc::rgb_issue_new_assert(plugin, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rpc_method(rpc_name = "rgbreceive", description = "RGB Receive a token on chain")]
|
#[rpc_method(rpc_name = "rgbreceive", description = "RGB Receive a asset on chain")]
|
||||||
fn rgb_receive(plugin: &mut Plugin<State>, request: Value) -> Result<Value, PluginError> {
|
fn rgb_receive(plugin: &mut Plugin<State>, request: Value) -> Result<Value, PluginError> {
|
||||||
walletrpc::rgb_receive(plugin, request)
|
walletrpc::rgb_receive(plugin, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rpc_method(rpc_name = "rgbsendasset", description = "RGB Send a asset on chain")]
|
||||||
|
fn rgb_send(plugin: &mut Plugin<State>, request: Value) -> Result<Value, PluginError> {
|
||||||
|
walletrpc::rgb_send(plugin, request)
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: this is just a test, we should remove it at some point
|
// FIXME: this is just a test, we should remove it at some point
|
||||||
#[rpc_method(rpc_name = "rgbinfo", description = "RGB Information")]
|
#[rpc_method(rpc_name = "rgbinfo", description = "RGB Information")]
|
||||||
fn rgb_info(plugin: &mut Plugin<State>, request: Value) -> Result<Value, PluginError> {
|
fn rgb_info(plugin: &mut Plugin<State>, request: Value) -> Result<Value, PluginError> {
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
//! Create macros
|
||||||
|
//!
|
||||||
|
//! Author: Vincenzo Palazzo <vincenzopalazzo@member.fsf.org>
|
||||||
|
|
||||||
|
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;
|
|
@ -12,12 +12,12 @@ use clightningrpc_plugin::error;
|
||||||
use clightningrpc_plugin::errors::PluginError;
|
use clightningrpc_plugin::errors::PluginError;
|
||||||
use clightningrpc_plugin::plugin::Plugin;
|
use clightningrpc_plugin::plugin::Plugin;
|
||||||
|
|
||||||
|
|
||||||
use rgb_common::bitcoin30;
|
|
||||||
use rgb_common::core::ContractId;
|
use rgb_common::core::ContractId;
|
||||||
|
use rgb_common::{bitcoin30, types};
|
||||||
|
|
||||||
use rgb_common::types::RgbInfo;
|
use rgb_common::types::RgbInfo;
|
||||||
|
|
||||||
|
use crate::plugin::macros::howmuchfees;
|
||||||
use crate::plugin::State;
|
use crate::plugin::State;
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
|
@ -203,25 +203,47 @@ pub fn rgb_receive(plugin: &mut Plugin<State>, request: Value) -> Result<Value,
|
||||||
log::info!("calling rgb receive with body `{request}`");
|
log::info!("calling rgb receive with body `{request}`");
|
||||||
let request: RgbReceiveRequest = json::from_value(request).map_err(|err| error!("{err}"))?;
|
let request: RgbReceiveRequest = json::from_value(request).map_err(|err| error!("{err}"))?;
|
||||||
let wallet = plugin.state.manager().wallet();
|
let wallet = plugin.state.manager().wallet();
|
||||||
|
let fee = howmuchfees!(plugin);
|
||||||
// Estimate the fee
|
log::info!("creating utxo with fee `{fee}`");
|
||||||
let fees: Value = plugin
|
|
||||||
.state
|
|
||||||
.call("estimatefees", json::json!({}))
|
|
||||||
.map_err(|err| error!("{err}"))?;
|
|
||||||
log::info!("estimated fee: {fees}");
|
|
||||||
|
|
||||||
let minimum = fees
|
|
||||||
.get("feerate_floor")
|
|
||||||
.ok_or(error!("not able to find the feerate_floor in: `{fees}`"))?;
|
|
||||||
let minimum = minimum.as_i64().unwrap_or_default();
|
|
||||||
log::info!("creating utxo with fee `{minimum}`");
|
|
||||||
wallet
|
wallet
|
||||||
.create_utxos(minimum as f32, |psbt| wallet.sing_with_master_key(psbt))
|
.create_utxos(fee as f32, |psbt| wallet.sing_with_master_key(psbt))
|
||||||
.map_err(|err| error!("{err}"))?;
|
.map_err(|err| error!("{err}"))?;
|
||||||
log::info!("get the new blind receive");
|
log::info!("get the new blind receive");
|
||||||
let receive = wallet
|
let receive = wallet
|
||||||
|
// FIXME: add the blocks inside the plugin configuration
|
||||||
.new_blind_receive(request.asset_id, 6)
|
.new_blind_receive(request.asset_id, 6)
|
||||||
.map_err(|err| error!("{err}"))?;
|
.map_err(|err| error!("{err}"))?;
|
||||||
Ok(json::json!(receive))
|
Ok(json::json!(receive))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct RgbSendRequest {
|
||||||
|
asset_id: String,
|
||||||
|
amount: u64,
|
||||||
|
blinded_utxo: String,
|
||||||
|
donation: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<types::RGBSendAssetData> 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<State>, request: Value) -> Result<Value, PluginError> {
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
|
@ -4,12 +4,13 @@ use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use amplify::map;
|
||||||
use bdk;
|
use bdk;
|
||||||
use bdk::blockchain::ElectrumBlockchain;
|
use bdk::blockchain::ElectrumBlockchain;
|
||||||
use bdk::electrum_client::Client;
|
use bdk::electrum_client::Client;
|
||||||
use bdk::SyncOptions;
|
use bdk::SyncOptions;
|
||||||
use bp::seals::txout::CloseMethod;
|
use bp::seals::txout::CloseMethod;
|
||||||
use rgb_lib::wallet::{AssetNIA, ReceiveData, Recipient};
|
use rgb_lib::wallet::SendResult;
|
||||||
use strict_encoding::{FieldName, TypeName};
|
use strict_encoding::{FieldName, TypeName};
|
||||||
|
|
||||||
use crate::bitcoin::bip32::ChildNumber;
|
use crate::bitcoin::bip32::ChildNumber;
|
||||||
|
@ -21,8 +22,11 @@ use crate::bitcoin::Network;
|
||||||
use crate::bitcoin::{ScriptBuf, TxOut};
|
use crate::bitcoin::{ScriptBuf, TxOut};
|
||||||
use crate::bitcoin30::psbt::PartiallySignedTransaction as RgbPsbt;
|
use crate::bitcoin30::psbt::PartiallySignedTransaction as RgbPsbt;
|
||||||
use crate::core::contract::Operation;
|
use crate::core::contract::Operation;
|
||||||
|
use crate::core::SecretSeal;
|
||||||
use crate::json;
|
use crate::json;
|
||||||
use crate::lib::utils::load_rgb_runtime;
|
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::wallet::{DatabaseType, Online, Wallet as RgbWallet, WalletData};
|
||||||
use crate::lib::BitcoinNetwork;
|
use crate::lib::BitcoinNetwork;
|
||||||
use crate::rgb::persistence::Inventory;
|
use crate::rgb::persistence::Inventory;
|
||||||
|
@ -157,6 +161,43 @@ impl Wallet {
|
||||||
Ok(addr)
|
Ok(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn send_asset<F>(
|
||||||
|
&self,
|
||||||
|
data: &types::RGBSendAssetData,
|
||||||
|
feerate: f32,
|
||||||
|
minconf: u8,
|
||||||
|
sign_psbt: F,
|
||||||
|
) -> anyhow::Result<SendResult>
|
||||||
|
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(
|
pub fn new_blind_receive(
|
||||||
&self,
|
&self,
|
||||||
asset_id: Option<String>,
|
asset_id: Option<String>,
|
||||||
|
|
|
@ -8,6 +8,15 @@ use crate::bitcoin::Txid;
|
||||||
use crate::core::{Anchor, TransitionBundle};
|
use crate::core::{Anchor, TransitionBundle};
|
||||||
use crate::std::contract::ContractId;
|
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
|
/// RGB channel info
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct RgbInfo {
|
pub struct RgbInfo {
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
stable
|
1.77.2
|
||||||
|
|
Loading…
Reference in New Issue