From fbe7a424b512103cb08308193ba842eadcc4aeb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kyle=20=F0=9F=90=86?= Date: Tue, 25 Nov 2025 18:08:47 -0500 Subject: [PATCH] Add input validation for fee rates, addresses, connections, and contract sizes --- ddk-node/src/command.rs | 25 +- ddk-node/src/ddkrpc.rs | 798 ++++++++++++++-------------------------- ddk-node/src/lib.rs | 71 +++- 3 files changed, 363 insertions(+), 531 deletions(-) diff --git a/ddk-node/src/command.rs b/ddk-node/src/command.rs index f2f1dd3..b5b6106 100644 --- a/ddk-node/src/command.rs +++ b/ddk-node/src/command.rs @@ -142,6 +142,12 @@ pub async fn cli_command( amount, fee_rate, } => { + if fee_rate > 1000 { + return Err(anyhow!( + "Fee rate too high: {} sat/vbyte (max: 1000)", + fee_rate + )); + } let txid = client .send(SendRequest { address, @@ -237,7 +243,12 @@ pub async fn cli_command( print!("{peers}"); } CliCommand::Connect { connect_string } => { - let parts = connect_string.split("@").collect::>(); + let parts: Vec<&str> = connect_string.split('@').collect(); + if parts.len() != 2 { + return Err(anyhow!( + "Invalid connection string format. Expected: pubkey@host:port" + )); + } client .connect_peer(ConnectRequest { pubkey: parts[0].to_string(), @@ -330,6 +341,12 @@ async fn interactive_contract_input( .prompt()? .parse()?; let fee_rate: u64 = Text::new("Fee rate (sats/vbyte):").prompt()?.parse()?; + if fee_rate > 1000 { + return Err(anyhow!( + "Fee rate too high: {} sat/vbyte (max: 1000)", + fee_rate + )); + } let min_price: u64 = Text::new("Minimum Bitcoin price:").prompt()?.parse()?; let max_price: u64 = Text::new("Maximum Bitcoin price:").prompt()?.parse()?; let num_steps: u64 = Text::new("Number of rounding steps:").prompt()?.parse()?; @@ -372,6 +389,12 @@ async fn interactive_contract_input( outcome_payouts.push(outcome_payout); } let fee_rate: u64 = Text::new("Fee rate (sats/vbyte):").prompt()?.parse()?; + if fee_rate > 1000 { + return Err(anyhow!( + "Fee rate too high: {} sat/vbyte (max: 1000)", + fee_rate + )); + } // TODO: list possible events. ddk_payouts::enumeration::create_contract_input( outcome_payouts, diff --git a/ddk-node/src/ddkrpc.rs b/ddk-node/src/ddkrpc.rs index ad25d1d..89ce09b 100644 --- a/ddk-node/src/ddkrpc.rs +++ b/ddk-node/src/ddkrpc.rs @@ -277,8 +277,8 @@ pub struct SignResponse { /// Generated client implementations. pub mod ddk_rpc_client { #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] - use tonic::codegen::*; use tonic::codegen::http::Uri; + use tonic::codegen::*; #[derive(Debug, Clone)] pub struct DdkRpcClient { inner: tonic::client::Grpc, @@ -322,9 +322,8 @@ pub mod ddk_rpc_client { >::ResponseBody, >, >, - , - >>::Error: Into + Send + Sync, + >>::Error: + Into + Send + Sync, { DdkRpcClient::new(InterceptedService::new(inner, interceptor)) } @@ -363,131 +362,103 @@ pub mod ddk_rpc_client { &mut self, request: impl tonic::IntoRequest, ) -> std::result::Result, tonic::Status> { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static("/ddkrpc.DdkRpc/Info"); let mut req = request.into_request(); - req.extensions_mut().insert(GrpcMethod::new("ddkrpc.DdkRpc", "Info")); + req.extensions_mut() + .insert(GrpcMethod::new("ddkrpc.DdkRpc", "Info")); self.inner.unary(req, path, codec).await } pub async fn send_offer( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; + ) -> std::result::Result, tonic::Status> { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static("/ddkrpc.DdkRpc/SendOffer"); let mut req = request.into_request(); - req.extensions_mut().insert(GrpcMethod::new("ddkrpc.DdkRpc", "SendOffer")); + req.extensions_mut() + .insert(GrpcMethod::new("ddkrpc.DdkRpc", "SendOffer")); self.inner.unary(req, path, codec).await } pub async fn accept_offer( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; + ) -> std::result::Result, tonic::Status> + { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/ddkrpc.DdkRpc/AcceptOffer", - ); + let path = http::uri::PathAndQuery::from_static("/ddkrpc.DdkRpc/AcceptOffer"); let mut req = request.into_request(); - req.extensions_mut().insert(GrpcMethod::new("ddkrpc.DdkRpc", "AcceptOffer")); + req.extensions_mut() + .insert(GrpcMethod::new("ddkrpc.DdkRpc", "AcceptOffer")); self.inner.unary(req, path, codec).await } pub async fn list_offers( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; + ) -> std::result::Result, tonic::Status> + { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static("/ddkrpc.DdkRpc/ListOffers"); let mut req = request.into_request(); - req.extensions_mut().insert(GrpcMethod::new("ddkrpc.DdkRpc", "ListOffers")); + req.extensions_mut() + .insert(GrpcMethod::new("ddkrpc.DdkRpc", "ListOffers")); self.inner.unary(req, path, codec).await } pub async fn new_address( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; + ) -> std::result::Result, tonic::Status> + { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static("/ddkrpc.DdkRpc/NewAddress"); let mut req = request.into_request(); - req.extensions_mut().insert(GrpcMethod::new("ddkrpc.DdkRpc", "NewAddress")); + req.extensions_mut() + .insert(GrpcMethod::new("ddkrpc.DdkRpc", "NewAddress")); self.inner.unary(req, path, codec).await } pub async fn wallet_balance( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; + ) -> std::result::Result, tonic::Status> + { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/ddkrpc.DdkRpc/WalletBalance", - ); + let path = http::uri::PathAndQuery::from_static("/ddkrpc.DdkRpc/WalletBalance"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("ddkrpc.DdkRpc", "WalletBalance")); @@ -496,64 +467,51 @@ pub mod ddk_rpc_client { pub async fn wallet_sync( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; + ) -> std::result::Result, tonic::Status> + { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static("/ddkrpc.DdkRpc/WalletSync"); let mut req = request.into_request(); - req.extensions_mut().insert(GrpcMethod::new("ddkrpc.DdkRpc", "WalletSync")); + req.extensions_mut() + .insert(GrpcMethod::new("ddkrpc.DdkRpc", "WalletSync")); self.inner.unary(req, path, codec).await } pub async fn sync( &mut self, request: impl tonic::IntoRequest, ) -> std::result::Result, tonic::Status> { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static("/ddkrpc.DdkRpc/Sync"); let mut req = request.into_request(); - req.extensions_mut().insert(GrpcMethod::new("ddkrpc.DdkRpc", "Sync")); + req.extensions_mut() + .insert(GrpcMethod::new("ddkrpc.DdkRpc", "Sync")); self.inner.unary(req, path, codec).await } pub async fn get_wallet_transactions( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; + ) -> std::result::Result, tonic::Status> + { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/ddkrpc.DdkRpc/GetWalletTransactions", - ); + let path = http::uri::PathAndQuery::from_static("/ddkrpc.DdkRpc/GetWalletTransactions"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("ddkrpc.DdkRpc", "GetWalletTransactions")); @@ -562,115 +520,85 @@ pub mod ddk_rpc_client { pub async fn list_utxos( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; + ) -> std::result::Result, tonic::Status> { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static("/ddkrpc.DdkRpc/ListUtxos"); let mut req = request.into_request(); - req.extensions_mut().insert(GrpcMethod::new("ddkrpc.DdkRpc", "ListUtxos")); + req.extensions_mut() + .insert(GrpcMethod::new("ddkrpc.DdkRpc", "ListUtxos")); self.inner.unary(req, path, codec).await } pub async fn list_peers( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; + ) -> std::result::Result, tonic::Status> { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static("/ddkrpc.DdkRpc/ListPeers"); let mut req = request.into_request(); - req.extensions_mut().insert(GrpcMethod::new("ddkrpc.DdkRpc", "ListPeers")); + req.extensions_mut() + .insert(GrpcMethod::new("ddkrpc.DdkRpc", "ListPeers")); self.inner.unary(req, path, codec).await } pub async fn connect_peer( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; + ) -> std::result::Result, tonic::Status> { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/ddkrpc.DdkRpc/ConnectPeer", - ); + let path = http::uri::PathAndQuery::from_static("/ddkrpc.DdkRpc/ConnectPeer"); let mut req = request.into_request(); - req.extensions_mut().insert(GrpcMethod::new("ddkrpc.DdkRpc", "ConnectPeer")); + req.extensions_mut() + .insert(GrpcMethod::new("ddkrpc.DdkRpc", "ConnectPeer")); self.inner.unary(req, path, codec).await } pub async fn list_oracles( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; + ) -> std::result::Result, tonic::Status> + { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/ddkrpc.DdkRpc/ListOracles", - ); + let path = http::uri::PathAndQuery::from_static("/ddkrpc.DdkRpc/ListOracles"); let mut req = request.into_request(); - req.extensions_mut().insert(GrpcMethod::new("ddkrpc.DdkRpc", "ListOracles")); + req.extensions_mut() + .insert(GrpcMethod::new("ddkrpc.DdkRpc", "ListOracles")); self.inner.unary(req, path, codec).await } pub async fn list_contracts( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; + ) -> std::result::Result, tonic::Status> + { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/ddkrpc.DdkRpc/ListContracts", - ); + let path = http::uri::PathAndQuery::from_static("/ddkrpc.DdkRpc/ListContracts"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("ddkrpc.DdkRpc", "ListContracts")); @@ -680,41 +608,32 @@ pub mod ddk_rpc_client { &mut self, request: impl tonic::IntoRequest, ) -> std::result::Result, tonic::Status> { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static("/ddkrpc.DdkRpc/Send"); let mut req = request.into_request(); - req.extensions_mut().insert(GrpcMethod::new("ddkrpc.DdkRpc", "Send")); + req.extensions_mut() + .insert(GrpcMethod::new("ddkrpc.DdkRpc", "Send")); self.inner.unary(req, path, codec).await } pub async fn oracle_announcements( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; + ) -> std::result::Result, tonic::Status> + { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/ddkrpc.DdkRpc/OracleAnnouncements", - ); + let path = http::uri::PathAndQuery::from_static("/ddkrpc.DdkRpc/OracleAnnouncements"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("ddkrpc.DdkRpc", "OracleAnnouncements")); @@ -723,45 +642,34 @@ pub mod ddk_rpc_client { pub async fn create_enum( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; + ) -> std::result::Result, tonic::Status> + { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static("/ddkrpc.DdkRpc/CreateEnum"); let mut req = request.into_request(); - req.extensions_mut().insert(GrpcMethod::new("ddkrpc.DdkRpc", "CreateEnum")); + req.extensions_mut() + .insert(GrpcMethod::new("ddkrpc.DdkRpc", "CreateEnum")); self.inner.unary(req, path, codec).await } pub async fn create_numeric( &mut self, request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; + ) -> std::result::Result, tonic::Status> + { + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/ddkrpc.DdkRpc/CreateNumeric", - ); + let path = http::uri::PathAndQuery::from_static("/ddkrpc.DdkRpc/CreateNumeric"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("ddkrpc.DdkRpc", "CreateNumeric")); @@ -771,19 +679,14 @@ pub mod ddk_rpc_client { &mut self, request: impl tonic::IntoRequest, ) -> std::result::Result, tonic::Status> { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; + self.inner.ready().await.map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/ddkrpc.DdkRpc/SignAnnouncement", - ); + let path = http::uri::PathAndQuery::from_static("/ddkrpc.DdkRpc/SignAnnouncement"); let mut req = request.into_request(); req.extensions_mut() .insert(GrpcMethod::new("ddkrpc.DdkRpc", "SignAnnouncement")); @@ -805,45 +708,27 @@ pub mod ddk_rpc_server { async fn send_offer( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn accept_offer( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn list_offers( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn new_address( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn wallet_balance( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn wallet_sync( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn sync( &self, request: tonic::Request, @@ -851,24 +736,15 @@ pub mod ddk_rpc_server { async fn get_wallet_transactions( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn list_utxos( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn list_peers( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn connect_peer( &self, request: tonic::Request, @@ -876,17 +752,11 @@ pub mod ddk_rpc_server { async fn list_oracles( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn list_contracts( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn send( &self, request: tonic::Request, @@ -894,24 +764,15 @@ pub mod ddk_rpc_server { async fn oracle_announcements( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn create_enum( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn create_numeric( &self, request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; + ) -> std::result::Result, tonic::Status>; async fn sign_announcement( &self, request: tonic::Request, @@ -940,10 +801,7 @@ pub mod ddk_rpc_server { max_encoding_message_size: None, } } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> InterceptedService + pub fn with_interceptor(inner: T, interceptor: F) -> InterceptedService where F: tonic::service::Interceptor, { @@ -999,21 +857,15 @@ pub mod ddk_rpc_server { "/ddkrpc.DdkRpc/Info" => { #[allow(non_camel_case_types)] struct InfoSvc(pub Arc); - impl tonic::server::UnaryService - for InfoSvc { + impl tonic::server::UnaryService for InfoSvc { type Response = super::InfoResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::info(&inner, request).await - }; + let fut = async move { ::info(&inner, request).await }; Box::pin(fut) } } @@ -1043,21 +895,16 @@ pub mod ddk_rpc_server { "/ddkrpc.DdkRpc/SendOffer" => { #[allow(non_camel_case_types)] struct SendOfferSvc(pub Arc); - impl tonic::server::UnaryService - for SendOfferSvc { + impl tonic::server::UnaryService for SendOfferSvc { type Response = super::SendOfferResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::send_offer(&inner, request).await - }; + let fut = + async move { ::send_offer(&inner, request).await }; Box::pin(fut) } } @@ -1087,23 +934,16 @@ pub mod ddk_rpc_server { "/ddkrpc.DdkRpc/AcceptOffer" => { #[allow(non_camel_case_types)] struct AcceptOfferSvc(pub Arc); - impl< - T: DdkRpc, - > tonic::server::UnaryService - for AcceptOfferSvc { + impl tonic::server::UnaryService for AcceptOfferSvc { type Response = super::AcceptOfferResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::accept_offer(&inner, request).await - }; + let fut = + async move { ::accept_offer(&inner, request).await }; Box::pin(fut) } } @@ -1133,21 +973,16 @@ pub mod ddk_rpc_server { "/ddkrpc.DdkRpc/ListOffers" => { #[allow(non_camel_case_types)] struct ListOffersSvc(pub Arc); - impl tonic::server::UnaryService - for ListOffersSvc { + impl tonic::server::UnaryService for ListOffersSvc { type Response = super::ListOffersResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::list_offers(&inner, request).await - }; + let fut = + async move { ::list_offers(&inner, request).await }; Box::pin(fut) } } @@ -1177,21 +1012,16 @@ pub mod ddk_rpc_server { "/ddkrpc.DdkRpc/NewAddress" => { #[allow(non_camel_case_types)] struct NewAddressSvc(pub Arc); - impl tonic::server::UnaryService - for NewAddressSvc { + impl tonic::server::UnaryService for NewAddressSvc { type Response = super::NewAddressResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::new_address(&inner, request).await - }; + let fut = + async move { ::new_address(&inner, request).await }; Box::pin(fut) } } @@ -1221,23 +1051,16 @@ pub mod ddk_rpc_server { "/ddkrpc.DdkRpc/WalletBalance" => { #[allow(non_camel_case_types)] struct WalletBalanceSvc(pub Arc); - impl< - T: DdkRpc, - > tonic::server::UnaryService - for WalletBalanceSvc { + impl tonic::server::UnaryService for WalletBalanceSvc { type Response = super::WalletBalanceResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::wallet_balance(&inner, request).await - }; + let fut = + async move { ::wallet_balance(&inner, request).await }; Box::pin(fut) } } @@ -1267,21 +1090,16 @@ pub mod ddk_rpc_server { "/ddkrpc.DdkRpc/WalletSync" => { #[allow(non_camel_case_types)] struct WalletSyncSvc(pub Arc); - impl tonic::server::UnaryService - for WalletSyncSvc { + impl tonic::server::UnaryService for WalletSyncSvc { type Response = super::WalletSyncResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::wallet_sync(&inner, request).await - }; + let fut = + async move { ::wallet_sync(&inner, request).await }; Box::pin(fut) } } @@ -1311,21 +1129,15 @@ pub mod ddk_rpc_server { "/ddkrpc.DdkRpc/Sync" => { #[allow(non_camel_case_types)] struct SyncSvc(pub Arc); - impl tonic::server::UnaryService - for SyncSvc { + impl tonic::server::UnaryService for SyncSvc { type Response = super::SyncResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::sync(&inner, request).await - }; + let fut = async move { ::sync(&inner, request).await }; Box::pin(fut) } } @@ -1355,23 +1167,18 @@ pub mod ddk_rpc_server { "/ddkrpc.DdkRpc/GetWalletTransactions" => { #[allow(non_camel_case_types)] struct GetWalletTransactionsSvc(pub Arc); - impl< - T: DdkRpc, - > tonic::server::UnaryService - for GetWalletTransactionsSvc { + impl tonic::server::UnaryService + for GetWalletTransactionsSvc + { type Response = super::GetWalletTransactionsResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); let fut = async move { - ::get_wallet_transactions(&inner, request) - .await + ::get_wallet_transactions(&inner, request).await }; Box::pin(fut) } @@ -1402,21 +1209,16 @@ pub mod ddk_rpc_server { "/ddkrpc.DdkRpc/ListUtxos" => { #[allow(non_camel_case_types)] struct ListUtxosSvc(pub Arc); - impl tonic::server::UnaryService - for ListUtxosSvc { + impl tonic::server::UnaryService for ListUtxosSvc { type Response = super::ListUtxosResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::list_utxos(&inner, request).await - }; + let fut = + async move { ::list_utxos(&inner, request).await }; Box::pin(fut) } } @@ -1446,21 +1248,16 @@ pub mod ddk_rpc_server { "/ddkrpc.DdkRpc/ListPeers" => { #[allow(non_camel_case_types)] struct ListPeersSvc(pub Arc); - impl tonic::server::UnaryService - for ListPeersSvc { + impl tonic::server::UnaryService for ListPeersSvc { type Response = super::ListPeersResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::list_peers(&inner, request).await - }; + let fut = + async move { ::list_peers(&inner, request).await }; Box::pin(fut) } } @@ -1490,21 +1287,16 @@ pub mod ddk_rpc_server { "/ddkrpc.DdkRpc/ConnectPeer" => { #[allow(non_camel_case_types)] struct ConnectPeerSvc(pub Arc); - impl tonic::server::UnaryService - for ConnectPeerSvc { + impl tonic::server::UnaryService for ConnectPeerSvc { type Response = super::ConnectResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::connect_peer(&inner, request).await - }; + let fut = + async move { ::connect_peer(&inner, request).await }; Box::pin(fut) } } @@ -1534,23 +1326,16 @@ pub mod ddk_rpc_server { "/ddkrpc.DdkRpc/ListOracles" => { #[allow(non_camel_case_types)] struct ListOraclesSvc(pub Arc); - impl< - T: DdkRpc, - > tonic::server::UnaryService - for ListOraclesSvc { + impl tonic::server::UnaryService for ListOraclesSvc { type Response = super::ListOraclesResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::list_oracles(&inner, request).await - }; + let fut = + async move { ::list_oracles(&inner, request).await }; Box::pin(fut) } } @@ -1580,23 +1365,16 @@ pub mod ddk_rpc_server { "/ddkrpc.DdkRpc/ListContracts" => { #[allow(non_camel_case_types)] struct ListContractsSvc(pub Arc); - impl< - T: DdkRpc, - > tonic::server::UnaryService - for ListContractsSvc { + impl tonic::server::UnaryService for ListContractsSvc { type Response = super::ListContractsResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::list_contracts(&inner, request).await - }; + let fut = + async move { ::list_contracts(&inner, request).await }; Box::pin(fut) } } @@ -1626,21 +1404,15 @@ pub mod ddk_rpc_server { "/ddkrpc.DdkRpc/Send" => { #[allow(non_camel_case_types)] struct SendSvc(pub Arc); - impl tonic::server::UnaryService - for SendSvc { + impl tonic::server::UnaryService for SendSvc { type Response = super::SendResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::send(&inner, request).await - }; + let fut = async move { ::send(&inner, request).await }; Box::pin(fut) } } @@ -1670,15 +1442,11 @@ pub mod ddk_rpc_server { "/ddkrpc.DdkRpc/OracleAnnouncements" => { #[allow(non_camel_case_types)] struct OracleAnnouncementsSvc(pub Arc); - impl< - T: DdkRpc, - > tonic::server::UnaryService - for OracleAnnouncementsSvc { + impl tonic::server::UnaryService + for OracleAnnouncementsSvc + { type Response = super::OracleAnnouncementsResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, @@ -1716,21 +1484,16 @@ pub mod ddk_rpc_server { "/ddkrpc.DdkRpc/CreateEnum" => { #[allow(non_camel_case_types)] struct CreateEnumSvc(pub Arc); - impl tonic::server::UnaryService - for CreateEnumSvc { + impl tonic::server::UnaryService for CreateEnumSvc { type Response = super::CreateEnumResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::create_enum(&inner, request).await - }; + let fut = + async move { ::create_enum(&inner, request).await }; Box::pin(fut) } } @@ -1760,23 +1523,16 @@ pub mod ddk_rpc_server { "/ddkrpc.DdkRpc/CreateNumeric" => { #[allow(non_camel_case_types)] struct CreateNumericSvc(pub Arc); - impl< - T: DdkRpc, - > tonic::server::UnaryService - for CreateNumericSvc { + impl tonic::server::UnaryService for CreateNumericSvc { type Response = super::CreateNumericResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); - let fut = async move { - ::create_numeric(&inner, request).await - }; + let fut = + async move { ::create_numeric(&inner, request).await }; Box::pin(fut) } } @@ -1806,13 +1562,9 @@ pub mod ddk_rpc_server { "/ddkrpc.DdkRpc/SignAnnouncement" => { #[allow(non_camel_case_types)] struct SignAnnouncementSvc(pub Arc); - impl tonic::server::UnaryService - for SignAnnouncementSvc { + impl tonic::server::UnaryService for SignAnnouncementSvc { type Response = super::SignResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; + type Future = BoxFuture, tonic::Status>; fn call( &mut self, request: tonic::Request, @@ -1847,18 +1599,14 @@ pub mod ddk_rpc_server { }; Box::pin(fut) } - _ => { - Box::pin(async move { - Ok( - http::Response::builder() - .status(200) - .header("grpc-status", "12") - .header("content-type", "application/grpc") - .body(empty_body()) - .unwrap(), - ) - }) - } + _ => Box::pin(async move { + Ok(http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap()) + }), } } } diff --git a/ddk-node/src/lib.rs b/ddk-node/src/lib.rs index 44d2563..7d993e9 100644 --- a/ddk-node/src/lib.rs +++ b/ddk-node/src/lib.rs @@ -143,8 +143,24 @@ impl DdkRpc for DdkNode { contract_input, counter_party, } = request.into_inner(); + let contract_input: ContractInput = - serde_json::from_slice(&contract_input).expect("couldn't get bytes correct"); + serde_json::from_slice(&contract_input).map_err(|e| { + Status::new( + Code::InvalidArgument, + format!("Invalid contract input: {}", e), + ) + })?; + + if contract_input.fee_rate > 1000 { + return Err(Status::new( + Code::InvalidArgument, + format!( + "Fee rate too high: {} sat/vbyte (max: 1000)", + contract_input.fee_rate + ), + )); + } let mut oracle_announcements = Vec::new(); for info in &contract_input.contract_infos { let announcement = self @@ -156,7 +172,12 @@ impl DdkRpc for DdkNode { oracle_announcements.push(announcement) } - let counter_party = PublicKey::from_str(&counter_party).expect("no public key"); + let counter_party = PublicKey::from_str(&counter_party).map_err(|e| { + Status::new( + Code::InvalidArgument, + format!("Invalid counter party pubkey: {}", e), + ) + })?; let offer_msg = self .node .send_dlc_offer(&contract_input, counter_party, oracle_announcements) @@ -179,8 +200,23 @@ impl DdkRpc for DdkNode { request: Request, ) -> Result, Status> { tracing::info!("Request to accept offer."); + let contract_id_hex = request.into_inner().contract_id; + let contract_id_bytes = hex::decode(&contract_id_hex).map_err(|e| { + Status::new( + Code::InvalidArgument, + format!("Invalid contract ID hex: {}", e), + ) + })?; + if contract_id_bytes.len() != 32 { + return Err(Status::new( + Code::InvalidArgument, + format!( + "Contract ID must be 32 bytes, got {}", + contract_id_bytes.len() + ), + )); + } let mut contract_id = [0u8; 32]; - let contract_id_bytes = hex::decode(&request.into_inner().contract_id).unwrap(); contract_id.copy_from_slice(&contract_id_bytes); let (contract_id, counter_party, accept_dlc) = self.node.accept_dlc_offer(contract_id).await.map_err(|e| { @@ -297,7 +333,16 @@ impl DdkRpc for DdkNode { request: Request, ) -> Result, Status> { let ConnectRequest { pubkey, host } = request.into_inner(); - let pubkey = PublicKey::from_str(&pubkey).unwrap(); + + if host.len() > 255 { + return Err(Status::new( + Code::InvalidArgument, + "Host string too long (max: 255 characters)", + )); + } + + let pubkey = PublicKey::from_str(&pubkey) + .map_err(|e| Status::new(Code::InvalidArgument, format!("Invalid pubkey: {}", e)))?; self.node.transport.connect_outbound(pubkey, &host).await; Ok(Response::new(ConnectResponse {})) } @@ -336,7 +381,23 @@ impl DdkRpc for DdkNode { amount, fee_rate, } = request.into_inner(); - let address = Address::from_str(&address).unwrap().assume_checked(); + + if fee_rate > 1000 { + return Err(Status::new( + Code::InvalidArgument, + format!("Fee rate too high: {} sat/vbyte (max: 1000)", fee_rate), + )); + } + + let address = Address::from_str(&address) + .map_err(|e| Status::new(Code::InvalidArgument, format!("Invalid address: {}", e)))? + .require_network(self.node.network()) + .map_err(|e| { + Status::new( + Code::InvalidArgument, + format!("Address network mismatch: {}", e), + ) + })?; let amount = Amount::from_sat(amount); let fee_rate = match FeeRate::from_sat_per_vb(fee_rate) { Some(f) => f,