plugin: implementing the open channel functionality

Signed-off-by: Vincenzo Palazzo <vincenzopalazzodev@gmail.com>
This commit is contained in:
Vincenzo Palazzo 2024-03-15 20:05:11 +01:00
parent c18eceed17
commit a03f55cafc
7 changed files with 163 additions and 17 deletions

View File

@ -7,10 +7,10 @@ use std::io;
use std::str::FromStr;
use std::sync::Arc;
use json::Value;
use lightning_signer::bitcoin as vlsbtc;
use lightning_signer::signer::derive::KeyDerive;
use lightning_signer::signer::derive::NativeKeyDerive;
use rgb_common::bitcoin::psbt::PartiallySignedTransaction;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use serde_json as json;
@ -19,15 +19,18 @@ use clightningrpc_common::client::Client;
use clightningrpc_plugin::error;
use clightningrpc_plugin::errors::PluginError;
use clightningrpc_plugin::{commands::RPCCommand, plugin::Plugin};
use clightningrpc_plugin_macros::plugin;
use clightningrpc_plugin_macros::{plugin, rpc_method};
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;
#[derive(Clone, Debug)]
pub(crate) struct State {
/// The RGB Manager where we ask to do everything
@ -77,7 +80,9 @@ pub fn build_plugin() -> anyhow::Result<Plugin<State>> {
state: State::new(),
dynamic: true,
notification: [ ],
methods: [],
methods: [
rgb_balance,
],
hooks: [],
};
plugin.on_init(on_init);
@ -86,6 +91,11 @@ pub fn build_plugin() -> anyhow::Result<Plugin<State>> {
Ok(plugin)
}
#[rpc_method(rpc_name = "rgbbalances", description = "Return the RGB balance")]
pub fn rgb_balance(plugin: &mut Plugin<State>, requet: Value) -> Result<Value, PluginError> {
walletrpc::rgb_balance(plugin, requet)
}
fn read_secret(file: fs::File, network: &str) -> anyhow::Result<ExtendedPrivKey> {
let buffer = io::BufReader::new(file);
let network = vlsbtc::Network::from_str(network)?;

View File

@ -0,0 +1,101 @@
//! RGB Wallet RPC methods
//!
//! Author: Vincenzo Palazzo <vincenzopalazzo@member.fsf.org>
use std::str::FromStr;
use rgb_common::core::ContractId;
use serde::{Deserialize, Serialize};
use serde_json as json;
use serde_json::Value;
use clightningrpc_plugin::error;
use clightningrpc_plugin::errors::PluginError;
use clightningrpc_plugin::plugin::Plugin;
use rgb_common::lib::wallet::Balance;
// TODO this should be hidden inside the common crate
use rgb_common::types::RgbInfo;
use crate::plugin::State;
#[derive(Deserialize, Serialize)]
pub struct RGBBalanceRequest {
asset_id: String,
}
/// Return the balance of an RGB assert
pub fn rgb_balance(plugin: &mut Plugin<State>, request: Value) -> Result<Value, PluginError> {
let request: RGBBalanceRequest = json::from_value(request).map_err(|err| error!("{err}"))?;
let balance = plugin
.state
.manager()
.assert_balance(request.asset_id)
.map_err(|err| error!("{err}"));
Ok(json::to_value(balance)?)
}
#[derive(Deserialize, Serialize)]
pub struct RGBFundChannelRequest {
peer_id: String,
amount_msat: String,
asset_id: String,
}
/// Opening a RGB channel
pub fn fund_rgb_channel(plugin: &mut Plugin<State>, request: Value) -> Result<Value, PluginError> {
let request: RGBFundChannelRequest = json::from_value(request)?;
// check if the asset id is valit
let contract_id = ContractId::from_str(&request.asset_id).map_err(|err| error!("{err}"))?;
let assert_balance: Balance = plugin
.state
.call(
"rpcbalance",
RGBBalanceRequest {
asset_id: request.asset_id.clone(),
},
)
.map_err(|err| error!("{err}"))?;
// FIXME: Check if we are connected with the peer otherwise connect to them
// FIXME: we need the magic of core lightning here
let balance = request
.amount_msat
.parse::<u64>()
.map_err(|err| error!("{err}"))?;
if balance < assert_balance.spendable {
return Err(error!(
"Balance avaialbe `{}` is not enough to open a channel of `{}` capacity",
assert_balance.spendable, balance
));
}
let fundchannel: json::Value = plugin
.state
.call("fundchannel", json::json!({}))
.map_err(|err| error!("{err}"))?;
let channel_id = fundchannel["channel_id"].to_string();
log::info!("RGB channel id `{channel_id}` created");
let info = RgbInfo {
channel_id,
contract_id,
local_rgb_amount: balance,
// FIXME: Check that we are not opening a dual funding channel with
// liquidity ads
remote_rgb_amount: 0,
};
plugin
.state
.manager()
.add_rgb_info(&info, true)
.map_err(|err| error!("{err}"))?;
Ok(json::json!({
"info": fundchannel,
"rgb_info": info,
}))
}

View File

@ -32,9 +32,9 @@ use crate::types::RgbInfo;
pub struct Wallet {
path: String,
network: BitcoinNetwork,
wallet: Arc<Mutex<RgbWallet>>,
online_wallet: Option<Online>,
pub network: BitcoinNetwork,
pub wallet: Arc<Mutex<RgbWallet>>,
pub online_wallet: Option<Online>,
}
impl Wallet {

View File

@ -3,7 +3,7 @@ mod internal_wallet;
mod proxy;
mod rgb_manager;
mod rgb_storage;
mod types;
pub mod types;
use lightning as ldk;
use reqwest::blocking::Client as BlockingClient;

View File

@ -1,9 +1,9 @@
//! RGB Manager
use std::path::Path;
use std::str::FromStr;
use std::sync::Arc;
use bitcoin::bip32::ExtendedPrivKey;
use rgb_lib::wallet::Balance;
use rgb_lib::wallet::Recipient;
use rgb_lib::wallet::RecipientData;
use rgb_lib::ScriptBuf;
@ -16,6 +16,7 @@ use crate::proxy;
use crate::rgb_storage as store;
use crate::rgb_storage::RGBStorage;
use crate::types;
use crate::types::RgbInfo;
/// Static blinding costant (will be removed in the future)
/// See https://github.com/RGB-Tools/rust-lightning/blob/80497c4086beea490b56e5b8413b7f6d86f2c042/lightning/src/rgb_utils/mod.rs#L53
@ -61,6 +62,20 @@ impl RGBManager {
self.consignment_proxy.clone()
}
pub fn assert_balance(&self, asset_id: String) -> anyhow::Result<Balance> {
let balance = self
.wallet
.wallet
.lock()
.unwrap()
.get_asset_balance(asset_id)?;
Ok(balance)
}
pub fn add_rgb_info(&self, info: &RgbInfo, pending: bool) -> anyhow::Result<()> {
self.storage.write_rgb_info(&info.channel_id, pending, info)
}
/// Modify the funding transaction before sign it with the node signer.
pub fn handle_onfunding_tx(
&self,

View File

@ -1,5 +1,5 @@
//! RGB Storage interface
use std::collections::HashMap;
use std::{cell::RefCell, collections::HashMap};
use crate::types::RgbInfo;
@ -14,10 +14,17 @@ pub trait RGBStorage {
fn get_rgb_channel_info_pending(&self, channel_id: &str) -> anyhow::Result<RgbInfo>;
fn is_channel_rgb(&self, channel_id: &str, is_pending: bool) -> anyhow::Result<bool>;
fn write_rgb_info(
&self,
channel_id: &str,
is_pending: bool,
info: &RgbInfo,
) -> anyhow::Result<()>;
}
pub struct InMemoryStorage {
inner: HashMap<String, String>,
inner: RefCell<HashMap<String, String>>,
}
impl InMemoryStorage {
@ -33,14 +40,14 @@ impl InMemoryStorage {
impl RGBStorage for InMemoryStorage {
fn new() -> anyhow::Result<Self> {
Ok(Self {
inner: HashMap::new(),
inner: RefCell::new(HashMap::new()),
})
}
fn get_rgb_channel_info(&self, channel_id: &str) -> anyhow::Result<RgbInfo> {
let key = self.derive_channel_db_key(channel_id, false)?;
let value = self
.inner
let map = self.inner.borrow();
let value = map
.get(&key)
.ok_or(anyhow::anyhow!("rgb channel with key `{key}` is not found"))?;
let info: RgbInfo = serde_json::from_str(&value)?;
@ -49,8 +56,8 @@ impl RGBStorage for InMemoryStorage {
fn get_rgb_channel_info_pending(&self, channel_id: &str) -> anyhow::Result<RgbInfo> {
let key = self.derive_channel_db_key(channel_id, true)?;
let value = self
.inner
let map = self.inner.borrow();
let value = map
.get(&key)
.ok_or(anyhow::anyhow!("rgb channel with key `{key}` is not found"))?;
let info: RgbInfo = serde_json::from_str(&value)?;
@ -59,6 +66,20 @@ impl RGBStorage for InMemoryStorage {
fn is_channel_rgb(&self, channel_id: &str, is_pending: bool) -> anyhow::Result<bool> {
let key = self.derive_channel_db_key(channel_id, is_pending)?;
Ok(self.inner.contains_key(&key))
let map = self.inner.borrow();
Ok(map.contains_key(&key))
}
fn write_rgb_info(
&self,
channel_id: &str,
is_pending: bool,
info: &RgbInfo,
) -> anyhow::Result<()> {
let key = self.derive_channel_db_key(channel_id, is_pending)?;
// FIXME: we need a lock before production
let mut map = self.inner.borrow_mut();
map.insert(key, serde_json::to_string(info)?);
Ok(())
}
}

View File

@ -12,7 +12,6 @@ use crate::std::contract::ContractId;
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct RgbInfo {
/// Channel_id
#[serde(skip)]
pub channel_id: String,
/// Channel contract ID
pub contract_id: ContractId,