Move witness inside of TxIn.
This is a rather large breaking API change, but is significantly more sensible. In the "do not allow internal representation to represent an invalid state" category, this ensures that witness cannot have an length other than the number of inputs. Further, it reduces vec propagation, which may help performance in some cases by reducing allocs. Fianlly, this just makes more sense (tm). Witness are a per-input field like the scriptSig, placing them outside of the TxIn is just where they are serialized, not where they logically belong.
This commit is contained in:
parent
0aa5cee39b
commit
a33f00621b
|
@ -61,7 +61,6 @@ fn bitcoin_genesis_tx() -> Transaction {
|
||||||
lock_time: 0,
|
lock_time: 0,
|
||||||
input: vec![],
|
input: vec![],
|
||||||
output: vec![],
|
output: vec![],
|
||||||
witness: vec![]
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Inputs
|
// Inputs
|
||||||
|
@ -73,7 +72,8 @@ fn bitcoin_genesis_tx() -> Transaction {
|
||||||
prev_hash: Default::default(),
|
prev_hash: Default::default(),
|
||||||
prev_index: 0xFFFFFFFF,
|
prev_index: 0xFFFFFFFF,
|
||||||
script_sig: in_script,
|
script_sig: in_script,
|
||||||
sequence: MAX_SEQUENCE
|
sequence: MAX_SEQUENCE,
|
||||||
|
witness: vec![],
|
||||||
});
|
});
|
||||||
|
|
||||||
// Outputs
|
// Outputs
|
||||||
|
|
|
@ -66,8 +66,14 @@ pub struct TxIn {
|
||||||
/// to ignore this feature. This is generally never used since
|
/// to ignore this feature. This is generally never used since
|
||||||
/// the miner behaviour cannot be enforced.
|
/// the miner behaviour cannot be enforced.
|
||||||
pub sequence: u32,
|
pub sequence: u32,
|
||||||
|
/// Witness data: an array of byte-arrays.
|
||||||
|
/// Note that this field is *not* (de)serialized with the rest of the TxIn in
|
||||||
|
/// ConsensusEncodable/ConsennsusDecodable, as it is (de)serialized at the end of the full
|
||||||
|
/// Transaction. It *is* (de)serialized with the rest of the TxIn in other (de)serializationn
|
||||||
|
/// routines.
|
||||||
|
pub witness: Vec<Vec<u8>>
|
||||||
}
|
}
|
||||||
serde_struct_impl!(TxIn, prev_hash, prev_index, script_sig, sequence);
|
serde_struct_impl!(TxIn, prev_hash, prev_index, 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)]
|
||||||
|
@ -98,10 +104,8 @@ pub struct Transaction {
|
||||||
pub input: Vec<TxIn>,
|
pub input: Vec<TxIn>,
|
||||||
/// List of outputs
|
/// List of outputs
|
||||||
pub output: Vec<TxOut>,
|
pub output: Vec<TxOut>,
|
||||||
/// Witness data: for each txin, an array of byte-arrays
|
|
||||||
pub witness: Vec<Vec<Vec<u8>>>
|
|
||||||
}
|
}
|
||||||
serde_struct_impl!(Transaction, version, lock_time, input, output, witness);
|
serde_struct_impl!(Transaction, version, lock_time, input, output);
|
||||||
|
|
||||||
impl Transaction {
|
impl Transaction {
|
||||||
/// Computes a "normalized TXID" which does not include any signatures.
|
/// Computes a "normalized TXID" which does not include any signatures.
|
||||||
|
@ -111,9 +115,8 @@ impl Transaction {
|
||||||
let cloned_tx = Transaction {
|
let cloned_tx = Transaction {
|
||||||
version: self.version,
|
version: self.version,
|
||||||
lock_time: self.lock_time,
|
lock_time: self.lock_time,
|
||||||
input: self.input.iter().map(|txin| TxIn { script_sig: Script::new(), .. *txin }).collect(),
|
input: self.input.iter().map(|txin| TxIn { script_sig: Script::new(), witness: vec![], .. *txin }).collect(),
|
||||||
output: self.output.clone(),
|
output: self.output.clone(),
|
||||||
witness: vec![]
|
|
||||||
};
|
};
|
||||||
cloned_tx.bitcoin_hash()
|
cloned_tx.bitcoin_hash()
|
||||||
}
|
}
|
||||||
|
@ -165,7 +168,6 @@ impl Transaction {
|
||||||
lock_time: self.lock_time,
|
lock_time: self.lock_time,
|
||||||
input: vec![],
|
input: vec![],
|
||||||
output: vec![],
|
output: vec![],
|
||||||
witness: vec![]
|
|
||||||
};
|
};
|
||||||
// Add all inputs necessary..
|
// Add all inputs necessary..
|
||||||
if anyone_can_pay {
|
if anyone_can_pay {
|
||||||
|
@ -173,7 +175,8 @@ impl Transaction {
|
||||||
prev_hash: self.input[input_index].prev_hash,
|
prev_hash: self.input[input_index].prev_hash,
|
||||||
script_sig: script_pubkey.clone(),
|
script_sig: script_pubkey.clone(),
|
||||||
prev_index: self.input[input_index].prev_index,
|
prev_index: self.input[input_index].prev_index,
|
||||||
sequence: self.input[input_index].sequence
|
sequence: self.input[input_index].sequence,
|
||||||
|
witness: vec![],
|
||||||
}];
|
}];
|
||||||
} else {
|
} else {
|
||||||
tx.input = Vec::with_capacity(self.input.len());
|
tx.input = Vec::with_capacity(self.input.len());
|
||||||
|
@ -182,7 +185,8 @@ impl Transaction {
|
||||||
prev_hash: input.prev_hash,
|
prev_hash: input.prev_hash,
|
||||||
prev_index: input.prev_index,
|
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![],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -238,13 +242,39 @@ impl BitcoinHash for Transaction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_consensus_encoding!(TxIn, prev_hash, prev_index, script_sig, sequence);
|
|
||||||
impl_consensus_encoding!(TxOut, value, script_pubkey);
|
impl_consensus_encoding!(TxOut, value, script_pubkey);
|
||||||
|
|
||||||
|
impl<S: SimpleEncoder> ConsensusEncodable<S> for TxIn {
|
||||||
|
fn consensus_encode(&self, s: &mut S) -> Result <(), S::Error> {
|
||||||
|
try!(self.prev_hash.consensus_encode(s));
|
||||||
|
try!(self.prev_index.consensus_encode(s));
|
||||||
|
try!(self.script_sig.consensus_encode(s));
|
||||||
|
self.sequence.consensus_encode(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<D: SimpleDecoder> ConsensusDecodable<D> for TxIn {
|
||||||
|
fn consensus_decode(d: &mut D) -> Result<TxIn, D::Error> {
|
||||||
|
Ok(TxIn {
|
||||||
|
prev_hash: try!(ConsensusDecodable::consensus_decode(d)),
|
||||||
|
prev_index: try!(ConsensusDecodable::consensus_decode(d)),
|
||||||
|
script_sig: try!(ConsensusDecodable::consensus_decode(d)),
|
||||||
|
sequence: try!(ConsensusDecodable::consensus_decode(d)),
|
||||||
|
witness: vec![],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<S: SimpleEncoder> ConsensusEncodable<S> for Transaction {
|
impl<S: SimpleEncoder> ConsensusEncodable<S> for Transaction {
|
||||||
fn consensus_encode(&self, s: &mut S) -> Result <(), S::Error> {
|
fn consensus_encode(&self, s: &mut S) -> Result <(), S::Error> {
|
||||||
try!(self.version.consensus_encode(s));
|
try!(self.version.consensus_encode(s));
|
||||||
if self.witness.is_empty() {
|
let mut have_witness = false;
|
||||||
|
for input in &self.input {
|
||||||
|
if !input.witness.is_empty() {
|
||||||
|
have_witness = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !have_witness {
|
||||||
try!(self.input.consensus_encode(s));
|
try!(self.input.consensus_encode(s));
|
||||||
try!(self.output.consensus_encode(s));
|
try!(self.output.consensus_encode(s));
|
||||||
} else {
|
} else {
|
||||||
|
@ -252,8 +282,8 @@ impl<S: SimpleEncoder> ConsensusEncodable<S> for Transaction {
|
||||||
try!(1u8.consensus_encode(s));
|
try!(1u8.consensus_encode(s));
|
||||||
try!(self.input.consensus_encode(s));
|
try!(self.input.consensus_encode(s));
|
||||||
try!(self.output.consensus_encode(s));
|
try!(self.output.consensus_encode(s));
|
||||||
for witness in &self.witness {
|
for input in &self.input {
|
||||||
try!(witness.consensus_encode(s));
|
try!(input.witness.consensus_encode(s));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.lock_time.consensus_encode(s)
|
self.lock_time.consensus_encode(s)
|
||||||
|
@ -275,22 +305,19 @@ impl<D: SimpleDecoder> ConsensusDecodable<D> for Transaction {
|
||||||
input: input,
|
input: input,
|
||||||
output: vec![],
|
output: vec![],
|
||||||
lock_time: try!(ConsensusDecodable::consensus_decode(d)),
|
lock_time: try!(ConsensusDecodable::consensus_decode(d)),
|
||||||
witness: vec![]
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// BIP144 input witnesses
|
// BIP144 input witnesses
|
||||||
1 => {
|
1 => {
|
||||||
let input: Vec<TxIn> = try!(ConsensusDecodable::consensus_decode(d));
|
let mut input: Vec<TxIn> = try!(ConsensusDecodable::consensus_decode(d));
|
||||||
let output: Vec<TxOut> = try!(ConsensusDecodable::consensus_decode(d));
|
let output: Vec<TxOut> = try!(ConsensusDecodable::consensus_decode(d));
|
||||||
let mut witness: Vec<Vec<Vec<u8>>> = Vec::with_capacity(input.len());
|
for txin in input.iter_mut() {
|
||||||
for _ in 0..input.len() {
|
txin.witness = try!(ConsensusDecodable::consensus_decode(d));
|
||||||
witness.push(try!(ConsensusDecodable::consensus_decode(d)));
|
|
||||||
}
|
}
|
||||||
Ok(Transaction {
|
Ok(Transaction {
|
||||||
version: version,
|
version: version,
|
||||||
input: input,
|
input: input,
|
||||||
output: output,
|
output: output,
|
||||||
witness: witness,
|
|
||||||
lock_time: try!(ConsensusDecodable::consensus_decode(d))
|
lock_time: try!(ConsensusDecodable::consensus_decode(d))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -306,7 +333,6 @@ impl<D: SimpleDecoder> ConsensusDecodable<D> for Transaction {
|
||||||
input: input,
|
input: input,
|
||||||
output: try!(ConsensusDecodable::consensus_decode(d)),
|
output: try!(ConsensusDecodable::consensus_decode(d)),
|
||||||
lock_time: try!(ConsensusDecodable::consensus_decode(d)),
|
lock_time: try!(ConsensusDecodable::consensus_decode(d)),
|
||||||
witness: vec![]
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue