2016-08-15 06:19:28 -07:00
|
|
|
|
|
|
|
//! Bitcoin trainsaction.
|
|
|
|
//! https://en.bitcoin.it/wiki/Protocol_documentation#tx
|
|
|
|
|
2016-08-15 07:35:07 -07:00
|
|
|
use reader::{Deserializable, Reader, Error as ReaderError};
|
2016-09-09 01:49:08 -07:00
|
|
|
use crypto::dhash256;
|
2016-08-16 03:09:40 -07:00
|
|
|
use hash::H256;
|
|
|
|
use stream::{Serializable, Stream, serialize};
|
2016-08-15 06:19:28 -07:00
|
|
|
use compact_integer::CompactInteger;
|
|
|
|
|
2016-09-08 06:05:57 -07:00
|
|
|
// Below flags apply in the context of BIP 68
|
|
|
|
// If this flag set, CTxIn::nSequence is NOT interpreted as a
|
|
|
|
// relative lock-time.
|
|
|
|
pub const SEQUENCE_LOCKTIME_DISABLE_FLAG: u32 = 1u32 << 31;
|
|
|
|
|
2016-08-15 06:19:28 -07:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct OutPoint {
|
2016-08-16 03:09:40 -07:00
|
|
|
hash: H256,
|
2016-08-15 06:19:28 -07:00
|
|
|
index: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Serializable for OutPoint {
|
|
|
|
fn serialize(&self, stream: &mut Stream) {
|
|
|
|
stream
|
|
|
|
.append_bytes(&self.hash)
|
|
|
|
.append(&self.index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-15 07:35:07 -07:00
|
|
|
impl Deserializable for OutPoint {
|
|
|
|
fn deserialize(reader: &mut Reader) -> Result<Self, ReaderError> where Self: Sized {
|
|
|
|
let mut hash = [0u8; 32];
|
|
|
|
hash.copy_from_slice(try!(reader.read_bytes(32)));
|
|
|
|
let index = try!(reader.read());
|
|
|
|
let result = OutPoint {
|
|
|
|
hash: hash,
|
|
|
|
index: index,
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(result)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-12 06:09:32 -07:00
|
|
|
impl OutPoint {
|
|
|
|
pub fn hash(&self) -> &H256 {
|
|
|
|
&self.hash
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn index(&self) -> u32 {
|
|
|
|
self.index
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-15 06:19:28 -07:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct TransactionInput {
|
|
|
|
previous_output: OutPoint,
|
2016-09-12 01:57:12 -07:00
|
|
|
script_sig: Vec<u8>,
|
2016-08-15 06:19:28 -07:00
|
|
|
sequence: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Serializable for TransactionInput {
|
|
|
|
fn serialize(&self, stream: &mut Stream) {
|
|
|
|
stream
|
|
|
|
.append(&self.previous_output)
|
2016-09-12 01:57:12 -07:00
|
|
|
.append(&CompactInteger::from(self.script_sig.len()))
|
|
|
|
.append_bytes(&self.script_sig)
|
2016-08-15 06:19:28 -07:00
|
|
|
.append(&self.sequence);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-15 07:35:07 -07:00
|
|
|
impl Deserializable for TransactionInput {
|
|
|
|
fn deserialize(reader: &mut Reader) -> Result<Self, ReaderError> where Self: Sized {
|
|
|
|
let previous_output = try!(reader.read());
|
2016-09-12 01:57:12 -07:00
|
|
|
let script_sig_len = try!(reader.read::<CompactInteger>());
|
|
|
|
let script_sig = try!(reader.read_bytes(script_sig_len.into())).to_vec();
|
2016-08-15 07:35:07 -07:00
|
|
|
let sequence = try!(reader.read());
|
|
|
|
|
|
|
|
let result = TransactionInput {
|
|
|
|
previous_output: previous_output,
|
2016-09-12 01:57:12 -07:00
|
|
|
script_sig: script_sig,
|
2016-08-15 07:35:07 -07:00
|
|
|
sequence: sequence,
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(result)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-12 06:09:32 -07:00
|
|
|
impl TransactionInput {
|
|
|
|
pub fn previous_output(&self) -> &OutPoint {
|
|
|
|
&self.previous_output
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn script_sig(&self) -> &[u8] {
|
|
|
|
&self.script_sig
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn sequence(&self) -> u32 {
|
|
|
|
self.sequence
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-15 06:19:28 -07:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct TransactionOutput {
|
|
|
|
value: u64,
|
2016-09-12 01:57:12 -07:00
|
|
|
script_pubkey: Vec<u8>,
|
2016-08-15 06:19:28 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Serializable for TransactionOutput {
|
|
|
|
fn serialize(&self, stream: &mut Stream) {
|
|
|
|
stream
|
|
|
|
.append(&self.value)
|
2016-09-12 01:57:12 -07:00
|
|
|
.append(&CompactInteger::from(self.script_pubkey.len()))
|
|
|
|
.append_bytes(&self.script_pubkey);
|
2016-08-15 06:19:28 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-15 07:35:07 -07:00
|
|
|
impl Deserializable for TransactionOutput {
|
|
|
|
fn deserialize(reader: &mut Reader) -> Result<Self, ReaderError> where Self: Sized {
|
|
|
|
let value = try!(reader.read());
|
2016-09-12 01:57:12 -07:00
|
|
|
let script_pubkey_len = try!(reader.read::<CompactInteger>());
|
|
|
|
let script_pubkey = try!(reader.read_bytes(script_pubkey_len.into())).to_vec();
|
2016-08-15 07:35:07 -07:00
|
|
|
|
|
|
|
let result = TransactionOutput {
|
|
|
|
value: value,
|
2016-09-12 01:57:12 -07:00
|
|
|
script_pubkey: script_pubkey,
|
2016-08-15 07:35:07 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
Ok(result)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-15 06:19:28 -07:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Transaction {
|
|
|
|
version: i32,
|
|
|
|
transaction_inputs: Vec<TransactionInput>,
|
|
|
|
transaction_outputs: Vec<TransactionOutput>,
|
|
|
|
lock_time: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Serializable for Transaction {
|
|
|
|
fn serialize(&self, stream: &mut Stream) {
|
|
|
|
stream
|
|
|
|
.append(&self.version)
|
|
|
|
.append(&CompactInteger::from(self.transaction_inputs.len()))
|
|
|
|
.append_list(&self.transaction_inputs)
|
|
|
|
.append(&CompactInteger::from(self.transaction_outputs.len()))
|
|
|
|
.append_list(&self.transaction_outputs)
|
|
|
|
.append(&self.lock_time);
|
|
|
|
}
|
|
|
|
}
|
2016-08-15 07:35:07 -07:00
|
|
|
|
|
|
|
impl Deserializable for Transaction {
|
|
|
|
fn deserialize(reader: &mut Reader) -> Result<Self, ReaderError> where Self: Sized {
|
|
|
|
let version = try!(reader.read());
|
|
|
|
let tx_inputs_len= try!(reader.read::<CompactInteger>());
|
|
|
|
let tx_inputs = try!(reader.read_list(tx_inputs_len.into()));
|
|
|
|
let tx_outputs_len= try!(reader.read::<CompactInteger>());
|
|
|
|
let tx_outputs = try!(reader.read_list(tx_outputs_len.into()));
|
|
|
|
let lock_time = try!(reader.read());
|
|
|
|
|
|
|
|
let result = Transaction {
|
|
|
|
version: version,
|
|
|
|
transaction_inputs: tx_inputs,
|
|
|
|
transaction_outputs: tx_outputs,
|
|
|
|
lock_time: lock_time,
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(result)
|
|
|
|
}
|
|
|
|
}
|
2016-08-16 03:09:40 -07:00
|
|
|
|
|
|
|
impl Transaction {
|
|
|
|
pub fn hash(&self) -> H256 {
|
2016-09-09 01:49:08 -07:00
|
|
|
dhash256(&serialize(self))
|
2016-08-16 03:09:40 -07:00
|
|
|
}
|
2016-09-11 03:22:25 -07:00
|
|
|
|
|
|
|
pub fn transaction_inputs(&self) -> &[TransactionInput] {
|
|
|
|
&self.transaction_inputs
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn transaction_outputs(&self) -> &[TransactionOutput] {
|
|
|
|
&self.transaction_outputs
|
|
|
|
}
|
2016-08-16 03:09:40 -07:00
|
|
|
}
|
|
|
|
|
2016-09-12 06:09:32 -07:00
|
|
|
pub struct MutableTransaction {
|
|
|
|
pub version: i32,
|
|
|
|
pub transaction_inputs: Vec<TransactionInput>,
|
|
|
|
pub transaction_outputs: Vec<TransactionOutput>,
|
|
|
|
pub lock_time: u32,
|
|
|
|
}
|
|
|
|
|
2016-08-16 03:09:40 -07:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2016-08-19 05:50:22 -07:00
|
|
|
use hex::FromHex;
|
2016-08-16 03:09:40 -07:00
|
|
|
use reader::deserialize;
|
2016-08-16 09:00:26 -07:00
|
|
|
use hash::h256_from_str;
|
2016-08-16 03:09:40 -07:00
|
|
|
use super::Transaction;
|
|
|
|
|
|
|
|
// real transaction from block 80000
|
|
|
|
// https://blockchain.info/rawtx/5a4ebf66822b0b2d56bd9dc64ece0bc38ee7844a23ff1d7320a88c5fdb2ad3e2
|
|
|
|
// https://blockchain.info/rawtx/5a4ebf66822b0b2d56bd9dc64ece0bc38ee7844a23ff1d7320a88c5fdb2ad3e2?format=hex
|
|
|
|
#[test]
|
|
|
|
fn test_transaction_reader() {
|
|
|
|
let encoded_tx = "0100000001a6b97044d03da79c005b20ea9c0e1a6d9dc12d9f7b91a5911c9030a439eed8f5000000004948304502206e21798a42fae0e854281abd38bacd1aeed3ee3738d9e1446618c4571d1090db022100e2ac980643b0b82c0e88ffdfec6b64e3e6ba35e7ba5fdd7d5d6cc8d25c6b241501ffffffff0100f2052a010000001976a914404371705fa9bd789a2fcd52d2c580b65d35549d88ac00000000".from_hex().unwrap();
|
|
|
|
let t: Transaction = deserialize(&encoded_tx).unwrap();
|
|
|
|
assert_eq!(t.version, 1);
|
|
|
|
assert_eq!(t.lock_time, 0);
|
|
|
|
assert_eq!(t.transaction_inputs.len(), 1);
|
|
|
|
assert_eq!(t.transaction_outputs.len(), 1);
|
|
|
|
let tx_input = &t.transaction_inputs[0];
|
|
|
|
assert_eq!(tx_input.sequence, 4294967295);
|
2016-09-12 01:57:12 -07:00
|
|
|
assert_eq!(tx_input.script_sig, "48304502206e21798a42fae0e854281abd38bacd1aeed3ee3738d9e1446618c4571d1090db022100e2ac980643b0b82c0e88ffdfec6b64e3e6ba35e7ba5fdd7d5d6cc8d25c6b241501".from_hex().unwrap());
|
2016-08-16 03:09:40 -07:00
|
|
|
let tx_output = &t.transaction_outputs[0];
|
|
|
|
assert_eq!(tx_output.value, 5000000000);
|
2016-09-12 01:57:12 -07:00
|
|
|
assert_eq!(tx_output.script_pubkey, "76a914404371705fa9bd789a2fcd52d2c580b65d35549d88ac".from_hex().unwrap());
|
2016-08-16 03:09:40 -07:00
|
|
|
}
|
2016-08-19 05:50:22 -07:00
|
|
|
|
2016-08-16 03:09:40 -07:00
|
|
|
#[test]
|
|
|
|
fn test_transaction_hash() {
|
|
|
|
let encoded_tx = "0100000001a6b97044d03da79c005b20ea9c0e1a6d9dc12d9f7b91a5911c9030a439eed8f5000000004948304502206e21798a42fae0e854281abd38bacd1aeed3ee3738d9e1446618c4571d1090db022100e2ac980643b0b82c0e88ffdfec6b64e3e6ba35e7ba5fdd7d5d6cc8d25c6b241501ffffffff0100f2052a010000001976a914404371705fa9bd789a2fcd52d2c580b65d35549d88ac00000000".from_hex().unwrap();
|
2016-08-16 09:00:26 -07:00
|
|
|
let hash = h256_from_str("5a4ebf66822b0b2d56bd9dc64ece0bc38ee7844a23ff1d7320a88c5fdb2ad3e2");
|
2016-08-16 03:09:40 -07:00
|
|
|
let t: Transaction = deserialize(&encoded_tx).unwrap();
|
2016-08-16 09:00:26 -07:00
|
|
|
assert_eq!(t.hash(), hash);
|
2016-08-16 03:09:40 -07:00
|
|
|
}
|
|
|
|
}
|