extern crate bigint;
extern crate block;
extern crate ethereumvm;
extern crate ethereumvm_network_classic;
extern crate ethereumvm_stateful;
extern crate hexutil;
extern crate trie;
#[macro_use]
extern crate lazy_static;
use bigint::{Address, Gas, U256};
use block::TransactionAction;
use ethereumvm::{AccountChange, HeaderParams, SeqTransactionVM, Storage, ValidTransaction, VM};
use ethereumvm_network_classic::MainnetEIP160Patch;
use ethereumvm_stateful::{LiteralAccount, MemoryStateful};
use hexutil::*;
use std::collections::{HashMap, HashSet};
use std::ops::Deref;
use std::rc::Rc;
use std::str::FromStr;
use std::sync::Arc;
use std::thread;
use trie::MemoryDatabase;
#[derive(Debug, Clone)]
pub enum SendableAccountChange {
Full {
nonce: U256,
address: Address,
balance: U256,
changing_storage: Storage,
code: Vec<u8>,
},
IncreaseBalance(Address, U256),
Create {
nonce: U256,
address: Address,
balance: U256,
storage: Storage,
code: Vec<u8>,
},
Nonexist(Address),
}
impl SendableAccountChange {
pub fn address(&self) -> Address {
match self {
&SendableAccountChange::Full { address, .. } => address,
&SendableAccountChange::IncreaseBalance(address, _) => address,
&SendableAccountChange::Create { address, .. } => address,
&SendableAccountChange::Nonexist(address) => address,
}
}
}
impl From<AccountChange> for SendableAccountChange {
fn from(change: AccountChange) -> Self {
match change {
AccountChange::Full {
nonce,
address,
balance,
changing_storage,
code,
} => SendableAccountChange::Full {
nonce,
address,
balance,
changing_storage,
code: code.deref().clone(),
},
AccountChange::IncreaseBalance(address, balance) => {
SendableAccountChange::IncreaseBalance(address, balance)
}
AccountChange::Create {
nonce,
address,
balance,
storage,
code,
} => SendableAccountChange::Create {
nonce,
address,
balance,
storage,
code: code.deref().clone(),
},
AccountChange::Nonexist(address) => SendableAccountChange::Nonexist(address),
}
}
}
impl Into<AccountChange> for SendableAccountChange {
fn into(self) -> AccountChange {
match self {
SendableAccountChange::Full {
nonce,
address,
balance,
changing_storage,
code,
} => AccountChange::Full {
nonce,
address,
balance,
changing_storage,
code: Rc::new(code),
},
SendableAccountChange::IncreaseBalance(address, balance) => {
AccountChange::IncreaseBalance(address, balance)
}
SendableAccountChange::Create {
nonce,
address,
balance,
storage,
code,
} => AccountChange::Create {
nonce,
address,
balance,
storage,
code: Rc::new(code),
},
SendableAccountChange::Nonexist(address) => AccountChange::Nonexist(address),
}
}
}
pub struct SendableValidTransaction {
pub caller: Option<Address>,
pub gas_price: Gas,
pub gas_limit: Gas,
pub action: TransactionAction,
pub value: U256,
pub input: Vec<u8>,
pub nonce: U256,
}
impl From<ValidTransaction> for SendableValidTransaction {
fn from(transaction: ValidTransaction) -> SendableValidTransaction {
match transaction {
ValidTransaction {
caller,
gas_price,
gas_limit,
action,
value,
input,
nonce,
} => SendableValidTransaction {
caller,
gas_price,
gas_limit,
action,
value,
nonce,
input: input.deref().clone(),
},
}
}
}
impl Into<ValidTransaction> for SendableValidTransaction {
fn into(self) -> ValidTransaction {
match self {
SendableValidTransaction {
caller,
gas_price,
gas_limit,
action,
value,
input,
nonce,
} => ValidTransaction {
caller,
gas_price,
gas_limit,
action,
value,
nonce,
input: Rc::new(input),
},
}
}
}
fn is_modified(modified_addresses: &HashSet<Address>, accounts: &[SendableAccountChange]) -> bool {
for account in accounts {
if modified_addresses.contains(&account.address()) {
return true;
}
}
return false;
}
pub fn parallel_execute(
stateful: MemoryStateful<'static>,
transactions: &[ValidTransaction],
) -> MemoryStateful<'static> {
let header = HeaderParams {
beneficiary: Address::zero(),
timestamp: 0,
number: U256::zero(),
difficulty: U256::zero(),
gas_limit: Gas::max_value(),
};
let stateful = Arc::new(stateful);
let mut threads = Vec::new();
for transaction in transactions {
let transaction: SendableValidTransaction = transaction.clone().into();
let header = header.clone();
let stateful = stateful.clone();
threads.push(thread::spawn(move || {
let patch = MainnetEIP160Patch::default();
let vm: SeqTransactionVM<_> = stateful.call(&patch, transaction.into(), &header, &[]);
let accounts: Vec<SendableAccountChange> =
vm.accounts().map(|v| SendableAccountChange::from(v.clone())).collect();
(accounts, vm.used_addresses())
}));
}
let mut thread_accounts = Vec::new();
for thread in threads {
let accounts = thread.join().unwrap();
thread_accounts.push(accounts);
}
let mut stateful = match Arc::try_unwrap(stateful) {
Ok(val) => val,
Err(_) => panic!(),
};
let mut modified_addresses = HashSet::new();
for (index, (accounts, used_addresses)) in thread_accounts.into_iter().enumerate() {
let (accounts, used_addresses) = if is_modified(&modified_addresses, &accounts) {
println!("Transaction index {}: conflict detected, re-execute.", index);
let patch = MainnetEIP160Patch::default();
let vm: SeqTransactionVM<_> = stateful.call(&patch, transactions[index].clone(), &header, &[]);
let accounts: Vec<AccountChange> = vm.accounts().map(|v| v.clone()).collect();
(accounts, vm.used_addresses())
} else {
println!("Transaction index {}: parallel execution successful.", index);
let accounts: Vec<AccountChange> = accounts.iter().map(|v| v.clone().into()).collect();
(accounts, used_addresses)
};
stateful.transit(&accounts);
modified_addresses.extend(used_addresses.into_iter());
}
stateful
}
lazy_static! {
static ref DATABASE: MemoryDatabase = MemoryDatabase::default();
}
fn main() {
let mut stateful = MemoryStateful::empty(&DATABASE);
let addr1 = Address::from_str("0x0000000000000000000000000000000000001000").unwrap();
let addr2 = Address::from_str("0x0000000000000000000000000000000000001001").unwrap();
let addr3 = Address::from_str("0x0000000000000000000000000000000000001002").unwrap();
stateful.sets(&[
(addr1, LiteralAccount {
nonce: U256::zero(),
balance: U256::from_str("0x1000000000000000000").unwrap(),
storage: HashMap::new(),
code: read_hex("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055").unwrap(),
}),
(addr2, LiteralAccount {
nonce: U256::zero(),
balance: U256::from_str("0x1000000000000000000").unwrap(),
storage: HashMap::new(),
code: read_hex("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055").unwrap(),
}),
(addr3, LiteralAccount {
nonce: U256::zero(),
balance: U256::from_str("0x1000000000000000000").unwrap(),
storage: HashMap::new(),
code: read_hex("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055").unwrap(),
}),
]);
let stateful = parallel_execute(
stateful,
&[
ValidTransaction {
caller: Some(addr1),
action: TransactionAction::Call(addr1),
gas_price: Gas::zero(),
gas_limit: Gas::max_value(),
value: U256::from_str("0x1000").unwrap(),
input: Rc::new(Vec::new()),
nonce: U256::zero(),
},
ValidTransaction {
caller: Some(addr2),
action: TransactionAction::Call(addr3),
gas_price: Gas::zero(),
gas_limit: Gas::max_value(),
value: U256::from_str("0x1000").unwrap(),
input: Rc::new(Vec::new()),
nonce: U256::zero(),
},
ValidTransaction {
caller: Some(addr3),
action: TransactionAction::Call(addr2),
gas_price: Gas::zero(),
gas_limit: Gas::max_value(),
value: U256::from_str("0x1000").unwrap(),
input: Rc::new(Vec::new()),
nonce: U256::zero(),
},
],
);
println!("New state root: 0x{:x}", stateful.root());
}