Rename `TxOutRef` to `OutPoint` and use it in `TxIn`.
Previously this structure was unused, it's now being used by the `TxIn` structure to simplify the code a little bit and avoid confusions. Also the rust-lightning source code has an `OutPoint` similar to this one but with the `vout` index as an `u16` to avoid unsafe conversions. I've added to new methods to `OutPoint`: - `null`: Creates a new "null" `OutPoint`. - `is_null`: Checks if the given `OutPoint` is null. Signed-off-by: Jean Pierre Dudey <jeandudey@hotmail.com>
This commit is contained in:
parent
85ddb66be0
commit
32631e44ad
|
@ -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()));
|
||||||
|
|
||||||
|
|
|
@ -36,28 +36,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,
|
||||||
|
@ -73,7 +110,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)]
|
||||||
|
@ -172,9 +209,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![],
|
||||||
}];
|
}];
|
||||||
|
@ -182,8 +218,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![],
|
||||||
|
@ -253,17 +288,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(())
|
||||||
|
@ -273,9 +308,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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,10 +326,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)
|
||||||
}
|
}
|
||||||
|
@ -302,8 +351,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![],
|
||||||
|
@ -482,9 +530,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);
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in New Issue