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
83 changed files with 1858 additions and 805 deletions

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

@@ -399,7 +399,8 @@ pub fn transaction_message(error: &TransactionError) -> String {
CodeBanned => "Code is banned in local queue.".into(),
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),
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()),
@@ -197,7 +214,8 @@ mod tests {
value: Some(U256::from_str("9184e72a").unwrap()),
data: Some("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex().unwrap().into()),
nonce: None,
condition: 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,
}
);
}