};
use crate::handlers::{AppContext, AsyncAppCommand, Init, OfflineOperations};
use crate::utils::types::{PsbtResult, StatusResult};
-use crate::utils::{parse_dns_recipient, parse_fee_rate, parse_outpoint, parse_recipient};
+use crate::utils::{
+ parse_dns_recipient, parse_fee_rate, parse_op_return_base64, parse_op_return_string,
+ parse_outpoint, parse_recipient,
+};
use bdk_wallet::KeychainKind;
-use bdk_wallet::bitcoin::base64::Engine;
-use bdk_wallet::bitcoin::base64::prelude::BASE64_STANDARD;
-use bdk_wallet::bitcoin::script::PushBytesBuf;
-use bdk_wallet::bitcoin::{Amount, FeeRate, OutPoint, ScriptBuf, Sequence};
+use bdk_wallet::bitcoin::{Amount, FeeRate, OutPoint, ScriptBuf, Sequence, script::PushBytesBuf};
use clap::Parser;
use std::collections::BTreeMap;
env = "ADD_STRING",
long = "add_string",
short = 's',
- conflicts_with = "add_data"
+ conflicts_with = "add_data",
+ value_parser = parse_op_return_string,
)]
- pub add_string: Option<String>,
+ pub add_string: Option<PushBytesBuf>,
#[arg(
env = "ADD_DATA",
long = "add_data",
short = 'o',
- conflicts_with = "add_string"
+ conflicts_with = "add_string",
+ value_parser = parse_op_return_base64,
)]
- pub add_data: Option<String>,
+ pub add_data: Option<PushBytesBuf>,
}
impl AsyncAppCommand<AppContext<OfflineOperations<'_>>> for CreateDnsTxCommand {
if let Some(unspendable) = &self.unspendable {
tx_builder.unspendable(unspendable.to_vec());
}
- if let Some(base64_data) = &self.add_data {
- let op_return_data = BASE64_STANDARD.decode(base64_data)?;
- tx_builder.add_data(&PushBytesBuf::try_from(op_return_data)?);
- } else if let Some(string_data) = &self.add_string {
- let data = PushBytesBuf::try_from(string_data.as_bytes().to_vec())?;
- tx_builder.add_data(&data);
+ if let Some(data) = self.add_data.as_ref().or(self.add_string.as_ref()) {
+ tx_builder.add_data(data);
}
let policies = vec![
use crate::error::BDKCliError as Error;
use crate::handlers::{AppCommand, AppContext, OfflineOperations};
use crate::utils::output::{FormatOutput, ListResult};
-use crate::utils::parse_address;
use crate::utils::types::{
AddressResult, BalanceResult, KeychainPair, PsbtResult, RawPsbt, TransactionDetails,
UnspentDetails,
};
-use crate::utils::{parse_fee_rate, parse_outpoint, parse_recipient};
+use crate::utils::{
+ parse_address, parse_fee_rate, parse_op_return_base64, parse_op_return_string, parse_outpoint,
+ parse_recipient,
+};
use bdk_wallet::bitcoin::base64::Engine;
use bdk_wallet::bitcoin::base64::prelude::BASE64_STANDARD;
use bdk_wallet::bitcoin::script::PushBytesBuf;
env = "ADD_STRING",
long = "add_string",
short = 's',
- conflicts_with = "add_data"
+ conflicts_with = "add_data",
+ value_parser = parse_op_return_string,
)]
- pub add_string: Option<String>,
+ pub add_string: Option<PushBytesBuf>,
/// Optionally create an OP_RETURN output containing given base64 encoded String. (max 80 bytes)
#[arg(
env = "ADD_DATA",
long = "add_data",
short = 'o',
- conflicts_with = "add_string"
+ conflicts_with = "add_string",
+ value_parser = parse_op_return_base64,
)]
- pub add_data: Option<String>,
+ pub add_data: Option<PushBytesBuf>,
}
impl AppCommand<AppContext<OfflineOperations<'_>>> for CreateTxCommand {
tx_builder.unspendable(unspendable.to_vec());
}
- if let Some(base64_data) = &self.add_data {
- let op_return_data = BASE64_STANDARD.decode(base64_data)?;
- tx_builder.add_data(&PushBytesBuf::try_from(op_return_data)?);
- } else if let Some(string_data) = &self.add_string {
- let data = PushBytesBuf::try_from(string_data.as_bytes().to_vec())?;
- tx_builder.add_data(&data);
+ if let Some(data) = self.add_data.as_ref().or(self.add_string.as_ref()) {
+ tx_builder.add_data(data);
}
let policies = vec![
env = "ADD_STRING",
long = "add_string",
short = 's',
- conflicts_with = "add_data"
+ conflicts_with = "add_data",
+ value_parser = parse_op_return_string,
)]
- pub add_string: Option<String>,
+ pub add_string: Option<PushBytesBuf>,
/// Optionally create an OP_RETURN output containing given base64 encoded String. (max 80 bytes)
#[arg(
env = "ADD_DATA",
long = "add_data",
short = 'o',
- conflicts_with = "add_string"
+ conflicts_with = "add_string",
+ value_parser = parse_op_return_base64,
)]
- pub add_data: Option<String>, //base 64 econding
+ pub add_data: Option<PushBytesBuf>,
}
#[cfg(feature = "silent-payments")]
tx_builder.unspendable(unspendable.to_vec());
}
- if let Some(base64_data) = &self.add_data {
- let op_return_data = BASE64_STANDARD.decode(base64_data)?;
- tx_builder.add_data(&PushBytesBuf::try_from(op_return_data)?);
- } else if let Some(string_data) = &self.add_string {
- let data = PushBytesBuf::try_from(string_data.as_bytes().to_vec())?;
- tx_builder.add_data(&data);
+ if let Some(data) = self.add_data.as_ref().or(self.add_string.as_ref()) {
+ tx_builder.add_data(data);
}
let policies = vec![
#[cfg(feature = "silent-payments")]
use bdk_sp::encoding::SilentPaymentCode;
use bdk_wallet::bitcoin::{Address, FeeRate, Network, OutPoint, ScriptBuf};
+use bdk_wallet::bitcoin::{
+ base64::{Engine, prelude::BASE64_STANDARD},
+ script::PushBytesBuf,
+};
#[cfg(any(
feature = "electrum",
feature = "esplora",
Ok(FeeRate::from_sat_per_kwu(sat_kwu as u64))
}
+/// Parse an `OP_RETURN` payload given as a UTF-8 string.
+pub(crate) fn parse_op_return_string(s: &str) -> Result<PushBytesBuf, Error> {
+ Ok(PushBytesBuf::try_from(s.as_bytes().to_vec())?)
+}
+
+/// Parse an `OP_RETURN` payload given as base64.
+pub(crate) fn parse_op_return_base64(s: &str) -> Result<PushBytesBuf, Error> {
+ Ok(PushBytesBuf::try_from(BASE64_STANDARD.decode(s)?)?)
+}
+
/// Prepare bdk-cli home directory
///
/// This function is called to check if [`crate::CliOpts`] datadir is set.
);
}
+ #[test]
+ fn parses_op_return_payloads() {
+ assert_eq!(parse_op_return_string("hi").unwrap().as_bytes(), b"hi");
+ assert_eq!(parse_op_return_base64("aGk=").unwrap().as_bytes(), b"hi");
+ assert!(parse_op_return_base64("!!!not-base64!!!").is_err());
+ }
+
#[test]
fn rejects_fee_rates_that_cannot_be_honoured() {
for input in ["0", "-5", "NaN", "inf", "1e30", "abc", ""] {
}
/// A malformed `create_tx` argument must be reported as an error (exit 1),
- /// never as a panic (exit 101).
+ /// never as a panic (exit 101). an unknown outpoint when building (exit 1), and
+ /// malformed base64 already at argument parsing (exit 2).
#[test]
fn test_create_tx_rejects_malformed_input_without_panicking() {
let (cli, mut cmd_init) = setup_wallet_config();
"!!!not-base64!!!",
])
.assert()
- .code(1)
+ .code(2)
.stderr(predicate::str::contains("Base64 decoding error"));
}