From 996b4b9dc0fca1af6ce576f0bd1957ac29244f65 Mon Sep 17 00:00:00 2001 From: debris Date: Wed, 31 Aug 2016 16:55:43 +0200 Subject: [PATCH 1/2] fixed transaction addresses mapping, fixes #1971 --- ethcore/src/blockchain/block_info.rs | 4 +- ethcore/src/blockchain/blockchain.rs | 163 ++++++++++++++++-- ethcore/src/blockchain/extras.rs | 2 +- ethcore/src/blockchain/generator/block.rs | 11 +- ethcore/src/blockchain/generator/generator.rs | 13 +- ethcore/src/blockchain/generator/mod.rs | 1 + .../src/blockchain/generator/transaction.rs | 35 ++++ 7 files changed, 207 insertions(+), 22 deletions(-) create mode 100644 ethcore/src/blockchain/generator/transaction.rs diff --git a/ethcore/src/blockchain/block_info.rs b/ethcore/src/blockchain/block_info.rs index 31f93232b..fee0e825b 100644 --- a/ethcore/src/blockchain/block_info.rs +++ b/ethcore/src/blockchain/block_info.rs @@ -31,7 +31,7 @@ pub struct BlockInfo { } /// Describes location of newly inserted block. -#[derive(Clone)] +#[derive(Debug, Clone)] pub enum BlockLocation { /// It's part of the canon chain. CanonChain, @@ -43,7 +43,7 @@ pub enum BlockLocation { BranchBecomingCanonChain(BranchBecomingCanonChainData), } -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct BranchBecomingCanonChainData { /// Hash of the newest common ancestor with old canon chain. pub ancestor: H256, diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index a581e59e9..1ca441e69 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -830,7 +830,7 @@ impl BlockChain { } } - /// Applt pending insertion updates + /// 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(); @@ -961,15 +961,45 @@ impl BlockChain { let block = BlockView::new(block_bytes); let transaction_hashes = block.transaction_hashes(); - transaction_hashes.into_iter() - .enumerate() - .fold(HashMap::new(), |mut acc, (i ,tx_hash)| { - acc.insert(tx_hash, TransactionAddress { - block_hash: info.hash.clone(), - index: i - }); - acc - }) + match info.location { + BlockLocation::CanonChain => { + transaction_hashes.into_iter() + .enumerate() + .map(|(i ,tx_hash)| { + (tx_hash, TransactionAddress { + block_hash: info.hash.clone(), + index: i + }) + }) + .collect() + }, + BlockLocation::BranchBecomingCanonChain(ref data) => { + let addresses = data.enacted.iter() + .map(|hash| (hash, self.block_body(hash).unwrap())) + .flat_map(|(hash, bytes)| { + let hashes = BodyView::new(&bytes).transaction_hashes(); + hashes.into_iter() + .enumerate() + .map(|(i, tx_hash)| (tx_hash, TransactionAddress { + block_hash: hash.clone(), + index: i, + })) + .collect::>() + }); + + let current_addresses = transaction_hashes.into_iter() + .enumerate() + .map(|(i ,tx_hash)| { + (tx_hash, TransactionAddress { + block_hash: info.hash.clone(), + index: i + }) + }); + + addresses.chain(current_addresses).collect() + }, + BlockLocation::Branch => HashMap::new(), + } } /// This functions returns modified blocks blooms. @@ -1116,7 +1146,6 @@ impl BlockChain { #[cfg(test)] mod tests { #![cfg_attr(feature="dev", allow(similar_names))] - use std::str::FromStr; use std::sync::Arc; use rustc_serialize::hex::FromHex; use util::{Database, DatabaseConfig}; @@ -1127,7 +1156,9 @@ mod tests { use tests::helpers::*; use devtools::*; use blockchain::generator::{ChainGenerator, ChainIterator, BlockFinalizer}; + use blockchain::extras::TransactionAddress; use views::BlockView; + use transaction::{Transaction, Action}; fn new_db(path: &str) -> Arc { Arc::new(Database::open(&DatabaseConfig::with_columns(::db::NUM_COLUMNS), path).unwrap()) @@ -1262,6 +1293,106 @@ mod tests { // TODO: insert block that already includes one of them as an uncle to check it's not allowed. } + #[test] + fn test_overwriting_transaction_addresses() { + let mut canon_chain = ChainGenerator::default(); + let mut finalizer = BlockFinalizer::default(); + let genesis = canon_chain.generate(&mut finalizer).unwrap(); + let mut fork_chain = canon_chain.fork(1); + let mut fork_finalizer = finalizer.fork(); + + 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(), + }.sign(&"".sha3()); + + 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(), + }.sign(&"".sha3()); + + 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(), + }.sign(&"".sha3()); + + let b1a = canon_chain + .with_transaction(t1.clone()) + .with_transaction(t2.clone()) + .generate(&mut finalizer).unwrap(); + + // insert transactions in different order + let b1b = fork_chain + .with_transaction(t2.clone()) + .with_transaction(t1.clone()) + .generate(&mut fork_finalizer).unwrap(); + + let b2 = fork_chain + .with_transaction(t3.clone()) + .generate(&mut fork_finalizer).unwrap(); + + let b1a_hash = BlockView::new(&b1a).header_view().sha3(); + let b1b_hash = BlockView::new(&b1b).header_view().sha3(); + let b2_hash = BlockView::new(&b2).header_view().sha3(); + + let t1_hash = t1.hash(); + let t2_hash = t2.hash(); + let t3_hash = t3.hash(); + + let temp = RandomTempPath::new(); + let db = new_db(temp.as_str()); + let bc = BlockChain::new(Config::default(), &genesis, db.clone()); + + let mut batch = db.transaction(); + let _ = bc.insert_block(&mut batch, &b1a, vec![]); + bc.commit(); + let _ = bc.insert_block(&mut batch, &b1b, vec![]); + bc.commit(); + db.write(batch).unwrap(); + + assert_eq!(bc.best_block_hash(), b1a_hash); + assert_eq!(bc.transaction_address(&t1_hash).unwrap(), TransactionAddress { + block_hash: b1a_hash.clone(), + index: 0, + }); + assert_eq!(bc.transaction_address(&t2_hash).unwrap(), TransactionAddress { + block_hash: b1a_hash.clone(), + index: 1, + }); + + // now let's make forked chain the canon chain + let mut batch = db.transaction(); + let _ = bc.insert_block(&mut batch, &b2, vec![]); + bc.commit(); + db.write(batch).unwrap(); + + assert_eq!(bc.best_block_hash(), b2_hash); + assert_eq!(bc.transaction_address(&t1_hash).unwrap(), TransactionAddress { + block_hash: b1b_hash.clone(), + index: 1, + }); + assert_eq!(bc.transaction_address(&t2_hash).unwrap(), TransactionAddress { + block_hash: b1b_hash.clone(), + index: 0, + }); + assert_eq!(bc.transaction_address(&t3_hash).unwrap(), TransactionAddress { + block_hash: b2_hash.clone(), + index: 0, + }); + } + #[test] #[cfg_attr(feature="dev", allow(cyclomatic_complexity))] fn test_small_fork() { @@ -1286,7 +1417,7 @@ mod tests { let db = new_db(temp.as_str()); let bc = BlockChain::new(Config::default(), &genesis, db.clone()); - let mut batch =db.transaction(); + let mut batch = db.transaction(); let ir1 = bc.insert_block(&mut batch, &b1, vec![]); bc.commit(); let ir2 = bc.insert_block(&mut batch, &b2, vec![]); @@ -1462,7 +1593,7 @@ mod tests { fn find_transaction_by_hash() { let genesis = "f901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0af81e09f8c46ca322193edfda764fa7e88e81923f802f1d325ec0b0308ac2cd0a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830200008083023e38808454c98c8142a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421880102030405060708c0c0".from_hex().unwrap(); let b1 = "f904a8f901faa0ce1f26f798dd03c8782d63b3e42e79a64eaea5694ea686ac5d7ce3df5171d1aea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0a65c2364cd0f1542d761823dc0109c6b072f14c20459598c5455c274601438f4a070616ebd7ad2ed6fb7860cf7e9df00163842351c38a87cac2c1cb193895035a2a05c5b4fc43c2d45787f54e1ae7d27afdb4ad16dfc567c5692070d5c4556e0b1d7b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830200000183023ec683021536845685109780a029f07836e4e59229b3a065913afc27702642c683bba689910b2b2fd45db310d3888957e6d004a31802f902a7f85f800a8255f094aaaf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca0575da4e21b66fa764be5f74da9389e67693d066fb0d1312e19e17e501da00ecda06baf5a5327595f6619dfc2fcb3f2e6fb410b5810af3cb52d0e7508038e91a188f85f010a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba04fa966bf34b93abc1bcd665554b7f316b50f928477b50be0f3285ead29d18c5ba017bba0eeec1625ab433746955e125d46d80b7fdc97386c51266f842d8e02192ef85f020a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca004377418ae981cc32b1312b4a427a1d69a821b28db8584f5f2bd8c6d42458adaa053a1dba1af177fac92f3b6af0a9fa46a22adf56e686c93794b6a012bf254abf5f85f030a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ca04fe13febd28a05f4fcb2f451d7ddc2dda56486d9f8c79a62b0ba4da775122615a0651b2382dd402df9ebc27f8cb4b2e0f3cea68dda2dca0ee9603608f0b6f51668f85f040a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba078e6a0ba086a08f8450e208a399bb2f2d2a0d984acd2517c7c7df66ccfab567da013254002cd45a97fac049ae00afbc43ed0d9961d0c56a3b2382c80ce41c198ddf85f050a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba0a7174d8f43ea71c8e3ca9477691add8d80ac8e0ed89d8d8b572041eef81f4a54a0534ea2e28ec4da3b5b944b18c51ec84a5cf35f5b3343c5fb86521fd2d388f506f85f060a82520894bbbf5374fce5edbc8e2a8697c15331677e6ebf0b0a801ba034bd04065833536a10c77ee2a43a5371bc6d34837088b861dd9d4b7f44074b59a078807715786a13876d3455716a6b9cb2186b7a4887a5c31160fc877454958616c0".from_hex().unwrap(); - let b1_hash = H256::from_str("f53f268d23a71e85c7d6d83a9504298712b84c1a2ba220441c86eeda0bf0b6e3").unwrap(); + let b1_hash: H256 = "f53f268d23a71e85c7d6d83a9504298712b84c1a2ba220441c86eeda0bf0b6e3".into(); let temp = RandomTempPath::new(); let db = new_db(temp.as_str()); @@ -1490,11 +1621,11 @@ mod tests { #[test] fn test_bloom_filter_simple() { // TODO: From here - let bloom_b1 = H2048::from_str("00000020000000000000000000000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000400000000000000000000002000").unwrap(); + let bloom_b1: H2048 = "00000020000000000000000000000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000400000000000000000000002000".into(); - let bloom_b2 = H2048::from_str("00000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(); + let bloom_b2: H2048 = "00000000000000000000000000000000000000000000020000001000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into(); - let bloom_ba = H2048::from_str("00000000000000000000000000000000000000000000020000000800000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(); + let bloom_ba: H2048 = "00000000000000000000000000000000000000000000020000000800000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000008000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".into(); let mut canon_chain = ChainGenerator::default(); let mut finalizer = BlockFinalizer::default(); diff --git a/ethcore/src/blockchain/extras.rs b/ethcore/src/blockchain/extras.rs index 6bb10276c..e2b4e89b3 100644 --- a/ethcore/src/blockchain/extras.rs +++ b/ethcore/src/blockchain/extras.rs @@ -176,7 +176,7 @@ impl Encodable for BlockDetails { } /// Represents address of certain transaction within block -#[derive(Clone)] +#[derive(Debug, PartialEq, Clone)] pub struct TransactionAddress { /// Block hash pub block_hash: H256, diff --git a/ethcore/src/blockchain/generator/block.rs b/ethcore/src/blockchain/generator/block.rs index 238051d2a..1fb9c13a1 100644 --- a/ethcore/src/blockchain/generator/block.rs +++ b/ethcore/src/blockchain/generator/block.rs @@ -16,7 +16,6 @@ use util::rlp::*; use util::{H256, H2048}; -use util::U256; use util::bytes::Bytes; use header::Header; use transaction::SignedTransaction; @@ -24,6 +23,7 @@ use transaction::SignedTransaction; use super::fork::Forkable; use super::bloom::WithBloom; use super::complete::CompleteBlock; +use super::transaction::WithTransaction; /// Helper structure, used for encoding blocks. #[derive(Default)] @@ -44,7 +44,7 @@ impl Encodable for Block { impl Forkable for Block { fn fork(mut self, fork_number: usize) -> Self where Self: Sized { - let difficulty = self.header.difficulty().clone() - U256::from(fork_number); + let difficulty = self.header.difficulty().clone() - fork_number.into(); self.header.set_difficulty(difficulty); self } @@ -57,6 +57,13 @@ impl WithBloom for Block { } } +impl WithTransaction for Block { + fn with_transaction(mut self, transaction: SignedTransaction) -> Self where Self: Sized { + self.transactions.push(transaction); + self + } +} + impl CompleteBlock for Block { fn complete(mut self, parent_hash: H256) -> Bytes { self.header.set_parent_hash(parent_hash); diff --git a/ethcore/src/blockchain/generator/generator.rs b/ethcore/src/blockchain/generator/generator.rs index 179839b5a..ebe9e22c0 100644 --- a/ethcore/src/blockchain/generator/generator.rs +++ b/ethcore/src/blockchain/generator/generator.rs @@ -16,10 +16,12 @@ use util::{U256, H2048, Bytes}; use header::BlockNumber; +use transaction::SignedTransaction; use super::fork::Fork; use super::bloom::Bloom; use super::complete::{BlockFinalizer, CompleteBlock, Complete}; use super::block::Block; +use super::transaction::Transaction; /// Chain iterator interface. pub trait ChainIterator: Iterator + Sized { @@ -28,6 +30,8 @@ pub trait ChainIterator: Iterator + Sized { fn fork(&self, fork_number: usize) -> Fork where Self: Clone; /// Should be called to make every consecutive block have given bloom. fn with_bloom(&mut self, bloom: H2048) -> Bloom; + /// Should be called to make every consecutive block have given transaction. + fn with_transaction(&mut self, transaction: SignedTransaction) -> Transaction; /// Should be called to complete block. Without complete, block may have incorrect hash. fn complete<'a>(&'a mut self, finalizer: &'a mut BlockFinalizer) -> Complete<'a, Self>; /// Completes and generates block. @@ -49,6 +53,13 @@ impl ChainIterator for I where I: Iterator + Sized { } } + fn with_transaction(&mut self, transaction: SignedTransaction) -> Transaction { + Transaction { + iter: self, + transaction: transaction, + } + } + fn complete<'a>(&'a mut self, finalizer: &'a mut BlockFinalizer) -> Complete<'a, Self> { Complete { iter: self, @@ -83,7 +94,7 @@ impl Default for ChainGenerator { fn default() -> Self { ChainGenerator { number: 0, - difficulty: U256::from(1000), + difficulty: 1000.into(), } } } diff --git a/ethcore/src/blockchain/generator/mod.rs b/ethcore/src/blockchain/generator/mod.rs index b02030d4e..683ab7dfb 100644 --- a/ethcore/src/blockchain/generator/mod.rs +++ b/ethcore/src/blockchain/generator/mod.rs @@ -21,6 +21,7 @@ mod block; mod complete; mod fork; pub mod generator; +mod transaction; pub use self::complete::BlockFinalizer; pub use self::generator::{ChainIterator, ChainGenerator}; diff --git a/ethcore/src/blockchain/generator/transaction.rs b/ethcore/src/blockchain/generator/transaction.rs new file mode 100644 index 000000000..aa1d3bdd3 --- /dev/null +++ b/ethcore/src/blockchain/generator/transaction.rs @@ -0,0 +1,35 @@ +// Copyright 2015, 2016 Ethcore (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use transaction::SignedTransaction; + +pub trait WithTransaction { + fn with_transaction(self, transaction: SignedTransaction) -> Self where Self: Sized; +} + +pub struct Transaction<'a, I> where I: 'a { + pub iter: &'a mut I, + pub transaction: SignedTransaction, +} + +impl <'a, I> Iterator for Transaction<'a, I> where I: Iterator, ::Item: WithTransaction { + type Item = ::Item; + + #[inline] + fn next(&mut self) -> Option { + self.iter.next().map(|item| item.with_transaction(self.transaction.clone())) + } +} From f5f4736e7c44aa661601119a6481002f389fa5e9 Mon Sep 17 00:00:00 2001 From: debris Date: Wed, 31 Aug 2016 17:36:49 +0200 Subject: [PATCH 2/2] simplified iterator --- ethcore/src/blockchain/blockchain.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 1ca441e69..943dc04c5 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -975,8 +975,8 @@ impl BlockChain { }, BlockLocation::BranchBecomingCanonChain(ref data) => { let addresses = data.enacted.iter() - .map(|hash| (hash, self.block_body(hash).unwrap())) - .flat_map(|(hash, bytes)| { + .flat_map(|hash| { + let bytes = self.block_body(hash).expect("Enacted block must be in database."); let hashes = BodyView::new(&bytes).transaction_hashes(); hashes.into_iter() .enumerate()