extern crate trie;
extern crate block;
extern crate hexutil;
extern crate bigint;
extern crate sputnikvm;
extern crate sputnikvm_stateful;
extern crate sputnikvm_network_classic;
#[macro_use] extern crate lazy_static;
use hexutil::*;
use block::TransactionAction;
use bigint::{Address, U256, Gas};
use sputnikvm::{AccountChange, HeaderParams, SeqTransactionVM, VM, Storage, ValidTransaction};
use sputnikvm_network_classic::MainnetEIP160Patch;
use trie::MemoryDatabase;
use sputnikvm_stateful::{MemoryStateful, LiteralAccount};
use std::thread;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use std::rc::Rc;
use std::ops::Deref;
use std::str::FromStr;
#[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 vm: SeqTransactionVM<MainnetEIP160Patch> = stateful.call(
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 vm: SeqTransactionVM<MainnetEIP160Patch> = stateful.call(
transactions[index].clone(), header.clone(), &[]);
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());
}