From 3b5f89fdf52c52123dc74d16146ed9b59b6f42a4 Mon Sep 17 00:00:00 2001 From: Sebastian Geisler Date: Fri, 30 Apr 2021 21:58:28 +0200 Subject: [PATCH] Replace u64 with Amount type in TxOut Some places, most prominently TxOut, were still using u64s for amounts (in sat). This PR replaces these with the Amount newtype and also implements the consensus encode traits for Amount to make it all work. --- src/blockdata/constants.rs | 6 ++++-- src/blockdata/script.rs | 6 +++--- src/blockdata/transaction.rs | 7 ++++--- src/util/amount.rs | 14 ++++++++++++++ src/util/bip143.rs | 11 ++++++----- src/util/psbt/mod.rs | 26 ++++++++++++++------------ 6 files changed, 45 insertions(+), 25 deletions(-) diff --git a/src/blockdata/constants.rs b/src/blockdata/constants.rs index a7108d531c..dd51d8bc06 100644 --- a/src/blockdata/constants.rs +++ b/src/blockdata/constants.rs @@ -29,6 +29,7 @@ use blockdata::transaction::{OutPoint, Transaction, TxOut, TxIn}; use blockdata::block::{Block, BlockHeader}; use network::constants::Network; use util::uint::Uint256; +use Amount; /// The maximum allowable sequence number pub const MAX_SEQUENCE: u32 = 0xFFFFFFFF; @@ -88,7 +89,7 @@ fn bitcoin_genesis_tx() -> Transaction { .push_opcode(opcodes::all::OP_CHECKSIG) .into_script(); ret.output.push(TxOut { - value: 50 * COIN_VALUE, + value: Amount::from_sat(50 * COIN_VALUE), script_pubkey: out_script }); @@ -166,6 +167,7 @@ mod test { use consensus::encode::serialize; use blockdata::constants::{genesis_block, bitcoin_genesis_tx}; use blockdata::constants::{MAX_SEQUENCE, COIN_VALUE}; + use Amount; #[test] fn bitcoin_genesis_first_transaction() { @@ -182,7 +184,7 @@ mod test { assert_eq!(gen.output.len(), 1); assert_eq!(serialize(&gen.output[0].script_pubkey), Vec::from_hex("434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac").unwrap()); - assert_eq!(gen.output[0].value, 50 * COIN_VALUE); + assert_eq!(gen.output[0].value, Amount::from_sat(50 * COIN_VALUE)); assert_eq!(gen.lock_time, 0); assert_eq!(format!("{:x}", gen.wtxid()), diff --git a/src/blockdata/script.rs b/src/blockdata/script.rs index 1486bc6dfc..8ae40edc66 100644 --- a/src/blockdata/script.rs +++ b/src/blockdata/script.rs @@ -454,8 +454,8 @@ impl Script { /// * index - the input index in spending which is spending this transaction /// * amount - the amount this script guards /// * spending - the transaction that attempts to spend the output holding this script - pub fn verify (&self, index: usize, amount: u64, spending: &[u8]) -> Result<(), Error> { - Ok(bitcoinconsensus::verify (&self.0[..], amount, spending, index)?) + pub fn verify (&self, index: usize, amount: ::Amount, spending: &[u8]) -> Result<(), Error> { + Ok(bitcoinconsensus::verify (&self.0[..], amount.as_sat(), spending, index)?) } /// Write the assembly decoding of the script bytes to the formatter. @@ -1243,7 +1243,7 @@ mod test { // a random segwit transaction from the blockchain using native segwit let spent = Builder::from(Vec::from_hex("0020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d").unwrap()).into_script(); let spending = Vec::from_hex("010000000001011f97548fbbe7a0db7588a66e18d803d0089315aa7d4cc28360b6ec50ef36718a0100000000ffffffff02df1776000000000017a9146c002a686959067f4866b8fb493ad7970290ab728757d29f0000000000220020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d04004730440220565d170eed95ff95027a69b313758450ba84a01224e1f7f130dda46e94d13f8602207bdd20e307f062594022f12ed5017bbf4a055a06aea91c10110a0e3bb23117fc014730440220647d2dc5b15f60bc37dc42618a370b2a1490293f9e5c8464f53ec4fe1dfe067302203598773895b4b16d37485cbe21b337f4e4b650739880098c592553add7dd4355016952210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae00000000").unwrap(); - spent.verify(0, 18393430, spending.as_slice()).unwrap(); + spent.verify(0, ::Amount::from_sat(18393430), spending.as_slice()).unwrap(); } } diff --git a/src/blockdata/transaction.rs b/src/blockdata/transaction.rs index bc28f34796..0af7323d45 100644 --- a/src/blockdata/transaction.rs +++ b/src/blockdata/transaction.rs @@ -35,7 +35,7 @@ use blockdata::constants::WITNESS_SCALE_FACTOR; use blockdata::script::Script; use consensus::{encode, Decodable, Encodable}; use hash_types::{SigHash, Txid, Wtxid}; -use VarInt; +use ::{VarInt, Amount}; /// A reference to a transaction output #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] @@ -210,7 +210,8 @@ impl Default for TxIn { #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct TxOut { /// The value of the output, in satoshis - pub value: u64, + #[cfg_attr(feature = "serde", serde(with="::util::amount::serde::as_sat"))] + pub value: Amount, /// The script which must satisfy for the output to be spent pub script_pubkey: Script } @@ -218,7 +219,7 @@ pub struct TxOut { // This is used as a "null txout" in consensus signing code impl Default for TxOut { fn default() -> TxOut { - TxOut { value: 0xffffffffffffffff, script_pubkey: Script::new() } + TxOut { value: Amount::from_sat(0xffffffffffffffff), script_pubkey: Script::new() } } } diff --git a/src/util/amount.rs b/src/util/amount.rs index f2fb3feed8..9e58eb41c2 100644 --- a/src/util/amount.rs +++ b/src/util/amount.rs @@ -17,9 +17,11 @@ use std::default; use std::error; use std::fmt::{self, Write}; +use std::io; use std::ops; use std::str::FromStr; use std::cmp::Ordering; +use consensus::{Encodable, Decodable, encode}; /// A set of denominations in which amounts can be expressed. #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] @@ -446,6 +448,18 @@ impl fmt::Display for Amount { } } +impl Encodable for Amount { + fn consensus_encode(&self, writer: W) -> Result { + self.0.consensus_encode(writer) + } +} + +impl Decodable for Amount { + fn consensus_decode(d: D) -> Result { + Ok(Amount(u64::consensus_decode(d)?)) + } +} + impl ops::Add for Amount { type Output = Amount; diff --git a/src/util/bip143.rs b/src/util/bip143.rs index 78f2dd7b04..219577dfda 100644 --- a/src/util/bip143.rs +++ b/src/util/bip143.rs @@ -27,6 +27,7 @@ use consensus::{encode, Encodable}; use std::io; use std::ops::{Deref, DerefMut}; +use Amount; /// Parts of a sighash which are common across inputs or signatures, and which are /// sufficient (in conjunction with a private key) to sign the transaction @@ -176,7 +177,7 @@ impl> SigHashCache { mut writer: Write, input_index: usize, script_code: &Script, - value: u64, + value: Amount, sighash_type: SigHashType, ) -> Result<(), encode::Error> { let zero_hash = sha256d::Hash::default(); @@ -229,7 +230,7 @@ impl> SigHashCache { &mut self, input_index: usize, script_code: &Script, - value: u64, + value: Amount, sighash_type: SigHashType ) -> SigHash { let mut enc = SigHash::engine(); @@ -247,7 +248,7 @@ impl> SigHashCache { /// ``` /// use bitcoin::blockdata::transaction::{Transaction, SigHashType}; /// use bitcoin::util::bip143::SigHashCache; - /// use bitcoin::Script; + /// use bitcoin::{Script, Amount}; /// /// let mut tx_to_sign = Transaction { version: 2, lock_time: 0, input: Vec::new(), output: Vec::new() }; /// let input_count = tx_to_sign.input.len(); @@ -255,7 +256,7 @@ impl> SigHashCache { /// let mut sig_hasher = SigHashCache::new(&mut tx_to_sign); /// for inp in 0..input_count { /// let prevout_script = Script::new(); - /// let _sighash = sig_hasher.signature_hash(inp, &prevout_script, 42, SigHashType::All); + /// let _sighash = sig_hasher.signature_hash(inp, &prevout_script, Amount::from_sat(42), SigHashType::All); /// // ... sign the sighash /// sig_hasher.access_witness(inp).push(Vec::new()); /// } @@ -293,7 +294,7 @@ mod tests { let expected_result = SigHash::from_slice(&raw_expected[..]).unwrap(); let mut cache = SigHashCache::new(&tx); let sighash_type = SigHashType::from_u32_consensus(hash_type); - let actual_result = cache.signature_hash(input_index, &script, value, sighash_type); + let actual_result = cache.signature_hash(input_index, &script, Amount::from_sat(value), sighash_type); assert_eq!(actual_result, expected_result); } diff --git a/src/util/psbt/mod.rs b/src/util/psbt/mod.rs index 9e7d911017..9ab75e5b48 100644 --- a/src/util/psbt/mod.rs +++ b/src/util/psbt/mod.rs @@ -228,6 +228,7 @@ mod tests { use super::PartiallySignedTransaction; use util::psbt::raw::ProprietaryKey; + use Amount; #[test] fn trivial_psbt() { @@ -316,13 +317,13 @@ mod tests { }], output: vec![ TxOut { - value: 99999699, + value: Amount::from_sat(99999699), script_pubkey: hex_script!( "76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac" ), }, TxOut { - value: 100000000, + value: Amount::from_sat(100000000), script_pubkey: hex_script!( "a9143545e6e33b832c47050f24d3eeb93c9c03948bc787" ), @@ -384,7 +385,7 @@ mod tests { }], output: vec![ TxOut { - value: 190303501938, + value: Amount::from_sat(190303501938), script_pubkey: hex_script!("a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587"), }, ], @@ -429,7 +430,7 @@ mod tests { inputs: vec![Input { non_witness_utxo: Some(tx), witness_utxo: Some(TxOut { - value: 190303501938, + value: Amount::from_sat(190303501938), script_pubkey: hex_script!("a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587"), }), sighash_type: Some("SIGHASH_SINGLE|SIGHASH_ANYONECANPAY".parse().unwrap()), @@ -475,6 +476,7 @@ mod tests { use util::psbt::map::{Map, Global, Input, Output}; use util::psbt::raw; use util::psbt::{PartiallySignedTransaction, Error}; + use Amount; #[test] #[should_panic(expected = "InvalidMagic")] @@ -572,11 +574,11 @@ mod tests { }], output: vec![ TxOut { - value: 99999699, + value: Amount::from_sat(99999699), script_pubkey: hex_script!("76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac"), }, TxOut { - value: 100000000, + value: Amount::from_sat(100000000), script_pubkey: hex_script!("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787"), }, ], @@ -620,11 +622,11 @@ mod tests { }], output: vec![ TxOut { - value: 200000000, + value: Amount::from_sat(200000000), script_pubkey: hex_script!("76a91485cff1097fd9e008bb34af709c62197b38978a4888ac"), }, TxOut { - value: 190303501938, + value: Amount::from_sat(190303501938), script_pubkey: hex_script!("a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587"), }, ], @@ -805,11 +807,11 @@ mod tests { }], output: vec![ TxOut { - value: 99999699, + value: Amount::from_sat(99999699), script_pubkey: hex_script!("76a914d0c59903c5bac2868760e90fd521a4665aa7652088ac"), }, TxOut { - value: 100000000, + value: Amount::from_sat(100000000), script_pubkey: hex_script!("a9143545e6e33b832c47050f24d3eeb93c9c03948bc787"), }, ], @@ -853,11 +855,11 @@ mod tests { }], output: vec![ TxOut { - value: 200000000, + value: Amount::from_sat(200000000), script_pubkey: hex_script!("76a91485cff1097fd9e008bb34af709c62197b38978a4888ac"), }, TxOut { - value: 190303501938, + value: Amount::from_sat(190303501938), script_pubkey: hex_script!("a914339725ba21efd62ac753a9bcd067d6c7a6a39d0587"), }, ],