Skip to content

Commit d22bad7

Browse files
authored
Merge pull request #6394 from fdefelici/refactor/btc-rpc-rollout-step2-6387
refactor: BitcoinRegtestController RPC roll out (STEP 1)
2 parents 54a7412 + 34a66e1 commit d22bad7

File tree

7 files changed

+86
-127
lines changed

7 files changed

+86
-127
lines changed

stacks-node/src/burnchains/bitcoin_regtest_controller.rs

Lines changed: 62 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ use url::Url;
8080
use super::super::operations::BurnchainOpSigner;
8181
use super::super::Config;
8282
use super::{BurnchainController, BurnchainTip, Error as BurnchainControllerError};
83+
use crate::burnchains::rpc::bitcoin_rpc_client::BitcoinRpcClient;
8384

8485
/// The number of bitcoin blocks that can have
8586
/// passed since the UTXO cache was last refreshed before
@@ -101,6 +102,7 @@ pub struct BitcoinRegtestController {
101102
burnchain_config: Option<Burnchain>,
102103
ongoing_block_commit: Option<OngoingBlockCommit>,
103104
should_keep_running: Option<Arc<AtomicBool>>,
105+
rpc_client: BitcoinRpcClient,
104106
}
105107

106108
#[derive(Clone)]
@@ -348,6 +350,9 @@ impl BitcoinRegtestController {
348350
should_keep_running: should_keep_running.clone(),
349351
};
350352

353+
let rpc_client = BitcoinRpcClient::from_stx_config(&config)
354+
.expect("unable to instantiate the RPC client!");
355+
351356
Self {
352357
use_coordinator: coordinator_channel,
353358
config,
@@ -358,6 +363,7 @@ impl BitcoinRegtestController {
358363
burnchain_config: burnchain,
359364
ongoing_block_commit: None,
360365
should_keep_running,
366+
rpc_client,
361367
}
362368
}
363369

@@ -394,6 +400,9 @@ impl BitcoinRegtestController {
394400
should_keep_running: None,
395401
};
396402

403+
let rpc_client = BitcoinRpcClient::from_stx_config(&config)
404+
.expect("unable to instantiate the RPC client!");
405+
397406
Self {
398407
use_coordinator: None,
399408
config,
@@ -404,6 +413,7 @@ impl BitcoinRegtestController {
404413
burnchain_config: None,
405414
ongoing_block_commit: None,
406415
should_keep_running: None,
416+
rpc_client,
407417
}
408418
}
409419

@@ -1520,11 +1530,7 @@ impl BitcoinRegtestController {
15201530
for txid in ongoing_op.txids.iter() {
15211531
// check if ongoing_op is in the burnchain_db *or* has been confirmed via the bitcoin RPC
15221532
let mined_op = burnchain_db.find_burnchain_op(&self.indexer, txid);
1523-
let ongoing_tx_confirmed = mined_op.is_some()
1524-
|| matches!(
1525-
BitcoinRPCRequest::check_transaction_confirmed(&self.config, txid),
1526-
Ok(true)
1527-
);
1533+
let ongoing_tx_confirmed = mined_op.is_some() || self.is_transaction_confirmed(txid);
15281534

15291535
test_debug!("Ongoing Tx confirmed: {ongoing_tx_confirmed} - TXID: {txid}");
15301536
if ongoing_tx_confirmed {
@@ -2147,6 +2153,26 @@ impl BitcoinRegtestController {
21472153
}
21482154
}
21492155
}
2156+
2157+
/// Checks whether a transaction has been confirmed by the burnchain
2158+
///
2159+
/// # Arguments
2160+
///
2161+
/// * `txid` - The transaction ID to check (in big-endian order)
2162+
///
2163+
/// # Returns
2164+
///
2165+
/// * `true` if the transaction is confirmed (has at least one confirmation).
2166+
/// * `false` if the transaction is unconfirmed or could not be found.
2167+
pub fn is_transaction_confirmed(&self, txid: &Txid) -> bool {
2168+
match self.rpc_client.get_transaction(txid) {
2169+
Ok(info) => info.confirmations > 0,
2170+
Err(e) => {
2171+
error!("Bitcoin RPC failure: checking tx confirmation {e:?}");
2172+
false
2173+
}
2174+
}
2175+
}
21502176
}
21512177

21522178
impl BurnchainController for BitcoinRegtestController {
@@ -2496,32 +2522,6 @@ impl BitcoinRPCRequest {
24962522
Ok(res.get("result").unwrap().as_str().unwrap().to_string())
24972523
}
24982524

2499-
/// Was a given transaction ID confirmed by the burnchain?
2500-
pub fn check_transaction_confirmed(config: &Config, txid: &Txid) -> RPCResult<bool> {
2501-
let payload = BitcoinRPCRequest {
2502-
method: "gettransaction".to_string(),
2503-
params: vec![format!("{txid}").into()],
2504-
id: "stacks".to_string(),
2505-
jsonrpc: "2.0".to_string(),
2506-
};
2507-
let res = BitcoinRPCRequest::send(config, payload)?;
2508-
let confirmations = res
2509-
.get("result")
2510-
.ok_or_else(|| RPCError::Parsing("No 'result' field in bitcoind RPC response".into()))?
2511-
.get("confirmations")
2512-
.ok_or_else(|| {
2513-
RPCError::Parsing("No 'confirmations' field in bitcoind RPC response".into())
2514-
})?
2515-
.as_i64()
2516-
.ok_or_else(|| {
2517-
RPCError::Parsing(
2518-
"Expected 'confirmations' field to be numeric in bitcoind RPC response".into(),
2519-
)
2520-
})?;
2521-
2522-
Ok(confirmations >= 1)
2523-
}
2524-
25252525
pub fn generate_to_address(config: &Config, num_blocks: u64, address: String) -> RPCResult<()> {
25262526
debug!("Generate {num_blocks} blocks to {address}");
25272527
let payload = BitcoinRPCRequest {
@@ -3118,8 +3118,7 @@ mod tests {
31183118
];
31193119

31203120
// test serialize_tx()
3121-
let mut config = Config::default();
3122-
config.burnchain.magic_bytes = "T3".as_bytes().into();
3121+
let config = utils::create_config();
31233122

31243123
let mut btc_controller = BitcoinRegtestController::new(config, None);
31253124
let mut utxo_set = UTXOSet {
@@ -3496,6 +3495,36 @@ mod tests {
34963495
);
34973496
}
34983497

3498+
#[test]
3499+
#[ignore]
3500+
fn test_tx_confirmed_from_utxo_ok() {
3501+
if env::var("BITCOIND_TEST") != Ok("1".into()) {
3502+
return;
3503+
}
3504+
3505+
let miner_pubkey = utils::create_miner1_pubkey();
3506+
3507+
let mut config = utils::create_config();
3508+
config.burnchain.local_mining_public_key = Some(miner_pubkey.to_hex());
3509+
3510+
let mut btcd_controller = BitcoinCoreController::from_stx_config(&config);
3511+
btcd_controller
3512+
.start_bitcoind()
3513+
.expect("bitcoind should be started!");
3514+
3515+
let btc_controller = BitcoinRegtestController::new(config.clone(), None);
3516+
3517+
btc_controller.bootstrap_chain(101);
3518+
let utxos = btc_controller.get_all_utxos(&miner_pubkey);
3519+
assert_eq!(1, utxos.len(), "One UTXO should be confirmed!");
3520+
3521+
let txid = Txid::from_bitcoin_tx_hash(&utxos[0].txid);
3522+
assert!(
3523+
btc_controller.is_transaction_confirmed(&txid),
3524+
"UTXO tx should be confirmed!"
3525+
);
3526+
}
3527+
34993528
/// Tests related to Leader Block Commit operation
35003529
mod leader_commit_op {
35013530
use super::*;

stacks-node/src/burnchains/rpc/bitcoin_rpc_client/mod.rs

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -161,14 +161,13 @@ pub struct ListUnspentResponse {
161161
pub confirmations: u32,
162162
}
163163

164-
/// Deserializes a JSON string (hex-encoded in big-endian order) into [`Txid`],
165-
/// storing bytes in little-endian order
164+
/// Deserializes a JSON string (hex-encoded in big-endian order) into [`Txid`].
166165
fn deserialize_string_to_txid<'de, D>(deserializer: D) -> Result<Txid, D::Error>
167166
where
168167
D: Deserializer<'de>,
169168
{
170169
let hex_str: String = Deserialize::deserialize(deserializer)?;
171-
let txid = Txid::from_bitcoin_hex(&hex_str).map_err(serde::de::Error::custom)?;
170+
let txid = Txid::from_hex(&hex_str).map_err(serde::de::Error::custom)?;
172171
Ok(txid)
173172
}
174173

@@ -305,14 +304,14 @@ impl<'de> Deserialize<'de> for GenerateToAddressResponse {
305304
/// Response mainly used as deserialization wrapper for [`Txid`]
306305
struct TxidWrapperResponse(pub Txid);
307306

308-
/// Deserializes a JSON string (hex-encoded, big-endian) into [`Txid`] and wrap it into [`TxidWrapperResponse`]
307+
/// Deserializes a JSON string (hex-encoded in big-endian order) into [`Txid`] and wrap it into [`TxidWrapperResponse`]
309308
impl<'de> Deserialize<'de> for TxidWrapperResponse {
310309
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
311310
where
312311
D: Deserializer<'de>,
313312
{
314313
let hex_str: String = Deserialize::deserialize(deserializer)?;
315-
let txid = Txid::from_bitcoin_hex(&hex_str).map_err(serde::de::Error::custom)?;
314+
let txid = Txid::from_hex(&hex_str).map_err(serde::de::Error::custom)?;
316315
Ok(TxidWrapperResponse(txid))
317316
}
318317
}
@@ -558,21 +557,19 @@ impl BitcoinRpcClient {
558557
/// hex-encoded transaction, and other metadata for a transaction tracked by the wallet.
559558
///
560559
/// # Arguments
561-
/// * `txid` - The transaction ID (as [`Txid`]) to query,
562-
/// which is intended to be created with [`Txid::from_bitcoin_hex`],
563-
/// or an analogous process.
560+
/// * `txid` - The transaction ID (as [`Txid`]) to query (in big-endian order).
564561
///
565562
/// # Returns
566563
/// A [`GetTransactionResponse`] containing detailed metadata for the specified transaction.
567564
///
568565
/// # Availability
569566
/// - **Since**: Bitcoin Core **v0.10.0**.
570567
pub fn get_transaction(&self, txid: &Txid) -> BitcoinRpcClientResult<GetTransactionResponse> {
571-
let btc_txid = txid.to_bitcoin_hex();
572-
573-
Ok(self
574-
.wallet_ep
575-
.send(&self.client_id, "gettransaction", vec![btc_txid.into()])?)
568+
Ok(self.wallet_ep.send(
569+
&self.client_id,
570+
"gettransaction",
571+
vec![txid.to_hex().into()],
572+
)?)
576573
}
577574

578575
/// Broadcasts a raw transaction to the Bitcoin network.
@@ -591,7 +588,7 @@ impl BitcoinRpcClient {
591588
/// - If `None`, defaults to `0`, meaning burning is not allowed.
592589
///
593590
/// # Returns
594-
/// A [`Txid`] as a transaction ID (storing internally bytes in **little-endian** order)
591+
/// A [`Txid`] as a transaction ID (in big-endian order)
595592
///
596593
/// # Availability
597594
/// - **Since**: Bitcoin Core **v0.7.0**.

stacks-node/src/burnchains/rpc/bitcoin_rpc_client/test_utils.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,21 +143,18 @@ impl BitcoinRpcClient {
143143
/// Retrieves and deserializes a raw Bitcoin transaction by its ID.
144144
///
145145
/// # Arguments
146-
/// * `txid` - Transaction ID to fetch, which is intended to be created with [`Txid::from_bitcoin_hex`],
147-
/// or an analogous process.
146+
/// * `txid` - The transaction ID (as [`Txid`]) to query (in big-endian order).
148147
///
149148
/// # Returns
150149
/// A [`Transaction`] struct representing the decoded transaction.
151150
///
152151
/// # Availability
153152
/// - **Since**: Bitcoin Core **v0.7.0**.
154153
pub fn get_raw_transaction(&self, txid: &Txid) -> BitcoinRpcClientResult<Transaction> {
155-
let btc_txid = txid.to_bitcoin_hex();
156-
157154
let raw_hex = self.global_ep.send::<String>(
158155
&self.client_id,
159156
"getrawtransaction",
160-
vec![btc_txid.to_string().into()],
157+
vec![txid.to_hex().into()],
161158
)?;
162159
Ok(deserialize_hex(&raw_hex)?)
163160
}
@@ -249,7 +246,7 @@ impl BitcoinRpcClient {
249246
/// * `amount` - Amount to send in BTC (not in satoshis).
250247
///
251248
/// # Returns
252-
/// A [`Txid`] struct representing the transaction ID (storing internally bytes in **little-endian** order)
249+
/// A [`Txid`] as a transaction ID (in big-endian order)
253250
///
254251
/// # Availability
255252
/// - **Since**: Bitcoin Core **v0.1.0**.

stacks-node/src/burnchains/rpc/bitcoin_rpc_client/tests.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ fn test_list_unspent_ok() {
351351
assert_eq!(0, utxo.vout);
352352
assert_eq!(expected_address, utxo.address.to_string());
353353
assert_eq!(6, utxo.confirmations);
354-
assert_eq!(expected_txid_str, utxo.txid.to_bitcoin_hex(),);
354+
assert_eq!(expected_txid_str, utxo.txid.to_hex(),);
355355
assert_eq!(expected_script_hex, format!("{:x}", utxo.script_pub_key),);
356356
}
357357

@@ -469,7 +469,7 @@ fn test_get_transaction_ok() {
469469

470470
let client = utils::setup_client(&server);
471471

472-
let txid = Txid::from_bitcoin_hex(&txid_hex).unwrap();
472+
let txid = Txid::from_hex(&txid_hex).unwrap();
473473
let info = client.get_transaction(&txid).expect("Should be ok!");
474474
assert_eq!(6, info.confirmations);
475475
}
@@ -503,7 +503,7 @@ fn test_get_raw_transaction_ok() {
503503

504504
let client = utils::setup_client(&server);
505505

506-
let txid = Txid::from_bitcoin_hex(txid_hex).unwrap();
506+
let txid = Txid::from_hex(txid_hex).unwrap();
507507
let raw_tx = client.get_raw_transaction(&txid).expect("Should be ok!");
508508
assert_eq!(txid_hex, raw_tx.txid().to_string());
509509
assert_eq!(expected_tx_hex, serialize_hex(&raw_tx).unwrap());
@@ -625,7 +625,7 @@ fn test_send_raw_transaction_ok_with_defaults() {
625625
let txid = client
626626
.send_raw_transaction(&raw_tx, None, None)
627627
.expect("Should work!");
628-
assert_eq!(expected_txid, txid.to_bitcoin_hex());
628+
assert_eq!(expected_txid, txid.to_hex());
629629
}
630630

631631
#[test]
@@ -661,7 +661,7 @@ fn test_send_raw_transaction_ok_with_custom_params() {
661661
let txid = client
662662
.send_raw_transaction(&raw_tx, Some(0.0), Some(5_000))
663663
.expect("Should work!");
664-
assert_eq!(expected_txid, txid.to_bitcoin_hex());
664+
assert_eq!(expected_txid, txid.to_hex());
665665
}
666666

667667
#[test]
@@ -882,7 +882,7 @@ fn test_send_to_address_ok() {
882882
let txid = client
883883
.send_to_address(&address, amount)
884884
.expect("Should be ok!");
885-
assert_eq!(expected_txid_str, txid.to_bitcoin_hex());
885+
assert_eq!(expected_txid_str, txid.to_hex());
886886
}
887887

888888
#[test]

stacks-node/src/tests/bitcoin_rpc_integrations.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,7 @@ fn test_get_raw_transaction_ok() {
564564
.get_raw_transaction(&txid)
565565
.expect("get raw transaction ok!");
566566

567-
assert_eq!(txid.to_bitcoin_hex(), raw_tx.txid().to_string());
567+
assert_eq!(txid.to_hex(), raw_tx.txid().to_string());
568568
}
569569

570570
#[ignore]
@@ -795,5 +795,5 @@ fn test_send_raw_transaction_rebroadcast_ok() {
795795
.send_raw_transaction(&raw_tx, None, None)
796796
.expect("send raw transaction (rebroadcast) ok!");
797797

798-
assert_eq!(txid.to_bitcoin_hex(), raw_tx.txid().to_string());
798+
assert_eq!(txid.to_hex(), raw_tx.txid().to_string());
799799
}

stacks-node/src/tests/signer/v0.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@ use tracing_subscriber::prelude::*;
104104
use tracing_subscriber::{fmt, EnvFilter};
105105

106106
use super::SignerTest;
107-
use crate::burnchains::bitcoin_regtest_controller::BitcoinRPCRequest;
108107
use crate::event_dispatcher::{
109108
EventObserver, MinedNakamotoBlockEvent, TEST_SKIP_BLOCK_ANNOUNCEMENT,
110109
};
@@ -4032,8 +4031,7 @@ fn tx_replay_failsafe() {
40324031

40334032
// Wait for the block commit re-broadcast to be confirmed
40344033
wait_for(10, || {
4035-
let is_confirmed =
4036-
BitcoinRPCRequest::check_transaction_confirmed(&conf, &commit_txid.unwrap()).unwrap();
4034+
let is_confirmed = btc_controller.is_transaction_confirmed(&commit_txid.unwrap());
40374035
Ok(is_confirmed)
40384036
})
40394037
.expect("Timed out waiting for transaction to be confirmed");
@@ -4317,8 +4315,7 @@ fn tx_replay_disagreement() {
43174315

43184316
// Wait for the block commit re-broadcast to be confirmed
43194317
wait_for(10, || {
4320-
let is_confirmed =
4321-
BitcoinRPCRequest::check_transaction_confirmed(&conf, &commit_txid.unwrap()).unwrap();
4318+
let is_confirmed = btc_controller.is_transaction_confirmed(&commit_txid.unwrap());
43224319
Ok(is_confirmed)
43234320
})
43244321
.expect("Timed out waiting for transaction to be confirmed");

0 commit comments

Comments
 (0)