Skip to content

Commit 60cdd1a

Browse files
committed
fix(types): add missing return fields for gettxoutsetinfo RPC
The RPC has missing fields which are enabled only when `coinstatsindex` or muhash hash type is used. Argument list is also added to header coment.
1 parent 20b58c4 commit 60cdd1a

File tree

11 files changed

+212
-46
lines changed

11 files changed

+212
-46
lines changed

types/src/model/blockchain.rs

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -684,8 +684,8 @@ pub struct GetTxOutSetInfo {
684684
pub height: u32,
685685
/// The hash of the block at the tip of the chain.
686686
pub best_block: BlockHash,
687-
/// The number of transactions with unspent outputs.
688-
pub transactions: u32,
687+
/// The number of transactions with unspent outputs (not available when coinstatsindex is used).
688+
pub transactions: Option<u32>,
689689
/// The number of unspent transaction outputs.
690690
pub tx_outs: u32,
691691
/// A meaningless metric for UTXO set size.
@@ -697,10 +697,44 @@ pub struct GetTxOutSetInfo {
697697
/// The serialized hash (only present if 'hash_serialized_3' hash_type is chosen).
698698
/// v26 and later only.
699699
pub hash_serialized_3: Option<String>,
700-
/// The estimated size of the chainstate on disk.
701-
pub disk_size: u32,
700+
/// The estimated size of the chainstate on disk (not available when coinstatsindex is used).
701+
pub disk_size: Option<u32>,
702702
/// The total amount.
703703
pub total_amount: Amount,
704+
/// The serialized hash (only present if 'muhash' hash_type is chosen)
705+
pub muhash: Option<String>,
706+
/// The total amount of coins permanently excluded from the UTXO set (only available if coinstatsindex is used).
707+
pub total_unspendable_amount: Option<Amount>,
708+
/// Info on amounts in the block at this block height (only available if coinstatsindex is used).
709+
pub block_info: Option<BlockInfo>,
710+
}
711+
712+
/// Detailed block-level info. Part of `gettxoutsetinfo`.
713+
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
714+
pub struct BlockInfo {
715+
/// Total amount of all prevouts spent in this block.
716+
pub prevout_spent: Amount,
717+
/// Coinbase subsidy amount of this block.
718+
pub coinbase: Amount,
719+
/// Total amount of new outputs created by this block.
720+
pub new_outputs_ex_coinbase: Amount,
721+
/// Total amount of unspendable outputs created in this block.
722+
pub unspendable: Amount,
723+
/// Detailed view of unspendable categories.
724+
pub unspendables: Unspendables,
725+
}
726+
727+
/// Categories of unspendable amounts. Part of `gettxoutsetinfo`.
728+
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
729+
pub struct Unspendables {
730+
/// The unspendable amount of the Genesis block subsidy.
731+
pub genesis_block: Amount,
732+
/// Transactions overridden by duplicates (no longer possible with BIP30).
733+
pub bip30: Amount,
734+
/// Amounts sent to scripts that are unspendable (for example OP_RETURN outputs).
735+
pub scripts: Amount,
736+
/// Fee rewards that miners did not claim in their coinbase transaction.
737+
pub unclaimed_rewards: Amount,
704738
}
705739

706740
/// Models the result of JSON-RPC method `gettxspendingprevout`.

types/src/model/mod.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,17 @@ use serde::{Deserialize, Serialize};
2727
pub use self::{
2828
blockchain::{
2929
ActivityEntry, Bip9Info, Bip9SoftforkInfo, Bip9SoftforkStatistics, Bip9SoftforkStatus,
30-
Bip9Statistics, ChainState, ChainTips, ChainTipsStatus, DeploymentInfo, DumpTxOutSet,
31-
GetBestBlockHash, GetBlockCount, GetBlockFilter, GetBlockHash, GetBlockHeader,
32-
GetBlockHeaderVerbose, GetBlockStats, GetBlockVerboseOne, GetBlockVerboseZero,
33-
GetBlockchainInfo, GetChainStates, GetChainTips, GetChainTxStats, GetDeploymentInfo,
34-
GetDescriptorActivity, GetDifficulty, GetMempoolAncestors, GetMempoolAncestorsVerbose,
35-
GetMempoolDescendants, GetMempoolDescendantsVerbose, GetMempoolEntry, GetMempoolInfo,
36-
GetRawMempool, GetRawMempoolVerbose, GetTxOut, GetTxOutSetInfo, GetTxSpendingPrevout,
37-
GetTxSpendingPrevoutItem, LoadTxOutSet, MempoolEntry, MempoolEntryFees, ReceiveActivity,
38-
ScanBlocksStart, ScanTxOutSetStart, ScanTxOutSetUnspent, Softfork, SoftforkType,
39-
SpendActivity, VerifyTxOutProof, WaitForBlock, WaitForBlockHeight, WaitForNewBlock,
30+
Bip9Statistics, BlockInfo, ChainState, ChainTips, ChainTipsStatus, DeploymentInfo,
31+
DumpTxOutSet, GetBestBlockHash, GetBlockCount, GetBlockFilter, GetBlockHash,
32+
GetBlockHeader, GetBlockHeaderVerbose, GetBlockStats, GetBlockVerboseOne,
33+
GetBlockVerboseZero, GetBlockchainInfo, GetChainStates, GetChainTips, GetChainTxStats,
34+
GetDeploymentInfo, GetDescriptorActivity, GetDifficulty, GetMempoolAncestors,
35+
GetMempoolAncestorsVerbose, GetMempoolDescendants, GetMempoolDescendantsVerbose,
36+
GetMempoolEntry, GetMempoolInfo, GetRawMempool, GetRawMempoolVerbose, GetTxOut,
37+
GetTxOutSetInfo, GetTxSpendingPrevout, GetTxSpendingPrevoutItem, LoadTxOutSet,
38+
MempoolEntry, MempoolEntryFees, ReceiveActivity, ScanBlocksStart, ScanTxOutSetStart,
39+
ScanTxOutSetUnspent, Softfork, SoftforkType, SpendActivity, Unspendables, VerifyTxOutProof,
40+
WaitForBlock, WaitForBlockHeight, WaitForNewBlock,
4041
},
4142
generating::{Generate, GenerateBlock, GenerateToAddress, GenerateToDescriptor},
4243
hidden::{

types/src/v17/blockchain/into.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -530,11 +530,11 @@ impl GetTxOutSetInfo {
530530

531531
let height = crate::to_u32(self.height, "height")?;
532532
let best_block = self.best_block.parse::<BlockHash>().map_err(E::BestBlock)?;
533-
let transactions = crate::to_u32(self.transactions, "transactions")?;
533+
let transactions = Some(crate::to_u32(self.transactions, "transactions")?);
534534
let tx_outs = crate::to_u32(self.tx_outs, "tx_outs")?;
535535
let bogo_size = crate::to_u32(self.bogo_size, "bogo_size")?;
536536
let hash_serialized_2 = Some(self.hash_serialized_2); // TODO: Convert this to a hash type.
537-
let disk_size = crate::to_u32(self.disk_size, "disk_size")?;
537+
let disk_size = Some(crate::to_u32(self.disk_size, "disk_size")?);
538538
let total_amount = Amount::from_btc(self.total_amount).map_err(E::TotalAmount)?;
539539

540540
Ok(model::GetTxOutSetInfo {
@@ -547,6 +547,9 @@ impl GetTxOutSetInfo {
547547
hash_serialized_3: None, // v26 and later only.
548548
disk_size,
549549
total_amount,
550+
muhash: None,
551+
total_unspendable_amount: None,
552+
block_info: None,
550553
})
551554
}
552555
}

types/src/v26/blockchain/error.rs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,16 +96,51 @@ pub enum GetTxOutSetInfoError {
9696
BestBlock(hex::HexToArrayError),
9797
/// Conversion of the transaction `total_amount` field failed.
9898
TotalAmount(amount::ParseAmountError),
99+
/// Conversion of the `prevout_spent` field failed.
100+
PrevoutSpent(amount::ParseAmountError),
101+
/// Conversion of the `coinbase` field failed.
102+
Coinbase(amount::ParseAmountError),
103+
/// Conversion of the `new_outputs_ex_coinbase` field failed.
104+
NewOutputsExCoinbase(amount::ParseAmountError),
105+
/// Conversion of the `unspendable` field failed.
106+
Unspendable(amount::ParseAmountError),
107+
/// Conversion of the `unspendables.genesis_block` field failed.
108+
UnspendablesGenesisBlock(amount::ParseAmountError),
109+
/// Conversion of the `unspendables.bip30` field failed.
110+
UnspendablesBip30(amount::ParseAmountError),
111+
/// Conversion of the `unspendables.scripts` field failed.
112+
UnspendablesScripts(amount::ParseAmountError),
113+
/// Conversion of the `unspendables.unclaimed_rewards` field failed.
114+
UnspendablesUnclaimedRewards(amount::ParseAmountError),
115+
/// Conversion of the `total_unspendable_amount` field failed.
116+
TotalUnspendableAmount(amount::ParseAmountError),
99117
}
100118

101119
impl fmt::Display for GetTxOutSetInfoError {
102120
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
103121
match *self {
104122
Self::Numeric(ref e) => write_err!(f, "numeric"; e),
105123
Self::BestBlock(ref e) =>
106-
write_err!(f, "conversion of the `beast_block` field failed"; e),
124+
write_err!(f, "conversion of the `best_block` field failed"; e),
107125
Self::TotalAmount(ref e) =>
108126
write_err!(f, "conversion of the `total_amount` field failed"; e),
127+
Self::PrevoutSpent(ref e) =>
128+
write_err!(f, "conversion of the `prevout_spent` field failed"; e),
129+
Self::Coinbase(ref e) => write_err!(f, "conversion of the `coinbase` field failed"; e),
130+
Self::NewOutputsExCoinbase(ref e) =>
131+
write_err!(f, "conversion of the `new_outputs_ex_coinbase` field failed"; e),
132+
Self::Unspendable(ref e) =>
133+
write_err!(f, "conversion of the `unspendable` field failed"; e),
134+
Self::UnspendablesGenesisBlock(ref e) =>
135+
write_err!(f, "conversion of the `unspendables.genesis_block` field failed"; e),
136+
Self::UnspendablesBip30(ref e) =>
137+
write_err!(f, "conversion of the `unspendables.bip30` field failed"; e),
138+
Self::UnspendablesScripts(ref e) =>
139+
write_err!(f, "conversion of the `unspendables.scripts` field failed"; e),
140+
Self::UnspendablesUnclaimedRewards(ref e) =>
141+
write_err!(f, "conversion of the `unspendables.unclaimed_rewards` field failed"; e),
142+
Self::TotalUnspendableAmount(ref e) =>
143+
write_err!(f, "conversion of the `total_unspendable_amount` field failed"; e),
109144
}
110145
}
111146
}
@@ -117,6 +152,15 @@ impl std::error::Error for GetTxOutSetInfoError {
117152
Self::Numeric(ref e) => Some(e),
118153
Self::BestBlock(ref e) => Some(e),
119154
Self::TotalAmount(ref e) => Some(e),
155+
Self::PrevoutSpent(ref e) => Some(e),
156+
Self::Coinbase(ref e) => Some(e),
157+
Self::NewOutputsExCoinbase(ref e) => Some(e),
158+
Self::Unspendable(ref e) => Some(e),
159+
Self::UnspendablesGenesisBlock(ref e) => Some(e),
160+
Self::UnspendablesBip30(ref e) => Some(e),
161+
Self::UnspendablesScripts(ref e) => Some(e),
162+
Self::UnspendablesUnclaimedRewards(ref e) => Some(e),
163+
Self::TotalUnspendableAmount(ref e) => Some(e),
120164
}
121165
}
122166
}

types/src/v26/blockchain/into.rs

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,18 @@ impl GetTxOutSetInfo {
6969
pub fn into_model(self) -> Result<model::GetTxOutSetInfo, GetTxOutSetInfoError> {
7070
use GetTxOutSetInfoError as E;
7171

72-
let height = crate::to_u32(self.height, "height")?;
72+
let height = crate::to_u32(self.height, "height").map_err(E::Numeric)?;
7373
let best_block = self.best_block.parse::<BlockHash>().map_err(E::BestBlock)?;
74-
let transactions = crate::to_u32(self.transactions, "transactions")?;
75-
let tx_outs = crate::to_u32(self.tx_outs, "tx_outs")?;
76-
let bogo_size = crate::to_u32(self.bogo_size, "bogo_size")?;
77-
let disk_size = crate::to_u32(self.disk_size, "disk_size")?;
74+
let transactions = self
75+
.transactions
76+
.map(|v| crate::to_u32(v, "transactions").map_err(E::Numeric))
77+
.transpose()?;
78+
let tx_outs = crate::to_u32(self.tx_outs, "tx_outs").map_err(E::Numeric)?;
79+
let bogo_size = crate::to_u32(self.bogo_size, "bogo_size").map_err(E::Numeric)?;
80+
let disk_size = self
81+
.disk_size
82+
.map(|v| crate::to_u32(v, "disk_size").map_err(E::Numeric))
83+
.transpose()?;
7884
let total_amount = Amount::from_btc(self.total_amount).map_err(E::TotalAmount)?;
7985

8086
Ok(model::GetTxOutSetInfo {
@@ -87,6 +93,40 @@ impl GetTxOutSetInfo {
8793
hash_serialized_3: self.hash_serialized_3,
8894
disk_size,
8995
total_amount,
96+
muhash: self.muhash,
97+
total_unspendable_amount: self
98+
.total_unspendable_amount
99+
.map(|v| Amount::from_btc(v).map_err(E::TotalUnspendableAmount))
100+
.transpose()?,
101+
block_info: match self.block_info {
102+
Some(b) => {
103+
let prevout_spent =
104+
Amount::from_btc(b.prevout_spent).map_err(E::PrevoutSpent)?;
105+
let coinbase = Amount::from_btc(b.coinbase).map_err(E::Coinbase)?;
106+
let new_outputs_ex_coinbase = Amount::from_btc(b.new_outputs_ex_coinbase)
107+
.map_err(E::NewOutputsExCoinbase)?;
108+
let unspendable = Amount::from_btc(b.unspendable).map_err(E::Unspendable)?;
109+
let unspendables = model::Unspendables {
110+
genesis_block: Amount::from_btc(b.unspendables.genesis_block)
111+
.map_err(E::UnspendablesGenesisBlock)?,
112+
bip30: Amount::from_btc(b.unspendables.bip30)
113+
.map_err(E::UnspendablesBip30)?,
114+
scripts: Amount::from_btc(b.unspendables.scripts)
115+
.map_err(E::UnspendablesScripts)?,
116+
unclaimed_rewards: Amount::from_btc(b.unspendables.unclaimed_rewards)
117+
.map_err(E::UnspendablesUnclaimedRewards)?,
118+
};
119+
120+
Some(model::BlockInfo {
121+
prevout_spent,
122+
coinbase,
123+
new_outputs_ex_coinbase,
124+
unspendable,
125+
unspendables,
126+
})
127+
}
128+
None => None,
129+
},
90130
})
91131
}
92132
}

types/src/v26/blockchain/mod.rs

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ pub struct ChainState {
8686
/// >
8787
/// > Returns statistics about the unspent transaction output set.
8888
/// > Note this call may take some time.
89+
/// >
90+
/// > Arguments:
91+
/// > 1. hash_type (string, optional, default="hash_serialized_3") Which UTXO set hash should be calculated. Options: 'hash_serialized_3' (the legacy algorithm), 'muhash', 'none'.
92+
/// > 2. hash_or_height (string or numeric, optional, default=the current best block) The block hash or height of the target height (only available with coinstatsindex).
93+
/// > 3. use_index (boolean, optional, default=true) Use coinstatsindex, if available.
8994
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
9095
#[cfg_attr(feature = "serde-deny-unknown-fields", serde(deny_unknown_fields))]
9196
pub struct GetTxOutSetInfo {
@@ -94,21 +99,59 @@ pub struct GetTxOutSetInfo {
9499
/// The hash of the block at the tip of the chain.
95100
#[serde(rename = "bestblock")]
96101
pub best_block: String,
97-
/// The number of transactions with unspent outputs.
98-
pub transactions: i64,
102+
/// The number of transactions with unspent outputs (not available when coinstatsindex is used).
103+
pub transactions: Option<i64>,
99104
/// The number of unspent transaction outputs.
100105
#[serde(rename = "txouts")]
101106
pub tx_outs: i64,
102107
/// A meaningless metric for UTXO set size.
103108
#[serde(rename = "bogosize")]
104109
pub bogo_size: i64,
105-
/// The estimated size of the chainstate on disk.
106-
pub disk_size: i64,
107-
/// The total amount.
108-
pub total_amount: f64,
109110
/// The serialized hash (only present if 'hash_serialized_3' hash_type is chosen).
110111
/// v26 and later only.
111112
pub hash_serialized_3: Option<String>,
113+
/// The estimated size of the chainstate on disk (not available when coinstatsindex is used).
114+
pub disk_size: Option<i64>,
115+
/// The total amount.
116+
pub total_amount: f64,
117+
/// The serialized hash (only present if 'muhash' hash_type is chosen)
118+
pub muhash: Option<String>,
119+
/// The total amount of coins permanently excluded from the UTXO set (only available if coinstatsindex is used).
120+
pub total_unspendable_amount: Option<f64>,
121+
/// Info on amounts in the block at this block height (only available if coinstatsindex is used).
122+
pub block_info: Option<BlockInfo>,
123+
}
124+
125+
/// Detailed block-level info returned by `gettxoutsetinfo` when coinstatsindex is enabled.
126+
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
127+
#[cfg_attr(feature = "serde-deny-unknown-fields", serde(deny_unknown_fields))]
128+
pub struct BlockInfo {
129+
/// Total amount of all prevouts spent in this block.
130+
#[serde(rename = "prevout_spent")]
131+
pub prevout_spent: f64,
132+
/// Coinbase subsidy amount of this block.
133+
pub coinbase: f64,
134+
/// Total amount of new outputs created by this block.
135+
#[serde(rename = "new_outputs_ex_coinbase")]
136+
pub new_outputs_ex_coinbase: f64,
137+
/// Total amount of unspendable outputs created in this block.
138+
pub unspendable: f64,
139+
/// Detailed view of unspendable categories.
140+
pub unspendables: Unspendables,
141+
}
142+
143+
/// Categories of unspendable amounts returned inside `BlockInfo`.
144+
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
145+
#[cfg_attr(feature = "serde-deny-unknown-fields", serde(deny_unknown_fields))]
146+
pub struct Unspendables {
147+
/// The unspendable amount of the Genesis block subsidy.
148+
pub genesis_block: f64,
149+
/// Transactions overridden by duplicates (no longer possible with BIP30).
150+
pub bip30: f64,
151+
/// Amounts sent to scripts that are unspendable (for example OP_RETURN outputs).
152+
pub scripts: f64,
153+
/// Fee rewards that miners did not claim in their coinbase transaction.
154+
pub unclaimed_rewards: f64,
112155
}
113156

114157
/// Result of JSON-RPC method `loadtxoutset`.

types/src/v26/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,8 +259,9 @@ mod wallet;
259259
#[doc(inline)]
260260
pub use self::{
261261
blockchain::{
262-
ChainState, DumpTxOutSet, DumpTxOutSetError, GetChainStates, GetChainStatesError,
263-
GetTxOutSetInfo, GetTxOutSetInfoError, LoadTxOutSet, LoadTxOutSetError,
262+
BlockInfo, ChainState, DumpTxOutSet, DumpTxOutSetError, GetChainStates,
263+
GetChainStatesError, GetTxOutSetInfo, GetTxOutSetInfoError, LoadTxOutSet,
264+
LoadTxOutSetError, Unspendables,
264265
},
265266
control::Logging,
266267
hidden::{GetRawAddrMan, RawAddrManEntry},

types/src/v27/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -333,14 +333,14 @@ pub use crate::{
333333
ScanTxOutSetUnspent, TestMempoolAccept, TestMempoolAcceptError,
334334
},
335335
v26::{
336-
AddrManInfoNetwork, ChainState, CreateWallet, DescriptorProcessPsbt,
336+
AddrManInfoNetwork, BlockInfo, ChainState, CreateWallet, DescriptorProcessPsbt,
337337
DescriptorProcessPsbtError, DumpTxOutSet, DumpTxOutSetError, GetAddrManInfo, GetBalances,
338338
GetBalancesError, GetChainStates, GetChainStatesError, GetPeerInfo, GetRawAddrMan,
339339
GetTransaction, GetTransactionError, GetTxOutSetInfo, GetTxOutSetInfoError, GetWalletInfo,
340340
GetWalletInfoError, GetWalletInfoScanning, LastProcessedBlock, LastProcessedBlockError,
341341
LoadTxOutSet, LoadTxOutSetError, LoadWallet, Logging, PeerInfo, RawAddrManEntry,
342342
SubmitPackage, SubmitPackageError, SubmitPackageTxResult, SubmitPackageTxResultError,
343-
SubmitPackageTxResultFees, SubmitPackageTxResultFeesError, UnloadWallet, WalletProcessPsbt,
344-
WalletProcessPsbtError,
343+
SubmitPackageTxResultFees, SubmitPackageTxResultFeesError, UnloadWallet, Unspendables,
344+
WalletProcessPsbt, WalletProcessPsbtError,
345345
},
346346
};

types/src/v28/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,12 +354,12 @@ pub use crate::{
354354
TestMempoolAcceptError,
355355
},
356356
v26::{
357-
AddrManInfoNetwork, ChainState, CreateWallet, DescriptorProcessPsbt,
357+
AddrManInfoNetwork, BlockInfo, ChainState, CreateWallet, DescriptorProcessPsbt,
358358
DescriptorProcessPsbtError, DumpTxOutSet, DumpTxOutSetError, GetAddrManInfo, GetBalances,
359359
GetBalancesError, GetChainStates, GetChainStatesError, GetPeerInfo, GetTransactionError,
360360
GetTxOutSetInfo, GetTxOutSetInfoError, GetWalletInfo, GetWalletInfoError,
361361
GetWalletInfoScanning, LastProcessedBlock, LastProcessedBlockError, LoadTxOutSet,
362-
LoadTxOutSetError, LoadWallet, PeerInfo, UnloadWallet, WalletProcessPsbt,
362+
LoadTxOutSetError, LoadWallet, PeerInfo, UnloadWallet, Unspendables, WalletProcessPsbt,
363363
WalletProcessPsbtError,
364364
},
365365
v27::{GetPrioritisedTransactions, PrioritisedTransaction},

0 commit comments

Comments
 (0)