Merge pull request #139 from jeandudey/2018-08-18-outpoint

Rename `TxOutRef` to `OutPoint` and use it in `TxIn`.
This commit is contained in:
Andrew Poelstra 2018-08-20 18:18:00 +00:00 committed by GitHub
commit 2d961412af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 83 additions and 41 deletions

View File

@ -23,7 +23,7 @@ use std::default::Default;
use blockdata::opcodes; use blockdata::opcodes;
use blockdata::script; use blockdata::script;
use blockdata::transaction::{Transaction, TxOut, TxIn}; use blockdata::transaction::{OutPoint, Transaction, TxOut, TxIn};
use blockdata::block::{Block, BlockHeader}; use blockdata::block::{Block, BlockHeader};
use network::constants::Network; use network::constants::Network;
use util::misc::hex_bytes; use util::misc::hex_bytes;
@ -69,8 +69,7 @@ fn bitcoin_genesis_tx() -> Transaction {
.push_slice(b"The Times 03/Jan/2009 Chancellor on brink of second bailout for banks") .push_slice(b"The Times 03/Jan/2009 Chancellor on brink of second bailout for banks")
.into_script(); .into_script();
ret.input.push(TxIn { ret.input.push(TxIn {
prev_hash: Default::default(), previous_output: OutPoint::null(),
prev_index: 0xFFFFFFFF,
script_sig: in_script, script_sig: in_script,
sequence: MAX_SEQUENCE, sequence: MAX_SEQUENCE,
witness: vec![], witness: vec![],
@ -154,8 +153,8 @@ mod test {
assert_eq!(gen.version, 1); assert_eq!(gen.version, 1);
assert_eq!(gen.input.len(), 1); assert_eq!(gen.input.len(), 1);
assert_eq!(gen.input[0].prev_hash, Default::default()); assert_eq!(gen.input[0].previous_output.txid, Default::default());
assert_eq!(gen.input[0].prev_index, 0xFFFFFFFF); assert_eq!(gen.input[0].previous_output.vout, 0xFFFFFFFF);
assert_eq!(serialize(&gen.input[0].script_sig).ok(), assert_eq!(serialize(&gen.input[0].script_sig).ok(),
Some(hex_decode("4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73").unwrap())); Some(hex_decode("4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73").unwrap()));

View File

@ -35,28 +35,65 @@ use network::serialize::{serialize, BitcoinHash, SimpleEncoder, SimpleDecoder};
use network::encodable::{ConsensusEncodable, ConsensusDecodable, VarInt}; use network::encodable::{ConsensusEncodable, ConsensusDecodable, VarInt};
/// A reference to a transaction output /// A reference to a transaction output
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct TxOutRef { pub struct OutPoint {
/// The referenced transaction's txid /// The referenced transaction's txid
pub txid: Sha256dHash, pub txid: Sha256dHash,
/// The index of the referenced output in its transaction's vout /// The index of the referenced output in its transaction's vout
pub index: usize pub vout: u32,
} }
serde_struct_impl!(TxOutRef, txid, index); serde_struct_impl!(OutPoint, txid, vout);
impl fmt::Display for TxOutRef { impl OutPoint {
/// Creates a "null" `OutPoint`.
///
/// This value is used for coinbase transactions because they don't have
/// any previous outputs.
#[inline]
pub fn null() -> OutPoint {
OutPoint {
txid: Default::default(),
vout: u32::max_value(),
}
}
/// Checks if an `OutPoint` is "null".
///
/// # Examples
///
/// ```rust
/// use bitcoin::blockdata::constants::genesis_block;
/// use bitcoin::network::constants::Network;
///
/// let block = genesis_block(Network::Bitcoin);
/// let tx = &block.txdata[0];
///
/// // Coinbase transactions don't have any previous output.
/// assert_eq!(tx.input[0].previous_output.is_null(), true);
/// ```
#[inline]
pub fn is_null(&self) -> bool {
*self == OutPoint::null()
}
}
impl Default for OutPoint {
fn default() -> Self {
OutPoint::null()
}
}
impl fmt::Display for OutPoint {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}:{}", self.txid, self.index) write!(f, "{}:{}", self.txid, self.vout)
} }
} }
/// A transaction input, which defines old coins to be consumed /// A transaction input, which defines old coins to be consumed
#[derive(Clone, PartialEq, Eq, Debug, Hash)] #[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub struct TxIn { pub struct TxIn {
/// The hash of the transaction whose output is being used an an input /// The reference to the previous output that is being used an an input
pub prev_hash: Sha256dHash, pub previous_output: OutPoint,
/// The index of the output in the previous transaction, which may have several
pub prev_index: u32,
/// The script which pushes values on the stack which will cause /// The script which pushes values on the stack which will cause
/// the referenced output's script to accept /// the referenced output's script to accept
pub script_sig: Script, pub script_sig: Script,
@ -72,7 +109,7 @@ pub struct TxIn {
/// routines. /// routines.
pub witness: Vec<Vec<u8>> pub witness: Vec<Vec<u8>>
} }
serde_struct_impl!(TxIn, prev_hash, prev_index, script_sig, sequence, witness); serde_struct_impl!(TxIn, previous_output, script_sig, sequence, witness);
/// A transaction output, which defines new coins to be created from old ones. /// A transaction output, which defines new coins to be created from old ones.
#[derive(Clone, PartialEq, Eq, Debug, Hash)] #[derive(Clone, PartialEq, Eq, Debug, Hash)]
@ -171,9 +208,8 @@ impl Transaction {
// Add all inputs necessary.. // Add all inputs necessary..
if anyone_can_pay { if anyone_can_pay {
tx.input = vec![TxIn { tx.input = vec![TxIn {
prev_hash: self.input[input_index].prev_hash, previous_output: self.input[input_index].previous_output,
script_sig: script_pubkey.clone(), script_sig: script_pubkey.clone(),
prev_index: self.input[input_index].prev_index,
sequence: self.input[input_index].sequence, sequence: self.input[input_index].sequence,
witness: vec![], witness: vec![],
}]; }];
@ -181,8 +217,7 @@ impl Transaction {
tx.input = Vec::with_capacity(self.input.len()); tx.input = Vec::with_capacity(self.input.len());
for (n, input) in self.input.iter().enumerate() { for (n, input) in self.input.iter().enumerate() {
tx.input.push(TxIn { tx.input.push(TxIn {
prev_hash: input.prev_hash, previous_output: input.previous_output,
prev_index: input.prev_index,
script_sig: if n == input_index { script_pubkey.clone() } else { Script::new() }, script_sig: if n == input_index { script_pubkey.clone() } else { Script::new() },
sequence: if n != input_index && (sighash == SigHashType::Single || sighash == SigHashType::None) { 0 } else { input.sequence }, sequence: if n != input_index && (sighash == SigHashType::Single || sighash == SigHashType::None) { 0 } else { input.sequence },
witness: vec![], witness: vec![],
@ -252,17 +287,17 @@ impl Transaction {
#[cfg(feature="bitcoinconsensus")] #[cfg(feature="bitcoinconsensus")]
/// Verify that this transaction is able to spend some outputs of spent transactions /// Verify that this transaction is able to spend some outputs of spent transactions
pub fn verify (&self, spent : &HashMap<Sha256dHash, Transaction>) -> Result<(), script::Error> { pub fn verify(&self, spent: &HashMap<Sha256dHash, Transaction>) -> Result<(), script::Error> {
if let Ok(tx) = serialize(&*self) { if let Ok(tx) = serialize(&*self) {
for (idx, input) in self.input.iter().enumerate() { for (idx, input) in self.input.iter().enumerate() {
if let Some(ref s) = spent.get(&input.prev_hash) { if let Some(ref s) = spent.get(&input.previous_output.txid) {
if let Some(ref output) = s.output.get(input.prev_index as usize) { if let Some(ref output) = s.output.get(input.previous_output.vout as usize) {
output.script_pubkey.verify(idx, output.value, tx.as_slice())?; output.script_pubkey.verify(idx, output.value, tx.as_slice())?;
} else { } else {
return Err(script::Error::WrongSpentOutputIndex(input.prev_index as usize)); return Err(script::Error::WrongSpentOutputIndex(input.previous_output.vout as usize));
} }
} else { } else {
return Err(script::Error::UnknownSpentTransaction(input.prev_hash)); return Err(script::Error::UnknownSpentTransaction(input.previous_output.txid));
} }
} }
Ok(()) Ok(())
@ -272,9 +307,9 @@ impl Transaction {
} }
} }
/// is this a coin base transaction? /// Is this a coin base transaction?
pub fn is_coin_base (&self) -> bool { pub fn is_coin_base(&self) -> bool {
self.input.len() == 1 && self.input[0].prev_index == 0xFFFFFFFF && self.input [0].prev_hash == Sha256dHash::default() self.input.len() == 1 && self.input[0].previous_output.is_null()
} }
} }
@ -290,10 +325,24 @@ impl BitcoinHash for Transaction {
impl_consensus_encoding!(TxOut, value, script_pubkey); impl_consensus_encoding!(TxOut, value, script_pubkey);
impl<S: SimpleEncoder> ConsensusEncodable<S> for OutPoint {
fn consensus_encode(&self, s: &mut S) -> Result <(), S::Error> {
self.txid.consensus_encode(s)?;
self.vout.consensus_encode(s)
}
}
impl<D: SimpleDecoder> ConsensusDecodable<D> for OutPoint {
fn consensus_decode(d: &mut D) -> Result<OutPoint, D::Error> {
Ok(OutPoint {
txid: ConsensusDecodable::consensus_decode(d)?,
vout: ConsensusDecodable::consensus_decode(d)?,
})
}
}
impl<S: SimpleEncoder> ConsensusEncodable<S> for TxIn { impl<S: SimpleEncoder> ConsensusEncodable<S> for TxIn {
fn consensus_encode(&self, s: &mut S) -> Result <(), S::Error> { fn consensus_encode(&self, s: &mut S) -> Result <(), S::Error> {
self.prev_hash.consensus_encode(s)?; self.previous_output.consensus_encode(s)?;
self.prev_index.consensus_encode(s)?;
self.script_sig.consensus_encode(s)?; self.script_sig.consensus_encode(s)?;
self.sequence.consensus_encode(s) self.sequence.consensus_encode(s)
} }
@ -301,8 +350,7 @@ impl<S: SimpleEncoder> ConsensusEncodable<S> for TxIn {
impl<D: SimpleDecoder> ConsensusDecodable<D> for TxIn { impl<D: SimpleDecoder> ConsensusDecodable<D> for TxIn {
fn consensus_decode(d: &mut D) -> Result<TxIn, D::Error> { fn consensus_decode(d: &mut D) -> Result<TxIn, D::Error> {
Ok(TxIn { Ok(TxIn {
prev_hash: ConsensusDecodable::consensus_decode(d)?, previous_output: ConsensusDecodable::consensus_decode(d)?,
prev_index: ConsensusDecodable::consensus_decode(d)?,
script_sig: ConsensusDecodable::consensus_decode(d)?, script_sig: ConsensusDecodable::consensus_decode(d)?,
sequence: ConsensusDecodable::consensus_decode(d)?, sequence: ConsensusDecodable::consensus_decode(d)?,
witness: vec![], witness: vec![],
@ -484,9 +532,9 @@ mod tests {
assert_eq!(realtx.input.len(), 1); assert_eq!(realtx.input.len(), 1);
// In particular this one is easy to get backward -- in bitcoin hashes are encoded // In particular this one is easy to get backward -- in bitcoin hashes are encoded
// as little-endian 256-bit numbers rather than as data strings. // as little-endian 256-bit numbers rather than as data strings.
assert_eq!(realtx.input[0].prev_hash.be_hex_string(), assert_eq!(realtx.input[0].previous_output.txid.be_hex_string(),
"ce9ea9f6f5e422c6a9dbcddb3b9a14d1c78fab9ab520cb281aa2a74a09575da1".to_string()); "ce9ea9f6f5e422c6a9dbcddb3b9a14d1c78fab9ab520cb281aa2a74a09575da1".to_string());
assert_eq!(realtx.input[0].prev_index, 1); assert_eq!(realtx.input[0].previous_output.vout, 1);
assert_eq!(realtx.output.len(), 1); assert_eq!(realtx.output.len(), 1);
assert_eq!(realtx.lock_time, 0); assert_eq!(realtx.lock_time, 0);

View File

@ -47,8 +47,7 @@ impl SighashComponents {
let hash_prevouts = { let hash_prevouts = {
let mut enc = Sha256dEncoder::new(); let mut enc = Sha256dEncoder::new();
for txin in &tx.input { for txin in &tx.input {
txin.prev_hash.consensus_encode(&mut enc).unwrap(); txin.previous_output.consensus_encode(&mut enc).unwrap();
txin.prev_index.consensus_encode(&mut enc).unwrap();
} }
enc.into_hash() enc.into_hash()
}; };
@ -86,11 +85,7 @@ impl SighashComponents {
self.hash_prevouts.consensus_encode(&mut enc).unwrap(); self.hash_prevouts.consensus_encode(&mut enc).unwrap();
self.hash_sequence.consensus_encode(&mut enc).unwrap(); self.hash_sequence.consensus_encode(&mut enc).unwrap();
txin txin
.prev_hash .previous_output
.consensus_encode(&mut enc)
.unwrap();
txin
.prev_index
.consensus_encode(&mut enc) .consensus_encode(&mut enc)
.unwrap(); .unwrap();
witness_script.consensus_encode(&mut enc).unwrap(); witness_script.consensus_encode(&mut enc).unwrap();