Skip to content

Commit d57ec01

Browse files
committed
Use Amount type for TxOut value field
1 parent ac66410 commit d57ec01

10 files changed

Lines changed: 91 additions & 85 deletions

File tree

bitcoin/examples/ecdsa-psbt.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -192,11 +192,8 @@ impl WatchOnly {
192192
witness: Witness::default(),
193193
}],
194194
output: vec![
195-
TxOut { value: to_amount.to_sat(), script_pubkey: to_address.script_pubkey() },
196-
TxOut {
197-
value: change_amount.to_sat(),
198-
script_pubkey: change_address.script_pubkey(),
199-
},
195+
TxOut { value: to_amount, script_pubkey: to_address.script_pubkey() },
196+
TxOut { value: change_amount, script_pubkey: change_address.script_pubkey() },
200197
],
201198
};
202199

@@ -282,7 +279,7 @@ fn previous_output() -> TxOut {
282279
.expect("failed to parse input utxo scriptPubkey");
283280
let amount = Amount::from_str(INPUT_UTXO_VALUE).expect("failed to parse input utxo value");
284281

285-
TxOut { value: amount.to_sat(), script_pubkey }
282+
TxOut { value: amount, script_pubkey }
286283
}
287284

288285
struct Error(Box<dyn std::error::Error>);

bitcoin/examples/taproot-psbt.rs

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const UTXO_SCRIPT_PUBKEY: &str =
4040
"5120be27fa8b1f5278faf82cab8da23e8761f8f9bd5d5ebebbb37e0e12a70d92dd16";
4141
const UTXO_PUBKEY: &str = "a6ac32163539c16b6b5dbbca01b725b8e8acaa5f821ba42c80e7940062140d19";
4242
const UTXO_MASTER_FINGERPRINT: &str = "e61b318f";
43-
const ABSOLUTE_FEES_IN_SATS: u64 = 1000;
43+
const ABSOLUTE_FEES_IN_SATS: Amount = Amount::from_sat(1_000);
4444

4545
// UTXO_1 will be used for spending example 1
4646
const UTXO_1: P2trUtxo = P2trUtxo {
@@ -49,7 +49,7 @@ const UTXO_1: P2trUtxo = P2trUtxo {
4949
script_pubkey: UTXO_SCRIPT_PUBKEY,
5050
pubkey: UTXO_PUBKEY,
5151
master_fingerprint: UTXO_MASTER_FINGERPRINT,
52-
amount_in_sats: 50 * COIN_VALUE, // 50 BTC
52+
amount_in_sats: Amount::from_sat(50 * 100_000_000), // 50 BTC
5353
derivation_path: BIP86_DERIVATION_PATH,
5454
};
5555

@@ -60,7 +60,7 @@ const UTXO_2: P2trUtxo = P2trUtxo {
6060
script_pubkey: UTXO_SCRIPT_PUBKEY,
6161
pubkey: UTXO_PUBKEY,
6262
master_fingerprint: UTXO_MASTER_FINGERPRINT,
63-
amount_in_sats: 50 * COIN_VALUE,
63+
amount_in_sats: Amount::from_sat(50 * 100_000_000), // 50 BTC
6464
derivation_path: BIP86_DERIVATION_PATH,
6565
};
6666

@@ -71,7 +71,7 @@ const UTXO_3: P2trUtxo = P2trUtxo {
7171
script_pubkey: UTXO_SCRIPT_PUBKEY,
7272
pubkey: UTXO_PUBKEY,
7373
master_fingerprint: UTXO_MASTER_FINGERPRINT,
74-
amount_in_sats: 50 * COIN_VALUE,
74+
amount_in_sats: Amount::from_sat(50 * 100_000_000), // 50 BTC
7575
derivation_path: BIP86_DERIVATION_PATH,
7676
};
7777

@@ -80,7 +80,6 @@ use std::str::FromStr;
8080

8181
use bitcoin::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey, Fingerprint};
8282
use bitcoin::consensus::encode;
83-
use bitcoin::constants::COIN_VALUE;
8483
use bitcoin::key::{TapTweak, XOnlyPublicKey};
8584
use bitcoin::opcodes::all::{OP_CHECKSIG, OP_CLTV, OP_DROP};
8685
use bitcoin::psbt::{self, Input, Output, Psbt, PsbtSighashType};
@@ -105,7 +104,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
105104
let change_address =
106105
Address::from_str("bcrt1pz449kexzydh2kaypatup5ultru3ej284t6eguhnkn6wkhswt0l7q3a7j76")?
107106
.require_network(Network::Regtest)?;
108-
let amount_to_send_in_sats = COIN_VALUE;
107+
let amount_to_send_in_sats = Amount::ONE_BTC;
109108
let change_amount = UTXO_1
110109
.amount_in_sats
111110
.checked_sub(amount_to_send_in_sats)
@@ -216,7 +215,7 @@ struct P2trUtxo<'a> {
216215
script_pubkey: &'a str,
217216
pubkey: &'a str,
218217
master_fingerprint: &'a str,
219-
amount_in_sats: u64,
218+
amount_in_sats: Amount,
220219
derivation_path: &'a str,
221220
}
222221

@@ -259,9 +258,7 @@ fn generate_bip86_key_spend_tx(
259258
witness_utxo: {
260259
let script_pubkey = ScriptBuf::from_hex(input_utxo.script_pubkey)
261260
.expect("failed to parse input utxo scriptPubkey");
262-
let amount = Amount::from_sat(from_amount);
263-
264-
Some(TxOut { value: amount.to_sat(), script_pubkey })
261+
Some(TxOut { value: from_amount, script_pubkey })
265262
},
266263
tap_key_origins: origins,
267264
..Default::default()
@@ -448,9 +445,7 @@ impl BenefactorWallet {
448445
let input = Input {
449446
witness_utxo: {
450447
let script_pubkey = script_pubkey;
451-
let amount = Amount::from_sat(value);
452-
453-
Some(TxOut { value: amount.to_sat(), script_pubkey })
448+
Some(TxOut { value, script_pubkey })
454449
},
455450
tap_key_origins: origins,
456451
tap_merkle_root: taproot_spend_info.merkle_root(),
@@ -594,9 +589,9 @@ impl BenefactorWallet {
594589
let input = Input {
595590
witness_utxo: {
596591
let script_pubkey = output_script_pubkey;
597-
let amount = Amount::from_sat(output_value);
592+
let amount = output_value;
598593

599-
Some(TxOut { value: amount.to_sat(), script_pubkey })
594+
Some(TxOut { value: amount, script_pubkey })
600595
},
601596
tap_key_origins: origins,
602597
tap_merkle_root: taproot_spend_info.merkle_root(),

bitcoin/src/amount.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ use core::fmt::{self, Write};
1111
use core::str::FromStr;
1212
use core::{default, ops};
1313

14+
use crate::consensus::encode::{self, Decodable, Encodable};
15+
use crate::io;
1416
use crate::prelude::*;
1517

1618
/// A set of denominations in which amounts can be expressed.
@@ -483,6 +485,8 @@ fn fmt_satoshi_in(
483485
/// the checked arithmetic methods.
484486
///
485487
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
488+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
489+
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
486490
pub struct Amount(u64);
487491

488492
impl Amount {
@@ -660,6 +664,20 @@ impl Amount {
660664
}
661665
}
662666

667+
impl Decodable for Amount {
668+
#[inline]
669+
fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
670+
Ok(Amount(Decodable::consensus_decode(r)?))
671+
}
672+
}
673+
674+
impl Encodable for Amount {
675+
#[inline]
676+
fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
677+
self.0.consensus_encode(w)
678+
}
679+
}
680+
663681
impl default::Default for Amount {
664682
fn default() -> Self { Amount::ZERO }
665683
}

bitcoin/src/bip152.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,8 @@ mod test {
378378
use crate::consensus::encode::{deserialize, serialize};
379379
use crate::hash_types::TxMerkleNode;
380380
use crate::{
381-
CompactTarget, OutPoint, ScriptBuf, Sequence, Transaction, TxIn, TxOut, Txid, Witness,
381+
Amount, CompactTarget, OutPoint, ScriptBuf, Sequence, Transaction, TxIn, TxOut, Txid,
382+
Witness,
382383
};
383384

384385
fn dummy_tx(nonce: &[u8]) -> Transaction {
@@ -391,7 +392,7 @@ mod test {
391392
sequence: Sequence(1),
392393
witness: Witness::new(),
393394
}],
394-
output: vec![TxOut { value: 1, script_pubkey: ScriptBuf::new() }],
395+
output: vec![TxOut { value: Amount::ONE_SAT, script_pubkey: ScriptBuf::new() }],
395396
}
396397
}
397398

bitcoin/src/blockdata/constants.rs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,8 @@ use crate::blockdata::witness::Witness;
2222
use crate::internal_macros::impl_bytes_newtype;
2323
use crate::network::constants::Network;
2424
use crate::pow::CompactTarget;
25+
use crate::Amount;
2526

26-
/// How many satoshis are in "one bitcoin".
27-
pub const COIN_VALUE: u64 = 100_000_000;
2827
/// How many seconds between blocks we expect on average.
2928
pub const TARGET_BLOCK_SPACING: u32 = 600;
3029
/// How many blocks between diffchanges.
@@ -61,11 +60,6 @@ pub const MAX_SCRIPTNUM_VALUE: u32 = 0x80000000; // 2^31
6160
/// Number of blocks needed for an output from a coinbase transaction to be spendable.
6261
pub const COINBASE_MATURITY: u32 = 100;
6362

64-
/// The maximum value allowed in an output (useful for sanity checking,
65-
/// since keeping everything below this value should prevent overflows
66-
/// if you are doing anything remotely sane with monetary values).
67-
pub const MAX_MONEY: u64 = 21_000_000 * COIN_VALUE;
68-
6963
/// Constructs and returns the coinbase (and only) transaction of the Bitcoin genesis block.
7064
fn bitcoin_genesis_tx() -> Transaction {
7165
// Base
@@ -93,7 +87,7 @@ fn bitcoin_genesis_tx() -> Transaction {
9387
let script_bytes = hex!("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f");
9488
let out_script =
9589
script::Builder::new().push_slice(script_bytes).push_opcode(OP_CHECKSIG).into_script();
96-
ret.output.push(TxOut { value: 50 * COIN_VALUE, script_pubkey: out_script });
90+
ret.output.push(TxOut { value: Amount::from_sat(50 * 100_000_000), script_pubkey: out_script });
9791

9892
// end
9993
ret
@@ -198,6 +192,8 @@ impl ChainHash {
198192

199193
#[cfg(test)]
200194
mod test {
195+
use core::str::FromStr;
196+
201197
use super::*;
202198
use crate::blockdata::locktime::absolute;
203199
use crate::consensus::encode::serialize;
@@ -219,7 +215,7 @@ mod test {
219215
assert_eq!(gen.output.len(), 1);
220216
assert_eq!(serialize(&gen.output[0].script_pubkey),
221217
hex!("434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac"));
222-
assert_eq!(gen.output[0].value, 50 * COIN_VALUE);
218+
assert_eq!(gen.output[0].value, Amount::from_str("50 BTC").unwrap());
223219
assert_eq!(gen.lock_time, absolute::LockTime::ZERO);
224220

225221
assert_eq!(

bitcoin/src/blockdata/transaction.rs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use crate::prelude::*;
3535
#[cfg(doc)]
3636
use crate::sighash::{EcdsaSighashType, TapSighashType};
3737
use crate::string::FromHexStr;
38-
use crate::{io, VarInt};
38+
use crate::{io, Amount, VarInt};
3939

4040
/// A reference to a transaction output.
4141
///
@@ -478,7 +478,7 @@ impl_parse_str_from_int_infallible!(Sequence, u32, from_consensus);
478478
#[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
479479
pub struct TxOut {
480480
/// The value of the output, in satoshis.
481-
pub value: u64,
481+
pub value: Amount,
482482
/// The script which must be satisfied for the output to be spent.
483483
pub script_pubkey: ScriptBuf,
484484
}
@@ -512,15 +512,17 @@ impl TxOut {
512512
let dust_amount = (len as u64) * 3;
513513

514514
TxOut {
515-
value: dust_amount + 1, // minimal non-dust amount is one higher than dust amount
515+
value: Amount::from_sat(dust_amount + 1), // minimal non-dust amount is one higher than dust amount
516516
script_pubkey,
517517
}
518518
}
519519
}
520520

521521
// This is used as a "null txout" in consensus signing code.
522522
impl Default for TxOut {
523-
fn default() -> TxOut { TxOut { value: 0xffffffffffffffff, script_pubkey: ScriptBuf::new() } }
523+
fn default() -> TxOut {
524+
TxOut { value: Amount::from_sat(0xffffffffffffffff), script_pubkey: ScriptBuf::new() }
525+
}
524526
}
525527

526528
/// Result of [`Transaction::encode_signing_data_to`].
@@ -962,12 +964,7 @@ impl Transaction {
962964
let flags: u32 = flags.into();
963965
for (idx, input) in self.input.iter().enumerate() {
964966
if let Some(output) = spent(&input.previous_output) {
965-
output.script_pubkey.verify_with_flags(
966-
idx,
967-
crate::Amount::from_sat(output.value),
968-
tx.as_slice(),
969-
flags,
970-
)?;
967+
output.script_pubkey.verify_with_flags(idx, output.value, tx.as_slice(), flags)?;
971968
} else {
972969
return Err(script::Error::UnknownSpentOutput(input.previous_output));
973970
}

bitcoin/src/crypto/sighash.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use crate::consensus::{encode, Encodable};
2222
use crate::error::impl_std_error;
2323
use crate::prelude::*;
2424
use crate::taproot::{LeafVersion, TapLeafHash, TAPROOT_ANNEX_PREFIX};
25-
use crate::{io, Script, ScriptBuf, Sequence, Transaction, TxIn, TxOut};
25+
use crate::{io, Amount, Script, ScriptBuf, Sequence, Transaction, TxIn, TxOut};
2626

2727
/// Used for signature hash for invalid use of SIGHASH_SINGLE.
2828
#[rustfmt::skip]
@@ -751,7 +751,7 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
751751
mut writer: Write,
752752
input_index: usize,
753753
script_code: &Script,
754-
value: u64,
754+
value: Amount,
755755
sighash_type: EcdsaSighashType,
756756
) -> Result<(), Error> {
757757
let zero_hash = sha256d::Hash::all_zeros();
@@ -810,7 +810,7 @@ impl<R: Borrow<Transaction>> SighashCache<R> {
810810
&mut self,
811811
input_index: usize,
812812
script_code: &Script,
813-
value: u64,
813+
value: Amount,
814814
sighash_type: EcdsaSighashType,
815815
) -> Result<SegwitV0Sighash, Error> {
816816
let mut enc = SegwitV0Sighash::engine();
@@ -1062,7 +1062,7 @@ impl<R: BorrowMut<Transaction>> SighashCache<R> {
10621062
///
10631063
/// This allows in-line signing such as
10641064
/// ```
1065-
/// use bitcoin::{absolute, Transaction, Script};
1065+
/// use bitcoin::{absolute, Amount, Transaction, Script};
10661066
/// use bitcoin::sighash::{EcdsaSighashType, SighashCache};
10671067
///
10681068
/// let mut tx_to_sign = Transaction { version: 2, lock_time: absolute::LockTime::ZERO, input: Vec::new(), output: Vec::new() };
@@ -1071,7 +1071,7 @@ impl<R: BorrowMut<Transaction>> SighashCache<R> {
10711071
/// let mut sig_hasher = SighashCache::new(&mut tx_to_sign);
10721072
/// for inp in 0..input_count {
10731073
/// let prevout_script = Script::empty();
1074-
/// let _sighash = sig_hasher.segwit_signature_hash(inp, prevout_script, 42, EcdsaSighashType::All);
1074+
/// let _sighash = sig_hasher.segwit_signature_hash(inp, prevout_script, Amount::ONE_SAT, EcdsaSighashType::All);
10751075
/// // ... sign the sighash
10761076
/// sig_hasher.witness_mut(inp).unwrap().push(&Vec::new());
10771077
/// }
@@ -1468,7 +1468,7 @@ mod tests {
14681468
#[serde(rename = "scriptPubKey")]
14691469
script_pubkey: ScriptBuf,
14701470
#[serde(rename = "amountSats")]
1471-
value: u64,
1471+
value: Amount,
14721472
}
14731473

14741474
#[derive(serde::Deserialize)]
@@ -1685,7 +1685,7 @@ mod tests {
16851685

16861686
let witness_script =
16871687
p2pkh_hex("025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357");
1688-
let value = 600_000_000;
1688+
let value = Amount::from_sat(600_000_000);
16891689

16901690
let mut cache = SighashCache::new(&tx);
16911691
assert_eq!(
@@ -1726,7 +1726,7 @@ mod tests {
17261726

17271727
let witness_script =
17281728
p2pkh_hex("03ad1d8e89212f0b92c74d23bb710c00662ad1470198ac48c43f7d6f93a2a26873");
1729-
let value = 1_000_000_000;
1729+
let value = Amount::from_sat(1_000_000_000);
17301730

17311731
let mut cache = SighashCache::new(&tx);
17321732
assert_eq!(
@@ -1773,7 +1773,7 @@ mod tests {
17731773
56ae",
17741774
)
17751775
.unwrap();
1776-
let value = 987654321;
1776+
let value = Amount::from_sat(987_654_321);
17771777

17781778
let mut cache = SighashCache::new(&tx);
17791779
assert_eq!(

0 commit comments

Comments
 (0)