Merge bitcoindevkit/bdk#551: Fix sent calculation in the RPC backend
bfd0d13779003f11fa099a0b665052c18ce6e500 [blockchain] Fix `sent` calculation in the RPC backend (Daniela Brozzoni) 128c37595c5cfeacdb8e999da5795a9aa28ad67b [tests] Pass tx inputs to the testutils macro (Daniela Brozzoni) Pull request description: <!-- You can erase any parts of this template not applicable to your Pull Request. --> ### Description <!-- Describe the purpose of this PR, what's being adding and/or fixed --> ### Notes to the reviewers <!-- In this section you can include notes directed to the reviewers, like explaining why some parts of the PR were done in a specific way --> ### Checklists #### All Submissions: * [x] I've signed all my commits * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md) * [x] I ran `cargo fmt` and `cargo clippy` before committing #### Bugfixes: * [x] I've added tests to reproduce the issue which are now passing ACKs for top commit: afilini: ACK bfd0d13779003f11fa099a0b665052c18ce6e500 Tree-SHA512: 1c214819c5dc1f1c30b1c6ef18a44a3013d587651890b26aecfc5203d128173fd91497337186bbee6934f77d3cfe1686e67b83ca6fe6e47b4c1d4b1dbcc656ee
This commit is contained in:
commit
760a6ca1a1
@ -257,9 +257,11 @@ impl Blockchain for RpcBlockchain {
|
|||||||
|
|
||||||
for input in tx.input.iter() {
|
for input in tx.input.iter() {
|
||||||
if let Some(previous_output) = db.get_previous_output(&input.previous_output)? {
|
if let Some(previous_output) = db.get_previous_output(&input.previous_output)? {
|
||||||
|
if db.is_mine(&previous_output.script_pubkey)? {
|
||||||
sent += previous_output.value;
|
sent += previous_output.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let td = TransactionDetails {
|
let td = TransactionDetails {
|
||||||
transaction: Some(tx),
|
transaction: Some(tx),
|
||||||
|
@ -90,13 +90,19 @@ impl TestClient {
|
|||||||
map.insert(out.to_address.clone(), Amount::from_sat(out.value));
|
map.insert(out.to_address.clone(), Amount::from_sat(out.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let input: Vec<_> = meta_tx
|
||||||
|
.input
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| x.into_raw_tx_input())
|
||||||
|
.collect();
|
||||||
|
|
||||||
if self.get_balance(None, None).unwrap() < Amount::from_sat(required_balance) {
|
if self.get_balance(None, None).unwrap() < Amount::from_sat(required_balance) {
|
||||||
panic!("Insufficient funds in bitcoind. Please generate a few blocks with: `bitcoin-cli generatetoaddress 10 {}`", self.get_new_address(None, None).unwrap());
|
panic!("Insufficient funds in bitcoind. Please generate a few blocks with: `bitcoin-cli generatetoaddress 10 {}`", self.get_new_address(None, None).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: core can't create a tx with two outputs to the same address
|
// FIXME: core can't create a tx with two outputs to the same address
|
||||||
let tx = self
|
let tx = self
|
||||||
.create_raw_transaction_hex(&[], &map, meta_tx.locktime, meta_tx.replaceable)
|
.create_raw_transaction_hex(&input, &map, meta_tx.locktime, meta_tx.replaceable)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let tx = self.fund_raw_transaction(tx, None, None).unwrap();
|
let tx = self.fund_raw_transaction(tx, None, None).unwrap();
|
||||||
let mut tx: Transaction = deserialize(&tx.hex).unwrap();
|
let mut tx: Transaction = deserialize(&tx.hex).unwrap();
|
||||||
@ -353,7 +359,7 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
fn $_fn_name:ident ( $( $test_client:ident : &TestClient )? $(,)? ) -> $blockchain:ty $block:block) => {
|
fn $_fn_name:ident ( $( $test_client:ident : &TestClient )? $(,)? ) -> $blockchain:ty $block:block) => {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod bdk_blockchain_tests {
|
mod bdk_blockchain_tests {
|
||||||
use $crate::bitcoin::Network;
|
use $crate::bitcoin::{Transaction, Network};
|
||||||
use $crate::testutils::blockchain_tests::TestClient;
|
use $crate::testutils::blockchain_tests::TestClient;
|
||||||
use $crate::blockchain::noop_progress;
|
use $crate::blockchain::noop_progress;
|
||||||
use $crate::database::MemoryDatabase;
|
use $crate::database::MemoryDatabase;
|
||||||
@ -1073,6 +1079,49 @@ macro_rules! bdk_blockchain_tests {
|
|||||||
let taproot_balance = taproot_wallet_client.get_balance(None, None).unwrap();
|
let taproot_balance = taproot_wallet_client.get_balance(None, None).unwrap();
|
||||||
assert_eq!(taproot_balance.as_sat(), 25_000, "node has incorrect taproot wallet balance");
|
assert_eq!(taproot_balance.as_sat(), 25_000, "node has incorrect taproot wallet balance");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tx_chain() {
|
||||||
|
use bitcoincore_rpc::RpcApi;
|
||||||
|
use bitcoin::consensus::encode::deserialize;
|
||||||
|
use $crate::wallet::AddressIndex;
|
||||||
|
|
||||||
|
// Here we want to test that we set correctly the send and receive
|
||||||
|
// fields in the transaction object. For doing so, we create two
|
||||||
|
// different txs, the second one spending from the first:
|
||||||
|
// 1.
|
||||||
|
// Core (#1) -> Core (#2)
|
||||||
|
// -> Us (#3)
|
||||||
|
// 2.
|
||||||
|
// Core (#2) -> Us (#4)
|
||||||
|
|
||||||
|
let (wallet, _, mut test_client) = init_single_sig();
|
||||||
|
let bdk_address = wallet.get_address(AddressIndex::New).unwrap().address;
|
||||||
|
let core_address = test_client.get_new_address(None, None).unwrap();
|
||||||
|
let tx = testutils! {
|
||||||
|
@tx ( (@addr bdk_address.clone()) => 50_000, (@addr core_address.clone()) => 40_000 )
|
||||||
|
};
|
||||||
|
|
||||||
|
// Tx one: from Core #1 to Core #2 and Us #3.
|
||||||
|
let txid_1 = test_client.receive(tx);
|
||||||
|
let tx_1: Transaction = deserialize(&test_client.get_transaction(&txid_1, None).unwrap().hex).unwrap();
|
||||||
|
let vout_1 = tx_1.output.into_iter().position(|o| o.script_pubkey == core_address.script_pubkey()).unwrap() as u32;
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
let tx_1 = wallet.list_transactions(false).unwrap().into_iter().find(|tx| tx.txid == txid_1).unwrap();
|
||||||
|
assert_eq!(tx_1.received, 50_000);
|
||||||
|
assert_eq!(tx_1.sent, 0);
|
||||||
|
|
||||||
|
// Tx two: from Core #2 to Us #4.
|
||||||
|
let tx = testutils! {
|
||||||
|
@tx ( (@addr bdk_address) => 10_000 ) ( @inputs (txid_1,vout_1))
|
||||||
|
};
|
||||||
|
let txid_2 = test_client.receive(tx);
|
||||||
|
|
||||||
|
wallet.sync(noop_progress(), None).unwrap();
|
||||||
|
let tx_2 = wallet.list_transactions(false).unwrap().into_iter().find(|tx| tx.txid == txid_2).unwrap();
|
||||||
|
assert_eq!(tx_2.received, 10_000);
|
||||||
|
assert_eq!(tx_2.sent, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -15,11 +15,37 @@
|
|||||||
pub mod blockchain_tests;
|
pub mod blockchain_tests;
|
||||||
|
|
||||||
use bitcoin::secp256k1::{Secp256k1, Verification};
|
use bitcoin::secp256k1::{Secp256k1, Verification};
|
||||||
use bitcoin::{Address, PublicKey};
|
use bitcoin::{Address, PublicKey, Txid};
|
||||||
|
|
||||||
use miniscript::descriptor::DescriptorPublicKey;
|
use miniscript::descriptor::DescriptorPublicKey;
|
||||||
use miniscript::{Descriptor, MiniscriptKey, TranslatePk};
|
use miniscript::{Descriptor, MiniscriptKey, TranslatePk};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct TestIncomingInput {
|
||||||
|
pub txid: Txid,
|
||||||
|
pub vout: u32,
|
||||||
|
pub sequence: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestIncomingInput {
|
||||||
|
pub fn new(txid: Txid, vout: u32, sequence: Option<u32>) -> Self {
|
||||||
|
Self {
|
||||||
|
txid,
|
||||||
|
vout,
|
||||||
|
sequence,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "test-blockchains")]
|
||||||
|
pub fn into_raw_tx_input(self) -> bitcoincore_rpc::json::CreateRawTransactionInput {
|
||||||
|
bitcoincore_rpc::json::CreateRawTransactionInput {
|
||||||
|
txid: self.txid,
|
||||||
|
vout: self.vout,
|
||||||
|
sequence: self.sequence,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct TestIncomingOutput {
|
pub struct TestIncomingOutput {
|
||||||
pub value: u64,
|
pub value: u64,
|
||||||
@ -37,6 +63,7 @@ impl TestIncomingOutput {
|
|||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct TestIncomingTx {
|
pub struct TestIncomingTx {
|
||||||
|
pub input: Vec<TestIncomingInput>,
|
||||||
pub output: Vec<TestIncomingOutput>,
|
pub output: Vec<TestIncomingOutput>,
|
||||||
pub min_confirmations: Option<u64>,
|
pub min_confirmations: Option<u64>,
|
||||||
pub locktime: Option<i64>,
|
pub locktime: Option<i64>,
|
||||||
@ -45,12 +72,14 @@ pub struct TestIncomingTx {
|
|||||||
|
|
||||||
impl TestIncomingTx {
|
impl TestIncomingTx {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
input: Vec<TestIncomingInput>,
|
||||||
output: Vec<TestIncomingOutput>,
|
output: Vec<TestIncomingOutput>,
|
||||||
min_confirmations: Option<u64>,
|
min_confirmations: Option<u64>,
|
||||||
locktime: Option<i64>,
|
locktime: Option<i64>,
|
||||||
replaceable: Option<bool>,
|
replaceable: Option<bool>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
input,
|
||||||
output,
|
output,
|
||||||
min_confirmations,
|
min_confirmations,
|
||||||
locktime,
|
locktime,
|
||||||
@ -58,6 +87,10 @@ impl TestIncomingTx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_input(&mut self, input: TestIncomingInput) {
|
||||||
|
self.input.push(input);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_output(&mut self, output: TestIncomingOutput) {
|
pub fn add_output(&mut self, output: TestIncomingOutput) {
|
||||||
self.output.push(output);
|
self.output.push(output);
|
||||||
}
|
}
|
||||||
@ -123,16 +156,21 @@ macro_rules! testutils {
|
|||||||
});
|
});
|
||||||
( @e $descriptors:expr, $child:expr ) => ({ testutils!(@external $descriptors, $child) });
|
( @e $descriptors:expr, $child:expr ) => ({ testutils!(@external $descriptors, $child) });
|
||||||
( @i $descriptors:expr, $child:expr ) => ({ testutils!(@internal $descriptors, $child) });
|
( @i $descriptors:expr, $child:expr ) => ({ testutils!(@internal $descriptors, $child) });
|
||||||
|
( @addr $addr:expr ) => ({ $addr });
|
||||||
|
|
||||||
( @tx ( $( ( $( $addr:tt )* ) => $amount:expr ),+ ) $( ( @locktime $locktime:expr ) )? $( ( @confirmations $confirmations:expr ) )? $( ( @replaceable $replaceable:expr ) )? ) => ({
|
( @tx ( $( ( $( $addr:tt )* ) => $amount:expr ),+ ) $( ( @inputs $( ($txid:expr, $vout:expr) ),+ ) )? $( ( @locktime $locktime:expr ) )? $( ( @confirmations $confirmations:expr ) )? $( ( @replaceable $replaceable:expr ) )? ) => ({
|
||||||
let outs = vec![$( $crate::testutils::TestIncomingOutput::new($amount, testutils!( $($addr)* ))),+];
|
let outs = vec![$( $crate::testutils::TestIncomingOutput::new($amount, testutils!( $($addr)* ))),+];
|
||||||
|
let _ins: Vec<$crate::testutils::TestIncomingInput> = vec![];
|
||||||
|
$(
|
||||||
|
let _ins = vec![$( $crate::testutils::TestIncomingInput { txid: $txid, vout: $vout, sequence: None }),+];
|
||||||
|
)?
|
||||||
|
|
||||||
let locktime = None::<i64>$(.or(Some($locktime)))?;
|
let locktime = None::<i64>$(.or(Some($locktime)))?;
|
||||||
|
|
||||||
let min_confirmations = None::<u64>$(.or(Some($confirmations)))?;
|
let min_confirmations = None::<u64>$(.or(Some($confirmations)))?;
|
||||||
let replaceable = None::<bool>$(.or(Some($replaceable)))?;
|
let replaceable = None::<bool>$(.or(Some($replaceable)))?;
|
||||||
|
|
||||||
$crate::testutils::TestIncomingTx::new(outs, min_confirmations, locktime, replaceable)
|
$crate::testutils::TestIncomingTx::new(_ins, outs, min_confirmations, locktime, replaceable)
|
||||||
});
|
});
|
||||||
|
|
||||||
( @literal $key:expr ) => ({
|
( @literal $key:expr ) => ({
|
||||||
|
Loading…
x
Reference in New Issue
Block a user