Skip to content

Commit 228597e

Browse files
authored
Merge pull request #5008 from stacks-network/07-25-fix_ensure_minimum_non-dust_amount_as_change_output_on_regtest
fix: ensure minimum non-dust amount as change output on regtest
2 parents 98b7d3d + 9e9aa77 commit 228597e

File tree

2 files changed

+57
-18
lines changed

2 files changed

+57
-18
lines changed

testnet/stacks-node/src/burnchains/bitcoin_regtest_controller.rs

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,11 @@ use stacks_common::util::sleep_ms;
5757
use super::super::operations::BurnchainOpSigner;
5858
use super::super::Config;
5959
use super::{BurnchainController, BurnchainTip, Error as BurnchainControllerError};
60-
use crate::config::BurnchainConfig;
60+
use crate::config::{
61+
BurnchainConfig, OP_TX_ANY_ESTIM_SIZE, OP_TX_DELEGATE_STACKS_ESTIM_SIZE,
62+
OP_TX_PRE_STACKS_ESTIM_SIZE, OP_TX_STACK_STX_ESTIM_SIZE, OP_TX_TRANSFER_STACKS_ESTIM_SIZE,
63+
OP_TX_VOTE_AGG_ESTIM_SIZE,
64+
};
6165

6266
/// The number of bitcoin blocks that can have
6367
/// passed since the UTXO cache was last refreshed before
@@ -868,6 +872,7 @@ impl BitcoinRegtestController {
868872
fee_rate,
869873
&mut utxos,
870874
signer,
875+
false,
871876
)?;
872877

873878
increment_btc_ops_sent_counter();
@@ -950,7 +955,7 @@ impl BitcoinRegtestController {
950955
utxo_to_use: Option<UTXO>,
951956
) -> Option<Transaction> {
952957
let public_key = signer.get_public_key();
953-
let max_tx_size = 230;
958+
let max_tx_size = OP_TX_TRANSFER_STACKS_ESTIM_SIZE;
954959
let (mut tx, mut utxos) = if let Some(utxo) = utxo_to_use {
955960
(
956961
Transaction {
@@ -1005,6 +1010,7 @@ impl BitcoinRegtestController {
10051010
get_satoshis_per_byte(&self.config),
10061011
&mut utxos,
10071012
signer,
1013+
false,
10081014
)?;
10091015

10101016
increment_btc_ops_sent_counter();
@@ -1032,7 +1038,7 @@ impl BitcoinRegtestController {
10321038
utxo_to_use: Option<UTXO>,
10331039
) -> Option<Transaction> {
10341040
let public_key = signer.get_public_key();
1035-
let max_tx_size = 230;
1041+
let max_tx_size = OP_TX_DELEGATE_STACKS_ESTIM_SIZE;
10361042

10371043
let (mut tx, mut utxos) = if let Some(utxo) = utxo_to_use {
10381044
(
@@ -1088,6 +1094,7 @@ impl BitcoinRegtestController {
10881094
get_satoshis_per_byte(&self.config),
10891095
&mut utxos,
10901096
signer,
1097+
false,
10911098
)?;
10921099

10931100
increment_btc_ops_sent_counter();
@@ -1110,7 +1117,7 @@ impl BitcoinRegtestController {
11101117
utxo_to_use: Option<UTXO>,
11111118
) -> Option<Transaction> {
11121119
let public_key = signer.get_public_key();
1113-
let max_tx_size = 230;
1120+
let max_tx_size = OP_TX_VOTE_AGG_ESTIM_SIZE;
11141121

11151122
let (mut tx, mut utxos) = if let Some(utxo) = utxo_to_use {
11161123
(
@@ -1162,6 +1169,7 @@ impl BitcoinRegtestController {
11621169
get_satoshis_per_byte(&self.config),
11631170
&mut utxos,
11641171
signer,
1172+
false,
11651173
)?;
11661174

11671175
increment_btc_ops_sent_counter();
@@ -1204,9 +1212,11 @@ impl BitcoinRegtestController {
12041212
signer: &mut BurnchainOpSigner,
12051213
) -> Option<Transaction> {
12061214
let public_key = signer.get_public_key();
1207-
let max_tx_size = 280;
1215+
let max_tx_size = OP_TX_PRE_STACKS_ESTIM_SIZE;
1216+
1217+
let max_tx_size_any_op = OP_TX_ANY_ESTIM_SIZE;
1218+
let output_amt = DUST_UTXO_LIMIT + max_tx_size_any_op * get_satoshis_per_byte(&self.config);
12081219

1209-
let output_amt = DUST_UTXO_LIMIT + max_tx_size * get_satoshis_per_byte(&self.config);
12101220
let (mut tx, mut utxos) =
12111221
self.prepare_tx(epoch_id, &public_key, output_amt, None, None, 0)?;
12121222

@@ -1238,6 +1248,7 @@ impl BitcoinRegtestController {
12381248
get_satoshis_per_byte(&self.config),
12391249
&mut utxos,
12401250
signer,
1251+
false,
12411252
)?;
12421253

12431254
increment_btc_ops_sent_counter();
@@ -1271,7 +1282,7 @@ impl BitcoinRegtestController {
12711282
utxo_to_use: Option<UTXO>,
12721283
) -> Option<Transaction> {
12731284
let public_key = signer.get_public_key();
1274-
let max_tx_size = 250;
1285+
let max_tx_size = OP_TX_STACK_STX_ESTIM_SIZE;
12751286

12761287
let (mut tx, mut utxos) = if let Some(utxo) = utxo_to_use {
12771288
(
@@ -1325,6 +1336,7 @@ impl BitcoinRegtestController {
13251336
get_satoshis_per_byte(&self.config),
13261337
&mut utxos,
13271338
signer,
1339+
false,
13281340
)?;
13291341

13301342
increment_btc_ops_sent_counter();
@@ -1415,6 +1427,7 @@ impl BitcoinRegtestController {
14151427
fee_rate,
14161428
&mut utxos,
14171429
signer,
1430+
true, // only block commit op requires change output to exist
14181431
)?;
14191432

14201433
let serialized_tx = SerializedTx::new(tx.clone());
@@ -1685,6 +1698,7 @@ impl BitcoinRegtestController {
16851698
fee_rate: u64,
16861699
utxos_set: &mut UTXOSet,
16871700
signer: &mut BurnchainOpSigner,
1701+
force_change_output: bool,
16881702
) -> Option<()> {
16891703
// spend UTXOs in order by confirmations. Spend the least-confirmed UTXO first, and in the
16901704
// event of a tie, spend the smallest-value UTXO first.
@@ -1715,6 +1729,7 @@ impl BitcoinRegtestController {
17151729
spent_in_outputs + min_tx_size * fee_rate + estimated_rbf,
17161730
&mut utxos_cloned,
17171731
signer,
1732+
force_change_output,
17181733
);
17191734
let serialized_tx = SerializedTx::new(tx_cloned);
17201735
cmp::max(min_tx_size, serialized_tx.bytes.len() as u64)
@@ -1731,6 +1746,7 @@ impl BitcoinRegtestController {
17311746
spent_in_outputs + tx_size * fee_rate + rbf_fee,
17321747
utxos_set,
17331748
signer,
1749+
force_change_output,
17341750
);
17351751
signer.dispose();
17361752
Some(())
@@ -1744,38 +1760,45 @@ impl BitcoinRegtestController {
17441760
&mut self,
17451761
epoch_id: StacksEpochId,
17461762
tx: &mut Transaction,
1747-
total_to_spend: u64,
1763+
tx_cost: u64,
17481764
utxos_set: &mut UTXOSet,
17491765
signer: &mut BurnchainOpSigner,
1766+
force_change_output: bool,
17501767
) -> bool {
17511768
let mut public_key = signer.get_public_key();
1752-
let mut total_consumed = 0;
1769+
1770+
let total_target = if force_change_output {
1771+
tx_cost + DUST_UTXO_LIMIT
1772+
} else {
1773+
tx_cost
1774+
};
17531775

17541776
// select UTXOs until we have enough to cover the cost
1777+
let mut total_consumed = 0;
17551778
let mut available_utxos = vec![];
17561779
available_utxos.append(&mut utxos_set.utxos);
17571780
for utxo in available_utxos.into_iter() {
17581781
total_consumed += utxo.amount;
17591782
utxos_set.utxos.push(utxo);
17601783

1761-
if total_consumed >= total_to_spend {
1784+
if total_consumed >= total_target {
17621785
break;
17631786
}
17641787
}
17651788

1766-
if total_consumed < total_to_spend {
1789+
if total_consumed < total_target {
17671790
warn!(
17681791
"Consumed total {} is less than intended spend: {}",
1769-
total_consumed, total_to_spend
1792+
total_consumed, total_target
17701793
);
17711794
return false;
17721795
}
17731796

17741797
// Append the change output
1775-
let value = total_consumed - total_to_spend;
1798+
let value = total_consumed - tx_cost;
17761799
debug!(
17771800
"Payments value: {:?}, total_consumed: {:?}, total_spent: {:?}",
1778-
value, total_consumed, total_to_spend
1801+
value, total_consumed, total_target
17791802
);
17801803
if value >= DUST_UTXO_LIMIT {
17811804
let change_output = if self.config.miner.segwit && epoch_id >= StacksEpochId::Epoch21 {

testnet/stacks-node/src/config.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,26 @@ use stacks_common::util::secp256k1::{Secp256k1PrivateKey, Secp256k1PublicKey};
4949
use crate::chain_data::MinerStats;
5050

5151
pub const DEFAULT_SATS_PER_VB: u64 = 50;
52+
pub const OP_TX_BLOCK_COMMIT_ESTIM_SIZE: u64 = 380;
53+
pub const OP_TX_DELEGATE_STACKS_ESTIM_SIZE: u64 = 230;
54+
pub const OP_TX_LEADER_KEY_ESTIM_SIZE: u64 = 290;
55+
pub const OP_TX_PRE_STACKS_ESTIM_SIZE: u64 = 280;
56+
pub const OP_TX_STACK_STX_ESTIM_SIZE: u64 = 250;
57+
pub const OP_TX_TRANSFER_STACKS_ESTIM_SIZE: u64 = 230;
58+
pub const OP_TX_VOTE_AGG_ESTIM_SIZE: u64 = 230;
59+
60+
pub const OP_TX_ANY_ESTIM_SIZE: u64 = fmax!(
61+
OP_TX_BLOCK_COMMIT_ESTIM_SIZE,
62+
OP_TX_DELEGATE_STACKS_ESTIM_SIZE,
63+
OP_TX_LEADER_KEY_ESTIM_SIZE,
64+
OP_TX_PRE_STACKS_ESTIM_SIZE,
65+
OP_TX_STACK_STX_ESTIM_SIZE,
66+
OP_TX_TRANSFER_STACKS_ESTIM_SIZE,
67+
OP_TX_VOTE_AGG_ESTIM_SIZE
68+
);
69+
5270
const DEFAULT_MAX_RBF_RATE: u64 = 150; // 1.5x
5371
const DEFAULT_RBF_FEE_RATE_INCREMENT: u64 = 5;
54-
const LEADER_KEY_TX_ESTIM_SIZE: u64 = 290;
55-
const BLOCK_COMMIT_TX_ESTIM_SIZE: u64 = 350;
5672
const INV_REWARD_CYCLES_TESTNET: u64 = 6;
5773

5874
#[derive(Clone, Deserialize, Default, Debug)]
@@ -1427,8 +1443,8 @@ impl BurnchainConfig {
14271443
poll_time_secs: 10, // TODO: this is a testnet specific value.
14281444
satoshis_per_byte: DEFAULT_SATS_PER_VB,
14291445
max_rbf: DEFAULT_MAX_RBF_RATE,
1430-
leader_key_tx_estimated_size: LEADER_KEY_TX_ESTIM_SIZE,
1431-
block_commit_tx_estimated_size: BLOCK_COMMIT_TX_ESTIM_SIZE,
1446+
leader_key_tx_estimated_size: OP_TX_LEADER_KEY_ESTIM_SIZE,
1447+
block_commit_tx_estimated_size: OP_TX_BLOCK_COMMIT_ESTIM_SIZE,
14321448
rbf_fee_increment: DEFAULT_RBF_FEE_RATE_INCREMENT,
14331449
first_burn_block_height: None,
14341450
first_burn_block_timestamp: None,

0 commit comments

Comments
 (0)