Skip to content

Commit f84d625

Browse files
committed
Test and update listsinceblock
The RPC was implemented for v17 and untested. Some fields are optional or undocumented. Fix the struct for v17. Update the struct, model, into_model, and error for changes in v20, v23, v24 and v28. Add a test and update the types tables.
1 parent 31fdc86 commit f84d625

File tree

29 files changed

+1280
-74
lines changed

29 files changed

+1280
-74
lines changed

integration_test/tests/wallet.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,23 @@ fn wallet__list_received_by_address__modelled() {
593593
assert!(model.0.iter().any(|item| &item.address == unchecked_addr));
594594
}
595595

596+
#[test]
597+
fn wallet__list_since_block__modelled() {
598+
let node = Node::with_wallet(Wallet::Default, &[]);
599+
node.fund_wallet();
600+
let addr = node.client.new_address().expect("newaddress");
601+
let amount = Amount::from_sat(5_000);
602+
node.client.send_to_address(&addr, amount).expect("sendtoaddress");
603+
node.mine_a_block();
604+
605+
let json: ListSinceBlock = node.client.list_since_block().expect("listsinceblock");
606+
let model: Result<mtype::ListSinceBlock, ListSinceBlockError> = json.into_model();
607+
let model = model.unwrap();
608+
609+
let first_tx: mtype::ListSinceBlockTransaction = model.transactions[0].clone();
610+
assert_eq!(first_tx.txid.unwrap().to_string().len(), 64);
611+
}
612+
596613
#[test]
597614
fn wallet__import_multi() {
598615
let node = match () {

types/src/model/wallet.rs

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,8 @@ pub struct ListSinceBlock {
609609
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
610610
#[serde(deny_unknown_fields)]
611611
pub struct ListSinceBlockTransaction {
612+
/// Only returns true if imported addresses were involved in transaction.
613+
pub involves_watch_only: Option<bool>,
612614
/// The bitcoin address of the transaction.
613615
pub address: Option<Address<NetworkUnchecked>>,
614616
/// The transaction category.
@@ -631,39 +633,58 @@ pub struct ListSinceBlockTransaction {
631633
/// Available for 'send' and 'receive' category of transactions. When it's < 0, it means the
632634
/// transaction conflicted that many blocks ago.
633635
pub confirmations: i64,
636+
/// Only present if the transaction's only input is a coinbase one. Only documented from v0.20 and later.
637+
pub generated: Option<bool>,
638+
/// Whether we consider the transaction to be trusted and safe to spend from. Only present
639+
/// when the transaction has 0 confirmations (or negative confirmations, if conflicted). v0.20 and later only.
640+
pub trusted: Option<bool>,
634641
/// The block hash containing the transaction.
635642
///
636643
/// Available for 'send' and 'receive' category of transactions.
637-
pub block_hash: BlockHash,
644+
pub block_hash: Option<BlockHash>,
645+
/// The block height containing the transaction. v20 and later only.
646+
pub block_height: Option<u32>,
638647
/// The index of the transaction in the block that includes it.
639648
///
640649
/// Available for 'send' and 'receive' category of transactions.
641-
pub block_index: u32,
650+
pub block_index: Option<u32>,
642651
/// The block time in seconds since epoch (1 Jan 1970 GMT).
643-
pub block_time: u32,
652+
pub block_time: Option<u32>,
644653
/// The transaction id.
645654
///
646655
/// Available for 'send' and 'receive' category of transactions.
647656
pub txid: Option<Txid>,
657+
/// The hash of serialized transaction, including witness data. v24 and later only.
658+
pub wtxid: Option<Txid>,
659+
/// Conflicting transaction ids. Only documented from v0.20 and later.
660+
pub wallet_conflicts: Option<Vec<Txid>>,
661+
/// The txid if this tx was replaced. v23 and later only.
662+
pub replaced_by_txid: Option<Txid>,
663+
/// The txid if this tx replaces one. v23 and later only.
664+
pub replaces_txid: Option<Txid>,
665+
/// Transactions in the mempool that directly conflict with either this transaction or an ancestor transaction. v28 and later only.
666+
pub mempool_conflicts: Option<Vec<Txid>>,
667+
/// If a comment to is associated with the transaction.
668+
pub to: Option<String>,
648669
/// The transaction time in seconds since epoch (Jan 1 1970 GMT).
649670
pub time: u32,
650671
/// The time received in seconds since epoch (Jan 1 1970 GMT).
651672
///
652673
/// Available for 'send' and 'receive' category of transactions.
653674
pub time_received: u32,
675+
/// If a comment is associated with the transaction.
676+
pub comment: Option<String>,
654677
/// Whether this transaction could be replaced due to BIP125 (replace-by-fee);
655678
/// may be unknown for unconfirmed transactions not in the mempool
656679
pub bip125_replaceable: Bip125Replaceable,
680+
/// Only if 'category' is 'received'. List of parent descriptors for the scriptPubKey of this coin. v24 and later only.
681+
pub parent_descriptors: Option<Vec<String>>,
657682
/// If the transaction has been abandoned (inputs are respendable).
658683
///
659684
/// Only available for the 'send' category of transactions.
660685
pub abandoned: Option<bool>,
661-
/// If a comment is associated with the transaction.
662-
pub comment: Option<String>,
663686
/// A comment for the address/transaction, if any.
664687
pub label: Option<String>,
665-
/// If a comment to is associated with the transaction.
666-
pub to: Option<String>,
667688
}
668689

669690
/// Models the result of JSON-RPC method `listtransactions`.

types/src/v17/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@
185185
//! | listlockunspent | version + model | |
186186
//! | listreceivedbyaccount | returns nothing | |
187187
//! | listreceivedbyaddress | version + model | |
188-
//! | listsinceblock | version + model | UNTESTED |
188+
//! | listsinceblock | version + model | |
189189
//! | listtransactions | version + model | UNTESTED |
190190
//! | listunspent | version + model | |
191191
//! | listwallets | version + model | |

types/src/v17/wallet/into.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -589,30 +589,44 @@ impl ListSinceBlockTransaction {
589589
let category = self.category.into_model();
590590
let amount = SignedAmount::from_btc(self.amount).map_err(E::Amount)?;
591591
let vout = crate::to_u32(self.vout, "vout")?;
592-
let fee = SignedAmount::from_btc(self.fee).map_err(E::Fee)?;
592+
let fee = self
593+
.fee
594+
.map(|f| SignedAmount::from_btc(f).map_err(E::Fee))
595+
.transpose()? // optional historically
596+
.unwrap_or_else(|| SignedAmount::from_sat(0));
593597
let block_hash = self.block_hash.parse::<BlockHash>().map_err(E::BlockHash)?;
594598
let block_index = crate::to_u32(self.block_index, "block_index")?;
595599
let txid = self.txid.map(|s| s.parse::<Txid>().map_err(E::Txid)).transpose()?;
596600
let bip125_replaceable = self.bip125_replaceable.into_model();
597601

598602
Ok(model::ListSinceBlockTransaction {
603+
involves_watch_only: None,
599604
address: Some(address),
600605
category,
601606
amount,
602607
vout,
603608
fee,
604609
confirmations: self.confirmations,
605-
block_hash,
606-
block_index,
607-
block_time: self.block_time,
610+
block_hash: Some(block_hash),
611+
block_index: Some(block_index),
612+
block_time: Some(self.block_time),
608613
txid,
614+
wtxid: None,
609615
time: self.time,
610616
time_received: self.time_received,
611617
bip125_replaceable,
618+
generated: None,
619+
trusted: None,
612620
abandoned: self.abandoned,
613621
comment: self.comment,
614622
label: self.label,
615623
to: self.to,
624+
block_height: None,
625+
wallet_conflicts: None,
626+
replaced_by_txid: None,
627+
replaces_txid: None,
628+
mempool_conflicts: None,
629+
parent_descriptors: None,
616630
})
617631
}
618632
}

types/src/v17/wallet/mod.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -716,7 +716,7 @@ pub struct ListSinceBlock {
716716
#[serde(deny_unknown_fields)]
717717
pub struct ListSinceBlockTransaction {
718718
/// DEPRECATED. The account name associated with the transaction. Will be "" for the default account.
719-
pub account: String,
719+
pub account: Option<String>,
720720
/// The bitcoin address of the transaction.
721721
///
722722
/// Not present for move transactions (category = move).
@@ -734,12 +734,14 @@ pub struct ListSinceBlockTransaction {
734734
/// The amount of the fee in BTC.
735735
///
736736
/// This is negative and only available for the 'send' category of transactions.
737-
pub fee: f64,
737+
pub fee: Option<f64>,
738738
/// The number of confirmations for the transaction.
739739
///
740740
/// Available for 'send' and 'receive' category of transactions. When it's < 0, it means the
741741
/// transaction conflicted that many blocks ago.
742742
pub confirmations: i64,
743+
/// Only present if transaction only input is a coinbase one.
744+
pub generated: Option<bool>,
743745
/// The block hash containing the transaction.
744746
///
745747
/// Available for 'send' and 'receive' category of transactions.
@@ -757,6 +759,9 @@ pub struct ListSinceBlockTransaction {
757759
///
758760
/// Available for 'send' and 'receive' category of transactions.
759761
pub txid: Option<String>,
762+
/// Conflicting transaction ids.
763+
#[serde(rename = "walletconflicts")]
764+
pub wallet_conflicts: Vec<String>,
760765
/// The transaction time in seconds since epoch (Jan 1 1970 GMT).
761766
pub time: u32,
762767
/// The time received in seconds since epoch (Jan 1 1970 GMT).

types/src/v18/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@
188188
//! | listlockunspent | version + model | |
189189
//! | listreceivedbyaddress | version + model | |
190190
//! | listreceivedbylabel | version + model | |
191-
//! | listsinceblock | version + model | UNTESTED |
191+
//! | listsinceblock | version + model | |
192192
//! | listtransactions | version + model | UNTESTED |
193193
//! | listunspent | version + model | |
194194
//! | listwalletdir | version | |

types/src/v19/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@
189189
//! | listlockunspent | version + model | |
190190
//! | listreceivedbyaddress | version + model | |
191191
//! | listreceivedbylabel | version + model | |
192-
//! | listsinceblock | version + model | UNTESTED |
192+
//! | listsinceblock | version + model | |
193193
//! | listtransactions | version + model | UNTESTED |
194194
//! | listunspent | version + model | |
195195
//! | listwalletdir | version | |

types/src/v20/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@
190190
//! | listlockunspent | version + model | |
191191
//! | listreceivedbyaddress | version + model | |
192192
//! | listreceivedbylabel | version + model | |
193-
//! | listsinceblock | version + model | UNTESTED |
193+
//! | listsinceblock | version + model | |
194194
//! | listtransactions | version + model | UNTESTED |
195195
//! | listunspent | version + model | |
196196
//! | listwalletdir | version | |
@@ -240,7 +240,8 @@ pub use self::{
240240
util::CreateMultisig,
241241
wallet::{
242242
AddMultisigAddress, GetAddressInfo, GetAddressInfoEmbedded, GetTransaction,
243-
GetTransactionDetail,
243+
GetTransactionDetail, ListSinceBlock, ListSinceBlockError, ListSinceBlockTransaction,
244+
ListSinceBlockTransactionError,
244245
},
245246
};
246247
#[doc(inline)]
@@ -264,8 +265,7 @@ pub use crate::{
264265
GetTransactionDetailError, GetTransactionError, GetTxOut, GetTxOutError, GetTxOutSetInfo,
265266
GetTxOutSetInfoError, GetUnconfirmedBalance, GetWalletInfoError, ListAddressGroupings,
266267
ListAddressGroupingsError, ListAddressGroupingsItem, ListLabels, ListLockUnspent,
267-
ListLockUnspentItem, ListLockUnspentItemError, ListReceivedByAddressError, ListSinceBlock,
268-
ListSinceBlockError, ListSinceBlockTransaction, ListSinceBlockTransactionError,
268+
ListLockUnspentItem, ListLockUnspentItemError, ListReceivedByAddressError,
269269
ListTransactions, ListTransactionsItem, ListTransactionsItemError, ListUnspentItemError,
270270
ListWallets, LoadWallet, LockUnspent, Locked, PruneBlockchain, RawTransactionError,
271271
RawTransactionInput, RawTransactionOutput, RescanBlockchain, ScriptType, SendMany,

types/src/v20/wallet/error.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// SPDX-License-Identifier: CC0-1.0
2+
3+
//! Errors for wallet types newly (re)defined in v20.
4+
5+
use core::fmt;
6+
7+
use bitcoin::amount::ParseAmountError;
8+
use bitcoin::{address, hex};
9+
10+
use crate::error::write_err;
11+
use crate::NumericError;
12+
13+
/// Error when converting a `ListSinceBlock` type into the model type.
14+
#[derive(Debug)]
15+
pub enum ListSinceBlockError {
16+
/// Conversion of item in `transactions` list failed.
17+
Transactions(ListSinceBlockTransactionError),
18+
/// Conversion of item in `removed` list failed.
19+
Removed(ListSinceBlockTransactionError),
20+
/// Conversion of the `last_block` field failed.
21+
LastBlock(hex::HexToArrayError),
22+
}
23+
24+
impl fmt::Display for ListSinceBlockError {
25+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
26+
use ListSinceBlockError::*;
27+
28+
match *self {
29+
Transactions(ref e) =>
30+
write_err!(f, "conversion of the `transactions` field failed"; e),
31+
Removed(ref e) => write_err!(f, "conversion of the `removed` field failed"; e),
32+
LastBlock(ref e) => write_err!(f, "conversion of the `last_block` field failed"; e),
33+
}
34+
}
35+
}
36+
37+
#[cfg(feature = "std")]
38+
impl std::error::Error for ListSinceBlockError {
39+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
40+
use ListSinceBlockError::*;
41+
42+
match *self {
43+
Transactions(ref e) => Some(e),
44+
Removed(ref e) => Some(e),
45+
LastBlock(ref e) => Some(e),
46+
}
47+
}
48+
}
49+
50+
/// Error when converting a `ListSinceBlockTransaction` type into the model type.
51+
///
52+
/// Note: Additional fields introduced in v20 (e.g. `generated`, `trusted`, `block_height`,
53+
/// `wallet_conflicts`, `involvesWatchonly`) are currently not modelled and therefore are
54+
/// intentionally ignored during conversion; as such they have no dedicated error variants.
55+
#[derive(Debug)]
56+
pub enum ListSinceBlockTransactionError {
57+
/// Conversion of numeric type to expected type failed.
58+
Numeric(NumericError),
59+
/// Conversion of the `address` field failed.
60+
Address(address::ParseError),
61+
/// Conversion of the `amount` field failed.
62+
Amount(ParseAmountError),
63+
/// Conversion of the `fee` field failed.
64+
Fee(ParseAmountError),
65+
/// Conversion of the `block_hash` field failed.
66+
BlockHash(hex::HexToArrayError),
67+
/// Conversion of the `txid` field failed.
68+
Txid(hex::HexToArrayError),
69+
/// Conversion of an item in the `wallet_conflicts` list failed.
70+
WalletConflicts(hex::HexToArrayError),
71+
}
72+
73+
impl fmt::Display for ListSinceBlockTransactionError {
74+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
75+
use ListSinceBlockTransactionError as E;
76+
77+
match *self {
78+
E::Numeric(ref e) => write_err!(f, "numeric"; e),
79+
E::Address(ref e) => write_err!(f, "conversion of the `address` field failed"; e),
80+
E::Amount(ref e) => write_err!(f, "conversion of the `amount` field failed"; e),
81+
E::Fee(ref e) => write_err!(f, "conversion of the `fee` field failed"; e),
82+
E::BlockHash(ref e) => write_err!(f, "conversion of the `block_hash` field failed"; e),
83+
E::Txid(ref e) => write_err!(f, "conversion of the `txid` field failed"; e),
84+
E::WalletConflicts(ref e) =>
85+
write_err!(f, "conversion of an item in the `wallet_conflicts` list failed"; e),
86+
}
87+
}
88+
}
89+
90+
#[cfg(feature = "std")]
91+
impl std::error::Error for ListSinceBlockTransactionError {
92+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
93+
use ListSinceBlockTransactionError as E;
94+
95+
match *self {
96+
E::Numeric(ref e) => Some(e),
97+
E::Address(ref e) => Some(e),
98+
E::Amount(ref e) => Some(e),
99+
E::Fee(ref e) => Some(e),
100+
E::BlockHash(ref e) => Some(e),
101+
E::Txid(ref e) => Some(e),
102+
E::WalletConflicts(ref e) => Some(e),
103+
}
104+
}
105+
}
106+
107+
impl From<NumericError> for ListSinceBlockTransactionError {
108+
fn from(e: NumericError) -> Self { Self::Numeric(e) }
109+
}

0 commit comments

Comments
 (0)