Skip to content
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
30 changes: 28 additions & 2 deletions integration_test/tests/raw_transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,8 +382,34 @@ fn raw_transactions__submit_package__modelled() {
}

#[test]
#[cfg(feature = "TODO")]
fn raw_transactions__test_mempool_accept__modelled() {}
fn raw_transactions__test_mempool_accept__modelled() {
let node = Node::with_wallet(Wallet::Default, &[]);
node.fund_wallet();
let tx = create_a_raw_transaction(&node);

// Sign (but don't broadcast).
let signed: SignRawTransaction = node
.client
.sign_raw_transaction_with_wallet(&tx)
.expect("signrawtransactionwithwallet");
let signed_model: mtype::SignRawTransaction = signed
.into_model()
.expect("SignRawTransaction into model");
let signed_tx = signed_model.tx;

// Call testmempoolaccept with the valid (not yet broadcast) transaction.
let json: TestMempoolAccept = node
.client
.test_mempool_accept(&[signed_tx.clone()])
.expect("testmempoolaccept");
let model: mtype::TestMempoolAccept = json
.into_model()
.expect("TestMempoolAccept into model");
assert_eq!(model.results.len(), 1);
let res = &model.results[0];
assert_eq!(res.txid, signed_tx.compute_txid());
assert!(res.allowed, "fresh signed tx should be allowed");
}

#[test]
#[cfg(not(feature = "v17"))] // utxoupdatepsbt was added in v0.18.
Expand Down
6 changes: 3 additions & 3 deletions types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,11 @@ impl std::error::Error for NumericError {}

/// Converts `fee_rate` in BTC/kB to `FeeRate`.
fn btc_per_kb(btc_per_kb: f64) -> Result<Option<FeeRate>, ParseAmountError> {
let btc_per_byte = btc_per_kb / 1000_f64;
let sats_per_byte = Amount::from_btc(btc_per_byte)?;
let sats_per_kb = Amount::from_btc(btc_per_kb)?;
let sats_per_byte = sats_per_kb.to_sat() / 1000;

// Virtual bytes equal bytes before segwit.
let rate = FeeRate::from_sat_per_vb(sats_per_byte.to_sat());
let rate = FeeRate::from_sat_per_vb(sats_per_byte);

Ok(rate)
}
Expand Down
6 changes: 3 additions & 3 deletions types/src/model/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ pub use self::{
AnalyzePsbt, AnalyzePsbtInput, AnalyzePsbtInputMissing, CombinePsbt, CombineRawTransaction,
ConvertToPsbt, CreatePsbt, CreateRawTransaction, DecodePsbt, DecodeRawTransaction,
DecodeScript, DescriptorProcessPsbt, FinalizePsbt, FundRawTransaction, GetRawTransaction,
GetRawTransactionVerbose, JoinPsbts, MempoolAcceptance, SendRawTransaction, SignFail,
SignRawTransaction, SubmitPackage, SubmitPackageTxResult, SubmitPackageTxResultFees,
TestMempoolAccept, UtxoUpdatePsbt,
GetRawTransactionVerbose, JoinPsbts, MempoolAcceptance, MempoolAcceptanceFees,
SendRawTransaction, SignFail, SignRawTransaction, SubmitPackage, SubmitPackageTxResult,
SubmitPackageTxResultFees, TestMempoolAccept, UtxoUpdatePsbt,
},
util::{
CreateMultisig, DeriveAddresses, DeriveAddressesMultipath, EstimateSmartFee,
Expand Down
23 changes: 22 additions & 1 deletion types/src/model/raw_transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,16 +272,37 @@ pub struct TestMempoolAccept {
pub results: Vec<MempoolAcceptance>,
}

/// Represents a single mempool acceptance test result.
/// Models a single mempool acceptance test result. Returned as part of `testmempoolaccept`.
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct MempoolAcceptance {
/// The transaction ID.
pub txid: Txid,
/// The transaction witness hash in hex.
pub wtxid: Option<Wtxid>,
/// If the mempool allows this transaction to be inserted.
pub allowed: bool,
/// Virtual transaction size as defined in BIP 141 (only present when 'allowed' is true).
pub vsize: Option<u32>,
/// Transaction fee in BTC (only present if 'allowed' is true).
pub fees: Option<MempoolAcceptanceFees>,
/// Rejection string (only present when 'allowed' is false).
pub reject_reason: Option<String>,
/// Rejection details (only present when 'allowed' is false and rejection details exist)
pub reject_details: Option<String>,
}

/// Models the fees field. Returned as part of `testmempoolaccept`.
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct MempoolAcceptanceFees {
/// Transaction fee in BTC.
pub base: Amount,
/// The effective feerate in BTC per KvB. May differ from the base feerate if, for example, there
/// are modified fees from `prioritisetransaction` or a package feerate was used.
pub effective_feerate: Option<FeeRate>,
/// Transactions whose fees and vsizes are included in `effective_feerate`.
pub effective_includes: Option<Vec<Wtxid>>,
}

/// Models the result of JSON-RPC method `utxoupdatepsbt;`.
Expand Down
2 changes: 1 addition & 1 deletion types/src/v17/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@
//! | sendrawtransaction | version + model | |
//! | signrawtransaction | version + model | |
//! | signrawtransactionwithkey | version + model | |
//! | testmempoolaccept | version + model | UNTESTED |
//! | testmempoolaccept | version + model | |
//!
//! </details>
//!
Expand Down
6 changes: 5 additions & 1 deletion types/src/v17/raw_transactions/into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ impl SignFail {
impl TestMempoolAccept {
/// Converts version specific type to a version nonspecific, more strongly typed type.
pub fn into_model(self) -> Result<model::TestMempoolAccept, hex::HexToArrayError> {
let results = self.results.into_iter().map(|r| r.into_model()).collect::<Result<_, _>>()?;
let results = self.0.into_iter().map(|r| r.into_model()).collect::<Result<_, _>>()?;

Ok(model::TestMempoolAccept { results })
}
Expand All @@ -462,8 +462,12 @@ impl MempoolAcceptance {

Ok(model::MempoolAcceptance {
txid,
wtxid: None, // v22 and later only.
allowed: self.allowed,
vsize: None, // v21 and later only.
fees: None, // v21 and later only.
reject_reason: self.reject_reason,
reject_details: None, // v29 and later only.
})
}
}
9 changes: 2 additions & 7 deletions types/src/v17/raw_transactions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,14 +451,9 @@ pub struct SignFail {
/// > 2. allowhighfees (boolean, optional, default=false) Allow high fees
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct TestMempoolAccept {
/// Array of test results for each raw transaction in the input array.
///
/// Length is exactly one for now.
pub results: Vec<MempoolAcceptance>,
}
pub struct TestMempoolAccept(pub Vec<MempoolAcceptance>);

/// Represents a single mempool acceptance test result.
/// Represents a single mempool acceptance test result, returned as part of `testmempoolaccept`.
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct MempoolAcceptance {
Expand Down
2 changes: 1 addition & 1 deletion types/src/v18/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@
//! | joinpsbts | version + model | UNTESTED |
//! | sendrawtransaction | version + model | |
//! | signrawtransactionwithkey | version + model | |
//! | testmempoolaccept | version + model | UNTESTED |
//! | testmempoolaccept | version + model | |
//! | utxoupdatepsbt | version + model | UNTESTED |
//!
//! </details>
Expand Down
2 changes: 1 addition & 1 deletion types/src/v19/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@
//! | joinpsbts | version + model | UNTESTED |
//! | sendrawtransaction | version + model | |
//! | signrawtransactionwithkey | version + model | |
//! | testmempoolaccept | version + model | UNTESTED |
//! | testmempoolaccept | version + model | |
//! | utxoupdatepsbt | version + model | UNTESTED |
//!
//! </details>
Expand Down
2 changes: 1 addition & 1 deletion types/src/v20/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@
//! | joinpsbts | version + model | UNTESTED |
//! | sendrawtransaction | version + model | |
//! | signrawtransactionwithkey | version + model | |
//! | testmempoolaccept | version + model | UNTESTED |
//! | testmempoolaccept | version + model | |
//! | utxoupdatepsbt | version + model | UNTESTED |
//!
//! </details>
Expand Down
13 changes: 8 additions & 5 deletions types/src/v21/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@
//! | joinpsbts | version + model | UNTESTED |
//! | sendrawtransaction | version + model | |
//! | signrawtransactionwithkey | version + model | |
//! | testmempoolaccept | version + model | UNTESTED |
//! | testmempoolaccept | version + model | |
//! | utxoupdatepsbt | version + model | UNTESTED |
//!
//! </details>
Expand Down Expand Up @@ -235,6 +235,7 @@
mod blockchain;
mod generating;
mod network;
mod raw_transactions;
mod util;
mod wallet;

Expand All @@ -247,6 +248,9 @@ pub use self::{
},
generating::GenerateBlock,
network::{GetNetworkInfo, GetPeerInfo, PeerInfo},
raw_transactions::{
MempoolAcceptance, MempoolAcceptanceError, TestMempoolAccept, TestMempoolAcceptError,
},
util::{GetIndexInfo, GetIndexInfoName},
wallet::{
ImportDescriptors, ImportDescriptorsResult, PsbtBumpFee, PsbtBumpFeeError, Send, SendError,
Expand Down Expand Up @@ -282,10 +286,9 @@ pub use crate::{
Locked, PruneBlockchain, RawTransactionError, RawTransactionInput, RawTransactionOutput,
RescanBlockchain, ScriptType, SendMany, SendRawTransaction, SendToAddress,
SetNetworkActive, SetTxFee, SignMessage, SignMessageWithPrivKey, SignRawTransaction,
SignRawTransactionError, SoftforkReject, TestMempoolAccept, TransactionCategory,
UploadTarget, ValidateAddress, ValidateAddressError, VerifyChain, VerifyMessage,
VerifyTxOutProof, WalletCreateFundedPsbt, WalletCreateFundedPsbtError, WalletProcessPsbt,
WitnessUtxo,
SignRawTransactionError, SoftforkReject, TransactionCategory, UploadTarget,
ValidateAddress, ValidateAddressError, VerifyChain, VerifyMessage, VerifyTxOutProof,
WalletCreateFundedPsbt, WalletCreateFundedPsbtError, WalletProcessPsbt, WitnessUtxo,
},
v18::{
ActiveCommand, AnalyzePsbt, AnalyzePsbtError, AnalyzePsbtInput, AnalyzePsbtInputMissing,
Expand Down
82 changes: 82 additions & 0 deletions types/src/v21/raw_transactions/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// SPDX-License-Identifier: CC0-1.0

use core::fmt;

use bitcoin::amount::ParseAmountError;
use bitcoin::hex;

use crate::error::write_err;
use crate::NumericError;

/// Error when converting a `TestMempoolAccept` type into the model type.
#[derive(Debug)]
pub enum TestMempoolAcceptError {
/// Conversion of one of the mempool acceptance results failed.
MempoolAcceptance(MempoolAcceptanceError),
}

impl fmt::Display for TestMempoolAcceptError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use TestMempoolAcceptError as E;

match *self {
E::MempoolAcceptance(ref e) =>
write_err!(f, "conversion of one of the mempool acceptance results failed"; e),
}
}
}

#[cfg(feature = "std")]
impl std::error::Error for TestMempoolAcceptError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use TestMempoolAcceptError as E;

match *self {
E::MempoolAcceptance(ref e) => Some(e),
}
}
}

impl From<MempoolAcceptanceError> for TestMempoolAcceptError {
fn from(e: MempoolAcceptanceError) -> Self { TestMempoolAcceptError::MempoolAcceptance(e) }
}

/// Error when converting a `MempoolAcceptance` type into the model type.
#[derive(Debug)]
pub enum MempoolAcceptanceError {
/// Conversion of a numeric field failed.
Numeric(NumericError),
/// Conversion of the `txid` field failed.
Txid(hex::HexToArrayError),
/// Conversion of the `base` fee field failed.
Base(ParseAmountError),
}

impl fmt::Display for MempoolAcceptanceError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use MempoolAcceptanceError as E;

match *self {
E::Numeric(ref e) => write_err!(f, "conversion of a numeric field failed"; e),
E::Txid(ref e) => write_err!(f, "conversion of the `txid` field failed"; e),
E::Base(ref e) => write_err!(f, "conversion of the `base` fee field failed"; e),
}
}
}

#[cfg(feature = "std")]
impl std::error::Error for MempoolAcceptanceError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use MempoolAcceptanceError as E;

match *self {
E::Numeric(ref e) => Some(e),
E::Txid(ref e) => Some(e),
E::Base(ref e) => Some(e),
}
}
}

impl From<NumericError> for MempoolAcceptanceError {
fn from(e: NumericError) -> Self { Self::Numeric(e) }
}
46 changes: 46 additions & 0 deletions types/src/v21/raw_transactions/into.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: CC0-1.0

use bitcoin::{Amount, Txid};

use super::{MempoolAcceptance, MempoolAcceptanceError, TestMempoolAccept, TestMempoolAcceptError};
use crate::model;

impl TestMempoolAccept {
/// Converts version specific type to a version nonspecific, more strongly typed type.
pub fn into_model(self) -> Result<model::TestMempoolAccept, TestMempoolAcceptError> {
let results = self.0.into_iter().map(|r| r.into_model()).collect::<Result<_, _>>()?;

Ok(model::TestMempoolAccept { results })
}
}

impl MempoolAcceptance {
/// Converts version specific type to a version nonspecific, more strongly typed type.
pub fn into_model(self) -> Result<model::MempoolAcceptance, MempoolAcceptanceError> {
use MempoolAcceptanceError as E;

let txid = self.txid.parse::<Txid>().map_err(E::Txid)?;
let vsize = self.vsize.map(|s| crate::to_u32(s, "vsize")).transpose()?;
let fees = match self.fees {
Some(s) => {
let base = Amount::from_btc(s.base).map_err(E::Base)?;
Some(model::MempoolAcceptanceFees {
base,
effective_feerate: None, // v25 and later only.
effective_includes: None, // v25 and later only.
})
}
None => None,
};

Ok(model::MempoolAcceptance {
txid,
wtxid: None, // v22 and later only.
allowed: self.allowed,
vsize,
fees,
reject_reason: self.reject_reason,
reject_details: None, // v29 and later only.
})
}
}
Loading