diff --git a/libs/gl-plugin/.resources/proto/glclient/greenlight.proto b/libs/gl-plugin/.resources/proto/glclient/greenlight.proto index 4730e18b2..a75224793 100644 --- a/libs/gl-plugin/.resources/proto/glclient/greenlight.proto +++ b/libs/gl-plugin/.resources/proto/glclient/greenlight.proto @@ -98,233 +98,6 @@ service Hsm { rpc Ping(Empty) returns (Empty) {} } -enum NetAddressType { - Ipv4 = 0; - Ipv6 = 1; - TorV2 = 2; - TorV3 = 3; -} -message Address { - NetAddressType type = 1; - string addr = 2; - uint32 port = 3; -} - -message GetInfoRequest {} - -message GetInfoResponse { - bytes node_id = 1; - string alias = 2; - bytes color = 3; - uint32 num_peers = 4; - repeated Address addresses = 5; - string version = 6; - uint32 blockheight = 7; - string network = 8; -} - -message StopRequest {} -message StopResponse {} - -message ConnectRequest { - string node_id = 1; - string addr = 2; -} - -message ConnectResponse { - string node_id = 1; - string features = 2; -} - - -message ListPeersRequest { - string node_id = 1; -} - -message Htlc { - string direction = 1; - uint64 id = 2; - string amount = 3; - uint64 expiry = 4; - string payment_hash = 5; - string state = 6; - bool local_trimmed = 7; -} - -message Aliases { - string local = 1; - string remote = 2; -} - -message Channel { - string state = 1; - string owner = 2; - Aliases alias = 18; - string short_channel_id = 3; - uint32 direction = 4; - string channel_id = 5; - string funding_txid = 6; - string close_to_addr = 7; - string close_to = 8; - bool private = 9; - string total = 10; - string dust_limit = 11; - string spendable = 12; - string receivable = 13; - uint32 their_to_self_delay = 14; - uint32 our_to_self_delay = 15; - repeated string status = 16; - repeated Htlc htlcs = 17; -} - -message Peer { - bytes id = 1; - bool connected = 2; - repeated Address addresses = 3; - string features = 4; - repeated Channel channels = 5; -} - -message ListPeersResponse { - repeated Peer peers = 1; -} - -message DisconnectRequest { - string node_id = 1; - bool force = 2; -} - -message DisconnectResponse {} - -enum BtcAddressType { - BECH32 = 0; // Default - P2SH_SEGWIT = 1; -} - -message NewAddrRequest { - BtcAddressType address_type = 1; -} -message NewAddrResponse { - BtcAddressType address_type = 1; - string address = 2; -} - -message ListFundsRequest { - Confirmation minconf = 1; -} - -enum OutputStatus { - CONFIRMED = 0; - UNCONFIRMED = 1; -} - -message ListFundsOutput { - Outpoint output = 1; - Amount amount = 2; - string address = 4; - OutputStatus status = 5; - bool reserved = 6; - uint32 reserved_to_block = 7; -} - -message ListFundsChannel { - bytes peer_id = 1; - bool connected = 2; - uint64 short_channel_id = 3; - uint64 our_amount_msat = 4; - uint64 amount_msat = 5; - bytes funding_txid = 6; - uint32 funding_output = 7; -} - -message ListFundsResponse { - repeated ListFundsOutput outputs = 1; - repeated ListFundsChannel channels = 2; -} - -// Let the node decide what feerate to apply based on its internal -// view of the network's current feerate. -enum FeeratePreset { - NORMAL = 0; - SLOW = 1; - URGENT = 2; -} - -message Feerate { - oneof value { - FeeratePreset preset = 1; - uint64 perkw = 5; - uint64 perkb = 6; - } -} - -message Confirmation { - uint32 blocks = 1; -} - -message WithdrawRequest { - string destination = 1; - Amount amount = 2; - Feerate feerate = 3; - Confirmation minconf = 7; - repeated Outpoint utxos = 8; -} - -message WithdrawResponse { - bytes tx = 1; - bytes txid = 2; -} - -// TODO: Extract AmountOrAll into its own message -// TODO: Extract Feerate into its own message - -message FundChannelRequest { - bytes node_id = 1; - Amount amount = 2; - Feerate feerate = 3; - bool announce = 7; - Confirmation minconf = 8; - //TODO Maybe add UTXOS - string close_to = 10; -} - -message Outpoint { - bytes txid = 1; - uint32 outnum = 2; -} - -message FundChannelResponse { - bytes tx = 1; - Outpoint outpoint = 2; - bytes channel_id = 3; - string close_to = 4; -} - -message Timeout { - uint32 seconds = 1; -} - -message BitcoinAddress { - string address = 1; -} - -message CloseChannelRequest { - bytes node_id = 1; - Timeout unilateraltimeout = 2; - BitcoinAddress destination = 3; -} - -enum CloseChannelType { - MUTUAL = 0; - UNILATERAL = 1; -} - -message CloseChannelResponse { - CloseChannelType close_type = 1; - bytes tx = 2; - bytes txid = 3; -} - message Amount { oneof unit { uint64 millisatoshi = 1; @@ -335,113 +108,10 @@ message Amount { } } -message InvoiceRequest { - Amount amount = 1; - string label = 2; - string description = 3; - bytes preimage = 4; -} - -enum InvoiceStatus { - UNPAID = 0; - PAID = 1; - EXPIRED = 2; -} - -message Invoice { - string label = 1; - string description = 2; - Amount amount = 3; - Amount received = 4; - InvoiceStatus status = 5; - uint32 payment_time = 6; - uint32 expiry_time = 7; - string bolt11 = 8; - bytes payment_hash = 9; - bytes payment_preimage = 10; -} - -message PayRequest { - string bolt11 = 1; - - // Only needed when the invoice does not specify an amount. - Amount amount = 2; - - // Non-zero number of seconds before we should stop retrying - // the payment and return an error. - uint32 timeout = 3; - - double maxfeepercent = 4; - - Amount maxfee = 5; -} - -enum PayStatus { - PENDING = 0; - COMPLETE = 1; - FAILED = 2; -} - -message Payment { - bytes destination = 1; - bytes payment_hash = 2; - bytes payment_preimage = 3; - PayStatus status = 4; - Amount amount = 5; - Amount amount_sent = 6; - string bolt11 = 7; - - // UTC Unix timestamp of the time the invoice was created. - double created_at = 8; - // UTC Unix timestamp of the time the payment was completed - // (successfully or failed). 0 if not completed yet. - uint64 completed_at = 9; -} - -// A payment identifier is a way to reference a unique payment, either -// by its bolt11 string or just the payment hash. Only one of the two -// may be provided at once, since having multiple ones may conflict -// with each other. -message PaymentIdentifier { - oneof id { - string bolt11 = 1; - bytes payment_hash = 2; - } -} - -// Request a list of payments that this node has performed. Optionally -// the query can be narrowed to a single payment by providing an -// identifier. -message ListPaymentsRequest { - PaymentIdentifier identifier = 1; -} - -// The response matching `ListPaymentRequest`. It returns a list of -// PayResponses, i.e., the same format as `Pay` returned its result. -message ListPaymentsResponse { - repeated Payment payments = 1; -} - -message InvoiceIdentifier { - oneof id { - string label = 1; - string invstring = 2; - bytes payment_hash = 3; - } -} - -message ListInvoicesRequest { - InvoiceIdentifier identifier = 1; -} - // Options to stream_incoming to specify what to stream. message StreamIncomingFilter { } -message ListInvoicesResponse { - repeated Invoice invoices = 1; -} - message TlvField { uint64 type = 1; // length is implied since the value field carries its own @@ -464,27 +134,6 @@ message IncomingPayment { } } -// A single hop in a Routehint -message RoutehintHop { - bytes node_id = 1; - string short_channel_id = 2; - uint64 fee_base = 3; - uint32 fee_prop = 4; - uint32 cltv_expiry_delta = 5; -} - -message Routehint { - repeated RoutehintHop hops = 1; -} - -message KeysendRequest { - bytes node_id = 1; - Amount amount = 2; - string label = 3; - repeated Routehint routehints = 4; - repeated TlvField extratlvs = 5; -} - message StreamLogRequest {}; message LogEntry { string line = 1; @@ -571,4 +220,4 @@ message TrampolinePayResponse { uint64 amount_msat = 5; uint64 amount_sent_msat = 6; bytes destination = 7; -} \ No newline at end of file +} diff --git a/libs/gl-plugin/src/lib.rs b/libs/gl-plugin/src/lib.rs index a9f512017..6dd36186a 100644 --- a/libs/gl-plugin/src/lib.rs +++ b/libs/gl-plugin/src/lib.rs @@ -1,12 +1,10 @@ use anyhow::Result; use cln_rpc; use log::{debug, warn}; -use rpc::LightningClient; use serde_json::json; use std::future::Future; use std::sync::Arc; use tokio::sync::broadcast; -use tokio::sync::Mutex; mod awaitables; pub mod config; @@ -17,7 +15,6 @@ pub mod node; pub mod pb; pub mod requests; pub mod responses; -pub mod rpc; pub mod stager; pub mod storage; pub mod tlv; @@ -29,7 +26,6 @@ mod context; #[derive(Clone)] pub struct GlPlugin { - rpc: Arc>, stage: Arc, events: broadcast::Sender, } @@ -95,11 +91,8 @@ pub async fn init( stage: Arc, events: tokio::sync::broadcast::Sender, ) -> Result { - let rpc = Arc::new(Mutex::new(LightningClient::new("lightning-rpc"))); - let state = GlPlugin { events: events.clone(), - rpc, stage, }; @@ -162,12 +155,9 @@ async fn on_peer_connected(plugin: Plugin, v: serde_json::Value) -> Result Result { debug!("Received an openchannel request: {:?}", v); let mut rpc = cln_rpc::ClnRpc::new(plugin.configuration().rpc_file).await?; - - let req = cln_rpc::model::requests::ListdatastoreRequest{ - key: Some(vec![ - "glconf".to_string(), - "request".to_string(), - ]) + + let req = cln_rpc::model::requests::ListdatastoreRequest { + key: Some(vec!["glconf".to_string(), "request".to_string()]), }; let res = rpc.call_typed(&req).await; @@ -178,13 +168,17 @@ async fn on_openchannel(plugin: Plugin, v: serde_json::Value) -> Result { - match _parse_gl_config_from_serialized_request(serialized_request.to_string()) { + match _parse_gl_config_from_serialized_request( + serialized_request.to_string(), + ) { Some(gl_config) => { - return Ok(json!({"result": "continue", "close_to": gl_config.close_to_addr})); + return Ok( + json!({"result": "continue", "close_to": gl_config.close_to_addr}), + ); } None => { debug!("Failed to parse the GlConfig from the serialized request's payload"); - } + } } } None => { @@ -193,10 +187,13 @@ async fn on_openchannel(plugin: Plugin, v: serde_json::Value) -> Result { - log::debug!("An error occurred while searching for a custom close_to address: {}", e); + log::debug!( + "An error occurred while searching for a custom close_to address: {}", + e + ); Ok(json!({"result": "continue"})) } } @@ -232,20 +229,18 @@ async fn on_invoice_payment(plugin: Plugin, v: serde_json::Value) -> Result("listinvoices", req) - .await - .unwrap() - .invoices - .pop() - { + let invoice = match rpc.call_typed(&req).await.unwrap().invoices.pop() { Some(i) => i, None => { warn!( @@ -274,7 +269,7 @@ async fn on_invoice_payment(plugin: Plugin, v: serde_json::Value) -> Result, - pub rpc: Arc>, rpc_path: PathBuf, events: tokio::sync::broadcast::Sender, signer_state: Arc>, @@ -78,8 +75,6 @@ impl PluginNodeServer { rpc_path.push("lightning-rpc"); info!("Connecting to lightning-rpc at {:?}", rpc_path); - let rpc = Arc::new(Mutex::new(LightningClient::new(rpc_path.clone()))); - // Bridge the RPC_BCAST into the events queue let tx = events.clone(); tokio::spawn(async move { @@ -95,15 +90,12 @@ impl PluginNodeServer { let ctx = crate::context::Context::new(); - let rrpc = rpc.clone(); - let s = PluginNodeServer { ctx, tls, - rpc, stage, events, - rpc_path, + rpc_path: rpc_path.clone(), signer_state: Arc::new(Mutex::new(signer_state)), signer_state_store: Arc::new(Mutex::new(signer_state_store)), grpc_binding: config.node_grpc_binding, @@ -114,10 +106,11 @@ impl PluginNodeServer { use tokio::time::{sleep, Duration}; // Move the lock into the closure so we can release it later. - let rpc = rrpc.lock().await; + let mut rpc = cln_rpc::ClnRpc::new(rpc_path.clone()).await.unwrap(); loop { - let res: Result = - rpc.call("getinfo", json!({})).await; + let res = rpc + .call_typed(&cln_rpc::model::requests::GetinfoRequest {}) + .await; match res { Ok(_) => break, Err(e) => { @@ -137,8 +130,7 @@ impl PluginNodeServer { key: Some(vec!["glconf".to_string(), "request".to_string()]), }; - let res: Result = - rpc.call("listdatastore", list_datastore_req).await; + let res = rpc.call_typed(&list_datastore_req).await; match res { Ok(list_datastore_res) => { @@ -177,11 +169,8 @@ impl PluginNodeServer { limiter.until_ready().await } - pub async fn get_rpc(&self) -> LightningClient { - let rpc = self.rpc.lock().await; - let r = rpc.clone(); - drop(rpc); - r + pub async fn get_rpc(&self) -> Result { + cln_rpc::ClnRpc::new(self.rpc_path.clone()).await } } @@ -229,8 +218,8 @@ impl Node for PluginNodeServer { // log entries are produced while we're streaming the // backlog out, but do we care? use tokio::io::{AsyncBufReadExt, BufReader}; - // The nodelet uses its CWD, but CLN creates a network - // subdirectory + // The nodelet uses its CWD, but CLN creates a network + // subdirectory let file = tokio::fs::File::open("../log").await?; let mut file = BufReader::new(file).lines(); @@ -475,10 +464,16 @@ impl Node for PluginNodeServer { ) -> Result, Status> { self.limit().await; let gl_config = req.into_inner(); - let rpc = self.get_rpc().await; + let mut rpc = self.get_rpc().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Internal, + format!("could not connect rpc client: {e}"), + ) + })?; - let res: Result = - rpc.call("getinfo", json!({})).await; + let res = rpc + .call_typed(&cln_rpc::model::requests::GetinfoRequest {}) + .await; let network = match res { Ok(get_info_response) => match get_info_response.network.parse() { @@ -528,20 +523,14 @@ impl Node for PluginNodeServer { .map(|r| r.into()) .collect(); let serialized_req = serde_json::to_string(&requests[0]).unwrap(); - let datastore_res: Result< - crate::cln_rpc::model::responses::DatastoreResponse, - crate::rpc::Error, - > = rpc - .call( - "datastore", - json!({ - "key": vec![ - "glconf".to_string(), - "request".to_string(), - ], - "string": serialized_req, - }), - ) + let datastore_res = rpc + .call_typed(&cln_rpc::model::requests::DatastoreRequest { + key: vec!["glconf".to_string(), "request".to_string()], + string: Some(serialized_req.clone()), + hex: None, + mode: None, + generation: None, + }) .await; match datastore_res { diff --git a/libs/gl-plugin/src/node/wrapper.rs b/libs/gl-plugin/src/node/wrapper.rs index 05b065e1f..0503c05b7 100644 --- a/libs/gl-plugin/src/node/wrapper.rs +++ b/libs/gl-plugin/src/node/wrapper.rs @@ -1,6 +1,9 @@ -use crate::LightningClient; +use std::collections::HashMap; +use std::str::FromStr; + use anyhow::Error; use cln_grpc::pb::{self, node_server::Node}; +use cln_rpc::{self, model::responses::ListpeerchannelsChannelsState}; use log::debug; use tonic::{Request, Response, Status}; @@ -35,8 +38,9 @@ impl Node for WrappedNodeServer { ) -> Result, Status> { let req = req.into_inner(); - use crate::rpc::LightningClient; - let mut rpc = LightningClient::new(self.node_server.rpc_path.clone()); + let mut rpc = cln_rpc::ClnRpc::new(self.node_server.rpc_path.clone()) + .await + .unwrap(); // First we get the incoming channels so we can force them to // be added to the invoice. This is best effort and will be @@ -81,8 +85,8 @@ impl Node for WrappedNodeServer { None => Some(144), // Use a day if not set }; - let res: Result = - rpc.call("invoice", pbreq).await; + let res: Result = + rpc.call_raw("invoice", &pbreq).await; let res: Result = res .map(|r| cln_grpc::pb::InvoiceResponse::from(r)) @@ -554,87 +558,88 @@ impl Node for WrappedNodeServer { } } -impl WrappedNodeServer { - async fn get_routehints(&self, rpc: &mut LightningClient) -> Result, Error> { - use crate::responses::Peer; - - // Get a list of active channels to peers so we can filter out - // offline peers or peers with unconfirmed or closing - // channels. - let res = rpc - .listpeers(None) - .await? - .peers - .into_iter() - .filter(|p| p.channels.len() > 0) - .collect::>(); - - // Now project channels to their state and flatten into a vec - // of short_channel_ids - let active: Vec = res - .iter() - .map(|p| { - p.channels - .iter() - .filter(|c| c.state == "CHANNELD_NORMAL") - .filter_map(|c| c.clone().short_channel_id) - }) - .flatten() - .collect(); +fn check_option(opt: Option, condition: F) -> bool +where + F: FnOnce(&T) -> bool, +{ + opt.as_ref().map_or(false, condition) +} - /* Due to a bug in `listincoming` in CLN we get the real - * `short_channel_id`, whereas we're supposed to use the - * remote alias if the channel is unannounced. This patches - * the issue in GL, and should work transparently once we fix - * `listincoming`. */ - use std::collections::HashMap; - let aliases: HashMap = HashMap::from_iter( - res.iter() - .map(|p| { - p.channels - .iter() - .filter(|c| { - c.short_channel_id.is_some() - && c.alias.is_some() - && c.alias.as_ref().unwrap().remote.is_some() - }) - .map(|c| { - ( - c.short_channel_id.clone().unwrap(), - c.alias.clone().unwrap().remote.unwrap(), - ) +impl WrappedNodeServer { + async fn get_routehints(&self, rpc: &mut cln_rpc::ClnRpc) -> Result, Error> { + // Get a map of active channels to peers with a status of + // "CHANNELD_NORMAL", and it aliases. + // Due to a bug in `listincoming` in CLN we get the real + // `short_channel_id`, whereas we're supposed to use the remote alias if + // the channel is unannounced. This patches the issue in GL, and should + // work transparently once we fix `listincoming`. + let req = cln_rpc::model::requests::ListpeerchannelsRequest { id: None }; + let res = rpc.call_typed(&req).await?; + let active_channels: HashMap< + cln_rpc::primitives::ShortChannelId, + cln_rpc::primitives::ShortChannelId, + > = match res.channels { + Some(channels) => channels + .into_iter() + .filter(|c| { + check_option(c.peer_connected, |&c| c) + && check_option(c.state, |&state| { + state == ListpeerchannelsChannelsState::CHANNELD_NORMAL }) }) - .flatten(), - ); + .filter_map(|c| { + c.short_channel_id.map(|scid| { + let value = c + .alias + .as_ref() + .and_then(|alias| alias.remote) + .unwrap_or(scid); + (scid, value) + }) + }) + .collect(), + None => HashMap::new(), + }; - // Now we can listincoming, filter with the above active list, - // and then map to become `pb::Routehint` instances - Ok(rpc - .listincoming() - .await? + let res: crate::responses::ListIncoming = rpc + .call_raw("listincoming", &crate::requests::ListIncoming {}) + .await?; + let active_incoming_channels: Vec = res .incoming .into_iter() - .filter(|i| active.contains(&i.short_channel_id)) + .filter(|i| { + if let Ok(scid) = cln_rpc::primitives::ShortChannelId::from_str(&i.short_channel_id) + { + active_channels.contains_key(&scid) + } else { + false + } + }) + .collect(); + + let route_hints = active_incoming_channels + .into_iter() .map(|i| { let base: Option = i.fee_base_msat.as_str().try_into().ok(); + let scid = + cln_rpc::primitives::ShortChannelId::from_str(&i.short_channel_id).unwrap(); + let alias = active_channels.get(&scid).unwrap_or(&scid); + let alias_str = format!("{}", alias); pb::Routehint { hops: vec![pb::RouteHop { id: hex::decode(i.id).expect("hex-decoding node_id"), - short_channel_id: aliases - .get(&i.short_channel_id) - .or(Some(&i.short_channel_id)) - .unwrap() - .to_owned(), + short_channel_id: alias_str, feebase: base.map(|b| b.into()), feeprop: i.fee_proportional_millionths, expirydelta: i.cltv_expiry_delta, }], } }) - .collect()) + .collect(); + + Ok(route_hints) } } diff --git a/libs/gl-plugin/src/pb.rs b/libs/gl-plugin/src/pb.rs index 1e8886bbb..b0f6d5fc1 100644 --- a/libs/gl-plugin/src/pb.rs +++ b/libs/gl-plugin/src/pb.rs @@ -1,256 +1,6 @@ tonic::include_proto!("greenlight"); -use self::amount::Unit; use crate::{messages, requests, responses}; -use anyhow::{anyhow, Context, Result}; -use bytes::{Buf, BufMut, Bytes, BytesMut}; -use cln_grpc::pb::RouteHop; -use cln_rpc::primitives::{self, ShortChannelId}; -use log::warn; -use std::str::FromStr; - -impl TryFrom for GetInfoResponse { - type Error = anyhow::Error; - fn try_from(i: responses::GetInfo) -> Result { - Ok(GetInfoResponse { - alias: i.alias, - blockheight: i.blockheight as u32, - color: hex::decode(i.color)?, - addresses: vec![], - node_id: hex::decode(i.id)?, - version: i.version, - num_peers: i.num_peers as u32, - network: i.network, - }) - } -} -impl From for ConnectResponse { - fn from(c: responses::Connect) -> ConnectResponse { - ConnectResponse { - node_id: c.id, - features: c.features, - } - } -} - -impl From for Aliases { - fn from(a: responses::Aliases) -> Self { - Self { - local: a.local.unwrap_or_default(), - remote: a.remote.unwrap_or_default(), - } - } -} - -impl From<&responses::Channel> for Channel { - fn from(c: &responses::Channel) -> Self { - Channel { - state: c.state.to_string(), - owner: c.owner.clone().unwrap_or_default(), - alias: c.alias.clone().map(|c| c.into()), - short_channel_id: c.short_channel_id.clone().unwrap_or_default(), - direction: c.direction.unwrap_or_default() as u32, - channel_id: c.channel_id.clone(), - funding_txid: c.funding_txid.clone(), - close_to_addr: c.close_to_addr.clone().unwrap_or_default(), - close_to: c.close_to.clone().unwrap_or_default(), - private: c.private, - total: c.total_msat.to_string(), - dust_limit: c.dust_limit_msat.to_string(), - spendable: c.spendable_msat.to_string(), - receivable: c.receivable_msat.to_string(), - their_to_self_delay: c.their_to_self_delay as u32, - our_to_self_delay: c.our_to_self_delay as u32, - status: vec![], - htlcs: vec![], // TODO implement - } - } -} - -impl TryFrom<&responses::Peer> for Peer { - type Error = anyhow::Error; - - fn try_from(p: &responses::Peer) -> Result { - let features = match &p.features { - Some(f) => f.to_string(), - None => "".to_string(), - }; - - Ok(Peer { - id: hex::decode(&p.id)?, - connected: p.connected, - features: features, - addresses: vec![], // TODO Add field - channels: p.channels.iter().map(|c| c.into()).collect(), - }) - } -} - -impl TryFrom for ListPeersResponse { - type Error = anyhow::Error; - fn try_from(lp: responses::ListPeers) -> Result { - let peers: Result> = lp.peers.iter().map(|p| p.try_into()).collect(); - - Ok(ListPeersResponse { peers: peers? }) - } -} - -impl From<&str> for OutputStatus { - fn from(s: &str) -> Self { - match s { - "confirmed" => OutputStatus::Confirmed, - "unconfirmed" => OutputStatus::Unconfirmed, - _ => panic!("Unknown output status {}", s), - } - } -} - -impl From<&responses::ListFundsOutput> for ListFundsOutput { - fn from(o: &responses::ListFundsOutput) -> Self { - let status: OutputStatus = o.status[..].into(); - ListFundsOutput { - address: o.address.clone(), - amount: Some(Amount { - unit: Some(amount::Unit::Millisatoshi(o.amount_msat.0)), - }), - reserved: o.reserved, - reserved_to_block: o.reserved_to_block.unwrap_or_default(), - output: Some(Outpoint { - outnum: o.output as u32, - txid: hex::decode(&o.txid).unwrap(), - }), - status: status as i32, - } - } -} - -/// Small helper to encode short_channel_ids as protobuf ints -fn parse_scid(scid: &Option) -> u64 { - match &scid { - Some(i) => match ShortChannelId::from_str(&i) { - Ok(i) => (i.block() as u64) << 40 | (i.txindex() as u64) << 16 | (i.outnum() as u64), - Err(e) => { - warn!( - "JSON-RPC returned an unparseable short_channel_id {}: {}", - i, e - ); - 0 - } - }, - None => 0, - } -} - -impl From<&responses::ListFundsChannel> for ListFundsChannel { - fn from(c: &responses::ListFundsChannel) -> Self { - ListFundsChannel { - peer_id: hex::decode(&c.peer_id).unwrap(), - connected: c.connected, - short_channel_id: parse_scid(&c.short_channel_id), - our_amount_msat: c.our_amount_msat.0, - amount_msat: c.amount_msat.0, - funding_txid: hex::decode(&c.funding_txid).unwrap(), - funding_output: c.funding_output as u32, - } - } -} - -impl From for ListFundsResponse { - fn from(lf: responses::ListFunds) -> Self { - ListFundsResponse { - outputs: lf.outputs.iter().map(|o| o.into()).collect(), - channels: lf.channels.iter().map(|c| c.into()).collect(), - } - } -} - -impl From for WithdrawResponse { - fn from(r: responses::Withdraw) -> Self { - WithdrawResponse { - tx: hex::decode(r.tx).unwrap(), - txid: hex::decode(r.txid).unwrap(), - } - } -} - -impl TryFrom for requests::FundChannel { - type Error = anyhow::Error; - fn try_from(f: FundChannelRequest) -> Result { - let amount: requests::Amount = match f.amount { - Some(v) => v.try_into()?, - None => return Err(anyhow!("Funding amount cannot be omitted.")), - }; - - if let requests::Amount::Any = amount { - return Err(anyhow!( - "Funding amount cannot be 'any'. Did you mean to use 'all'?" - )); - } - - if let requests::Amount::Millisatoshi(a) = amount { - if a % 1000 != 0 { - return Err(anyhow!("Funding amount must be expressed integer satoshis. Millisatoshi amount {} is not.", a)); - } - } - - Ok(requests::FundChannel { - id: hex::encode(f.node_id), - amount: amount, - feerate: None, - announce: Some(f.announce), - minconf: f.minconf.map(|c| c.blocks), - close_to: match f.close_to.as_ref() { - "" => None, - v => Some(v.to_string()), - }, - }) - } -} - -impl From for FundChannelResponse { - fn from(r: responses::FundChannel) -> Self { - FundChannelResponse { - tx: hex::decode(r.tx).unwrap(), - outpoint: Some(Outpoint { - txid: hex::decode(r.txid).unwrap(), - outnum: r.outpoint, - }), - channel_id: hex::decode(r.channel_id).unwrap(), - close_to: r.close_to.unwrap_or("".to_string()), - } - } -} - -impl HsmRequestContext { - pub fn to_client_hsmfd_msg(&self) -> Result { - let mut buf = BytesMut::with_capacity(2 + 33 + 8 + 8); - - buf.put_u16(9); // client_hsmfd type - buf.put_slice(&self.node_id[..]); - buf.put_u64(self.dbid); - buf.put_u64(self.capabilities); - - Ok(buf.freeze()) - } - - pub fn from_client_hsmfd_msg(msg: &mut Bytes) -> Result { - let typ = msg.get_u16(); - - if typ != 9 { - return Err(anyhow!("message is not an init")); - } - - let mut node_id = [0u8; 33]; - msg.copy_to_slice(&mut node_id); - let dbid = msg.get_u64(); - let caps = msg.get_u64(); - - Ok(HsmRequestContext { - dbid, - node_id: node_id.to_vec(), - capabilities: caps, - }) - } -} +use cln_rpc::primitives; impl HsmRequest { pub fn get_type(&self) -> u16 { @@ -258,79 +8,6 @@ impl HsmRequest { } } -impl TryFrom for CloseChannelResponse { - type Error = anyhow::Error; - fn try_from(c: responses::CloseChannel) -> Result { - Ok(CloseChannelResponse { - close_type: match c.close_type.as_ref() { - "mutual" => CloseChannelType::Mutual as i32, - "unilateral" => CloseChannelType::Unilateral as i32, - _ => return Err(anyhow!("Unexpected close type: {}", c.close_type)), - }, - tx: hex::decode(c.tx).unwrap(), - txid: hex::decode(c.txid).unwrap(), - }) - } -} -impl From for requests::CloseChannel { - fn from(r: CloseChannelRequest) -> Self { - requests::CloseChannel { - node_id: hex::encode(r.node_id), - timeout: match r.unilateraltimeout { - Some(v) => Some(v.seconds), - None => None, - }, - destination: match r.destination { - None => None, - Some(v) => Some(v.address), - }, - } - } -} - -impl TryFrom for requests::Invoice { - type Error = anyhow::Error; - - fn try_from(i: InvoiceRequest) -> Result { - if i.label.is_empty() { - return Err(anyhow!( - "Label must be set, not empty and unique from all other invoice labels." - )); - } - - let amt: Amount = i.amount.ok_or(anyhow!( - "No amount specified. Use Amount(any=true) if you want an amountless invoice" - ))?; - - let preimage = (!i.preimage.is_empty()).then(|| hex::encode(&i.preimage)); - let amount_msat: primitives::AmountOrAny = match amt.unit.ok_or( - anyhow!("No amount specified. Use Amount(any=true) if you want an amountless invoice") - )? { - Unit::All(_) => return Err(anyhow!( - "Amount cannot be set to `all`. Use Amount(any=true) if you want an amountless invoice" - )), - Unit::Any(_) => primitives::AmountOrAny::Any, - Unit::Millisatoshi(a) => primitives::AmountOrAny::Amount(primitives::Amount::from_msat(a)), - Unit::Satoshi(a) => primitives::AmountOrAny::Amount(primitives::Amount::from_sat(a)), - Unit::Bitcoin(a) => primitives::AmountOrAny::Amount(primitives::Amount::from_btc(a)), - - }; - - Ok(requests::Invoice { - amount_msat, - label: i.label, - description: i.description, - exposeprivatechannels: None, - expiry: None, - fallbacks: None, - preimage, - cltv: None, - deschashonly: None, - dev_routes: None, - }) - } -} - impl From for Amount { fn from(i: u64) -> Self { Amount { @@ -339,301 +16,6 @@ impl From for Amount { } } -/// Converts the result of the `invoice` call into the shared format -/// for invoices in protobuf, hence the subset of fields that are -/// initialized to default values. -impl From for Invoice { - fn from(i: responses::Invoice) -> Invoice { - Invoice { - label: "".to_string(), - description: "".to_string(), - payment_preimage: vec![], - amount: None, - received: None, - payment_time: 0, - status: InvoiceStatus::Unpaid as i32, - bolt11: i.bolt11, - payment_hash: hex::decode(i.payment_hash).unwrap(), - expiry_time: i.expiry_time, - } - } -} - -impl TryFrom for requests::Amount { - type Error = anyhow::Error; - fn try_from(a: Amount) -> Result { - match a.unit { - Some(amount::Unit::Millisatoshi(v)) => Ok(requests::Amount::Millisatoshi(v)), - Some(amount::Unit::Satoshi(v)) => Ok(requests::Amount::Satoshi(v)), - Some(amount::Unit::Bitcoin(v)) => Ok(requests::Amount::Bitcoin(v)), - Some(amount::Unit::All(_)) => Ok(requests::Amount::All), - Some(amount::Unit::Any(_)) => Ok(requests::Amount::Any), - None => Err(anyhow!("cannot convert a unit-less amount")), - } - } -} - -impl From for Payment { - fn from(p: responses::Pay) -> Self { - Payment { - destination: hex::decode(p.destination).unwrap(), - payment_hash: hex::decode(p.payment_hash).unwrap(), - payment_preimage: p - .preimage - .map(|p| hex::decode(p).unwrap()) - .unwrap_or_default(), - amount: Some(Amount { - unit: Some(amount::Unit::Millisatoshi(p.msatoshi)), - }), - amount_sent: Some(Amount { - unit: Some(amount::Unit::Millisatoshi(p.msatoshi_sent)), - }), - status: match p.status.as_ref() { - "pending" => PayStatus::Pending as i32, - "complete" => PayStatus::Complete as i32, - "failed" => PayStatus::Failed as i32, - o => panic!("Unmapped pay status: {}", o), - }, - bolt11: p.bolt11.unwrap_or("".to_string()), - created_at: p.created_at, - completed_at: p.completed_at.unwrap_or_default(), - } - } -} - -impl From for requests::Pay { - fn from(p: PayRequest) -> Self { - // PartialEq papers over the differences between 0.0 and -0.0 - // in floats. - let maxfeepercent = if p.maxfeepercent.eq(&0.0) { - None - } else { - Some(p.maxfeepercent) - }; - - requests::Pay { - bolt11: p.bolt11, - amount: p.amount.map(|a| a.try_into().unwrap()), - retry_for: match p.timeout { - 0 => None, - v => Some(v), - }, - maxfee: p.maxfee.map(|a| a.try_into().unwrap()), - maxfeepercent, - } - } -} - -impl TryFrom for requests::Feerate { - type Error = anyhow::Error; - - fn try_from(value: Feerate) -> Result { - use requests as r; - let res = match value.value { - Some(v) => match v { - feerate::Value::Preset(p) => match p { - 0 => r::Feerate::Normal, - 1 => r::Feerate::Slow, - 2 => r::Feerate::Urgent, - n => return Err(anyhow!("no such feerate preset {}", n)), - }, - feerate::Value::Perkw(v) => r::Feerate::PerKw(v), - feerate::Value::Perkb(v) => r::Feerate::PerKb(v), - }, - None => return Err(anyhow!("Feerate must have a value set")), - }; - Ok(res) - } -} - -impl TryFrom for requests::Outpoint { - type Error = anyhow::Error; - - fn try_from(value: Outpoint) -> Result { - if value.txid.len() != 32 { - return Err(anyhow!( - "{} is not a valid transaction ID", - hex::encode(&value.txid) - )); - } - - Ok(requests::Outpoint { - txid: value.txid, - outnum: value.outnum.try_into().context("outnum out of range")?, - }) - } -} - -impl TryFrom for requests::ListPays { - type Error = anyhow::Error; - - fn try_from(v: ListPaymentsRequest) -> Result { - match v.identifier { - Some(identifier) => match identifier.id { - Some(payment_identifier::Id::Bolt11(b)) => Ok(requests::ListPays { - payment_hash: None, - bolt11: Some(b), - }), - Some(payment_identifier::Id::PaymentHash(h)) => Ok(requests::ListPays { - payment_hash: Some(hex::encode(h)), - bolt11: None, - }), - None => Ok(requests::ListPays { - bolt11: None, - payment_hash: None, - }), - }, - None => Ok(requests::ListPays { - bolt11: None, - payment_hash: None, - }), - } - } -} - -impl TryFrom for ListPaymentsResponse { - type Error = anyhow::Error; - - fn try_from(v: responses::ListPays) -> Result { - let payments: Result> = v.pays.iter().map(|p| p.clone().try_into()).collect(); - Ok(ListPaymentsResponse { - payments: payments?, - }) - } -} - -impl TryFrom for Payment { - type Error = anyhow::Error; - fn try_from(p: responses::ListPaysPay) -> Result { - Ok(Payment { - destination: hex::decode(p.destination).unwrap(), - payment_hash: hex::decode(p.payment_hash).unwrap(), - payment_preimage: p - .payment_preimage - .map_or(vec![], |p| hex::decode(p).unwrap()), - status: match p.status.as_ref() { - "pending" => PayStatus::Pending as i32, - "complete" => PayStatus::Complete as i32, - "failed" => PayStatus::Failed as i32, - o => panic!("Unmapped pay status: {}", o), - }, - amount: p.amount_msat.map(|a| a.try_into().unwrap()), - amount_sent: Some(p.amount_sent_msat.try_into()?), - bolt11: p.bolt11.unwrap_or("".to_string()), - created_at: p.created_at, - completed_at: p.completed_at.unwrap_or_default(), - }) - } -} - -impl TryFrom for Amount { - type Error = anyhow::Error; - fn try_from(s: String) -> Result { - match s.strip_suffix("msat") { - Some(v) => Ok(Amount { - unit: Some(amount::Unit::Millisatoshi(v.parse()?)), - }), - None => Err(anyhow!("Amount {} does not have 'msat' suffix", s)), - } - } -} - -impl TryFrom for Amount { - type Error = anyhow::Error; - fn try_from(s: crate::responses::MSat) -> Result { - Ok(Amount { - unit: Some(amount::Unit::Millisatoshi(s.0)), - }) - } -} - -impl TryFrom for InvoiceStatus { - type Error = anyhow::Error; - - fn try_from(v: String) -> Result { - match v.as_ref() { - "unpaid" => Ok(InvoiceStatus::Unpaid), - "paid" => Ok(InvoiceStatus::Paid), - "expired" => Ok(InvoiceStatus::Expired), - s => Err(anyhow!("{} is not a valid InvoiceStatus", s)), - } - } -} - -impl TryFrom for requests::ListInvoices { - type Error = anyhow::Error; - - fn try_from(v: ListInvoicesRequest) -> Result { - match v.identifier { - None => Ok(requests::ListInvoices { - label: None, - invstring: None, - payment_hash: None, - }), - Some(i) => match i.id { - None => Ok(requests::ListInvoices { - label: None, - invstring: None, - payment_hash: None, - }), - Some(invoice_identifier::Id::Label(s)) => Ok(requests::ListInvoices { - label: Some(s), - invstring: None, - payment_hash: None, - }), - Some(invoice_identifier::Id::PaymentHash(s)) => Ok(requests::ListInvoices { - label: None, - invstring: None, - payment_hash: Some(hex::encode(s)), - }), - Some(invoice_identifier::Id::Invstring(s)) => Ok(requests::ListInvoices { - label: None, - invstring: Some(s), - payment_hash: None, - }), - }, - } - } -} - -impl From<&responses::ListInvoiceInvoice> for Invoice { - fn from(i: &responses::ListInvoiceInvoice) -> Invoice { - let status: InvoiceStatus = i.status.clone().try_into().unwrap(); - let amount: Amount = if i.amount.is_none() { - Amount { - unit: Some(crate::pb::amount::Unit::Any(true)), - } - } else { - i.amount.clone().unwrap().try_into().unwrap() - }; - - Invoice { - amount: Some(amount), - bolt11: i.bolt11.clone(), - description: i.description.clone(), - expiry_time: i.expiry_time, - label: i.label.clone(), - payment_hash: hex::decode(&i.payment_hash).unwrap(), - payment_preimage: i - .payment_preimage - .clone() - .map(|p| hex::decode(p).unwrap()) - .unwrap_or_default(), - status: status as i32, - received: i.received.clone().map(|i| i.try_into().unwrap()), - payment_time: i.payment_time.clone().unwrap_or_default(), - } - } -} - -impl TryFrom for ListInvoicesResponse { - type Error = anyhow::Error; - fn try_from(l: responses::ListInvoices) -> Result { - let invoices: Vec = l.invoices.iter().map(|i| i.into()).collect(); - Ok(ListInvoicesResponse { invoices }) - } -} - impl From for TlvField { fn from(f: messages::TlvField) -> TlvField { TlvField { @@ -643,118 +25,12 @@ impl From for TlvField { } } -impl TryFrom for requests::Keysend { - type Error = anyhow::Error; - fn try_from(r: KeysendRequest) -> Result { - use std::collections::HashMap; - // Transform the extratlvs into aa key-value dict: - let mut tlvs: HashMap = HashMap::new(); - - for e in r.extratlvs { - tlvs.insert(e.r#type, hex::encode(e.value)); - } - - let mut routehints = vec![]; - for rh in r.routehints { - routehints.push(rh.into()) - } - - Ok(requests::Keysend { - destination: hex::encode(r.node_id), - msatoshi: r.amount.unwrap().try_into()?, - label: r.label, - exemptfee: None, - maxfeepercent: None, - maxdelay: None, - extratlvs: Some(tlvs), - routehints: Some(routehints), - retry_for: None, - }) - } -} - -impl From for Payment { - fn from(r: responses::Keysend) -> Payment { - use std::time::SystemTime; - - let amount_msat = r.msatoshi.unwrap_or_else(|| { - r.amount_msat - .expect("neither amount_msat nor msatoshi is set in keysend response") - .0 - }); - - let amount_sent_msat = r.msatoshi_sent.unwrap_or_else(|| { - r.amount_sent_msat - .expect("neither amount_sent_msat nor msatoshi_sent is set in keysend response") - .0 - }); - - Payment { - destination: hex::decode(r.destination).unwrap(), - payment_hash: hex::decode(r.payment_hash).unwrap(), - payment_preimage: hex::decode(r.payment_preimage.unwrap_or("".to_string())).unwrap(), - status: match r.status.as_ref() { - "pending" => PayStatus::Pending as i32, - "complete" => PayStatus::Complete as i32, - "failed" => PayStatus::Failed as i32, - o => panic!("Unmapped pay status: {}", o), - }, - amount: Some(Amount { - unit: Some(amount::Unit::Millisatoshi(amount_msat)), - }), - amount_sent: Some(Amount { - unit: Some(amount::Unit::Millisatoshi(amount_sent_msat)), - }), - bolt11: "".to_string(), - created_at: SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap() - .as_secs_f64(), - completed_at: 0, - } - } -} - -impl From for requests::RoutehintHop { - fn from(r: RoutehintHop) -> requests::RoutehintHop { - requests::RoutehintHop { - id: hex::encode(r.node_id), - scid: r.short_channel_id, - feebase: r.fee_base, - feeprop: r.fee_prop, - expirydelta: r.cltv_expiry_delta as u16, - } - } -} - -impl From for Vec { - fn from(r: Routehint) -> Vec { - let mut hops = vec![]; - for h in r.hops { - hops.push(h.into()) - } - hops - } -} - -impl From for requests::RoutehintHopDev { - fn from(r: RoutehintHop) -> requests::RoutehintHopDev { - requests::RoutehintHopDev { - id: hex::encode(r.node_id), - short_channel_id: r.short_channel_id, - fee_base_msat: r.fee_base, - fee_proportional_millionths: r.fee_prop, - cltv_expiry_delta: r.cltv_expiry_delta as u16, - } - } -} - -impl From for requests::RoutehintHopDev { - fn from(r: RouteHop) -> requests::RoutehintHopDev { +impl From for requests::RoutehintHopDev { + fn from(r: cln_grpc::pb::RouteHop) -> requests::RoutehintHopDev { requests::RoutehintHopDev { id: hex::encode(r.id), short_channel_id: r.short_channel_id, - fee_base_msat: r.feebase.map(|f| f.msat).unwrap(), + fee_base_msat: r.feebase.map(|f| f.msat), fee_proportional_millionths: r.feeprop, cltv_expiry_delta: r.expirydelta as u16, } @@ -801,14 +77,3 @@ impl From for cln_grpc::pb::InvoiceResponse { } } } -impl From for requests::Amount { - fn from(a: cln_grpc::pb::AmountOrAny) -> Self { - match a.value { - Some(cln_grpc::pb::amount_or_any::Value::Any(_)) => requests::Amount::Any, - Some(cln_grpc::pb::amount_or_any::Value::Amount(a)) => { - requests::Amount::Millisatoshi(a.msat) - } - None => panic!(), - } - } -} diff --git a/libs/gl-plugin/src/requests.rs b/libs/gl-plugin/src/requests.rs index af52416da..0309eca38 100644 --- a/libs/gl-plugin/src/requests.rs +++ b/libs/gl-plugin/src/requests.rs @@ -180,7 +180,7 @@ pub struct RoutehintHop { pub struct RoutehintHopDev { pub id: String, pub short_channel_id: String, - pub fee_base_msat: u64, + pub fee_base_msat: Option, pub fee_proportional_millionths: u32, pub cltv_expiry_delta: u16, } diff --git a/libs/gl-plugin/src/rpc.rs b/libs/gl-plugin/src/rpc.rs deleted file mode 100644 index 663043795..000000000 --- a/libs/gl-plugin/src/rpc.rs +++ /dev/null @@ -1,189 +0,0 @@ -use crate::{requests, responses}; -use clightningrpc::Response; -use cln_rpc::codec::JsonCodec; -use futures::{SinkExt, StreamExt}; -use log::{debug, error, warn}; -use serde::{de::DeserializeOwned, Serialize}; -use serde_json::{json, Deserializer, Value}; -use std::path::{Path, PathBuf}; -use thiserror::Error; -use tokio::net::UnixStream; -use tokio_util::codec::Framed; - -#[derive(Error, Debug)] -pub enum Error { - #[error("error calling RPC: {0}")] - RpcError(clightningrpc::Error), - #[error("error calling RPC: {0}")] - ClnRpcError(anyhow::Error), - #[error("empty result from RPC")] - EmptyResult, - #[error("this is unsupported: {0}")] - Unsupported(String), - #[error("IO error talking to RPC: {0}")] - IoError(#[from] std::io::Error), - #[error("error serializing / deserializing: {0}")] - Serialization(#[from] serde_json::Error), - #[error("unknown error {0}")] - Other(Box), -} - -#[derive(Clone, Debug)] -pub struct LightningClient { - sockpath: PathBuf, -} - -impl LightningClient { - pub fn new>(sockpath: P) -> LightningClient { - LightningClient { - sockpath: sockpath.as_ref().to_path_buf(), - } - } - - pub async fn send_request( - &self, - method: &str, - params: S, - ) -> Result, Error> { - // Setup connection - let stream = UnixStream::connect(&self.sockpath).await?; - let mut codec = Framed::new(stream, JsonCodec::default()); - - // TODO Re-enable timeout for the socket - //stream.set_read_timeout(self.timeout)?; - //stream.set_write_timeout(self.timeout)?; - - let request = json!({ - "method": method, - "params": params, - "id": 0, // we always open a new connection, so we don't have to care about the nonce - "jsonrpc": "2.0", - }); - - debug!( - "Sending request to JSON-RPC: {}", - serde_json::to_string(&request).unwrap() - ); - - if let Err(e) = codec.send(request).await { - warn!("Error sending request to RPC interface: {}", e); - return Err(Error::ClnRpcError(e)); - } - - let response = match codec.next().await { - Some(Ok(v)) => v, - Some(Err(e)) => { - warn!("Error from RPC: {:?}", e); - return Err(Error::ClnRpcError(e)); - } - None => { - warn!("Error reading response from RPC interface, returned None"); - return Err(Error::EmptyResult); - } - }; - - debug!( - "Read response from JSON-RPC: {}", - serde_json::to_string(&response).unwrap() - ); - - // TODO (cdecker) inefficient: serialize just to re-serialize, - // but it's how I got it working. - let response: Response = Deserializer::from_str(&response.to_string()) - .into_iter() - .next() - .map_or(Err(Error::EmptyResult), |res| Ok(res?))?; - Ok(response) - } - - /// Generic call function for RPC calls. - pub async fn call( - &self, - method: &str, - input: T, - ) -> Result { - self.send_request(method, input) - .await? - .into_result() - .map_err(|e| Error::RpcError(e)) - } - - /// Show information about this node. - pub async fn getinfo(&self) -> Result { - self.call("getinfo", json!({})).await - } - - pub async fn stop(&self) -> Result<(), Error> { - match self.call::("stop", json!({})).await { - Ok(()) => Ok(()), - Err(e) => { - debug!("Ignoring error on `stop` call: {}", e); - Ok(()) - } - } - } - - pub async fn connect<'a>( - &self, - req: &requests::Connect<'a>, - ) -> Result { - self.call("connect", req).await - } - - pub async fn listpeers( - &self, - node_id: Option<&str>, - ) -> Result { - self.call( - "listpeers", - requests::ListPeers { - id: node_id, - level: None, - }, - ) - .await - } - - pub async fn disconnect(&self, node_id: &str, force: bool) -> Result<(), Error> { - if force { - return Err(Error::Unsupported( - "Force-disconnects are currently not supported".to_owned(), - )); - } - - self.call::( - "disconnect", - requests::Disconnect { id: node_id }, - ) - .await?; - Ok(()) - } - - pub async fn newaddr(&self, typ: crate::pb::BtcAddressType) -> Result { - use crate::pb::BtcAddressType; - let addresstype = match typ { - BtcAddressType::Bech32 => "bech32", - BtcAddressType::P2shSegwit => "p2sh-segwit", - }; - let res: responses::NewAddr = self - .call( - "newaddr", - requests::NewAddr { - addresstype: Some(addresstype), - }, - ) - .await?; - - let addr = match typ { - BtcAddressType::Bech32 => res.bech32.unwrap(), - BtcAddressType::P2shSegwit => res.p2sh_segwit.unwrap(), - }; - - Ok(addr) - } - - pub async fn listincoming(&self) -> Result { - self.call("listincoming", crate::requests::ListIncoming {}) - .await - } -} diff --git a/libs/gl-signerproxy/.resources/proto/glclient/greenlight.proto b/libs/gl-signerproxy/.resources/proto/glclient/greenlight.proto index 4730e18b2..a75224793 100644 --- a/libs/gl-signerproxy/.resources/proto/glclient/greenlight.proto +++ b/libs/gl-signerproxy/.resources/proto/glclient/greenlight.proto @@ -98,233 +98,6 @@ service Hsm { rpc Ping(Empty) returns (Empty) {} } -enum NetAddressType { - Ipv4 = 0; - Ipv6 = 1; - TorV2 = 2; - TorV3 = 3; -} -message Address { - NetAddressType type = 1; - string addr = 2; - uint32 port = 3; -} - -message GetInfoRequest {} - -message GetInfoResponse { - bytes node_id = 1; - string alias = 2; - bytes color = 3; - uint32 num_peers = 4; - repeated Address addresses = 5; - string version = 6; - uint32 blockheight = 7; - string network = 8; -} - -message StopRequest {} -message StopResponse {} - -message ConnectRequest { - string node_id = 1; - string addr = 2; -} - -message ConnectResponse { - string node_id = 1; - string features = 2; -} - - -message ListPeersRequest { - string node_id = 1; -} - -message Htlc { - string direction = 1; - uint64 id = 2; - string amount = 3; - uint64 expiry = 4; - string payment_hash = 5; - string state = 6; - bool local_trimmed = 7; -} - -message Aliases { - string local = 1; - string remote = 2; -} - -message Channel { - string state = 1; - string owner = 2; - Aliases alias = 18; - string short_channel_id = 3; - uint32 direction = 4; - string channel_id = 5; - string funding_txid = 6; - string close_to_addr = 7; - string close_to = 8; - bool private = 9; - string total = 10; - string dust_limit = 11; - string spendable = 12; - string receivable = 13; - uint32 their_to_self_delay = 14; - uint32 our_to_self_delay = 15; - repeated string status = 16; - repeated Htlc htlcs = 17; -} - -message Peer { - bytes id = 1; - bool connected = 2; - repeated Address addresses = 3; - string features = 4; - repeated Channel channels = 5; -} - -message ListPeersResponse { - repeated Peer peers = 1; -} - -message DisconnectRequest { - string node_id = 1; - bool force = 2; -} - -message DisconnectResponse {} - -enum BtcAddressType { - BECH32 = 0; // Default - P2SH_SEGWIT = 1; -} - -message NewAddrRequest { - BtcAddressType address_type = 1; -} -message NewAddrResponse { - BtcAddressType address_type = 1; - string address = 2; -} - -message ListFundsRequest { - Confirmation minconf = 1; -} - -enum OutputStatus { - CONFIRMED = 0; - UNCONFIRMED = 1; -} - -message ListFundsOutput { - Outpoint output = 1; - Amount amount = 2; - string address = 4; - OutputStatus status = 5; - bool reserved = 6; - uint32 reserved_to_block = 7; -} - -message ListFundsChannel { - bytes peer_id = 1; - bool connected = 2; - uint64 short_channel_id = 3; - uint64 our_amount_msat = 4; - uint64 amount_msat = 5; - bytes funding_txid = 6; - uint32 funding_output = 7; -} - -message ListFundsResponse { - repeated ListFundsOutput outputs = 1; - repeated ListFundsChannel channels = 2; -} - -// Let the node decide what feerate to apply based on its internal -// view of the network's current feerate. -enum FeeratePreset { - NORMAL = 0; - SLOW = 1; - URGENT = 2; -} - -message Feerate { - oneof value { - FeeratePreset preset = 1; - uint64 perkw = 5; - uint64 perkb = 6; - } -} - -message Confirmation { - uint32 blocks = 1; -} - -message WithdrawRequest { - string destination = 1; - Amount amount = 2; - Feerate feerate = 3; - Confirmation minconf = 7; - repeated Outpoint utxos = 8; -} - -message WithdrawResponse { - bytes tx = 1; - bytes txid = 2; -} - -// TODO: Extract AmountOrAll into its own message -// TODO: Extract Feerate into its own message - -message FundChannelRequest { - bytes node_id = 1; - Amount amount = 2; - Feerate feerate = 3; - bool announce = 7; - Confirmation minconf = 8; - //TODO Maybe add UTXOS - string close_to = 10; -} - -message Outpoint { - bytes txid = 1; - uint32 outnum = 2; -} - -message FundChannelResponse { - bytes tx = 1; - Outpoint outpoint = 2; - bytes channel_id = 3; - string close_to = 4; -} - -message Timeout { - uint32 seconds = 1; -} - -message BitcoinAddress { - string address = 1; -} - -message CloseChannelRequest { - bytes node_id = 1; - Timeout unilateraltimeout = 2; - BitcoinAddress destination = 3; -} - -enum CloseChannelType { - MUTUAL = 0; - UNILATERAL = 1; -} - -message CloseChannelResponse { - CloseChannelType close_type = 1; - bytes tx = 2; - bytes txid = 3; -} - message Amount { oneof unit { uint64 millisatoshi = 1; @@ -335,113 +108,10 @@ message Amount { } } -message InvoiceRequest { - Amount amount = 1; - string label = 2; - string description = 3; - bytes preimage = 4; -} - -enum InvoiceStatus { - UNPAID = 0; - PAID = 1; - EXPIRED = 2; -} - -message Invoice { - string label = 1; - string description = 2; - Amount amount = 3; - Amount received = 4; - InvoiceStatus status = 5; - uint32 payment_time = 6; - uint32 expiry_time = 7; - string bolt11 = 8; - bytes payment_hash = 9; - bytes payment_preimage = 10; -} - -message PayRequest { - string bolt11 = 1; - - // Only needed when the invoice does not specify an amount. - Amount amount = 2; - - // Non-zero number of seconds before we should stop retrying - // the payment and return an error. - uint32 timeout = 3; - - double maxfeepercent = 4; - - Amount maxfee = 5; -} - -enum PayStatus { - PENDING = 0; - COMPLETE = 1; - FAILED = 2; -} - -message Payment { - bytes destination = 1; - bytes payment_hash = 2; - bytes payment_preimage = 3; - PayStatus status = 4; - Amount amount = 5; - Amount amount_sent = 6; - string bolt11 = 7; - - // UTC Unix timestamp of the time the invoice was created. - double created_at = 8; - // UTC Unix timestamp of the time the payment was completed - // (successfully or failed). 0 if not completed yet. - uint64 completed_at = 9; -} - -// A payment identifier is a way to reference a unique payment, either -// by its bolt11 string or just the payment hash. Only one of the two -// may be provided at once, since having multiple ones may conflict -// with each other. -message PaymentIdentifier { - oneof id { - string bolt11 = 1; - bytes payment_hash = 2; - } -} - -// Request a list of payments that this node has performed. Optionally -// the query can be narrowed to a single payment by providing an -// identifier. -message ListPaymentsRequest { - PaymentIdentifier identifier = 1; -} - -// The response matching `ListPaymentRequest`. It returns a list of -// PayResponses, i.e., the same format as `Pay` returned its result. -message ListPaymentsResponse { - repeated Payment payments = 1; -} - -message InvoiceIdentifier { - oneof id { - string label = 1; - string invstring = 2; - bytes payment_hash = 3; - } -} - -message ListInvoicesRequest { - InvoiceIdentifier identifier = 1; -} - // Options to stream_incoming to specify what to stream. message StreamIncomingFilter { } -message ListInvoicesResponse { - repeated Invoice invoices = 1; -} - message TlvField { uint64 type = 1; // length is implied since the value field carries its own @@ -464,27 +134,6 @@ message IncomingPayment { } } -// A single hop in a Routehint -message RoutehintHop { - bytes node_id = 1; - string short_channel_id = 2; - uint64 fee_base = 3; - uint32 fee_prop = 4; - uint32 cltv_expiry_delta = 5; -} - -message Routehint { - repeated RoutehintHop hops = 1; -} - -message KeysendRequest { - bytes node_id = 1; - Amount amount = 2; - string label = 3; - repeated Routehint routehints = 4; - repeated TlvField extratlvs = 5; -} - message StreamLogRequest {}; message LogEntry { string line = 1; @@ -571,4 +220,4 @@ message TrampolinePayResponse { uint64 amount_msat = 5; uint64 amount_sent_msat = 6; bytes destination = 7; -} \ No newline at end of file +}