Skip to content

Implement methods in raw transactions section #117

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion client/src/client_sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ pub mod v26;
pub mod v27;
pub mod v28;

use std::collections::HashMap;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::PathBuf;

use bitcoin::Txid;
use bitcoin::{Address, Amount, Txid};
use serde::{Deserialize, Serialize};

pub use crate::client_sync::error::Error;
Expand Down Expand Up @@ -235,6 +236,23 @@ pub struct Input {
pub sequence: Option<bitcoin::Sequence>,
}

/// Output used as parameter to `create_raw_transaction`.
// Abuse `HashMap` so we can derive serialize to get the correct JSON object.
#[derive(Debug, Serialize)]
pub struct Output(
/// Map of address to value. Always only has a single item in it.
HashMap<String, f64>,
);

impl Output {
/// Creates a single output that serializes as Core expects.
pub fn new(addr: Address, value: Amount) -> Self {
let mut map = HashMap::new();
map.insert(addr.to_string(), value.to_btc());
Output(map)
}
}

/// An element in the `inputs` argument of method `walletcreatefundedpsbt`.
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct WalletCreateFundedPsbtInput {
Expand Down
12 changes: 12 additions & 0 deletions client/src/client_sync/v17/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,21 @@ crate::impl_client_v17__getnetworkinfo!();
crate::impl_client_v17__getpeerinfo!();

// == Rawtransactions ==
crate::impl_client_v17__combinepsbt!();
crate::impl_client_v17__combinerawtransaction!();
crate::impl_client_v17__converttopsbt!();
crate::impl_client_v17__createpsbt!();
crate::impl_client_v17__createrawtransaction!();
crate::impl_client_v17__decodepsbt!();
crate::impl_client_v17__decoderawtransaction!();
crate::impl_client_v17__decodescript!();
crate::impl_client_v17__finalizepsbt!();
crate::impl_client_v17__fundrawtransaction!();
crate::impl_client_v17__getrawtransaction!();
crate::impl_client_v17__sendrawtransaction!();
crate::impl_client_v17__signrawtransaction!();
crate::impl_client_v17__signrawtransactionwithkey!();
crate::impl_client_v17__testmempoolaccept!();

// == Wallet ==
crate::impl_client_v17__addmultisigaddress!();
Expand Down
194 changes: 191 additions & 3 deletions client/src/client_sync/v17/raw_transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,67 @@
//!
//! See or use the `define_jsonrpc_minreq_client!` macro to define a `Client`.

/// Implements Bitcoin Core JSON-RPC API method `combinepsbt`
#[macro_export]
macro_rules! impl_client_v17__combinepsbt {
() => {
impl Client {
pub fn combine_psbt(&self, txs: &[bitcoin::Psbt]) -> Result<CombinePsbt> {
let txs = txs.iter().map(|psbt| format!("{}", psbt)).collect::<Vec<String>>();
self.call("combinepsbt", &[txs.into()])
}
}
};
}

/// Implements Bitcoin Core JSON-RPC API method `combinerawtransaction`
#[macro_export]
macro_rules! impl_client_v17__combinerawtransaction {
() => {
impl Client {
pub fn combine_raw_transaction(
&self,
txs: &[bitcoin::Transaction],
) -> Result<CombineRawTransaction> {
let encoded = txs
.iter()
.map(|tx| bitcoin::consensus::encode::serialize_hex(tx))
.collect::<Vec<String>>();
self.call("combinerawtransaction", &[into_json(encoded)?])
}
}
};
}

/// Implements Bitcoin Core JSON-RPC API method `converttopsbt`
#[macro_export]
macro_rules! impl_client_v17__converttopsbt {
() => {
impl Client {
pub fn convert_to_psbt(&self, tx: &bitcoin::Transaction) -> Result<ConvertToPsbt> {
let hex = bitcoin::consensus::encode::serialize_hex(tx);
self.call("converttopsbt", &[hex.into()])
}
}
};
}

/// Implements Bitcoin Core JSON-RPC API method `createpsbt`
#[macro_export]
macro_rules! impl_client_v17__createpsbt {
() => {
impl Client {
pub fn create_psbt(
&self,
inputs: &[$crate::client_sync::Input],
outputs: &[$crate::client_sync::Output],
) -> Result<CreatePsbt> {
self.call("createpsbt", &[into_json(inputs)?, into_json(outputs)?])
}
}
};
}

/// Implements Bitcoin Core JSON-RPC API method `createrawtransaction`
#[macro_export]
macro_rules! impl_client_v17__createrawtransaction {
Expand All @@ -17,24 +78,98 @@ macro_rules! impl_client_v17__createrawtransaction {
pub fn create_raw_transaction(
&self,
inputs: &[$crate::client_sync::Input],
outputs: &std::collections::BTreeMap<String, f64>, // Map of address to amount.
outputs: &[$crate::client_sync::Output],
) -> Result<CreateRawTransaction> {
self.call("createrawtransaction", &[into_json(inputs)?, into_json(outputs)?])
}
}
};
}

/// Implements Bitcoin Core JSON-RPC API method `decodepsbt`
#[macro_export]
macro_rules! impl_client_v17__decodepsbt {
() => {
impl Client {
pub fn decode_psbt(&self, psbt: &str) -> Result<DecodePsbt> {
self.call("decodepsbt", &[psbt.into()])
}
}
};
}

/// Implements Bitcoin Core JSON-RPC API method `finalizepsbt`
#[macro_export]
macro_rules! impl_client_v17__finalizepsbt {
() => {
impl Client {
pub fn finalize_psbt(&self, psbt: &bitcoin::Psbt) -> Result<FinalizePsbt> {
let psbt = format!("{}", psbt);
self.call("finalizepsbt", &[psbt.into()])
}
}
};
}

/// Implements Bitcoin Core JSON-RPC API method `decoderawtransaction`
#[macro_export]
macro_rules! impl_client_v17__decoderawtransaction {
() => {
impl Client {
pub fn decode_raw_transaction(
&self,
tx: &bitcoin::Transaction,
) -> Result<DecodeRawTransaction> {
let hex = bitcoin::consensus::encode::serialize_hex(tx);
self.call("decoderawtransaction", &[hex.into()])
}
}
};
}

/// Implements Bitcoin Core JSON-RPC API method `decodescript`
#[macro_export]
macro_rules! impl_client_v17__decodescript {
() => {
impl Client {
// Arg is the hex encoded script we want to decode.
pub fn decode_script(&self, script: &str) -> Result<DecodeScript> {
self.call("decodescript", &[script.into()])
}
}
};
}

/// Implements Bitcoin Core JSON-RPC API method `fundrawtransaction`
#[macro_export]
macro_rules! impl_client_v17__fundrawtransaction {
() => {
impl Client {
pub fn fund_raw_transaction(
&self,
hex: &str, // Hex encoded transaction.
tx: &bitcoin::Transaction,
) -> Result<FundRawTransaction> {
self.call("fundrawtransaction", &[into_json(hex)?])
let hex = bitcoin::consensus::encode::serialize_hex(tx);
self.call("fundrawtransaction", &[hex.into()])
}
}
};
}

/// Implements Bitcoin Core JSON-RPC API method `getrawtransaction`
#[macro_export]
macro_rules! impl_client_v17__getrawtransaction {
() => {
impl Client {
pub fn get_raw_transaction(&self, txid: bitcoin::Txid) -> Result<GetRawTransaction> {
self.call("getrawtransaction", &[into_json(&txid)?, false.into()])
}

pub fn get_raw_transaction_verbose(
&self,
txid: Txid,
) -> Result<GetRawTransactionVerbose> {
self.call("getrawtransaction", &[into_json(&txid)?, true.into()])
}
}
};
Expand All @@ -55,3 +190,56 @@ macro_rules! impl_client_v17__sendrawtransaction {
}
};
}

/// Implements Bitcoin Core JSON-RPC API method `signrawtransaction`
#[macro_export]
macro_rules! impl_client_v17__signrawtransaction {
() => {
impl Client {
pub fn sign_raw_transaction(
&self,
tx: &bitcoin::Transaction,
) -> Result<SignRawTransaction> {
let hex = bitcoin::consensus::encode::serialize_hex(tx);
self.call("signrawtransaction", &[hex.into()])
}
}
};
}

/// Implements Bitcoin Core JSON-RPC API method `signrawtransactionwithkey`
#[macro_export]
macro_rules! impl_client_v17__signrawtransactionwithkey {
() => {
impl Client {
pub fn sign_raw_transaction_with_key(
&self,
tx: &bitcoin::Transaction,
keys: &[bitcoin::PrivateKey],
) -> Result<SignRawTransaction> {
let hex = bitcoin::consensus::encode::serialize_hex(tx);
let keys = keys.iter().map(|k| format!("{}", k)).collect::<Vec<String>>();
self.call("signrawtransactionwithkey", &[hex.into(), into_json(keys)?])
}
}
};
}

/// Implements Bitcoin Core JSON-RPC API method `testmempoolaccept`
#[macro_export]
macro_rules! impl_client_v17__testmempoolaccept {
() => {
impl Client {
pub fn test_mempool_accept(
&self,
txs: &[bitcoin::Transaction],
) -> Result<TestMempoolAccept> {
let encoded = txs
.iter()
.map(|tx| bitcoin::consensus::encode::serialize_hex(tx))
.collect::<Vec<String>>();
self.call("testmempoolaccept", &[into_json(encoded)?])
}
}
};
}
5 changes: 3 additions & 2 deletions client/src/client_sync/v17/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -414,8 +414,9 @@ macro_rules! impl_client_v17__signrawtransactionwithwallet {
// `hexstring`: The transaction hex string.
pub fn sign_raw_transaction_with_wallet(
&self,
hex: &str,
) -> Result<SignRawTransactionWithWallet> {
tx: &bitcoin::Transaction,
) -> Result<SignRawTransaction> {
let hex = bitcoin::consensus::encode::serialize_hex(tx);
self.call("signrawtransactionwithwallet", &[into_json(hex)?])
}
}
Expand Down
16 changes: 16 additions & 0 deletions client/src/client_sync/v18/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
//! We ignore option arguments unless they effect the shape of the returned JSON data.

pub mod control;
pub mod raw_transactions;

use std::collections::BTreeMap;
use std::path::Path;
Expand Down Expand Up @@ -70,9 +71,24 @@ crate::impl_client_v17__getnetworkinfo!();
crate::impl_client_v17__getpeerinfo!();

// == Rawtransactions ==
crate::impl_client_v18__analyzepsbt!();
crate::impl_client_v17__combinepsbt!();
crate::impl_client_v17__combinerawtransaction!();
crate::impl_client_v17__converttopsbt!();
crate::impl_client_v17__createpsbt!();
crate::impl_client_v17__createrawtransaction!();
crate::impl_client_v17__decodepsbt!();
crate::impl_client_v17__decoderawtransaction!();
crate::impl_client_v17__decodescript!();
crate::impl_client_v17__finalizepsbt!();
crate::impl_client_v17__fundrawtransaction!();
crate::impl_client_v17__getrawtransaction!();
crate::impl_client_v18__joinpsbts!();
crate::impl_client_v17__sendrawtransaction!();
crate::impl_client_v17__signrawtransaction!();
crate::impl_client_v17__signrawtransactionwithkey!();
crate::impl_client_v17__testmempoolaccept!();
crate::impl_client_v18__utxoupdatepsbt!();

// == Wallet ==
crate::impl_client_v17__addmultisigaddress!();
Expand Down
49 changes: 49 additions & 0 deletions client/src/client_sync/v18/raw_transactions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: CC0-1.0

//! Macros for implementing JSON-RPC methods on a client.
//!
//! Specifically this is methods found under the `== Rawtransactions ==` section of the
//! API docs of Bitcoin Core `v0.18`.
//!
//! All macros require `Client` to be in scope.
//!
//! See or use the `define_jsonrpc_minreq_client!` macro to define a `Client`.

/// Implements Bitcoin Core JSON-RPC API method `analyzepsbt`
#[macro_export]
macro_rules! impl_client_v18__analyzepsbt {
() => {
impl Client {
pub fn analyze_psbt(&self, psbt: &bitcoin::Psbt) -> Result<AnalyzePsbt> {
let psbt = format!("{}", psbt);
self.call("analyzepsbt", &[psbt.into()])
}
}
};
}

/// Implements Bitcoin Core JSON-RPC API method `joinpsbts`
#[macro_export]
macro_rules! impl_client_v18__joinpsbts {
() => {
impl Client {
pub fn join_psbts(&self, psbts: &[bitcoin::Psbt]) -> Result<JoinPsbts> {
let psbts = psbts.iter().map(|psbt| format!("{}", psbt)).collect::<Vec<String>>();
self.call("joinpsbts", &[psbts.into()])
}
}
};
}

/// Implements Bitcoin Core JSON-RPC API method `uxtoupdatepsbt`
#[macro_export]
macro_rules! impl_client_v18__utxoupdatepsbt {
() => {
impl Client {
pub fn utxo_update_psbt(&self, psbt: &bitcoin::Psbt) -> Result<JoinPsbts> {
let psbt = format!("{}", psbt);
self.call("uxtoupdatepsbt", &[psbt.into()])
}
}
};
}
Loading