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_derive",
"rustc-hex 1.0.0",
"serde",
"serde_json",
"serde_repr",
"unexpected",
]
@ -4274,6 +4277,17 @@ dependencies = [
"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]]
name = "sha-1"
version = "0.8.1"

View File

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

View File

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

View File

@ -18,12 +18,14 @@
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 heapsize::HeapSizeOf;
use kvdb::PREFIX_LEN as DB_PREFIX_LEN;
use rlp;
use rlp_derive::{RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper};
use rlp_derive::{RlpDecodable, RlpEncodable};
use crate::db::Key;
@ -235,15 +237,27 @@ impl HeapSizeOf for TransactionAddress {
}
/// Contains all block receipts.
#[derive(Clone, RlpEncodableWrapper, RlpDecodableWrapper)]
#[derive(Clone)]
pub struct BlockReceipts {
/// 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 {
/// Create new block receipts wrapper.
pub fn new(receipts: Vec<Receipt>) -> Self {
pub fn new(receipts: Vec<TypedReceipt>) -> Self {
BlockReceipts { receipts: receipts }
}
}

View File

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

View File

@ -48,10 +48,10 @@ use verification::PreverifiedBlock;
use vm::{EnvInfo, LastHashes};
use hash::keccak;
use rlp::{encode_list, Encodable, RlpStream};
use rlp::{encode_list, RlpStream};
use types::{
header::{ExtendedHeader, Header},
receipt::{Receipt, TransactionOutcome},
receipt::{TransactionOutcome, TypedReceipt},
transaction::{Error as TransactionError, SignedTransaction},
};
@ -99,7 +99,7 @@ pub struct ExecutedBlock {
/// Uncles.
pub uncles: Vec<Header>,
/// Transaction receipts.
pub receipts: Vec<Receipt>,
pub receipts: Vec<TypedReceipt>,
/// Hashes of already executed transactions.
pub transactions_set: HashSet<H256>,
/// Underlaying state.
@ -253,7 +253,7 @@ impl<'x> OpenBlock<'x> {
&mut self,
t: SignedTransaction,
h: Option<H256>,
) -> Result<&Receipt, Error> {
) -> Result<&TypedReceipt, Error> {
if self.block.transactions_set.contains(&t.hash()) {
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
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);
s.block.header.set_uncles_hash(keccak(&uncle_bytes));
s.block.header.set_state_root(s.block.state.root().clone());
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
.header
@ -453,7 +453,7 @@ impl LockedBlock {
receipt.outcome = TransactionOutcome::Unknown;
}
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 {
let mut block_rlp = RlpStream::new_list(3);
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.out()
}

View File

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

View File

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

View File

@ -36,7 +36,7 @@ use itertools::Itertools;
use kvdb::DBValue;
use kvdb_memorydb;
use parking_lot::RwLock;
use rlp::{Rlp, RlpStream};
use rlp::RlpStream;
use rustc_hex::FromHex;
use types::{
basic_account::BasicAccount,
@ -45,8 +45,11 @@ use types::{
header::Header,
log_entry::LocalizedLogEntry,
pruning_info::PruningInfo,
receipt::{LocalizedReceipt, Receipt, TransactionOutcome},
transaction::{self, Action, LocalizedTransaction, SignedTransaction, Transaction},
receipt::{LegacyReceipt, LocalizedReceipt, TransactionOutcome, TypedReceipt},
transaction::{
self, Action, LocalizedTransaction, SignedTransaction, Transaction, TypedTransaction,
TypedTxId,
},
view,
views::BlockView,
BlockNumber,
@ -296,16 +299,16 @@ impl TestBlockChainClient {
for _ in 0..num_transactions {
// Update nonces value
let tx = Transaction {
let tx = TypedTransaction::Legacy(Transaction {
action: Action::Create,
value: U256::from(100),
data: "3331600055".from_hex().unwrap(),
gas: U256::from(100_000),
gas_price: U256::from(200_000_000_000u64),
nonce: nonce,
};
});
let signed_tx = tx.sign(keypair.secret(), None);
txs.append(&signed_tx);
signed_tx.rlp_append(&mut txs);
nonce += U256::one();
}
@ -369,14 +372,14 @@ impl TestBlockChainClient {
/// 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 {
let keypair = Random.generate().unwrap();
let tx = Transaction {
let tx = TypedTransaction::Legacy(Transaction {
action: Action::Create,
value: U256::from(100),
data: "3331600055".from_hex().unwrap(),
gas: U256::from(100_000),
gas_price: gas_price,
nonce: U256::zero(),
};
});
let signed_tx = tx.sign(keypair.secret(), None);
self.set_balance(signed_tx.sender(), 10_000_000_000_000_000_000u64.into());
let hash = signed_tx.hash();
@ -938,10 +941,13 @@ impl BlockChainClient for TestBlockChainClient {
fn block_receipts(&self, hash: &H256) -> Option<BlockReceipts> {
// starts with 'f' ?
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()),
U256::zero(),
vec![],
),
)]);
return Some(receipt);
}
@ -1027,16 +1033,20 @@ impl BlockChainClient for TestBlockChainClient {
}
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),
action: Action::Call(address),
gas: self.spec.gas_limit,
gas_price: U256::zero(),
value: U256::default(),
data: data,
};
});
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();
self.miner.import_own_transaction(self, signed.into())
}
@ -1051,7 +1061,7 @@ impl IoClient for TestBlockChainClient {
// import right here
let txs = transactions
.into_iter()
.filter_map(|bytes| Rlp::new(&bytes).as_val().ok())
.filter_map(|bytes| TypedTransaction::decode(&bytes).ok())
.collect();
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 types::{
header::Header,
transaction::{Action, Transaction},
transaction::{Action, Transaction, TypedTransaction},
};
fn aura<F>(f: F) -> Arc<AuthorityRound>
@ -2287,14 +2287,14 @@ mod tests {
)
.unwrap();
b2.push_transaction(
Transaction {
TypedTransaction::Legacy(Transaction {
action: Action::Create,
nonce: U256::from(0),
gas_price: U256::from(3000),
gas: U256::from(53_000),
value: U256::from(1),
data: vec![],
}
})
.fake_sign(addr2),
None,
)

View File

@ -605,8 +605,10 @@ pub trait EthEngine: Engine<::machine::EthereumMachine> {
fn decode_transaction(
&self,
transaction: &[u8],
best_block_number: BlockNumber,
) -> 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 parking_lot::RwLock;
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 super::{simple_list::SimpleList, SystemCall, ValidatorSet};
@ -91,7 +91,7 @@ fn check_first_proof(
old_header: Header,
state_items: &[DBValue],
) -> 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.
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 from = Address::default();
let tx = Transaction {
let tx = TypedTransaction::Legacy(Transaction {
nonce: machine.account_start_nonce(number),
action: Action::Call(contract_address),
gas: PROVIDED_GAS.into(),
gas_price: U256::default(),
value: U256::default(),
data,
}
})
.fake_sign(from);
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.
// checking will involve ensuring that the receipts match the header and
// 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);
stream.append(header).append_list(receipts);
stream.append(header);
TypedReceipt::rlp_append_list(&mut stream, receipts);
stream.drain()
}
fn decode_proof(rlp: &Rlp) -> Result<(Header, Vec<Receipt>), ::error::Error> {
Ok((rlp.val_at(0)?, rlp.list_at(1)?))
fn decode_proof(rlp: &Rlp) -> Result<(Header, Vec<TypedReceipt>), ::error::Error> {
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
@ -265,7 +266,7 @@ impl ValidatorSafeContract {
&self,
bloom: Bloom,
header: &Header,
receipts: &[Receipt],
receipts: &[TypedReceipt],
) -> Option<SimpleList> {
let check_log = |log: &LogEntry| {
log.address == self.contract_address
@ -406,7 +407,7 @@ impl ValidatorSet for ValidatorSafeContract {
// ensure receipts match header.
// 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() {
return Err(::error::BlockError::InvalidReceiptsRoot(Mismatch {
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 types::{
ids::BlockId,
transaction::{Action, Transaction},
transaction::{Action, Transaction, TypedTransaction},
};
use verification::queue::kind::blocks::Unverified;
@ -537,7 +538,7 @@ mod tests {
client.miner().set_author(miner::Author::Sealer(signer));
// Remove "1" validator.
let tx = Transaction {
let tx = TypedTransaction::Legacy(Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 500_000.into(),
@ -546,7 +547,7 @@ mod tests {
data: "bfc708a000000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1"
.from_hex()
.unwrap(),
}
})
.sign(&s0, Some(chain_id));
client
.miner()
@ -555,7 +556,7 @@ mod tests {
EngineClient::update_sealing(&*client, ForceUpdateSealing::No);
assert_eq!(client.chain_info().best_block_number, 1);
// Add "1" validator back in.
let tx = Transaction {
let tx = TypedTransaction::Legacy(Transaction {
nonce: 1.into(),
gas_price: 0.into(),
gas: 500_000.into(),
@ -564,7 +565,7 @@ mod tests {
data: "4d238c8e00000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1"
.from_hex()
.unwrap(),
}
})
.sign(&s0, Some(chain_id));
client
.miner()
@ -582,14 +583,14 @@ mod tests {
// Switch back to the added validator, since the state is updated.
let signer = Box::new((tap.clone(), v1, "".into()));
client.miner().set_author(miner::Author::Sealer(signer));
let tx = Transaction {
let tx = TypedTransaction::Legacy(Transaction {
nonce: 2.into(),
gas_price: 0.into(),
gas: 21000.into(),
action: Action::Call(Address::default()),
value: 0.into(),
data: Vec::new(),
}
})
.sign(&s0, Some(chain_id));
client
.miner()

View File

@ -28,7 +28,7 @@ use state::{Backend as StateBackend, CleanupMode, State, Substate};
use std::{cmp, sync::Arc};
use trace::{self, Tracer, VMTracer};
use transaction_ext::Transaction;
use types::transaction::{Action, SignedTransaction};
use types::transaction::{Action, SignedTransaction, TypedTransaction};
use vm::{
self, AccessList, ActionParams, ActionValue, CleanDustMode, CreateContractAddress, EnvInfo,
ResumeCall, ResumeCreate, ReturnData, Schedule, TrapError,
@ -1109,7 +1109,10 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
{
let sender = t.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 {
// give the sender a sufficient balance
self.state
@ -1132,16 +1135,51 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
T: Tracer,
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 nonce = self.state.nonce(&sender)?;
let schedule = self.schedule;
let base_gas_required = U256::from(t.gas_required(&schedule));
let mut base_gas_required = U256::from(t.tx().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 {
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);
}
let init_gas = t.gas - base_gas_required;
let init_gas = t.tx().gas - base_gas_required;
// validate transaction nonce
if check_nonce && t.nonce != nonce {
if check_nonce && t.tx().nonce != nonce {
return Err(ExecutionError::InvalidNonce {
expected: nonce,
got: t.nonce,
got: t.tx().nonce,
});
}
// 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 {
gas_limit: self.info.gas_limit,
gas_used: self.info.gas_used,
gas: t.gas,
gas: t.tx().gas,
});
}
// TODO: we might need bigints here, or at least check overflows.
let balance = self.state.balance(&sender)?;
let gas_cost = t.gas.full_mul(t.gas_price);
let total_cost = U512::from(t.value) + gas_cost;
let gas_cost = t.tx().gas.full_mul(t.tx().gas_price);
let total_cost = U512::from(t.tx().value) + gas_cost;
// avoid unaffordable transactions
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);
// 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),
)?;
let (result, output) = match t.action {
let (result, output) = match t.tx().action {
Action::Create => {
let (new_address, code_hash) = contract_address(
self.machine.create_address_scheme(self.info.number),
&sender,
&nonce,
&t.data,
&t.tx().data,
);
let params = ActionParams {
code_address: new_address.clone(),
@ -1220,9 +1251,9 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
sender: sender.clone(),
origin: sender.clone(),
gas: init_gas,
gas_price: t.gas_price,
value: ActionValue::Transfer(t.value),
code: Some(Arc::new(t.data.clone())),
gas_price: t.tx().gas_price,
value: ActionValue::Transfer(t.tx().value),
code: Some(Arc::new(t.tx().data.clone())),
data: None,
call_type: CallType::None,
params_type: vm::ParamsType::Embedded,
@ -1242,11 +1273,11 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
sender: sender.clone(),
origin: sender.clone(),
gas: init_gas,
gas_price: t.gas_price,
value: ActionValue::Transfer(t.value),
gas_price: t.tx().gas_price,
value: ActionValue::Transfer(t.tx().value),
code: self.state.code(address)?,
code_hash: self.state.code_hash(address)?,
data: Some(t.data.clone()),
data: Some(t.tx().data.clone()),
call_type: CallType::Call,
params_type: vm::ParamsType::Separate,
access_list: access_list,
@ -1445,12 +1476,12 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
Ok(FinalizationResult { gas_left, .. }) => gas_left,
_ => 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_used = t.gas.saturating_sub(gas_left);
let (refund_value, overflow_1) = gas_left.overflowing_mul(t.gas_price);
let (fees_value, overflow_2) = gas_used.overflowing_mul(t.gas_price);
let gas_used = t.tx().gas.saturating_sub(gas_left);
let (refund_value, overflow_1) = gas_left.overflowing_mul(t.tx().gas_price);
let (fees_value, overflow_2) = gas_used.overflowing_mul(t.tx().gas_price);
if overflow_1 || overflow_2 {
return Err(ExecutionError::TransactionMalformed(
"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",
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();
trace!(
@ -1487,7 +1518,11 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
// perform garbage-collection
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 {
None
};
@ -1502,10 +1537,10 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
Err(vm::Error::Internal(msg)) => Err(ExecutionError::Internal(msg)),
Err(exception) => Ok(Executed {
exception: Some(exception),
gas: t.gas,
gas_used: t.gas,
gas: t.tx().gas,
gas_used: t.tx().gas,
refunded: U256::zero(),
cumulative_gas_used: self.info.gas_used + t.gas,
cumulative_gas_used: self.info.gas_used + t.tx().gas,
logs: vec![],
contracts_created: vec![],
output: output,
@ -1519,7 +1554,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
} else {
Some(vm::Error::Reverted)
},
gas: t.gas,
gas: t.tx().gas,
gas_used: gas_used,
refunded: refunded,
cumulative_gas_used: self.info.gas_used + gas_used,
@ -1551,7 +1586,7 @@ mod tests {
trace, ExecutiveTracer, ExecutiveVMTracer, FlatTrace, MemoryDiff, NoopTracer, NoopVMTracer,
StorageDiff, Tracer, VMExecutedOperation, VMOperation, VMTrace, VMTracer,
};
use types::transaction::{Action, Transaction};
use types::transaction::{Action, Transaction, TypedTransaction};
use vm::{ActionParams, ActionValue, CallType, CreateContractAddress, EnvInfo};
fn make_frontier_machine(max_depth: usize) -> EthereumMachine {
@ -2445,14 +2480,14 @@ mod tests {
evm_test_ignore! {test_transact_simple: test_transact_simple_int}
fn test_transact_simple(factory: Factory) {
let keypair = Random.generate().unwrap();
let t = Transaction {
let t = TypedTransaction::Legacy(Transaction {
action: Action::Create,
value: U256::from(17),
data: "3331600055".from_hex().unwrap(),
gas: U256::from(100_000),
gas_price: U256::zero(),
nonce: U256::zero(),
}
})
.sign(keypair.secret(), None);
let sender = t.sender();
let contract = contract_address(
@ -2496,14 +2531,14 @@ mod tests {
evm_test! {test_transact_invalid_nonce: test_transact_invalid_nonce_int}
fn test_transact_invalid_nonce(factory: Factory) {
let keypair = Random.generate().unwrap();
let t = Transaction {
let t = TypedTransaction::Legacy(Transaction {
action: Action::Create,
value: U256::from(17),
data: "3331600055".from_hex().unwrap(),
gas: U256::from(100_000),
gas_price: U256::zero(),
nonce: U256::one(),
}
})
.sign(keypair.secret(), None);
let sender = t.sender();
@ -2535,14 +2570,14 @@ mod tests {
evm_test! {test_transact_gas_limit_reached: test_transact_gas_limit_reached_int}
fn test_transact_gas_limit_reached(factory: Factory) {
let keypair = Random.generate().unwrap();
let t = Transaction {
let t = TypedTransaction::Legacy(Transaction {
action: Action::Create,
value: U256::from(17),
data: "3331600055".from_hex().unwrap(),
gas: U256::from(80_001),
gas_price: U256::zero(),
nonce: U256::zero(),
}
})
.sign(keypair.secret(), None);
let sender = t.sender();
@ -2580,14 +2615,14 @@ mod tests {
evm_test! {test_not_enough_cash: test_not_enough_cash_int}
fn test_not_enough_cash(factory: Factory) {
let keypair = Random.generate().unwrap();
let t = Transaction {
let t = TypedTransaction::Legacy(Transaction {
action: Action::Create,
value: U256::from(18),
data: "3331600055".from_hex().unwrap(),
gas: U256::from(100_000),
gas_price: U256::one(),
nonce: U256::zero(),
}
})
.sign(keypair.secret(), None);
let sender = t.sender();

View File

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

View File

@ -23,11 +23,11 @@ use std::{
};
use ethereum_types::{Address, H256, U256};
use rlp::Rlp;
use types::{
header::Header,
transaction::{
self, SignedTransaction, UnverifiedTransaction, SYSTEM_ADDRESS, UNSIGNED_SENDER,
self, SignedTransaction, TypedTransaction, UnverifiedTransaction, SYSTEM_ADDRESS,
UNSIGNED_SENDER,
},
BlockNumber,
};
@ -455,17 +455,27 @@ impl EthereumMachine {
pub fn decode_transaction(
&self,
transaction: &[u8],
schedule: &Schedule,
) -> Result<UnverifiedTransaction, transaction::Error> {
let rlp = Rlp::new(&transaction);
if rlp.as_raw().len() > self.params().max_transaction_size {
if transaction.len() > self.params().max_transaction_size {
debug!(
"Rejected oversized transaction of {} bytes",
rlp.as_raw().len()
transaction.len()
);
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.
pub bytes: Option<&'a [u8]>,
/// 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.
@ -550,7 +560,7 @@ mod tests {
fn should_disallow_unsigned_transactions() {
let rlp = "ea80843b9aca0083015f90948921ebb5f79e9e3920abe571004d0b1d5119c154865af3107a400080038080";
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 ethparams = get_default_ethash_extensions();

View File

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

View File

@ -195,7 +195,8 @@ where
&self,
transaction: &[u8],
) -> 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 rlp::{DecoderError, Rlp, RlpStream};
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 BLOCK_FIELDS: usize = 2;
@ -62,9 +62,9 @@ impl AbridgedBlock {
.append(&header.extra_data());
// write block values.
stream
.append_list(&block_view.transactions())
.append_list(&block_view.uncles());
TypedTransaction::rlp_append_list(&mut stream, &block_view.transactions());
stream.append_list(&block_view.uncles());
// write seal fields.
for field in seal_fields {
@ -97,10 +97,17 @@ impl AbridgedBlock {
header.set_timestamp(rlp.val_at(6)?);
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)?;
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);
let mut uncles_rlp = RlpStream::new();
@ -131,7 +138,7 @@ mod tests {
use ethereum_types::{Address, H256, U256};
use types::{
block::Block,
transaction::{Action, Transaction},
transaction::{Action, Transaction, TypedTransaction},
view,
views::BlockView,
};
@ -165,24 +172,24 @@ mod tests {
fn with_transactions() {
let mut b = Block::default();
let t1 = Transaction {
let t1 = TypedTransaction::Legacy(Transaction {
action: Action::Create,
nonce: U256::from(42),
gas_price: U256::from(3000),
gas: U256::from(50_000),
value: U256::from(1),
data: b"Hello!".to_vec(),
}
})
.fake_sign(Address::from(0x69));
let t2 = Transaction {
let t2 = TypedTransaction::Legacy(Transaction {
action: Action::Create,
nonce: U256::from(88),
gas_price: U256::from(12345),
gas: U256::from(300000),
value: U256::from(1000000000),
data: "Eep!".into(),
}
})
.fake_sign(Address::from(0x55));
b.transactions.push(t1.into());
@ -191,7 +198,7 @@ mod tests {
let receipts_root = b.header.receipts_root().clone();
b.header
.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);

View File

@ -36,7 +36,9 @@ use ethereum_types::{H256, U256};
use itertools::{Itertools, Position};
use kvdb::KeyValueDB;
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.
/// Chunk format:
@ -114,9 +116,9 @@ impl SnapshotComponents for PoaSnapshot {
rlps.push({
let mut stream = RlpStream::new_list(5);
stream.append(&block.header);
TypedTransaction::rlp_append_list(&mut stream, &block.transactions);
stream
.append(&block.header)
.append_list(&block.transactions)
.append_list(&block.uncles)
.append(&receipts)
.append(&parent_td);
@ -349,11 +351,11 @@ impl Rebuilder for ChunkRebuilder {
let last_rlp = rlp.at(num_items - 1)?;
let block = Block {
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)?,
};
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();

View File

@ -291,8 +291,15 @@ impl Rebuilder for PowRebuilder {
let pair = rlp.at(idx)?;
let abridged_rlp = pair.at(0)?.as_raw().to_owned();
let abridged_block = AbridgedBlock::from_raw(abridged_rlp);
let receipts: Vec<::types::receipt::Receipt> = pair.list_at(1)?;
let receipts_root = ordered_trie_root(pair.at(1)?.iter().map(|r| r.as_raw()));
let receipts = ::types::receipt::TypedReceipt::decode_rlp_list(&pair.at(1)?)?;
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_bytes = encoded::Block::new(block.rlp_bytes());

View File

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

View File

@ -137,6 +137,8 @@ pub struct CommonParams {
pub eip2315_transition: BlockNumber,
/// Number of first block where EIP-2929 rules begin.
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.
pub dust_protection_transition: BlockNumber,
/// 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.have_subs = block_number >= self.eip2315_transition;
schedule.eip2929 = block_number >= self.eip2929_transition;
schedule.eip2930 = block_number >= self.eip2930_transition;
if block_number >= self.eip1884_transition {
schedule.have_selfbalance = true;
@ -370,6 +373,9 @@ impl From<ethjson::spec::Params> for CommonParams {
eip2929_transition: p
.eip2929_transition
.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
.map_or_else(BlockNumber::max_value, Into::into),
@ -956,7 +962,7 @@ impl Spec {
/// initialize genesis epoch data, using in-memory database for
/// constructor.
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();
@ -983,14 +989,14 @@ impl Spec {
};
let from = Address::default();
let tx = Transaction {
let tx = TypedTransaction::Legacy(Transaction {
nonce: self.engine.account_start_nonce(0),
action: Action::Call(a),
gas: U256::max_value(),
gas_price: U256::default(),
value: U256::default(),
data: d,
}
})
.fake_sign(from);
let res = ::state::prove_transaction_virtual(

View File

@ -38,10 +38,11 @@ use state_db::StateDB;
use trace::{self, FlatTrace, VMTrace};
use types::{
basic_account::BasicAccount,
receipt::{Receipt, TransactionOutcome},
receipt::{LegacyReceipt, TransactionOutcome, TypedReceipt},
state_diff::StateDiff,
transaction::SignedTransaction,
};
use vm::EnvInfo;
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.
pub struct ApplyOutcome<T, V> {
/// The receipt for the applied transaction.
pub receipt: Receipt,
pub receipt: TypedReceipt,
/// The output of the applied transaction.
pub output: Bytes,
/// 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 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);
Ok(ApplyOutcome {
@ -1602,7 +1606,7 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5);
let t = Transaction {
let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
@ -1610,7 +1614,7 @@ mod tests {
value: 100.into(),
data: FromHex::from_hex("601080600c6000396000f3006000355415600957005b60203560003555")
.unwrap(),
}
})
.sign(&secret(), None);
state
@ -1667,14 +1671,14 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5);
let t = Transaction {
let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
action: Action::Create,
value: 100.into(),
data: FromHex::from_hex("5b600056").unwrap(),
}
})
.sign(&secret(), None);
state
@ -1706,14 +1710,14 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5);
let t = Transaction {
let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
action: Action::Call(0xa.into()),
value: 100.into(),
data: vec![],
}
})
.sign(&secret(), None);
state
@ -1753,14 +1757,14 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5);
let t = Transaction {
let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
action: Action::Call(0xa.into()),
value: 100.into(),
data: vec![],
}
})
.sign(&secret(), None);
state
@ -1797,14 +1801,14 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = Spec::new_test_machine();
let t = Transaction {
let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
action: Action::Call(0x1.into()),
value: 0.into(),
data: vec![],
}
})
.sign(&secret(), None);
let result = state.apply(&info, &machine, &t, true).unwrap();
@ -1839,14 +1843,14 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = Spec::new_test_machine();
let t = Transaction {
let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
action: Action::Call(0xa.into()),
value: 0.into(),
data: vec![],
}
})
.sign(&secret(), None);
state
@ -1887,14 +1891,14 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = Spec::new_test_machine();
let t = Transaction {
let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
action: Action::Call(0xa.into()),
value: 0.into(),
data: vec![],
}
})
.sign(&secret(), None);
state
@ -1957,14 +1961,14 @@ mod tests {
info.number = 0x789b0;
let machine = Spec::new_test_machine();
let t = Transaction {
let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
action: Action::Call(0xa.into()),
value: 0.into(),
data: vec![],
}
})
.sign(&secret(), None);
state
@ -2029,14 +2033,14 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5);
let t = Transaction {
let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
action: Action::Call(0xa.into()),
value: 100.into(),
data: vec![],
}
})
.sign(&secret(), None);
state
@ -2073,14 +2077,14 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5);
let t = Transaction {
let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
action: Action::Call(0xa.into()),
value: 100.into(),
data: vec![],
}
})
.sign(&secret(), None);
state
@ -2145,14 +2149,14 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5);
let t = Transaction {
let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
action: Action::Call(0xa.into()),
value: 100.into(),
data: vec![],
}
})
.sign(&secret(), None);
state
@ -2210,14 +2214,14 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5);
let t = Transaction {
let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
action: Action::Call(0xa.into()),
value: 100.into(),
data: vec![],
}
})
.sign(&secret(), None);
state
@ -2260,14 +2264,14 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5);
let t = Transaction {
let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
action: Action::Call(0xa.into()),
value: 100.into(),
data: vec![], //600480600b6000396000f35b600056
}
})
.sign(&secret(), None);
state
@ -2328,14 +2332,14 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5);
let t = Transaction {
let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
action: Action::Call(0xa.into()),
value: 100.into(),
data: vec![],
}
})
.sign(&secret(), None);
state
@ -2421,14 +2425,14 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5);
let t = Transaction {
let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
action: Action::Call(0xa.into()),
value: 100.into(),
data: vec![], //600480600b6000396000f35b600056
}
})
.sign(&secret(), None);
state
@ -2512,14 +2516,14 @@ mod tests {
info.gas_limit = 1_000_000.into();
let machine = make_frontier_machine(5);
let t = Transaction {
let t = TypedTransaction::Legacy(Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 100_000.into(),
action: Action::Call(0xa.into()),
value: 100.into(),
data: vec![],
}
})
.sign(&secret(), None);
state

View File

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

View File

@ -45,7 +45,7 @@ use types::{
data_format::DataFormat,
filter::Filter,
ids::BlockId,
transaction::{Action, Condition, PendingTransaction, Transaction},
transaction::{Action, Condition, PendingTransaction, Transaction, TypedTransaction},
view,
views::BlockView,
};
@ -362,26 +362,26 @@ fn does_not_propagate_delayed_transactions() {
let key = KeyPair::from_secret(keccak("test").into()).unwrap();
let secret = key.secret();
let tx0 = PendingTransaction::new(
Transaction {
TypedTransaction::Legacy(Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 21000.into(),
action: Action::Call(Address::default()),
value: 0.into(),
data: Vec::new(),
}
})
.sign(secret, None),
Some(Condition::Number(2)),
);
let tx1 = PendingTransaction::new(
Transaction {
TypedTransaction::Legacy(Transaction {
nonce: 1.into(),
gas_price: 0.into(),
gas: 21000.into(),
action: Action::Call(Address::default()),
value: 0.into(),
data: Vec::new(),
}
})
.sign(secret, None),
None,
);
@ -443,14 +443,14 @@ fn transaction_proof() {
client.import_sealed_block(b).unwrap(); // account change is in the journal overlay
}
let transaction = Transaction {
let transaction = TypedTransaction::Legacy(Transaction {
nonce: 0.into(),
gas_price: 0.into(),
gas: 21000.into(),
action: Action::Call(Address::default()),
value: 5.into(),
data: Vec::new(),
}
})
.fake_sign(address);
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 types::{
header::Header,
transaction::{Action, Transaction},
transaction::{Action, Transaction, TypedTransaction},
view,
views::BlockView,
};
@ -171,14 +171,14 @@ fn can_trace_block_and_uncle_reward() {
for _ in 0..1 {
block
.push_transaction(
Transaction {
TypedTransaction::Legacy(Transaction {
nonce: n.into(),
gas_price: 10000.into(),
gas: 100000.into(),
action: Action::Create,
data: vec![],
value: U256::zero(),
}
})
.sign(kp.secret(), Some(spec.network_id())),
None,
)

View File

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

View File

@ -80,7 +80,10 @@ pub mod blocks {
use engines::EthEngine;
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 bytes::Bytes;
@ -151,7 +154,7 @@ pub mod blocks {
let (header, transactions, uncles) = {
let rlp = Rlp::new(&bytes);
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)?;
(header, transactions, uncles)
};

View File

@ -133,7 +133,7 @@ pub fn verify_block_unordered(
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)
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());
}
}
@ -493,7 +493,16 @@ fn verify_parent(header: &Header, parent: &Header, engine: &dyn EthEngine) -> Re
fn verify_block_integrity(block: &Unverified) -> Result<(), Error> {
let block_rlp = Rlp::new(&block.bytes);
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() {
bail!(BlockError::InvalidTransactionsRoot(Mismatch {
expected: expected_root,
@ -531,7 +540,7 @@ mod tests {
use types::{
encoded,
log_entry::{LocalizedLogEntry, LogEntry},
transaction::{Action, SignedTransaction, Transaction, UnverifiedTransaction},
transaction::{Action, SignedTransaction, Transaction, TypedTransaction},
};
fn check_ok(result: Result<(), Error>) {
@ -764,34 +773,34 @@ mod tests {
let keypair = Random.generate().unwrap();
let tr1 = Transaction {
let tr1 = TypedTransaction::Legacy(Transaction {
action: Action::Create,
value: U256::from(0),
data: Bytes::new(),
gas: U256::from(30_000),
gas_price: U256::from(40_000),
nonce: U256::one(),
}
})
.sign(keypair.secret(), None);
let tr2 = Transaction {
let tr2 = TypedTransaction::Legacy(Transaction {
action: Action::Create,
value: U256::from(0),
data: Bytes::new(),
gas: U256::from(30_000),
gas_price: U256::from(40_000),
nonce: U256::from(2),
}
})
.sign(keypair.secret(), None);
let tr3 = Transaction {
let tr3 = TypedTransaction::Legacy(Transaction {
action: Action::Call(0x0.into()),
value: U256::from(0),
data: Bytes::new(),
gas: U256::from(30_000),
gas_price: U256::from(0),
nonce: U256::zero(),
}
})
.null_sign(0);
let good_transactions = [tr1.clone(), tr2.clone()];
@ -834,16 +843,10 @@ mod tests {
let mut uncles_rlp = RlpStream::new();
uncles_rlp.append_list(&good_uncles);
let good_uncles_hash = keccak(uncles_rlp.as_raw());
let good_transactions_root = ordered_trie_root(
good_transactions
.iter()
.map(|t| ::rlp::encode::<UnverifiedTransaction>(t)),
);
let eip86_transactions_root = ordered_trie_root(
eip86_transactions
.iter()
.map(|t| ::rlp::encode::<UnverifiedTransaction>(t)),
);
let good_transactions_root =
ordered_trie_root(good_transactions.iter().map(|t| t.encode()));
let eip86_transactions_root =
ordered_trie_root(eip86_transactions.iter().map(|t| t.encode()));
let mut parent = good.clone();
parent.set_number(9);
@ -1114,14 +1117,14 @@ mod tests {
let keypair = Random.generate().unwrap();
let bad_transactions: Vec<_> = (0..3)
.map(|i| {
Transaction {
TypedTransaction::Legacy(Transaction {
action: Action::Create,
value: U256::zero(),
data: Vec::new(),
gas: 0.into(),
gas_price: U256::zero(),
nonce: i.into(),
}
})
.sign(keypair.secret(), None)
})
.collect();

View File

@ -762,7 +762,7 @@ mod tests {
use triehash_ethereum::ordered_trie_root;
use types::{
header::Header as BlockHeader,
transaction::{SignedTransaction, Transaction},
transaction::{SignedTransaction, Transaction, TypedTransaction},
};
fn dummy_header(number: u64, parent_hash: H256) -> BlockHeader {
@ -778,7 +778,7 @@ mod tests {
fn dummy_signed_tx() -> SignedTransaction {
let keypair = Random.generate().unwrap();
Transaction::default().sign(keypair.secret(), None)
TypedTransaction::Legacy(Transaction::default()).sign(keypair.secret(), None)
}
fn import_headers(
@ -949,8 +949,16 @@ mod tests {
::rlp::EMPTY_LIST_RLP.to_vec()
};
let txs = encode_list(&[dummy_signed_tx()]);
let tx_root = ordered_trie_root(Rlp::new(&txs).iter().map(|r| r.as_raw()));
let mut rlp_strem = RlpStream::new();
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);
rlp.append_raw(&txs, 1);
@ -1019,14 +1027,20 @@ mod tests {
// 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
// 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 {
encode_list(&[0u32])
vec![0xC1, 0]
} else {
encode_list(&[i as u32])
vec![0xC1, i as u8]
};
let receipts_root =
ordered_trie_root(Rlp::new(&receipts_rlp).iter().map(|r| r.as_raw()));
let receipts_root = ordered_trie_root(Rlp::new(&receipts_rlp).iter().map(|r| {
if r.is_list() {
r.as_raw()
} else {
r.data().expect("expect proper test data")
}
}));
receipts.push(receipts_rlp);
// Construct the block header.

View File

@ -23,7 +23,10 @@ use network;
use rlp::{DecoderError, Rlp, RlpStream};
use std::collections::{hash_map, BTreeMap, HashMap, HashSet};
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);
@ -65,7 +68,7 @@ impl SyncBody {
let result = SyncBody {
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: uncles_rlp.as_list()?,
};
@ -454,11 +457,14 @@ impl BlockCollection {
fn insert_body(&mut self, body: SyncBody) -> Result<H256, network::Error> {
let header_id = {
let tx_root = ordered_trie_root(
Rlp::new(&body.transactions_bytes)
.iter()
.map(|r| r.as_raw()),
);
let tx_root = ordered_trie_root(Rlp::new(&body.transactions_bytes).iter().map(|r| {
if r.is_list() {
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);
HeaderId {
transactions_root: tx_root,
@ -491,7 +497,22 @@ impl BlockCollection {
fn insert_receipt(&mut self, r: Bytes) -> Result<Vec<H256>, network::Error> {
let receipt_root = {
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);
match self.receipt_ids.entry(receipt_root) {

View File

@ -837,9 +837,12 @@ impl SyncHandler {
let item_count = r.item_count()?;
trace!(target: "sync", "{:02} -> Transactions ({} entries)", peer_id, item_count);
let mut transactions = Vec::with_capacity(item_count);
for i in 0..item_count {
let rlp = r.at(i)?;
let tx = rlp.as_raw().to_vec();
for i in r.iter() {
let tx = if i.is_list() {
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);
}
io.chain().queue_transactions(transactions, peer_id);

View File

@ -21,7 +21,7 @@ use ethereum_types::H256;
use fastmap::H256FastSet;
use network::{client_version::ClientCapabilities, PeerId};
use rand::Rng;
use rlp::{Encodable, RlpStream};
use rlp::RlpStream;
use sync_io::SyncIo;
use types::{blockchain_info::BlockChainInfo, transaction::SignedTransaction, BlockNumber};
@ -121,7 +121,7 @@ impl SyncPropagator {
let (transactions, service_transactions): (Vec<_>, Vec<_>) = transactions
.iter()
.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
let mut affected_peers = HashSet::new();
@ -171,7 +171,7 @@ impl SyncPropagator {
let all_transactions_rlp = {
let mut packet = RlpStream::new_list(transactions.len());
for tx in &transactions {
packet.append(&**tx);
tx.rlp_append(&mut packet);
}
packet.out()
};
@ -238,13 +238,8 @@ impl SyncPropagator {
for tx in &transactions {
let hash = tx.hash();
if to_send.contains(&hash) {
let mut transaction = RlpStream::new();
tx.rlp_append(&mut transaction);
let appended = packet.append_raw_checked(
&transaction.drain(),
1,
MAX_TRANSACTION_PACKET_SIZE,
);
let appended =
packet.append_raw_checked(&tx.encode(), 1, MAX_TRANSACTION_PACKET_SIZE);
if !appended {
// 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());
@ -373,6 +368,7 @@ mod tests {
use rlp::Rlp;
use std::collections::VecDeque;
use tests::{helpers::TestIo, snapshot::TestSnapshotService};
use types::transaction::TypedTransaction;
use super::{
super::{tests::*, *},
@ -707,7 +703,9 @@ mod tests {
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();
assert_eq!(sent_transactions.len(), 2);

View File

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

View File

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

View File

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

View File

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

View File

@ -16,9 +16,14 @@
//! Receipt
use super::transaction::TypedTxId;
use ethereum_types::{Address, Bloom, H160, H256, U256};
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 BlockNumber;
@ -36,7 +41,7 @@ pub enum TransactionOutcome {
/// Information describing execution of a transaction.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Receipt {
pub struct LegacyReceipt {
/// The total gas used in the block following execution of the transaction.
pub gas_used: U256,
/// The OR-wide combination of all logs' blooms for this transaction.
@ -47,10 +52,9 @@ pub struct Receipt {
pub outcome: TransactionOutcome,
}
impl Receipt {
/// Create a new receipt.
impl LegacyReceipt {
pub fn new(outcome: TransactionOutcome, gas_used: U256, logs: Vec<LogEntry>) -> Self {
Self {
LegacyReceipt {
gas_used,
log_bloom: logs.iter().fold(Bloom::default(), |mut b, l| {
b.accrue_bloom(&l.bloom());
@ -60,10 +64,32 @@ impl Receipt {
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 {
fn rlp_append(&self, s: &mut RlpStream) {
pub fn rlp_append(&self, s: &mut RlpStream) {
match self.outcome {
TransactionOutcome::Unknown => {
s.begin_list(3);
@ -83,42 +109,142 @@ impl Encodable for Receipt {
}
}
impl Decodable for Receipt {
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
if rlp.item_count()? == 3 {
Ok(Receipt {
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()?)
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TypedReceipt {
Legacy(LegacyReceipt),
AccessList(LegacyReceipt),
}
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());
}
}
}
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 HeapSizeOf for Receipt {
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 {
self.logs.heap_size_of_children()
self.receipt().logs.heap_size_of_children()
}
}
/// Receipt with additional info.
#[derive(Debug, Clone, PartialEq)]
pub struct RichReceipt {
/// Transaction type
pub transaction_type: TypedTxId,
/// Transaction hash.
pub transaction_hash: H256,
/// Transaction index.
@ -146,6 +272,8 @@ pub struct RichReceipt {
/// Receipt with additional info.
#[derive(Debug, Clone, PartialEq)]
pub struct LocalizedReceipt {
/// Transaction type
pub transaction_type: TypedTxId,
/// Transaction hash.
pub transaction_hash: H256,
/// Transaction index.
@ -176,13 +304,15 @@ pub struct LocalizedReceipt {
#[cfg(test)]
mod tests {
use super::{Receipt, TransactionOutcome};
use super::{LegacyReceipt, TransactionOutcome, TypedReceipt, TypedTxId};
use log_entry::LogEntry;
#[test]
fn test_no_state_root() {
let expected = ::rustc_hex::FromHex::from_hex("f9014183040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap();
let r = Receipt::new(
let r = TypedReceipt::new(
TypedTxId::Legacy,
LegacyReceipt::new(
TransactionOutcome::Unknown,
0x40cae.into(),
vec![LogEntry {
@ -190,14 +320,17 @@ mod tests {
topics: vec![],
data: vec![0u8; 32],
}],
),
);
assert_eq!(&::rlp::encode(&r)[..], &expected[..]);
assert_eq!(r.encode(), expected);
}
#[test]
fn test_basic() {
fn test_basic_legacy() {
let expected = ::rustc_hex::FromHex::from_hex("f90162a02f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee83040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap();
let r = Receipt::new(
let r = TypedReceipt::new(
TypedTxId::Legacy,
LegacyReceipt::new(
TransactionOutcome::StateRoot(
"2f697d671e9ae4ee24a43c4b0d7e15f1cb4ba6de1561120d43b9a4e8c4a8a6ee".into(),
),
@ -207,17 +340,43 @@ mod tests {
topics: vec![],
data: vec![0u8; 32],
}],
),
);
let encoded = ::rlp::encode(&r);
assert_eq!(&encoded[..], &expected[..]);
let decoded: Receipt = ::rlp::decode(&encoded).expect("decoding receipt failed");
let encoded = r.encode();
assert_eq!(encoded, expected);
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);
}
#[test]
fn test_status_code() {
let expected = ::rustc_hex::FromHex::from_hex("f901428083040caeb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000f838f794dcf421d093428b096ca501a7cd1a740855a7976fc0a00000000000000000000000000000000000000000000000000000000000000000").unwrap();
let r = Receipt::new(
let r = TypedReceipt::new(
TypedTxId::Legacy,
LegacyReceipt::new(
TransactionOutcome::StatusCode(0),
0x40cae.into(),
vec![LogEntry {
@ -225,10 +384,11 @@ mod tests {
topics: vec![],
data: vec![0u8; 32],
}],
),
);
let encoded = ::rlp::encode(&r);
let encoded = r.encode();
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);
}
}

View File

@ -84,6 +84,8 @@ pub enum Error {
TooBig,
/// Invalid RLP encoding
InvalidRlp(String),
/// Transaciton is still not enabled.
TransactionTypeNotEnabled,
}
impl From<ethkey::Error> for Error {
@ -133,6 +135,9 @@ impl fmt::Display for Error {
}
TooBig => "Transaction too big".into(),
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))

View File

@ -18,5 +18,6 @@
mod error;
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 hash::keccak;
use header::Header;
use transaction::{LocalizedTransaction, UnverifiedTransaction};
use transaction::{LocalizedTransaction, TypedTransaction, UnverifiedTransaction};
use views::{HeaderView, TransactionView};
/// View onto block rlp.
@ -77,7 +77,12 @@ impl<'a> BlockView<'a> {
/// Return List of transactions in given block.
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.
@ -126,10 +131,14 @@ impl<'a> BlockView<'a> {
/// Returns transaction at given index without deserializing unnecessary data.
pub fn transaction_at(&self, index: usize) -> Option<UnverifiedTransaction> {
self.transactions_rlp()
.iter()
.nth(index)
.map(|rlp| rlp.as_val())
self.transactions_rlp().iter().nth(index).map(|rlp| {
TypedTransaction::decode_rlp(&rlp.rlp).unwrap_or_else(|e| {
panic!(
"block transaction_at, view rlp is trusted and should be valid.{:?}",
e
)
})
})
}
/// Returns localized transaction at given index.

View File

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

View File

@ -213,7 +213,7 @@ mod tests {
access_list.insert_address(Address::from(1));
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,

View File

@ -24,6 +24,10 @@ pub const EIP2929_COLD_ACCOUNT_ACCESS_COST: usize = 2600;
pub const EIP2929_WARM_STORAGE_READ_COST: usize = 100;
// Gas per sstore reset
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.
#[derive(Debug)]
@ -149,6 +153,8 @@ pub struct Schedule {
pub wasm: Option<WasmCosts>,
/// Enable EIP-2929 rules
pub eip2929: bool,
/// Enable EIP-2930 rules for optional access list transactions. it depends on EIP-2929
pub eip2930: bool,
}
/// Wasm cost table
@ -295,6 +301,7 @@ impl Schedule {
keep_unsigned_nonce: false,
wasm: None,
eip2929: false,
eip2930: false,
}
}
@ -343,6 +350,7 @@ impl Schedule {
schedule.eip1283 = true;
schedule.eip2929 = true;
schedule.eip2930 = true;
schedule.cold_sload_cost = EIP2929_COLD_SLOAD_COST;
schedule.cold_account_access_cost = EIP2929_COLD_ACCOUNT_ACCESS_COST;
@ -421,6 +429,7 @@ impl Schedule {
keep_unsigned_nonce: false,
wasm: None,
eip2929: false,
eip2930: false,
}
}

View File

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

View File

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

View File

@ -20,9 +20,9 @@ use std::{fmt, sync::Arc, time::Duration};
use io::IoHandler;
use kvdb::KeyValueDB;
use rlp::Rlp;
use types::transaction::{
Condition as TransactionCondition, PendingTransaction, SignedTransaction, UnverifiedTransaction,
Condition as TransactionCondition, PendingTransaction, SignedTransaction, TypedTransaction,
UnverifiedTransaction,
};
extern crate common_types as types;
@ -98,7 +98,7 @@ struct TransactionEntry {
impl TransactionEntry {
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) => {
warn!(target: "local_store", "Invalid persistent transaction stored: {}", e);
return None;
@ -120,7 +120,7 @@ impl TransactionEntry {
impl From<PendingTransaction> for TransactionEntry {
fn from(pending: PendingTransaction) -> Self {
TransactionEntry {
rlp_bytes: ::rlp::encode(&pending.transaction),
rlp_bytes: pending.transaction.encode(),
condition: pending.condition.map(Into::into),
}
}
@ -239,7 +239,7 @@ mod tests {
use ethkey::{Brain, Generator};
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.
// 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 transactions: Vec<_> = (0..10u64)
.map(|nonce| {
let mut tx = Transaction::default();
tx.nonce = nonce.into();
let mut tx = TypedTransaction::Legacy(Transaction::default());
tx.tx_mut().nonce = nonce.into();
let signed = tx.sign(keypair.secret(), None);
let condition = match nonce {
@ -308,8 +308,8 @@ mod tests {
let keypair = Brain::new("abcd".into()).generate().unwrap();
let mut transactions: Vec<_> = (0..10u64)
.map(|nonce| {
let mut tx = Transaction::default();
tx.nonce = nonce.into();
let mut tx = TypedTransaction::Legacy(Transaction::default());
tx.tx_mut().nonce = nonce.into();
let signed = tx.sign(keypair.secret(), None);
@ -318,8 +318,8 @@ mod tests {
.collect();
transactions.push({
let mut tx = Transaction::default();
tx.nonce = 10.into();
let mut tx = TypedTransaction::Legacy(Transaction::default());
tx.tx_mut().nonce = 10.into();
let signed = tx.fake_sign(Default::default());
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 = tx.hash(),
sender = tx.sender(),
nonce = tx.signed().nonce,
gas_price = tx.signed().gas_price,
gas = tx.signed().gas,
value = tx.signed().value,
data = tx.signed().data.len(),
nonce = tx.signed().tx().nonce,
gas_price = tx.signed().tx().gas_price,
gas = tx.signed().tx().gas,
value = tx.signed().tx().value,
data = tx.signed().tx().data.len(),
);
if let Some(old) = old {
@ -150,7 +150,7 @@ mod tests {
assert_eq!(
*received.lock(),
vec![
"13aff4201ac1dc49daf6a7cf07b558ed956511acbaabf9502bdacc353953766d"
"de96bdcdf864c95eb7f81eff1e3290be24a0f327732e0c4251c1896a565a80db"
.parse()
.unwrap()
]
@ -158,14 +158,14 @@ mod tests {
}
fn new_tx() -> Arc<Transaction> {
let signed = transaction::Transaction {
let signed = transaction::TypedTransaction::Legacy(transaction::Transaction {
action: transaction::Action::Create,
data: vec![1, 2, 3],
nonce: 5.into(),
gas: 21_000.into(),
gas_price: 5.into(),
value: 0.into(),
}
})
.fake_sign(5.into());
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> {
let keypair = Random.generate().unwrap();
let signed = transaction::Transaction {
let signed = transaction::TypedTransaction::Legacy(transaction::Transaction {
action: transaction::Action::Create,
value: U256::from(100),
data: Default::default(),
gas: U256::from(10),
gas_price: U256::from(1245),
nonce: nonce.into(),
}
})
.sign(keypair.secret(), None);
let mut tx = Transaction::from_pending_block_transaction(signed);

View File

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

View File

@ -519,7 +519,7 @@ impl TransactionQueue {
.read()
.pending_from_sender(state_readiness, address)
.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.
@ -573,7 +573,7 @@ impl TransactionQueue {
/// Returns gas price of currently the worst transaction in the pool.
pub fn current_worst_gas_price(&self) -> U256 {
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,
}
}
@ -660,7 +660,7 @@ mod tests {
);
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 {
// Check 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;
}
_ => {}
@ -81,7 +81,7 @@ impl<C: NonceClient> txpool::Ready<VerifiedTransaction> for State<C> {
let state = &self.state;
let state_nonce = || state.account_nonce(sender);
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
cmp::Ordering::Greater => match self.stale_id {
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
.nonces
.entry(*sender)
.or_insert_with(|| state(sender).unwrap_or_else(|| tx.transaction.nonce));
match tx.transaction.nonce.cmp(nonce) {
.or_insert_with(|| state(sender).unwrap_or_else(|| tx.transaction.tx().nonce));
match tx.transaction.tx().nonce.cmp(nonce) {
cmp::Ordering::Greater => txpool::Readiness::Future,
cmp::Ordering::Less => txpool::Readiness::Stale,
cmp::Ordering::Equal => {

View File

@ -67,7 +67,7 @@ impl NonceAndGasPrice {
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 rlp::Rlp;
use types::transaction::{self, SignedTransaction, Transaction, UnverifiedTransaction};
use types::transaction::{
self, SignedTransaction, Transaction, TypedTransaction, UnverifiedTransaction,
};
use pool::{self, client::AccountDetails};
@ -150,7 +152,7 @@ impl pool::client::Client for TestClient {
if rlp.as_raw().len() > self.max_transaction_size {
return Err(transaction::Error::TooBig);
}
rlp.as_val()
TypedTransaction::decode(transaction)
.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 sender = tx1.sender();
let nonce = tx1.nonce;
let nonce = tx1.tx().nonce;
// when
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 sender = tx1.sender();
let nonce = tx1.nonce;
let nonce = tx1.tx().nonce;
// when
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!(
txq.pending(client.clone(), PendingSettings::all_prioritized(0, 0))[0]
.signed()
.tx()
.gas_price,
U256::from(20)
);
assert_eq!(
txq.pending(client.clone(), PendingSettings::all_prioritized(0, 0))[1]
.signed()
.tx()
.gas_price,
U256::from(2)
);
@ -688,7 +690,7 @@ fn should_return_correct_nonce_when_transactions_from_given_address_exist() {
let txq = new_queue();
let tx = Tx::default().signed();
let from = tx.sender();
let nonce = tx.nonce;
let nonce = tx.tx().nonce;
// when
txq.import(TestClient::new(), vec![tx.local()]);

View File

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

View File

@ -31,7 +31,6 @@ use std::{
};
use ethereum_types::{H256, U256};
use rlp::Encodable;
use txpool;
use types::transaction;
@ -97,21 +96,21 @@ impl Transaction {
/// Return transaction gas price
pub fn gas_price(&self) -> &U256 {
match *self {
Transaction::Unverified(ref tx) => &tx.gas_price,
Transaction::Retracted(ref tx) => &tx.gas_price,
Transaction::Local(ref tx) => &tx.gas_price,
Transaction::Unverified(ref tx) => &tx.tx().gas_price,
Transaction::Retracted(ref tx) => &tx.tx().gas_price,
Transaction::Local(ref tx) => &tx.tx().gas_price,
}
}
fn gas(&self) -> &U256 {
match *self {
Transaction::Unverified(ref tx) => &tx.gas,
Transaction::Retracted(ref tx) => &tx.gas,
Transaction::Local(ref tx) => &tx.gas,
Transaction::Unverified(ref tx) => &tx.tx().gas,
Transaction::Retracted(ref tx) => &tx.tx().gas,
Transaction::Local(ref tx) => &tx.tx().gas,
}
}
fn transaction(&self) -> &transaction::Transaction {
fn transaction(&self) -> &transaction::TypedTransaction {
match *self {
Transaction::Unverified(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 {
trace!(target: "txqueue",
"[{:?}] 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: {} < {})",
hash,
tx.gas_price(),
vtx.transaction.gas_price,
vtx.transaction.tx().gas_price,
);
return Err(transaction::Error::TooCheapToReplace {
prev: Some(vtx.transaction.gas_price),
prev: Some(vtx.transaction.tx().gas_price),
new: Some(*tx.gas_price()),
});
}
@ -273,7 +272,7 @@ impl<C: Client> txpool::Verifier<Transaction>
};
// 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);
bail!(err)
}
@ -281,7 +280,7 @@ impl<C: Client> txpool::Verifier<Transaction>
let sender = transaction.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);
if let TransactionType::Service = transaction_type {
debug!(target: "txqueue", "Service tx {:?} below minimal gas price accepted", hash);
@ -292,18 +291,21 @@ impl<C: Client> txpool::Verifier<Transaction>
target: "txqueue",
"[{:?}] Rejected tx below minimal gas price threshold: {} < {}",
hash,
transaction.gas_price,
transaction.tx().gas_price,
self.options.minimal_gas_price,
);
bail!(transaction::Error::InsufficientGasPrice {
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 (cost, overflow_2) = transaction.value.overflowing_add(full_gas_price);
let (full_gas_price, overflow_1) = transaction
.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 {
trace!(
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!(
target: "txqueue",
"[{:?}] Rejected tx with old nonce ({} < {})",
hash,
transaction.nonce,
transaction.tx().nonce,
account_details.nonce,
);
bail!(transaction::Error::Old);

View File

@ -45,7 +45,7 @@ impl ServiceTransactionChecker {
) -> Result<bool, String> {
let sender = tx.sender();
// 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);
}

View File

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

View File

@ -21,7 +21,10 @@ use bytes::Bytes;
use crypto::DEFAULT_MAC;
use ethereum_types::{Address, H256, U256};
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 v1::helpers::{errors, FilledTransactionRequest};
@ -48,16 +51,28 @@ impl super::Accounts for Signer {
nonce: U256,
password: SignWith,
) -> Result<WithToken<SignedTransaction>> {
let t = Transaction {
nonce: nonce,
let legacy_tx = Transaction {
nonce,
action: filled.to.map_or(Action::Create, Action::Call),
gas: filled.gas,
gas_price: filled.gas_price,
value: filled.value,
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)?;
Ok(signature.map(|sig| {

View File

@ -400,6 +400,7 @@ pub fn transaction_message(error: &TransactionError) -> String {
NotAllowed => "Transaction is not permitted.".into(),
TooBig => "Transaction is too big, see chain specification for the limit.".into(),
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 {
ConfirmationPayload::SendTransaction(FilledTransactionRequest {
tx_type: Default::default(),
from: Address::from(1),
used_default_from: false,
to: Some(Address::from(2)),
@ -265,6 +266,7 @@ mod test {
data: vec![],
nonce: None,
condition: None,
access_list: None,
})
}

View File

@ -15,7 +15,9 @@
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use std::cmp::min;
use types::transaction::{Action, SignedTransaction, Transaction};
use types::transaction::{
AccessListTx, Action, SignedTransaction, Transaction, TypedTransaction, TypedTxId,
};
use ethereum_types::U256;
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 gas = min(request.gas.unwrap_or(max_gas), max_gas);
let from = request.from.unwrap_or_default();
Ok(Transaction {
let tx_legacy = Transaction {
nonce: request.nonce.unwrap_or_default(),
action: request.to.map_or(Action::Create, Action::Call),
gas,
gas_price: request.gas_price.unwrap_or_default(),
value: request.value.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 ethereum_types::{Address, H256, U256};
use types::transaction::{AccessList, TypedTxId};
use v1::types::{Origin, TransactionCondition};
/// Transaction request coming from RPC
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)]
pub struct TransactionRequest {
/// type of transaction.
pub tx_type: TypedTxId,
/// Sender
pub from: Option<Address>,
/// Recipient
@ -38,11 +41,15 @@ pub struct TransactionRequest {
pub nonce: Option<U256>,
/// Delay until this condition is met.
pub condition: Option<TransactionCondition>,
/// Access list
pub access_list: Option<AccessList>,
}
/// Transaction request coming from RPC with default values filled in.
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)]
pub struct FilledTransactionRequest {
/// type of transaction.
pub tx_type: TypedTxId,
/// Sender
pub from: Address,
/// Indicates if the sender was filled by default value.
@ -61,11 +68,14 @@ pub struct FilledTransactionRequest {
pub nonce: Option<U256>,
/// Delay until this condition is met.
pub condition: Option<TransactionCondition>,
/// Access list
pub access_list: Option<AccessList>,
}
impl From<FilledTransactionRequest> for TransactionRequest {
fn from(r: FilledTransactionRequest) -> Self {
TransactionRequest {
tx_type: r.tx_type,
from: Some(r.from),
to: r.to,
gas_price: Some(r.gas_price),
@ -74,6 +84,7 @@ impl From<FilledTransactionRequest> for TransactionRequest {
data: Some(r.data),
nonce: r.nonce,
condition: r.condition,
access_list: r.access_list,
}
}
}
@ -81,6 +92,8 @@ impl From<FilledTransactionRequest> for TransactionRequest {
/// Call request
#[derive(Debug, Default, PartialEq)]
pub struct CallRequest {
/// type of transaction.
pub tx_type: TypedTxId,
/// From
pub from: Option<Address>,
/// To
@ -95,6 +108,8 @@ pub struct CallRequest {
pub data: Option<Vec<u8>>,
/// Nonce
pub nonce: Option<U256>,
/// Access list
pub access_list: Option<AccessList>,
}
/// Confirmation object

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -178,7 +178,7 @@ fn rpc_parity_set_hash_content() {
#[test]
fn rpc_parity_remove_transaction() {
use types::transaction::{Action, Transaction};
use types::transaction::{Action, Transaction, TypedTransaction};
let miner = miner_service();
let client = client_service();
@ -187,14 +187,14 @@ fn rpc_parity_remove_transaction() {
let mut io = IoHandler::new();
io.extend_with(parity_set_client(&client, &miner, &network).to_delegate());
let tx = Transaction {
let tx = TypedTransaction::Legacy(Transaction {
nonce: 1.into(),
gas_price: 0x9184e72a000u64.into(),
gas: 0x76c0.into(),
action: Action::Call(5.into()),
value: 0x9184e72au64.into(),
data: vec![],
};
});
let signed = tx.fake_sign(2.into());
let hash = signed.hash();
@ -202,7 +202,7 @@ fn rpc_parity_remove_transaction() {
.to_owned()
+ &format!("0x{:x}", hash)
+ 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);
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 parity_runtime::Runtime;
use parking_lot::Mutex;
use types::transaction::{Action, Transaction};
use types::transaction::{Action, Transaction, TypedTransaction};
use ethkey::Secret;
use serde_json::to_value;
@ -91,9 +91,6 @@ fn setup_with(c: Config) -> PersonalTester {
tester
}
#[cfg(test)]
use rustc_hex::ToHex;
#[test]
fn accounts() {
let tester = setup();
@ -265,7 +262,7 @@ fn sign_and_send_test(method: &str) {
"id": 1
}"#;
let t = Transaction {
let t = TypedTransaction::Legacy(Transaction {
nonce: U256::zero(),
gas_price: U256::from(0x9184e72a000u64),
gas: U256::from(0x76c0),
@ -274,12 +271,15 @@ fn sign_and_send_test(method: &str) {
),
value: U256::from(0x9184e72au64),
data: vec![],
};
});
tester
.accounts
.unlock_account_temporarily(address, "password123".into())
.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 response = r#"{"jsonrpc":"2.0","result":""#.to_owned()
@ -293,7 +293,7 @@ fn sign_and_send_test(method: &str) {
tester.miner.increment_nonce(&address);
let t = Transaction {
let t = TypedTransaction::Legacy(Transaction {
nonce: U256::one(),
gas_price: U256::from(0x9184e72a000u64),
gas: U256::from(0x76c0),
@ -302,12 +302,15 @@ fn sign_and_send_test(method: &str) {
),
value: U256::from(0x9184e72au64),
data: vec![],
};
});
tester
.accounts
.unlock_account_temporarily(address, "password123".into())
.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 response = r#"{"jsonrpc":"2.0","result":""#.to_owned()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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