Merge pull request #14 from vincenzopalazzo/macros/cln-async

core: be able to send an asset onchain
This commit is contained in:
Vincenzo Palazzo 2024-04-15 19:13:17 +02:00 committed by GitHub
commit 05ddb866ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 133 additions and 26 deletions

24
Cargo.lock generated
View File

@ -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"

View File

@ -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<State>, request: Value) -> Result<Value,
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> {
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
#[rpc_method(rpc_name = "rgbinfo", description = "RGB Information")]
fn rgb_info(plugin: &mut Plugin<State>, request: Value) -> Result<Value, PluginError> {

View File

@ -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;

View File

@ -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<State>, request: Value) -> Result<Value,
log::info!("calling rgb receive with body `{request}`");
let request: RgbReceiveRequest = json::from_value(request).map_err(|err| error!("{err}"))?;
let wallet = plugin.state.manager().wallet();
// Estimate the 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}`");
let fee = howmuchfees!(plugin);
log::info!("creating utxo with fee `{fee}`");
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}"))?;
log::info!("get the new blind receive");
let receive = wallet
// FIXME: add the blocks inside the plugin configuration
.new_blind_receive(request.asset_id, 6)
.map_err(|err| error!("{err}"))?;
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))
}

View File

@ -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<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(
&self,
asset_id: Option<String>,

View File

@ -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 {

View File

@ -1 +1 @@
stable
1.77.2