Skip to content

Commit 881e5fd

Browse files
committed
crc: add utxo address field and improve test scenarios, #6250
1 parent 59a247f commit 881e5fd

File tree

3 files changed

+121
-10
lines changed

3 files changed

+121
-10
lines changed

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use stacks::burnchains::bitcoin::address::BitcoinAddress;
3131
use stacks::burnchains::Txid;
3232
use stacks::config::Config;
3333
use stacks::types::chainstate::BurnchainHeaderHash;
34+
use stacks::types::Address;
3435
use stacks::util::hash::hex_bytes;
3536
use stacks_common::deps_common::bitcoin::blockdata::script::Script;
3637
use stacks_common::deps_common::bitcoin::blockdata::transaction::Transaction;
@@ -144,6 +145,9 @@ pub struct ListUnspentResponse {
144145
pub txid: Txid,
145146
/// The index of the output in the transaction.
146147
pub vout: u32,
148+
/// The Bitcoin destination address
149+
#[serde(deserialize_with = "deserialize_string_to_bitcoin_address")]
150+
pub address: BitcoinAddress,
147151
/// The script associated with the output.
148152
#[serde(
149153
rename = "scriptPubKey",
@@ -168,6 +172,30 @@ where
168172
Ok(txid)
169173
}
170174

175+
/// Deserializes a JSON string into [`BitcoinAddress`]
176+
fn deserialize_string_to_bitcoin_address<'de, D>(
177+
deserializer: D,
178+
) -> Result<BitcoinAddress, D::Error>
179+
where
180+
D: Deserializer<'de>,
181+
{
182+
let addr_str: String = Deserialize::deserialize(deserializer)?;
183+
if addr_str.starts_with("bcrt") {
184+
//Currently BitcoinAddress doesn't manage Regtest HRP
185+
return Err(serde::de::Error::custom(
186+
"BitcoinAddress cannot manage Regtest HRP ('bcrt')",
187+
));
188+
}
189+
190+
if let Some(addr) = BitcoinAddress::from_string(&addr_str) {
191+
Ok(addr)
192+
} else {
193+
Err(serde::de::Error::custom(
194+
"BitcoinAddress failed to create from string",
195+
))
196+
}
197+
}
198+
171199
/// Deserializes a JSON string into [`Script`]
172200
fn deserialize_string_to_script<'de, D>(deserializer: D) -> Result<Script, D::Error>
173201
where

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ fn test_list_wallets_ok() {
290290
fn test_list_unspent_ok() {
291291
let expected_txid_str = utils::BITCOIN_TX1_TXID_HEX;
292292
let expected_script_hex = utils::BITCOIN_UTXO_SCRIPT_HEX;
293+
let expected_address = utils::BITCOIN_ADDRESS_LEGACY_STR;
293294

294295
let expected_request = json!({
295296
"jsonrpc": "2.0",
@@ -312,6 +313,7 @@ fn test_list_unspent_ok() {
312313
"result": [{
313314
"txid": expected_txid_str,
314315
"vout": 0,
316+
"address": expected_address,
315317
"scriptPubKey": expected_script_hex,
316318
"amount": 0.00001,
317319
"confirmations": 6
@@ -347,6 +349,7 @@ fn test_list_unspent_ok() {
347349
let utxo = &result[0];
348350
assert_eq!(1_000, utxo.amount);
349351
assert_eq!(0, utxo.vout);
352+
assert_eq!(expected_address, utxo.address.to_string());
350353
assert_eq!(6, utxo.confirmations);
351354
assert_eq!(expected_txid_str, utxo.txid.to_bitcoin_hex(),);
352355
assert_eq!(expected_script_hex, format!("{:x}", utxo.script_pub_key),);

stacks-node/src/tests/bitcoin_rpc_integrations.rs

Lines changed: 90 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ fn test_generate_to_address_ok() {
322322

323323
#[ignore]
324324
#[test]
325-
fn test_list_unspent_ok() {
325+
fn test_list_unspent_one_address_ok() {
326326
if env::var("BITCOIND_TEST") != Ok("1".into()) {
327327
return;
328328
}
@@ -341,24 +341,104 @@ fn test_list_unspent_ok() {
341341
.get_new_address(None, Some(AddressType::Legacy))
342342
.expect("Should work!");
343343

344-
let utxos = client
344+
let no_utxos = client
345345
.list_unspent(None, None, None, Some(false), Some(1), Some(10))
346-
.expect("list_unspent should be ok!");
347-
assert_eq!(0, utxos.len());
346+
.expect("list_unspent empty should be ok!");
347+
assert_eq!(0, no_utxos.len());
348348

349349
_ = client
350350
.generate_to_address(102, &address)
351351
.expect("generate to address ok!");
352352

353-
let utxos = client
353+
let all_utxos = client
354354
.list_unspent(None, None, None, Some(false), Some(1), Some(10))
355-
.expect("list_unspent should be ok!");
356-
assert_eq!(2, utxos.len());
355+
.expect("all list_unspent should be ok!");
356+
assert_eq!(2, all_utxos.len());
357+
assert_eq!(address, all_utxos[0].address);
358+
assert_eq!(address, all_utxos[1].address);
359+
360+
let addr_utxos = client
361+
.list_unspent(
362+
None,
363+
None,
364+
Some(&[&address]),
365+
Some(false),
366+
Some(1),
367+
Some(10),
368+
)
369+
.expect("list_unspent per address should be ok!");
370+
assert_eq!(2, addr_utxos.len());
371+
assert_eq!(address, addr_utxos[0].address);
372+
assert_eq!(address, addr_utxos[1].address);
357373

358-
let utxos = client
374+
let max1_utxos = client
359375
.list_unspent(None, None, None, Some(false), Some(1), Some(1))
360-
.expect("list_unspent should be ok!");
361-
assert_eq!(1, utxos.len());
376+
.expect("list_unspent per address and max count should be ok!");
377+
assert_eq!(1, max1_utxos.len());
378+
assert_eq!(address, max1_utxos[0].address);
379+
}
380+
381+
#[ignore]
382+
#[test]
383+
fn test_list_unspent_two_addresses_ok() {
384+
if env::var("BITCOIND_TEST") != Ok("1".into()) {
385+
return;
386+
}
387+
388+
let mut config = utils::create_stx_config();
389+
config.burnchain.wallet_name = "my_wallet".to_string();
390+
391+
let mut btcd_controller = BitcoinCoreController::new(config.clone());
392+
btcd_controller
393+
.start_bitcoind()
394+
.expect("bitcoind should be started!");
395+
396+
let client = BitcoinRpcClient::from_stx_config(&config).expect("Client creation ok!");
397+
client.create_wallet("my_wallet", Some(false)).expect("OK");
398+
let address1 = client
399+
.get_new_address(None, Some(AddressType::Legacy))
400+
.expect("address 1 ok!");
401+
402+
let address2 = client
403+
.get_new_address(None, Some(AddressType::Legacy))
404+
.expect("address 2 ok!");
405+
406+
_ = client
407+
.generate_to_address(2, &address1)
408+
.expect("generate to address 1 ok!");
409+
_ = client
410+
.generate_to_address(101, &address2)
411+
.expect("generate to address 2 ok!");
412+
413+
let all_utxos = client
414+
.list_unspent(None, None, None, Some(false), None, None)
415+
.expect("all list_unspent should be ok!");
416+
assert_eq!(3, all_utxos.len());
417+
418+
let addr1_utxos = client
419+
.list_unspent(None, None, Some(&[&address1]), Some(false), None, None)
420+
.expect("list_unspent per address1 should be ok!");
421+
assert_eq!(2, addr1_utxos.len());
422+
assert_eq!(address1, addr1_utxos[0].address);
423+
assert_eq!(address1, addr1_utxos[1].address);
424+
425+
let addr2_utxos = client
426+
.list_unspent(None, None, Some(&[&address2]), Some(false), None, None)
427+
.expect("list_unspent per address2 should be ok!");
428+
assert_eq!(1, addr2_utxos.len());
429+
assert_eq!(address2, addr2_utxos[0].address);
430+
431+
let all2_utxos = client
432+
.list_unspent(
433+
None,
434+
None,
435+
Some(&[&address1, &address2]),
436+
Some(false),
437+
None,
438+
None,
439+
)
440+
.expect("all list_unspent for both addresses should be ok!");
441+
assert_eq!(3, all2_utxos.len());
362442
}
363443

364444
#[ignore]

0 commit comments

Comments
 (0)