Store Move account data in a deterministic order (#5276)
This commit is contained in:
parent
535df0026d
commit
bab0f6be1e
|
@ -1,2 +1,3 @@
|
||||||
/farf/
|
/farf/
|
||||||
/target/
|
/target/
|
||||||
|
Cargo.lock
|
||||||
|
|
|
@ -11,6 +11,7 @@ edition = "2018"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bincode = "1.1.4"
|
bincode = "1.1.4"
|
||||||
byteorder = "1.3.2"
|
byteorder = "1.3.2"
|
||||||
|
indexmap = "1.0.2"
|
||||||
libc = "0.2.58"
|
libc = "0.2.58"
|
||||||
log = "0.4.2"
|
log = "0.4.2"
|
||||||
serde = "1.0.94"
|
serde = "1.0.94"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use failure::prelude::*;
|
use failure::prelude::*;
|
||||||
|
use indexmap::IndexMap;
|
||||||
use log::*;
|
use log::*;
|
||||||
use state_view::StateView;
|
use state_view::StateView;
|
||||||
use std::collections::HashMap;
|
|
||||||
use types::{
|
use types::{
|
||||||
access_path::AccessPath,
|
access_path::AccessPath,
|
||||||
account_address::AccountAddress,
|
account_address::AccountAddress,
|
||||||
|
@ -20,12 +20,12 @@ use vm_runtime::{
|
||||||
/// An in-memory implementation of [`StateView`] and [`RemoteCache`] for the VM.
|
/// An in-memory implementation of [`StateView`] and [`RemoteCache`] for the VM.
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct DataStore {
|
pub struct DataStore {
|
||||||
data: HashMap<AccessPath, Vec<u8>>,
|
data: IndexMap<AccessPath, Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DataStore {
|
impl DataStore {
|
||||||
/// Creates a new `DataStore` with the provided initial data.
|
/// Creates a new `DataStore` with the provided initial data.
|
||||||
pub fn new(data: HashMap<AccessPath, Vec<u8>>) -> Self {
|
pub fn new(data: IndexMap<AccessPath, Vec<u8>>) -> Self {
|
||||||
DataStore { data }
|
DataStore { data }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,9 +44,9 @@ impl DataStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a `WriteSet` for each account in the `DataStore`
|
/// Returns a `WriteSet` for each account in the `DataStore`
|
||||||
pub fn into_write_sets(mut self) -> Result<HashMap<AccountAddress, WriteSet>> {
|
pub fn into_write_sets(mut self) -> Result<IndexMap<AccountAddress, WriteSet>> {
|
||||||
let mut write_set_muts: HashMap<AccountAddress, WriteSetMut> = HashMap::new();
|
let mut write_set_muts: IndexMap<AccountAddress, WriteSetMut> = IndexMap::new();
|
||||||
for (access_path, value) in self.data.drain() {
|
for (access_path, value) in self.data.drain(..) {
|
||||||
match write_set_muts.get_mut(&access_path.address) {
|
match write_set_muts.get_mut(&access_path.address) {
|
||||||
Some(write_set_mut) => write_set_mut.push((access_path, WriteOp::Value(value))),
|
Some(write_set_mut) => write_set_mut.push((access_path, WriteOp::Value(value))),
|
||||||
None => {
|
None => {
|
||||||
|
@ -58,8 +58,8 @@ impl DataStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Freeze each WriteSet
|
// Freeze each WriteSet
|
||||||
let mut write_sets: HashMap<AccountAddress, WriteSet> = HashMap::new();
|
let mut write_sets: IndexMap<AccountAddress, WriteSet> = IndexMap::new();
|
||||||
for (address, write_set_mut) in write_set_muts.drain() {
|
for (address, write_set_mut) in write_set_muts.drain(..) {
|
||||||
write_sets.insert(address, write_set_mut.freeze()?);
|
write_sets.insert(address, write_set_mut.freeze()?);
|
||||||
}
|
}
|
||||||
Ok(write_sets)
|
Ok(write_sets)
|
||||||
|
@ -109,7 +109,7 @@ impl DataStore {
|
||||||
/// Dumps the data store to stdout
|
/// Dumps the data store to stdout
|
||||||
pub fn dump(&self) {
|
pub fn dump(&self) {
|
||||||
for (access_path, value) in &self.data {
|
for (access_path, value) in &self.data {
|
||||||
trace!("{:?}: \"{:?}\"", access_path, value.len());
|
trace!("{:?}: {:?}", access_path, value.len());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,3 +158,52 @@ fn get_account_struct_def() -> StructDef {
|
||||||
int_type.clone(),
|
int_type.clone(),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use types::account_address::ADDRESS_LENGTH;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_write_set_order() {
|
||||||
|
solana_logger::setup();
|
||||||
|
|
||||||
|
let mut data_store = DataStore::default();
|
||||||
|
let address1 = AccountAddress::new([0; ADDRESS_LENGTH]);
|
||||||
|
let address2 = AccountAddress::new([1; ADDRESS_LENGTH]);
|
||||||
|
let address3 = AccountAddress::new([2; ADDRESS_LENGTH]);
|
||||||
|
|
||||||
|
let mut before1 = WriteSetMut::default();
|
||||||
|
let mut before2 = WriteSetMut::default();
|
||||||
|
let mut before3 = WriteSetMut::default();
|
||||||
|
for i in 1..10 {
|
||||||
|
before1.push((
|
||||||
|
AccessPath::new(address1, AccountAddress::random().to_vec()),
|
||||||
|
WriteOp::Value(vec![i]),
|
||||||
|
));
|
||||||
|
before2.push((
|
||||||
|
AccessPath::new(address2, AccountAddress::random().to_vec()),
|
||||||
|
WriteOp::Value(vec![i]),
|
||||||
|
));
|
||||||
|
before3.push((
|
||||||
|
AccessPath::new(address3, AccountAddress::random().to_vec()),
|
||||||
|
WriteOp::Value(vec![i]),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let before1 = before1.freeze().unwrap();
|
||||||
|
let before2 = before2.freeze().unwrap();
|
||||||
|
let before3 = before3.freeze().unwrap();
|
||||||
|
data_store.apply_write_set(&before1);
|
||||||
|
data_store.apply_write_set(&before2);
|
||||||
|
data_store.apply_write_set(&before3);
|
||||||
|
|
||||||
|
let write_sets = data_store.into_write_sets().unwrap();
|
||||||
|
let after1 = write_sets.get(&address1).unwrap();
|
||||||
|
let after2 = write_sets.get(&address2).unwrap();
|
||||||
|
let after3 = write_sets.get(&address3).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(&before1, after1);
|
||||||
|
assert_eq!(&before2, after2);
|
||||||
|
assert_eq!(&before3, after3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue