TypedTransaction (EIP-2718) and Optional access list (EIP-2930) (#135)

This commit is contained in:
rakita 2020-12-10 16:42:05 +01:00 committed by GitHub
parent 3f01b69084
commit ea3efd926e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
83 changed files with 1858 additions and 805 deletions

14
Cargo.lock generated
View File

@ -447,6 +447,9 @@ dependencies = [
"rlp 0.3.0", "rlp 0.3.0",
"rlp_derive", "rlp_derive",
"rustc-hex 1.0.0", "rustc-hex 1.0.0",
"serde",
"serde_json",
"serde_repr",
"unexpected", "unexpected",
] ]
@ -4274,6 +4277,17 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "serde_repr"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dc6b7951b17b051f3210b063f12cc17320e2fe30ae05b0fe2a3abb068551c76"
dependencies = [
"proc-macro2 1.0.20",
"quote 1.0.7",
"syn 1.0.40",
]
[[package]] [[package]]
name = "sha-1" name = "sha-1"
version = "0.8.1" version = "0.8.1"

View File

@ -34,7 +34,7 @@ use common_types::{
}, },
header::{ExtendedHeader, Header}, header::{ExtendedHeader, Header},
log_entry::{LocalizedLogEntry, LogEntry}, log_entry::{LocalizedLogEntry, LogEntry},
receipt::Receipt, receipt::TypedReceipt,
transaction::LocalizedTransaction, transaction::LocalizedTransaction,
tree_route::TreeRoute, tree_route::TreeRoute,
view, view,
@ -460,13 +460,13 @@ impl BlockProvider for BlockChain {
warn!("Block {} ({}) has different number of receipts ({}) to transactions ({}). Database corrupt?", number, hash, receipts.len(), hashes.len()); warn!("Block {} ({}) has different number of receipts ({}) to transactions ({}). Database corrupt?", number, hash, receipts.len(), hashes.len());
assert!(false); assert!(false);
} }
let mut log_index = receipts.iter().fold(0, |sum, receipt| sum + receipt.logs.len()); let mut log_index = receipts.iter().fold(0, |sum, receipt| sum + receipt.receipt().logs.len());
let receipts_len = receipts.len(); let receipts_len = receipts.len();
hashes.reverse(); hashes.reverse();
receipts.reverse(); receipts.reverse();
receipts.into_iter() receipts.into_iter()
.map(|receipt| receipt.logs) .map(|receipt| receipt.receipt().logs.clone())
.zip(hashes) .zip(hashes)
.enumerate() .enumerate()
.flat_map(move |(index, (mut logs, tx_hash))| { .flat_map(move |(index, (mut logs, tx_hash))| {
@ -895,7 +895,7 @@ impl BlockChain {
&self, &self,
batch: &mut DBTransaction, batch: &mut DBTransaction,
block: encoded::Block, block: encoded::Block,
receipts: Vec<Receipt>, receipts: Vec<TypedReceipt>,
parent_td: Option<U256>, parent_td: Option<U256>,
is_best: bool, is_best: bool,
is_ancient: bool, is_ancient: bool,
@ -1265,7 +1265,7 @@ impl BlockChain {
&self, &self,
batch: &mut DBTransaction, batch: &mut DBTransaction,
block: encoded::Block, block: encoded::Block,
receipts: Vec<Receipt>, receipts: Vec<TypedReceipt>,
extras: ExtrasInsert, extras: ExtrasInsert,
) -> ImportRoute { ) -> ImportRoute {
let parent_hash = block.header_view().parent_hash(); let parent_hash = block.header_view().parent_hash();
@ -1283,7 +1283,7 @@ impl BlockChain {
&self, &self,
batch: &mut DBTransaction, batch: &mut DBTransaction,
block: encoded::Block, block: encoded::Block,
receipts: Vec<Receipt>, receipts: Vec<TypedReceipt>,
route: TreeRoute, route: TreeRoute,
extras: ExtrasInsert, extras: ExtrasInsert,
) -> ImportRoute { ) -> ImportRoute {
@ -1659,7 +1659,7 @@ impl BlockChain {
/// This function returns modified block receipts. /// This function returns modified block receipts.
fn prepare_block_receipts_update( fn prepare_block_receipts_update(
&self, &self,
receipts: Vec<Receipt>, receipts: Vec<TypedReceipt>,
info: &BlockInfo, info: &BlockInfo,
) -> HashMap<H256, BlockReceipts> { ) -> HashMap<H256, BlockReceipts> {
let mut block_receipts = HashMap::new(); let mut block_receipts = HashMap::new();
@ -1921,8 +1921,8 @@ mod tests {
use crate::generator::{BlockBuilder, BlockGenerator, BlockOptions}; use crate::generator::{BlockBuilder, BlockGenerator, BlockOptions};
use common_types::{ use common_types::{
receipt::{Receipt, TransactionOutcome}, receipt::{LegacyReceipt, TransactionOutcome, TypedReceipt},
transaction::{Action, Transaction}, transaction::{Action, Transaction, TypedTransaction},
}; };
use ethkey::Secret; use ethkey::Secret;
use keccak_hash::keccak; use keccak_hash::keccak;
@ -1975,7 +1975,7 @@ mod tests {
db: &Arc<dyn BlockChainDB>, db: &Arc<dyn BlockChainDB>,
bc: &BlockChain, bc: &BlockChain,
block: encoded::Block, block: encoded::Block,
receipts: Vec<Receipt>, receipts: Vec<TypedReceipt>,
) -> ImportRoute { ) -> ImportRoute {
insert_block_commit(db, bc, block, receipts, true) insert_block_commit(db, bc, block, receipts, true)
} }
@ -1984,7 +1984,7 @@ mod tests {
db: &Arc<dyn BlockChainDB>, db: &Arc<dyn BlockChainDB>,
bc: &BlockChain, bc: &BlockChain,
block: encoded::Block, block: encoded::Block,
receipts: Vec<Receipt>, receipts: Vec<TypedReceipt>,
commit: bool, commit: bool,
) -> ImportRoute { ) -> ImportRoute {
let mut batch = db.key_value().transaction(); let mut batch = db.key_value().transaction();
@ -2000,7 +2000,7 @@ mod tests {
batch: &mut DBTransaction, batch: &mut DBTransaction,
bc: &BlockChain, bc: &BlockChain,
block: encoded::Block, block: encoded::Block,
receipts: Vec<Receipt>, receipts: Vec<TypedReceipt>,
) -> ImportRoute { ) -> ImportRoute {
let fork_choice = { let fork_choice = {
let header = block.header_view(); let header = block.header_view();
@ -2157,7 +2157,7 @@ mod tests {
#[test] #[test]
fn test_fork_transaction_addresses() { fn test_fork_transaction_addresses() {
let t1 = Transaction { let t1 = TypedTransaction::Legacy(Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 100_000.into(), gas: 100_000.into(),
@ -2166,7 +2166,7 @@ mod tests {
data: "601080600c6000396000f3006000355415600957005b60203560003555" data: "601080600c6000396000f3006000355415600957005b60203560003555"
.from_hex() .from_hex()
.unwrap(), .unwrap(),
} })
.sign(&secret(), None); .sign(&secret(), None);
let t1_hash = t1.hash(); let t1_hash = t1.hash();
@ -2211,7 +2211,7 @@ mod tests {
#[test] #[test]
fn test_overwriting_transaction_addresses() { fn test_overwriting_transaction_addresses() {
let t1 = Transaction { let t1 = TypedTransaction::Legacy(Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 100_000.into(), gas: 100_000.into(),
@ -2220,10 +2220,10 @@ mod tests {
data: "601080600c6000396000f3006000355415600957005b60203560003555" data: "601080600c6000396000f3006000355415600957005b60203560003555"
.from_hex() .from_hex()
.unwrap(), .unwrap(),
} })
.sign(&secret(), None); .sign(&secret(), None);
let t2 = Transaction { let t2 = TypedTransaction::Legacy(Transaction {
nonce: 1.into(), nonce: 1.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 100_000.into(), gas: 100_000.into(),
@ -2232,10 +2232,10 @@ mod tests {
data: "601080600c6000396000f3006000355415600957005b60203560003555" data: "601080600c6000396000f3006000355415600957005b60203560003555"
.from_hex() .from_hex()
.unwrap(), .unwrap(),
} })
.sign(&secret(), None); .sign(&secret(), None);
let t3 = Transaction { let t3 = TypedTransaction::Legacy(Transaction {
nonce: 2.into(), nonce: 2.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 100_000.into(), gas: 100_000.into(),
@ -2244,7 +2244,7 @@ mod tests {
data: "601080600c6000396000f3006000355415600957005b60203560003555" data: "601080600c6000396000f3006000355415600957005b60203560003555"
.from_hex() .from_hex()
.unwrap(), .unwrap(),
} })
.sign(&secret(), None); .sign(&secret(), None);
let genesis = BlockBuilder::genesis(); let genesis = BlockBuilder::genesis();
@ -2509,7 +2509,7 @@ mod tests {
#[test] #[test]
fn test_logs() { fn test_logs() {
let t1 = Transaction { let t1 = TypedTransaction::Legacy(Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 100_000.into(), gas: 100_000.into(),
@ -2518,9 +2518,9 @@ mod tests {
data: "601080600c6000396000f3006000355415600957005b60203560003555" data: "601080600c6000396000f3006000355415600957005b60203560003555"
.from_hex() .from_hex()
.unwrap(), .unwrap(),
} })
.sign(&secret(), None); .sign(&secret(), None);
let t2 = Transaction { let t2 = TypedTransaction::Legacy(Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 100_000.into(), gas: 100_000.into(),
@ -2529,9 +2529,9 @@ mod tests {
data: "601080600c6000396000f3006000355415600957005b60203560003555" data: "601080600c6000396000f3006000355415600957005b60203560003555"
.from_hex() .from_hex()
.unwrap(), .unwrap(),
} })
.sign(&secret(), None); .sign(&secret(), None);
let t3 = Transaction { let t3 = TypedTransaction::Legacy(Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 100_000.into(), gas: 100_000.into(),
@ -2540,9 +2540,9 @@ mod tests {
data: "601080600c6000396000f3006000355415600957005b60203560003555" data: "601080600c6000396000f3006000355415600957005b60203560003555"
.from_hex() .from_hex()
.unwrap(), .unwrap(),
} })
.sign(&secret(), None); .sign(&secret(), None);
let t4 = Transaction { let t4 = TypedTransaction::Legacy(Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 100_000.into(), gas: 100_000.into(),
@ -2551,7 +2551,7 @@ mod tests {
data: "601080600c6000396000f3006000355415600957005b60203560003555" data: "601080600c6000396000f3006000355415600957005b60203560003555"
.from_hex() .from_hex()
.unwrap(), .unwrap(),
} })
.sign(&secret(), None); .sign(&secret(), None);
let tx_hash1 = t1.hash(); let tx_hash1 = t1.hash();
let tx_hash2 = t2.hash(); let tx_hash2 = t2.hash();
@ -2580,7 +2580,7 @@ mod tests {
&bc, &bc,
b1.last().encoded(), b1.last().encoded(),
vec![ vec![
Receipt { TypedReceipt::Legacy(LegacyReceipt {
outcome: TransactionOutcome::StateRoot(H256::default()), outcome: TransactionOutcome::StateRoot(H256::default()),
gas_used: 10_000.into(), gas_used: 10_000.into(),
log_bloom: Default::default(), log_bloom: Default::default(),
@ -2596,8 +2596,8 @@ mod tests {
data: vec![2], data: vec![2],
}, },
], ],
}, }),
Receipt { TypedReceipt::Legacy(LegacyReceipt {
outcome: TransactionOutcome::StateRoot(H256::default()), outcome: TransactionOutcome::StateRoot(H256::default()),
gas_used: 10_000.into(), gas_used: 10_000.into(),
log_bloom: Default::default(), log_bloom: Default::default(),
@ -2606,14 +2606,14 @@ mod tests {
topics: vec![], topics: vec![],
data: vec![3], data: vec![3],
}], }],
}, }),
], ],
); );
insert_block( insert_block(
&db, &db,
&bc, &bc,
b2.last().encoded(), b2.last().encoded(),
vec![Receipt { vec![TypedReceipt::Legacy(LegacyReceipt {
outcome: TransactionOutcome::StateRoot(H256::default()), outcome: TransactionOutcome::StateRoot(H256::default()),
gas_used: 10_000.into(), gas_used: 10_000.into(),
log_bloom: Default::default(), log_bloom: Default::default(),
@ -2622,13 +2622,13 @@ mod tests {
topics: vec![], topics: vec![],
data: vec![4], data: vec![4],
}], }],
}], })],
); );
insert_block( insert_block(
&db, &db,
&bc, &bc,
b3.last().encoded(), b3.last().encoded(),
vec![Receipt { vec![TypedReceipt::Legacy(LegacyReceipt {
outcome: TransactionOutcome::StateRoot(H256::default()), outcome: TransactionOutcome::StateRoot(H256::default()),
gas_used: 10_000.into(), gas_used: 10_000.into(),
log_bloom: Default::default(), log_bloom: Default::default(),
@ -2637,7 +2637,7 @@ mod tests {
topics: vec![], topics: vec![],
data: vec![5], data: vec![5],
}], }],
}], })],
); );
// when // when

View File

@ -22,17 +22,16 @@ use std::collections::VecDeque;
use common_types::{ use common_types::{
encoded, encoded,
header::Header, header::Header,
transaction::{Action, SignedTransaction, Transaction}, transaction::{Action, SignedTransaction, Transaction, TypedTransaction},
view, view,
views::BlockView, views::BlockView,
}; };
use keccak_hash::keccak; use keccak_hash::keccak;
use rlp::encode; use rlp::{encode, RlpStream};
use rlp_derive::RlpEncodable;
use triehash_ethereum::ordered_trie_root; use triehash_ethereum::ordered_trie_root;
/// Helper structure, used for encoding blocks. /// Helper structure, used for encoding blocks.
#[derive(Default, Clone, RlpEncodable)] #[derive(Default, Clone)]
pub struct Block { pub struct Block {
/// Block header /// Block header
pub header: Header, pub header: Header,
@ -42,6 +41,15 @@ pub struct Block {
pub uncles: Vec<Header>, pub uncles: Vec<Header>,
} }
impl rlp::Encodable for Block {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(3);
s.append(&self.header);
SignedTransaction::rlp_append_list(s, &self.transactions);
s.append_list(&self.uncles);
}
}
impl Block { impl Block {
/// Get a copy of the header /// Get a copy of the header
#[inline] #[inline]
@ -154,14 +162,14 @@ impl BlockBuilder {
let data = std::iter::repeat_with(|| rand::random::<u8>()) let data = std::iter::repeat_with(|| rand::random::<u8>())
.take(data_len as usize) .take(data_len as usize)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
Transaction { TypedTransaction::Legacy(Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 100_000.into(), gas: 100_000.into(),
action: Action::Create, action: Action::Create,
value: 100.into(), value: 100.into(),
data, data,
} })
.sign(&keccak("").into(), None) .sign(&keccak("").into(), None)
}) })
.take(count); .take(count);
@ -205,7 +213,7 @@ impl BlockBuilder {
let metadata = get_metadata(); let metadata = get_metadata();
let block_number = parent_number + 1; let block_number = parent_number + 1;
let transactions = metadata.transactions; let transactions = metadata.transactions;
let transactions_root = ordered_trie_root(transactions.iter().map(rlp::encode)); let transactions_root = ordered_trie_root(transactions.iter().map(|tx| tx.encode()));
block.header.set_parent_hash(parent_hash); block.header.set_parent_hash(parent_hash);
block.header.set_number(block_number); block.header.set_number(block_number);

View File

@ -18,12 +18,14 @@
use std::{io::Write, ops}; use std::{io::Write, ops};
use common_types::{engines::epoch::Transition as EpochTransition, receipt::Receipt, BlockNumber}; use common_types::{
engines::epoch::Transition as EpochTransition, receipt::TypedReceipt, BlockNumber,
};
use ethereum_types::{H256, H264, U256}; use ethereum_types::{H256, H264, U256};
use heapsize::HeapSizeOf; use heapsize::HeapSizeOf;
use kvdb::PREFIX_LEN as DB_PREFIX_LEN; use kvdb::PREFIX_LEN as DB_PREFIX_LEN;
use rlp; use rlp;
use rlp_derive::{RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper}; use rlp_derive::{RlpDecodable, RlpEncodable};
use crate::db::Key; use crate::db::Key;
@ -235,15 +237,27 @@ impl HeapSizeOf for TransactionAddress {
} }
/// Contains all block receipts. /// Contains all block receipts.
#[derive(Clone, RlpEncodableWrapper, RlpDecodableWrapper)] #[derive(Clone)]
pub struct BlockReceipts { pub struct BlockReceipts {
/// Block receipts /// Block receipts
pub receipts: Vec<Receipt>, pub receipts: Vec<TypedReceipt>,
}
impl rlp::Encodable for BlockReceipts {
fn rlp_append(&self, s: &mut rlp::RlpStream) {
TypedReceipt::rlp_append_list(s, &self.receipts);
}
}
impl rlp::Decodable for BlockReceipts {
fn decode(rlp: &rlp::Rlp) -> Result<Self, rlp::DecoderError> {
Ok(BlockReceipts::new(TypedReceipt::decode_rlp_list(rlp)?))
}
} }
impl BlockReceipts { impl BlockReceipts {
/// Create new block receipts wrapper. /// Create new block receipts wrapper.
pub fn new(receipts: Vec<Receipt>) -> Self { pub fn new(receipts: Vec<TypedReceipt>) -> Self {
BlockReceipts { receipts: receipts } BlockReceipts { receipts: receipts }
} }
} }

View File

@ -44,7 +44,8 @@
"eip1884Transition": "0x0", "eip1884Transition": "0x0",
"eip2028Transition": "0x0", "eip2028Transition": "0x0",
"eip2315Transition": "0x0", "eip2315Transition": "0x0",
"eip2929Transition": "0x0" "eip2929Transition": "0x0",
"eip2930Transition": "0x0"
}, },
"genesis": { "genesis": {
"seal": { "seal": {

View File

@ -48,10 +48,10 @@ use verification::PreverifiedBlock;
use vm::{EnvInfo, LastHashes}; use vm::{EnvInfo, LastHashes};
use hash::keccak; use hash::keccak;
use rlp::{encode_list, Encodable, RlpStream}; use rlp::{encode_list, RlpStream};
use types::{ use types::{
header::{ExtendedHeader, Header}, header::{ExtendedHeader, Header},
receipt::{Receipt, TransactionOutcome}, receipt::{TransactionOutcome, TypedReceipt},
transaction::{Error as TransactionError, SignedTransaction}, transaction::{Error as TransactionError, SignedTransaction},
}; };
@ -99,7 +99,7 @@ pub struct ExecutedBlock {
/// Uncles. /// Uncles.
pub uncles: Vec<Header>, pub uncles: Vec<Header>,
/// Transaction receipts. /// Transaction receipts.
pub receipts: Vec<Receipt>, pub receipts: Vec<TypedReceipt>,
/// Hashes of already executed transactions. /// Hashes of already executed transactions.
pub transactions_set: HashSet<H256>, pub transactions_set: HashSet<H256>,
/// Underlaying state. /// Underlaying state.
@ -253,7 +253,7 @@ impl<'x> OpenBlock<'x> {
&mut self, &mut self,
t: SignedTransaction, t: SignedTransaction,
h: Option<H256>, h: Option<H256>,
) -> Result<&Receipt, Error> { ) -> Result<&TypedReceipt, Error> {
if self.block.transactions_set.contains(&t.hash()) { if self.block.transactions_set.contains(&t.hash()) {
return Err(TransactionError::AlreadyImported.into()); return Err(TransactionError::AlreadyImported.into());
} }
@ -360,13 +360,13 @@ impl<'x> OpenBlock<'x> {
// t_nb 8.5.3 fill open block header with all other fields // t_nb 8.5.3 fill open block header with all other fields
s.block.header.set_transactions_root(ordered_trie_root( s.block.header.set_transactions_root(ordered_trie_root(
s.block.transactions.iter().map(|e| e.rlp_bytes()), s.block.transactions.iter().map(|e| e.encode()),
)); ));
let uncle_bytes = encode_list(&s.block.uncles); let uncle_bytes = encode_list(&s.block.uncles);
s.block.header.set_uncles_hash(keccak(&uncle_bytes)); s.block.header.set_uncles_hash(keccak(&uncle_bytes));
s.block.header.set_state_root(s.block.state.root().clone()); s.block.header.set_state_root(s.block.state.root().clone());
s.block.header.set_receipts_root(ordered_trie_root( s.block.header.set_receipts_root(ordered_trie_root(
s.block.receipts.iter().map(|r| r.rlp_bytes()), s.block.receipts.iter().map(|r| r.encode()),
)); ));
s.block s.block
.header .header
@ -453,7 +453,7 @@ impl LockedBlock {
receipt.outcome = TransactionOutcome::Unknown; receipt.outcome = TransactionOutcome::Unknown;
} }
self.block.header.set_receipts_root(ordered_trie_root( self.block.header.set_receipts_root(ordered_trie_root(
self.block.receipts.iter().map(|r| r.rlp_bytes()), self.block.receipts.iter().map(|r| r.encode()),
)); ));
} }
@ -503,7 +503,7 @@ impl SealedBlock {
pub fn rlp_bytes(&self) -> Bytes { pub fn rlp_bytes(&self) -> Bytes {
let mut block_rlp = RlpStream::new_list(3); let mut block_rlp = RlpStream::new_list(3);
block_rlp.append(&self.block.header); block_rlp.append(&self.block.header);
block_rlp.append_list(&self.block.transactions); SignedTransaction::rlp_append_list(&mut block_rlp, &self.block.transactions);
block_rlp.append_list(&self.block.uncles); block_rlp.append_list(&self.block.uncles);
block_rlp.out() block_rlp.out()
} }

View File

@ -41,7 +41,7 @@ use itertools::Itertools;
use kvdb::{DBTransaction, DBValue, KeyValueDB}; use kvdb::{DBTransaction, DBValue, KeyValueDB};
use parking_lot::{Mutex, RwLock}; use parking_lot::{Mutex, RwLock};
use rand::OsRng; use rand::OsRng;
use rlp::PayloadInfo; use rlp::{PayloadInfo, Rlp};
use rustc_hex::FromHex; use rustc_hex::FromHex;
use trie::{Trie, TrieFactory, TrieSpec}; use trie::{Trie, TrieFactory, TrieSpec};
use types::{ use types::{
@ -51,8 +51,11 @@ use types::{
filter::Filter, filter::Filter,
header::{ExtendedHeader, Header}, header::{ExtendedHeader, Header},
log_entry::LocalizedLogEntry, log_entry::LocalizedLogEntry,
receipt::{LocalizedReceipt, Receipt}, receipt::{LocalizedReceipt, TypedReceipt},
transaction::{self, Action, LocalizedTransaction, SignedTransaction, UnverifiedTransaction}, transaction::{
self, Action, LocalizedTransaction, SignedTransaction, TypedTransaction,
UnverifiedTransaction,
},
BlockNumber, BlockNumber,
}; };
use vm::{EnvInfo, LastHashes}; use vm::{EnvInfo, LastHashes};
@ -524,7 +527,8 @@ impl Importer {
db: &dyn KeyValueDB, db: &dyn KeyValueDB,
chain: &BlockChain, chain: &BlockChain,
) -> EthcoreResult<()> { ) -> EthcoreResult<()> {
let receipts = ::rlp::decode_list(receipts_bytes); let receipts = TypedReceipt::decode_rlp_list(&Rlp::new(receipts_bytes))
.unwrap_or_else(|e| panic!("Receipt bytes should be valid: {:?}", e));
let _import_lock = self.import_lock.lock(); let _import_lock = self.import_lock.lock();
{ {
@ -714,7 +718,7 @@ impl Importer {
&self, &self,
header: &Header, header: &Header,
block_bytes: &[u8], block_bytes: &[u8],
receipts: &[Receipt], receipts: &[TypedReceipt],
state_db: &StateDB, state_db: &StateDB,
client: &Client, client: &Client,
) -> EthcoreResult<Option<PendingTransition>> { ) -> EthcoreResult<Option<PendingTransition>> {
@ -1436,7 +1440,7 @@ impl Client {
data: Bytes, data: Bytes,
) -> SignedTransaction { ) -> SignedTransaction {
let from = Address::default(); let from = Address::default();
transaction::Transaction { TypedTransaction::Legacy(transaction::Transaction {
nonce: self nonce: self
.nonce(&from, block_id) .nonce(&from, block_id)
.unwrap_or_else(|| self.engine.account_start_nonce(0)), .unwrap_or_else(|| self.engine.account_start_nonce(0)),
@ -1445,7 +1449,7 @@ impl Client {
gas_price: U256::default(), gas_price: U256::default(),
value: U256::default(), value: U256::default(),
data: data, data: data,
} })
.fake_sign(from) .fake_sign(from)
} }
@ -1889,7 +1893,7 @@ impl Call for Client {
let exec = |gas| { let exec = |gas| {
let mut tx = t.as_unsigned().clone(); let mut tx = t.as_unsigned().clone();
tx.gas = gas; tx.tx_mut().gas = gas;
let tx = tx.fake_sign(sender); let tx = tx.fake_sign(sender);
let mut clone = state.clone(); let mut clone = state.clone();
@ -1920,6 +1924,7 @@ impl Call for Client {
} }
} }
let lower = t let lower = t
.tx()
.gas_required(&self.engine.schedule(env_info.number)) .gas_required(&self.engine.schedule(env_info.number))
.into(); .into();
if cond(lower) { if cond(lower) {
@ -2571,18 +2576,18 @@ impl BlockChainClient for Client {
} else { } else {
self.importer.miner.sensible_gas_price() self.importer.miner.sensible_gas_price()
}; };
let transaction = transaction::Transaction { let transaction = TypedTransaction::Legacy(transaction::Transaction {
nonce: self.latest_nonce(&authoring_params.author), nonce: self.latest_nonce(&authoring_params.author),
action: Action::Call(address), action: Action::Call(address),
gas: self.importer.miner.sensible_gas_limit(), gas: self.importer.miner.sensible_gas_limit(),
gas_price, gas_price,
value: U256::zero(), value: U256::zero(),
data: data, data: data,
}; });
let chain_id = self.engine.signing_chain_id(&self.latest_env_info()); let chain_id = self.engine.signing_chain_id(&self.latest_env_info());
let signature = self let signature = self
.engine .engine
.sign(transaction.hash(chain_id)) .sign(transaction.signature_hash(chain_id))
.map_err(|e| transaction::Error::InvalidSignature(e.to_string()))?; .map_err(|e| transaction::Error::InvalidSignature(e.to_string()))?;
let signed = SignedTransaction::new(transaction.with_signature(signature, chain_id))?; let signed = SignedTransaction::new(transaction.with_signature(signature, chain_id))?;
self.importer self.importer
@ -2602,10 +2607,15 @@ impl IoClient for Client {
self.queue_transactions self.queue_transactions
.queue(&self.io_channel.read(), len, move |client| { .queue(&self.io_channel.read(), len, move |client| {
trace_time!("import_queued_transactions"); trace_time!("import_queued_transactions");
let best_block_number = client.best_block_header().number();
let txs: Vec<UnverifiedTransaction> = transactions let txs: Vec<UnverifiedTransaction> = transactions
.iter() .iter()
.filter_map(|bytes| client.engine.decode_transaction(bytes).ok()) .filter_map(|bytes| {
client
.engine
.decode_transaction(bytes, best_block_number)
.ok()
})
.collect(); .collect();
client.notify(|notify| { client.notify(|notify| {
@ -2954,7 +2964,7 @@ impl ProvingBlockChainClient for Client {
_ => return None, _ => return None,
}; };
env_info.gas_limit = transaction.gas.clone(); env_info.gas_limit = transaction.tx().gas.clone();
let mut jdb = self.state_db.read().journal_db().boxed_clone(); let mut jdb = self.state_db.read().journal_db().boxed_clone();
state::prove_transaction_virtual( state::prove_transaction_virtual(
@ -3112,7 +3122,7 @@ impl ImportExportBlocks for Client {
fn transaction_receipt( fn transaction_receipt(
machine: &::machine::EthereumMachine, machine: &::machine::EthereumMachine,
mut tx: LocalizedTransaction, mut tx: LocalizedTransaction,
receipt: Receipt, receipt: TypedReceipt,
prior_gas_used: U256, prior_gas_used: U256,
prior_no_of_logs: usize, prior_no_of_logs: usize,
) -> LocalizedReceipt { ) -> LocalizedReceipt {
@ -3121,27 +3131,31 @@ fn transaction_receipt(
let block_hash = tx.block_hash; let block_hash = tx.block_hash;
let block_number = tx.block_number; let block_number = tx.block_number;
let transaction_index = tx.transaction_index; let transaction_index = tx.transaction_index;
let transaction_type = tx.tx_type();
let receipt = receipt.receipt().clone();
LocalizedReceipt { LocalizedReceipt {
from: sender, from: sender,
to: match tx.action { to: match tx.tx().action {
Action::Create => None, Action::Create => None,
Action::Call(ref address) => Some(address.clone().into()), Action::Call(ref address) => Some(address.clone().into()),
}, },
transaction_hash: transaction_hash, transaction_hash: transaction_hash,
transaction_index: transaction_index, transaction_index: transaction_index,
transaction_type: transaction_type,
block_hash: block_hash, block_hash: block_hash,
block_number: block_number, block_number: block_number,
cumulative_gas_used: receipt.gas_used, cumulative_gas_used: receipt.gas_used,
gas_used: receipt.gas_used - prior_gas_used, gas_used: receipt.gas_used - prior_gas_used,
contract_address: match tx.action { contract_address: match tx.tx().action {
Action::Call(_) => None, Action::Call(_) => None,
Action::Create => Some( Action::Create => Some(
contract_address( contract_address(
machine.create_address_scheme(block_number), machine.create_address_scheme(block_number),
&sender, &sender,
&tx.nonce, &tx.tx().nonce,
&tx.data, &tx.tx().data,
) )
.0, .0,
), ),
@ -3161,7 +3175,7 @@ fn transaction_receipt(
}) })
.collect(), .collect(),
log_bloom: receipt.log_bloom, log_bloom: receipt.log_bloom,
outcome: receipt.outcome, outcome: receipt.outcome.clone(),
} }
} }
@ -3457,8 +3471,8 @@ mod tests {
use hash::keccak; use hash::keccak;
use types::{ use types::{
log_entry::{LocalizedLogEntry, LogEntry}, log_entry::{LocalizedLogEntry, LogEntry},
receipt::{LocalizedReceipt, Receipt, TransactionOutcome}, receipt::{LegacyReceipt, LocalizedReceipt, TransactionOutcome, TypedReceipt},
transaction::{Action, LocalizedTransaction, Transaction}, transaction::{Action, LocalizedTransaction, Transaction, TypedTransaction},
}; };
// given // given
@ -3470,14 +3484,14 @@ mod tests {
let block_hash = 5.into(); let block_hash = 5.into();
let state_root = 99.into(); let state_root = 99.into();
let gas_used = 10.into(); let gas_used = 10.into();
let raw_tx = Transaction { let raw_tx = TypedTransaction::Legacy(Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 21000.into(), gas: 21000.into(),
action: Action::Call(10.into()), action: Action::Call(10.into()),
value: 0.into(), value: 0.into(),
data: vec![], data: vec![],
}; });
let tx1 = raw_tx.clone().sign(secret, None); let tx1 = raw_tx.clone().sign(secret, None);
let transaction = LocalizedTransaction { let transaction = LocalizedTransaction {
signed: tx1.clone().into(), signed: tx1.clone().into(),
@ -3498,12 +3512,12 @@ mod tests {
data: vec![], data: vec![],
}, },
]; ];
let receipt = Receipt { let receipt = TypedReceipt::Legacy(LegacyReceipt {
outcome: TransactionOutcome::StateRoot(state_root), outcome: TransactionOutcome::StateRoot(state_root),
gas_used: gas_used, gas_used: gas_used,
log_bloom: Default::default(), log_bloom: Default::default(),
logs: logs.clone(), logs: logs.clone(),
}; });
// when // when
let receipt = transaction_receipt(&machine, transaction, receipt, 5.into(), 1); let receipt = transaction_receipt(&machine, transaction, receipt, 5.into(), 1);
@ -3513,12 +3527,13 @@ mod tests {
receipt, receipt,
LocalizedReceipt { LocalizedReceipt {
from: tx1.sender().into(), from: tx1.sender().into(),
to: match tx1.action { to: match tx1.tx().action {
Action::Create => None, Action::Create => None,
Action::Call(ref address) => Some(address.clone().into()), Action::Call(ref address) => Some(address.clone().into()),
}, },
transaction_hash: tx1.hash(), transaction_hash: tx1.hash(),
transaction_index: 1, transaction_index: 1,
transaction_type: tx1.tx_type(),
block_hash: block_hash, block_hash: block_hash,
block_number: block_number, block_number: block_number,
cumulative_gas_used: gas_used, cumulative_gas_used: gas_used,

View File

@ -281,7 +281,7 @@ impl<'a> EvmTestClient<'a> {
tracer: T, tracer: T,
vm_tracer: V, vm_tracer: V,
) -> std::result::Result<TransactSuccess<T::Output, V::Output>, TransactErr> { ) -> std::result::Result<TransactSuccess<T::Output, V::Output>, TransactErr> {
let initial_gas = transaction.gas; let initial_gas = transaction.tx().gas;
// Verify transaction // Verify transaction
let is_ok = transaction.verify_basic(true, None); let is_ok = transaction.verify_basic(true, None);
if let Err(error) = is_ok { if let Err(error) = is_ok {
@ -342,18 +342,18 @@ impl<'a> EvmTestClient<'a> {
Ok(result) => Ok(TransactSuccess { Ok(result) => Ok(TransactSuccess {
state_root, state_root,
gas_left: initial_gas - result.receipt.gas_used, gas_left: initial_gas - result.receipt.gas_used,
outcome: result.receipt.outcome, outcome: result.receipt.outcome.clone(),
output: result.output, output: result.output,
trace: result.trace, trace: result.trace,
vm_trace: result.vm_trace, vm_trace: result.vm_trace,
logs: result.receipt.logs, logs: result.receipt.logs.clone(),
contract_address: if let transaction::Action::Create = transaction.action { contract_address: if let transaction::Action::Create = transaction.tx().action {
Some( Some(
executive::contract_address( executive::contract_address(
scheme, scheme,
&transaction.sender(), &transaction.sender(),
&transaction.nonce, &transaction.tx().nonce,
&transaction.data, &transaction.tx().data,
) )
.0, .0,
) )

View File

@ -36,7 +36,7 @@ use itertools::Itertools;
use kvdb::DBValue; use kvdb::DBValue;
use kvdb_memorydb; use kvdb_memorydb;
use parking_lot::RwLock; use parking_lot::RwLock;
use rlp::{Rlp, RlpStream}; use rlp::RlpStream;
use rustc_hex::FromHex; use rustc_hex::FromHex;
use types::{ use types::{
basic_account::BasicAccount, basic_account::BasicAccount,
@ -45,8 +45,11 @@ use types::{
header::Header, header::Header,
log_entry::LocalizedLogEntry, log_entry::LocalizedLogEntry,
pruning_info::PruningInfo, pruning_info::PruningInfo,
receipt::{LocalizedReceipt, Receipt, TransactionOutcome}, receipt::{LegacyReceipt, LocalizedReceipt, TransactionOutcome, TypedReceipt},
transaction::{self, Action, LocalizedTransaction, SignedTransaction, Transaction}, transaction::{
self, Action, LocalizedTransaction, SignedTransaction, Transaction, TypedTransaction,
TypedTxId,
},
view, view,
views::BlockView, views::BlockView,
BlockNumber, BlockNumber,
@ -296,16 +299,16 @@ impl TestBlockChainClient {
for _ in 0..num_transactions { for _ in 0..num_transactions {
// Update nonces value // Update nonces value
let tx = Transaction { let tx = TypedTransaction::Legacy(Transaction {
action: Action::Create, action: Action::Create,
value: U256::from(100), value: U256::from(100),
data: "3331600055".from_hex().unwrap(), data: "3331600055".from_hex().unwrap(),
gas: U256::from(100_000), gas: U256::from(100_000),
gas_price: U256::from(200_000_000_000u64), gas_price: U256::from(200_000_000_000u64),
nonce: nonce, nonce: nonce,
}; });
let signed_tx = tx.sign(keypair.secret(), None); let signed_tx = tx.sign(keypair.secret(), None);
txs.append(&signed_tx); signed_tx.rlp_append(&mut txs);
nonce += U256::one(); nonce += U256::one();
} }
@ -369,14 +372,14 @@ impl TestBlockChainClient {
/// Inserts a transaction with given gas price to miners transactions queue. /// Inserts a transaction with given gas price to miners transactions queue.
pub fn insert_transaction_with_gas_price_to_queue(&self, gas_price: U256) -> H256 { pub fn insert_transaction_with_gas_price_to_queue(&self, gas_price: U256) -> H256 {
let keypair = Random.generate().unwrap(); let keypair = Random.generate().unwrap();
let tx = Transaction { let tx = TypedTransaction::Legacy(Transaction {
action: Action::Create, action: Action::Create,
value: U256::from(100), value: U256::from(100),
data: "3331600055".from_hex().unwrap(), data: "3331600055".from_hex().unwrap(),
gas: U256::from(100_000), gas: U256::from(100_000),
gas_price: gas_price, gas_price: gas_price,
nonce: U256::zero(), nonce: U256::zero(),
}; });
let signed_tx = tx.sign(keypair.secret(), None); let signed_tx = tx.sign(keypair.secret(), None);
self.set_balance(signed_tx.sender(), 10_000_000_000_000_000_000u64.into()); self.set_balance(signed_tx.sender(), 10_000_000_000_000_000_000u64.into());
let hash = signed_tx.hash(); let hash = signed_tx.hash();
@ -938,10 +941,13 @@ impl BlockChainClient for TestBlockChainClient {
fn block_receipts(&self, hash: &H256) -> Option<BlockReceipts> { fn block_receipts(&self, hash: &H256) -> Option<BlockReceipts> {
// starts with 'f' ? // starts with 'f' ?
if *hash > H256::from("f000000000000000000000000000000000000000000000000000000000000000") { if *hash > H256::from("f000000000000000000000000000000000000000000000000000000000000000") {
let receipt = BlockReceipts::new(vec![Receipt::new( let receipt = BlockReceipts::new(vec![TypedReceipt::new(
TypedTxId::Legacy,
LegacyReceipt::new(
TransactionOutcome::StateRoot(H256::zero()), TransactionOutcome::StateRoot(H256::zero()),
U256::zero(), U256::zero(),
vec![], vec![],
),
)]); )]);
return Some(receipt); return Some(receipt);
} }
@ -1027,16 +1033,20 @@ impl BlockChainClient for TestBlockChainClient {
} }
fn transact_contract(&self, address: Address, data: Bytes) -> Result<(), transaction::Error> { fn transact_contract(&self, address: Address, data: Bytes) -> Result<(), transaction::Error> {
let transaction = Transaction { let transaction = TypedTransaction::Legacy(Transaction {
nonce: self.latest_nonce(&self.miner.authoring_params().author), nonce: self.latest_nonce(&self.miner.authoring_params().author),
action: Action::Call(address), action: Action::Call(address),
gas: self.spec.gas_limit, gas: self.spec.gas_limit,
gas_price: U256::zero(), gas_price: U256::zero(),
value: U256::default(), value: U256::default(),
data: data, data: data,
}; });
let chain_id = Some(self.spec.chain_id()); let chain_id = Some(self.spec.chain_id());
let sig = self.spec.engine.sign(transaction.hash(chain_id)).unwrap(); let sig = self
.spec
.engine
.sign(transaction.signature_hash(chain_id))
.unwrap();
let signed = SignedTransaction::new(transaction.with_signature(sig, chain_id)).unwrap(); let signed = SignedTransaction::new(transaction.with_signature(sig, chain_id)).unwrap();
self.miner.import_own_transaction(self, signed.into()) self.miner.import_own_transaction(self, signed.into())
} }
@ -1051,7 +1061,7 @@ impl IoClient for TestBlockChainClient {
// import right here // import right here
let txs = transactions let txs = transactions
.into_iter() .into_iter()
.filter_map(|bytes| Rlp::new(&bytes).as_val().ok()) .filter_map(|bytes| TypedTransaction::decode(&bytes).ok())
.collect(); .collect();
self.miner.import_external_transactions(self, txs); self.miner.import_external_transactions(self, txs);
} }

View File

@ -1765,7 +1765,7 @@ mod tests {
use test_helpers::{generate_dummy_client_with_spec, get_temp_state_db, TestNotify}; use test_helpers::{generate_dummy_client_with_spec, get_temp_state_db, TestNotify};
use types::{ use types::{
header::Header, header::Header,
transaction::{Action, Transaction}, transaction::{Action, Transaction, TypedTransaction},
}; };
fn aura<F>(f: F) -> Arc<AuthorityRound> fn aura<F>(f: F) -> Arc<AuthorityRound>
@ -2287,14 +2287,14 @@ mod tests {
) )
.unwrap(); .unwrap();
b2.push_transaction( b2.push_transaction(
Transaction { TypedTransaction::Legacy(Transaction {
action: Action::Create, action: Action::Create,
nonce: U256::from(0), nonce: U256::from(0),
gas_price: U256::from(3000), gas_price: U256::from(3000),
gas: U256::from(53_000), gas: U256::from(53_000),
value: U256::from(1), value: U256::from(1),
data: vec![], data: vec![],
} })
.fake_sign(addr2), .fake_sign(addr2),
None, None,
) )

View File

@ -605,8 +605,10 @@ pub trait EthEngine: Engine<::machine::EthereumMachine> {
fn decode_transaction( fn decode_transaction(
&self, &self,
transaction: &[u8], transaction: &[u8],
best_block_number: BlockNumber,
) -> Result<UnverifiedTransaction, transaction::Error> { ) -> Result<UnverifiedTransaction, transaction::Error> {
self.machine().decode_transaction(transaction) let schedule = self.schedule(best_block_number);
self.machine().decode_transaction(transaction, &schedule)
} }
} }

View File

@ -25,7 +25,7 @@ use kvdb::DBValue;
use memory_cache::MemoryLruCache; use memory_cache::MemoryLruCache;
use parking_lot::RwLock; use parking_lot::RwLock;
use rlp::{Rlp, RlpStream}; use rlp::{Rlp, RlpStream};
use types::{header::Header, ids::BlockId, log_entry::LogEntry, receipt::Receipt}; use types::{header::Header, ids::BlockId, log_entry::LogEntry, receipt::TypedReceipt};
use unexpected::Mismatch; use unexpected::Mismatch;
use super::{simple_list::SimpleList, SystemCall, ValidatorSet}; use super::{simple_list::SimpleList, SystemCall, ValidatorSet};
@ -91,7 +91,7 @@ fn check_first_proof(
old_header: Header, old_header: Header,
state_items: &[DBValue], state_items: &[DBValue],
) -> Result<Vec<Address>, String> { ) -> Result<Vec<Address>, String> {
use types::transaction::{Action, Transaction}; use types::transaction::{Action, Transaction, TypedTransaction};
// TODO: match client contract_call_tx more cleanly without duplication. // TODO: match client contract_call_tx more cleanly without duplication.
const PROVIDED_GAS: u64 = 50_000_000; const PROVIDED_GAS: u64 = 50_000_000;
@ -116,14 +116,14 @@ fn check_first_proof(
let (data, decoder) = validator_set::functions::get_validators::call(); let (data, decoder) = validator_set::functions::get_validators::call();
let from = Address::default(); let from = Address::default();
let tx = Transaction { let tx = TypedTransaction::Legacy(Transaction {
nonce: machine.account_start_nonce(number), nonce: machine.account_start_nonce(number),
action: Action::Call(contract_address), action: Action::Call(contract_address),
gas: PROVIDED_GAS.into(), gas: PROVIDED_GAS.into(),
gas_price: U256::default(), gas_price: U256::default(),
value: U256::default(), value: U256::default(),
data, data,
} })
.fake_sign(from); .fake_sign(from);
let res = ::state::check_proof( let res = ::state::check_proof(
@ -161,14 +161,15 @@ fn decode_first_proof(rlp: &Rlp) -> Result<(Header, Vec<DBValue>), ::error::Erro
// inter-contract proofs are a header and receipts. // inter-contract proofs are a header and receipts.
// checking will involve ensuring that the receipts match the header and // checking will involve ensuring that the receipts match the header and
// extracting the validator set from the receipts. // extracting the validator set from the receipts.
fn encode_proof(header: &Header, receipts: &[Receipt]) -> Bytes { fn encode_proof(header: &Header, receipts: &[TypedReceipt]) -> Bytes {
let mut stream = RlpStream::new_list(2); let mut stream = RlpStream::new_list(2);
stream.append(header).append_list(receipts); stream.append(header);
TypedReceipt::rlp_append_list(&mut stream, receipts);
stream.drain() stream.drain()
} }
fn decode_proof(rlp: &Rlp) -> Result<(Header, Vec<Receipt>), ::error::Error> { fn decode_proof(rlp: &Rlp) -> Result<(Header, Vec<TypedReceipt>), ::error::Error> {
Ok((rlp.val_at(0)?, rlp.list_at(1)?)) Ok((rlp.val_at(0)?, TypedReceipt::decode_rlp_list(&rlp.at(1)?)?))
} }
// given a provider and caller, generate proof. this will just be a state proof // given a provider and caller, generate proof. this will just be a state proof
@ -265,7 +266,7 @@ impl ValidatorSafeContract {
&self, &self,
bloom: Bloom, bloom: Bloom,
header: &Header, header: &Header,
receipts: &[Receipt], receipts: &[TypedReceipt],
) -> Option<SimpleList> { ) -> Option<SimpleList> {
let check_log = |log: &LogEntry| { let check_log = |log: &LogEntry| {
log.address == self.contract_address log.address == self.contract_address
@ -406,7 +407,7 @@ impl ValidatorSet for ValidatorSafeContract {
// ensure receipts match header. // ensure receipts match header.
// TODO: optimize? these were just decoded. // TODO: optimize? these were just decoded.
let found_root = ::triehash::ordered_trie_root(receipts.iter().map(::rlp::encode)); let found_root = ::triehash::ordered_trie_root(receipts.iter().map(|r| r.encode()));
if found_root != *old_header.receipts_root() { if found_root != *old_header.receipts_root() {
return Err(::error::BlockError::InvalidReceiptsRoot(Mismatch { return Err(::error::BlockError::InvalidReceiptsRoot(Mismatch {
expected: *old_header.receipts_root(), expected: *old_header.receipts_root(),
@ -491,7 +492,7 @@ mod tests {
use test_helpers::{generate_dummy_client_with_spec, generate_dummy_client_with_spec_and_data}; use test_helpers::{generate_dummy_client_with_spec, generate_dummy_client_with_spec_and_data};
use types::{ use types::{
ids::BlockId, ids::BlockId,
transaction::{Action, Transaction}, transaction::{Action, Transaction, TypedTransaction},
}; };
use verification::queue::kind::blocks::Unverified; use verification::queue::kind::blocks::Unverified;
@ -537,7 +538,7 @@ mod tests {
client.miner().set_author(miner::Author::Sealer(signer)); client.miner().set_author(miner::Author::Sealer(signer));
// Remove "1" validator. // Remove "1" validator.
let tx = Transaction { let tx = TypedTransaction::Legacy(Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 500_000.into(), gas: 500_000.into(),
@ -546,7 +547,7 @@ mod tests {
data: "bfc708a000000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1" data: "bfc708a000000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1"
.from_hex() .from_hex()
.unwrap(), .unwrap(),
} })
.sign(&s0, Some(chain_id)); .sign(&s0, Some(chain_id));
client client
.miner() .miner()
@ -555,7 +556,7 @@ mod tests {
EngineClient::update_sealing(&*client, ForceUpdateSealing::No); EngineClient::update_sealing(&*client, ForceUpdateSealing::No);
assert_eq!(client.chain_info().best_block_number, 1); assert_eq!(client.chain_info().best_block_number, 1);
// Add "1" validator back in. // Add "1" validator back in.
let tx = Transaction { let tx = TypedTransaction::Legacy(Transaction {
nonce: 1.into(), nonce: 1.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 500_000.into(), gas: 500_000.into(),
@ -564,7 +565,7 @@ mod tests {
data: "4d238c8e00000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1" data: "4d238c8e00000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1"
.from_hex() .from_hex()
.unwrap(), .unwrap(),
} })
.sign(&s0, Some(chain_id)); .sign(&s0, Some(chain_id));
client client
.miner() .miner()
@ -582,14 +583,14 @@ mod tests {
// Switch back to the added validator, since the state is updated. // Switch back to the added validator, since the state is updated.
let signer = Box::new((tap.clone(), v1, "".into())); let signer = Box::new((tap.clone(), v1, "".into()));
client.miner().set_author(miner::Author::Sealer(signer)); client.miner().set_author(miner::Author::Sealer(signer));
let tx = Transaction { let tx = TypedTransaction::Legacy(Transaction {
nonce: 2.into(), nonce: 2.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 21000.into(), gas: 21000.into(),
action: Action::Call(Address::default()), action: Action::Call(Address::default()),
value: 0.into(), value: 0.into(),
data: Vec::new(), data: Vec::new(),
} })
.sign(&s0, Some(chain_id)); .sign(&s0, Some(chain_id));
client client
.miner() .miner()

View File

@ -28,7 +28,7 @@ use state::{Backend as StateBackend, CleanupMode, State, Substate};
use std::{cmp, sync::Arc}; use std::{cmp, sync::Arc};
use trace::{self, Tracer, VMTracer}; use trace::{self, Tracer, VMTracer};
use transaction_ext::Transaction; use transaction_ext::Transaction;
use types::transaction::{Action, SignedTransaction}; use types::transaction::{Action, SignedTransaction, TypedTransaction};
use vm::{ use vm::{
self, AccessList, ActionParams, ActionValue, CleanDustMode, CreateContractAddress, EnvInfo, self, AccessList, ActionParams, ActionValue, CleanDustMode, CreateContractAddress, EnvInfo,
ResumeCall, ResumeCreate, ReturnData, Schedule, TrapError, ResumeCall, ResumeCreate, ReturnData, Schedule, TrapError,
@ -1109,7 +1109,10 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
{ {
let sender = t.sender(); let sender = t.sender();
let balance = self.state.balance(&sender)?; let balance = self.state.balance(&sender)?;
let needed_balance = t.value.saturating_add(t.gas.saturating_mul(t.gas_price)); let needed_balance = t
.tx()
.value
.saturating_add(t.tx().gas.saturating_mul(t.tx().gas_price));
if balance < needed_balance { if balance < needed_balance {
// give the sender a sufficient balance // give the sender a sufficient balance
self.state self.state
@ -1132,16 +1135,51 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
T: Tracer, T: Tracer,
V: VMTracer, V: VMTracer,
{ {
let schedule = self.schedule;
// check if particualar transaction type is enabled at this block number in schedule
match t.as_unsigned() {
TypedTransaction::AccessList(_) => {
if !schedule.eip2930 {
return Err(ExecutionError::TransactionMalformed(
"OptionalAccessList EIP-2930 or EIP-2929 not enabled".into(),
));
}
}
TypedTransaction::Legacy(_) => (), //legacy transactions are allways valid
};
let sender = t.sender(); let sender = t.sender();
let nonce = self.state.nonce(&sender)?; let nonce = self.state.nonce(&sender)?;
let schedule = self.schedule; let mut base_gas_required = U256::from(t.tx().gas_required(&schedule));
let base_gas_required = U256::from(t.gas_required(&schedule));
if t.gas < base_gas_required { let mut access_list = AccessList::new(schedule.eip2929);
if schedule.eip2929 {
for (address, _) in self.machine.builtins() {
access_list.insert_address(*address);
}
if schedule.eip2930 {
// optional access list
if let TypedTransaction::AccessList(al_tx) = t.as_unsigned() {
for item in al_tx.access_list.iter() {
access_list.insert_address(item.0);
base_gas_required += vm::schedule::EIP2930_ACCESS_LIST_ADDRESS_COST.into();
for key in item.1.iter() {
access_list.insert_storage_key(item.0, *key);
base_gas_required +=
vm::schedule::EIP2930_ACCESS_LIST_STORAGE_KEY_COST.into();
}
}
}
}
}
if t.tx().gas < base_gas_required {
return Err(ExecutionError::NotEnoughBaseGas { return Err(ExecutionError::NotEnoughBaseGas {
required: base_gas_required, required: base_gas_required,
got: t.gas, got: t.tx().gas,
}); });
} }
@ -1153,29 +1191,29 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
return Err(ExecutionError::SenderMustExist); return Err(ExecutionError::SenderMustExist);
} }
let init_gas = t.gas - base_gas_required; let init_gas = t.tx().gas - base_gas_required;
// validate transaction nonce // validate transaction nonce
if check_nonce && t.nonce != nonce { if check_nonce && t.tx().nonce != nonce {
return Err(ExecutionError::InvalidNonce { return Err(ExecutionError::InvalidNonce {
expected: nonce, expected: nonce,
got: t.nonce, got: t.tx().nonce,
}); });
} }
// validate if transaction fits into given block // validate if transaction fits into given block
if self.info.gas_used + t.gas > self.info.gas_limit { if self.info.gas_used + t.tx().gas > self.info.gas_limit {
return Err(ExecutionError::BlockGasLimitReached { return Err(ExecutionError::BlockGasLimitReached {
gas_limit: self.info.gas_limit, gas_limit: self.info.gas_limit,
gas_used: self.info.gas_used, gas_used: self.info.gas_used,
gas: t.gas, gas: t.tx().gas,
}); });
} }
// TODO: we might need bigints here, or at least check overflows. // TODO: we might need bigints here, or at least check overflows.
let balance = self.state.balance(&sender)?; let balance = self.state.balance(&sender)?;
let gas_cost = t.gas.full_mul(t.gas_price); let gas_cost = t.tx().gas.full_mul(t.tx().gas_price);
let total_cost = U512::from(t.value) + gas_cost; let total_cost = U512::from(t.tx().value) + gas_cost;
// avoid unaffordable transactions // avoid unaffordable transactions
let balance512 = U512::from(balance); let balance512 = U512::from(balance);
@ -1186,13 +1224,6 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
}); });
} }
let mut access_list = AccessList::new(schedule.eip2929);
if schedule.eip2929 {
for (address, _) in self.machine.builtins() {
access_list.insert_address(*address);
}
}
let mut substate = Substate::from_access_list(&access_list); let mut substate = Substate::from_access_list(&access_list);
// NOTE: there can be no invalid transactions from this point. // NOTE: there can be no invalid transactions from this point.
@ -1205,13 +1236,13 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
&mut substate.to_cleanup_mode(&schedule), &mut substate.to_cleanup_mode(&schedule),
)?; )?;
let (result, output) = match t.action { let (result, output) = match t.tx().action {
Action::Create => { Action::Create => {
let (new_address, code_hash) = contract_address( let (new_address, code_hash) = contract_address(
self.machine.create_address_scheme(self.info.number), self.machine.create_address_scheme(self.info.number),
&sender, &sender,
&nonce, &nonce,
&t.data, &t.tx().data,
); );
let params = ActionParams { let params = ActionParams {
code_address: new_address.clone(), code_address: new_address.clone(),
@ -1220,9 +1251,9 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
sender: sender.clone(), sender: sender.clone(),
origin: sender.clone(), origin: sender.clone(),
gas: init_gas, gas: init_gas,
gas_price: t.gas_price, gas_price: t.tx().gas_price,
value: ActionValue::Transfer(t.value), value: ActionValue::Transfer(t.tx().value),
code: Some(Arc::new(t.data.clone())), code: Some(Arc::new(t.tx().data.clone())),
data: None, data: None,
call_type: CallType::None, call_type: CallType::None,
params_type: vm::ParamsType::Embedded, params_type: vm::ParamsType::Embedded,
@ -1242,11 +1273,11 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
sender: sender.clone(), sender: sender.clone(),
origin: sender.clone(), origin: sender.clone(),
gas: init_gas, gas: init_gas,
gas_price: t.gas_price, gas_price: t.tx().gas_price,
value: ActionValue::Transfer(t.value), value: ActionValue::Transfer(t.tx().value),
code: self.state.code(address)?, code: self.state.code(address)?,
code_hash: self.state.code_hash(address)?, code_hash: self.state.code_hash(address)?,
data: Some(t.data.clone()), data: Some(t.tx().data.clone()),
call_type: CallType::Call, call_type: CallType::Call,
params_type: vm::ParamsType::Separate, params_type: vm::ParamsType::Separate,
access_list: access_list, access_list: access_list,
@ -1445,12 +1476,12 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
Ok(FinalizationResult { gas_left, .. }) => gas_left, Ok(FinalizationResult { gas_left, .. }) => gas_left,
_ => 0.into(), _ => 0.into(),
}; };
let refunded = cmp::min(refunds_bound, (t.gas - gas_left_prerefund) >> 1); let refunded = cmp::min(refunds_bound, (t.tx().gas - gas_left_prerefund) >> 1);
let gas_left = gas_left_prerefund + refunded; let gas_left = gas_left_prerefund + refunded;
let gas_used = t.gas.saturating_sub(gas_left); let gas_used = t.tx().gas.saturating_sub(gas_left);
let (refund_value, overflow_1) = gas_left.overflowing_mul(t.gas_price); let (refund_value, overflow_1) = gas_left.overflowing_mul(t.tx().gas_price);
let (fees_value, overflow_2) = gas_used.overflowing_mul(t.gas_price); let (fees_value, overflow_2) = gas_used.overflowing_mul(t.tx().gas_price);
if overflow_1 || overflow_2 { if overflow_1 || overflow_2 {
return Err(ExecutionError::TransactionMalformed( return Err(ExecutionError::TransactionMalformed(
"U256 Overflow".to_string(), "U256 Overflow".to_string(),
@ -1458,7 +1489,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
} }
trace!("exec::finalize: t.gas={}, sstore_refunds={}, suicide_refunds={}, refunds_bound={}, gas_left_prerefund={}, refunded={}, gas_left={}, gas_used={}, refund_value={}, fees_value={}\n", trace!("exec::finalize: t.gas={}, sstore_refunds={}, suicide_refunds={}, refunds_bound={}, gas_left_prerefund={}, refunded={}, gas_left={}, gas_used={}, refund_value={}, fees_value={}\n",
t.gas, sstore_refunds, suicide_refunds, refunds_bound, gas_left_prerefund, refunded, gas_left, gas_used, refund_value, fees_value); t.tx().gas, sstore_refunds, suicide_refunds, refunds_bound, gas_left_prerefund, refunded, gas_left, gas_used, refund_value, fees_value);
let sender = t.sender(); let sender = t.sender();
trace!( trace!(
@ -1487,7 +1518,11 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
// perform garbage-collection // perform garbage-collection
let min_balance = if schedule.kill_dust != CleanDustMode::Off { let min_balance = if schedule.kill_dust != CleanDustMode::Off {
Some(U256::from(schedule.tx_gas).overflowing_mul(t.gas_price).0) Some(
U256::from(schedule.tx_gas)
.overflowing_mul(t.tx().gas_price)
.0,
)
} else { } else {
None None
}; };
@ -1502,10 +1537,10 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
Err(vm::Error::Internal(msg)) => Err(ExecutionError::Internal(msg)), Err(vm::Error::Internal(msg)) => Err(ExecutionError::Internal(msg)),
Err(exception) => Ok(Executed { Err(exception) => Ok(Executed {
exception: Some(exception), exception: Some(exception),
gas: t.gas, gas: t.tx().gas,
gas_used: t.gas, gas_used: t.tx().gas,
refunded: U256::zero(), refunded: U256::zero(),
cumulative_gas_used: self.info.gas_used + t.gas, cumulative_gas_used: self.info.gas_used + t.tx().gas,
logs: vec![], logs: vec![],
contracts_created: vec![], contracts_created: vec![],
output: output, output: output,
@ -1519,7 +1554,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
} else { } else {
Some(vm::Error::Reverted) Some(vm::Error::Reverted)
}, },
gas: t.gas, gas: t.tx().gas,
gas_used: gas_used, gas_used: gas_used,
refunded: refunded, refunded: refunded,
cumulative_gas_used: self.info.gas_used + gas_used, cumulative_gas_used: self.info.gas_used + gas_used,
@ -1551,7 +1586,7 @@ mod tests {
trace, ExecutiveTracer, ExecutiveVMTracer, FlatTrace, MemoryDiff, NoopTracer, NoopVMTracer, trace, ExecutiveTracer, ExecutiveVMTracer, FlatTrace, MemoryDiff, NoopTracer, NoopVMTracer,
StorageDiff, Tracer, VMExecutedOperation, VMOperation, VMTrace, VMTracer, StorageDiff, Tracer, VMExecutedOperation, VMOperation, VMTrace, VMTracer,
}; };
use types::transaction::{Action, Transaction}; use types::transaction::{Action, Transaction, TypedTransaction};
use vm::{ActionParams, ActionValue, CallType, CreateContractAddress, EnvInfo}; use vm::{ActionParams, ActionValue, CallType, CreateContractAddress, EnvInfo};
fn make_frontier_machine(max_depth: usize) -> EthereumMachine { fn make_frontier_machine(max_depth: usize) -> EthereumMachine {
@ -2445,14 +2480,14 @@ mod tests {
evm_test_ignore! {test_transact_simple: test_transact_simple_int} evm_test_ignore! {test_transact_simple: test_transact_simple_int}
fn test_transact_simple(factory: Factory) { fn test_transact_simple(factory: Factory) {
let keypair = Random.generate().unwrap(); let keypair = Random.generate().unwrap();
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
action: Action::Create, action: Action::Create,
value: U256::from(17), value: U256::from(17),
data: "3331600055".from_hex().unwrap(), data: "3331600055".from_hex().unwrap(),
gas: U256::from(100_000), gas: U256::from(100_000),
gas_price: U256::zero(), gas_price: U256::zero(),
nonce: U256::zero(), nonce: U256::zero(),
} })
.sign(keypair.secret(), None); .sign(keypair.secret(), None);
let sender = t.sender(); let sender = t.sender();
let contract = contract_address( let contract = contract_address(
@ -2496,14 +2531,14 @@ mod tests {
evm_test! {test_transact_invalid_nonce: test_transact_invalid_nonce_int} evm_test! {test_transact_invalid_nonce: test_transact_invalid_nonce_int}
fn test_transact_invalid_nonce(factory: Factory) { fn test_transact_invalid_nonce(factory: Factory) {
let keypair = Random.generate().unwrap(); let keypair = Random.generate().unwrap();
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
action: Action::Create, action: Action::Create,
value: U256::from(17), value: U256::from(17),
data: "3331600055".from_hex().unwrap(), data: "3331600055".from_hex().unwrap(),
gas: U256::from(100_000), gas: U256::from(100_000),
gas_price: U256::zero(), gas_price: U256::zero(),
nonce: U256::one(), nonce: U256::one(),
} })
.sign(keypair.secret(), None); .sign(keypair.secret(), None);
let sender = t.sender(); let sender = t.sender();
@ -2535,14 +2570,14 @@ mod tests {
evm_test! {test_transact_gas_limit_reached: test_transact_gas_limit_reached_int} evm_test! {test_transact_gas_limit_reached: test_transact_gas_limit_reached_int}
fn test_transact_gas_limit_reached(factory: Factory) { fn test_transact_gas_limit_reached(factory: Factory) {
let keypair = Random.generate().unwrap(); let keypair = Random.generate().unwrap();
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
action: Action::Create, action: Action::Create,
value: U256::from(17), value: U256::from(17),
data: "3331600055".from_hex().unwrap(), data: "3331600055".from_hex().unwrap(),
gas: U256::from(80_001), gas: U256::from(80_001),
gas_price: U256::zero(), gas_price: U256::zero(),
nonce: U256::zero(), nonce: U256::zero(),
} })
.sign(keypair.secret(), None); .sign(keypair.secret(), None);
let sender = t.sender(); let sender = t.sender();
@ -2580,14 +2615,14 @@ mod tests {
evm_test! {test_not_enough_cash: test_not_enough_cash_int} evm_test! {test_not_enough_cash: test_not_enough_cash_int}
fn test_not_enough_cash(factory: Factory) { fn test_not_enough_cash(factory: Factory) {
let keypair = Random.generate().unwrap(); let keypair = Random.generate().unwrap();
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
action: Action::Create, action: Action::Create,
value: U256::from(18), value: U256::from(18),
data: "3331600055".from_hex().unwrap(), data: "3331600055".from_hex().unwrap(),
gas: U256::from(100_000), gas: U256::from(100_000),
gas_price: U256::one(), gas_price: U256::one(),
nonce: U256::zero(), nonce: U256::zero(),
} })
.sign(keypair.secret(), None); .sign(keypair.secret(), None);
let sender = t.sender(); let sender = t.sender();

View File

@ -17,10 +17,12 @@
use super::test_common::*; use super::test_common::*;
use client::EvmTestClient; use client::EvmTestClient;
use ethjson; use ethjson;
use rlp::Rlp;
use std::path::Path; use std::path::Path;
use transaction_ext::Transaction; use transaction_ext::Transaction;
use types::{header::Header, transaction::UnverifiedTransaction}; use types::{
header::Header,
transaction::{TypedTransaction, UnverifiedTransaction},
};
pub fn json_transaction_test<H: FnMut(&str, HookType)>( pub fn json_transaction_test<H: FnMut(&str, HookType)>(
path: &Path, path: &Path,
@ -65,8 +67,7 @@ pub fn json_transaction_test<H: FnMut(&str, HookType)>(
}; };
let rlp: Vec<u8> = test.rlp.clone().into(); let rlp: Vec<u8> = test.rlp.clone().into();
let res = Rlp::new(&rlp) let res = TypedTransaction::decode(&rlp)
.as_val()
.map_err(::error::Error::from) .map_err(::error::Error::from)
.and_then(|t: UnverifiedTransaction| { .and_then(|t: UnverifiedTransaction| {
let mut header: Header = Default::default(); let mut header: Header = Default::default();
@ -74,12 +75,13 @@ pub fn json_transaction_test<H: FnMut(&str, HookType)>(
header.set_number(BLOCK_NUMBER); header.set_number(BLOCK_NUMBER);
let minimal = t let minimal = t
.tx()
.gas_required(&spec.engine.schedule(header.number())) .gas_required(&spec.engine.schedule(header.number()))
.into(); .into();
if t.gas < minimal { if t.tx().gas < minimal {
return Err(::types::transaction::Error::InsufficientGas { return Err(::types::transaction::Error::InsufficientGas {
minimal, minimal,
got: t.gas, got: t.tx().gas,
} }
.into()); .into());
} }

View File

@ -23,11 +23,11 @@ use std::{
}; };
use ethereum_types::{Address, H256, U256}; use ethereum_types::{Address, H256, U256};
use rlp::Rlp;
use types::{ use types::{
header::Header, header::Header,
transaction::{ transaction::{
self, SignedTransaction, UnverifiedTransaction, SYSTEM_ADDRESS, UNSIGNED_SENDER, self, SignedTransaction, TypedTransaction, UnverifiedTransaction, SYSTEM_ADDRESS,
UNSIGNED_SENDER,
}, },
BlockNumber, BlockNumber,
}; };
@ -455,17 +455,27 @@ impl EthereumMachine {
pub fn decode_transaction( pub fn decode_transaction(
&self, &self,
transaction: &[u8], transaction: &[u8],
schedule: &Schedule,
) -> Result<UnverifiedTransaction, transaction::Error> { ) -> Result<UnverifiedTransaction, transaction::Error> {
let rlp = Rlp::new(&transaction); if transaction.len() > self.params().max_transaction_size {
if rlp.as_raw().len() > self.params().max_transaction_size {
debug!( debug!(
"Rejected oversized transaction of {} bytes", "Rejected oversized transaction of {} bytes",
rlp.as_raw().len() transaction.len()
); );
return Err(transaction::Error::TooBig); return Err(transaction::Error::TooBig);
} }
rlp.as_val()
.map_err(|e| transaction::Error::InvalidRlp(e.to_string())) let tx = TypedTransaction::decode(transaction)
.map_err(|e| transaction::Error::InvalidRlp(e.to_string()))?;
match tx.tx_type() {
transaction::TypedTxId::AccessList if schedule.eip2930 => {
return Err(transaction::Error::TransactionTypeNotEnabled)
}
_ => (),
};
Ok(tx)
} }
} }
@ -476,7 +486,7 @@ pub struct AuxiliaryData<'a> {
/// The full block bytes, including the header. /// The full block bytes, including the header.
pub bytes: Option<&'a [u8]>, pub bytes: Option<&'a [u8]>,
/// The block receipts. /// The block receipts.
pub receipts: Option<&'a [::types::receipt::Receipt]>, pub receipts: Option<&'a [::types::receipt::TypedReceipt]>,
} }
/// Type alias for a function we can make calls through synchronously. /// Type alias for a function we can make calls through synchronously.
@ -550,7 +560,7 @@ mod tests {
fn should_disallow_unsigned_transactions() { fn should_disallow_unsigned_transactions() {
let rlp = "ea80843b9aca0083015f90948921ebb5f79e9e3920abe571004d0b1d5119c154865af3107a400080038080"; let rlp = "ea80843b9aca0083015f90948921ebb5f79e9e3920abe571004d0b1d5119c154865af3107a400080038080";
let transaction: UnverifiedTransaction = let transaction: UnverifiedTransaction =
::rlp::decode(&::rustc_hex::FromHex::from_hex(rlp).unwrap()).unwrap(); TypedTransaction::decode(&::rustc_hex::FromHex::from_hex(rlp).unwrap()).unwrap();
let spec = ::ethereum::new_ropsten_test(); let spec = ::ethereum::new_ropsten_test();
let ethparams = get_default_ethash_extensions(); let ethparams = get_default_ethash_extensions();

View File

@ -1195,15 +1195,16 @@ impl miner::MinerService for Miner {
let receipt = &receipts[index]; let receipt = &receipts[index];
RichReceipt { RichReceipt {
from: tx.sender(), from: tx.sender(),
to: match tx.action { to: match tx.tx().action {
Action::Create => None, Action::Create => None,
Action::Call(ref address) => Some(*address), Action::Call(ref address) => Some(*address),
}, },
transaction_hash: tx.hash(), transaction_hash: tx.hash(),
transaction_type: tx.tx_type(),
transaction_index: index, transaction_index: index,
cumulative_gas_used: receipt.gas_used, cumulative_gas_used: receipt.gas_used,
gas_used: receipt.gas_used - prev_gas, gas_used: receipt.gas_used - prev_gas,
contract_address: match tx.action { contract_address: match tx.tx().action {
Action::Call(_) => None, Action::Call(_) => None,
Action::Create => { Action::Create => {
let sender = tx.sender(); let sender = tx.sender();
@ -1212,8 +1213,8 @@ impl miner::MinerService for Miner {
self.engine self.engine
.create_address_scheme(pending.header.number()), .create_address_scheme(pending.header.number()),
&sender, &sender,
&tx.nonce, &tx.tx().nonce,
&tx.data, &tx.tx().data,
) )
.0, .0,
) )
@ -1509,7 +1510,7 @@ mod tests {
use client::{ChainInfo, EachBlockWith, ImportSealedBlock, TestBlockChainClient}; use client::{ChainInfo, EachBlockWith, ImportSealedBlock, TestBlockChainClient};
use miner::{MinerService, PendingOrdering}; use miner::{MinerService, PendingOrdering};
use test_helpers::{generate_dummy_client, generate_dummy_client_with_spec}; use test_helpers::{generate_dummy_client, generate_dummy_client_with_spec};
use types::transaction::Transaction; use types::transaction::{Transaction, TypedTransaction};
#[test] #[test]
fn should_prepare_block_to_seal() { fn should_prepare_block_to_seal() {
@ -1583,14 +1584,14 @@ mod tests {
fn transaction_with_chain_id(chain_id: u64) -> SignedTransaction { fn transaction_with_chain_id(chain_id: u64) -> SignedTransaction {
let keypair = Random.generate().unwrap(); let keypair = Random.generate().unwrap();
Transaction { TypedTransaction::Legacy(Transaction {
action: Action::Create, action: Action::Create,
value: U256::zero(), value: U256::zero(),
data: "3331600055".from_hex().unwrap(), data: "3331600055".from_hex().unwrap(),
gas: U256::from(100_000), gas: U256::from(100_000),
gas_price: U256::zero(), gas_price: U256::zero(),
nonce: U256::zero(), nonce: U256::zero(),
} })
.sign(keypair.secret(), Some(chain_id)) .sign(keypair.secret(), Some(chain_id))
} }

View File

@ -195,7 +195,8 @@ where
&self, &self,
transaction: &[u8], transaction: &[u8],
) -> Result<UnverifiedTransaction, transaction::Error> { ) -> Result<UnverifiedTransaction, transaction::Error> {
self.engine.decode_transaction(transaction) let number = self.chain.best_block_header().number();
self.engine.decode_transaction(transaction, number)
} }
} }

View File

@ -21,7 +21,7 @@ use ethereum_types::H256;
use hash::keccak; use hash::keccak;
use rlp::{DecoderError, Rlp, RlpStream}; use rlp::{DecoderError, Rlp, RlpStream};
use triehash::ordered_trie_root; use triehash::ordered_trie_root;
use types::{block::Block, header::Header, views::BlockView}; use types::{block::Block, header::Header, transaction::TypedTransaction, views::BlockView};
const HEADER_FIELDS: usize = 8; const HEADER_FIELDS: usize = 8;
const BLOCK_FIELDS: usize = 2; const BLOCK_FIELDS: usize = 2;
@ -62,9 +62,9 @@ impl AbridgedBlock {
.append(&header.extra_data()); .append(&header.extra_data());
// write block values. // write block values.
stream
.append_list(&block_view.transactions()) TypedTransaction::rlp_append_list(&mut stream, &block_view.transactions());
.append_list(&block_view.uncles()); stream.append_list(&block_view.uncles());
// write seal fields. // write seal fields.
for field in seal_fields { for field in seal_fields {
@ -97,10 +97,17 @@ impl AbridgedBlock {
header.set_timestamp(rlp.val_at(6)?); header.set_timestamp(rlp.val_at(6)?);
header.set_extra_data(rlp.val_at(7)?); header.set_extra_data(rlp.val_at(7)?);
let transactions = rlp.list_at(8)?; let transactions = TypedTransaction::decode_rlp_list(&rlp.at(8)?)?;
let uncles: Vec<Header> = rlp.list_at(9)?; let uncles: Vec<Header> = rlp.list_at(9)?;
header.set_transactions_root(ordered_trie_root(rlp.at(8)?.iter().map(|r| r.as_raw()))); header.set_transactions_root(ordered_trie_root(rlp.at(8)?.iter().map(|r| {
if r.is_list() {
r.as_raw()
} else {
// We already checked if list is valid with decode_rlp_list above
r.data().expect("To raw rlp list to be valid")
}
})));
header.set_receipts_root(receipts_root); header.set_receipts_root(receipts_root);
let mut uncles_rlp = RlpStream::new(); let mut uncles_rlp = RlpStream::new();
@ -131,7 +138,7 @@ mod tests {
use ethereum_types::{Address, H256, U256}; use ethereum_types::{Address, H256, U256};
use types::{ use types::{
block::Block, block::Block,
transaction::{Action, Transaction}, transaction::{Action, Transaction, TypedTransaction},
view, view,
views::BlockView, views::BlockView,
}; };
@ -165,24 +172,24 @@ mod tests {
fn with_transactions() { fn with_transactions() {
let mut b = Block::default(); let mut b = Block::default();
let t1 = Transaction { let t1 = TypedTransaction::Legacy(Transaction {
action: Action::Create, action: Action::Create,
nonce: U256::from(42), nonce: U256::from(42),
gas_price: U256::from(3000), gas_price: U256::from(3000),
gas: U256::from(50_000), gas: U256::from(50_000),
value: U256::from(1), value: U256::from(1),
data: b"Hello!".to_vec(), data: b"Hello!".to_vec(),
} })
.fake_sign(Address::from(0x69)); .fake_sign(Address::from(0x69));
let t2 = Transaction { let t2 = TypedTransaction::Legacy(Transaction {
action: Action::Create, action: Action::Create,
nonce: U256::from(88), nonce: U256::from(88),
gas_price: U256::from(12345), gas_price: U256::from(12345),
gas: U256::from(300000), gas: U256::from(300000),
value: U256::from(1000000000), value: U256::from(1000000000),
data: "Eep!".into(), data: "Eep!".into(),
} })
.fake_sign(Address::from(0x55)); .fake_sign(Address::from(0x55));
b.transactions.push(t1.into()); b.transactions.push(t1.into());
@ -191,7 +198,7 @@ mod tests {
let receipts_root = b.header.receipts_root().clone(); let receipts_root = b.header.receipts_root().clone();
b.header b.header
.set_transactions_root(::triehash::ordered_trie_root( .set_transactions_root(::triehash::ordered_trie_root(
b.transactions.iter().map(::rlp::encode), b.transactions.iter().map(|tx| tx.encode()),
)); ));
let encoded = encode_block(&b); let encoded = encode_block(&b);

View File

@ -36,7 +36,9 @@ use ethereum_types::{H256, U256};
use itertools::{Itertools, Position}; use itertools::{Itertools, Position};
use kvdb::KeyValueDB; use kvdb::KeyValueDB;
use rlp::{Rlp, RlpStream}; use rlp::{Rlp, RlpStream};
use types::{encoded, header::Header, ids::BlockId, receipt::Receipt}; use types::{
encoded, header::Header, ids::BlockId, receipt::TypedReceipt, transaction::TypedTransaction,
};
/// Snapshot creation and restoration for PoA chains. /// Snapshot creation and restoration for PoA chains.
/// Chunk format: /// Chunk format:
@ -114,9 +116,9 @@ impl SnapshotComponents for PoaSnapshot {
rlps.push({ rlps.push({
let mut stream = RlpStream::new_list(5); let mut stream = RlpStream::new_list(5);
stream.append(&block.header);
TypedTransaction::rlp_append_list(&mut stream, &block.transactions);
stream stream
.append(&block.header)
.append_list(&block.transactions)
.append_list(&block.uncles) .append_list(&block.uncles)
.append(&receipts) .append(&receipts)
.append(&parent_td); .append(&parent_td);
@ -349,11 +351,11 @@ impl Rebuilder for ChunkRebuilder {
let last_rlp = rlp.at(num_items - 1)?; let last_rlp = rlp.at(num_items - 1)?;
let block = Block { let block = Block {
header: last_rlp.val_at(0)?, header: last_rlp.val_at(0)?,
transactions: last_rlp.list_at(1)?, transactions: TypedTransaction::decode_rlp_list(&last_rlp.at(1)?)?,
uncles: last_rlp.list_at(2)?, uncles: last_rlp.list_at(2)?,
}; };
let block_data = block.rlp_bytes(); let block_data = block.rlp_bytes();
let receipts: Vec<Receipt> = last_rlp.list_at(3)?; let receipts = TypedReceipt::decode_rlp_list(&last_rlp.at(3)?)?;
{ {
let hash = block.header.hash(); let hash = block.header.hash();

View File

@ -291,8 +291,15 @@ impl Rebuilder for PowRebuilder {
let pair = rlp.at(idx)?; let pair = rlp.at(idx)?;
let abridged_rlp = pair.at(0)?.as_raw().to_owned(); let abridged_rlp = pair.at(0)?.as_raw().to_owned();
let abridged_block = AbridgedBlock::from_raw(abridged_rlp); let abridged_block = AbridgedBlock::from_raw(abridged_rlp);
let receipts: Vec<::types::receipt::Receipt> = pair.list_at(1)?; let receipts = ::types::receipt::TypedReceipt::decode_rlp_list(&pair.at(1)?)?;
let receipts_root = ordered_trie_root(pair.at(1)?.iter().map(|r| r.as_raw())); let receipts_root = ordered_trie_root(pair.at(1)?.iter().map(|r| {
if r.is_list() {
r.as_raw()
} else {
// We have allready checked validity by decoding rlp list in line above
r.data().expect("Expect for raw receipts list to be valid.")
}
}));
let block = abridged_block.to_block(parent_hash, cur_number, receipts_root)?; let block = abridged_block.to_block(parent_hash, cur_number, receipts_root)?;
let block_bytes = encoded::Block::new(block.rlp_bytes()); let block_bytes = encoded::Block::new(block.rlp_bytes());

View File

@ -25,7 +25,7 @@ use snapshot::tests::helpers as snapshot_helpers;
use spec::Spec; use spec::Spec;
use tempdir::TempDir; use tempdir::TempDir;
use test_helpers::generate_dummy_client_with_spec; use test_helpers::generate_dummy_client_with_spec;
use types::transaction::{Action, SignedTransaction, Transaction}; use types::transaction::{Action, SignedTransaction, Transaction, TypedTransaction};
use ethereum_types::Address; use ethereum_types::Address;
use test_helpers; use test_helpers;
@ -128,14 +128,14 @@ fn make_chain(
// and force sealing. // and force sealing.
let make_useless_transactions = || { let make_useless_transactions = || {
let mut nonce = nonce.borrow_mut(); let mut nonce = nonce.borrow_mut();
let transaction = Transaction { let transaction = TypedTransaction::Legacy(Transaction {
nonce: *nonce, nonce: *nonce,
gas_price: 1.into(), gas_price: 1.into(),
gas: 21_000.into(), gas: 21_000.into(),
action: Action::Call(Address::new()), action: Action::Call(Address::new()),
value: 1.into(), value: 1.into(),
data: Vec::new(), data: Vec::new(),
} })
.sign(&*RICH_SECRET, client.signing_chain_id()); .sign(&*RICH_SECRET, client.signing_chain_id());
*nonce = *nonce + 1; *nonce = *nonce + 1;
@ -171,14 +171,14 @@ fn make_chain(
let data = let data =
test_validator_set::functions::set_validators::encode_input(new_set.clone()); test_validator_set::functions::set_validators::encode_input(new_set.clone());
let mut nonce = nonce.borrow_mut(); let mut nonce = nonce.borrow_mut();
let transaction = Transaction { let transaction = TypedTransaction::Legacy(Transaction {
nonce: *nonce, nonce: *nonce,
gas_price: 0.into(), gas_price: 0.into(),
gas: 1_000_000.into(), gas: 1_000_000.into(),
action: Action::Call(*address), action: Action::Call(*address),
value: 0.into(), value: 0.into(),
data, data,
} })
.sign(&*RICH_SECRET, client.signing_chain_id()); .sign(&*RICH_SECRET, client.signing_chain_id());
*nonce = *nonce + 1; *nonce = *nonce + 1;

View File

@ -301,7 +301,7 @@ fn recover_aborted_recovery() {
generate_dummy_client_with_spec_and_data(Spec::new_null, NUM_BLOCKS, 5, &gas_prices); generate_dummy_client_with_spec_and_data(Spec::new_null, NUM_BLOCKS, 5, &gas_prices);
let spec = Spec::new_null(); let spec = Spec::new_null();
let tempdir = TempDir::new("").unwrap(); let tempdir = TempDir::new("oe_snapshot").unwrap();
let db_config = DatabaseConfig::with_columns(::db::NUM_COLUMNS); let db_config = DatabaseConfig::with_columns(::db::NUM_COLUMNS);
let client_db = new_db(); let client_db = new_db();
let client2 = Client::new( let client2 = Client::new(

View File

@ -137,6 +137,8 @@ pub struct CommonParams {
pub eip2315_transition: BlockNumber, pub eip2315_transition: BlockNumber,
/// Number of first block where EIP-2929 rules begin. /// Number of first block where EIP-2929 rules begin.
pub eip2929_transition: BlockNumber, pub eip2929_transition: BlockNumber,
/// Number of first block where EIP-2930 rules begin.
pub eip2930_transition: BlockNumber,
/// Number of first block where dust cleanup rules (EIP-168 and EIP169) begin. /// Number of first block where dust cleanup rules (EIP-168 and EIP169) begin.
pub dust_protection_transition: BlockNumber, pub dust_protection_transition: BlockNumber,
/// Nonce cap increase per block. Nonce cap is only checked if dust protection is enabled. /// Nonce cap increase per block. Nonce cap is only checked if dust protection is enabled.
@ -212,6 +214,7 @@ impl CommonParams {
schedule.eip1706 = block_number >= self.eip1706_transition; schedule.eip1706 = block_number >= self.eip1706_transition;
schedule.have_subs = block_number >= self.eip2315_transition; schedule.have_subs = block_number >= self.eip2315_transition;
schedule.eip2929 = block_number >= self.eip2929_transition; schedule.eip2929 = block_number >= self.eip2929_transition;
schedule.eip2930 = block_number >= self.eip2930_transition;
if block_number >= self.eip1884_transition { if block_number >= self.eip1884_transition {
schedule.have_selfbalance = true; schedule.have_selfbalance = true;
@ -370,6 +373,9 @@ impl From<ethjson::spec::Params> for CommonParams {
eip2929_transition: p eip2929_transition: p
.eip2929_transition .eip2929_transition
.map_or_else(BlockNumber::max_value, Into::into), .map_or_else(BlockNumber::max_value, Into::into),
eip2930_transition: p
.eip2930_transition
.map_or_else(BlockNumber::max_value, Into::into),
dust_protection_transition: p dust_protection_transition: p
.dust_protection_transition .dust_protection_transition
.map_or_else(BlockNumber::max_value, Into::into), .map_or_else(BlockNumber::max_value, Into::into),
@ -956,7 +962,7 @@ impl Spec {
/// initialize genesis epoch data, using in-memory database for /// initialize genesis epoch data, using in-memory database for
/// constructor. /// constructor.
pub fn genesis_epoch_data(&self) -> Result<Vec<u8>, String> { pub fn genesis_epoch_data(&self) -> Result<Vec<u8>, String> {
use types::transaction::{Action, Transaction}; use types::transaction::{Action, Transaction, TypedTransaction};
let genesis = self.genesis_header(); let genesis = self.genesis_header();
@ -983,14 +989,14 @@ impl Spec {
}; };
let from = Address::default(); let from = Address::default();
let tx = Transaction { let tx = TypedTransaction::Legacy(Transaction {
nonce: self.engine.account_start_nonce(0), nonce: self.engine.account_start_nonce(0),
action: Action::Call(a), action: Action::Call(a),
gas: U256::max_value(), gas: U256::max_value(),
gas_price: U256::default(), gas_price: U256::default(),
value: U256::default(), value: U256::default(),
data: d, data: d,
} })
.fake_sign(from); .fake_sign(from);
let res = ::state::prove_transaction_virtual( let res = ::state::prove_transaction_virtual(

View File

@ -38,10 +38,11 @@ use state_db::StateDB;
use trace::{self, FlatTrace, VMTrace}; use trace::{self, FlatTrace, VMTrace};
use types::{ use types::{
basic_account::BasicAccount, basic_account::BasicAccount,
receipt::{Receipt, TransactionOutcome}, receipt::{LegacyReceipt, TransactionOutcome, TypedReceipt},
state_diff::StateDiff, state_diff::StateDiff,
transaction::SignedTransaction, transaction::SignedTransaction,
}; };
use vm::EnvInfo; use vm::EnvInfo;
use bytes::Bytes; use bytes::Bytes;
@ -63,7 +64,7 @@ pub use self::{account::Account, backend::Backend, substate::Substate};
/// Used to return information about an `State::apply` operation. /// Used to return information about an `State::apply` operation.
pub struct ApplyOutcome<T, V> { pub struct ApplyOutcome<T, V> {
/// The receipt for the applied transaction. /// The receipt for the applied transaction.
pub receipt: Receipt, pub receipt: TypedReceipt,
/// The output of the applied transaction. /// The output of the applied transaction.
pub output: Bytes, pub output: Bytes,
/// The trace for the applied transaction, empty if tracing was not produced. /// The trace for the applied transaction, empty if tracing was not produced.
@ -955,7 +956,10 @@ impl<B: Backend> State<B> {
}; };
let output = e.output; let output = e.output;
let receipt = Receipt::new(outcome, e.cumulative_gas_used, e.logs); let receipt = TypedReceipt::new(
t.tx_type(),
LegacyReceipt::new(outcome, e.cumulative_gas_used, e.logs),
);
trace!(target: "state", "Transaction receipt: {:?}", receipt); trace!(target: "state", "Transaction receipt: {:?}", receipt);
Ok(ApplyOutcome { Ok(ApplyOutcome {
@ -1602,7 +1606,7 @@ mod tests {
info.gas_limit = 1_000_000.into(); info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5); let machine = make_frontier_machine(5);
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 100_000.into(), gas: 100_000.into(),
@ -1610,7 +1614,7 @@ mod tests {
value: 100.into(), value: 100.into(),
data: FromHex::from_hex("601080600c6000396000f3006000355415600957005b60203560003555") data: FromHex::from_hex("601080600c6000396000f3006000355415600957005b60203560003555")
.unwrap(), .unwrap(),
} })
.sign(&secret(), None); .sign(&secret(), None);
state state
@ -1667,14 +1671,14 @@ mod tests {
info.gas_limit = 1_000_000.into(); info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5); let machine = make_frontier_machine(5);
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 100_000.into(), gas: 100_000.into(),
action: Action::Create, action: Action::Create,
value: 100.into(), value: 100.into(),
data: FromHex::from_hex("5b600056").unwrap(), data: FromHex::from_hex("5b600056").unwrap(),
} })
.sign(&secret(), None); .sign(&secret(), None);
state state
@ -1706,14 +1710,14 @@ mod tests {
info.gas_limit = 1_000_000.into(); info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5); let machine = make_frontier_machine(5);
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 100_000.into(), gas: 100_000.into(),
action: Action::Call(0xa.into()), action: Action::Call(0xa.into()),
value: 100.into(), value: 100.into(),
data: vec![], data: vec![],
} })
.sign(&secret(), None); .sign(&secret(), None);
state state
@ -1753,14 +1757,14 @@ mod tests {
info.gas_limit = 1_000_000.into(); info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5); let machine = make_frontier_machine(5);
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 100_000.into(), gas: 100_000.into(),
action: Action::Call(0xa.into()), action: Action::Call(0xa.into()),
value: 100.into(), value: 100.into(),
data: vec![], data: vec![],
} })
.sign(&secret(), None); .sign(&secret(), None);
state state
@ -1797,14 +1801,14 @@ mod tests {
info.gas_limit = 1_000_000.into(); info.gas_limit = 1_000_000.into();
let machine = Spec::new_test_machine(); let machine = Spec::new_test_machine();
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 100_000.into(), gas: 100_000.into(),
action: Action::Call(0x1.into()), action: Action::Call(0x1.into()),
value: 0.into(), value: 0.into(),
data: vec![], data: vec![],
} })
.sign(&secret(), None); .sign(&secret(), None);
let result = state.apply(&info, &machine, &t, true).unwrap(); let result = state.apply(&info, &machine, &t, true).unwrap();
@ -1839,14 +1843,14 @@ mod tests {
info.gas_limit = 1_000_000.into(); info.gas_limit = 1_000_000.into();
let machine = Spec::new_test_machine(); let machine = Spec::new_test_machine();
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 100_000.into(), gas: 100_000.into(),
action: Action::Call(0xa.into()), action: Action::Call(0xa.into()),
value: 0.into(), value: 0.into(),
data: vec![], data: vec![],
} })
.sign(&secret(), None); .sign(&secret(), None);
state state
@ -1887,14 +1891,14 @@ mod tests {
info.gas_limit = 1_000_000.into(); info.gas_limit = 1_000_000.into();
let machine = Spec::new_test_machine(); let machine = Spec::new_test_machine();
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 100_000.into(), gas: 100_000.into(),
action: Action::Call(0xa.into()), action: Action::Call(0xa.into()),
value: 0.into(), value: 0.into(),
data: vec![], data: vec![],
} })
.sign(&secret(), None); .sign(&secret(), None);
state state
@ -1957,14 +1961,14 @@ mod tests {
info.number = 0x789b0; info.number = 0x789b0;
let machine = Spec::new_test_machine(); let machine = Spec::new_test_machine();
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 100_000.into(), gas: 100_000.into(),
action: Action::Call(0xa.into()), action: Action::Call(0xa.into()),
value: 0.into(), value: 0.into(),
data: vec![], data: vec![],
} })
.sign(&secret(), None); .sign(&secret(), None);
state state
@ -2029,14 +2033,14 @@ mod tests {
info.gas_limit = 1_000_000.into(); info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5); let machine = make_frontier_machine(5);
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 100_000.into(), gas: 100_000.into(),
action: Action::Call(0xa.into()), action: Action::Call(0xa.into()),
value: 100.into(), value: 100.into(),
data: vec![], data: vec![],
} })
.sign(&secret(), None); .sign(&secret(), None);
state state
@ -2073,14 +2077,14 @@ mod tests {
info.gas_limit = 1_000_000.into(); info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5); let machine = make_frontier_machine(5);
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 100_000.into(), gas: 100_000.into(),
action: Action::Call(0xa.into()), action: Action::Call(0xa.into()),
value: 100.into(), value: 100.into(),
data: vec![], data: vec![],
} })
.sign(&secret(), None); .sign(&secret(), None);
state state
@ -2145,14 +2149,14 @@ mod tests {
info.gas_limit = 1_000_000.into(); info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5); let machine = make_frontier_machine(5);
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 100_000.into(), gas: 100_000.into(),
action: Action::Call(0xa.into()), action: Action::Call(0xa.into()),
value: 100.into(), value: 100.into(),
data: vec![], data: vec![],
} })
.sign(&secret(), None); .sign(&secret(), None);
state state
@ -2210,14 +2214,14 @@ mod tests {
info.gas_limit = 1_000_000.into(); info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5); let machine = make_frontier_machine(5);
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 100_000.into(), gas: 100_000.into(),
action: Action::Call(0xa.into()), action: Action::Call(0xa.into()),
value: 100.into(), value: 100.into(),
data: vec![], data: vec![],
} })
.sign(&secret(), None); .sign(&secret(), None);
state state
@ -2260,14 +2264,14 @@ mod tests {
info.gas_limit = 1_000_000.into(); info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5); let machine = make_frontier_machine(5);
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 100_000.into(), gas: 100_000.into(),
action: Action::Call(0xa.into()), action: Action::Call(0xa.into()),
value: 100.into(), value: 100.into(),
data: vec![], //600480600b6000396000f35b600056 data: vec![], //600480600b6000396000f35b600056
} })
.sign(&secret(), None); .sign(&secret(), None);
state state
@ -2328,14 +2332,14 @@ mod tests {
info.gas_limit = 1_000_000.into(); info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5); let machine = make_frontier_machine(5);
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 100_000.into(), gas: 100_000.into(),
action: Action::Call(0xa.into()), action: Action::Call(0xa.into()),
value: 100.into(), value: 100.into(),
data: vec![], data: vec![],
} })
.sign(&secret(), None); .sign(&secret(), None);
state state
@ -2421,14 +2425,14 @@ mod tests {
info.gas_limit = 1_000_000.into(); info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5); let machine = make_frontier_machine(5);
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 100_000.into(), gas: 100_000.into(),
action: Action::Call(0xa.into()), action: Action::Call(0xa.into()),
value: 100.into(), value: 100.into(),
data: vec![], //600480600b6000396000f35b600056 data: vec![], //600480600b6000396000f35b600056
} })
.sign(&secret(), None); .sign(&secret(), None);
state state
@ -2512,14 +2516,14 @@ mod tests {
info.gas_limit = 1_000_000.into(); info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5); let machine = make_frontier_machine(5);
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 100_000.into(), gas: 100_000.into(),
action: Action::Call(0xa.into()), action: Action::Call(0xa.into()),
value: 100.into(), value: 100.into(),
data: vec![], data: vec![],
} })
.sign(&secret(), None); .sign(&secret(), None);
state state

View File

@ -36,7 +36,7 @@ use tempdir::TempDir;
use types::{ use types::{
encoded, encoded,
header::Header, header::Header,
transaction::{Action, SignedTransaction, Transaction}, transaction::{Action, SignedTransaction, Transaction, TypedTransaction},
view, view,
views::BlockView, views::BlockView,
}; };
@ -104,7 +104,7 @@ pub fn create_test_block_with_data(
rlp.append(header); rlp.append(header);
rlp.begin_list(transactions.len()); rlp.begin_list(transactions.len());
for t in transactions { for t in transactions {
rlp.append_raw(&rlp::encode(t), 1); t.rlp_append(&mut rlp);
} }
rlp.append_list(&uncles); rlp.append_list(&uncles);
rlp.out() rlp.out()
@ -197,14 +197,14 @@ where
// first block we don't have any balance, so can't send any transactions. // first block we don't have any balance, so can't send any transactions.
for _ in 0..txs_per_block { for _ in 0..txs_per_block {
b.push_transaction( b.push_transaction(
Transaction { TypedTransaction::Legacy(Transaction {
nonce: n.into(), nonce: n.into(),
gas_price: tx_gas_prices[n % tx_gas_prices.len()], gas_price: tx_gas_prices[n % tx_gas_prices.len()],
gas: 100000.into(), gas: 100000.into(),
action: Action::Create, action: Action::Create,
data: vec![], data: vec![],
value: U256::zero(), value: U256::zero(),
} })
.sign(kp.secret(), Some(test_spec.chain_id())), .sign(kp.secret(), Some(test_spec.chain_id())),
None, None,
) )

View File

@ -45,7 +45,7 @@ use types::{
data_format::DataFormat, data_format::DataFormat,
filter::Filter, filter::Filter,
ids::BlockId, ids::BlockId,
transaction::{Action, Condition, PendingTransaction, Transaction}, transaction::{Action, Condition, PendingTransaction, Transaction, TypedTransaction},
view, view,
views::BlockView, views::BlockView,
}; };
@ -362,26 +362,26 @@ fn does_not_propagate_delayed_transactions() {
let key = KeyPair::from_secret(keccak("test").into()).unwrap(); let key = KeyPair::from_secret(keccak("test").into()).unwrap();
let secret = key.secret(); let secret = key.secret();
let tx0 = PendingTransaction::new( let tx0 = PendingTransaction::new(
Transaction { TypedTransaction::Legacy(Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 21000.into(), gas: 21000.into(),
action: Action::Call(Address::default()), action: Action::Call(Address::default()),
value: 0.into(), value: 0.into(),
data: Vec::new(), data: Vec::new(),
} })
.sign(secret, None), .sign(secret, None),
Some(Condition::Number(2)), Some(Condition::Number(2)),
); );
let tx1 = PendingTransaction::new( let tx1 = PendingTransaction::new(
Transaction { TypedTransaction::Legacy(Transaction {
nonce: 1.into(), nonce: 1.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 21000.into(), gas: 21000.into(),
action: Action::Call(Address::default()), action: Action::Call(Address::default()),
value: 0.into(), value: 0.into(),
data: Vec::new(), data: Vec::new(),
} })
.sign(secret, None), .sign(secret, None),
None, None,
); );
@ -443,14 +443,14 @@ fn transaction_proof() {
client.import_sealed_block(b).unwrap(); // account change is in the journal overlay client.import_sealed_block(b).unwrap(); // account change is in the journal overlay
} }
let transaction = Transaction { let transaction = TypedTransaction::Legacy(Transaction {
nonce: 0.into(), nonce: 0.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 21000.into(), gas: 21000.into(),
action: Action::Call(Address::default()), action: Action::Call(Address::default()),
value: 5.into(), value: 5.into(),
data: Vec::new(), data: Vec::new(),
} })
.fake_sign(address); .fake_sign(address);
let proof = client let proof = client

View File

@ -29,7 +29,7 @@ use test_helpers::{self, get_temp_state_db};
use trace::{trace::Action::Reward, LocalizedTrace, RewardType}; use trace::{trace::Action::Reward, LocalizedTrace, RewardType};
use types::{ use types::{
header::Header, header::Header,
transaction::{Action, Transaction}, transaction::{Action, Transaction, TypedTransaction},
view, view,
views::BlockView, views::BlockView,
}; };
@ -171,14 +171,14 @@ fn can_trace_block_and_uncle_reward() {
for _ in 0..1 { for _ in 0..1 {
block block
.push_transaction( .push_transaction(
Transaction { TypedTransaction::Legacy(Transaction {
nonce: n.into(), nonce: n.into(),
gas_price: 10000.into(), gas_price: 10000.into(),
gas: 100000.into(), gas: 100000.into(),
action: Action::Create, action: Action::Create,
data: vec![], data: vec![],
value: U256::zero(), value: U256::zero(),
} })
.sign(kp.secret(), Some(spec.network_id())), .sign(kp.secret(), Some(spec.network_id())),
None, None,
) )

View File

@ -83,7 +83,7 @@ impl TransactionFilter {
let mut permission_cache = self.permission_cache.lock(); let mut permission_cache = self.permission_cache.lock();
let mut contract_version_cache = self.contract_version_cache.lock(); let mut contract_version_cache = self.contract_version_cache.lock();
let (tx_type, to) = match transaction.action { let (tx_type, to) = match transaction.tx().action {
Action::Create => (tx_permissions::CREATE, Address::new()), Action::Create => (tx_permissions::CREATE, Address::new()),
Action::Call(address) => { Action::Call(address) => {
if client if client
@ -98,7 +98,7 @@ impl TransactionFilter {
}; };
let sender = transaction.sender(); let sender = transaction.sender();
let value = transaction.value; let value = transaction.tx().value;
let key = (*parent_hash, sender); let key = (*parent_hash, sender);
if let Some(permissions) = permission_cache.get_mut(&key) { if let Some(permissions) = permission_cache.get_mut(&key) {
@ -181,7 +181,7 @@ mod test {
use std::sync::Arc; use std::sync::Arc;
use tempdir::TempDir; use tempdir::TempDir;
use test_helpers; use test_helpers;
use types::transaction::{Action, Transaction}; use types::transaction::{Action, Transaction, TypedTransaction};
/// Contract code: https://gist.github.com/VladLupashevskyi/84f18eabb1e4afadf572cf92af3e7e7f /// Contract code: https://gist.github.com/VladLupashevskyi/84f18eabb1e4afadf572cf92af3e7e7f
#[test] #[test]
@ -230,28 +230,30 @@ mod test {
.unwrap(); .unwrap();
let filter = TransactionFilter::from_params(spec.params()).unwrap(); let filter = TransactionFilter::from_params(spec.params()).unwrap();
let mut basic_tx = Transaction::default(); let mut basic_tx = TypedTransaction::Legacy(Transaction::default());
basic_tx.action = Action::Call(Address::from("d41c057fd1c78805aac12b0a94a405c0461a6fbb")); basic_tx.tx_mut().action =
let create_tx = Transaction::default();
let mut call_tx = Transaction::default();
call_tx.action = Action::Call(Address::from("0000000000000000000000000000000000000005"));
let mut basic_tx_with_ether_and_to_key7 = Transaction::default();
basic_tx_with_ether_and_to_key7.action =
Action::Call(Address::from("d41c057fd1c78805aac12b0a94a405c0461a6fbb")); Action::Call(Address::from("d41c057fd1c78805aac12b0a94a405c0461a6fbb"));
basic_tx_with_ether_and_to_key7.value = U256::from(123123); let create_tx = TypedTransaction::Legacy(Transaction::default());
let mut call_tx_with_ether = Transaction::default(); let mut call_tx = TypedTransaction::Legacy(Transaction::default());
call_tx_with_ether.action = call_tx.tx_mut().action =
Action::Call(Address::from("0000000000000000000000000000000000000005")); Action::Call(Address::from("0000000000000000000000000000000000000005"));
call_tx_with_ether.value = U256::from(123123);
let mut basic_tx_to_key6 = Transaction::default(); let mut basic_tx_with_ether_and_to_key7 = TypedTransaction::Legacy(Transaction::default());
basic_tx_to_key6.action = basic_tx_with_ether_and_to_key7.tx_mut().action =
Action::Call(Address::from("d41c057fd1c78805aac12b0a94a405c0461a6fbb"));
basic_tx_with_ether_and_to_key7.tx_mut().value = U256::from(123123);
let mut call_tx_with_ether = TypedTransaction::Legacy(Transaction::default());
call_tx_with_ether.tx_mut().action =
Action::Call(Address::from("0000000000000000000000000000000000000005"));
call_tx_with_ether.tx_mut().value = U256::from(123123);
let mut basic_tx_to_key6 = TypedTransaction::Legacy(Transaction::default());
basic_tx_to_key6.tx_mut().action =
Action::Call(Address::from("e57bfe9f44b819898f47bf37e5af72a0783e1141")); Action::Call(Address::from("e57bfe9f44b819898f47bf37e5af72a0783e1141"));
let mut basic_tx_with_ether_and_to_key6 = Transaction::default(); let mut basic_tx_with_ether_and_to_key6 = TypedTransaction::Legacy(Transaction::default());
basic_tx_with_ether_and_to_key6.action = basic_tx_with_ether_and_to_key6.tx_mut().action =
Action::Call(Address::from("e57bfe9f44b819898f47bf37e5af72a0783e1141")); Action::Call(Address::from("e57bfe9f44b819898f47bf37e5af72a0783e1141"));
basic_tx_with_ether_and_to_key6.value = U256::from(123123); basic_tx_with_ether_and_to_key6.tx_mut().value = U256::from(123123);
let genesis = client.block_hash(BlockId::Latest).unwrap(); let genesis = client.block_hash(BlockId::Latest).unwrap();
let block_number = 1; let block_number = 1;
@ -444,11 +446,13 @@ mod test {
.unwrap(); .unwrap();
let filter = TransactionFilter::from_params(spec.params()).unwrap(); let filter = TransactionFilter::from_params(spec.params()).unwrap();
let mut basic_tx = Transaction::default(); let mut basic_tx = TypedTransaction::Legacy(Transaction::default());
basic_tx.action = Action::Call(Address::from("000000000000000000000000000000000000032")); basic_tx.tx_mut().action =
let create_tx = Transaction::default(); Action::Call(Address::from("000000000000000000000000000000000000032"));
let mut call_tx = Transaction::default(); let create_tx = TypedTransaction::Legacy(Transaction::default());
call_tx.action = Action::Call(Address::from("0000000000000000000000000000000000000005")); let mut call_tx = TypedTransaction::Legacy(Transaction::default());
call_tx.tx_mut().action =
Action::Call(Address::from("0000000000000000000000000000000000000005"));
let genesis = client.block_hash(BlockId::Latest).unwrap(); let genesis = client.block_hash(BlockId::Latest).unwrap();
let block_number = 1; let block_number = 1;

View File

@ -80,7 +80,10 @@ pub mod blocks {
use engines::EthEngine; use engines::EthEngine;
use error::{BlockError, Error, ErrorKind}; use error::{BlockError, Error, ErrorKind};
use types::{header::Header, transaction::UnverifiedTransaction}; use types::{
header::Header,
transaction::{TypedTransaction, UnverifiedTransaction},
};
use verification::{verify_block_basic, verify_block_unordered, PreverifiedBlock}; use verification::{verify_block_basic, verify_block_unordered, PreverifiedBlock};
use bytes::Bytes; use bytes::Bytes;
@ -151,7 +154,7 @@ pub mod blocks {
let (header, transactions, uncles) = { let (header, transactions, uncles) = {
let rlp = Rlp::new(&bytes); let rlp = Rlp::new(&bytes);
let header = rlp.val_at(0)?; let header = rlp.val_at(0)?;
let transactions = rlp.list_at(1)?; let transactions = TypedTransaction::decode_rlp_list(&rlp.at(1)?)?;
let uncles = rlp.list_at(2)?; let uncles = rlp.list_at(2)?;
(header, transactions, uncles) (header, transactions, uncles)
}; };

View File

@ -133,7 +133,7 @@ pub fn verify_block_unordered(
let t = engine.verify_transaction_unordered(t, &header)?; let t = engine.verify_transaction_unordered(t, &header)?;
// t_nb 5.3.2 check if nonce is more then max nonce (EIP-168 and EIP169) // t_nb 5.3.2 check if nonce is more then max nonce (EIP-168 and EIP169)
if let Some(max_nonce) = nonce_cap { if let Some(max_nonce) = nonce_cap {
if t.nonce >= max_nonce { if t.tx().nonce >= max_nonce {
return Err(BlockError::TooManyTransactions(t.sender()).into()); return Err(BlockError::TooManyTransactions(t.sender()).into());
} }
} }
@ -493,7 +493,16 @@ fn verify_parent(header: &Header, parent: &Header, engine: &dyn EthEngine) -> Re
fn verify_block_integrity(block: &Unverified) -> Result<(), Error> { fn verify_block_integrity(block: &Unverified) -> Result<(), Error> {
let block_rlp = Rlp::new(&block.bytes); let block_rlp = Rlp::new(&block.bytes);
let tx = block_rlp.at(1)?; let tx = block_rlp.at(1)?;
let expected_root = ordered_trie_root(tx.iter().map(|r| r.as_raw())); let expected_root = ordered_trie_root(tx.iter().map(|r| {
if r.is_list() {
r.as_raw()
} else {
// This is already checked in Unverified structure and that is why we are okay to asume that data is valid.
r.data().expect(
"Unverified block should already check if raw list of transactions is valid",
)
}
}));
if &expected_root != block.header.transactions_root() { if &expected_root != block.header.transactions_root() {
bail!(BlockError::InvalidTransactionsRoot(Mismatch { bail!(BlockError::InvalidTransactionsRoot(Mismatch {
expected: expected_root, expected: expected_root,
@ -531,7 +540,7 @@ mod tests {
use types::{ use types::{
encoded, encoded,
log_entry::{LocalizedLogEntry, LogEntry}, log_entry::{LocalizedLogEntry, LogEntry},
transaction::{Action, SignedTransaction, Transaction, UnverifiedTransaction}, transaction::{Action, SignedTransaction, Transaction, TypedTransaction},
}; };
fn check_ok(result: Result<(), Error>) { fn check_ok(result: Result<(), Error>) {
@ -764,34 +773,34 @@ mod tests {
let keypair = Random.generate().unwrap(); let keypair = Random.generate().unwrap();
let tr1 = Transaction { let tr1 = TypedTransaction::Legacy(Transaction {
action: Action::Create, action: Action::Create,
value: U256::from(0), value: U256::from(0),
data: Bytes::new(), data: Bytes::new(),
gas: U256::from(30_000), gas: U256::from(30_000),
gas_price: U256::from(40_000), gas_price: U256::from(40_000),
nonce: U256::one(), nonce: U256::one(),
} })
.sign(keypair.secret(), None); .sign(keypair.secret(), None);
let tr2 = Transaction { let tr2 = TypedTransaction::Legacy(Transaction {
action: Action::Create, action: Action::Create,
value: U256::from(0), value: U256::from(0),
data: Bytes::new(), data: Bytes::new(),
gas: U256::from(30_000), gas: U256::from(30_000),
gas_price: U256::from(40_000), gas_price: U256::from(40_000),
nonce: U256::from(2), nonce: U256::from(2),
} })
.sign(keypair.secret(), None); .sign(keypair.secret(), None);
let tr3 = Transaction { let tr3 = TypedTransaction::Legacy(Transaction {
action: Action::Call(0x0.into()), action: Action::Call(0x0.into()),
value: U256::from(0), value: U256::from(0),
data: Bytes::new(), data: Bytes::new(),
gas: U256::from(30_000), gas: U256::from(30_000),
gas_price: U256::from(0), gas_price: U256::from(0),
nonce: U256::zero(), nonce: U256::zero(),
} })
.null_sign(0); .null_sign(0);
let good_transactions = [tr1.clone(), tr2.clone()]; let good_transactions = [tr1.clone(), tr2.clone()];
@ -834,16 +843,10 @@ mod tests {
let mut uncles_rlp = RlpStream::new(); let mut uncles_rlp = RlpStream::new();
uncles_rlp.append_list(&good_uncles); uncles_rlp.append_list(&good_uncles);
let good_uncles_hash = keccak(uncles_rlp.as_raw()); let good_uncles_hash = keccak(uncles_rlp.as_raw());
let good_transactions_root = ordered_trie_root( let good_transactions_root =
good_transactions ordered_trie_root(good_transactions.iter().map(|t| t.encode()));
.iter() let eip86_transactions_root =
.map(|t| ::rlp::encode::<UnverifiedTransaction>(t)), ordered_trie_root(eip86_transactions.iter().map(|t| t.encode()));
);
let eip86_transactions_root = ordered_trie_root(
eip86_transactions
.iter()
.map(|t| ::rlp::encode::<UnverifiedTransaction>(t)),
);
let mut parent = good.clone(); let mut parent = good.clone();
parent.set_number(9); parent.set_number(9);
@ -1114,14 +1117,14 @@ mod tests {
let keypair = Random.generate().unwrap(); let keypair = Random.generate().unwrap();
let bad_transactions: Vec<_> = (0..3) let bad_transactions: Vec<_> = (0..3)
.map(|i| { .map(|i| {
Transaction { TypedTransaction::Legacy(Transaction {
action: Action::Create, action: Action::Create,
value: U256::zero(), value: U256::zero(),
data: Vec::new(), data: Vec::new(),
gas: 0.into(), gas: 0.into(),
gas_price: U256::zero(), gas_price: U256::zero(),
nonce: i.into(), nonce: i.into(),
} })
.sign(keypair.secret(), None) .sign(keypair.secret(), None)
}) })
.collect(); .collect();

View File

@ -762,7 +762,7 @@ mod tests {
use triehash_ethereum::ordered_trie_root; use triehash_ethereum::ordered_trie_root;
use types::{ use types::{
header::Header as BlockHeader, header::Header as BlockHeader,
transaction::{SignedTransaction, Transaction}, transaction::{SignedTransaction, Transaction, TypedTransaction},
}; };
fn dummy_header(number: u64, parent_hash: H256) -> BlockHeader { fn dummy_header(number: u64, parent_hash: H256) -> BlockHeader {
@ -778,7 +778,7 @@ mod tests {
fn dummy_signed_tx() -> SignedTransaction { fn dummy_signed_tx() -> SignedTransaction {
let keypair = Random.generate().unwrap(); let keypair = Random.generate().unwrap();
Transaction::default().sign(keypair.secret(), None) TypedTransaction::Legacy(Transaction::default()).sign(keypair.secret(), None)
} }
fn import_headers( fn import_headers(
@ -949,8 +949,16 @@ mod tests {
::rlp::EMPTY_LIST_RLP.to_vec() ::rlp::EMPTY_LIST_RLP.to_vec()
}; };
let txs = encode_list(&[dummy_signed_tx()]); let mut rlp_strem = RlpStream::new();
let tx_root = ordered_trie_root(Rlp::new(&txs).iter().map(|r| r.as_raw())); SignedTransaction::rlp_append_list(&mut rlp_strem, &[dummy_signed_tx()]);
let txs = rlp_strem.drain();
let tx_root = ordered_trie_root(Rlp::new(&txs).iter().map(|r| {
if r.is_list() {
r.as_raw()
} else {
r.data().expect("It is expected that raw rlp list is valid")
}
}));
let mut rlp = RlpStream::new_list(2); let mut rlp = RlpStream::new_list(2);
rlp.append_raw(&txs, 1); rlp.append_raw(&txs, 1);
@ -1019,14 +1027,20 @@ mod tests {
// Construct the receipts. Receipt root for the first two blocks is the same. // Construct the receipts. Receipt root for the first two blocks is the same.
// //
// The RLP-encoded integers are clearly not receipts, but the BlockDownloader treats // The RLP-encoded integers are clearly not receipts, but the BlockDownloader treats
// all receipts as byte blobs, so it does not matter. // all receipts as byte blobs, so it does not matter. It is just important that they are
// represended as list (0xc1) so that they passes as legacy list
let receipts_rlp = if i < 2 { let receipts_rlp = if i < 2 {
encode_list(&[0u32]) vec![0xC1, 0]
} else { } else {
encode_list(&[i as u32]) vec![0xC1, i as u8]
}; };
let receipts_root = let receipts_root = ordered_trie_root(Rlp::new(&receipts_rlp).iter().map(|r| {
ordered_trie_root(Rlp::new(&receipts_rlp).iter().map(|r| r.as_raw())); if r.is_list() {
r.as_raw()
} else {
r.data().expect("expect proper test data")
}
}));
receipts.push(receipts_rlp); receipts.push(receipts_rlp);
// Construct the block header. // Construct the block header.

View File

@ -23,7 +23,10 @@ use network;
use rlp::{DecoderError, Rlp, RlpStream}; use rlp::{DecoderError, Rlp, RlpStream};
use std::collections::{hash_map, BTreeMap, HashMap, HashSet}; use std::collections::{hash_map, BTreeMap, HashMap, HashSet};
use triehash_ethereum::ordered_trie_root; use triehash_ethereum::ordered_trie_root;
use types::{header::Header as BlockHeader, transaction::UnverifiedTransaction}; use types::{
header::Header as BlockHeader,
transaction::{TypedTransaction, UnverifiedTransaction},
};
known_heap_size!(0, HeaderId); known_heap_size!(0, HeaderId);
@ -65,7 +68,7 @@ impl SyncBody {
let result = SyncBody { let result = SyncBody {
transactions_bytes: transactions_rlp.as_raw().to_vec(), transactions_bytes: transactions_rlp.as_raw().to_vec(),
transactions: transactions_rlp.as_list()?, transactions: TypedTransaction::decode_rlp_list(&transactions_rlp)?,
uncles_bytes: uncles_rlp.as_raw().to_vec(), uncles_bytes: uncles_rlp.as_raw().to_vec(),
uncles: uncles_rlp.as_list()?, uncles: uncles_rlp.as_list()?,
}; };
@ -454,11 +457,14 @@ impl BlockCollection {
fn insert_body(&mut self, body: SyncBody) -> Result<H256, network::Error> { fn insert_body(&mut self, body: SyncBody) -> Result<H256, network::Error> {
let header_id = { let header_id = {
let tx_root = ordered_trie_root( let tx_root = ordered_trie_root(Rlp::new(&body.transactions_bytes).iter().map(|r| {
Rlp::new(&body.transactions_bytes) if r.is_list() {
.iter() r.as_raw()
.map(|r| r.as_raw()), } else {
); // this list is already decoded and passed validation, for this we are okay to expect proper data
r.data().expect("Expect raw transaction list to be valid")
}
}));
let uncles = keccak(&body.uncles_bytes); let uncles = keccak(&body.uncles_bytes);
HeaderId { HeaderId {
transactions_root: tx_root, transactions_root: tx_root,
@ -491,7 +497,22 @@ impl BlockCollection {
fn insert_receipt(&mut self, r: Bytes) -> Result<Vec<H256>, network::Error> { fn insert_receipt(&mut self, r: Bytes) -> Result<Vec<H256>, network::Error> {
let receipt_root = { let receipt_root = {
let receipts = Rlp::new(&r); let receipts = Rlp::new(&r);
ordered_trie_root(receipts.iter().map(|r| r.as_raw())) //check receipts data before calculating trie root
let mut temp_receipts: Vec<&[u8]> = Vec::new();
for receipt_byte in receipts.iter() {
if receipt_byte.is_list() {
temp_receipts.push(receipt_byte.as_raw())
} else {
temp_receipts.push(
receipt_byte
.data()
.map_err(|e| network::ErrorKind::Rlp(e))?,
);
}
}
// calculate trie root and use it as hash
ordered_trie_root(temp_receipts.iter())
}; };
self.downloading_receipts.remove(&receipt_root); self.downloading_receipts.remove(&receipt_root);
match self.receipt_ids.entry(receipt_root) { match self.receipt_ids.entry(receipt_root) {

View File

@ -837,9 +837,12 @@ impl SyncHandler {
let item_count = r.item_count()?; let item_count = r.item_count()?;
trace!(target: "sync", "{:02} -> Transactions ({} entries)", peer_id, item_count); trace!(target: "sync", "{:02} -> Transactions ({} entries)", peer_id, item_count);
let mut transactions = Vec::with_capacity(item_count); let mut transactions = Vec::with_capacity(item_count);
for i in 0..item_count { for i in r.iter() {
let rlp = r.at(i)?; let tx = if i.is_list() {
let tx = rlp.as_raw().to_vec(); i.as_raw().to_vec() // legacy transaction. just add it raw
} else {
i.data()?.to_vec() // typed transaction. remove header from start and send only payload.
};
transactions.push(tx); transactions.push(tx);
} }
io.chain().queue_transactions(transactions, peer_id); io.chain().queue_transactions(transactions, peer_id);

View File

@ -21,7 +21,7 @@ use ethereum_types::H256;
use fastmap::H256FastSet; use fastmap::H256FastSet;
use network::{client_version::ClientCapabilities, PeerId}; use network::{client_version::ClientCapabilities, PeerId};
use rand::Rng; use rand::Rng;
use rlp::{Encodable, RlpStream}; use rlp::RlpStream;
use sync_io::SyncIo; use sync_io::SyncIo;
use types::{blockchain_info::BlockChainInfo, transaction::SignedTransaction, BlockNumber}; use types::{blockchain_info::BlockChainInfo, transaction::SignedTransaction, BlockNumber};
@ -121,7 +121,7 @@ impl SyncPropagator {
let (transactions, service_transactions): (Vec<_>, Vec<_>) = transactions let (transactions, service_transactions): (Vec<_>, Vec<_>) = transactions
.iter() .iter()
.map(|tx| tx.signed()) .map(|tx| tx.signed())
.partition(|tx| !tx.gas_price.is_zero()); .partition(|tx| !tx.tx().gas_price.is_zero());
// usual transactions could be propagated to all peers // usual transactions could be propagated to all peers
let mut affected_peers = HashSet::new(); let mut affected_peers = HashSet::new();
@ -171,7 +171,7 @@ impl SyncPropagator {
let all_transactions_rlp = { let all_transactions_rlp = {
let mut packet = RlpStream::new_list(transactions.len()); let mut packet = RlpStream::new_list(transactions.len());
for tx in &transactions { for tx in &transactions {
packet.append(&**tx); tx.rlp_append(&mut packet);
} }
packet.out() packet.out()
}; };
@ -238,13 +238,8 @@ impl SyncPropagator {
for tx in &transactions { for tx in &transactions {
let hash = tx.hash(); let hash = tx.hash();
if to_send.contains(&hash) { if to_send.contains(&hash) {
let mut transaction = RlpStream::new(); let appended =
tx.rlp_append(&mut transaction); packet.append_raw_checked(&tx.encode(), 1, MAX_TRANSACTION_PACKET_SIZE);
let appended = packet.append_raw_checked(
&transaction.drain(),
1,
MAX_TRANSACTION_PACKET_SIZE,
);
if !appended { if !appended {
// Maximal packet size reached just proceed with sending // Maximal packet size reached just proceed with sending
debug!(target: "sync", "Transaction packet size limit reached. Sending incomplete set of {}/{} transactions.", pushed, to_send.len()); debug!(target: "sync", "Transaction packet size limit reached. Sending incomplete set of {}/{} transactions.", pushed, to_send.len());
@ -373,6 +368,7 @@ mod tests {
use rlp::Rlp; use rlp::Rlp;
use std::collections::VecDeque; use std::collections::VecDeque;
use tests::{helpers::TestIo, snapshot::TestSnapshotService}; use tests::{helpers::TestIo, snapshot::TestSnapshotService};
use types::transaction::TypedTransaction;
use super::{ use super::{
super::{tests::*, *}, super::{tests::*, *},
@ -707,7 +703,9 @@ mod tests {
return None; return None;
} }
rlp.at(0).ok().and_then(|r| r.as_val().ok()) rlp.at(0)
.ok()
.and_then(|r| TypedTransaction::decode_rlp(&r).ok())
}) })
.collect(); .collect();
assert_eq!(sent_transactions.len(), 2); assert_eq!(sent_transactions.len(), 2);

View File

@ -26,18 +26,18 @@ use ethkey::{KeyPair, Secret};
use hash::keccak; use hash::keccak;
use io::{IoChannel, IoHandler}; use io::{IoChannel, IoHandler};
use std::sync::Arc; use std::sync::Arc;
use types::transaction::{Action, PendingTransaction, Transaction}; use types::transaction::{Action, PendingTransaction, Transaction, TypedTransaction};
use SyncConfig; use SyncConfig;
fn new_tx(secret: &Secret, nonce: U256, chain_id: u64) -> PendingTransaction { fn new_tx(secret: &Secret, nonce: U256, chain_id: u64) -> PendingTransaction {
let signed = Transaction { let signed = TypedTransaction::Legacy(Transaction {
nonce: nonce.into(), nonce: nonce.into(),
gas_price: 0.into(), gas_price: 0.into(),
gas: 21000.into(), gas: 21000.into(),
action: Action::Call(Address::default()), action: Action::Call(Address::default()),
value: 0.into(), value: 0.into(),
data: Vec::new(), data: Vec::new(),
} })
.sign(secret, Some(chain_id)); .sign(secret, Some(chain_id));
PendingTransaction::new(signed, None) PendingTransaction::new(signed, None)
} }

View File

@ -14,6 +14,9 @@ parity-bytes = "0.1"
rlp = { version = "0.3.0", features = ["ethereum"] } rlp = { version = "0.3.0", features = ["ethereum"] }
rlp_derive = { path = "../../util/rlp-derive" } rlp_derive = { path = "../../util/rlp-derive" }
unexpected = { path = "../../util/unexpected" } unexpected = { path = "../../util/unexpected" }
serde = "1.0"
serde_json = "1.0"
serde_repr = "0.1"
[dev-dependencies] [dev-dependencies]
rustc-hex = "1.0" rustc-hex = "1.0"

View File

@ -35,7 +35,7 @@ use bytes::Bytes;
use header::Header; use header::Header;
use rlp::{Decodable, DecoderError, Rlp, RlpStream}; use rlp::{Decodable, DecoderError, Rlp, RlpStream};
use transaction::UnverifiedTransaction; use transaction::{TypedTransaction, UnverifiedTransaction};
/// A block, encoded as it is on the block chain. /// A block, encoded as it is on the block chain.
#[derive(Default, Debug, Clone, PartialEq)] #[derive(Default, Debug, Clone, PartialEq)]
@ -53,7 +53,7 @@ impl Block {
pub fn rlp_bytes(&self) -> Bytes { pub fn rlp_bytes(&self) -> Bytes {
let mut block_rlp = RlpStream::new_list(3); let mut block_rlp = RlpStream::new_list(3);
block_rlp.append(&self.header); block_rlp.append(&self.header);
block_rlp.append_list(&self.transactions); TypedTransaction::rlp_append_list(&mut block_rlp, &self.transactions);
block_rlp.append_list(&self.uncles); block_rlp.append_list(&self.uncles);
block_rlp.out() block_rlp.out()
} }
@ -69,7 +69,7 @@ impl Decodable for Block {
} }
Ok(Block { Ok(Block {
header: rlp.val_at(0)?, header: rlp.val_at(0)?,
transactions: rlp.list_at(1)?, transactions: TypedTransaction::decode_rlp_list(&rlp.at(1)?)?,
uncles: rlp.list_at(2)?, uncles: rlp.list_at(2)?,
}) })
} }

View File

@ -41,6 +41,7 @@ extern crate heapsize;
extern crate keccak_hash as hash; extern crate keccak_hash as hash;
extern crate parity_bytes as bytes; extern crate parity_bytes as bytes;
extern crate rlp; extern crate rlp;
extern crate serde_repr;
extern crate unexpected; extern crate unexpected;
#[macro_use] #[macro_use]

View File

@ -16,9 +16,14 @@
//! Receipt //! Receipt
use super::transaction::TypedTxId;
use ethereum_types::{Address, Bloom, H160, H256, U256}; use ethereum_types::{Address, Bloom, H160, H256, U256};
use heapsize::HeapSizeOf; use heapsize::HeapSizeOf;
use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; use rlp::{DecoderError, Rlp, RlpStream};
use std::{
convert::TryInto,
ops::{Deref, DerefMut},
};
use log_entry::{LocalizedLogEntry, LogEntry}; use log_entry::{LocalizedLogEntry, LogEntry};
use BlockNumber; use BlockNumber;
@ -36,7 +41,7 @@ pub enum TransactionOutcome {
/// Information describing execution of a transaction. /// Information describing execution of a transaction.
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct Receipt { pub struct LegacyReceipt {
/// The total gas used in the block following execution of the transaction. /// The total gas used in the block following execution of the transaction.
pub gas_used: U256, pub gas_used: U256,
/// The OR-wide combination of all logs' blooms for this transaction. /// The OR-wide combination of all logs' blooms for this transaction.
@ -47,10 +52,9 @@ pub struct Receipt {
pub outcome: TransactionOutcome, pub outcome: TransactionOutcome,
} }
impl Receipt { impl LegacyReceipt {
/// Create a new receipt.
pub fn new(outcome: TransactionOutcome, gas_used: U256, logs: Vec<LogEntry>) -> Self { pub fn new(outcome: TransactionOutcome, gas_used: U256, logs: Vec<LogEntry>) -> Self {
Self { LegacyReceipt {
gas_used, gas_used,
log_bloom: logs.iter().fold(Bloom::default(), |mut b, l| { log_bloom: logs.iter().fold(Bloom::default(), |mut b, l| {
b.accrue_bloom(&l.bloom()); b.accrue_bloom(&l.bloom());
@ -60,10 +64,32 @@ impl Receipt {
outcome, outcome,
} }
} }
pub fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
match rlp.item_count()? {
3 => Ok(LegacyReceipt {
outcome: TransactionOutcome::Unknown,
gas_used: rlp.val_at(0)?,
log_bloom: rlp.val_at(1)?,
logs: rlp.list_at(2)?,
}),
4 => Ok(LegacyReceipt {
gas_used: rlp.val_at(1)?,
log_bloom: rlp.val_at(2)?,
logs: rlp.list_at(3)?,
outcome: {
let first = rlp.at(0)?;
if first.is_data() && first.data()?.len() <= 1 {
TransactionOutcome::StatusCode(first.as_val()?)
} else {
TransactionOutcome::StateRoot(first.as_val()?)
}
},
}),
_ => Err(DecoderError::RlpIncorrectListLen),
}
} }
impl Encodable for Receipt { pub fn rlp_append(&self, s: &mut RlpStream) {
fn rlp_append(&self, s: &mut RlpStream) {
match self.outcome { match self.outcome {
TransactionOutcome::Unknown => { TransactionOutcome::Unknown => {
s.begin_list(3); s.begin_list(3);
@ -83,42 +109,142 @@ impl Encodable for Receipt {
} }
} }
impl Decodable for Receipt { #[derive(Debug, Clone, PartialEq, Eq)]
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> { pub enum TypedReceipt {
if rlp.item_count()? == 3 { Legacy(LegacyReceipt),
Ok(Receipt { AccessList(LegacyReceipt),
outcome: TransactionOutcome::Unknown,
gas_used: rlp.val_at(0)?,
log_bloom: rlp.val_at(1)?,
logs: rlp.list_at(2)?,
})
} else {
Ok(Receipt {
gas_used: rlp.val_at(1)?,
log_bloom: rlp.val_at(2)?,
logs: rlp.list_at(3)?,
outcome: {
let first = rlp.at(0)?;
if first.is_data() && first.data()?.len() <= 1 {
TransactionOutcome::StatusCode(first.as_val()?)
} else {
TransactionOutcome::StateRoot(first.as_val()?)
} }
},
}) impl TypedReceipt {
/// Create a new receipt.
pub fn new(type_id: TypedTxId, legacy_receipt: LegacyReceipt) -> Self {
//curently we are using same receipt for both legacy and typed transaction
match type_id {
TypedTxId::AccessList => Self::AccessList(legacy_receipt),
TypedTxId::Legacy => Self::Legacy(legacy_receipt),
}
}
pub fn tx_type(&self) -> TypedTxId {
match self {
Self::Legacy(_) => TypedTxId::Legacy,
Self::AccessList(_) => TypedTxId::AccessList,
}
}
pub fn receipt(&self) -> &LegacyReceipt {
match self {
Self::Legacy(receipt) => receipt,
Self::AccessList(receipt) => receipt,
}
}
pub fn receipt_mut(&mut self) -> &mut LegacyReceipt {
match self {
Self::Legacy(receipt) => receipt,
Self::AccessList(receipt) => receipt,
}
}
fn decode(tx: &[u8]) -> Result<Self, DecoderError> {
if tx.is_empty() {
// at least one byte needs to be present
return Err(DecoderError::RlpIncorrectListLen);
}
let id = tx[0].try_into();
if id.is_err() {
return Err(DecoderError::Custom("Unknown transaction"));
}
//other transaction types
match id.unwrap() {
TypedTxId::AccessList => {
let rlp = Rlp::new(&tx[1..]);
Ok(Self::AccessList(LegacyReceipt::decode(&rlp)?))
}
TypedTxId::Legacy => Ok(Self::Legacy(LegacyReceipt::decode(&Rlp::new(tx))?)),
}
}
pub fn decode_rlp(rlp: &Rlp) -> Result<Self, DecoderError> {
if rlp.is_list() {
//legacy transaction wrapped around RLP encoding
Ok(Self::Legacy(LegacyReceipt::decode(rlp)?))
} else {
Self::decode(rlp.data()?)
}
}
pub fn decode_rlp_list(rlp: &Rlp) -> Result<Vec<Self>, DecoderError> {
if !rlp.is_list() {
// at least one byte needs to be present
return Err(DecoderError::RlpIncorrectListLen);
}
let mut output = Vec::with_capacity(rlp.item_count()?);
for tx in rlp.iter() {
output.push(Self::decode_rlp(&tx)?);
}
Ok(output)
}
pub fn rlp_append(&self, s: &mut RlpStream) {
match self {
Self::Legacy(receipt) => receipt.rlp_append(s),
Self::AccessList(receipt) => {
let mut rlps = RlpStream::new();
receipt.rlp_append(&mut rlps);
s.append(&[&[TypedTxId::AccessList as u8], rlps.as_raw()].concat());
} }
} }
} }
impl HeapSizeOf for Receipt { pub fn rlp_append_list(s: &mut RlpStream, list: &[TypedReceipt]) {
s.begin_list(list.len());
for rec in list.iter() {
rec.rlp_append(s)
}
}
pub fn encode(&self) -> Vec<u8> {
match self {
Self::Legacy(receipt) => {
let mut s = RlpStream::new();
receipt.rlp_append(&mut s);
s.drain()
}
Self::AccessList(receipt) => {
let mut rlps = RlpStream::new();
receipt.rlp_append(&mut rlps);
[&[TypedTxId::AccessList as u8], rlps.as_raw()].concat()
}
}
}
}
impl Deref for TypedReceipt {
type Target = LegacyReceipt;
fn deref(&self) -> &Self::Target {
self.receipt()
}
}
impl DerefMut for TypedReceipt {
fn deref_mut(&mut self) -> &mut LegacyReceipt {
self.receipt_mut()
}
}
impl HeapSizeOf for TypedReceipt {
fn heap_size_of_children(&self) -> usize { fn heap_size_of_children(&self) -> usize {
self.logs.heap_size_of_children() self.receipt().logs.heap_size_of_children()
} }
} }
/// Receipt with additional info. /// Receipt with additional info.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct RichReceipt { pub struct RichReceipt {
/// Transaction type
pub transaction_type: TypedTxId,
/// Transaction hash. /// Transaction hash.
pub transaction_hash: H256, pub transaction_hash: H256,
/// Transaction index. /// Transaction index.
@ -146,6 +272,8 @@ pub struct RichReceipt {
/// Receipt with additional info. /// Receipt with additional info.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct LocalizedReceipt { pub struct LocalizedReceipt {
/// Transaction type
pub transaction_type: TypedTxId,
/// Transaction hash. /// Transaction hash.
pub transaction_hash: H256, pub transaction_hash: H256,
/// Transaction index. /// Transaction index.
@ -176,13 +304,15 @@ pub struct LocalizedReceipt {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{Receipt, TransactionOutcome}; use super::{LegacyReceipt, TransactionOutcome, TypedReceipt, TypedTxId};
use log_entry::LogEntry; use log_entry::LogEntry;
#[test] #[test]
fn test_no_state_root() { fn test_no_state_root() {
let expected = ::rustc_hex::FromHex::from_hex("f9014183040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); let expected = ::rustc_hex::FromHex::from_hex("f9014183040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap();
let r = Receipt::new( let r = TypedReceipt::new(
TypedTxId::Legacy,
LegacyReceipt::new(
TransactionOutcome::Unknown, TransactionOutcome::Unknown,
0x40cae.into(), 0x40cae.into(),
vec![LogEntry { vec![LogEntry {
@ -190,14 +320,17 @@ mod tests {
topics: vec![], topics: vec![],
data: vec![0u8; 32], data: vec![0u8; 32],
}], }],
),
); );
assert_eq!(&::rlp::encode(&r)[..], &expected[..]); assert_eq!(r.encode(), expected);
} }
#[test] #[test]
fn test_basic() { fn test_basic_legacy() {
let expected = ::rustc_hex::FromHex::from_hex("f90162a02f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee83040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); let expected = ::rustc_hex::FromHex::from_hex("f90162a02f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee83040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap();
let r = Receipt::new( let r = TypedReceipt::new(
TypedTxId::Legacy,
LegacyReceipt::new(
TransactionOutcome::StateRoot( TransactionOutcome::StateRoot(
"2f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee".into(), "2f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee".into(),
), ),
@ -207,17 +340,43 @@ mod tests {
topics: vec![], topics: vec![],
data: vec![0u8; 32], data: vec![0u8; 32],
}], }],
),
); );
let encoded = ::rlp::encode(&r); let encoded = r.encode();
assert_eq!(&encoded[..], &expected[..]); assert_eq!(encoded, expected);
let decoded: Receipt = ::rlp::decode(&encoded).expect("decoding receipt failed"); let decoded = TypedReceipt::decode(&encoded).expect("decoding receipt failed");
assert_eq!(decoded, r);
}
#[test]
fn test_basic_access_list() {
let expected = ::rustc_hex::FromHex::from_hex("01f90162a02f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee83040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap();
let r = TypedReceipt::new(
TypedTxId::AccessList,
LegacyReceipt::new(
TransactionOutcome::StateRoot(
"2f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee".into(),
),
0x40cae.into(),
vec![LogEntry {
address: "dcf421d093428b096ca501a7cd1a740855a7976f".into(),
topics: vec![],
data: vec![0u8; 32],
}],
),
);
let encoded = r.encode();
assert_eq!(&encoded, &expected);
let decoded = TypedReceipt::decode(&encoded).expect("decoding receipt failed");
assert_eq!(decoded, r); assert_eq!(decoded, r);
} }
#[test] #[test]
fn test_status_code() { fn test_status_code() {
let expected = ::rustc_hex::FromHex::from_hex("f901428083040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap(); let expected = ::rustc_hex::FromHex::from_hex("f901428083040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap();
let r = Receipt::new( let r = TypedReceipt::new(
TypedTxId::Legacy,
LegacyReceipt::new(
TransactionOutcome::StatusCode(0), TransactionOutcome::StatusCode(0),
0x40cae.into(), 0x40cae.into(),
vec![LogEntry { vec![LogEntry {
@ -225,10 +384,11 @@ mod tests {
topics: vec![], topics: vec![],
data: vec![0u8; 32], data: vec![0u8; 32],
}], }],
),
); );
let encoded = ::rlp::encode(&r); let encoded = r.encode();
assert_eq!(&encoded[..], &expected[..]); assert_eq!(&encoded[..], &expected[..]);
let decoded: Receipt = ::rlp::decode(&encoded).expect("decoding receipt failed"); let decoded = TypedReceipt::decode(&encoded).expect("decoding receipt failed");
assert_eq!(decoded, r); assert_eq!(decoded, r);
} }
} }

View File

@ -84,6 +84,8 @@ pub enum Error {
TooBig, TooBig,
/// Invalid RLP encoding /// Invalid RLP encoding
InvalidRlp(String), InvalidRlp(String),
/// Transaciton is still not enabled.
TransactionTypeNotEnabled,
} }
impl From<ethkey::Error> for Error { impl From<ethkey::Error> for Error {
@ -133,6 +135,9 @@ impl fmt::Display for Error {
} }
TooBig => "Transaction too big".into(), TooBig => "Transaction too big".into(),
InvalidRlp(ref err) => format!("Transaction has invalid RLP structure: {}.", err), InvalidRlp(ref err) => format!("Transaction has invalid RLP structure: {}.", err),
TransactionTypeNotEnabled => {
format!("Transaction type is not enabled for current block")
}
}; };
f.write_fmt(format_args!("Transaction error ({})", msg)) f.write_fmt(format_args!("Transaction error ({})", msg))

View File

@ -18,5 +18,6 @@
mod error; mod error;
mod transaction; mod transaction;
mod transaction_id;
pub use self::{error::Error, transaction::*}; pub use self::{error::Error, transaction::*, transaction_id::*};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,45 @@
// Copyright 2020-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum 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.
// OpenEthereum 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 OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
//! Transaction Id.
use serde_repr::*;
use std::convert::TryFrom;
#[derive(Serialize_repr, Eq, Hash, Deserialize_repr, Debug, Clone, PartialEq)]
#[repr(u8)]
pub enum TypedTxId {
AccessList = 0x01,
Legacy = 0x80, // With 0x80 we are sure that all other types will not overlap
}
impl Default for TypedTxId {
fn default() -> TypedTxId {
TypedTxId::Legacy
}
}
impl TryFrom<u8> for TypedTxId {
type Error = ();
fn try_from(v: u8) -> Result<Self, Self::Error> {
match v {
x if x == TypedTxId::AccessList as u8 => Ok(TypedTxId::AccessList),
x if (x & 0x80) != 0x00 => Ok(TypedTxId::Legacy),
_ => Err(()),
}
}
}

View File

@ -21,7 +21,7 @@ use bytes::Bytes;
use ethereum_types::H256; use ethereum_types::H256;
use hash::keccak; use hash::keccak;
use header::Header; use header::Header;
use transaction::{LocalizedTransaction, UnverifiedTransaction}; use transaction::{LocalizedTransaction, TypedTransaction, UnverifiedTransaction};
use views::{HeaderView, TransactionView}; use views::{HeaderView, TransactionView};
/// View onto block rlp. /// View onto block rlp.
@ -77,7 +77,12 @@ impl<'a> BlockView<'a> {
/// Return List of transactions in given block. /// Return List of transactions in given block.
pub fn transactions(&self) -> Vec<UnverifiedTransaction> { pub fn transactions(&self) -> Vec<UnverifiedTransaction> {
self.rlp.list_at(1) TypedTransaction::decode_rlp_list(&self.rlp.at(1).rlp).unwrap_or_else(|e| {
panic!(
"block transactions, view rlp is trusted and should be valid: {:?}",
e
)
})
} }
/// Return List of transactions with additional localization info. /// Return List of transactions with additional localization info.
@ -126,10 +131,14 @@ impl<'a> BlockView<'a> {
/// Returns transaction at given index without deserializing unnecessary data. /// Returns transaction at given index without deserializing unnecessary data.
pub fn transaction_at(&self, index: usize) -> Option<UnverifiedTransaction> { pub fn transaction_at(&self, index: usize) -> Option<UnverifiedTransaction> {
self.transactions_rlp() self.transactions_rlp().iter().nth(index).map(|rlp| {
.iter() TypedTransaction::decode_rlp(&rlp.rlp).unwrap_or_else(|e| {
.nth(index) panic!(
.map(|rlp| rlp.as_val()) "block transaction_at, view rlp is trusted and should be valid.{:?}",
e
)
})
})
} }
/// Returns localized transaction at given index. /// Returns localized transaction at given index.

View File

@ -21,7 +21,7 @@ use bytes::Bytes;
use ethereum_types::H256; use ethereum_types::H256;
use hash::keccak; use hash::keccak;
use header::Header; use header::Header;
use transaction::{LocalizedTransaction, UnverifiedTransaction}; use transaction::{LocalizedTransaction, TypedTransaction, UnverifiedTransaction};
use views::{HeaderView, TransactionView}; use views::{HeaderView, TransactionView};
use BlockNumber; use BlockNumber;
@ -58,7 +58,12 @@ impl<'a> BodyView<'a> {
/// Return List of transactions in given block. /// Return List of transactions in given block.
pub fn transactions(&self) -> Vec<UnverifiedTransaction> { pub fn transactions(&self) -> Vec<UnverifiedTransaction> {
self.rlp.list_at(0) TypedTransaction::decode_rlp_list(&self.rlp.at(0).rlp).unwrap_or_else(|e| {
panic!(
"body transactions, view rlp is trusted and should be valid: {:?}",
e
)
})
} }
/// Return List of transactions with additional localization info. /// Return List of transactions with additional localization info.
@ -107,10 +112,14 @@ impl<'a> BodyView<'a> {
/// Returns transaction at given index without deserializing unnecessary data. /// Returns transaction at given index without deserializing unnecessary data.
pub fn transaction_at(&self, index: usize) -> Option<UnverifiedTransaction> { pub fn transaction_at(&self, index: usize) -> Option<UnverifiedTransaction> {
self.transactions_rlp() self.transactions_rlp().iter().nth(index).map(|rlp| {
.iter() TypedTransaction::decode_rlp(&rlp.rlp).unwrap_or_else(|e| {
.nth(index) panic!(
.map(|rlp| rlp.as_val()) "body transaction_a, view rlp is trusted and should be valid: {:?}",
e
)
})
})
} }
/// Returns localized transaction at given index. /// Returns localized transaction at given index.

View File

@ -213,7 +213,7 @@ mod tests {
access_list.insert_address(Address::from(1)); access_list.insert_address(Address::from(1));
access_list.insert_storage_key(Address::from(2), H256::from(3)); access_list.insert_storage_key(Address::from(2), H256::from(3));
let mut access_list_call = access_list.clone(); let access_list_call = access_list.clone();
assert_eq!(true, access_list_call.contains_address(&Address::from(1))); assert_eq!(true, access_list_call.contains_address(&Address::from(1)));
assert_eq!( assert_eq!(
true, true,

View File

@ -24,6 +24,10 @@ pub const EIP2929_COLD_ACCOUNT_ACCESS_COST: usize = 2600;
pub const EIP2929_WARM_STORAGE_READ_COST: usize = 100; pub const EIP2929_WARM_STORAGE_READ_COST: usize = 100;
// Gas per sstore reset // Gas per sstore reset
pub const EIP2929_SSTORE_RESET_GAS: usize = 5000 - EIP2929_COLD_SLOAD_COST; pub const EIP2929_SSTORE_RESET_GAS: usize = 5000 - EIP2929_COLD_SLOAD_COST;
/// Gas per received storage key
pub const EIP2930_ACCESS_LIST_STORAGE_KEY_COST: usize = 1900;
/// Gas per received address
pub const EIP2930_ACCESS_LIST_ADDRESS_COST: usize = 2400;
/// Definition of the cost schedule and other parameterisations for the EVM. /// Definition of the cost schedule and other parameterisations for the EVM.
#[derive(Debug)] #[derive(Debug)]
@ -149,6 +153,8 @@ pub struct Schedule {
pub wasm: Option<WasmCosts>, pub wasm: Option<WasmCosts>,
/// Enable EIP-2929 rules /// Enable EIP-2929 rules
pub eip2929: bool, pub eip2929: bool,
/// Enable EIP-2930 rules for optional access list transactions. it depends on EIP-2929
pub eip2930: bool,
} }
/// Wasm cost table /// Wasm cost table
@ -295,6 +301,7 @@ impl Schedule {
keep_unsigned_nonce: false, keep_unsigned_nonce: false,
wasm: None, wasm: None,
eip2929: false, eip2929: false,
eip2930: false,
} }
} }
@ -343,6 +350,7 @@ impl Schedule {
schedule.eip1283 = true; schedule.eip1283 = true;
schedule.eip2929 = true; schedule.eip2929 = true;
schedule.eip2930 = true;
schedule.cold_sload_cost = EIP2929_COLD_SLOAD_COST; schedule.cold_sload_cost = EIP2929_COLD_SLOAD_COST;
schedule.cold_account_access_cost = EIP2929_COLD_ACCOUNT_ACCESS_COST; schedule.cold_account_access_cost = EIP2929_COLD_ACCOUNT_ACCESS_COST;
@ -421,6 +429,7 @@ impl Schedule {
keep_unsigned_nonce: false, keep_unsigned_nonce: false,
wasm: None, wasm: None,
eip2929: false, eip2929: false,
eip2930: false,
} }
} }

View File

@ -141,7 +141,7 @@ pub fn run_transaction<T: Informant>(
let result = run( let result = run(
&spec, &spec,
trie_spec, trie_spec,
transaction.gas, transaction.tx().gas,
pre_state, pre_state,
|mut client| { |mut client| {
let result = client.transact(env_info, transaction, trace::NoopTracer, informant); let result = client.transact(env_info, transaction, trace::NoopTracer, informant);

View File

@ -108,6 +108,8 @@ pub struct Params {
/// See `CommonParams` docs. /// See `CommonParams` docs.
pub eip2929_transition: Option<Uint>, pub eip2929_transition: Option<Uint>,
/// See `CommonParams` docs. /// See `CommonParams` docs.
pub eip2930_transition: Option<Uint>,
/// See `CommonParams` docs.
pub dust_protection_transition: Option<Uint>, pub dust_protection_transition: Option<Uint>,
/// See `CommonParams` docs. /// See `CommonParams` docs.
pub nonce_cap_increment: Option<Uint>, pub nonce_cap_increment: Option<Uint>,

View File

@ -20,9 +20,9 @@ use std::{fmt, sync::Arc, time::Duration};
use io::IoHandler; use io::IoHandler;
use kvdb::KeyValueDB; use kvdb::KeyValueDB;
use rlp::Rlp;
use types::transaction::{ use types::transaction::{
Condition as TransactionCondition, PendingTransaction, SignedTransaction, UnverifiedTransaction, Condition as TransactionCondition, PendingTransaction, SignedTransaction, TypedTransaction,
UnverifiedTransaction,
}; };
extern crate common_types as types; extern crate common_types as types;
@ -98,7 +98,7 @@ struct TransactionEntry {
impl TransactionEntry { impl TransactionEntry {
fn into_pending(self) -> Option<PendingTransaction> { fn into_pending(self) -> Option<PendingTransaction> {
let tx: UnverifiedTransaction = match Rlp::new(&self.rlp_bytes).as_val() { let tx: UnverifiedTransaction = match TypedTransaction::decode(&self.rlp_bytes) {
Err(e) => { Err(e) => {
warn!(target: "local_store", "Invalid persistent transaction stored: {}", e); warn!(target: "local_store", "Invalid persistent transaction stored: {}", e);
return None; return None;
@ -120,7 +120,7 @@ impl TransactionEntry {
impl From<PendingTransaction> for TransactionEntry { impl From<PendingTransaction> for TransactionEntry {
fn from(pending: PendingTransaction) -> Self { fn from(pending: PendingTransaction) -> Self {
TransactionEntry { TransactionEntry {
rlp_bytes: ::rlp::encode(&pending.transaction), rlp_bytes: pending.transaction.encode(),
condition: pending.condition.map(Into::into), condition: pending.condition.map(Into::into),
} }
} }
@ -239,7 +239,7 @@ mod tests {
use ethkey::{Brain, Generator}; use ethkey::{Brain, Generator};
use std::sync::Arc; use std::sync::Arc;
use types::transaction::{Condition, PendingTransaction, Transaction}; use types::transaction::{Condition, PendingTransaction, Transaction, TypedTransaction};
// we want to test: round-trip of good transactions. // we want to test: round-trip of good transactions.
// failure to roundtrip bad transactions (but that it doesn't panic) // failure to roundtrip bad transactions (but that it doesn't panic)
@ -271,8 +271,8 @@ mod tests {
let keypair = Brain::new("abcd".into()).generate().unwrap(); let keypair = Brain::new("abcd".into()).generate().unwrap();
let transactions: Vec<_> = (0..10u64) let transactions: Vec<_> = (0..10u64)
.map(|nonce| { .map(|nonce| {
let mut tx = Transaction::default(); let mut tx = TypedTransaction::Legacy(Transaction::default());
tx.nonce = nonce.into(); tx.tx_mut().nonce = nonce.into();
let signed = tx.sign(keypair.secret(), None); let signed = tx.sign(keypair.secret(), None);
let condition = match nonce { let condition = match nonce {
@ -308,8 +308,8 @@ mod tests {
let keypair = Brain::new("abcd".into()).generate().unwrap(); let keypair = Brain::new("abcd".into()).generate().unwrap();
let mut transactions: Vec<_> = (0..10u64) let mut transactions: Vec<_> = (0..10u64)
.map(|nonce| { .map(|nonce| {
let mut tx = Transaction::default(); let mut tx = TypedTransaction::Legacy(Transaction::default());
tx.nonce = nonce.into(); tx.tx_mut().nonce = nonce.into();
let signed = tx.sign(keypair.secret(), None); let signed = tx.sign(keypair.secret(), None);
@ -318,8 +318,8 @@ mod tests {
.collect(); .collect();
transactions.push({ transactions.push({
let mut tx = Transaction::default(); let mut tx = TypedTransaction::Legacy(Transaction::default());
tx.nonce = 10.into(); tx.tx_mut().nonce = 10.into();
let signed = tx.fake_sign(Default::default()); let signed = tx.fake_sign(Default::default());
PendingTransaction::new(signed, None) PendingTransaction::new(signed, None)

View File

@ -79,11 +79,11 @@ impl txpool::Listener<Transaction> for Logger {
"[{hash:?}] Sender: {sender}, nonce: {nonce}, gasPrice: {gas_price}, gas: {gas}, value: {value}, dataLen: {data}))", "[{hash:?}] Sender: {sender}, nonce: {nonce}, gasPrice: {gas_price}, gas: {gas}, value: {value}, dataLen: {data}))",
hash = tx.hash(), hash = tx.hash(),
sender = tx.sender(), sender = tx.sender(),
nonce = tx.signed().nonce, nonce = tx.signed().tx().nonce,
gas_price = tx.signed().gas_price, gas_price = tx.signed().tx().gas_price,
gas = tx.signed().gas, gas = tx.signed().tx().gas,
value = tx.signed().value, value = tx.signed().tx().value,
data = tx.signed().data.len(), data = tx.signed().tx().data.len(),
); );
if let Some(old) = old { if let Some(old) = old {
@ -150,7 +150,7 @@ mod tests {
assert_eq!( assert_eq!(
*received.lock(), *received.lock(),
vec![ vec![
"13aff4201ac1dc49daf6a7cf07b558ed956511acbaabf9502bdacc353953766d" "de96bdcdf864c95eb7f81eff1e3290be24a0f327732e0c4251c1896a565a80db"
.parse() .parse()
.unwrap() .unwrap()
] ]
@ -158,14 +158,14 @@ mod tests {
} }
fn new_tx() -> Arc<Transaction> { fn new_tx() -> Arc<Transaction> {
let signed = transaction::Transaction { let signed = transaction::TypedTransaction::Legacy(transaction::Transaction {
action: transaction::Action::Create, action: transaction::Action::Create,
data: vec![1, 2, 3], data: vec![1, 2, 3],
nonce: 5.into(), nonce: 5.into(),
gas: 21_000.into(), gas: 21_000.into(),
gas_price: 5.into(), gas_price: 5.into(),
value: 0.into(), value: 0.into(),
} })
.fake_sign(5.into()); .fake_sign(5.into());
Arc::new(Transaction::from_pending_block_transaction(signed)) Arc::new(Transaction::from_pending_block_transaction(signed))

View File

@ -340,14 +340,14 @@ mod tests {
fn new_tx<T: Into<U256>>(nonce: T) -> Arc<Transaction> { fn new_tx<T: Into<U256>>(nonce: T) -> Arc<Transaction> {
let keypair = Random.generate().unwrap(); let keypair = Random.generate().unwrap();
let signed = transaction::Transaction { let signed = transaction::TypedTransaction::Legacy(transaction::Transaction {
action: transaction::Action::Create, action: transaction::Action::Create,
value: U256::from(100), value: U256::from(100),
data: Default::default(), data: Default::default(),
gas: U256::from(10), gas: U256::from(10),
gas_price: U256::from(1245), gas_price: U256::from(1245),
nonce: nonce.into(), nonce: nonce.into(),
} })
.sign(keypair.secret(), None); .sign(keypair.secret(), None);
let mut tx = Transaction::from_pending_block_transaction(signed); let mut tx = Transaction::from_pending_block_transaction(signed);

View File

@ -192,11 +192,11 @@ impl ScoredTransaction for VerifiedTransaction {
/// Gets transaction gas price. /// Gets transaction gas price.
fn gas_price(&self) -> &U256 { fn gas_price(&self) -> &U256 {
&self.transaction.gas_price &self.transaction.tx().gas_price
} }
/// Gets transaction nonce. /// Gets transaction nonce.
fn nonce(&self) -> U256 { fn nonce(&self) -> U256 {
self.transaction.nonce self.transaction.tx().nonce
} }
} }

View File

@ -519,7 +519,7 @@ impl TransactionQueue {
.read() .read()
.pending_from_sender(state_readiness, address) .pending_from_sender(state_readiness, address)
.last() .last()
.map(|tx| tx.signed().nonce.saturating_add(U256::from(1))) .map(|tx| tx.signed().tx().nonce.saturating_add(U256::from(1)))
} }
/// Retrieve a transaction from the pool. /// Retrieve a transaction from the pool.
@ -573,7 +573,7 @@ impl TransactionQueue {
/// Returns gas price of currently the worst transaction in the pool. /// Returns gas price of currently the worst transaction in the pool.
pub fn current_worst_gas_price(&self) -> U256 { pub fn current_worst_gas_price(&self) -> U256 {
match self.pool.read().worst_transaction() { match self.pool.read().worst_transaction() {
Some(tx) => tx.signed().gas_price, Some(tx) => tx.signed().tx().gas_price,
None => self.options.read().minimal_gas_price, None => self.options.read().minimal_gas_price,
} }
} }
@ -660,7 +660,7 @@ mod tests {
); );
for tx in pending { for tx in pending {
assert!(tx.signed().nonce > 0.into()); assert!(tx.signed().tx().nonce > 0.into());
} }
} }
} }

View File

@ -71,7 +71,7 @@ impl<C: NonceClient> txpool::Ready<VerifiedTransaction> for State<C> {
fn is_ready(&mut self, tx: &VerifiedTransaction) -> txpool::Readiness { fn is_ready(&mut self, tx: &VerifiedTransaction) -> txpool::Readiness {
// Check max nonce // Check max nonce
match self.max_nonce { match self.max_nonce {
Some(nonce) if tx.transaction.nonce > nonce => { Some(nonce) if tx.transaction.tx().nonce > nonce => {
return txpool::Readiness::Future; return txpool::Readiness::Future;
} }
_ => {} _ => {}
@ -81,7 +81,7 @@ impl<C: NonceClient> txpool::Ready<VerifiedTransaction> for State<C> {
let state = &self.state; let state = &self.state;
let state_nonce = || state.account_nonce(sender); let state_nonce = || state.account_nonce(sender);
let nonce = self.nonces.entry(*sender).or_insert_with(state_nonce); let nonce = self.nonces.entry(*sender).or_insert_with(state_nonce);
match tx.transaction.nonce.cmp(nonce) { match tx.transaction.tx().nonce.cmp(nonce) {
// Before marking as future check for stale ids // Before marking as future check for stale ids
cmp::Ordering::Greater => match self.stale_id { cmp::Ordering::Greater => match self.stale_id {
Some(id) if tx.insertion_id() < id => txpool::Readiness::Stale, Some(id) if tx.insertion_id() < id => txpool::Readiness::Stale,
@ -150,8 +150,8 @@ impl<C: Fn(&Address) -> Option<U256>> txpool::Ready<VerifiedTransaction> for Opt
let nonce = self let nonce = self
.nonces .nonces
.entry(*sender) .entry(*sender)
.or_insert_with(|| state(sender).unwrap_or_else(|| tx.transaction.nonce)); .or_insert_with(|| state(sender).unwrap_or_else(|| tx.transaction.tx().nonce));
match tx.transaction.nonce.cmp(nonce) { match tx.transaction.tx().nonce.cmp(nonce) {
cmp::Ordering::Greater => txpool::Readiness::Future, cmp::Ordering::Greater => txpool::Readiness::Future,
cmp::Ordering::Less => txpool::Readiness::Stale, cmp::Ordering::Less => txpool::Readiness::Stale,
cmp::Ordering::Equal => { cmp::Ordering::Equal => {

View File

@ -67,7 +67,7 @@ impl NonceAndGasPrice {
return true; return true;
} }
&old.transaction.gas_price > new.gas_price() &old.transaction.tx().gas_price > new.gas_price()
} }
} }

View File

@ -18,7 +18,9 @@ use std::sync::{atomic, Arc};
use ethereum_types::{Address, H256, U256}; use ethereum_types::{Address, H256, U256};
use rlp::Rlp; use rlp::Rlp;
use types::transaction::{self, SignedTransaction, Transaction, UnverifiedTransaction}; use types::transaction::{
self, SignedTransaction, Transaction, TypedTransaction, UnverifiedTransaction,
};
use pool::{self, client::AccountDetails}; use pool::{self, client::AccountDetails};
@ -150,7 +152,7 @@ impl pool::client::Client for TestClient {
if rlp.as_raw().len() > self.max_transaction_size { if rlp.as_raw().len() > self.max_transaction_size {
return Err(transaction::Error::TooBig); return Err(transaction::Error::TooBig);
} }
rlp.as_val() TypedTransaction::decode(transaction)
.map_err(|e| transaction::Error::InvalidRlp(e.to_string())) .map_err(|e| transaction::Error::InvalidRlp(e.to_string()))
} }
} }

View File

@ -69,7 +69,7 @@ fn should_return_correct_nonces_when_dropped_because_of_limit() {
); );
let (tx1, tx2) = Tx::gas_price(2).signed_pair(); let (tx1, tx2) = Tx::gas_price(2).signed_pair();
let sender = tx1.sender(); let sender = tx1.sender();
let nonce = tx1.nonce; let nonce = tx1.tx().nonce;
// when // when
let r1 = txq.import(TestClient::new(), vec![tx1].retracted()); let r1 = txq.import(TestClient::new(), vec![tx1].retracted());
@ -129,7 +129,7 @@ fn should_never_drop_local_transactions_from_different_senders() {
); );
let (tx1, tx2) = Tx::gas_price(2).signed_pair(); let (tx1, tx2) = Tx::gas_price(2).signed_pair();
let sender = tx1.sender(); let sender = tx1.sender();
let nonce = tx1.nonce; let nonce = tx1.tx().nonce;
// when // when
let r1 = txq.import(TestClient::new(), vec![tx1].local()); let r1 = txq.import(TestClient::new(), vec![tx1].local());
@ -662,12 +662,14 @@ fn should_not_replace_same_transaction_if_the_fee_is_less_than_minimal_bump() {
assert_eq!( assert_eq!(
txq.pending(client.clone(), PendingSettings::all_prioritized(0, 0))[0] txq.pending(client.clone(), PendingSettings::all_prioritized(0, 0))[0]
.signed() .signed()
.tx()
.gas_price, .gas_price,
U256::from(20) U256::from(20)
); );
assert_eq!( assert_eq!(
txq.pending(client.clone(), PendingSettings::all_prioritized(0, 0))[1] txq.pending(client.clone(), PendingSettings::all_prioritized(0, 0))[1]
.signed() .signed()
.tx()
.gas_price, .gas_price,
U256::from(2) U256::from(2)
); );
@ -688,7 +690,7 @@ fn should_return_correct_nonce_when_transactions_from_given_address_exist() {
let txq = new_queue(); let txq = new_queue();
let tx = Tx::default().signed(); let tx = Tx::default().signed();
let from = tx.sender(); let from = tx.sender();
let nonce = tx.nonce; let nonce = tx.tx().nonce;
// when // when
txq.import(TestClient::new(), vec![tx.local()]); txq.import(TestClient::new(), vec![tx.local()]);

View File

@ -17,7 +17,9 @@
use ethereum_types::{H256, U256}; use ethereum_types::{H256, U256};
use ethkey::{Generator, Random}; use ethkey::{Generator, Random};
use rustc_hex::FromHex; use rustc_hex::FromHex;
use types::transaction::{self, SignedTransaction, Transaction, UnverifiedTransaction}; use types::transaction::{
self, SignedTransaction, Transaction, TypedTransaction, UnverifiedTransaction,
};
use pool::{verifier, VerifiedTransaction}; use pool::{verifier, VerifiedTransaction};
@ -76,20 +78,20 @@ impl Tx {
(tx1, tx2) (tx1, tx2)
} }
pub fn unsigned(self) -> Transaction { pub fn unsigned(self) -> TypedTransaction {
Transaction { TypedTransaction::Legacy(Transaction {
action: transaction::Action::Create, action: transaction::Action::Create,
value: U256::from(100), value: U256::from(100),
data: "3331600055".from_hex().unwrap(), data: "3331600055".from_hex().unwrap(),
gas: self.gas.into(), gas: self.gas.into(),
gas_price: self.gas_price.into(), gas_price: self.gas_price.into(),
nonce: self.nonce.into(), nonce: self.nonce.into(),
} })
} }
pub fn big_one(self) -> SignedTransaction { pub fn big_one(self) -> SignedTransaction {
let keypair = Random.generate().unwrap(); let keypair = Random.generate().unwrap();
let tx = Transaction { let tx = TypedTransaction::Legacy(Transaction {
action: transaction::Action::Create, action: transaction::Action::Create,
value: U256::from(100), value: U256::from(100),
data: include_str!("../res/big_transaction.data") data: include_str!("../res/big_transaction.data")
@ -98,7 +100,7 @@ impl Tx {
gas: self.gas.into(), gas: self.gas.into(),
gas_price: self.gas_price.into(), gas_price: self.gas_price.into(),
nonce: self.nonce.into(), nonce: self.nonce.into(),
}; });
tx.sign(keypair.secret(), None) tx.sign(keypair.secret(), None)
} }
} }

View File

@ -31,7 +31,6 @@ use std::{
}; };
use ethereum_types::{H256, U256}; use ethereum_types::{H256, U256};
use rlp::Encodable;
use txpool; use txpool;
use types::transaction; use types::transaction;
@ -97,21 +96,21 @@ impl Transaction {
/// Return transaction gas price /// Return transaction gas price
pub fn gas_price(&self) -> &U256 { pub fn gas_price(&self) -> &U256 {
match *self { match *self {
Transaction::Unverified(ref tx) => &tx.gas_price, Transaction::Unverified(ref tx) => &tx.tx().gas_price,
Transaction::Retracted(ref tx) => &tx.gas_price, Transaction::Retracted(ref tx) => &tx.tx().gas_price,
Transaction::Local(ref tx) => &tx.gas_price, Transaction::Local(ref tx) => &tx.tx().gas_price,
} }
} }
fn gas(&self) -> &U256 { fn gas(&self) -> &U256 {
match *self { match *self {
Transaction::Unverified(ref tx) => &tx.gas, Transaction::Unverified(ref tx) => &tx.tx().gas,
Transaction::Retracted(ref tx) => &tx.gas, Transaction::Retracted(ref tx) => &tx.tx().gas,
Transaction::Local(ref tx) => &tx.gas, Transaction::Local(ref tx) => &tx.tx().gas,
} }
} }
fn transaction(&self) -> &transaction::Transaction { fn transaction(&self) -> &transaction::TypedTransaction {
match *self { match *self {
Transaction::Unverified(ref tx) => &*tx, Transaction::Unverified(ref tx) => &*tx,
Transaction::Retracted(ref tx) => &*tx, Transaction::Retracted(ref tx) => &*tx,
@ -198,7 +197,7 @@ impl<C: Client> txpool::Verifier<Transaction>
}); });
} }
let minimal_gas = self.client.required_gas(tx.transaction()); let minimal_gas = self.client.required_gas(tx.transaction().tx());
if tx.gas() < &minimal_gas { if tx.gas() < &minimal_gas {
trace!(target: "txqueue", trace!(target: "txqueue",
"[{:?}] Rejected transaction with insufficient gas: {} < {}", "[{:?}] Rejected transaction with insufficient gas: {} < {}",
@ -240,10 +239,10 @@ impl<C: Client> txpool::Verifier<Transaction>
"[{:?}] Rejected tx early, cause it doesn't have any chance to get to the pool: (gas price: {} < {})", "[{:?}] Rejected tx early, cause it doesn't have any chance to get to the pool: (gas price: {} < {})",
hash, hash,
tx.gas_price(), tx.gas_price(),
vtx.transaction.gas_price, vtx.transaction.tx().gas_price,
); );
return Err(transaction::Error::TooCheapToReplace { return Err(transaction::Error::TooCheapToReplace {
prev: Some(vtx.transaction.gas_price), prev: Some(vtx.transaction.tx().gas_price),
new: Some(*tx.gas_price()), new: Some(*tx.gas_price()),
}); });
} }
@ -273,7 +272,7 @@ impl<C: Client> txpool::Verifier<Transaction>
}; };
// Verify RLP payload // Verify RLP payload
if let Err(err) = self.client.decode_transaction(&transaction.rlp_bytes()) { if let Err(err) = self.client.decode_transaction(&transaction.encode()) {
debug!(target: "txqueue", "[{:?}] Rejected transaction's rlp payload", err); debug!(target: "txqueue", "[{:?}] Rejected transaction's rlp payload", err);
bail!(err) bail!(err)
} }
@ -281,7 +280,7 @@ impl<C: Client> txpool::Verifier<Transaction>
let sender = transaction.sender(); let sender = transaction.sender();
let account_details = self.client.account_details(&sender); let account_details = self.client.account_details(&sender);
if transaction.gas_price < self.options.minimal_gas_price { if transaction.tx().gas_price < self.options.minimal_gas_price {
let transaction_type = self.client.transaction_type(&transaction); let transaction_type = self.client.transaction_type(&transaction);
if let TransactionType::Service = transaction_type { if let TransactionType::Service = transaction_type {
debug!(target: "txqueue", "Service tx {:?} below minimal gas price accepted", hash); debug!(target: "txqueue", "Service tx {:?} below minimal gas price accepted", hash);
@ -292,18 +291,21 @@ impl<C: Client> txpool::Verifier<Transaction>
target: "txqueue", target: "txqueue",
"[{:?}] Rejected tx below minimal gas price threshold: {} < {}", "[{:?}] Rejected tx below minimal gas price threshold: {} < {}",
hash, hash,
transaction.gas_price, transaction.tx().gas_price,
self.options.minimal_gas_price, self.options.minimal_gas_price,
); );
bail!(transaction::Error::InsufficientGasPrice { bail!(transaction::Error::InsufficientGasPrice {
minimal: self.options.minimal_gas_price, minimal: self.options.minimal_gas_price,
got: transaction.gas_price, got: transaction.tx().gas_price,
}); });
} }
} }
let (full_gas_price, overflow_1) = transaction.gas_price.overflowing_mul(transaction.gas); let (full_gas_price, overflow_1) = transaction
let (cost, overflow_2) = transaction.value.overflowing_add(full_gas_price); .tx()
.gas_price
.overflowing_mul(transaction.tx().gas);
let (cost, overflow_2) = transaction.tx().value.overflowing_add(full_gas_price);
if overflow_1 || overflow_2 { if overflow_1 || overflow_2 {
trace!( trace!(
target: "txqueue", target: "txqueue",
@ -329,12 +331,12 @@ impl<C: Client> txpool::Verifier<Transaction>
}); });
} }
if transaction.nonce < account_details.nonce { if transaction.tx().nonce < account_details.nonce {
debug!( debug!(
target: "txqueue", target: "txqueue",
"[{:?}] Rejected tx with old nonce ({} < {})", "[{:?}] Rejected tx with old nonce ({} < {})",
hash, hash,
transaction.nonce, transaction.tx().nonce,
account_details.nonce, account_details.nonce,
); );
bail!(transaction::Error::Old); bail!(transaction::Error::Old);

View File

@ -45,7 +45,7 @@ impl ServiceTransactionChecker {
) -> Result<bool, String> { ) -> Result<bool, String> {
let sender = tx.sender(); let sender = tx.sender();
// Skip checking the contract if the transaction does not have zero gas price // Skip checking the contract if the transaction does not have zero gas price
if !tx.gas_price.is_zero() { if !tx.tx().gas_price.is_zero() {
return Ok(false); return Ok(false);
} }

View File

@ -120,6 +120,7 @@ impl<C: miner::BlockChainClient + BlockChainClient, M: MinerService> Dispatcher
}; };
Box::new(future::ok(FilledTransactionRequest { Box::new(future::ok(FilledTransactionRequest {
tx_type: request.tx_type,
from, from,
used_default_from: request.from.is_none(), used_default_from: request.from.is_none(),
to: request.to, to: request.to,
@ -133,6 +134,7 @@ impl<C: miner::BlockChainClient + BlockChainClient, M: MinerService> Dispatcher
value: request.value.unwrap_or_else(|| 0.into()), value: request.value.unwrap_or_else(|| 0.into()),
data: request.data.unwrap_or_else(Vec::new), data: request.data.unwrap_or_else(Vec::new),
condition: request.condition, condition: request.condition,
access_list: request.access_list,
})) }))
} }

View File

@ -21,7 +21,10 @@ use bytes::Bytes;
use crypto::DEFAULT_MAC; use crypto::DEFAULT_MAC;
use ethereum_types::{Address, H256, U256}; use ethereum_types::{Address, H256, U256};
use ethkey::Signature; use ethkey::Signature;
use types::transaction::{Action, SignedTransaction, Transaction}; use jsonrpc_core::{Error, ErrorCode};
use types::transaction::{
AccessListTx, Action, SignedTransaction, Transaction, TypedTransaction, TypedTxId,
};
use jsonrpc_core::Result; use jsonrpc_core::Result;
use v1::helpers::{errors, FilledTransactionRequest}; use v1::helpers::{errors, FilledTransactionRequest};
@ -48,16 +51,28 @@ impl super::Accounts for Signer {
nonce: U256, nonce: U256,
password: SignWith, password: SignWith,
) -> Result<WithToken<SignedTransaction>> { ) -> Result<WithToken<SignedTransaction>> {
let t = Transaction { let legacy_tx = Transaction {
nonce: nonce, nonce,
action: filled.to.map_or(Action::Create, Action::Call), action: filled.to.map_or(Action::Create, Action::Call),
gas: filled.gas, gas: filled.gas,
gas_price: filled.gas_price, gas_price: filled.gas_price,
value: filled.value, value: filled.value,
data: filled.data, data: filled.data,
}; };
let t = match filled.tx_type {
TypedTxId::Legacy => TypedTransaction::Legacy(legacy_tx),
TypedTxId::AccessList => {
if filled.access_list.is_none() {
return Err(Error::new(ErrorCode::InvalidParams));
}
TypedTransaction::AccessList(AccessListTx::new(
legacy_tx,
filled.access_list.unwrap(),
))
}
};
let hash = t.hash(chain_id); let hash = t.signature_hash(chain_id);
let signature = signature(&*self.accounts, filled.from, hash, password)?; let signature = signature(&*self.accounts, filled.from, hash, password)?;
Ok(signature.map(|sig| { Ok(signature.map(|sig| {

View File

@ -400,6 +400,7 @@ pub fn transaction_message(error: &TransactionError) -> String {
NotAllowed => "Transaction is not permitted.".into(), NotAllowed => "Transaction is not permitted.".into(),
TooBig => "Transaction is too big, see chain specification for the limit.".into(), TooBig => "Transaction is too big, see chain specification for the limit.".into(),
InvalidRlp(ref descr) => format!("Invalid RLP data: {}", descr), InvalidRlp(ref descr) => format!("Invalid RLP data: {}", descr),
TransactionTypeNotEnabled => format!("Transaction type is not enabled for current block"),
} }
} }

View File

@ -256,6 +256,7 @@ mod test {
fn request() -> ConfirmationPayload { fn request() -> ConfirmationPayload {
ConfirmationPayload::SendTransaction(FilledTransactionRequest { ConfirmationPayload::SendTransaction(FilledTransactionRequest {
tx_type: Default::default(),
from: Address::from(1), from: Address::from(1),
used_default_from: false, used_default_from: false,
to: Some(Address::from(2)), to: Some(Address::from(2)),
@ -265,6 +266,7 @@ mod test {
data: vec![], data: vec![],
nonce: None, nonce: None,
condition: None, condition: None,
access_list: None,
}) })
} }

View File

@ -15,7 +15,9 @@
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>. // along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use std::cmp::min; use std::cmp::min;
use types::transaction::{Action, SignedTransaction, Transaction}; use types::transaction::{
AccessListTx, Action, SignedTransaction, Transaction, TypedTransaction, TypedTxId,
};
use ethereum_types::U256; use ethereum_types::U256;
use jsonrpc_core::Error; use jsonrpc_core::Error;
@ -25,14 +27,20 @@ pub fn sign_call(request: CallRequest) -> Result<SignedTransaction, Error> {
let max_gas = U256::from(500_000_000); let max_gas = U256::from(500_000_000);
let gas = min(request.gas.unwrap_or(max_gas), max_gas); let gas = min(request.gas.unwrap_or(max_gas), max_gas);
let from = request.from.unwrap_or_default(); let from = request.from.unwrap_or_default();
let tx_legacy = Transaction {
Ok(Transaction {
nonce: request.nonce.unwrap_or_default(), nonce: request.nonce.unwrap_or_default(),
action: request.to.map_or(Action::Create, Action::Call), action: request.to.map_or(Action::Create, Action::Call),
gas, gas,
gas_price: request.gas_price.unwrap_or_default(), gas_price: request.gas_price.unwrap_or_default(),
value: request.value.unwrap_or_default(), value: request.value.unwrap_or_default(),
data: request.data.unwrap_or_default(), data: request.data.unwrap_or_default(),
} };
.fake_sign(from)) let tx_typed = match request.tx_type {
TypedTxId::Legacy => TypedTransaction::Legacy(tx_legacy),
TypedTxId::AccessList => TypedTransaction::AccessList(AccessListTx::new(
tx_legacy,
request.access_list.unwrap_or_default(),
)),
};
Ok(tx_typed.fake_sign(from))
} }

View File

@ -16,12 +16,15 @@
use bytes::Bytes; use bytes::Bytes;
use ethereum_types::{Address, H256, U256}; use ethereum_types::{Address, H256, U256};
use types::transaction::{AccessList, TypedTxId};
use v1::types::{Origin, TransactionCondition}; use v1::types::{Origin, TransactionCondition};
/// Transaction request coming from RPC /// Transaction request coming from RPC
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Default, Eq, PartialEq, Hash)]
pub struct TransactionRequest { pub struct TransactionRequest {
/// type of transaction.
pub tx_type: TypedTxId,
/// Sender /// Sender
pub from: Option<Address>, pub from: Option<Address>,
/// Recipient /// Recipient
@ -38,11 +41,15 @@ pub struct TransactionRequest {
pub nonce: Option<U256>, pub nonce: Option<U256>,
/// Delay until this condition is met. /// Delay until this condition is met.
pub condition: Option<TransactionCondition>, pub condition: Option<TransactionCondition>,
/// Access list
pub access_list: Option<AccessList>,
} }
/// Transaction request coming from RPC with default values filled in. /// Transaction request coming from RPC with default values filled in.
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Default, Eq, PartialEq, Hash)]
pub struct FilledTransactionRequest { pub struct FilledTransactionRequest {
/// type of transaction.
pub tx_type: TypedTxId,
/// Sender /// Sender
pub from: Address, pub from: Address,
/// Indicates if the sender was filled by default value. /// Indicates if the sender was filled by default value.
@ -61,11 +68,14 @@ pub struct FilledTransactionRequest {
pub nonce: Option<U256>, pub nonce: Option<U256>,
/// Delay until this condition is met. /// Delay until this condition is met.
pub condition: Option<TransactionCondition>, pub condition: Option<TransactionCondition>,
/// Access list
pub access_list: Option<AccessList>,
} }
impl From<FilledTransactionRequest> for TransactionRequest { impl From<FilledTransactionRequest> for TransactionRequest {
fn from(r: FilledTransactionRequest) -> Self { fn from(r: FilledTransactionRequest) -> Self {
TransactionRequest { TransactionRequest {
tx_type: r.tx_type,
from: Some(r.from), from: Some(r.from),
to: r.to, to: r.to,
gas_price: Some(r.gas_price), gas_price: Some(r.gas_price),
@ -74,6 +84,7 @@ impl From<FilledTransactionRequest> for TransactionRequest {
data: Some(r.data), data: Some(r.data),
nonce: r.nonce, nonce: r.nonce,
condition: r.condition, condition: r.condition,
access_list: r.access_list,
} }
} }
} }
@ -81,6 +92,8 @@ impl From<FilledTransactionRequest> for TransactionRequest {
/// Call request /// Call request
#[derive(Debug, Default, PartialEq)] #[derive(Debug, Default, PartialEq)]
pub struct CallRequest { pub struct CallRequest {
/// type of transaction.
pub tx_type: TypedTxId,
/// From /// From
pub from: Option<Address>, pub from: Option<Address>,
/// To /// To
@ -95,6 +108,8 @@ pub struct CallRequest {
pub data: Option<Vec<u8>>, pub data: Option<Vec<u8>>,
/// Nonce /// Nonce
pub nonce: Option<U256>, pub nonce: Option<U256>,
/// Access list
pub access_list: Option<AccessList>,
} }
/// Confirmation object /// Confirmation object

View File

@ -24,7 +24,6 @@ use std::{
use ethereum_types::{Address, H160, H256, H64, U256, U64}; use ethereum_types::{Address, H160, H256, H64, U256, U64};
use parking_lot::Mutex; use parking_lot::Mutex;
use rlp::Rlp;
use ethash::{self, SeedHashCompute}; use ethash::{self, SeedHashCompute};
use ethcore::{ use ethcore::{
@ -42,7 +41,7 @@ use types::{
encoded, encoded,
filter::Filter as EthcoreFilter, filter::Filter as EthcoreFilter,
header::Header, header::Header,
transaction::{LocalizedTransaction, SignedTransaction}, transaction::{LocalizedTransaction, SignedTransaction, TypedTransaction},
BlockNumber as EthBlockNumber, BlockNumber as EthBlockNumber,
}; };
@ -1065,8 +1064,7 @@ where
} }
fn send_raw_transaction(&self, raw: Bytes) -> Result<H256> { fn send_raw_transaction(&self, raw: Bytes) -> Result<H256> {
Rlp::new(&raw.into_vec()) TypedTransaction::decode(&raw.into_vec())
.as_val()
.map_err(errors::rlp) .map_err(errors::rlp)
.and_then(|tx| SignedTransaction::new(tx).map_err(errors::transaction)) .and_then(|tx| SignedTransaction::new(tx).map_err(errors::transaction))
.and_then(|signed_transaction| { .and_then(|signed_transaction| {

View File

@ -22,8 +22,7 @@ use ethereum_types::U256;
use ethkey; use ethkey;
use parity_runtime::Executor; use parity_runtime::Executor;
use parking_lot::Mutex; use parking_lot::Mutex;
use rlp::Rlp; use types::transaction::{PendingTransaction, SignedTransaction, TypedTransaction};
use types::transaction::{PendingTransaction, SignedTransaction};
use jsonrpc_core::{ use jsonrpc_core::{
futures::{future, future::Either, Future, IntoFuture}, futures::{future, future::Either, Future, IntoFuture},
@ -161,17 +160,17 @@ impl<D: Dispatcher + 'static> SignerClient<D> {
where where
F: FnOnce(PendingTransaction) -> Result<ConfirmationResponse>, F: FnOnce(PendingTransaction) -> Result<ConfirmationResponse>,
{ {
let signed_transaction = Rlp::new(&bytes.0).as_val().map_err(errors::rlp)?; let signed_transaction = TypedTransaction::decode(&bytes.0).map_err(errors::rlp)?;
let signed_transaction = SignedTransaction::new(signed_transaction) let signed_transaction = SignedTransaction::new(signed_transaction)
.map_err(|e| errors::invalid_params("Invalid signature.", e))?; .map_err(|e| errors::invalid_params("Invalid signature.", e))?;
let sender = signed_transaction.sender(); let sender = signed_transaction.sender();
// Verification // Verification
let sender_matches = sender == request.from; let sender_matches = sender == request.from;
let data_matches = signed_transaction.data == request.data; let data_matches = signed_transaction.tx().data == request.data;
let value_matches = signed_transaction.value == request.value; let value_matches = signed_transaction.tx().value == request.value;
let nonce_matches = match request.nonce { let nonce_matches = match request.nonce {
Some(nonce) => signed_transaction.nonce == nonce, Some(nonce) => signed_transaction.tx().nonce == nonce,
None => true, None => true,
}; };

View File

@ -22,8 +22,7 @@ use ethcore::client::{
BlockChainClient, BlockId, Call, CallAnalytics, StateClient, StateInfo, TraceId, TransactionId, BlockChainClient, BlockId, Call, CallAnalytics, StateClient, StateInfo, TraceId, TransactionId,
}; };
use ethereum_types::H256; use ethereum_types::H256;
use rlp::Rlp; use types::transaction::{SignedTransaction, TypedTransaction};
use types::transaction::SignedTransaction;
use jsonrpc_core::Result; use jsonrpc_core::Result;
use v1::{ use v1::{
@ -194,9 +193,8 @@ where
) -> Result<TraceResults> { ) -> Result<TraceResults> {
let block = block.unwrap_or_default(); let block = block.unwrap_or_default();
let tx = Rlp::new(&raw_transaction.into_vec()) let tx = TypedTransaction::decode(&raw_transaction.0)
.as_val() .map_err(|e| errors::invalid_params("Transaction is not in valid Format", e))?;
.map_err(|e| errors::invalid_params("Transaction is not valid RLP", e))?;
let signed = SignedTransaction::new(tx).map_err(errors::transaction)?; let signed = SignedTransaction::new(tx).map_err(errors::transaction)?;
let id = match block { let id = match block {

View File

@ -30,14 +30,13 @@ use ethereum_types::{Address, Bloom, H160, H256, U256};
use miner::external::ExternalMiner; use miner::external::ExternalMiner;
use parity_runtime::Runtime; use parity_runtime::Runtime;
use parking_lot::Mutex; use parking_lot::Mutex;
use rlp;
use rustc_hex::{FromHex, ToHex}; use rustc_hex::{FromHex, ToHex};
use sync::SyncState; use sync::SyncState;
use types::{ use types::{
ids::{BlockId, TransactionId}, ids::{BlockId, TransactionId},
log_entry::{LocalizedLogEntry, LogEntry}, log_entry::{LocalizedLogEntry, LogEntry},
receipt::{LocalizedReceipt, RichReceipt, TransactionOutcome}, receipt::{LocalizedReceipt, RichReceipt, TransactionOutcome},
transaction::{Action, Transaction}, transaction::{Action, Transaction, TypedTransaction, TypedTxId},
}; };
use jsonrpc_core::IoHandler; use jsonrpc_core::IoHandler;
@ -694,13 +693,12 @@ fn rpc_eth_transaction_count_by_number_pending() {
#[test] #[test]
fn rpc_eth_pending_transaction_by_hash() { fn rpc_eth_pending_transaction_by_hash() {
use ethereum_types::H256; use ethereum_types::H256;
use rlp;
use types::transaction::SignedTransaction; use types::transaction::SignedTransaction;
let tester = EthTester::default(); let tester = EthTester::default();
{ {
let bytes = FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap(); let bytes = FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap();
let tx = rlp::decode(&bytes).expect("decoding failure"); let tx = TypedTransaction::decode(&bytes).expect("decoding failure");
let tx = SignedTransaction::new(tx).unwrap(); let tx = SignedTransaction::new(tx).unwrap();
tester tester
.miner .miner
@ -1050,6 +1048,23 @@ fn rpc_eth_estimate_gas_default_block() {
fn rpc_eth_send_raw_transaction_error() { fn rpc_eth_send_raw_transaction_error() {
let tester = EthTester::default(); let tester = EthTester::default();
let req = r#"{
"jsonrpc": "2.0",
"method": "eth_sendRawTransaction",
"params": [
"0x1123"
],
"id": 1
}"#;
let res = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid RLP.","data":"Custom(\"Unknown transaction\")"},"id":1}"#.into();
assert_eq!(tester.io.handle_request_sync(&req), Some(res));
}
#[test]
fn rpc_eth_send_raw_01_transaction_error() {
let tester = EthTester::default();
let req = r#"{ let req = r#"{
"jsonrpc": "2.0", "jsonrpc": "2.0",
"method": "eth_sendRawTransaction", "method": "eth_sendRawTransaction",
@ -1075,7 +1090,7 @@ fn rpc_eth_send_raw_transaction() {
.unlock_account_permanently(address, "abcd".into()) .unlock_account_permanently(address, "abcd".into())
.unwrap(); .unwrap();
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: U256::zero(), nonce: U256::zero(),
gas_price: U256::from(0x9184e72a000u64), gas_price: U256::from(0x9184e72a000u64),
gas: U256::from(0x76c0), gas: U256::from(0x76c0),
@ -1084,14 +1099,14 @@ fn rpc_eth_send_raw_transaction() {
), ),
value: U256::from(0x9184e72au64), value: U256::from(0x9184e72au64),
data: vec![], data: vec![],
}; });
let signature = tester let signature = tester
.accounts_provider .accounts_provider
.sign(address, None, t.hash(None)) .sign(address, None, t.signature_hash(None))
.unwrap(); .unwrap();
let t = t.with_signature(signature, None); let t = t.with_signature(signature, None);
let rlp = rlp::encode(&t).to_hex(); let rlp = t.encode().to_hex();
let req = r#"{ let req = r#"{
"jsonrpc": "2.0", "jsonrpc": "2.0",
@ -1118,6 +1133,7 @@ fn rpc_eth_transaction_receipt() {
to: Some(H160::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), to: Some(H160::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
transaction_hash: H256::zero(), transaction_hash: H256::zero(),
transaction_index: 0, transaction_index: 0,
transaction_type: TypedTxId::Legacy,
block_hash: H256::from_str( block_hash: H256::from_str(
"ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5", "ed76641c68a1c641aee09a94b3b471f4dc0316efe5ac19cf488e2674cf8d05b5",
) )
@ -1204,6 +1220,7 @@ fn rpc_eth_pending_receipt() {
) )
.unwrap(), .unwrap(),
transaction_index: 0, transaction_index: 0,
transaction_type: TypedTxId::Legacy,
cumulative_gas_used: U256::from(0x20), cumulative_gas_used: U256::from(0x20),
gas_used: U256::from(0x10), gas_used: U256::from(0x10),
contract_address: None, contract_address: None,

View File

@ -21,7 +21,10 @@ use ethstore::ethkey::{Generator, Random};
use miner::pool::local_transactions::Status as LocalTransactionStatus; use miner::pool::local_transactions::Status as LocalTransactionStatus;
use std::sync::Arc; use std::sync::Arc;
use sync::ManageNetwork; use sync::ManageNetwork;
use types::receipt::{LocalizedReceipt, TransactionOutcome}; use types::{
receipt::{LocalizedReceipt, TransactionOutcome},
transaction::TypedTxId,
};
use super::manage_network::TestManageNetwork; use super::manage_network::TestManageNetwork;
use jsonrpc_core::IoHandler; use jsonrpc_core::IoHandler;
@ -368,16 +371,17 @@ fn rpc_parity_transactions_stats() {
#[test] #[test]
fn rpc_parity_local_transactions() { fn rpc_parity_local_transactions() {
use types::transaction::{Transaction, TypedTransaction};
let deps = Dependencies::new(); let deps = Dependencies::new();
let io = deps.default_client(); let io = deps.default_client();
let tx = ::types::transaction::Transaction { let tx = TypedTransaction::Legacy(Transaction {
value: 5.into(), value: 5.into(),
gas: 3.into(), gas: 3.into(),
gas_price: 2.into(), gas_price: 2.into(),
action: ::types::transaction::Action::Create, action: ::types::transaction::Action::Create,
data: vec![1, 2, 3], data: vec![1, 2, 3],
nonce: 0.into(), nonce: 0.into(),
} })
.fake_sign(3.into()); .fake_sign(3.into());
let tx = Arc::new(::miner::pool::VerifiedTransaction::from_pending_block_transaction(tx)); let tx = Arc::new(::miner::pool::VerifiedTransaction::from_pending_block_transaction(tx));
deps.miner deps.miner
@ -466,6 +470,7 @@ fn rpc_parity_block_receipts() {
TransactionId::Hash(1.into()), TransactionId::Hash(1.into()),
LocalizedReceipt { LocalizedReceipt {
transaction_hash: 1.into(), transaction_hash: 1.into(),
transaction_type: TypedTxId::Legacy,
transaction_index: 0, transaction_index: 0,
block_hash: 3.into(), block_hash: 3.into(),
block_number: 0, block_number: 0,

View File

@ -178,7 +178,7 @@ fn rpc_parity_set_hash_content() {
#[test] #[test]
fn rpc_parity_remove_transaction() { fn rpc_parity_remove_transaction() {
use types::transaction::{Action, Transaction}; use types::transaction::{Action, Transaction, TypedTransaction};
let miner = miner_service(); let miner = miner_service();
let client = client_service(); let client = client_service();
@ -187,14 +187,14 @@ fn rpc_parity_remove_transaction() {
let mut io = IoHandler::new(); let mut io = IoHandler::new();
io.extend_with(parity_set_client(&client, &miner, &network).to_delegate()); io.extend_with(parity_set_client(&client, &miner, &network).to_delegate());
let tx = Transaction { let tx = TypedTransaction::Legacy(Transaction {
nonce: 1.into(), nonce: 1.into(),
gas_price: 0x9184e72a000u64.into(), gas_price: 0x9184e72a000u64.into(),
gas: 0x76c0.into(), gas: 0x76c0.into(),
action: Action::Call(5.into()), action: Action::Call(5.into()),
value: 0x9184e72au64.into(), value: 0x9184e72au64.into(),
data: vec![], data: vec![],
}; });
let signed = tx.fake_sign(2.into()); let signed = tx.fake_sign(2.into());
let hash = signed.hash(); let hash = signed.hash();
@ -202,7 +202,7 @@ fn rpc_parity_remove_transaction() {
.to_owned() .to_owned()
+ &format!("0x{:x}", hash) + &format!("0x{:x}", hash)
+ r#""], "id": 1}"#; + r#""], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"chainId":null,"condition":null,"creates":null,"from":"0x0000000000000000000000000000000000000002","gas":"0x76c0","gasPrice":"0x9184e72a000","hash":"0xa2e0da8a8064e0b9f93e95a53c2db6d01280efb8ac72a708d25487e67dd0f8fc","input":"0x","nonce":"0x1","publicKey":null,"r":"0x1","raw":"0xe9018609184e72a0008276c0940000000000000000000000000000000000000005849184e72a80800101","s":"0x1","standardV":"0x4","to":"0x0000000000000000000000000000000000000005","transactionIndex":null,"v":"0x0","value":"0x9184e72a"},"id":1}"#; let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"chainId":null,"condition":null,"creates":null,"from":"0x0000000000000000000000000000000000000002","gas":"0x76c0","gasPrice":"0x9184e72a000","hash":"0x49569012bc8523519642c337fded3f20ba987beab31e14c67223b3d31359956f","input":"0x","nonce":"0x1","publicKey":null,"r":"0x1","raw":"0xe9018609184e72a0008276c0940000000000000000000000000000000000000005849184e72a801f0101","s":"0x1","standardV":"0x4","to":"0x0000000000000000000000000000000000000005","transactionIndex":null,"v":"0x1f","value":"0x9184e72a"},"id":1}"#;
miner.pending_transactions.lock().insert(hash, signed); miner.pending_transactions.lock().insert(hash, signed);
assert_eq!(io.handle_request_sync(&request), Some(response.to_owned())); assert_eq!(io.handle_request_sync(&request), Some(response.to_owned()));

View File

@ -24,7 +24,7 @@ use hash::keccak;
use jsonrpc_core::IoHandler; use jsonrpc_core::IoHandler;
use parity_runtime::Runtime; use parity_runtime::Runtime;
use parking_lot::Mutex; use parking_lot::Mutex;
use types::transaction::{Action, Transaction}; use types::transaction::{Action, Transaction, TypedTransaction};
use ethkey::Secret; use ethkey::Secret;
use serde_json::to_value; use serde_json::to_value;
@ -91,9 +91,6 @@ fn setup_with(c: Config) -> PersonalTester {
tester tester
} }
#[cfg(test)]
use rustc_hex::ToHex;
#[test] #[test]
fn accounts() { fn accounts() {
let tester = setup(); let tester = setup();
@ -265,7 +262,7 @@ fn sign_and_send_test(method: &str) {
"id": 1 "id": 1
}"#; }"#;
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: U256::zero(), nonce: U256::zero(),
gas_price: U256::from(0x9184e72a000u64), gas_price: U256::from(0x9184e72a000u64),
gas: U256::from(0x76c0), gas: U256::from(0x76c0),
@ -274,12 +271,15 @@ fn sign_and_send_test(method: &str) {
), ),
value: U256::from(0x9184e72au64), value: U256::from(0x9184e72au64),
data: vec![], data: vec![],
}; });
tester tester
.accounts .accounts
.unlock_account_temporarily(address, "password123".into()) .unlock_account_temporarily(address, "password123".into())
.unwrap(); .unwrap();
let signature = tester.accounts.sign(address, None, t.hash(None)).unwrap(); let signature = tester
.accounts
.sign(address, None, t.signature_hash(None))
.unwrap();
let t = t.with_signature(signature, None); let t = t.with_signature(signature, None);
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() let response = r#"{"jsonrpc":"2.0","result":""#.to_owned()
@ -293,7 +293,7 @@ fn sign_and_send_test(method: &str) {
tester.miner.increment_nonce(&address); tester.miner.increment_nonce(&address);
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: U256::one(), nonce: U256::one(),
gas_price: U256::from(0x9184e72a000u64), gas_price: U256::from(0x9184e72a000u64),
gas: U256::from(0x76c0), gas: U256::from(0x76c0),
@ -302,12 +302,15 @@ fn sign_and_send_test(method: &str) {
), ),
value: U256::from(0x9184e72au64), value: U256::from(0x9184e72au64),
data: vec![], data: vec![],
}; });
tester tester
.accounts .accounts
.unlock_account_temporarily(address, "password123".into()) .unlock_account_temporarily(address, "password123".into())
.unwrap(); .unwrap();
let signature = tester.accounts.sign(address, None, t.hash(None)).unwrap(); let signature = tester
.accounts
.sign(address, None, t.signature_hash(None))
.unwrap();
let t = t.with_signature(signature, None); let t = t.with_signature(signature, None);
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() let response = r#"{"jsonrpc":"2.0","result":""#.to_owned()

View File

@ -22,8 +22,7 @@ use accounts::AccountProvider;
use ethcore::client::TestBlockChainClient; use ethcore::client::TestBlockChainClient;
use parity_runtime::Runtime; use parity_runtime::Runtime;
use parking_lot::Mutex; use parking_lot::Mutex;
use rlp::encode; use types::transaction::{Action, SignedTransaction, Transaction, TypedTransaction};
use types::transaction::{Action, SignedTransaction, Transaction};
use jsonrpc_core::IoHandler; use jsonrpc_core::IoHandler;
use serde_json; use serde_json;
@ -92,6 +91,7 @@ fn should_return_list_of_items_to_confirm() {
.signer .signer
.add_request( .add_request(
ConfirmationPayload::SendTransaction(FilledTransactionRequest { ConfirmationPayload::SendTransaction(FilledTransactionRequest {
tx_type: Default::default(),
from: Address::from(1), from: Address::from(1),
used_default_from: false, used_default_from: false,
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
@ -101,6 +101,7 @@ fn should_return_list_of_items_to_confirm() {
data: vec![], data: vec![],
nonce: None, nonce: None,
condition: None, condition: None,
access_list: None,
}), }),
Origin::Unknown, Origin::Unknown,
) )
@ -137,6 +138,7 @@ fn should_reject_transaction_from_queue_without_dispatching() {
.signer .signer
.add_request( .add_request(
ConfirmationPayload::SendTransaction(FilledTransactionRequest { ConfirmationPayload::SendTransaction(FilledTransactionRequest {
tx_type: Default::default(),
from: Address::from(1), from: Address::from(1),
used_default_from: false, used_default_from: false,
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
@ -146,6 +148,7 @@ fn should_reject_transaction_from_queue_without_dispatching() {
data: vec![], data: vec![],
nonce: None, nonce: None,
condition: None, condition: None,
access_list: None,
}), }),
Origin::Unknown, Origin::Unknown,
) )
@ -173,6 +176,7 @@ fn should_not_remove_transaction_if_password_is_invalid() {
.signer .signer
.add_request( .add_request(
ConfirmationPayload::SendTransaction(FilledTransactionRequest { ConfirmationPayload::SendTransaction(FilledTransactionRequest {
tx_type: Default::default(),
from: Address::from(1), from: Address::from(1),
used_default_from: false, used_default_from: false,
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
@ -182,6 +186,7 @@ fn should_not_remove_transaction_if_password_is_invalid() {
data: vec![], data: vec![],
nonce: None, nonce: None,
condition: None, condition: None,
access_list: None,
}), }),
Origin::Unknown, Origin::Unknown,
) )
@ -237,6 +242,7 @@ fn should_confirm_transaction_and_dispatch() {
.signer .signer
.add_request( .add_request(
ConfirmationPayload::SendTransaction(FilledTransactionRequest { ConfirmationPayload::SendTransaction(FilledTransactionRequest {
tx_type: Default::default(),
from: address, from: address,
used_default_from: false, used_default_from: false,
to: Some(recipient), to: Some(recipient),
@ -246,24 +252,28 @@ fn should_confirm_transaction_and_dispatch() {
data: vec![], data: vec![],
nonce: None, nonce: None,
condition: None, condition: None,
access_list: None,
}), }),
Origin::Unknown, Origin::Unknown,
) )
.unwrap(); .unwrap();
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: U256::zero(), nonce: U256::zero(),
gas_price: U256::from(0x1000), gas_price: U256::from(0x1000),
gas: U256::from(0x50505), gas: U256::from(0x50505),
action: Action::Call(recipient), action: Action::Call(recipient),
value: U256::from(0x1), value: U256::from(0x1),
data: vec![], data: vec![],
}; });
tester tester
.accounts .accounts
.unlock_account_temporarily(address, "test".into()) .unlock_account_temporarily(address, "test".into())
.unwrap(); .unwrap();
let signature = tester.accounts.sign(address, None, t.hash(None)).unwrap(); let signature = tester
.accounts
.sign(address, None, t.signature_hash(None))
.unwrap();
let t = t.with_signature(signature, None); let t = t.with_signature(signature, None);
assert_eq!(tester.signer.requests().len(), 1); assert_eq!(tester.signer.requests().len(), 1);
@ -297,6 +307,7 @@ fn should_alter_the_sender_and_nonce() {
.signer .signer
.add_request( .add_request(
ConfirmationPayload::SendTransaction(FilledTransactionRequest { ConfirmationPayload::SendTransaction(FilledTransactionRequest {
tx_type: Default::default(),
from: 0.into(), from: 0.into(),
used_default_from: false, used_default_from: false,
to: Some(recipient), to: Some(recipient),
@ -306,24 +317,25 @@ fn should_alter_the_sender_and_nonce() {
data: vec![], data: vec![],
nonce: Some(10.into()), nonce: Some(10.into()),
condition: None, condition: None,
access_list: None,
}), }),
Origin::Unknown, Origin::Unknown,
) )
.unwrap(); .unwrap();
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: U256::zero(), nonce: U256::zero(),
gas_price: U256::from(0x1000), gas_price: U256::from(0x1000),
gas: U256::from(0x50505), gas: U256::from(0x50505),
action: Action::Call(recipient), action: Action::Call(recipient),
value: U256::from(0x1), value: U256::from(0x1),
data: vec![], data: vec![],
}; });
let address = tester.accounts.new_account(&"test".into()).unwrap(); let address = tester.accounts.new_account(&"test".into()).unwrap();
let signature = tester let signature = tester
.accounts .accounts
.sign(address, Some("test".into()), t.hash(None)) .sign(address, Some("test".into()), t.signature_hash(None))
.unwrap(); .unwrap();
let t = t.with_signature(signature, None); let t = t.with_signature(signature, None);
@ -361,6 +373,7 @@ fn should_confirm_transaction_with_token() {
.signer .signer
.add_request( .add_request(
ConfirmationPayload::SendTransaction(FilledTransactionRequest { ConfirmationPayload::SendTransaction(FilledTransactionRequest {
tx_type: Default::default(),
from: address, from: address,
used_default_from: false, used_default_from: false,
to: Some(recipient), to: Some(recipient),
@ -370,22 +383,23 @@ fn should_confirm_transaction_with_token() {
data: vec![], data: vec![],
nonce: None, nonce: None,
condition: None, condition: None,
access_list: None,
}), }),
Origin::Unknown, Origin::Unknown,
) )
.unwrap(); .unwrap();
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: U256::zero(), nonce: U256::zero(),
gas_price: U256::from(0x1000), gas_price: U256::from(0x1000),
gas: U256::from(10_000_000), gas: U256::from(10_000_000),
action: Action::Call(recipient), action: Action::Call(recipient),
value: U256::from(0x1), value: U256::from(0x1),
data: vec![], data: vec![],
}; });
let (signature, token) = tester let (signature, token) = tester
.accounts .accounts
.sign_with_token(address, "test".into(), t.hash(None)) .sign_with_token(address, "test".into(), t.signature_hash(None))
.unwrap(); .unwrap();
let t = t.with_signature(signature, None); let t = t.with_signature(signature, None);
@ -427,6 +441,7 @@ fn should_confirm_transaction_with_rlp() {
.signer .signer
.add_request( .add_request(
ConfirmationPayload::SendTransaction(FilledTransactionRequest { ConfirmationPayload::SendTransaction(FilledTransactionRequest {
tx_type: Default::default(),
from: address, from: address,
used_default_from: false, used_default_from: false,
to: Some(recipient), to: Some(recipient),
@ -436,25 +451,26 @@ fn should_confirm_transaction_with_rlp() {
data: vec![], data: vec![],
nonce: None, nonce: None,
condition: None, condition: None,
access_list: None,
}), }),
Origin::Unknown, Origin::Unknown,
) )
.unwrap(); .unwrap();
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: U256::zero(), nonce: U256::zero(),
gas_price: U256::from(0x1000), gas_price: U256::from(0x1000),
gas: U256::from(10_000_000), gas: U256::from(10_000_000),
action: Action::Call(recipient), action: Action::Call(recipient),
value: U256::from(0x1), value: U256::from(0x1),
data: vec![], data: vec![],
}; });
let signature = tester let signature = tester
.accounts .accounts
.sign(address, Some("test".into()), t.hash(None)) .sign(address, Some("test".into()), t.signature_hash(None))
.unwrap(); .unwrap();
let t = t.with_signature(signature, None); let t = t.with_signature(signature, None);
let rlp = encode(&t); let rlp = t.encode();
assert_eq!(tester.signer.requests().len(), 1); assert_eq!(tester.signer.requests().len(), 1);
@ -491,6 +507,7 @@ fn should_return_error_when_sender_does_not_match() {
.signer .signer
.add_request( .add_request(
ConfirmationPayload::SendTransaction(FilledTransactionRequest { ConfirmationPayload::SendTransaction(FilledTransactionRequest {
tx_type: Default::default(),
from: Address::default(), from: Address::default(),
used_default_from: false, used_default_from: false,
to: Some(recipient), to: Some(recipient),
@ -500,26 +517,30 @@ fn should_return_error_when_sender_does_not_match() {
data: vec![], data: vec![],
nonce: None, nonce: None,
condition: None, condition: None,
access_list: None,
}), }),
Origin::Unknown, Origin::Unknown,
) )
.unwrap(); .unwrap();
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: U256::zero(), nonce: U256::zero(),
gas_price: U256::from(0x1000), gas_price: U256::from(0x1000),
gas: U256::from(10_000_000), gas: U256::from(10_000_000),
action: Action::Call(recipient), action: Action::Call(recipient),
value: U256::from(0x1), value: U256::from(0x1),
data: vec![], data: vec![],
}; });
tester tester
.accounts .accounts
.unlock_account_temporarily(address, "test".into()) .unlock_account_temporarily(address, "test".into())
.unwrap(); .unwrap();
let signature = tester.accounts.sign(address, None, t.hash(None)).unwrap(); let signature = tester
.accounts
.sign(address, None, t.signature_hash(None))
.unwrap();
let t = t.with_signature(signature, None); let t = t.with_signature(signature, None);
let rlp = encode(&t); let rlp = t.encode();
assert_eq!(tester.signer.requests().len(), 1); assert_eq!(tester.signer.requests().len(), 1);
@ -553,6 +574,7 @@ fn should_confirm_sign_transaction_with_rlp() {
.signer .signer
.add_request( .add_request(
ConfirmationPayload::SignTransaction(FilledTransactionRequest { ConfirmationPayload::SignTransaction(FilledTransactionRequest {
tx_type: Default::default(),
from: address, from: address,
used_default_from: false, used_default_from: false,
to: Some(recipient), to: Some(recipient),
@ -562,26 +584,27 @@ fn should_confirm_sign_transaction_with_rlp() {
data: vec![], data: vec![],
nonce: None, nonce: None,
condition: None, condition: None,
access_list: None,
}), }),
Origin::Unknown, Origin::Unknown,
) )
.unwrap(); .unwrap();
assert_eq!(tester.signer.requests().len(), 1); assert_eq!(tester.signer.requests().len(), 1);
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: U256::zero(), nonce: U256::zero(),
gas_price: U256::from(0x1000), gas_price: U256::from(0x1000),
gas: U256::from(10_000_000), gas: U256::from(10_000_000),
action: Action::Call(recipient), action: Action::Call(recipient),
value: U256::from(0x1), value: U256::from(0x1),
data: vec![], data: vec![],
}; });
let signature = tester let signature = tester
.accounts .accounts
.sign(address, Some("test".into()), t.hash(None)) .sign(address, Some("test".into()), t.signature_hash(None))
.unwrap(); .unwrap();
let t = SignedTransaction::new(t.with_signature(signature.clone(), None)).unwrap(); let t = SignedTransaction::new(t.with_signature(signature.clone(), None)).unwrap();
let rlp = encode(&t); let rlp = t.encode();
// when // when
let request = r#"{ let request = r#"{

View File

@ -14,7 +14,6 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>. // along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use rlp;
use std::{str::FromStr, sync::Arc, thread, time::Duration}; use std::{str::FromStr, sync::Arc, thread, time::Duration};
use jsonrpc_core::{futures::Future, IoHandler, Success}; use jsonrpc_core::{futures::Future, IoHandler, Success};
@ -40,7 +39,7 @@ use ethstore::ethkey::{Generator, Random};
use parity_runtime::{Executor, Runtime}; use parity_runtime::{Executor, Runtime};
use parking_lot::Mutex; use parking_lot::Mutex;
use serde_json; use serde_json;
use types::transaction::{Action, SignedTransaction, Transaction}; use types::transaction::{Action, SignedTransaction, Transaction, TypedTransaction};
struct SigningTester { struct SigningTester {
pub runtime: Runtime, pub runtime: Runtime,
@ -379,7 +378,7 @@ fn should_add_sign_transaction_to_the_queue() {
"id": 1 "id": 1
}"#; }"#;
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: U256::one(), nonce: U256::one(),
gas_price: U256::from(0x9184e72a000u64), gas_price: U256::from(0x9184e72a000u64),
gas: U256::from(0x76c0), gas: U256::from(0x76c0),
@ -388,15 +387,15 @@ fn should_add_sign_transaction_to_the_queue() {
), ),
value: U256::from(0x9184e72au64), value: U256::from(0x9184e72au64),
data: vec![], data: vec![],
}; });
let signature = tester let signature = tester
.accounts .accounts
.sign(address, Some("test".into()), t.hash(None)) .sign(address, Some("test".into()), t.signature_hash(None))
.unwrap(); .unwrap();
let t = t.with_signature(signature, None); let t = t.with_signature(signature, None);
let t = SignedTransaction::new(t).unwrap(); let t = SignedTransaction::new(t).unwrap();
let signature = t.signature(); let signature = t.signature();
let rlp = rlp::encode(&t); let rlp = t.encode();
let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned()
+ r#""raw":"0x"# + r#""raw":"0x"#
@ -459,7 +458,7 @@ fn should_dispatch_transaction_if_account_is_unlock() {
.unlock_account_permanently(acc, "test".into()) .unlock_account_permanently(acc, "test".into())
.unwrap(); .unwrap();
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: U256::zero(), nonce: U256::zero(),
gas_price: U256::from(0x9184e72a000u64), gas_price: U256::from(0x9184e72a000u64),
gas: U256::from(0x76c0), gas: U256::from(0x76c0),
@ -468,8 +467,11 @@ fn should_dispatch_transaction_if_account_is_unlock() {
), ),
value: U256::from(0x9184e72au64), value: U256::from(0x9184e72au64),
data: vec![], data: vec![],
}; });
let signature = tester.accounts.sign(acc, None, t.hash(None)).unwrap(); let signature = tester
.accounts
.sign(acc, None, t.signature_hash(None))
.unwrap();
let t = t.with_signature(signature, None); let t = t.with_signature(signature, None);
// when // when

View File

@ -21,9 +21,7 @@ use ethcore::client::TestBlockChainClient;
use ethereum_types::{Address, U256}; use ethereum_types::{Address, U256};
use parity_runtime::Runtime; use parity_runtime::Runtime;
use parking_lot::Mutex; use parking_lot::Mutex;
use rlp; use types::transaction::{Action, Transaction, TypedTransaction};
use rustc_hex::ToHex;
use types::transaction::{Action, Transaction};
use jsonrpc_core::IoHandler; use jsonrpc_core::IoHandler;
use v1::{ use v1::{
@ -117,7 +115,7 @@ fn rpc_eth_send_transaction() {
"id": 1 "id": 1
}"#; }"#;
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: U256::zero(), nonce: U256::zero(),
gas_price: U256::from(0x9184e72a000u64), gas_price: U256::from(0x9184e72a000u64),
gas: U256::from(0x76c0), gas: U256::from(0x76c0),
@ -126,10 +124,10 @@ fn rpc_eth_send_transaction() {
), ),
value: U256::from(0x9184e72au64), value: U256::from(0x9184e72au64),
data: vec![], data: vec![],
}; });
let signature = tester let signature = tester
.accounts_provider .accounts_provider
.sign(address, None, t.hash(None)) .sign(address, None, t.signature_hash(None))
.unwrap(); .unwrap();
let t = t.with_signature(signature, None); let t = t.with_signature(signature, None);
@ -141,7 +139,7 @@ fn rpc_eth_send_transaction() {
tester.miner.increment_nonce(&address); tester.miner.increment_nonce(&address);
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: U256::one(), nonce: U256::one(),
gas_price: U256::from(0x9184e72a000u64), gas_price: U256::from(0x9184e72a000u64),
gas: U256::from(0x76c0), gas: U256::from(0x76c0),
@ -150,10 +148,10 @@ fn rpc_eth_send_transaction() {
), ),
value: U256::from(0x9184e72au64), value: U256::from(0x9184e72au64),
data: vec![], data: vec![],
}; });
let signature = tester let signature = tester
.accounts_provider .accounts_provider
.sign(address, None, t.hash(None)) .sign(address, None, t.signature_hash(None))
.unwrap(); .unwrap();
let t = t.with_signature(signature, None); let t = t.with_signature(signature, None);
@ -166,6 +164,7 @@ fn rpc_eth_send_transaction() {
#[test] #[test]
fn rpc_eth_sign_transaction() { fn rpc_eth_sign_transaction() {
use rustc_hex::ToHex;
let tester = EthTester::default(); let tester = EthTester::default();
let address = tester.accounts_provider.new_account(&"".into()).unwrap(); let address = tester.accounts_provider.new_account(&"".into()).unwrap();
tester tester
@ -188,7 +187,7 @@ fn rpc_eth_sign_transaction() {
"id": 1 "id": 1
}"#; }"#;
let t = Transaction { let t = TypedTransaction::Legacy(Transaction {
nonce: U256::one(), nonce: U256::one(),
gas_price: U256::from(0x9184e72a000u64), gas_price: U256::from(0x9184e72a000u64),
gas: U256::from(0x76c0), gas: U256::from(0x76c0),
@ -197,14 +196,14 @@ fn rpc_eth_sign_transaction() {
), ),
value: U256::from(0x9184e72au64), value: U256::from(0x9184e72au64),
data: vec![], data: vec![],
}; });
let signature = tester let signature = tester
.accounts_provider .accounts_provider
.sign(address, None, t.hash(None)) .sign(address, None, t.signature_hash(None))
.unwrap(); .unwrap();
let t = t.with_signature(signature, None); let t = t.with_signature(signature, None);
let signature = t.signature(); let signature = t.signature();
let rlp = rlp::encode(&t); let rlp = t.encode();
let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned()
+ r#""raw":"0x"# + r#""raw":"0x"#

View File

@ -15,6 +15,7 @@
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>. // along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use ethereum_types::{H160, U256}; use ethereum_types::{H160, U256};
use types::transaction::{AccessList, TypedTxId};
use v1::{helpers::CallRequest as Request, types::Bytes}; use v1::{helpers::CallRequest as Request, types::Bytes};
/// Call request /// Call request
@ -22,6 +23,9 @@ use v1::{helpers::CallRequest as Request, types::Bytes};
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct CallRequest { pub struct CallRequest {
/// transaction type
#[serde(default)]
pub tx_type: TypedTxId,
/// From /// From
pub from: Option<H160>, pub from: Option<H160>,
/// To /// To
@ -36,11 +40,14 @@ pub struct CallRequest {
pub data: Option<Bytes>, pub data: Option<Bytes>,
/// Nonce /// Nonce
pub nonce: Option<U256>, pub nonce: Option<U256>,
/// Access list
pub access_list: Option<AccessList>,
} }
impl Into<Request> for CallRequest { impl Into<Request> for CallRequest {
fn into(self) -> Request { fn into(self) -> Request {
Request { Request {
tx_type: self.tx_type,
from: self.from.map(Into::into), from: self.from.map(Into::into),
to: self.to.map(Into::into), to: self.to.map(Into::into),
gas_price: self.gas_price.map(Into::into), gas_price: self.gas_price.map(Into::into),
@ -48,6 +55,7 @@ impl Into<Request> for CallRequest {
value: self.value.map(Into::into), value: self.value.map(Into::into),
data: self.data.map(Into::into), data: self.data.map(Into::into),
nonce: self.nonce.map(Into::into), nonce: self.nonce.map(Into::into),
access_list: self.access_list.map(Into::into),
} }
} }
} }
@ -76,6 +84,7 @@ mod tests {
assert_eq!( assert_eq!(
deserialized, deserialized,
CallRequest { CallRequest {
tx_type: Default::default(),
from: Some(H160::from(1)), from: Some(H160::from(1)),
to: Some(H160::from(2)), to: Some(H160::from(2)),
gas_price: Some(U256::from(1)), gas_price: Some(U256::from(1)),
@ -83,6 +92,7 @@ mod tests {
value: Some(U256::from(3)), value: Some(U256::from(3)),
data: Some(vec![0x12, 0x34, 0x56].into()), data: Some(vec![0x12, 0x34, 0x56].into()),
nonce: Some(U256::from(4)), nonce: Some(U256::from(4)),
access_list: None,
} }
); );
} }
@ -100,13 +110,15 @@ mod tests {
let deserialized: CallRequest = serde_json::from_str(s).unwrap(); let deserialized: CallRequest = serde_json::from_str(s).unwrap();
assert_eq!(deserialized, CallRequest { assert_eq!(deserialized, CallRequest {
tx_type: Default::default(),
from: Some(H160::from_str("b60e8dd61c5d32be8058bb8eb970870f07233155").unwrap()), from: Some(H160::from_str("b60e8dd61c5d32be8058bb8eb970870f07233155").unwrap()),
to: Some(H160::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), to: Some(H160::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
gas_price: Some(U256::from_str("9184e72a000").unwrap()), gas_price: Some(U256::from_str("9184e72a000").unwrap()),
gas: Some(U256::from_str("76c0").unwrap()), gas: Some(U256::from_str("76c0").unwrap()),
value: Some(U256::from_str("9184e72a").unwrap()), value: Some(U256::from_str("9184e72a").unwrap()),
data: Some("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex().unwrap().into()), data: Some("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex().unwrap().into()),
nonce: None nonce: None,
access_list: None,
}); });
} }
@ -118,6 +130,7 @@ mod tests {
assert_eq!( assert_eq!(
deserialized, deserialized,
CallRequest { CallRequest {
tx_type: Default::default(),
from: Some(H160::from(1)), from: Some(H160::from(1)),
to: None, to: None,
gas_price: None, gas_price: None,
@ -125,6 +138,7 @@ mod tests {
value: None, value: None,
data: None, data: None,
nonce: None, nonce: None,
access_list: None,
} }
); );
} }

View File

@ -331,6 +331,7 @@ mod tests {
id: 15.into(), id: 15.into(),
payload: helpers::ConfirmationPayload::SendTransaction( payload: helpers::ConfirmationPayload::SendTransaction(
helpers::FilledTransactionRequest { helpers::FilledTransactionRequest {
tx_type: Default::default(),
from: 0.into(), from: 0.into(),
used_default_from: false, used_default_from: false,
to: None, to: None,
@ -340,6 +341,7 @@ mod tests {
data: vec![1, 2, 3], data: vec![1, 2, 3],
nonce: Some(1.into()), nonce: Some(1.into()),
condition: None, condition: None,
access_list: None,
}, },
), ),
origin: Origin::Signer { session: 5.into() }, origin: Origin::Signer { session: 5.into() },
@ -360,6 +362,7 @@ mod tests {
id: 15.into(), id: 15.into(),
payload: helpers::ConfirmationPayload::SignTransaction( payload: helpers::ConfirmationPayload::SignTransaction(
helpers::FilledTransactionRequest { helpers::FilledTransactionRequest {
tx_type: Default::default(),
from: 0.into(), from: 0.into(),
used_default_from: false, used_default_from: false,
to: None, to: None,
@ -369,6 +372,7 @@ mod tests {
data: vec![1, 2, 3], data: vec![1, 2, 3],
nonce: Some(1.into()), nonce: Some(1.into()),
condition: None, condition: None,
access_list: None,
}, },
), ),
origin: Origin::Unknown, origin: Origin::Unknown,

View File

@ -15,13 +15,19 @@
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>. // along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use ethereum_types::{Bloom as H2048, H160, H256, U256, U64}; use ethereum_types::{Bloom as H2048, H160, H256, U256, U64};
use types::receipt::{LocalizedReceipt, Receipt as EthReceipt, RichReceipt, TransactionOutcome}; use types::{
receipt::{LocalizedReceipt, RichReceipt, TransactionOutcome, TypedReceipt},
transaction::TypedTxId,
};
use v1::types::Log; use v1::types::Log;
/// Receipt /// Receipt
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Receipt { pub struct Receipt {
/// Transaction Type
#[serde(skip_serializing)]
pub transaction_type: TypedTxId,
/// Transaction Hash /// Transaction Hash
pub transaction_hash: Option<H256>, pub transaction_hash: Option<H256>,
/// Transaction index /// Transaction index
@ -75,6 +81,7 @@ impl From<LocalizedReceipt> for Receipt {
Receipt { Receipt {
to: r.to.map(Into::into), to: r.to.map(Into::into),
from: Some(r.from), from: Some(r.from),
transaction_type: r.transaction_type,
transaction_hash: Some(r.transaction_hash), transaction_hash: Some(r.transaction_hash),
transaction_index: Some(r.transaction_index.into()), transaction_index: Some(r.transaction_index.into()),
block_hash: Some(r.block_hash), block_hash: Some(r.block_hash),
@ -95,6 +102,7 @@ impl From<RichReceipt> for Receipt {
Receipt { Receipt {
from: Some(r.from), from: Some(r.from),
to: r.to.map(Into::into), to: r.to.map(Into::into),
transaction_type: r.transaction_type,
transaction_hash: Some(r.transaction_hash), transaction_hash: Some(r.transaction_hash),
transaction_index: Some(r.transaction_index.into()), transaction_index: Some(r.transaction_index.into()),
block_hash: None, block_hash: None,
@ -110,11 +118,14 @@ impl From<RichReceipt> for Receipt {
} }
} }
impl From<EthReceipt> for Receipt { impl From<TypedReceipt> for Receipt {
fn from(r: EthReceipt) -> Self { fn from(r: TypedReceipt) -> Self {
let transaction_type = r.tx_type();
let r = r.receipt().clone();
Receipt { Receipt {
from: None, from: None,
to: None, to: None,
transaction_type,
transaction_hash: None, transaction_hash: None,
transaction_index: None, transaction_index: None,
block_hash: None, block_hash: None,
@ -142,6 +153,7 @@ mod tests {
let receipt = Receipt { let receipt = Receipt {
from: None, from: None,
to: None, to: None,
transaction_type: Default::default(),
transaction_hash: Some(0.into()), transaction_hash: Some(0.into()),
transaction_index: Some(0.into()), transaction_index: Some(0.into()),
block_hash: Some( block_hash: Some(

View File

@ -20,7 +20,10 @@ use ethcore::{contract_address, CreateContractAddress};
use ethereum_types::{H160, H256, H512, U256, U64}; use ethereum_types::{H160, H256, H512, U256, U64};
use miner; use miner;
use serde::{ser::SerializeStruct, Serialize, Serializer}; use serde::{ser::SerializeStruct, Serialize, Serializer};
use types::transaction::{Action, LocalizedTransaction, PendingTransaction, SignedTransaction}; use types::transaction::{
AccessList, Action, LocalizedTransaction, PendingTransaction, SignedTransaction,
TypedTransaction,
};
use v1::types::{Bytes, TransactionCondition}; use v1::types::{Bytes, TransactionCondition};
/// Transaction /// Transaction
@ -67,6 +70,12 @@ pub struct Transaction {
pub s: U256, pub s: U256,
/// Transaction activates at specified block. /// Transaction activates at specified block.
pub condition: Option<TransactionCondition>, pub condition: Option<TransactionCondition>,
/// transaction type
#[serde(skip_serializing)]
pub transaction_type: u8,
/// optional access list
#[serde(skip_serializing)]
pub access_list: AccessList,
} }
/// Local Transaction Status /// Local Transaction Status
@ -176,26 +185,35 @@ impl Transaction {
pub fn from_localized(mut t: LocalizedTransaction) -> Transaction { pub fn from_localized(mut t: LocalizedTransaction) -> Transaction {
let signature = t.signature(); let signature = t.signature();
let scheme = CreateContractAddress::FromSenderAndNonce; let scheme = CreateContractAddress::FromSenderAndNonce;
let access_list = if let TypedTransaction::AccessList(al) = t.as_unsigned() {
al.access_list.clone()
} else {
Vec::new()
};
Transaction { Transaction {
hash: t.hash(), hash: t.hash(),
nonce: t.nonce, nonce: t.tx().nonce,
block_hash: Some(t.block_hash), block_hash: Some(t.block_hash),
block_number: Some(t.block_number.into()), block_number: Some(t.block_number.into()),
transaction_index: Some(t.transaction_index.into()), transaction_index: Some(t.transaction_index.into()),
from: t.sender(), from: t.sender(),
to: match t.action { to: match t.tx().action {
Action::Create => None, Action::Create => None,
Action::Call(ref address) => Some(*address), Action::Call(ref address) => Some(*address),
}, },
value: t.value, value: t.tx().value,
gas_price: t.gas_price, gas_price: t.tx().gas_price,
gas: t.gas, gas: t.tx().gas,
input: Bytes::new(t.data.clone()), input: Bytes::new(t.tx().data.clone()),
creates: match t.action { creates: match t.tx().action {
Action::Create => Some(contract_address(scheme, &t.sender(), &t.nonce, &t.data).0), Action::Create => {
Some(contract_address(scheme, &t.sender(), &t.tx().nonce, &t.tx().data).0)
}
Action::Call(_) => None, Action::Call(_) => None,
}, },
raw: ::rlp::encode(&t.signed).into(), raw: Bytes::new(t.signed.encode()),
public_key: t.recover_public().ok().map(Into::into), public_key: t.recover_public().ok().map(Into::into),
chain_id: t.chain_id().map(U64::from), chain_id: t.chain_id().map(U64::from),
standard_v: t.standard_v().into(), standard_v: t.standard_v().into(),
@ -203,6 +221,8 @@ impl Transaction {
r: signature.r().into(), r: signature.r().into(),
s: signature.s().into(), s: signature.s().into(),
condition: None, condition: None,
transaction_type: t.signed.tx_type() as u8,
access_list,
} }
} }
@ -210,26 +230,33 @@ impl Transaction {
pub fn from_signed(t: SignedTransaction) -> Transaction { pub fn from_signed(t: SignedTransaction) -> Transaction {
let signature = t.signature(); let signature = t.signature();
let scheme = CreateContractAddress::FromSenderAndNonce; let scheme = CreateContractAddress::FromSenderAndNonce;
let access_list = if let TypedTransaction::AccessList(al) = t.as_unsigned() {
al.access_list.clone()
} else {
Vec::new()
};
Transaction { Transaction {
hash: t.hash(), hash: t.hash(),
nonce: t.nonce, nonce: t.tx().nonce,
block_hash: None, block_hash: None,
block_number: None, block_number: None,
transaction_index: None, transaction_index: None,
from: t.sender(), from: t.sender(),
to: match t.action { to: match t.tx().action {
Action::Create => None, Action::Create => None,
Action::Call(ref address) => Some(*address), Action::Call(ref address) => Some(*address),
}, },
value: t.value, value: t.tx().value,
gas_price: t.gas_price, gas_price: t.tx().gas_price,
gas: t.gas, gas: t.tx().gas,
input: Bytes::new(t.data.clone()), input: Bytes::new(t.tx().data.clone()),
creates: match t.action { creates: match t.tx().action {
Action::Create => Some(contract_address(scheme, &t.sender(), &t.nonce, &t.data).0), Action::Create => {
Some(contract_address(scheme, &t.sender(), &t.tx().nonce, &t.tx().data).0)
}
Action::Call(_) => None, Action::Call(_) => None,
}, },
raw: ::rlp::encode(&t).into(), raw: t.encode().into(),
public_key: t.public_key().map(Into::into), public_key: t.public_key().map(Into::into),
chain_id: t.chain_id().map(U64::from), chain_id: t.chain_id().map(U64::from),
standard_v: t.standard_v().into(), standard_v: t.standard_v().into(),
@ -237,6 +264,8 @@ impl Transaction {
r: signature.r().into(), r: signature.r().into(),
s: signature.s().into(), s: signature.s().into(),
condition: None, condition: None,
transaction_type: t.tx_type() as u8,
access_list,
} }
} }
@ -265,7 +294,7 @@ impl LocalTransactionStatus {
Canceled(tx) => LocalTransactionStatus::Canceled(convert(tx)), Canceled(tx) => LocalTransactionStatus::Canceled(convert(tx)),
Replaced { old, new } => LocalTransactionStatus::Replaced( Replaced { old, new } => LocalTransactionStatus::Replaced(
convert(old), convert(old),
new.signed().gas_price, new.signed().tx().gas_price,
new.signed().hash(), new.signed().hash(),
), ),
} }

View File

@ -18,6 +18,7 @@
use ansi_term::Colour; use ansi_term::Colour;
use ethereum_types::{H160, U256}; use ethereum_types::{H160, U256};
use types::transaction::{AccessList, TypedTxId};
use v1::{ use v1::{
helpers, helpers,
types::{Bytes, TransactionCondition}, types::{Bytes, TransactionCondition},
@ -30,6 +31,10 @@ use std::fmt;
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct TransactionRequest { pub struct TransactionRequest {
/// type of transaction. If none we assume it is legacy
#[serde(default)]
#[serde(skip_serializing)]
pub tx_type: TypedTxId,
/// Sender /// Sender
pub from: Option<H160>, pub from: Option<H160>,
/// Recipient /// Recipient
@ -46,6 +51,9 @@ pub struct TransactionRequest {
pub nonce: Option<U256>, pub nonce: Option<U256>,
/// Delay until this block condition. /// Delay until this block condition.
pub condition: Option<TransactionCondition>, pub condition: Option<TransactionCondition>,
/// Access list
#[serde(skip_serializing)]
pub access_list: Option<AccessList>,
} }
pub fn format_ether(i: U256) -> String { pub fn format_ether(i: U256) -> String {
@ -97,6 +105,7 @@ impl fmt::Display for TransactionRequest {
impl From<helpers::TransactionRequest> for TransactionRequest { impl From<helpers::TransactionRequest> for TransactionRequest {
fn from(r: helpers::TransactionRequest) -> Self { fn from(r: helpers::TransactionRequest) -> Self {
TransactionRequest { TransactionRequest {
tx_type: r.tx_type,
from: r.from.map(Into::into), from: r.from.map(Into::into),
to: r.to.map(Into::into), to: r.to.map(Into::into),
gas_price: r.gas_price.map(Into::into), gas_price: r.gas_price.map(Into::into),
@ -105,6 +114,7 @@ impl From<helpers::TransactionRequest> for TransactionRequest {
data: r.data.map(Into::into), data: r.data.map(Into::into),
nonce: r.nonce.map(Into::into), nonce: r.nonce.map(Into::into),
condition: r.condition.map(Into::into), condition: r.condition.map(Into::into),
access_list: r.access_list.map(Into::into),
} }
} }
} }
@ -112,6 +122,7 @@ impl From<helpers::TransactionRequest> for TransactionRequest {
impl From<helpers::FilledTransactionRequest> for TransactionRequest { impl From<helpers::FilledTransactionRequest> for TransactionRequest {
fn from(r: helpers::FilledTransactionRequest) -> Self { fn from(r: helpers::FilledTransactionRequest) -> Self {
TransactionRequest { TransactionRequest {
tx_type: r.tx_type,
from: Some(r.from), from: Some(r.from),
to: r.to, to: r.to,
gas_price: Some(r.gas_price), gas_price: Some(r.gas_price),
@ -120,6 +131,7 @@ impl From<helpers::FilledTransactionRequest> for TransactionRequest {
data: Some(r.data.into()), data: Some(r.data.into()),
nonce: r.nonce, nonce: r.nonce,
condition: r.condition, condition: r.condition,
access_list: r.access_list,
} }
} }
} }
@ -127,6 +139,7 @@ impl From<helpers::FilledTransactionRequest> for TransactionRequest {
impl Into<helpers::TransactionRequest> for TransactionRequest { impl Into<helpers::TransactionRequest> for TransactionRequest {
fn into(self) -> helpers::TransactionRequest { fn into(self) -> helpers::TransactionRequest {
helpers::TransactionRequest { helpers::TransactionRequest {
tx_type: self.tx_type,
from: self.from.map(Into::into), from: self.from.map(Into::into),
to: self.to.map(Into::into), to: self.to.map(Into::into),
gas_price: self.gas_price.map(Into::into), gas_price: self.gas_price.map(Into::into),
@ -135,6 +148,7 @@ impl Into<helpers::TransactionRequest> for TransactionRequest {
data: self.data.map(Into::into), data: self.data.map(Into::into),
nonce: self.nonce.map(Into::into), nonce: self.nonce.map(Into::into),
condition: self.condition.map(Into::into), condition: self.condition.map(Into::into),
access_list: self.access_list.map(Into::into),
} }
} }
} }
@ -165,6 +179,7 @@ mod tests {
assert_eq!( assert_eq!(
deserialized, deserialized,
TransactionRequest { TransactionRequest {
tx_type: Default::default(),
from: Some(H160::from(1)), from: Some(H160::from(1)),
to: Some(H160::from(2)), to: Some(H160::from(2)),
gas_price: Some(U256::from(1)), gas_price: Some(U256::from(1)),
@ -173,6 +188,7 @@ mod tests {
data: Some(vec![0x12, 0x34, 0x56].into()), data: Some(vec![0x12, 0x34, 0x56].into()),
nonce: Some(U256::from(4)), nonce: Some(U256::from(4)),
condition: Some(TransactionCondition::Number(0x13)), condition: Some(TransactionCondition::Number(0x13)),
access_list: None,
} }
); );
} }
@ -190,6 +206,7 @@ mod tests {
let deserialized: TransactionRequest = serde_json::from_str(s).unwrap(); let deserialized: TransactionRequest = serde_json::from_str(s).unwrap();
assert_eq!(deserialized, TransactionRequest { assert_eq!(deserialized, TransactionRequest {
tx_type: Default::default(),
from: Some(H160::from_str("b60e8dd61c5d32be8058bb8eb970870f07233155").unwrap()), from: Some(H160::from_str("b60e8dd61c5d32be8058bb8eb970870f07233155").unwrap()),
to: Some(H160::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()), to: Some(H160::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
gas_price: Some(U256::from_str("9184e72a000").unwrap()), gas_price: Some(U256::from_str("9184e72a000").unwrap()),
@ -198,6 +215,7 @@ mod tests {
data: Some("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex().unwrap().into()), data: Some("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex().unwrap().into()),
nonce: None, nonce: None,
condition: None, condition: None,
access_list: None,
}); });
} }
@ -209,6 +227,7 @@ mod tests {
assert_eq!( assert_eq!(
deserialized, deserialized,
TransactionRequest { TransactionRequest {
tx_type: Default::default(),
from: Some(H160::from(1).into()), from: Some(H160::from(1).into()),
to: None, to: None,
gas_price: None, gas_price: None,
@ -217,6 +236,7 @@ mod tests {
data: None, data: None,
nonce: None, nonce: None,
condition: None, condition: None,
access_list: None,
} }
); );
} }
@ -236,6 +256,7 @@ mod tests {
assert_eq!( assert_eq!(
deserialized, deserialized,
TransactionRequest { TransactionRequest {
tx_type: Default::default(),
from: Some(H160::from_str("b5f7502a2807cb23615c7456055e1d65b2508625").unwrap()), from: Some(H160::from_str("b5f7502a2807cb23615c7456055e1d65b2508625").unwrap()),
to: Some(H160::from_str("895d32f2db7d01ebb50053f9e48aacf26584fe40").unwrap()), to: Some(H160::from_str("895d32f2db7d01ebb50053f9e48aacf26584fe40").unwrap()),
gas_price: Some(U256::from_str("0ba43b7400").unwrap()), gas_price: Some(U256::from_str("0ba43b7400").unwrap()),
@ -244,6 +265,7 @@ mod tests {
data: Some(vec![0x85, 0x95, 0xba, 0xb1].into()), data: Some(vec![0x85, 0x95, 0xba, 0xb1].into()),
nonce: None, nonce: None,
condition: None, condition: None,
access_list: None,
} }
); );
} }

View File

@ -16,7 +16,7 @@
use bytes::Bytes; use bytes::Bytes;
use call_contract::RegistryInfo; use call_contract::RegistryInfo;
use common_types::transaction::{Action, SignedTransaction, Transaction}; use common_types::transaction::{Action, SignedTransaction, Transaction, TypedTransaction};
use ethcore::{ use ethcore::{
client::{BlockChainClient, BlockId, ChainInfo, Client, Nonce}, client::{BlockChainClient, BlockId, ChainInfo, Client, Nonce},
miner::{Miner, MinerService}, miner::{Miner, MinerService},
@ -89,16 +89,18 @@ impl TrustedClient {
.upgrade() .upgrade()
.ok_or_else(|| Error::Internal("cannot submit tx when miner is offline".into()))?; .ok_or_else(|| Error::Internal("cannot submit tx when miner is offline".into()))?;
let engine = client.engine(); let engine = client.engine();
let transaction = Transaction { let transaction = TypedTransaction::Legacy(Transaction {
nonce: client.latest_nonce(&self.self_key_pair.address()), nonce: client.latest_nonce(&self.self_key_pair.address()),
action: Action::Call(contract), action: Action::Call(contract),
gas: miner.authoring_params().gas_range_target.0, gas: miner.authoring_params().gas_range_target.0,
gas_price: miner.sensible_gas_price(), gas_price: miner.sensible_gas_price(),
value: Default::default(), value: Default::default(),
data: tx_data, data: tx_data,
}; });
let chain_id = engine.signing_chain_id(&client.latest_env_info()); let chain_id = engine.signing_chain_id(&client.latest_env_info());
let signature = self.self_key_pair.sign(&transaction.hash(chain_id))?; let signature = self
.self_key_pair
.sign(&transaction.signature_hash(chain_id))?;
let signed = SignedTransaction::new(transaction.with_signature(signature, chain_id))?; let signed = SignedTransaction::new(transaction.with_signature(signature, chain_id))?;
miner miner
.import_own_transaction(&*client, signed.into()) .import_own_transaction(&*client, signed.into())