From 9548429b6231ebd22b7c9f87ca511d0340eb5da7 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Tue, 20 Feb 2024 11:56:30 +0100 Subject: [PATCH] common: adds basic function for rgb Signed-off-by: Vincenzo Palazzo --- flake.nix | 1 + rgb-cln/Cargo.toml | 1 + rgb-cln/src/main.rs | 2 + rgb-cln/src/plugin.rs | 2 + rgb-common/Cargo.toml | 24 ++++++++ rgb-common/src/comm.rs | 104 ++++++++++++++++++++++++++++++++++ rgb-common/src/lib.rs | 15 +++++ rgb-common/src/proxy.rs | 72 +++++++++++++++++++++++ rgb-common/src/rgb_manager.rs | 42 ++++++++++++++ 9 files changed, 263 insertions(+) create mode 100644 rgb-common/src/comm.rs create mode 100644 rgb-common/src/proxy.rs create mode 100644 rgb-common/src/rgb_manager.rs diff --git a/flake.nix b/flake.nix index 996ae6b..667ef00 100644 --- a/flake.nix +++ b/flake.nix @@ -38,6 +38,7 @@ libcap gcc pkg-config + openssl git gnumake diff --git a/rgb-cln/Cargo.toml b/rgb-cln/Cargo.toml index 627b7ca..a4042ad 100644 --- a/rgb-cln/Cargo.toml +++ b/rgb-cln/Cargo.toml @@ -11,3 +11,4 @@ log = "0.4.20" anyhow = "1.0.79" serde = "1.0.159" serde_json = "1.0.95" +rgb-common = { path = "../rgb-common" } diff --git a/rgb-cln/src/main.rs b/rgb-cln/src/main.rs index f449181..e4c4efc 100644 --- a/rgb-cln/src/main.rs +++ b/rgb-cln/src/main.rs @@ -1,3 +1,5 @@ +use rgb_common::anyhow; + mod plugin; fn main() -> anyhow::Result<()> { diff --git a/rgb-cln/src/plugin.rs b/rgb-cln/src/plugin.rs index 55496d8..47bda1c 100644 --- a/rgb-cln/src/plugin.rs +++ b/rgb-cln/src/plugin.rs @@ -6,6 +6,8 @@ use serde_json as json; use clightningrpc_plugin::{commands::RPCCommand, plugin::Plugin}; use clightningrpc_plugin_macros::plugin; +use rgb_common::anyhow; + #[derive(Clone, Debug)] pub(crate) struct State; diff --git a/rgb-common/Cargo.toml b/rgb-common/Cargo.toml index f1700b7..933244b 100644 --- a/rgb-common/Cargo.toml +++ b/rgb-common/Cargo.toml @@ -4,3 +4,27 @@ version = "0.1.0" edition = "2021" [dependencies] +anyhow = "1.0.71" + +bitcoin = { version = "0.29.0", default-features = false, features = ["secp-recovery"] } +lightning = "0.0.118" + +amplify = "=4.5.0" +base64 = "0.13.0" +bp-core = "=0.10.11" +commit_verify = "=0.10.6" +futures = "0.3" +hex = "0.4" +reqwest = { version = "0.11", default-features = false, features = ["json", "blocking"] } +rgb-contracts = { version = "=0.10.2", features = ["electrum"] } +rgb_core = { package = "rgb-core", version = "=0.10.8" } +rgb-lib = { git = "https://github.com/RGB-Tools/rgb-lib", branch = "rln_v0.10" } +rgb-std = "=0.10.9" +rgb-wallet = "=0.10.9" +serde = { version = "^1.0", features = ["derive"] } +serde_json = "^1.0" +strict_encoding = "=2.6.1" +tokio = { version = "1.36", features = ["macros", "rt-multi-thread"] } + +# Fixing dependencies resolution :/ +amplify_num = "=0.5.1" diff --git a/rgb-common/src/comm.rs b/rgb-common/src/comm.rs new file mode 100644 index 0000000..339c2ad --- /dev/null +++ b/rgb-common/src/comm.rs @@ -0,0 +1,104 @@ +//! A module to provide RGB functionality +use std::fs; +use std::path::PathBuf; +use std::str::FromStr; + +use bitcoin::OutPoint as BtcOutPoint; +use serde::{Deserialize, Serialize}; + +use bp::seals::txout::CloseMethod; +use rgb_core::ContractId; + +use crate::ldk; +use crate::proxy; +use crate::std::persistence::ConsignerError::Reveal; + +use ldk::ln::PaymentHash; + +/// RGB channel info +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct RgbInfo { + /// Channel contract ID + pub contract_id: ContractId, + /// Channel RGB local amount + pub local_rgb_amount: u64, + /// Channel RGB remote amount + pub remote_rgb_amount: u64, +} + +/// RGB payment info +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct RgbPaymentInfo { + /// RGB contract ID + pub contract_id: ContractId, + /// RGB payment amount + pub amount: u64, + /// RGB local amount + pub local_rgb_amount: u64, + /// RGB remote amount + pub remote_rgb_amount: u64, +} + +/// Get RgbPaymentInfo file +pub fn get_rgb_payment_info(payment_hash: &PaymentHash, ldk_data_dir: &PathBuf) -> RgbPaymentInfo { + let rgb_payment_info_path = ldk_data_dir.join(hex::encode(payment_hash.0)); + parse_rgb_payment_info(&rgb_payment_info_path) +} + +/// Parse RgbPaymentInfo +pub fn parse_rgb_payment_info(rgb_payment_info_path: &PathBuf) -> RgbPaymentInfo { + let serialized_info = + fs::read_to_string(&rgb_payment_info_path).expect("valid rgb payment info"); + serde_json::from_str(&serialized_info).expect("valid rgb info file") +} + +/// Get RgbInfo file +pub fn get_rgb_channel_info( + channel_id: &[u8; 32], + ldk_data_dir: &PathBuf, +) -> anyhow::Result<(RgbInfo, PathBuf)> { + let info_file_path = ldk_data_dir.join(hex::encode(channel_id)); + let serialized_info = fs::read_to_string(&info_file_path)?; + let info: RgbInfo = serde_json::from_str(&serialized_info)?; + Ok((info, info_file_path)) +} + +/// Write RgbInfo file +pub fn write_rgb_channel_info(path: &PathBuf, rgb_info: &RgbInfo) { + let serialized_info = serde_json::to_string(&rgb_info).expect("valid rgb info"); + fs::write(path, serialized_info).expect("able to write") +} + +/// Rename RgbInfo file to channel_id +pub(crate) fn rename_rgbinfo_file( + channel_id: &[u8; 32], + temporary_channel_id: &[u8; 32], + ldk_data_dir: &PathBuf, +) { + let temporary_channel_id_path = ldk_data_dir.join(hex::encode(temporary_channel_id)); + let channel_id_path = ldk_data_dir.join(hex::encode(channel_id)); + fs::rename(temporary_channel_id_path, channel_id_path).expect("rename ok"); +} + +/// Update RGB channel amount +pub(crate) fn update_rgb_channel_amount( + channel_id: &[u8; 32], + rgb_offered_htlc: u64, + rgb_received_htlc: u64, + ldk_data_dir: &PathBuf, +) -> anyhow::Result<()> { + let (mut rgb_info, info_file_path) = get_rgb_channel_info(channel_id, ldk_data_dir)?; + + if rgb_offered_htlc > rgb_received_htlc { + let spent = rgb_offered_htlc - rgb_received_htlc; + rgb_info.local_rgb_amount -= spent; + rgb_info.remote_rgb_amount += spent; + } else { + let received = rgb_received_htlc - rgb_offered_htlc; + rgb_info.local_rgb_amount += received; + rgb_info.remote_rgb_amount -= received; + } + + write_rgb_channel_info(&info_file_path, &rgb_info); + Ok(()) +} diff --git a/rgb-common/src/lib.rs b/rgb-common/src/lib.rs index 8b13789..4222fa3 100644 --- a/rgb-common/src/lib.rs +++ b/rgb-common/src/lib.rs @@ -1 +1,16 @@ +mod comm; +mod proxy; +mod rgb_manager; +use lightning as ldk; +use reqwest::blocking::Client as BlockingClient; + +pub use anyhow; +// Re-exporting RGB dependencies under a single module. +pub use rgb; +pub use rgb::interface::rgb20 as asset20; +pub use rgb_core as core; +pub use rgb_lib as lib; +pub use rgb_manager::RGBManager; +pub use rgbstd as std; +pub use rgbwallet as wallet; diff --git a/rgb-common/src/proxy.rs b/rgb-common/src/proxy.rs new file mode 100644 index 0000000..3b22a55 --- /dev/null +++ b/rgb-common/src/proxy.rs @@ -0,0 +1,72 @@ +//! A module for operating an RGB HTTP JSON-RPC proxy + +use amplify::s; +use reqwest::header::CONTENT_TYPE; +use serde::{Deserialize, Serialize}; +use tokio::task; + +use core::time::Duration; + +use crate::BlockingClient; + +const JSON: &str = "application/json"; +const PROXY_TIMEOUT: u8 = 90; + +/// JSON-RPC Error +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct JsonRpcError { + pub(crate) code: i64, + message: String, +} + +/// JSON-RPC request +#[derive(Debug, Deserialize, Serialize)] +pub struct JsonRpcRequest

{ + method: String, + jsonrpc: String, + id: Option, + params: Option

, +} + +/// JSON-RPC response +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct JsonRpcResponse { + id: Option, + pub(crate) result: Option, + pub(crate) error: Option, +} + +/// Blinded UTXO parameter +#[derive(Debug, Deserialize, Serialize)] +pub struct BlindedUtxoParam { + blinded_utxo: String, +} + +pub(crate) fn get_blocking_client() -> BlockingClient { + BlockingClient::builder() + .timeout(Duration::from_secs(PROXY_TIMEOUT as u64)) + .build() + .expect("valid proxy") +} + +pub(crate) fn get_consignment( + url: &str, + consignment_id: String, +) -> Result, reqwest::Error> { + task::block_in_place(|| { + let body = JsonRpcRequest { + method: s!("consignment.get"), + jsonrpc: s!("2.0"), + id: None, + params: Some(BlindedUtxoParam { + blinded_utxo: consignment_id, + }), + }; + get_blocking_client() + .post(url) + .header(CONTENT_TYPE, JSON) + .json(&body) + .send()? + .json::>() + }) +} diff --git a/rgb-common/src/rgb_manager.rs b/rgb-common/src/rgb_manager.rs new file mode 100644 index 0000000..37039c6 --- /dev/null +++ b/rgb-common/src/rgb_manager.rs @@ -0,0 +1,42 @@ +//! RGB Manager +use std::str::FromStr; +use std::sync::Arc; +use std::sync::Mutex; + +use crate::lib::wallet::{DatabaseType, Wallet, WalletData}; +use crate::lib::BitcoinNetwork; +use crate::proxy; + +pub struct RGBManager { + proxy_client: Arc, + wallet: Arc>, +} + +impl RGBManager { + pub fn init(root_dir: &str, pubkey: &str, network: &str) -> anyhow::Result { + let client = proxy::get_blocking_client(); + let wallet = Wallet::new(WalletData { + data_dir: root_dir.to_owned(), + bitcoin_network: BitcoinNetwork::from_str(network)?, + database_type: DatabaseType::Sqlite, + max_allocations_per_utxo: 11, + pubkey: pubkey.to_owned(), + mnemonic: None, + vanilla_keychain: None, + })?; + // FIXME: go online + // FIXME: setting up the correct proxy client URL + Ok(Self { + proxy_client: Arc::new(client), + wallet: Arc::new(Mutex::new(wallet)), + }) + } + + pub fn wallet(&self) -> Arc> { + self.wallet.clone() + } + + pub fn proxy_client(&self) -> Arc { + self.proxy_client.clone() + } +}