// Copyright 2015-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 . //! General test deserialization. use bytes::Bytes; use ethkey::Secret; use hash::{Address, H256}; use maybe::MaybeEmpty; use serde_json::{self, Error}; use spec::ForkSpec; use state::{AccountState, Env}; use std::{collections::BTreeMap, io::Read}; use types::transaction::{AccessListTx, Action, SignedTransaction, Transaction, TypedTransaction}; use uint::Uint; use crate::blockchain::transaction::AccessList; /// State test deserializer. #[derive(Debug, PartialEq, Deserialize)] pub struct Test(BTreeMap); impl IntoIterator for Test { type Item = as IntoIterator>::Item; type IntoIter = as IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } impl Test { /// Loads test from json. pub fn load(reader: R) -> Result where R: Read, { serde_json::from_reader(reader) } } /// State test deserialization. #[derive(Debug, PartialEq, Deserialize)] pub struct State { /// Environment. pub env: Env, /// Pre state. #[serde(rename = "pre")] pub pre_state: AccountState, /// Post state. #[serde(rename = "post")] pub post_states: BTreeMap>, /// Transaction. pub transaction: MultiTransaction, } /// State test transaction deserialization. #[derive(Debug, PartialEq, Deserialize)] #[serde(rename_all = "camelCase")] pub struct MultiTransaction { /// Transaction data set. pub data: Vec, /// Optional access list pub access_lists: Option>>, /// Gas limit set. pub gas_limit: Vec, /// Gas price. pub gas_price: Uint, /// Nonce. pub nonce: Uint, /// Secret key. #[serde(rename = "secretKey")] pub secret: Option, /// To. pub to: MaybeEmpty
, /// Value set. pub value: Vec, } fn sign_with_secret(tx: TypedTransaction, secret: Option) -> SignedTransaction { match secret { Some(s) => tx.sign(&s, None), None => tx.null_sign(1), } } impl MultiTransaction { /// Build transaction with given indexes. pub fn select(&self, indexes: &PostStateIndexes) -> SignedTransaction { let secret = self.secret.clone().map(|s| Secret::from(s.0)); let to: Option
= self.to.clone().into(); let transaction = Transaction { nonce: self.nonce.clone().into(), gas_price: self.gas_price.clone().into(), gas: self.gas_limit[indexes.gas as usize].clone().into(), action: match to { Some(to) => Action::Call(to.into()), None => Action::Create, }, value: self.value[indexes.value as usize].clone().into(), data: self.data[indexes.data as usize].clone().into(), }; if let Some(access_lists) = self.access_lists.as_ref() { if access_lists.len() > indexes.data as usize { if let Some(access_list) = access_lists[indexes.data as usize].clone() { //access list type of transaction let access_list = access_list .into_iter() .map(|elem| { ( elem.address.into(), elem.storage_keys.into_iter().map(Into::into).collect(), ) }) .collect(); let tx = TypedTransaction::AccessList(AccessListTx { transaction, access_list, }); return sign_with_secret(tx, secret); } } } let tx = TypedTransaction::Legacy(transaction); sign_with_secret(tx, secret) } } /// State test indexes deserialization. #[derive(Debug, PartialEq, Deserialize)] pub struct PostStateIndexes { /// Index into transaction data set. pub data: u64, /// Index into transaction gas limit set. pub gas: u64, /// Index into transaction value set. pub value: u64, } /// State test indexed state result deserialization. #[derive(Debug, PartialEq, Deserialize)] pub struct PostStateResult { /// Post state hash pub hash: H256, /// Indexes pub indexes: PostStateIndexes, } #[cfg(test)] mod tests { use super::{MultiTransaction, State}; use serde_json; #[test] fn multi_transaction_deserialization() { let s = r#"{ "data" : [ "" ], "gasLimit" : [ "0x2dc6c0", "0x222222" ], "gasPrice" : "0x01", "nonce" : "0x00", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "to" : "1000000000000000000000000000000000000000", "value" : [ "0x00", "0x01", "0x02" ] }"#; let _deserialized: MultiTransaction = serde_json::from_str(s).unwrap(); } #[test] fn multi_transaction_deserialization_with_access_list() { let s = r#"{ "data" : [ "" ], "accessLists" : [ null, [ ], [ { "address" : "0x0000000000000000000000000000000000000102", "storageKeys" : [ ] }, { "address" : "0x0000000000000000000000000000000000000101", "storageKeys" : [ "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000010" ] } ] ], "gasLimit" : [ "0x2dc6c0", "0x222222" ], "gasPrice" : "0x01", "nonce" : "0x00", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "to" : "1000000000000000000000000000000000000000", "value" : [ "0x00", "0x01", "0x02" ] }"#; let _deserialized: MultiTransaction = serde_json::from_str(s).unwrap(); } #[test] fn state_deserialization() { let s = r#"{ "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "0x0100", "currentGasLimit" : "0x01c9c380", "currentNumber" : "0x00", "currentTimestamp" : "0x01", "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" }, "post" : { "EIP150" : [ { "hash" : "3e6dacc1575c6a8c76422255eca03529bbf4c0dda75dfc110b22d6dc4152396f", "indexes" : { "data" : 0, "gas" : 0, "value" : 0 } }, { "hash" : "99a450d8ce5b987a71346d8a0a1203711f770745c7ef326912e46761f14cd764", "indexes" : { "data" : 0, "gas" : 0, "value" : 1 } } ], "EIP158" : [ { "hash" : "3e6dacc1575c6a8c76422255eca03529bbf4c0dda75dfc110b22d6dc4152396f", "indexes" : { "data" : 0, "gas" : 0, "value" : 0 } }, { "hash" : "99a450d8ce5b987a71346d8a0a1203711f770745c7ef326912e46761f14cd764", "indexes" : { "data" : 0, "gas" : 0, "value" : 1 } } ] }, "pre" : { "1000000000000000000000000000000000000000" : { "balance" : "0x0de0b6b3a7640000", "code" : "0x6040600060406000600173100000000000000000000000000000000000000162055730f1600055", "nonce" : "0x00", "storage" : { } }, "1000000000000000000000000000000000000001" : { "balance" : "0x0de0b6b3a7640000", "code" : "0x604060006040600060027310000000000000000000000000000000000000026203d090f1600155", "nonce" : "0x00", "storage" : { } }, "1000000000000000000000000000000000000002" : { "balance" : "0x00", "code" : "0x600160025533600455346007553060e6553260e8553660ec553860ee553a60f055", "nonce" : "0x00", "storage" : { } }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "balance" : "0x0de0b6b3a7640000", "code" : "0x", "nonce" : "0x00", "storage" : { } } }, "transaction" : { "data" : [ "" ], "gasLimit" : [ "285000", "100000", "6000" ], "gasPrice" : "0x01", "nonce" : "0x00", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", "value" : [ "10", "0" ] } }"#; let _deserialized: State = serde_json::from_str(s).unwrap(); // TODO: validate all fields } }