Merge branch 'master' into fix_121
This commit is contained in:
commit
09de1e051a
|
@ -69,6 +69,10 @@ impl OutPoint {
|
||||||
pub fn index(&self) -> u32 {
|
pub fn index(&self) -> u32 {
|
||||||
self.index
|
self.index
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_null(&self) -> bool {
|
||||||
|
self.hash.is_zero() && self.index == u32::max_value()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Default, Clone)]
|
#[derive(Debug, PartialEq, Default, Clone)]
|
||||||
|
@ -247,8 +251,7 @@ impl Transaction {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_coinbase(&self) -> bool {
|
pub fn is_coinbase(&self) -> bool {
|
||||||
if self.inputs.len() != 1 { return false; }
|
self.inputs.len() == 1 && self.inputs[0].previous_output.is_null()
|
||||||
self.inputs[0].previous_output.hash.is_zero() && self.inputs[0].previous_output.index == 0xffffffff
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn total_spends(&self) -> u64 {
|
pub fn total_spends(&self) -> u64 {
|
||||||
|
|
|
@ -3,7 +3,7 @@ use super::BlockLocation;
|
||||||
use chain;
|
use chain;
|
||||||
use error::Error;
|
use error::Error;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct Reorganization {
|
pub struct Reorganization {
|
||||||
pub height: u32,
|
pub height: u32,
|
||||||
canonized: Vec<H256>,
|
canonized: Vec<H256>,
|
||||||
|
@ -24,7 +24,7 @@ impl Reorganization {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum BlockInsertedChain {
|
pub enum BlockInsertedChain {
|
||||||
Disconnected,
|
Disconnected,
|
||||||
Main,
|
Main,
|
||||||
|
|
|
@ -23,6 +23,10 @@ impl Error {
|
||||||
Error::Consistency(ConsistencyError::UnknownNumber(n))
|
Error::Consistency(ConsistencyError::UnknownNumber(n))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn unknown_spending(h: &H256) -> Self {
|
||||||
|
Error::Consistency(ConsistencyError::UnknownSpending(h.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn double_spend(h: &H256) -> Self {
|
pub fn double_spend(h: &H256) -> Self {
|
||||||
Error::Consistency(ConsistencyError::DoubleSpend(h.clone()))
|
Error::Consistency(ConsistencyError::DoubleSpend(h.clone()))
|
||||||
}
|
}
|
||||||
|
|
|
@ -297,7 +297,7 @@ impl Database {
|
||||||
cfs = cfnames.iter().enumerate().map(|(i, n)| db.create_cf(n, &cf_options[i]).unwrap()).collect();
|
cfs = cfnames.iter().enumerate().map(|(i, n)| db.create_cf(n, &cf_options[i]).unwrap()).collect();
|
||||||
Ok(db)
|
Ok(db)
|
||||||
},
|
},
|
||||||
err @ Err(_) => err,
|
err => err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
//! Bitcoin storage
|
//! Bitcoin storage
|
||||||
|
|
||||||
use std::{self, fs};
|
use std::fs;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use kvdb::{Database, DatabaseConfig};
|
use kvdb::{Database, DatabaseConfig};
|
||||||
use byteorder::{LittleEndian, ByteOrder};
|
use byteorder::{LittleEndian, ByteOrder};
|
||||||
use primitives::hash::H256;
|
use primitives::hash::H256;
|
||||||
use primitives::bytes::Bytes;
|
use primitives::bytes::Bytes;
|
||||||
use super::{BlockRef, BestBlock, BlockLocation};
|
use super::{BlockRef, BestBlock, BlockLocation};
|
||||||
use serialization;
|
use serialization::{serialize, deserialize};
|
||||||
use chain::{self, RepresentH256};
|
use chain::{self, RepresentH256};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use transaction_meta::TransactionMeta;
|
use transaction_meta::TransactionMeta;
|
||||||
|
@ -110,22 +110,9 @@ impl Storage {
|
||||||
self.read_meta(key).map(|val| LittleEndian::read_u32(&val))
|
self.read_meta(key).map(|val| LittleEndian::read_u32(&val))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// is invoked on database non-fatal query errors
|
|
||||||
fn db_error(&self, msg: String) {
|
|
||||||
println!("Low-level database error: {}", &msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// get the value of the key in the database
|
/// get the value of the key in the database
|
||||||
/// if the key is not present, reports non-fatal error and returns nothing
|
|
||||||
fn get(&self, col: u32, key: &[u8]) -> Option<Bytes> {
|
fn get(&self, col: u32, key: &[u8]) -> Option<Bytes> {
|
||||||
let res = self.database.get(Some(col), key);
|
self.database.get(Some(col), key).expect("fatal db error")
|
||||||
match res {
|
|
||||||
Err(msg) => {
|
|
||||||
self.db_error(msg);
|
|
||||||
None
|
|
||||||
},
|
|
||||||
Ok(val) => val.map(|v| v.into()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// resolves hash for the block reference (which can be referenced by number or
|
/// resolves hash for the block reference (which can be referenced by number or
|
||||||
|
@ -140,7 +127,7 @@ impl Storage {
|
||||||
/// loads block transaction list by the provided block hash
|
/// loads block transaction list by the provided block hash
|
||||||
fn block_transaction_hashes_by_hash(&self, h: &H256) -> Vec<H256> {
|
fn block_transaction_hashes_by_hash(&self, h: &H256) -> Vec<H256> {
|
||||||
self.get(COL_BLOCK_TRANSACTIONS, &**h)
|
self.get(COL_BLOCK_TRANSACTIONS, &**h)
|
||||||
.unwrap_or(Vec::new().into())
|
.unwrap_or_else(Bytes::new)
|
||||||
.chunks(H256::size())
|
.chunks(H256::size())
|
||||||
.map(H256::from)
|
.map(H256::from)
|
||||||
.collect()
|
.collect()
|
||||||
|
@ -150,25 +137,19 @@ impl Storage {
|
||||||
self.block_transaction_hashes_by_hash(h)
|
self.block_transaction_hashes_by_hash(h)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|tx_hash| {
|
.filter_map(|tx_hash| {
|
||||||
self.transaction_bytes(&tx_hash).and_then(|tx_bytes| {
|
self.transaction_bytes(&tx_hash)
|
||||||
match serialization::deserialize::<_, chain::Transaction>(tx_bytes.as_ref()) {
|
.map(|tx_bytes| {
|
||||||
Ok(tx) => Some(tx),
|
deserialize::<_, chain::Transaction>(tx_bytes.as_ref())
|
||||||
Err(e) => {
|
.expect("Error deserializing transaction, possible db corruption")
|
||||||
self.db_error(format!("Error deserializing transaction, possible db corruption ({:?})", e));
|
})
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_header_by_hash(&self, h: &H256) -> Option<chain::BlockHeader> {
|
fn block_header_by_hash(&self, h: &H256) -> Option<chain::BlockHeader> {
|
||||||
self.get(COL_BLOCK_HEADERS, &**h).and_then(|val|
|
self.get(COL_BLOCK_HEADERS, &**h).map(|val| {
|
||||||
serialization::deserialize(val.as_ref()).map_err(
|
deserialize(val.as_ref()).expect("Error deserializing block header, possible db corruption")
|
||||||
|e| self.db_error(format!("Error deserializing block header, possible db corruption ({:?})", e))
|
})
|
||||||
).ok()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -176,15 +157,14 @@ impl Storage {
|
||||||
fn update_transactions_meta(&self, context: &mut UpdateContext, number: u32, accepted_txs: &[chain::Transaction])
|
fn update_transactions_meta(&self, context: &mut UpdateContext, number: u32, accepted_txs: &[chain::Transaction])
|
||||||
-> Result<(), Error>
|
-> Result<(), Error>
|
||||||
{
|
{
|
||||||
for (accepted_idx, accepted_tx) in accepted_txs.iter().enumerate() {
|
if let Some(accepted_tx) = accepted_txs.iter().next() {
|
||||||
if accepted_idx == 0 {
|
context.meta.insert(
|
||||||
context.meta.insert(
|
accepted_tx.hash(),
|
||||||
accepted_tx.hash(),
|
TransactionMeta::new(number, accepted_tx.outputs.len()).coinbase()
|
||||||
TransactionMeta::new(number, accepted_tx.outputs.len()).coinbase()
|
);
|
||||||
);
|
}
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
for accepted_tx in accepted_txs.iter().skip(1) {
|
||||||
context.meta.insert(
|
context.meta.insert(
|
||||||
accepted_tx.hash(),
|
accepted_tx.hash(),
|
||||||
TransactionMeta::new(number, accepted_tx.outputs.len())
|
TransactionMeta::new(number, accepted_tx.outputs.len())
|
||||||
|
@ -202,11 +182,8 @@ impl Storage {
|
||||||
},
|
},
|
||||||
None => false,
|
None => false,
|
||||||
} {
|
} {
|
||||||
let mut meta =
|
let mut meta = self.transaction_meta(&input.previous_output.hash)
|
||||||
try!(
|
.ok_or(Error::unknown_spending(&input.previous_output.hash))?;
|
||||||
self.transaction_meta(&input.previous_output.hash)
|
|
||||||
.ok_or(Error::Consistency(ConsistencyError::UnknownSpending(input.previous_output.hash.clone())))
|
|
||||||
);
|
|
||||||
|
|
||||||
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));
|
||||||
|
@ -314,8 +291,8 @@ impl Storage {
|
||||||
try!(self.update_transactions_meta(context, at_height, &transactions));
|
try!(self.update_transactions_meta(context, at_height, &transactions));
|
||||||
|
|
||||||
// only canonical blocks are allowed to wield a number
|
// only canonical blocks are allowed to wield a number
|
||||||
context.db_transaction.put(Some(COL_BLOCK_HASHES), &u32_key(at_height), std::ops::Deref::deref(hash));
|
context.db_transaction.put(Some(COL_BLOCK_HASHES), &u32_key(at_height), &**hash);
|
||||||
context.db_transaction.write_u32(Some(COL_BLOCK_NUMBERS), std::ops::Deref::deref(hash), at_height);
|
context.db_transaction.write_u32(Some(COL_BLOCK_NUMBERS), &**hash, at_height);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -330,7 +307,7 @@ impl Storage {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// todo: log error here
|
// todo: log error here
|
||||||
context.restore();
|
context.restore();
|
||||||
println!("Error while reorganizing to {}: {:?}", hash, e);
|
error!("Error while reorganizing to {}: {:?}", hash, e);
|
||||||
Err(e)
|
Err(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -410,16 +387,11 @@ impl BlockProvider for Storage {
|
||||||
fn block(&self, block_ref: BlockRef) -> Option<chain::Block> {
|
fn block(&self, block_ref: BlockRef) -> Option<chain::Block> {
|
||||||
self.resolve_hash(block_ref).and_then(|block_hash|
|
self.resolve_hash(block_ref).and_then(|block_hash|
|
||||||
self.get(COL_BLOCK_HEADERS, &*block_hash)
|
self.get(COL_BLOCK_HEADERS, &*block_hash)
|
||||||
.and_then(|header_bytes| {
|
.map(|header_bytes| {
|
||||||
let transactions = self.block_transactions_by_hash(&block_hash);;
|
let transactions = self.block_transactions_by_hash(&block_hash);
|
||||||
let maybe_header = match serialization::deserialize::<_, chain::BlockHeader>(header_bytes.as_ref()) {
|
let header = deserialize::<_, chain::BlockHeader>(header_bytes.as_ref())
|
||||||
Ok(header) => Some(header),
|
.expect("Error deserializing header, possible db corruption");
|
||||||
Err(e) => {
|
chain::Block::new(header, transactions)
|
||||||
self.db_error(format!("Error deserializing header, possible db corruption ({:?})", e));
|
|
||||||
None
|
|
||||||
}
|
|
||||||
};
|
|
||||||
maybe_header.map(|header| chain::Block::new(header, transactions))
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -457,7 +429,7 @@ impl BlockStapler for Storage {
|
||||||
context.db_transaction.put(
|
context.db_transaction.put(
|
||||||
Some(COL_TRANSACTIONS),
|
Some(COL_TRANSACTIONS),
|
||||||
&*tx_hash,
|
&*tx_hash,
|
||||||
&serialization::serialize(tx),
|
&serialize(tx),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
context.db_transaction.put(Some(COL_BLOCK_TRANSACTIONS), &*block_hash, &tx_refs);
|
context.db_transaction.put(Some(COL_BLOCK_TRANSACTIONS), &*block_hash, &tx_refs);
|
||||||
|
@ -465,7 +437,7 @@ impl BlockStapler for Storage {
|
||||||
context.db_transaction.put(
|
context.db_transaction.put(
|
||||||
Some(COL_BLOCK_HEADERS),
|
Some(COL_BLOCK_HEADERS),
|
||||||
&*block_hash,
|
&*block_hash,
|
||||||
&serialization::serialize(block.header())
|
&serialize(block.header())
|
||||||
);
|
);
|
||||||
|
|
||||||
// the block is continuing the main chain
|
// the block is continuing the main chain
|
||||||
|
@ -474,8 +446,8 @@ impl BlockStapler for Storage {
|
||||||
context.db_transaction.write_u32(Some(COL_META), KEY_BEST_BLOCK_NUMBER, new_best_number);
|
context.db_transaction.write_u32(Some(COL_META), KEY_BEST_BLOCK_NUMBER, new_best_number);
|
||||||
|
|
||||||
// updating main chain height reference
|
// updating main chain height reference
|
||||||
context.db_transaction.put(Some(COL_BLOCK_HASHES), &u32_key(new_best_number), std::ops::Deref::deref(&block_hash));
|
context.db_transaction.put(Some(COL_BLOCK_HASHES), &u32_key(new_best_number), &*block_hash);
|
||||||
context.db_transaction.write_u32(Some(COL_BLOCK_NUMBERS), std::ops::Deref::deref(&block_hash), new_best_number);
|
context.db_transaction.write_u32(Some(COL_BLOCK_NUMBERS), &*block_hash, new_best_number);
|
||||||
|
|
||||||
BlockInsertedChain::Main
|
BlockInsertedChain::Main
|
||||||
}
|
}
|
||||||
|
@ -493,8 +465,8 @@ impl BlockStapler for Storage {
|
||||||
// and we canonize it also by provisioning transactions
|
// and we canonize it also by provisioning transactions
|
||||||
try!(self.update_transactions_meta(&mut context, new_best_number, block.transactions()));
|
try!(self.update_transactions_meta(&mut context, new_best_number, block.transactions()));
|
||||||
context.db_transaction.write_u32(Some(COL_META), KEY_BEST_BLOCK_NUMBER, new_best_number);
|
context.db_transaction.write_u32(Some(COL_META), KEY_BEST_BLOCK_NUMBER, new_best_number);
|
||||||
context.db_transaction.put(Some(COL_BLOCK_HASHES), &u32_key(new_best_number), std::ops::Deref::deref(&new_best_hash));
|
context.db_transaction.put(Some(COL_BLOCK_HASHES), &u32_key(new_best_number), &*new_best_hash);
|
||||||
context.db_transaction.write_u32(Some(COL_BLOCK_NUMBERS), std::ops::Deref::deref(&new_best_hash), new_best_number);
|
context.db_transaction.write_u32(Some(COL_BLOCK_NUMBERS), &*new_best_hash, new_best_number);
|
||||||
|
|
||||||
reorg.push_canonized(&new_best_hash);
|
reorg.push_canonized(&new_best_hash);
|
||||||
|
|
||||||
|
@ -551,7 +523,7 @@ impl BlockStapler for Storage {
|
||||||
};
|
};
|
||||||
|
|
||||||
// we always update best hash even if it is not changed
|
// we always update best hash even if it is not changed
|
||||||
context.db_transaction.put(Some(COL_META), KEY_BEST_BLOCK_HASH, std::ops::Deref::deref(&new_best_hash));
|
context.db_transaction.put(Some(COL_META), KEY_BEST_BLOCK_HASH, &*new_best_hash);
|
||||||
|
|
||||||
// write accumulated transactions meta
|
// write accumulated transactions meta
|
||||||
try!(context.apply(&self.database));
|
try!(context.apply(&self.database));
|
||||||
|
@ -593,8 +565,7 @@ impl TransactionProvider for Storage {
|
||||||
|
|
||||||
fn transaction(&self, hash: &H256) -> Option<chain::Transaction> {
|
fn transaction(&self, hash: &H256) -> Option<chain::Transaction> {
|
||||||
self.transaction_bytes(hash).map(|tx_bytes| {
|
self.transaction_bytes(hash).map(|tx_bytes| {
|
||||||
serialization::deserialize(tx_bytes.as_ref())
|
deserialize(tx_bytes.as_ref()).expect("Failed to deserialize transaction: db corrupted?")
|
||||||
.unwrap_or_else(|e| panic!("Failed to deserialize transaction: db corrupted? ({:?})", e))
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -603,7 +574,7 @@ impl TransactionMetaProvider for Storage {
|
||||||
|
|
||||||
fn transaction_meta(&self, hash: &H256) -> Option<TransactionMeta> {
|
fn transaction_meta(&self, hash: &H256) -> Option<TransactionMeta> {
|
||||||
self.get(COL_TRANSACTIONS_META, &**hash).map(|val|
|
self.get(COL_TRANSACTIONS_META, &**hash).map(|val|
|
||||||
TransactionMeta::from_bytes(&val).unwrap_or_else(|e| panic!("Invalid transaction metadata: db corrupted? ({:?})", e))
|
TransactionMeta::from_bytes(&val).expect("Invalid transaction metadata: db corrupted?")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1172,8 +1143,7 @@ mod tests {
|
||||||
|
|
||||||
let inserted_chain = store.insert_block(&test_data::genesis()).unwrap();
|
let inserted_chain = store.insert_block(&test_data::genesis()).unwrap();
|
||||||
|
|
||||||
if let BlockInsertedChain::Main = inserted_chain { }
|
assert_eq!(inserted_chain, BlockInsertedChain::Main, "Genesis should become main chain");
|
||||||
else { panic!("Genesis should become main chain"); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1186,8 +1156,7 @@ mod tests {
|
||||||
|
|
||||||
let inserted_chain = store.insert_block(&test_data::block_h1()).unwrap();
|
let inserted_chain = store.insert_block(&test_data::block_h1()).unwrap();
|
||||||
|
|
||||||
if let BlockInsertedChain::Main = inserted_chain { }
|
assert_eq!(inserted_chain, BlockInsertedChain::Main, "h1 should become main chain");
|
||||||
else { panic!("h1 should become main chain"); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1213,8 +1182,7 @@ mod tests {
|
||||||
|
|
||||||
let inserted_chain = store.insert_block(&block2_side).unwrap();
|
let inserted_chain = store.insert_block(&block2_side).unwrap();
|
||||||
|
|
||||||
if let BlockInsertedChain::Side = inserted_chain { }
|
assert_eq!(inserted_chain, BlockInsertedChain::Side, "h1 should become main chain");
|
||||||
else { panic!("h1 should become main chain"); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -141,12 +141,11 @@ impl BlockStapler for TestStorage {
|
||||||
|
|
||||||
// supports only main chain in test storage
|
// supports only main chain in test storage
|
||||||
fn accepted_location(&self, header: &chain::BlockHeader) -> Option<BlockLocation> {
|
fn accepted_location(&self, header: &chain::BlockHeader) -> Option<BlockLocation> {
|
||||||
if self.best_block().is_none() { return Some(BlockLocation::Main(0)); }
|
match self.best_block() {
|
||||||
|
None => Some(BlockLocation::Main(0)),
|
||||||
let best = self.best_block().unwrap();
|
Some(ref best) if best.hash == header.previous_header_hash => Some(BlockLocation::Main(best.number + 1)),
|
||||||
if best.hash == header.previous_header_hash { return Some(BlockLocation::Main(best.number + 1)); }
|
_ => None
|
||||||
|
}
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ use std::collections::HashMap;
|
||||||
use storage::COL_TRANSACTIONS_META;
|
use storage::COL_TRANSACTIONS_META;
|
||||||
use primitives::hash::H256;
|
use primitives::hash::H256;
|
||||||
use error::Error;
|
use error::Error;
|
||||||
use std;
|
|
||||||
|
|
||||||
pub struct UpdateContext {
|
pub struct UpdateContext {
|
||||||
pub meta: HashMap<H256, TransactionMeta>,
|
pub meta: HashMap<H256, TransactionMeta>,
|
||||||
|
@ -38,7 +37,7 @@ impl UpdateContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn restore(&mut self) {
|
pub fn restore(&mut self) {
|
||||||
if let Some(meta_snapshot) = std::mem::replace(&mut self.meta_snapshot, None) {
|
if let Some(meta_snapshot) = self.meta_snapshot.take() {
|
||||||
self.meta = meta_snapshot;
|
self.meta = meta_snapshot;
|
||||||
self.db_transaction.rollback();
|
self.db_transaction.rollback();
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,10 @@ use hex::{ToHex, FromHex, FromHexError};
|
||||||
pub struct Bytes(Vec<u8>);
|
pub struct Bytes(Vec<u8>);
|
||||||
|
|
||||||
impl Bytes {
|
impl Bytes {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Bytes::default()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new_with_len(len: usize) -> Self {
|
pub fn new_with_len(len: usize) -> Self {
|
||||||
Bytes(vec![0; len])
|
Bytes(vec![0; len])
|
||||||
}
|
}
|
||||||
|
|
|
@ -282,7 +282,7 @@ mod tests {
|
||||||
let server = Arc::new(DummyServer::new());
|
let server = Arc::new(DummyServer::new());
|
||||||
let config = Config { threads_num: 1 };
|
let config = Config { threads_num: 1 };
|
||||||
let client_core = SynchronizationClientCore::new(config, &handle, executor.clone(), chain.clone());
|
let client_core = SynchronizationClientCore::new(config, &handle, executor.clone(), chain.clone());
|
||||||
let mut verifier = DummyVerifier::new();
|
let mut verifier = DummyVerifier::default();
|
||||||
verifier.set_sink(client_core.clone());
|
verifier.set_sink(client_core.clone());
|
||||||
let client = SynchronizationClient::new(client_core, verifier);
|
let client = SynchronizationClient::new(client_core, verifier);
|
||||||
let local_node = LocalNode::new(server.clone(), client, executor.clone());
|
let local_node = LocalNode::new(server.clone(), client, executor.clone());
|
||||||
|
|
|
@ -1107,10 +1107,7 @@ pub mod tests {
|
||||||
let config = Config { threads_num: 1 };
|
let config = Config { threads_num: 1 };
|
||||||
|
|
||||||
let client_core = SynchronizationClientCore::new(config, &handle, executor.clone(), chain.clone());
|
let client_core = SynchronizationClientCore::new(config, &handle, executor.clone(), chain.clone());
|
||||||
let mut verifier = match verifier {
|
let mut verifier = verifier.unwrap_or_default();
|
||||||
Some(verifier) => verifier,
|
|
||||||
None => DummyVerifier::new(),
|
|
||||||
};
|
|
||||||
verifier.set_sink(client_core.clone());
|
verifier.set_sink(client_core.clone());
|
||||||
let client = SynchronizationClient::new(client_core, verifier);
|
let client = SynchronizationClient::new(client_core, verifier);
|
||||||
(event_loop, handle, executor, chain, client)
|
(event_loop, handle, executor, chain, client)
|
||||||
|
@ -1773,8 +1770,8 @@ pub mod tests {
|
||||||
let b22 = test_data::block_builder().header().parent(b21.hash()).build().build();
|
let b22 = test_data::block_builder().header().parent(b21.hash()).build().build();
|
||||||
let b23 = test_data::block_builder().header().parent(b22.hash()).build().build();
|
let b23 = test_data::block_builder().header().parent(b22.hash()).build().build();
|
||||||
|
|
||||||
// simulate verification during b21 verification
|
// TODO: simulate verification during b21 verification
|
||||||
let mut dummy_verifier = DummyVerifier::new();
|
let mut dummy_verifier = DummyVerifier::default();
|
||||||
dummy_verifier.error_when_verifying(b21.hash(), "simulated");
|
dummy_verifier.error_when_verifying(b21.hash(), "simulated");
|
||||||
|
|
||||||
let (_, _, _, _, sync) = create_sync(None, Some(dummy_verifier));
|
let (_, _, _, _, sync) = create_sync(None, Some(dummy_verifier));
|
||||||
|
|
|
@ -157,19 +157,13 @@ pub mod tests {
|
||||||
use primitives::hash::H256;
|
use primitives::hash::H256;
|
||||||
use super::{Verifier, VerificationSink};
|
use super::{Verifier, VerificationSink};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
pub struct DummyVerifier {
|
pub struct DummyVerifier {
|
||||||
sink: Option<Arc<Mutex<SynchronizationClientCore<DummyTaskExecutor>>>>,
|
sink: Option<Arc<Mutex<SynchronizationClientCore<DummyTaskExecutor>>>>,
|
||||||
errors: HashMap<H256, String>
|
errors: HashMap<H256, String>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DummyVerifier {
|
impl DummyVerifier {
|
||||||
pub fn new() -> Self {
|
|
||||||
DummyVerifier {
|
|
||||||
sink: None,
|
|
||||||
errors: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_sink(&mut self, sink: Arc<Mutex<SynchronizationClientCore<DummyTaskExecutor>>>) {
|
pub fn set_sink(&mut self, sink: Arc<Mutex<SynchronizationClientCore<DummyTaskExecutor>>>) {
|
||||||
self.sink = Some(sink);
|
self.sink = Some(sink);
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ impl ChainVerifier {
|
||||||
let coinbase_spends = block.transactions()[0].total_spends();
|
let coinbase_spends = block.transactions()[0].total_spends();
|
||||||
|
|
||||||
let mut total_unspent = 0u64;
|
let mut total_unspent = 0u64;
|
||||||
for (tx_index, tx) in block.transactions().iter().skip(1).enumerate() {
|
for (tx_index, tx) in block.transactions().iter().enumerate().skip(1) {
|
||||||
|
|
||||||
let mut total_claimed: u64 = 0;
|
let mut total_claimed: u64 = 0;
|
||||||
|
|
||||||
|
@ -66,11 +66,10 @@ impl ChainVerifier {
|
||||||
if let Some(previous_meta) = self.store.transaction_meta(&input.previous_output.hash) {
|
if let Some(previous_meta) = self.store.transaction_meta(&input.previous_output.hash) {
|
||||||
// check if it exists only
|
// check if it exists only
|
||||||
// it will fail a little later if there is no transaction at all
|
// it will fail a little later if there is no transaction at all
|
||||||
if previous_meta.is_coinbase()
|
if previous_meta.is_coinbase() &&
|
||||||
&& (at_height < COINBASE_MATURITY ||
|
(at_height < COINBASE_MATURITY || at_height - COINBASE_MATURITY < previous_meta.height())
|
||||||
at_height - COINBASE_MATURITY < previous_meta.height())
|
|
||||||
{
|
{
|
||||||
return Err(Error::Transaction(tx_index+1, TransactionError::Maturity));
|
return Err(Error::Transaction(tx_index, TransactionError::Maturity));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,13 +79,13 @@ impl ChainVerifier {
|
||||||
// todo: optimize block decomposition vec<transaction> -> hashmap<h256, transaction>
|
// todo: optimize block decomposition vec<transaction> -> hashmap<h256, transaction>
|
||||||
.or(block.transactions().iter().find(|tx| !tx.is_coinbase() && tx.hash() == input.previous_output.hash).cloned())
|
.or(block.transactions().iter().find(|tx| !tx.is_coinbase() && tx.hash() == input.previous_output.hash).cloned())
|
||||||
.ok_or(
|
.ok_or(
|
||||||
Error::Transaction(tx_index+1, TransactionError::UnknownReference(input.previous_output.hash.clone()))
|
Error::Transaction(tx_index, TransactionError::UnknownReference(input.previous_output.hash.clone()))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
let output = try!(reference_tx.outputs.get(input.previous_output.index as usize)
|
let output = try!(reference_tx.outputs.get(input.previous_output.index as usize)
|
||||||
.ok_or(
|
.ok_or(
|
||||||
Error::Transaction(tx_index+1, TransactionError::Input(input.previous_output.index as usize))
|
Error::Transaction(tx_index, TransactionError::Input(input.previous_output.index as usize))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -96,7 +95,7 @@ impl ChainVerifier {
|
||||||
let total_spends = tx.total_spends();
|
let total_spends = tx.total_spends();
|
||||||
|
|
||||||
if total_claimed < total_spends {
|
if total_claimed < total_spends {
|
||||||
return Err(Error::Transaction(tx_index+1, TransactionError::Overspend));
|
return Err(Error::Transaction(tx_index, TransactionError::Overspend));
|
||||||
}
|
}
|
||||||
|
|
||||||
// total_claimed is greater than total_spends, checked above and returned otherwise, cannot overflow; qed
|
// total_claimed is greater than total_spends, checked above and returned otherwise, cannot overflow; qed
|
||||||
|
|
Loading…
Reference in New Issue