From 01cb9091252b592ca907c8c30e4145b9a03c4762 Mon Sep 17 00:00:00 2001 From: NikVolf Date: Mon, 7 Nov 2016 21:01:25 +0300 Subject: [PATCH] double reorganize and transaction spend tests --- db/src/storage.rs | 135 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 131 insertions(+), 4 deletions(-) diff --git a/db/src/storage.rs b/db/src/storage.rs index e8cdf4a0..9aea281f 100644 --- a/db/src/storage.rs +++ b/db/src/storage.rs @@ -93,6 +93,8 @@ pub enum Error { Meta(MetaError), /// Unknown hash Unknown(H256), + /// Unknown number + UnknownNumber(u32), /// Not the block from the main chain NotMain(H256), /// Fork too long @@ -318,6 +320,9 @@ impl Storage { // ensure that block is of the main chain try!(self.block_number(hash).ok_or(Error::NotMain(hash.clone()))); + // only canonical blocks have numbers, so remove this number entry for the hash + context.db_transaction.delete(Some(COL_BLOCK_NUMBERS), &**hash); + // transaction de-provisioning let tx_hashes = self.block_transaction_hashes_by_hash(hash); for (tx_hash_num, tx_hash) in tx_hashes.iter().enumerate() { @@ -419,8 +424,8 @@ impl Storage { // decanonizing main chain to the split point loop { - let next_to_decanonize = try!(self.best_hash().ok_or(Error::NoBestBlock)); - try!(self.decanonize_block(context, &next_to_decanonize)); + let next_decanonize = try!(self.block_hash(now_best).ok_or(Error::UnknownNumber(now_best))); + try!(self.decanonize_block(context, &next_decanonize)); now_best -= 1; @@ -901,6 +906,9 @@ mod tests { last_side_block_hash = new_side_hash; } + let (height, route) = store.fork_route(128, &last_side_block_hash).unwrap(); + assert_eq!(height, 0); + assert_eq!(route.len(), 31); let (reorg_side_hash, reorg_side_block) = test_data::block_hash_builder() .block() @@ -915,11 +923,131 @@ mod tests { assert_eq!(store.best_block().unwrap().hash, reorg_side_hash); } + #[test] + fn fork_transactions() { + let path = RandomTempPath::create_dir(); + let store = Storage::new(path.as_path()).unwrap(); + + let genesis = test_data::genesis(); + store.insert_block(&genesis).unwrap(); + let genesis_coinbase = genesis.transactions()[0].hash(); + + // Having 2 blocks initially in the main chain + + let block1 = test_data::block_builder() + .header() + .nonce(10) + .parent(genesis.hash()) + .build() + .transaction().coinbase().build() + .transaction() + .input().hash(genesis_coinbase).build() + .output().value(1).build() + .output().value(3).build() + .output().value(5).build() + .output().value(7).build() + .output().value(9).build() + .build() + .build(); + store.insert_block(&block1).expect("Block #2 should get inserted with no error"); + let parent_tx = block1.transactions()[1].hash(); + + let block2 = test_data::block_builder() + .header() + .nonce(20) + .parent(block1.hash()) + .build() + .transaction().coinbase().build() + .transaction() + .input().hash(parent_tx.clone()).index(1).build() + .input().hash(parent_tx.clone()).index(3).build() + .output().value(10).build() + .build() + .build(); + store.insert_block(&block2).expect("Block #2 should get inserted with no error"); + + // Reorganizing to side chain adding two blocks to it + + let side_block2 = test_data::block_builder() + .header().parent(block1.hash()) + .nonce(30) + .build() + .transaction().coinbase().build() + .transaction() + .input().hash(parent_tx.clone()).index(0).build() + .input().hash(parent_tx.clone()).index(2).build() + .output().value(6).build() + .build() + .build(); + store.insert_block(&side_block2).expect("Side block #2 should get inserted with no error"); + + let side_block3 = test_data::block_builder() + .header().parent(side_block2.hash()) + .nonce(40) + .build() + .transaction().coinbase().build() + .transaction() + .input().hash(parent_tx.clone()).index(1).build() + .output().value(3).build() + .build() + .build(); + store.insert_block(&side_block3).expect("Side block #3 should get inserted with no error"); + + let meta = store.transaction_meta(&parent_tx).expect("Transaction meta from block # 1 should exist"); + // outputs 0, 1, 2 should be spent in the side chain branch + // we reorganized to the side chain branch + // so, outputs 0, 1, 2 should be spent + assert!(meta.is_spent(0)); + assert!(meta.is_spent(1)); + assert!(meta.is_spent(2)); + + // outputs 3, 4 should not be spent in the side chain branch + // we reorganized to the side chain branch + // so, outputs 3, 4 should not be spent + assert!(!meta.is_spent(3)); + assert!(!meta.is_spent(4)); + + // Reorganizing back to main chain with 2 blocks in a row + + let block3 = test_data::block_builder() + .header().parent(block2.hash()) + .nonce(50) + .build() + .transaction().coinbase().build() + .build(); + store.insert_block(&block3).expect("Block #3 should get inserted with no error"); + + let block4 = test_data::block_builder() + .header().parent(block3.hash()) + .nonce(60) + .build() + .transaction().coinbase().build() + .transaction() + .input().hash(parent_tx.clone()).index(4).build() + .output().value(9).build() + .build() + .build(); + store.insert_block(&block4).expect("Block #4 should get inserted with no error"); + + let meta = store.transaction_meta(&parent_tx).expect("Transaction meta from block # 1 should exist"); + // outputs 1, 3, 4 should be spent in the main branch + // we reorganized to the main branch again after reorganized to side branch + // so, outputs 1, 3, 4 should be spent + assert!(meta.is_spent(1)); + assert!(meta.is_spent(3)); + assert!(meta.is_spent(4)); + + // outputs 0, 2 should not be spent in the main branch + // we reorganized to the main branch again after reorganized to side branch + // so, outputs 0, 2 should not be spent + assert!(!meta.is_spent(0)); + assert!(!meta.is_spent(2)); + } + // test simulates when main chain and side chain are competing all along, each adding // block one by one #[test] fn fork_competing() { - let path = RandomTempPath::create_dir(); let store = Storage::new(path.as_path()).unwrap(); @@ -1074,7 +1202,6 @@ mod tests { .build(); store.insert_block(&side_block3).expect("side block 3 should insert with no problems"); - let (h, route) = store.fork_route(16, &side_hash3).expect("Fork route should have been built"); assert_eq!(h, 0);