commit
23b1c804e3
|
@ -398,6 +398,7 @@ name = "network"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chain 0.1.0",
|
"chain 0.1.0",
|
||||||
|
"primitives 0.1.0",
|
||||||
"serialization 0.1.0",
|
"serialization 0.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,10 @@ impl Block {
|
||||||
pub fn hash(&self) -> H256 {
|
pub fn hash(&self) -> H256 {
|
||||||
self.block_header.hash()
|
self.block_header.hash()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_final(&self, height: u32) -> bool {
|
||||||
|
self.transactions.iter().all(|t| t.is_final(height, self.block_header.time))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -23,7 +23,8 @@ pub use self::merkle_root::merkle_node_hash;
|
||||||
pub use self::transaction::{
|
pub use self::transaction::{
|
||||||
Transaction, TransactionInput, TransactionOutput, OutPoint,
|
Transaction, TransactionInput, TransactionOutput, OutPoint,
|
||||||
SEQUENCE_LOCKTIME_DISABLE_FLAG, SEQUENCE_FINAL,
|
SEQUENCE_LOCKTIME_DISABLE_FLAG, SEQUENCE_FINAL,
|
||||||
SEQUENCE_LOCKTIME_TYPE_FLAG, SEQUENCE_LOCKTIME_MASK
|
SEQUENCE_LOCKTIME_TYPE_FLAG, SEQUENCE_LOCKTIME_MASK,
|
||||||
|
LOCKTIME_THRESHOLD
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type ShortTransactionID = hash::H48;
|
pub type ShortTransactionID = hash::H48;
|
||||||
|
|
|
@ -30,6 +30,9 @@ pub const SEQUENCE_LOCKTIME_TYPE_FLAG: u32 = (1 << 22);
|
||||||
// applied to extract that lock-time from the sequence field.
|
// applied to extract that lock-time from the sequence field.
|
||||||
pub const SEQUENCE_LOCKTIME_MASK: u32 = 0x0000ffff;
|
pub const SEQUENCE_LOCKTIME_MASK: u32 = 0x0000ffff;
|
||||||
|
|
||||||
|
/// Threshold for `nLockTime`: below this value it is interpreted as block number,
|
||||||
|
/// otherwise as UNIX timestamp.
|
||||||
|
pub const LOCKTIME_THRESHOLD: u32 = 500000000; // Tue Nov 5 00:53:20 1985 UTC
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Default)]
|
#[derive(Debug, PartialEq, Clone, Default)]
|
||||||
pub struct OutPoint {
|
pub struct OutPoint {
|
||||||
|
@ -66,10 +69,6 @@ impl OutPoint {
|
||||||
&self.hash
|
&self.hash
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn index(&self) -> u32 {
|
|
||||||
self.index
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_null(&self) -> bool {
|
pub fn is_null(&self) -> bool {
|
||||||
self.hash.is_zero() && self.index == u32::max_value()
|
self.hash.is_zero() && self.index == u32::max_value()
|
||||||
}
|
}
|
||||||
|
@ -82,6 +81,12 @@ pub struct TransactionInput {
|
||||||
pub sequence: u32,
|
pub sequence: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TransactionInput {
|
||||||
|
pub fn is_final(&self) -> bool {
|
||||||
|
self.sequence == SEQUENCE_FINAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Serializable for TransactionInput {
|
impl Serializable for TransactionInput {
|
||||||
fn serialize(&self, stream: &mut Stream) {
|
fn serialize(&self, stream: &mut Stream) {
|
||||||
stream
|
stream
|
||||||
|
@ -116,20 +121,6 @@ impl HeapSizeOf for TransactionInput {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct TransactionOutput {
|
pub struct TransactionOutput {
|
||||||
pub value: u64,
|
pub value: u64,
|
||||||
|
@ -176,16 +167,6 @@ impl HeapSizeOf for TransactionOutput {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransactionOutput {
|
|
||||||
pub fn value(&self) -> u64 {
|
|
||||||
self.value
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn script_pubkey(&self) -> &[u8] {
|
|
||||||
&self.script_pubkey
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Default, Clone)]
|
#[derive(Debug, PartialEq, Default, Clone)]
|
||||||
pub struct Transaction {
|
pub struct Transaction {
|
||||||
pub version: i32,
|
pub version: i32,
|
||||||
|
@ -254,6 +235,24 @@ impl Transaction {
|
||||||
self.inputs.len() == 1 && self.inputs[0].previous_output.is_null()
|
self.inputs.len() == 1 && self.inputs[0].previous_output.is_null()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_final(&self, block_height: u32, block_time: u32) -> bool {
|
||||||
|
if self.lock_time == 0 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let max_lock_time = if self.lock_time < LOCKTIME_THRESHOLD {
|
||||||
|
block_height
|
||||||
|
} else {
|
||||||
|
block_time
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.lock_time < max_lock_time {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.inputs.iter().all(TransactionInput::is_final)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn total_spends(&self) -> u64 {
|
pub fn total_spends(&self) -> u64 {
|
||||||
self.outputs
|
self.outputs
|
||||||
.iter()
|
.iter()
|
||||||
|
|
|
@ -175,7 +175,7 @@ impl Storage {
|
||||||
if let Some(accepted_tx) = accepted_txs.iter().next() {
|
if let Some(accepted_tx) = accepted_txs.iter().next() {
|
||||||
context.meta.insert(
|
context.meta.insert(
|
||||||
accepted_tx.hash(),
|
accepted_tx.hash(),
|
||||||
TransactionMeta::new(number, accepted_tx.outputs.len()).coinbase()
|
TransactionMeta::new_coinbase(number, accepted_tx.outputs.len())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,29 +186,27 @@ impl Storage {
|
||||||
);
|
);
|
||||||
|
|
||||||
for input in &accepted_tx.inputs {
|
for input in &accepted_tx.inputs {
|
||||||
if !match context.meta.get_mut(&input.previous_output.hash) {
|
use std::collections::hash_map::Entry;
|
||||||
Some(ref mut meta) => {
|
|
||||||
|
match context.meta.entry(input.previous_output.hash.clone()) {
|
||||||
|
Entry::Occupied(mut entry) => {
|
||||||
|
let meta = entry.get_mut();
|
||||||
|
if meta.is_spent(input.previous_output.index as usize) {
|
||||||
|
return Err(Error::double_spend(&input.previous_output.hash));
|
||||||
|
}
|
||||||
|
meta.denote_used(input.previous_output.index as usize);
|
||||||
|
},
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
let mut meta = self.transaction_meta(&input.previous_output.hash)
|
||||||
|
.ok_or(Error::unknown_spending(&input.previous_output.hash))?;
|
||||||
|
|
||||||
if meta.is_spent(input.previous_output.index as usize) {
|
if meta.is_spent(input.previous_output.index as usize) {
|
||||||
return Err(Error::double_spend(&input.previous_output.hash));
|
return Err(Error::double_spend(&input.previous_output.hash));
|
||||||
}
|
}
|
||||||
|
|
||||||
meta.denote_used(input.previous_output.index as usize);
|
meta.denote_used(input.previous_output.index as usize);
|
||||||
true
|
entry.insert(meta);
|
||||||
},
|
},
|
||||||
None => false,
|
|
||||||
} {
|
|
||||||
let mut meta = self.transaction_meta(&input.previous_output.hash)
|
|
||||||
.ok_or(Error::unknown_spending(&input.previous_output.hash))?;
|
|
||||||
|
|
||||||
if meta.is_spent(input.previous_output.index as usize) {
|
|
||||||
return Err(Error::double_spend(&input.previous_output.hash));
|
|
||||||
}
|
|
||||||
|
|
||||||
meta.denote_used(input.previous_output.index as usize);
|
|
||||||
|
|
||||||
context.meta.insert(
|
|
||||||
input.previous_output.hash.clone(),
|
|
||||||
meta);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -238,30 +236,24 @@ impl Storage {
|
||||||
// remove meta
|
// remove meta
|
||||||
context.db_transaction.delete(Some(COL_TRANSACTIONS_META), &**tx_hash);
|
context.db_transaction.delete(Some(COL_TRANSACTIONS_META), &**tx_hash);
|
||||||
|
|
||||||
// denote outputs used
|
// coinbase transaction does not have inputs
|
||||||
if tx_hash_num == 0 { continue; } // coinbase transaction does not have inputs
|
if tx_hash_num == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// denote outputs as unused
|
||||||
for input in &tx.inputs {
|
for input in &tx.inputs {
|
||||||
if !match context.meta.get_mut(&input.previous_output.hash) {
|
use std::collections::hash_map::Entry;
|
||||||
Some(ref mut meta) => {
|
match context.meta.entry(input.previous_output.hash.clone()) {
|
||||||
meta.denote_unused(input.previous_output.index as usize);
|
Entry::Occupied(mut entry) => {
|
||||||
true
|
entry.get_mut().denote_unused(input.previous_output.index as usize);
|
||||||
|
},
|
||||||
|
Entry::Vacant(entry) => {
|
||||||
|
let mut meta = self.transaction_meta(&input.previous_output.hash)
|
||||||
|
.expect("No transaction metadata! Possible db corruption");
|
||||||
|
meta.denote_unused(input.previous_output.index as usize);
|
||||||
|
entry.insert(meta);
|
||||||
},
|
},
|
||||||
None => false,
|
|
||||||
} {
|
|
||||||
let mut meta =
|
|
||||||
self.transaction_meta(&input.previous_output.hash)
|
|
||||||
.unwrap_or_else(|| panic!(
|
|
||||||
// decanonization should always have meta
|
|
||||||
// because block could not have made canonical without writing meta
|
|
||||||
"No transaction metadata for {}! Corrupted DB? Reindex?",
|
|
||||||
&input.previous_output.hash
|
|
||||||
));
|
|
||||||
|
|
||||||
meta.denote_unused(input.previous_output.index as usize);
|
|
||||||
|
|
||||||
context.meta.insert(
|
|
||||||
input.previous_output.hash.clone(),
|
|
||||||
meta);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,8 @@ use byteorder::{LittleEndian, ByteOrder};
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TransactionMeta {
|
pub struct TransactionMeta {
|
||||||
block_height: u32,
|
block_height: u32,
|
||||||
// first bit is coinbase flag, others - one per output listed
|
/// first bit indicate if transaction is a coinbase transaction
|
||||||
|
/// next bits indicate if transaction has spend outputs
|
||||||
bits: BitVec,
|
bits: BitVec,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +18,7 @@ pub enum Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransactionMeta {
|
impl TransactionMeta {
|
||||||
/// new transaction description for indexing
|
/// New transaction description for indexing
|
||||||
pub fn new(block_height: u32, outputs: usize) -> Self {
|
pub fn new(block_height: u32, outputs: usize) -> Self {
|
||||||
TransactionMeta {
|
TransactionMeta {
|
||||||
block_height: block_height,
|
block_height: block_height,
|
||||||
|
@ -25,22 +26,25 @@ impl TransactionMeta {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn coinbase(mut self) -> Self {
|
/// New coinbase transaction
|
||||||
self.bits.set(0, true);
|
pub fn new_coinbase(block_height: u32, outputs: usize) -> Self {
|
||||||
self
|
let mut result = Self::new(block_height, outputs);
|
||||||
|
result.bits.set(0, true);
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if it is a coinbase transaction
|
||||||
pub fn is_coinbase(&self) -> bool {
|
pub fn is_coinbase(&self) -> bool {
|
||||||
self.bits.get(0)
|
self.bits.get(0)
|
||||||
.expect("One bit should always exists, since it is created as usize + 1; minimum value of usize is 0; 0 + 1 = 1; qed")
|
.expect("One bit should always exists, since it is created as usize + 1; minimum value of usize is 0; 0 + 1 = 1; qed")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// denote particular output as used
|
/// Denote particular output as used
|
||||||
pub fn denote_used(&mut self, index: usize) {
|
pub fn denote_used(&mut self, index: usize) {
|
||||||
self.bits.set(index + 1 , true);
|
self.bits.set(index + 1 , true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// denote particular output as not used
|
/// Denote particular output as not used
|
||||||
pub fn denote_unused(&mut self, index: usize) {
|
pub fn denote_unused(&mut self, index: usize) {
|
||||||
self.bits.set(index + 1, false);
|
self.bits.set(index + 1, false);
|
||||||
}
|
}
|
||||||
|
@ -61,8 +65,34 @@ impl TransactionMeta {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn height(&self) -> u32 { self.block_height }
|
pub fn height(&self) -> u32 {
|
||||||
|
self.block_height
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_spent(&self, idx: usize) -> bool { self.bits.get(idx + 1).expect("Index should be verified by the caller") }
|
pub fn is_spent(&self, idx: usize) -> bool {
|
||||||
|
self.bits.get(idx + 1).expect("Index should be verified by the caller")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_fully_spent(&self) -> bool {
|
||||||
|
// skip coinbase bit, the rest needs to true
|
||||||
|
self.bits.iter().skip(1).all(|x| x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::TransactionMeta;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_fully_spent() {
|
||||||
|
let t = TransactionMeta::new(0, 0);
|
||||||
|
assert!(t.is_fully_spent());
|
||||||
|
|
||||||
|
let mut t = TransactionMeta::new(0, 1);
|
||||||
|
assert!(!t.is_fully_spent());
|
||||||
|
t.denote_used(0);
|
||||||
|
assert!(t.is_fully_spent());
|
||||||
|
t.denote_unused(0);
|
||||||
|
assert!(!t.is_fully_spent());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,3 +6,4 @@ authors = ["debris <marek.kotewicz@gmail.com>"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serialization = { path = "../serialization" }
|
serialization = { path = "../serialization" }
|
||||||
chain = { path = "../chain" }
|
chain = { path = "../chain" }
|
||||||
|
primitives = { path = "../primitives" }
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use hash::H256;
|
||||||
use super::Magic;
|
use super::Magic;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -28,6 +29,11 @@ impl ConsensusParams {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_bip30_exception(&self, hash: &H256, height: u32) -> bool {
|
||||||
|
(height == 91842 && hash == &H256::from_reversed_str("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) ||
|
||||||
|
(height == 91880 && hash == &H256::from_reversed_str("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
extern crate chain;
|
extern crate chain;
|
||||||
|
extern crate primitives;
|
||||||
extern crate serialization as ser;
|
extern crate serialization as ser;
|
||||||
|
|
||||||
mod consensus;
|
mod consensus;
|
||||||
mod magic;
|
mod magic;
|
||||||
|
|
||||||
|
pub use primitives::hash;
|
||||||
|
|
||||||
pub use consensus::ConsensusParams;
|
pub use consensus::ConsensusParams;
|
||||||
pub use magic::Magic;
|
pub use magic::Magic;
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,6 @@ pub const MAX_PUBKEYS_PER_MULTISIG: usize = 20;
|
||||||
/// Maximum script length in bytes
|
/// Maximum script length in bytes
|
||||||
pub const MAX_SCRIPT_SIZE: usize = 10000;
|
pub const MAX_SCRIPT_SIZE: usize = 10000;
|
||||||
|
|
||||||
/// Threshold for `nLockTime`: below this value it is interpreted as block number,
|
|
||||||
/// otherwise as UNIX timestamp.
|
|
||||||
pub const LOCKTIME_THRESHOLD: u32 = 500000000; // Tue Nov 5 00:53:20 1985 UTC
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
pub enum ScriptType {
|
pub enum ScriptType {
|
||||||
NonStandard,
|
NonStandard,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use keys::{Public, Signature};
|
use keys::{Public, Signature};
|
||||||
use chain::{
|
use chain::{
|
||||||
SEQUENCE_FINAL, SEQUENCE_LOCKTIME_DISABLE_FLAG,
|
self, SEQUENCE_FINAL, SEQUENCE_LOCKTIME_DISABLE_FLAG,
|
||||||
SEQUENCE_LOCKTIME_MASK, SEQUENCE_LOCKTIME_TYPE_FLAG
|
SEQUENCE_LOCKTIME_MASK, SEQUENCE_LOCKTIME_TYPE_FLAG
|
||||||
};
|
};
|
||||||
use {script, SignatureVersion, Script, TransactionInputSigner, Num};
|
use {SignatureVersion, Script, TransactionInputSigner, Num};
|
||||||
|
|
||||||
pub trait SignatureChecker {
|
pub trait SignatureChecker {
|
||||||
fn check_signature(
|
fn check_signature(
|
||||||
|
@ -64,8 +64,8 @@ impl SignatureChecker for TransactionSignatureChecker {
|
||||||
// the nLockTime in the transaction.
|
// the nLockTime in the transaction.
|
||||||
let lock_time_u32: u32 = lock_time.into();
|
let lock_time_u32: u32 = lock_time.into();
|
||||||
if !(
|
if !(
|
||||||
(self.signer.lock_time < script::LOCKTIME_THRESHOLD && lock_time_u32 < script::LOCKTIME_THRESHOLD) ||
|
(self.signer.lock_time < chain::LOCKTIME_THRESHOLD && lock_time_u32 < chain::LOCKTIME_THRESHOLD) ||
|
||||||
(self.signer.lock_time >= script::LOCKTIME_THRESHOLD && lock_time_u32 >= script::LOCKTIME_THRESHOLD)
|
(self.signer.lock_time >= chain::LOCKTIME_THRESHOLD && lock_time_u32 >= chain::LOCKTIME_THRESHOLD)
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,13 @@ impl ChainVerifier {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ordered_verify(&self, block: &chain::Block, at_height: u32) -> Result<(), Error> {
|
fn ordered_verify(&self, block: &chain::Block, at_height: u32) -> Result<(), Error> {
|
||||||
|
if !block.is_final(at_height) {
|
||||||
|
return Err(Error::NonFinalBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
let block_hash = block.hash();
|
||||||
|
let consensus_params = self.network.consensus_params();
|
||||||
|
|
||||||
// check that difficulty matches the adjusted level
|
// check that difficulty matches the adjusted level
|
||||||
if let Some(work) = self.work_required(block, at_height) {
|
if let Some(work) = self.work_required(block, at_height) {
|
||||||
if !self.skip_pow && work != block.header().nbits {
|
if !self.skip_pow && work != block.header().nbits {
|
||||||
|
@ -68,6 +75,15 @@ impl ChainVerifier {
|
||||||
|
|
||||||
let coinbase_spends = block.transactions()[0].total_spends();
|
let coinbase_spends = block.transactions()[0].total_spends();
|
||||||
|
|
||||||
|
// bip30
|
||||||
|
for (tx_index, tx) in block.transactions.iter().enumerate() {
|
||||||
|
if let Some(meta) = self.store.transaction_meta(&tx.hash()) {
|
||||||
|
if !meta.is_fully_spent() && !consensus_params.is_bip30_exception(&block_hash, at_height) {
|
||||||
|
return Err(Error::Transaction(tx_index, TransactionError::UnspentTransactionWithTheSameHash));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut total_unspent = 0u64;
|
let mut total_unspent = 0u64;
|
||||||
for (tx_index, tx) in block.transactions().iter().enumerate().skip(1) {
|
for (tx_index, tx) in block.transactions().iter().enumerate().skip(1) {
|
||||||
|
|
||||||
|
@ -168,7 +184,7 @@ impl ChainVerifier {
|
||||||
signer: signer,
|
signer: signer,
|
||||||
input_index: input_index,
|
input_index: input_index,
|
||||||
};
|
};
|
||||||
let input: Script = input.script_sig().to_vec().into();
|
let input: Script = input.script_sig.to_vec().into();
|
||||||
let output: Script = paired_output.script_pubkey.to_vec().into();
|
let output: Script = paired_output.script_pubkey.to_vec().into();
|
||||||
|
|
||||||
if is_strict_p2sh && output.is_pay_to_script_hash() {
|
if is_strict_p2sh && output.is_pay_to_script_hash() {
|
||||||
|
@ -240,7 +256,7 @@ impl ChainVerifier {
|
||||||
// check that coinbase has a valid signature
|
// check that coinbase has a valid signature
|
||||||
let coinbase = &block.transactions()[0];
|
let coinbase = &block.transactions()[0];
|
||||||
// is_coinbase() = true above guarantees that there is at least one input
|
// is_coinbase() = true above guarantees that there is at least one input
|
||||||
let coinbase_script_len = coinbase.inputs[0].script_sig().len();
|
let coinbase_script_len = coinbase.inputs[0].script_sig.len();
|
||||||
if coinbase_script_len < 2 || coinbase_script_len > 100 {
|
if coinbase_script_len < 2 || coinbase_script_len > 100 {
|
||||||
return Err(Error::CoinbaseSignatureLength(coinbase_script_len));
|
return Err(Error::CoinbaseSignatureLength(coinbase_script_len));
|
||||||
}
|
}
|
||||||
|
@ -455,6 +471,7 @@ mod tests {
|
||||||
let genesis = test_data::block_builder()
|
let genesis = test_data::block_builder()
|
||||||
.transaction()
|
.transaction()
|
||||||
.coinbase()
|
.coinbase()
|
||||||
|
.output().value(1).build()
|
||||||
.build()
|
.build()
|
||||||
.transaction()
|
.transaction()
|
||||||
.output().value(50).build()
|
.output().value(50).build()
|
||||||
|
@ -488,6 +505,7 @@ mod tests {
|
||||||
let genesis = test_data::block_builder()
|
let genesis = test_data::block_builder()
|
||||||
.transaction()
|
.transaction()
|
||||||
.coinbase()
|
.coinbase()
|
||||||
|
.output().value(1).build()
|
||||||
.build()
|
.build()
|
||||||
.transaction()
|
.transaction()
|
||||||
.output().value(50).build()
|
.output().value(50).build()
|
||||||
|
@ -525,6 +543,7 @@ mod tests {
|
||||||
let genesis = test_data::block_builder()
|
let genesis = test_data::block_builder()
|
||||||
.transaction()
|
.transaction()
|
||||||
.coinbase()
|
.coinbase()
|
||||||
|
.output().value(1).build()
|
||||||
.build()
|
.build()
|
||||||
.transaction()
|
.transaction()
|
||||||
.output().value(50).build()
|
.output().value(50).build()
|
||||||
|
|
|
@ -59,6 +59,8 @@ pub enum Error {
|
||||||
CoinbaseSignatureLength(usize),
|
CoinbaseSignatureLength(usize),
|
||||||
/// Block size is invalid
|
/// Block size is invalid
|
||||||
Size(usize),
|
Size(usize),
|
||||||
|
/// Block transactions are not final.
|
||||||
|
NonFinalBlock,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
|
@ -84,6 +86,8 @@ pub enum TransactionError {
|
||||||
SigopsP2SH(usize),
|
SigopsP2SH(usize),
|
||||||
/// Coinbase transaction is found at position that is not 0
|
/// Coinbase transaction is found at position that is not 0
|
||||||
MisplacedCoinbase(usize),
|
MisplacedCoinbase(usize),
|
||||||
|
/// Not fully spent transaction with the same hash already exists, bip30.
|
||||||
|
UnspentTransactionWithTheSameHash,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
|
|
|
@ -121,7 +121,7 @@ pub fn transaction_sigops(transaction: &chain::Transaction) -> Result<usize, scr
|
||||||
if transaction.is_coinbase() { return Ok(result); }
|
if transaction.is_coinbase() { return Ok(result); }
|
||||||
|
|
||||||
for input in &transaction.inputs {
|
for input in &transaction.inputs {
|
||||||
let input_script: Script = input.script_sig().to_vec().into();
|
let input_script: Script = input.script_sig.to_vec().into();
|
||||||
result += try!(input_script.sigop_count(false));
|
result += try!(input_script.sigop_count(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue