Introduce ordered mapping for PodAccount and related structures.

This commit is contained in:
Gav Wood 2016-01-13 12:14:11 +01:00
parent 9d2ac7fc37
commit 28341fef9f
3 changed files with 66 additions and 27 deletions

View File

@ -3,26 +3,14 @@ use util::*;
pub const SHA3_EMPTY: H256 = H256( [0xc5, 0xd2, 0x46, 0x01, 0x86, 0xf7, 0x23, 0x3c, 0x92, 0x7e, 0x7d, 0xb2, 0xdc, 0xc7, 0x03, 0xc0, 0xe5, 0x00, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b, 0x7b, 0xfa, 0xd8, 0x04, 0x5d, 0x85, 0xa4, 0x70] ); pub const SHA3_EMPTY: H256 = H256( [0xc5, 0xd2, 0x46, 0x01, 0x86, 0xf7, 0x23, 0x3c, 0x92, 0x7e, 0x7d, 0xb2, 0xdc, 0xc7, 0x03, 0xc0, 0xe5, 0x00, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b, 0x7b, 0xfa, 0xd8, 0x04, 0x5d, 0x85, 0xa4, 0x70] );
#[derive(Debug)] #[derive(Debug)]
/// Genesis account data. Does no thave a DB overlay cache /// Genesis account data. Does not have a DB overlay cache.
pub struct PodAccount { pub struct PodAccount {
// Balance of the account. // Balance of the account.
pub balance: U256, pub balance: U256,
// Nonce of the account. // Nonce of the account.
pub nonce: U256, pub nonce: U256,
pub code: Bytes, pub code: Bytes,
pub storage: HashMap<H256, H256>, pub storage: BTreeMap<H256, H256>,
}
impl PodAccount {
pub fn rlp(&self) -> Bytes {
let mut stream = RlpStream::new_list(4);
stream.append(&self.nonce);
stream.append(&self.balance);
// TODO.
stream.append(&SHA3_NULL_RLP);
stream.append(&self.code.sha3());
stream.out()
}
} }
/// Single account in the system. /// Single account in the system.
@ -42,6 +30,29 @@ pub struct Account {
code_cache: Bytes, code_cache: Bytes,
} }
impl PodAccount {
/// Convert Account to a PodAccount.
/// NOTE: This will silently fail unless the account is fully cached.
pub fn from_account(acc: &Account) -> PodAccount {
PodAccount {
balance: acc.balance.clone(),
nonce: acc.nonce.clone(),
storage: acc.storage_overlay.borrow().iter().fold(BTreeMap::new(), |mut m, (k, v)| {m.insert(k.clone(), v.clone()); m}),
code: acc.code_cache.clone()
}
}
pub fn rlp(&self) -> Bytes {
let mut stream = RlpStream::new_list(4);
stream.append(&self.nonce);
stream.append(&self.balance);
// TODO.
stream.append(&SHA3_NULL_RLP);
stream.append(&self.code.sha3());
stream.out()
}
}
impl Account { impl Account {
/// General constructor. /// General constructor.
pub fn new(balance: U256, nonce: U256, storage: HashMap<H256, H256>, code: Bytes) -> Account { pub fn new(balance: U256, nonce: U256, storage: HashMap<H256, H256>, code: Bytes) -> Account {
@ -61,7 +72,7 @@ impl Account {
balance: pod.balance, balance: pod.balance,
nonce: pod.nonce, nonce: pod.nonce,
storage_root: SHA3_NULL_RLP, storage_root: SHA3_NULL_RLP,
storage_overlay: RefCell::new(pod.storage), storage_overlay: RefCell::new(pod.storage.into_iter().fold(HashMap::new(), |mut m, (k, v)| {m.insert(k, v); m})),
code_hash: Some(pod.code.sha3()), code_hash: Some(pod.code.sha3()),
code_cache: pod.code code_cache: pod.code
} }
@ -242,7 +253,7 @@ impl Account {
impl fmt::Debug for Account { impl fmt::Debug for Account {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", PodAccount{balance: self.balance.clone(), nonce: self.nonce.clone(), storage: self.storage_overlay.borrow().clone(), code: self.code_cache.clone()}) write!(f, "{:?}", PodAccount::from_account(self))
} }
} }

View File

@ -181,12 +181,34 @@ impl State {
} }
/// Populate the state from `accounts`. /// Populate the state from `accounts`.
pub fn populate_from(&mut self, accounts: HashMap<Address, PodAccount>) { pub fn populate_from(&mut self, accounts: BTreeMap<Address, PodAccount>) {
for (add, acc) in accounts.into_iter() { for (add, acc) in accounts.into_iter() {
self.cache.borrow_mut().insert(add, Some(Account::from_pod(acc))); self.cache.borrow_mut().insert(add, Some(Account::from_pod(acc)));
} }
} }
/// Populate a PodAccount map from this state.
pub fn to_hashmap_pod(&self) -> HashMap<Address, PodAccount> {
// TODO: handle database rather than just the cache.
self.cache.borrow().iter().fold(HashMap::new(), |mut m, (add, opt)| {
if let &Some(ref acc) = opt {
m.insert(add.clone(), PodAccount::from_account(acc));
}
m
})
}
/// Populate a PodAccount map from this state.
pub fn to_pod_map(&self) -> BTreeMap<Address, PodAccount> {
// TODO: handle database rather than just the cache.
self.cache.borrow().iter().fold(BTreeMap::new(), |mut m, (add, opt)| {
if let &Some(ref acc) = opt {
m.insert(add.clone(), PodAccount::from_account(acc));
}
m
})
}
/// Pull account `a` in our cache from the trie DB and return it. /// Pull account `a` in our cache from the trie DB and return it.
/// `require_code` requires that the code be cached, too. /// `require_code` requires that the code be cached, too.
fn get(&self, a: &Address, require_code: bool) -> Ref<Option<Account>> { fn get(&self, a: &Address, require_code: bool) -> Ref<Option<Account>> {

View File

@ -9,18 +9,25 @@ pub fn hashmap_h256_h256_from_json(json: &Json) -> HashMap<H256, H256> {
}) })
} }
pub fn map_h256_h256_from_json(json: &Json) -> BTreeMap<H256, H256> {
json.as_object().unwrap().iter().fold(BTreeMap::new(), |mut m, (key, value)| {
m.insert(H256::from(&u256_from_hex(key)), H256::from(&u256_from_json(value)));
m
})
}
/// Translate the JSON object into a hash map of account information ready for insertion into State. /// Translate the JSON object into a hash map of account information ready for insertion into State.
pub fn pod_account_map_from_json(json: &Json) -> HashMap<Address, PodAccount> { pub fn pod_map_from_json(json: &Json) -> BTreeMap<Address, PodAccount> {
json.as_object().unwrap().iter().fold(HashMap::new(), |mut state, (address, acc)| { json.as_object().unwrap().iter().fold(BTreeMap::new(), |mut state, (address, acc)| {
let balance = acc.find("balance").map(&u256_from_json); let balance = acc.find("balance").map(&u256_from_json);
let nonce = acc.find("nonce").map(&u256_from_json); let nonce = acc.find("nonce").map(&u256_from_json);
let storage: Option<HashMap<H256, H256>> = acc.find("storage").map(&hashmap_h256_h256_from_json);; let storage = acc.find("storage").map(&map_h256_h256_from_json);;
let code = acc.find("code").map(&bytes_from_json); let code = acc.find("code").map(&bytes_from_json);
if balance.is_some() || nonce.is_some() { if balance.is_some() || nonce.is_some() || storage.is_some() || code.is_some() {
state.insert(address_from_hex(address), PodAccount{ state.insert(address_from_hex(address), PodAccount{
balance: balance.unwrap_or(U256::zero()), balance: balance.unwrap_or(U256::zero()),
nonce: nonce.unwrap_or(U256::zero()), nonce: nonce.unwrap_or(U256::zero()),
storage: storage.unwrap_or(HashMap::new()), storage: storage.unwrap_or(BTreeMap::new()),
code: code.unwrap_or(Vec::new()) code: code.unwrap_or(Vec::new())
}); });
} }
@ -42,8 +49,8 @@ fn do_json_test(json_data: &[u8]) -> Vec<String> {
let env = EnvInfo::from_json(&test["env"]); let env = EnvInfo::from_json(&test["env"]);
let out = bytes_from_json(&test["out"]); let out = bytes_from_json(&test["out"]);
let post_state_root = h256_from_json(&test["postStateRoot"]); let post_state_root = h256_from_json(&test["postStateRoot"]);
let pre = pod_account_map_from_json(&test["pre"]); let pre = pod_map_from_json(&test["pre"]);
let post = pod_account_map_from_json(&test["post"]); let post = pod_map_from_json(&test["post"]);
// TODO: read test["logs"] // TODO: read test["logs"]
println!("Transaction: {:?}", t); println!("Transaction: {:?}", t);
@ -55,12 +62,11 @@ fn do_json_test(json_data: &[u8]) -> Vec<String> {
let mut s = State::new_temp(); let mut s = State::new_temp();
s.populate_from(pre); s.populate_from(pre);
s.apply(&env, engine.deref(), &t).unwrap(); s.apply(&env, engine.deref(), &t).unwrap();
let our_post = s.to_pod_map();
println!("{:?} ? {:?}", s.root(), post_state_root);
if fail_unless(s.root() == &post_state_root) { if fail_unless(s.root() == &post_state_root) {
println!("EXPECTED:\n{:?}", post); println!("EXPECTED:\n{:?}", post);
println!("GOT:\n{:?}", s); println!("GOT:\n{:?}", our_post);
} }
// TODO: Compare logs. // TODO: Compare logs.