diff --git a/Cargo.lock b/Cargo.lock index 433d1d91..d5d1c5c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -855,6 +855,7 @@ dependencies = [ "async-trait", "base64", "beacon", + "bs58", "clap", "config", "exponential-backoff", diff --git a/Cargo.toml b/Cargo.toml index 048ef068..231d2f58 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ clap = { version = "4", default-features = false, features = [ "std", "error-context", ] } +bs58 = "0" semver = "0" config = { version = "0", default-features = false, features = ["toml"] } serde = { workspace = true } diff --git a/src/api/server.rs b/src/api/server.rs index 25fba212..ae43a5d4 100644 --- a/src/api/server.rs +++ b/src/api/server.rs @@ -1,10 +1,7 @@ use super::{ AddGatewayReq, AddGatewayRes, PubkeyReq, PubkeyRes, RegionReq, RegionRes, RouterReq, RouterRes, }; -use crate::{ - packet_router, region_watcher, settings::StakingMode, Error, Keypair, PublicKey, Result, - Settings, TxnFee, TxnFeeConfig, -}; +use crate::{packet_router, region_watcher, Error, Keypair, PublicKey, Result, Settings}; use futures::TryFutureExt; use helium_crypto::Sign; use helium_proto::services::local::{Api, Server}; @@ -90,23 +87,13 @@ impl Api for LocalServer { let _ = PublicKey::from_bytes(&request.payer) .map_err(|_err| Status::invalid_argument("Invalid payer address"))?; - let mode = StakingMode::from(request.staking_mode()); - let fee_config = TxnFeeConfig::default(); let mut txn = BlockchainTxnAddGatewayV1 { gateway: self.keypair.public_key().to_vec(), owner: request.owner.clone(), payer: request.payer, - fee: 0, - staking_fee: fee_config.get_staking_fee(&mode), - owner_signature: vec![], - gateway_signature: vec![], - payer_signature: vec![], + ..Default::default() }; - txn.fee = txn - .txn_fee(&fee_config) - .map_err(|_err| Status::internal("Failed to get txn fees"))?; - let signature = self .keypair .sign(&txn.encode_to_vec()) diff --git a/src/traits/base64.rs b/src/base64.rs similarity index 100% rename from src/traits/base64.rs rename to src/base64.rs diff --git a/src/cmd/add.rs b/src/cmd/add.rs index ce9b479a..cc3959c7 100644 --- a/src/cmd/add.rs +++ b/src/cmd/add.rs @@ -5,12 +5,13 @@ use serde_json::json; /// Construct an add gateway transaction for this gateway. #[derive(Debug, clap::Args)] pub struct Cmd { - /// The target owner account of this gateway - #[arg(long)] + /// The solana address of the target owner for this gateway + #[arg(long, value_parser = parse_pubkey)] owner: PublicKey, - /// The account that will pay account for this addition - #[arg(long)] + /// The solana address of the payer account that will pay account for this + /// addition + #[arg(long, value_parser = parse_pubkey)] payer: PublicKey, /// The staking mode for adding the gateway @@ -33,13 +34,30 @@ fn print_txn(mode: &StakingMode, txn: BlockchainTxnAddGatewayV1) -> Result { let table = json!({ "mode": mode.to_string(), "address": PublicKey::from_bytes(&txn.gateway)?.to_string(), - "payer": PublicKey::from_bytes(&txn.payer)?.to_string(), - "owner": PublicKey::from_bytes(&txn.owner)?.to_string(), - "fee": txn.fee, - "staking fee": txn.staking_fee, + "payer": PublicKey::from_bytes(&txn.payer).and_then(solana_pubkey)?, + "owner": PublicKey::from_bytes(&txn.owner).and_then(solana_pubkey)?, "txn": BlockchainTxn { txn: Some(Txn::AddGateway(txn)) }.encode_to_vec().to_b64() }); print_json(&table) } + +fn parse_pubkey(str: &str) -> Result { + use helium_crypto::{ed25519, ReadFrom}; + use std::{io::Cursor, str::FromStr}; + + match PublicKey::from_str(str) { + Ok(pk) => Ok(pk), + Err(_) => { + let bytes = bs58::decode(str).into_vec()?; + let public_key = ed25519::PublicKey::read_from(&mut Cursor::new(bytes))?; + Ok(public_key.into()) + } + } +} + +fn solana_pubkey(key: PublicKey) -> std::result::Result { + let bytes = &key.to_vec()[1..]; + Ok(bs58::encode(bytes).into_string()) +} diff --git a/src/error.rs b/src/error.rs index 545f05cd..55671b08 100644 --- a/src/error.rs +++ b/src/error.rs @@ -45,6 +45,8 @@ pub enum DecodeError { KeypairUri(String), #[error("json decode: {0}")] Json(#[from] serde_json::Error), + #[error("base58 decode: {0}")] + Base58(#[from] bs58::decode::Error), #[error("base64 decode: {0}")] Base64(#[from] base64::DecodeError), #[error("network address decode: {0}")] @@ -115,6 +117,7 @@ from_err!(EncodeError, prost::EncodeError); // Decode Errors from_err!(DecodeError, http::uri::InvalidUri); from_err!(DecodeError, base64::DecodeError); +from_err!(DecodeError, bs58::decode::Error); from_err!(DecodeError, serde_json::Error); from_err!(DecodeError, net::AddrParseError); from_err!(DecodeError, prost::DecodeError); diff --git a/src/lib.rs b/src/lib.rs index a9038995..024b514d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,15 +15,15 @@ pub mod settings; pub mod sync; mod api; -mod traits; +mod base64; +pub(crate) use base64::*; pub use beacon::{Region, RegionParams}; pub use error::{Error, Result}; pub use keyed_uri::KeyedUri; pub use keypair::{Keypair, PublicKey}; pub use packet::{PacketDown, PacketUp}; pub use settings::Settings; -pub(crate) use traits::*; use futures::{Future as StdFuture, Stream as StdStream}; use std::pin::Pin; diff --git a/src/traits/mod.rs b/src/traits/mod.rs deleted file mode 100644 index a71ec9a1..00000000 --- a/src/traits/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod base64; -mod txn_fee; - -pub(crate) use self::base64::Base64; -pub(crate) use txn_fee::{TxnFee, TxnFeeConfig}; diff --git a/src/traits/txn_fee.rs b/src/traits/txn_fee.rs deleted file mode 100644 index fac6aefd..00000000 --- a/src/traits/txn_fee.rs +++ /dev/null @@ -1,104 +0,0 @@ -use crate::{settings::StakingMode, Result}; -use helium_proto::{BlockchainTxn, BlockchainTxnAddGatewayV1, Message, Txn}; -use serde::Deserialize; - -pub trait TxnFee { - fn txn_fee(&self, config: &TxnFeeConfig) -> Result; -} - -const TXN_FEE_SIGNATURE_SIZE: usize = 64; -const TXN_FEE_MULTIPLIER: u64 = 5000; - -macro_rules! payer_sig_clear { - (basic, $txn:ident) => {}; - (payer, $txn:ident) => { - if $txn.payer.is_empty() { - $txn.payer_signature = vec![] - } else { - $txn.payer_signature = vec![0; TXN_FEE_SIGNATURE_SIZE] - }; - }; -} - -macro_rules! impl_txn_fee { - (($kind:ident, $txn_type:ty, $txn_env:expr), $( $sig:ident ),+ ) => { - impl TxnFee for $txn_type { - fn txn_fee(&self, config: &TxnFeeConfig) -> Result { - let mut txn: $txn_type = self.clone(); - txn.fee = 0; - $(txn.$sig = vec![0; TXN_FEE_SIGNATURE_SIZE];)+ - payer_sig_clear!($kind, txn); - let buf = BlockchainTxn { - txn: Some($txn_env(txn)) - }.encode_to_vec(); - Ok(config.get_txn_fee(buf.len())) - } - } - }; - ($txn_type:ty, $($tail:tt)*) => { - impl_txn_fee!((basic, $txn_type), $($tail)*); - } -} - -impl_txn_fee!( - (payer, BlockchainTxnAddGatewayV1, Txn::AddGateway), - owner_signature, - gateway_signature -); - -// TODO: Transaction fees are hard coded in the default implementation, -// specifically whether txn fees are enabled and what the dc multiplier is -// supposed to be. -#[derive(Clone, Deserialize, Debug)] -pub struct TxnFeeConfig { - // whether transaction fees are active - txn_fees: bool, - // a multiplier which will be applied to the txn fee of all txns, in order - // to make their DC costs meaningful - txn_fee_multiplier: u64, - // the staking fee in DC for adding a gateway - #[serde(default = "TxnFeeConfig::default_full_staking_fee")] - staking_fee_txn_add_gateway_v1: u64, - // the staking fee in DC for adding a data only gateway - #[serde(default = "TxnFeeConfig::default_dataonly_staking_fee")] - staking_fee_txn_add_dataonly_gateway_v1: u64, -} - -impl Default for TxnFeeConfig { - fn default() -> Self { - Self { - txn_fees: true, - txn_fee_multiplier: TXN_FEE_MULTIPLIER, - staking_fee_txn_add_gateway_v1: Self::default_full_staking_fee(), - staking_fee_txn_add_dataonly_gateway_v1: Self::default_dataonly_staking_fee(), - } - } -} - -impl TxnFeeConfig { - fn default_full_staking_fee() -> u64 { - 4000000 - } - - fn default_dataonly_staking_fee() -> u64 { - 1000000 - } - - pub fn get_staking_fee(&self, staking_mode: &StakingMode) -> u64 { - match staking_mode { - StakingMode::Full => self.staking_fee_txn_add_gateway_v1, - StakingMode::DataOnly => self.staking_fee_txn_add_dataonly_gateway_v1, - } - } - - pub fn get_txn_fee(&self, payload_size: usize) -> u64 { - let dc_payload_size = if self.txn_fees { 24 } else { 1 }; - let fee = if payload_size <= dc_payload_size { - 1 - } else { - // integer div/ceil from: https://stackoverflow.com/a/2745086 - ((payload_size + dc_payload_size - 1) / dc_payload_size) as u64 - }; - fee * self.txn_fee_multiplier - } -}