Newer
Older
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
// number higher than current best block
*write_blocks_blooms = update.blocks_blooms;
for (key, value) in write_blocks_blooms.iter() {
batch.write(db::COL_EXTRA, key, value);
}
},
BlockLocation::CanonChain => {
// update all existing blooms groups
for (key, value) in update.blocks_blooms {
match write_blocks_blooms.entry(key) {
hash_map::Entry::Occupied(mut entry) => {
entry.get_mut().accrue_bloom_group(&value);
batch.write(db::COL_EXTRA, entry.key(), entry.get());
},
hash_map::Entry::Vacant(entry) => {
batch.write(db::COL_EXTRA, entry.key(), &value);
entry.insert(value);
},
}
}
},
}
}
// These cached values must be updated last with all four locks taken to avoid
// cache decoherence
let mut best_block = self.pending_best_block.write();
if is_best && update.info.location != BlockLocation::Branch {
batch.put(db::COL_EXTRA, b"best", &update.info.hash);
*best_block = Some(BestBlock {
hash: update.info.hash,
number: update.info.number,
total_difficulty: update.info.total_difficulty,
timestamp: update.timestamp,
block: update.block.to_vec(),
});
let mut write_hashes = self.pending_block_hashes.write();
let mut write_details = self.pending_block_details.write();
let mut write_txs = self.pending_transaction_addresses.write();
batch.extend_with_cache(db::COL_EXTRA, &mut *write_details, update.block_details, CacheUpdatePolicy::Overwrite);
batch.extend_with_cache(db::COL_EXTRA, &mut *write_hashes, update.block_hashes, CacheUpdatePolicy::Overwrite);
batch.extend_with_option_cache(db::COL_EXTRA, &mut *write_txs, update.transactions_addresses, CacheUpdatePolicy::Overwrite);
/// Apply pending insertion updates
pub fn commit(&self) {
let mut pending_best_block = self.pending_best_block.write();
let mut pending_write_hashes = self.pending_block_hashes.write();
let mut pending_block_details = self.pending_block_details.write();
let mut pending_write_txs = self.pending_transaction_addresses.write();
let mut best_block = self.best_block.write();
let mut write_block_details = self.block_details.write();
let mut write_hashes = self.block_hashes.write();
let mut write_txs = self.transaction_addresses.write();
// update best block
if let Some(block) = pending_best_block.take() {
*best_block = block;
}
let pending_txs = mem::replace(&mut *pending_write_txs, HashMap::new());
let (retracted_txs, enacted_txs) = pending_txs.into_iter().partition::<HashMap<_, _>, _>(|&(_, ref value)| value.is_none());
let pending_hashes_keys: Vec<_> = pending_write_hashes.keys().cloned().collect();
let enacted_txs_keys: Vec<_> = enacted_txs.keys().cloned().collect();
let pending_block_hashes: Vec<_> = pending_block_details.keys().cloned().collect();
write_hashes.extend(mem::replace(&mut *pending_write_hashes, HashMap::new()));
write_txs.extend(enacted_txs.into_iter().map(|(k, v)| (k, v.expect("Transactions were partitioned; qed"))));
write_block_details.extend(mem::replace(&mut *pending_block_details, HashMap::new()));
for hash in retracted_txs.keys() {
write_txs.remove(hash);
}
let mut cache_man = self.cache_man.lock();
for n in pending_hashes_keys {
cache_man.note_used(CacheId::BlockHashes(n));
for hash in enacted_txs_keys {
cache_man.note_used(CacheId::TransactionAddresses(hash));
for hash in pending_block_hashes {
cache_man.note_used(CacheId::BlockDetails(hash));
/// Iterator that lists `first` and then all of `first`'s ancestors, by hash.
pub fn ancestry_iter(&self, first: H256) -> Option<AncestryIter> {
if self.is_known(&first) {
Some(AncestryIter {
current: first,
/// Given a block's `parent`, find every block header which represents a valid possible uncle.
pub fn find_uncle_headers(&self, parent: &H256, uncle_generations: usize) -> Option<Vec<Header>> {
self.find_uncle_hashes(parent, uncle_generations).map(|v| v.into_iter().filter_map(|h| self.block_header(&h)).collect())
}
/// Given a block's `parent`, find every block hash which represents a valid possible uncle.
pub fn find_uncle_hashes(&self, parent: &H256, uncle_generations: usize) -> Option<Vec<H256>> {
Marek Kotewicz
committed
if !self.is_known(parent) {
return None;
}
let mut excluded = HashSet::new();
Marek Kotewicz
committed
let ancestry = self.ancestry_iter(parent.clone())?;
for a in ancestry.clone().take(uncle_generations) {
if let Some(uncles) = self.uncle_hashes(&a) {
excluded.extend(uncles);
excluded.insert(a);
} else {
break
}
}
let mut ret = Vec::new();
for a in ancestry.skip(1).take(uncle_generations) {
if let Some(details) = self.block_details(&a) {
ret.extend(details.children.iter().filter(|h| !excluded.contains(h)))
} else {
break
}
/// This function returns modified block hashes.
fn prepare_block_hashes_update(&self, block_bytes: &[u8], info: &BlockInfo) -> HashMap<BlockNumber, H256> {
let mut block_hashes = HashMap::new();
Marek Kotewicz
committed
let block = BlockView::new(block_bytes);
let header = block.header_view();
let number = header.number();
match info.location {
BlockLocation::Branch => (),
Marek Kotewicz
committed
BlockLocation::CanonChain => {
block_hashes.insert(number, info.hash);
Marek Kotewicz
committed
},
BlockLocation::BranchBecomingCanonChain(ref data) => {
let ancestor_number = self.block_number(&data.ancestor).expect("Block number of ancestor is always in DB");
Marek Kotewicz
committed
let start_number = ancestor_number + 1;
for (index, hash) in data.enacted.iter().cloned().enumerate() {
block_hashes.insert(start_number + index as BlockNumber, hash);
Marek Kotewicz
committed
}
block_hashes.insert(number, info.hash);
Marek Kotewicz
committed
}
}
block_hashes
Marek Kotewicz
committed
}
/// This function returns modified block details.
/// Uses the given parent details or attempts to load them from the database.
fn prepare_block_details_update(&self, block_bytes: &[u8], info: &BlockInfo) -> HashMap<H256, BlockDetails> {
Marek Kotewicz
committed
let block = BlockView::new(block_bytes);
let header = block.header_view();
let parent_hash = header.parent_hash();
Marek Kotewicz
committed
// update parent
let mut parent_details = self.block_details(&parent_hash).unwrap_or_else(|| panic!("Invalid parent hash: {:?}", parent_hash));
parent_details.children.push(info.hash);
Marek Kotewicz
committed
// create current block details.
let details = BlockDetails {
number: header.number(),
Marek Kotewicz
committed
total_difficulty: info.total_difficulty,
children: vec![],
Marek Kotewicz
committed
// write to batch
let mut block_details = HashMap::new();
block_details.insert(parent_hash, parent_details);
block_details.insert(info.hash, details);
block_details
Marek Kotewicz
committed
}
/// This function returns modified block receipts.
fn prepare_block_receipts_update(&self, receipts: Vec<Receipt>, info: &BlockInfo) -> HashMap<H256, BlockReceipts> {
let mut block_receipts = HashMap::new();
block_receipts.insert(info.hash, BlockReceipts::new(receipts));
block_receipts
Marek Kotewicz
committed
}
/// This function returns modified transaction addresses.
fn prepare_transaction_addresses_update(&self, block_bytes: &[u8], info: &BlockInfo) -> HashMap<H256, Option<TransactionAddress>> {
Marek Kotewicz
committed
let block = BlockView::new(block_bytes);
let transaction_hashes = block.transaction_hashes();
match info.location {
BlockLocation::CanonChain => {
transaction_hashes.into_iter()
.enumerate()
.map(|(i ,tx_hash)| {
(tx_hash, Some(TransactionAddress {
})
.collect()
},
BlockLocation::BranchBecomingCanonChain(ref data) => {
let addresses = data.enacted.iter()
let body = self.block_body(hash).expect("Enacted block must be in database.");
let hashes = body.transaction_hashes();
hashes.into_iter()
.enumerate()
.map(|(i, tx_hash)| (tx_hash, Some(TransactionAddress {
})))
.collect::<HashMap<H256, Option<TransactionAddress>>>()
});
let current_addresses = transaction_hashes.into_iter()
.enumerate()
.map(|(i ,tx_hash)| {
(tx_hash, Some(TransactionAddress {
let retracted = data.retracted.iter().flat_map(|hash| {
let body = self.block_body(hash).expect("Retracted block must be in database.");
let hashes = body.transaction_hashes();
hashes.into_iter().map(|hash| (hash, None)).collect::<HashMap<H256, Option<TransactionAddress>>>()
});
// The order here is important! Don't remove transaction if it was part of enacted blocks as well.
retracted.chain(addresses).chain(current_addresses).collect()
},
BlockLocation::Branch => HashMap::new(),
}
Marek Kotewicz
committed
}
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
/// This functions returns modified blocks blooms.
///
/// To accelerate blooms lookups, blomms are stored in multiple
/// layers (BLOOM_LEVELS, currently 3).
/// ChainFilter is responsible for building and rebuilding these layers.
/// It returns them in HashMap, where values are Blooms and
/// keys are BloomIndexes. BloomIndex represents bloom location on one
/// of these layers.
///
/// To reduce number of queries to databse, block blooms are stored
/// in BlocksBlooms structure which contains info about several
/// (BLOOM_INDEX_SIZE, currently 16) consecutive blocks blooms.
///
/// Later, BloomIndexer is used to map bloom location on filter layer (BloomIndex)
/// to bloom location in database (BlocksBloomLocation).
///
fn prepare_block_blooms_update(&self, block_bytes: &[u8], info: &BlockInfo) -> HashMap<GroupPosition, BloomGroup> {
let block = BlockView::new(block_bytes);
let header = block.header_view();
let log_blooms = match info.location {
BlockLocation::Branch => HashMap::new(),
BlockLocation::CanonChain => {
let log_bloom = header.log_bloom();
if log_bloom.is_zero() {
HashMap::new()
} else {
let chain = bc::group::BloomGroupChain::new(self.blooms_config, self);
chain.insert(info.number as bc::Number, log_bloom)
}
},
BlockLocation::BranchBecomingCanonChain(ref data) => {
let ancestor_number = self.block_number(&data.ancestor).unwrap();
let start_number = ancestor_number + 1;
let range = start_number as bc::Number..self.best_block_number() as bc::Number;
let mut blooms: Vec<Bloom> = data.enacted.iter()
.map(|hash| self.block_header_data(hash).unwrap())
.map(|h| h.log_bloom())
.collect();
blooms.push(header.log_bloom());
let chain = bc::group::BloomGroupChain::new(self.blooms_config, self);
chain.replace(&range, blooms)
}
};
log_blooms.into_iter()
.map(|p| (From::from(p.0), From::from(p.1)))
.collect()
}
pub fn best_block_hash(&self) -> H256 {
self.best_block.read().hash
pub fn best_block_number(&self) -> BlockNumber {
self.best_block.read().number
/// Get best block timestamp.
pub fn best_block_timestamp(&self) -> u64 {
self.best_block.read().timestamp
}
/// Get best block total difficulty.
pub fn best_block_total_difficulty(&self) -> U256 {
self.best_block.read().total_difficulty
pub fn best_block_header(&self) -> encoded::Header {
let raw = BlockView::new(&block.block).header_view().rlp().as_raw().to_vec();
encoded::Header::new(raw)
pub fn cache_size(&self) -> CacheSize {
CacheSize {
blocks: self.block_headers.read().heap_size_of_children() + self.block_bodies.read().heap_size_of_children(),
block_details: self.block_details.read().heap_size_of_children(),
transaction_addresses: self.transaction_addresses.read().heap_size_of_children(),
blocks_blooms: self.blocks_blooms.read().heap_size_of_children(),
block_receipts: self.block_receipts.read().heap_size_of_children(),
/// Ticks our cache system and throws out any old data.
let current_size = self.cache_size().total();
let mut block_headers = self.block_headers.write();
let mut block_bodies = self.block_bodies.write();
let mut block_details = self.block_details.write();
let mut block_hashes = self.block_hashes.write();
let mut transaction_addresses = self.transaction_addresses.write();
let mut blocks_blooms = self.blocks_blooms.write();
let mut block_receipts = self.block_receipts.write();
let mut cache_man = self.cache_man.lock();
cache_man.collect_garbage(current_size, | ids | {
for id in &ids {
match *id {
CacheId::BlockHeader(ref h) => { block_headers.remove(h); },
CacheId::BlockBody(ref h) => { block_bodies.remove(h); },
CacheId::BlockDetails(ref h) => { block_details.remove(h); }
CacheId::BlockHashes(ref h) => { block_hashes.remove(h); }
CacheId::TransactionAddresses(ref h) => { transaction_addresses.remove(h); }
CacheId::BlocksBlooms(ref h) => { blocks_blooms.remove(h); }
CacheId::BlockReceipts(ref h) => { block_receipts.remove(h); }
block_headers.shrink_to_fit();
block_bodies.shrink_to_fit();
block_details.shrink_to_fit();
block_hashes.shrink_to_fit();
transaction_addresses.shrink_to_fit();
block_receipts.shrink_to_fit();
block_headers.heap_size_of_children() +
block_bodies.heap_size_of_children() +
block_details.heap_size_of_children() +
block_hashes.heap_size_of_children() +
transaction_addresses.heap_size_of_children() +
blocks_blooms.heap_size_of_children() +
/// Create a block body from a block.
pub fn block_to_body(block: &[u8]) -> Bytes {
let mut body = RlpStream::new_list(2);
let block_rlp = Rlp::new(block);
body.append_raw(block_rlp.at(1).as_raw(), 1);
body.append_raw(block_rlp.at(2).as_raw(), 1);
body.out()
}
/// Returns general blockchain information
pub fn chain_info(&self) -> BlockChainInfo {
// ensure data consistencly by locking everything first
let best_block = self.best_block.read();
let best_ancient_block = self.best_ancient_block.read();
BlockChainInfo {
total_difficulty: best_block.total_difficulty.clone(),
pending_total_difficulty: best_block.total_difficulty.clone(),
genesis_hash: self.genesis_hash(),
best_block_hash: best_block.hash,
best_block_timestamp: best_block.timestamp,
first_block_hash: self.first_block(),
first_block_number: From::from(self.first_block_number()),
ancient_block_hash: best_ancient_block.as_ref().map(|b| b.hash),
ancient_block_number: best_ancient_block.as_ref().map(|b| b.number),
}
}
use kvdb::KeyValueDB;
use kvdb_memorydb;
use receipt::{Receipt, TransactionOutcome};
use blockchain::{BlockProvider, BlockChain, Config, ImportRoute};
use tests::helpers::{
generate_dummy_blockchain, generate_dummy_blockchain_with_extra,
generate_dummy_empty_blockchain
};
use blockchain::generator::{BlockGenerator, BlockBuilder, BlockOptions};
use blockchain::extras::TransactionAddress;
use transaction::{Transaction, Action};
use log_entry::{LogEntry, LocalizedLogEntry};
Tomasz Drwięga
committed
use ethkey::Secret;
fn new_db() -> Arc<KeyValueDB> {
Arc::new(kvdb_memorydb::create(::db::NUM_COLUMNS.unwrap_or(0)))
fn new_chain(genesis: &[u8], db: Arc<KeyValueDB>) -> BlockChain {
#[test]
fn should_cache_best_block() {
// given
let genesis = BlockBuilder::genesis();
let first = genesis.add_block();
let db = new_db();
let bc = new_chain(&genesis.last().encoded(), db.clone());
assert_eq!(bc.best_block_number(), 0);
// when
let mut batch = db.transaction();
bc.insert_block(&mut batch, &first.last().encoded(), vec![]);
assert_eq!(bc.best_block_number(), 0);
bc.commit();
// NOTE no db.write here (we want to check if best block is cached)
// then
assert_eq!(bc.best_block_number(), 1);
assert!(bc.block(&bc.best_block_hash()).is_some(), "Best block should be queryable even without DB write.");
}
let genesis = BlockBuilder::genesis();
let first = genesis.add_block();
let genesis = genesis.last();
let first = first.last();
let genesis_hash = genesis.hash();
let first_hash = first.hash();
let db = new_db();
let bc = new_chain(&genesis.encoded(), db.clone());
assert_eq!(bc.genesis_hash(), genesis_hash);
assert_eq!(bc.best_block_hash(), genesis_hash);
assert_eq!(bc.block_hash(0), Some(genesis_hash));
assert_eq!(bc.block_details(&genesis_hash).unwrap().children, vec![]);
let mut batch = db.transaction();
bc.insert_block(&mut batch, &first.encoded(), vec![]);
assert_eq!(bc.block_hash(0), Some(genesis_hash));
assert_eq!(bc.best_block_hash(), first_hash);
assert_eq!(bc.block_hash(1), Some(first_hash));
assert_eq!(bc.block_details(&first_hash).unwrap().parent, genesis_hash);
assert_eq!(bc.block_details(&genesis_hash).unwrap().children, vec![first_hash]);
let genesis = BlockBuilder::genesis();
let first_10 = genesis.add_blocks(10);
let generator = BlockGenerator::new(vec![first_10]);
let db = new_db();
let bc = new_chain(&genesis.last().encoded(), db.clone());
let mut block_hashes = vec![genesis.last().hash()];
for block in generator {
block_hashes.push(block.hash());
bc.insert_block(&mut batch, &block.encoded(), vec![]);
assert_eq!(bc.ancestry_iter(block_hashes[0].clone()).unwrap().collect::<Vec<_>>(), block_hashes);
assert_eq!(block_hashes.len(), 11);
let genesis = BlockBuilder::genesis();
let b1a = genesis.add_block();
let b2a = b1a.add_block();
let b3a = b2a.add_block();
let b4a = b3a.add_block();
let b5a = b4a.add_block();
let b1b = genesis.add_block_with_difficulty(9);
let b2b = b1a.add_block_with_difficulty(9);
let b3b = b2a.add_block_with_difficulty(9);
let b4b = b3a.add_block_with_difficulty(9);
let b5b = b4a.add_block_with_difficulty(9);
let uncle_headers = vec![b4b.last().header(), b3b.last().header(), b2b.last().header()];
let b4a_hash = b4a.last().hash();
let generator = BlockGenerator::new(
vec![b1a, b1b, b2a, b2b, b3a, b3b, b4a, b4b, b5a, b5b]
);
let db = new_db();
let bc = new_chain(&genesis.last().encoded(), db.clone());
let mut batch = db.transaction();
for b in generator {
bc.insert_block(&mut batch, &b.encoded(), vec![]);
bc.commit();
}
assert_eq!(uncle_headers, bc.find_uncle_headers(&b4a_hash, 3).unwrap());
// TODO: insert block that already includes one of them as an uncle to check it's not allowed.
}
Tomasz Drwięga
committed
fn secret() -> Secret {
Tomasz Drwięga
committed
}
#[test]
fn test_fork_transaction_addresses() {
let t1 = Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
action: Action::Create,
value: 100.into(),
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
Tomasz Drwięga
committed
}.sign(&secret(), None);
let genesis = BlockBuilder::genesis();
let b1a = genesis.add_block_with_transactions(iter::once(t1));
let b1b = genesis.add_block_with_difficulty(9);
let b2 = b1b.add_block();
let b1a_hash = b1a.last().hash();
let b2_hash = b2.last().hash();
let db = new_db();
let bc = new_chain(&genesis.last().encoded(), db.clone());
let mut batch = db.transaction();
let _ = bc.insert_block(&mut batch, &b1a.last().encoded(), vec![]);
let _ = bc.insert_block(&mut batch, &b1b.last().encoded(), vec![]);
bc.commit();
db.write(batch).unwrap();
assert_eq!(bc.best_block_hash(), b1a_hash);
assert_eq!(bc.transaction_address(&t1_hash), Some(TransactionAddress {
index: 0,
}));
// now let's make forked chain the canon chain
let mut batch = db.transaction();
let _ = bc.insert_block(&mut batch, &b2.last().encoded(), vec![]);
bc.commit();
db.write(batch).unwrap();
// Transaction should be retracted
assert_eq!(bc.best_block_hash(), b2_hash);
assert_eq!(bc.transaction_address(&t1_hash), None);
}
#[test]
fn test_overwriting_transaction_addresses() {
let t1 = Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
action: Action::Create,
value: 100.into(),
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
Tomasz Drwięga
committed
}.sign(&secret(), None);
let t2 = Transaction {
nonce: 1.into(),
gas_price: 0.into(),
gas: 100_000.into(),
action: Action::Create,
value: 100.into(),
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
Tomasz Drwięga
committed
}.sign(&secret(), None);
let t3 = Transaction {
nonce: 2.into(),
gas_price: 0.into(),
gas: 100_000.into(),
action: Action::Create,
value: 100.into(),
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
Tomasz Drwięga
committed
}.sign(&secret(), None);
let genesis = BlockBuilder::genesis();
let b1a = genesis.add_block_with_transactions(vec![t1.clone(), t2.clone()]);
// insert transactions in different order,
// the block has lower difficulty, so the hash is also different
let b1b = genesis.add_block_with(|| BlockOptions {
difficulty: 9.into(),
transactions: vec![t2.clone(), t1.clone()],
..Default::default()
});
let b2 = b1b.add_block_with_transactions(iter::once(t3.clone()));
let b1a_hash = b1a.last().hash();
let b1b_hash = b1b.last().hash();
let b2_hash = b2.last().hash();
let t1_hash = t1.hash();
let t2_hash = t2.hash();
let t3_hash = t3.hash();
let db = new_db();
let bc = new_chain(&genesis.last().encoded(), db.clone());
let mut batch = db.transaction();
let _ = bc.insert_block(&mut batch, &b1a.last().encoded(), vec![]);
let _ = bc.insert_block(&mut batch, &b1b.last().encoded(), vec![]);
bc.commit();
db.write(batch).unwrap();
assert_eq!(bc.best_block_hash(), b1a_hash);
assert_eq!(bc.transaction_address(&t1_hash), Some(TransactionAddress {
}));
assert_eq!(bc.transaction_address(&t2_hash), Some(TransactionAddress {
// now let's make forked chain the canon chain
let mut batch = db.transaction();
let _ = bc.insert_block(&mut batch, &b2.last().encoded(), vec![]);
bc.commit();
db.write(batch).unwrap();
assert_eq!(bc.best_block_hash(), b2_hash);
assert_eq!(bc.transaction_address(&t1_hash), Some(TransactionAddress {
}));
assert_eq!(bc.transaction_address(&t2_hash), Some(TransactionAddress {
}));
assert_eq!(bc.transaction_address(&t3_hash), Some(TransactionAddress {
let genesis = BlockBuilder::genesis();
let b1 = genesis.add_block();
let b2 = b1.add_block();
let b3a = b2.add_block();
let b3b = b2.add_block_with_difficulty(9);
let genesis_hash = genesis.last().hash();
let b1_hash = b1.last().hash();
let b2_hash = b2.last().hash();
let b3a_hash = b3a.last().hash();
let b3b_hash = b3b.last().hash();
// b3a is a part of canon chain, whereas b3b is part of sidechain
let best_block_hash = b3a_hash;
let db = new_db();
let bc = new_chain(&genesis.last().encoded(), db.clone());
let mut batch = db.transaction();
let ir1 = bc.insert_block(&mut batch, &b1.last().encoded(), vec![]);
let ir2 = bc.insert_block(&mut batch, &b2.last().encoded(), vec![]);
let ir3b = bc.insert_block(&mut batch, &b3b.last().encoded(), vec![]);
assert_eq!(bc.block_hash(3).unwrap(), b3b_hash);
let mut batch = db.transaction();
let ir3a = bc.insert_block(&mut batch, &b3a.last().encoded(), vec![]);
enacted: vec![b1_hash],
retracted: vec![],
enacted: vec![b2_hash],
retracted: vec![],
enacted: vec![b3b_hash],
retracted: vec![],
enacted: vec![b3a_hash],
retracted: vec![b3b_hash],
assert_eq!(bc.best_block_hash(), best_block_hash);
assert_eq!(bc.block_number(&genesis_hash).unwrap(), 0);
assert_eq!(bc.block_number(&b1_hash).unwrap(), 1);
assert_eq!(bc.block_number(&b2_hash).unwrap(), 2);
assert_eq!(bc.block_number(&b3a_hash).unwrap(), 3);
assert_eq!(bc.block_number(&b3b_hash).unwrap(), 3);
assert_eq!(bc.block_hash(0).unwrap(), genesis_hash);
assert_eq!(bc.block_hash(1).unwrap(), b1_hash);
assert_eq!(bc.block_hash(2).unwrap(), b2_hash);
assert_eq!(bc.block_hash(3).unwrap(), b3a_hash);
let r0_1 = bc.tree_route(genesis_hash, b1_hash).unwrap();
assert_eq!(r0_1.blocks, [b1_hash]);
let r0_2 = bc.tree_route(genesis_hash, b2_hash).unwrap();
assert_eq!(r0_2.blocks, [b1_hash, b2_hash]);
let r1_3a = bc.tree_route(b1_hash, b3a_hash).unwrap();
assert_eq!(r1_3a.blocks, [b2_hash, b3a_hash]);
let r1_3b = bc.tree_route(b1_hash, b3b_hash).unwrap();
assert_eq!(r1_3b.blocks, [b2_hash, b3b_hash]);
let r3a_3b = bc.tree_route(b3a_hash, b3b_hash).unwrap();
assert_eq!(r3a_3b.blocks, [b3a_hash, b3b_hash]);
let r1_0 = bc.tree_route(b1_hash, genesis_hash).unwrap();
assert_eq!(r1_0.blocks, [b1_hash]);
let r2_0 = bc.tree_route(b2_hash, genesis_hash).unwrap();
assert_eq!(r2_0.blocks, [b2_hash, b1_hash]);
let r3a_1 = bc.tree_route(b3a_hash, b1_hash).unwrap();
assert_eq!(r3a_1.blocks, [b3a_hash, b2_hash]);
let r3b_1 = bc.tree_route(b3b_hash, b1_hash).unwrap();
assert_eq!(r3b_1.blocks, [b3b_hash, b2_hash]);
let r3b_3a = bc.tree_route(b3b_hash, b3a_hash).unwrap();
assert_eq!(r3b_3a.blocks, [b3b_hash, b3a_hash]);
#[test]
fn test_reopen_blockchain_db() {
let genesis = BlockBuilder::genesis();
let first = genesis.add_block();
let genesis_hash = genesis.last().hash();
let first_hash = first.last().hash();
let db = new_db();
let bc = new_chain(&genesis.last().encoded(), db.clone());
assert_eq!(bc.best_block_hash(), genesis_hash);
let mut batch = db.transaction();
bc.insert_block(&mut batch, &first.last().encoded(), vec![]);
assert_eq!(bc.best_block_hash(), first_hash);
let bc = new_chain(&genesis.last().encoded(), db.clone());
assert_eq!(bc.best_block_hash(), first_hash);
fn can_contain_arbitrary_block_sequence() {
let bc = generate_dummy_blockchain(50);
assert_eq!(bc.best_block_number(), 49);
#[test]
fn can_collect_garbage() {
let bc = generate_dummy_blockchain(3000);
assert_eq!(bc.best_block_number(), 2999);
let best_hash = bc.best_block_hash();
let mut block_header = bc.block_header(&best_hash);
while !block_header.is_none() {
block_header = bc.block_header(block_header.unwrap().parent_hash());
}
assert!(bc.cache_size().blocks > 1024 * 1024);
assert!(bc.cache_size().blocks < 1024 * 1024);
}
#[test]
fn can_contain_arbitrary_block_sequence_with_extra() {
let bc = generate_dummy_blockchain_with_extra(25);
assert_eq!(bc.best_block_number(), 24);
}
#[test]
fn can_contain_only_genesis_block() {
let bc = generate_dummy_empty_blockchain();
#[test]
fn find_transaction_by_hash() {
let genesis = "f901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0af81e09f8c46ca322193edfda764fa7e88e81923f802f1d325ec0b0308ac2cd0a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830200008083023e38808454c98c8142a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421880102030405060708c0c0".from_hex().unwrap();
let b1 = "f904a8f901faa0ce1f26f798dd03c8782d63b3e42e79a64eaea5694ea686ac5d7ce3df5171d1aea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0a65c2364cd0f1542d761823dc0109c6b072f14c20459598c5455c274601438f4a070616ebd7ad2ed6fb7860cf7e9df00163842351c38a87cac2c1cb193895035a2a05c5b4fc43c2d45787f54e1ae7d27afdb4ad16dfc567c5692070d5c4556e0b1d7b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830200000183023ec683021536845685109780a029f07836e4e59229b3a065913afc27702642c683bba689910b2b2fd45db310d3888957e6d004a31802f902a7f85f800a8255f094aaaf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca0575da4e21b66fa764be5f74da9389e67693d066fb0d1312e19e17e501da00ecda06baf5a5327595f6619dfc2fcb3f2e6fb410b5810af3cb52d0e7508038e91a188f85f010a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba04fa966bf34b93abc1bcd665554b7f316b50f928477b50be0f3285ead29d18c5ba017bba0eeec1625ab433746955e125d46d80b7fdc97386c51266f842d8e02192ef85f020a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca004377418ae981cc32b1312b4a427a1d69a821b28db8584f5f2bd8c6d42458adaa053a1dba1af177fac92f3b6af0a9fa46a22adf56e686c93794b6a012bf254abf5f85f030a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca04fe13febd28a05f4fcb2f451d7ddc2dda56486d9f8c79a62b0ba4da775122615a0651b2382dd402df9ebc27f8cb4b2e0f3cea68dda2dca0ee9603608f0b6f51668f85f040a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba078e6a0ba086a08f8450e208a399bb2f2d2a0d984acd2517c7c7df66ccfab567da013254002cd45a97fac049ae00afbc43ed0d9961d0c56a3b2382c80ce41c198ddf85f050a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba0a7174d8f43ea71c8e3ca9477691add8d80ac8e0ed89d8d8b572041eef81f4a54a0534ea2e28ec4da3b5b944b18c51ec84a5cf35f5b3343c5fb86521fd2d388f506f85f060a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba034bd04065833536a10c77ee2a43a5371bc6d34837088b861dd9d4b7f44074b59a078807715786a13876d3455716a6b9cb2186b7a4887a5c31160fc877454958616c0".from_hex().unwrap();
let b1_hash: H256 = "f53f268d23a71e85c7d6d83a9504298712b84c1a2ba220441c86eeda0bf0b6e3".into();
let db = new_db();
let mut batch =db.transaction();
bc.insert_block(&mut batch, &b1, vec![]);
let transactions = bc.transactions(&b1_hash).unwrap();
assert_eq!(transactions.len(), 7);
for t in transactions {
assert_eq!(bc.transaction(&bc.transaction_address(&t.hash()).unwrap()).unwrap(), t);
fn insert_block(db: &Arc<KeyValueDB>, bc: &BlockChain, bytes: &[u8], receipts: Vec<Receipt>) -> ImportRoute {
let res = bc.insert_block(&mut batch, bytes, receipts);
#[test]
fn test_logs() {
let t1 = Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
action: Action::Create,
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
Tomasz Drwięga
committed
}.sign(&secret(), None);
let t2 = Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
action: Action::Create,
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
Tomasz Drwięga
committed
}.sign(&secret(), None);
let t3 = Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
action: Action::Create,
data: "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(),
Tomasz Drwięga
committed
}.sign(&secret(), None);
let tx_hash1 = t1.hash();
let tx_hash2 = t2.hash();
let tx_hash3 = t3.hash();
let genesis = BlockBuilder::genesis();
let b1 = genesis.add_block_with_transactions(vec![t1, t2]);
let b2 = b1.add_block_with_transactions(iter::once(t3));
let b1_hash = b1.last().hash();
let b1_number = b1.last().number();
let b2_hash = b2.last().hash();
let b2_number = b2.last().number();
let db = new_db();
let bc = new_chain(&genesis.last().encoded(), db.clone());
insert_block(&db, &bc, &b1.last().encoded(), vec![Receipt {
outcome: TransactionOutcome::StateRoot(H256::default()),
gas_used: 10_000.into(),
log_bloom: Default::default(),
logs: vec![
LogEntry { address: Default::default(), topics: vec![], data: vec![1], },
LogEntry { address: Default::default(), topics: vec![], data: vec![2], },
],
},
Receipt {
outcome: TransactionOutcome::StateRoot(H256::default()),
gas_used: 10_000.into(),
log_bloom: Default::default(),
logs: vec![
LogEntry { address: Default::default(), topics: vec![], data: vec![3], },
],
}]);
insert_block(&db, &bc, &b2.last().encoded(), vec![
outcome: TransactionOutcome::StateRoot(H256::default()),
gas_used: 10_000.into(),
log_bloom: Default::default(),
logs: vec![
LogEntry { address: Default::default(), topics: vec![], data: vec![4], },
],
}
]);
// when
let logs1 = bc.logs(vec![1, 2], |_| true, None);
let logs2 = bc.logs(vec![1, 2], |_| true, Some(1));
// then
assert_eq!(logs1, vec![
LocalizedLogEntry {
entry: LogEntry { address: Default::default(), topics: vec![], data: vec![1] },
block_hash: b1_hash,
block_number: b1_number,
transaction_hash: tx_hash1,
transaction_log_index: 0,