261 lines
7.9 KiB
Rust
261 lines
7.9 KiB
Rust
use ethereum_types::{Address, H256};
|
|
use std::{
|
|
borrow::Borrow,
|
|
collections::HashMap,
|
|
hash::{Hash, Hasher},
|
|
};
|
|
|
|
use std::{cell::RefCell, rc::Rc};
|
|
|
|
// Implementation of a hasheable borrowed pair
|
|
trait KeyPair<A, B> {
|
|
fn a(&self) -> &A;
|
|
fn b(&self) -> &B;
|
|
}
|
|
impl<'a, A, B> Borrow<dyn KeyPair<A, B> + 'a> for (A, B)
|
|
where
|
|
A: Eq + Hash + 'a,
|
|
B: Eq + Hash + 'a,
|
|
{
|
|
fn borrow(&self) -> &(dyn KeyPair<A, B> + 'a) {
|
|
self
|
|
}
|
|
}
|
|
impl<A: Hash, B: Hash> Hash for (dyn KeyPair<A, B> + '_) {
|
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
self.a().hash(state);
|
|
self.b().hash(state);
|
|
}
|
|
}
|
|
impl<A: Eq, B: Eq> PartialEq for (dyn KeyPair<A, B> + '_) {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
self.a() == other.a() && self.b() == other.b()
|
|
}
|
|
}
|
|
impl<A: Eq, B: Eq> Eq for (dyn KeyPair<A, B> + '_) {}
|
|
impl<A, B> KeyPair<A, B> for (A, B) {
|
|
fn a(&self) -> &A {
|
|
&self.0
|
|
}
|
|
fn b(&self) -> &B {
|
|
&self.1
|
|
}
|
|
}
|
|
impl<A, B> KeyPair<A, B> for (&A, &B) {
|
|
fn a(&self) -> &A {
|
|
self.0
|
|
}
|
|
fn b(&self) -> &B {
|
|
self.1
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct Journal {
|
|
enabled: bool,
|
|
last_id: usize,
|
|
addresses: HashMap<Address, usize>,
|
|
storage_keys: HashMap<(Address, H256), usize>,
|
|
}
|
|
#[derive(Debug)]
|
|
pub struct AccessList {
|
|
id: usize,
|
|
journal: Rc<RefCell<Journal>>,
|
|
}
|
|
|
|
impl Clone for AccessList {
|
|
fn clone(&self) -> Self {
|
|
let mut journal = self.journal.as_ref().borrow_mut();
|
|
let id = journal.last_id + 1;
|
|
journal.last_id = id;
|
|
Self {
|
|
id: id,
|
|
journal: self.journal.clone(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Default for AccessList {
|
|
fn default() -> Self {
|
|
AccessList::new(false)
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Display for AccessList {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
let journal = self.journal.as_ref().borrow();
|
|
for (addr, id) in journal.addresses.iter() {
|
|
write!(f, "| ADDR {} -> {}\n", addr, id)?;
|
|
}
|
|
for ((addr, slot), id) in journal.storage_keys.iter() {
|
|
write!(f, "| SLOT {}:{} -> {}\n", addr, slot, id)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl AccessList {
|
|
/// Returns if the list is enabled
|
|
pub fn new(enabled: bool) -> Self {
|
|
let journal = Journal {
|
|
enabled,
|
|
last_id: 0,
|
|
addresses: HashMap::new(),
|
|
storage_keys: HashMap::new(),
|
|
};
|
|
Self {
|
|
id: 0,
|
|
journal: Rc::new(RefCell::new(journal)),
|
|
}
|
|
}
|
|
|
|
/// Returns if the list is enabled
|
|
pub fn is_enabled(&self) -> bool {
|
|
let journal = self.journal.as_ref().borrow();
|
|
journal.enabled
|
|
}
|
|
|
|
/// Enable the access list control
|
|
pub fn enable(&mut self) {
|
|
let mut journal = self.journal.as_ref().borrow_mut();
|
|
journal.enabled = true;
|
|
}
|
|
|
|
/// Checks if contains an storage key
|
|
pub fn contains_storage_key(&self, address: &Address, key: &H256) -> bool {
|
|
let journal = self.journal.as_ref().borrow();
|
|
if journal.enabled {
|
|
journal
|
|
.storage_keys
|
|
.contains_key(&(address, key) as &dyn KeyPair<Address, H256>)
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
/// Inserts a storage key
|
|
pub fn insert_storage_key(&mut self, address: Address, key: H256) {
|
|
let mut journal = self.journal.as_ref().borrow_mut();
|
|
if journal.enabled
|
|
&& !journal
|
|
.storage_keys
|
|
.contains_key(&(address, key) as &dyn KeyPair<Address, H256>)
|
|
{
|
|
journal.storage_keys.insert((address, key), self.id);
|
|
}
|
|
}
|
|
|
|
/// Checks if contains an address
|
|
pub fn contains_address(&self, address: &Address) -> bool {
|
|
let journal = self.journal.as_ref().borrow();
|
|
if journal.enabled {
|
|
journal.addresses.contains_key(&address)
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
/// Inserts an address
|
|
pub fn insert_address(&mut self, address: Address) {
|
|
let mut journal = self.journal.as_ref().borrow_mut();
|
|
if journal.enabled && !journal.addresses.contains_key(&address) {
|
|
journal.addresses.insert(address, self.id);
|
|
}
|
|
}
|
|
/// Removes all changes in journal
|
|
pub fn rollback(&self) {
|
|
let mut journal = self.journal.as_ref().borrow_mut();
|
|
// `id < self.id` instead `id != self.if` is to take care about recursive calls
|
|
journal.addresses.retain(|_, id| *id < self.id);
|
|
journal.storage_keys.retain(|_, id| *id < self.id);
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
#[test]
|
|
fn default_accesslist_is_disabled() {
|
|
let access_list = AccessList::default();
|
|
assert_eq!(false, access_list.is_enabled());
|
|
}
|
|
|
|
#[test]
|
|
fn default_disabled_accesslist_does_nothing() {
|
|
let mut access_list = AccessList::default();
|
|
access_list.insert_address(Address::from(1));
|
|
access_list.insert_storage_key(Address::from(2), H256::from(3));
|
|
assert_eq!(false, access_list.contains_address(&Address::from(1)));
|
|
assert_eq!(
|
|
false,
|
|
access_list.contains_storage_key(&Address::from(2), &H256::from(3))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn default_enabled_accesslist_registers() {
|
|
let mut access_list = AccessList::default();
|
|
access_list.enable();
|
|
assert_eq!(true, access_list.is_enabled());
|
|
access_list.insert_address(Address::from(1));
|
|
access_list.insert_storage_key(Address::from(2), H256::from(3));
|
|
assert_eq!(true, access_list.contains_address(&Address::from(1)));
|
|
assert_eq!(
|
|
true,
|
|
access_list.contains_storage_key(&Address::from(2), &H256::from(3))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn cloned_accesslist_registers_in_parent() {
|
|
let mut access_list = AccessList::default();
|
|
access_list.enable();
|
|
assert_eq!(true, access_list.is_enabled());
|
|
access_list.insert_address(Address::from(1));
|
|
access_list.insert_storage_key(Address::from(2), H256::from(3));
|
|
|
|
let mut access_list_call = access_list.clone();
|
|
assert_eq!(true, access_list_call.contains_address(&Address::from(1)));
|
|
assert_eq!(
|
|
true,
|
|
access_list_call.contains_storage_key(&Address::from(2), &H256::from(3))
|
|
);
|
|
access_list.insert_address(Address::from(4));
|
|
assert_eq!(true, access_list_call.contains_address(&Address::from(4)));
|
|
|
|
assert_eq!(true, access_list.contains_address(&Address::from(4)));
|
|
}
|
|
#[test]
|
|
fn cloned_accesslist_rollbacks_in_parent() {
|
|
let mut access_list = AccessList::default();
|
|
access_list.enable();
|
|
assert_eq!(true, access_list.is_enabled());
|
|
access_list.insert_address(Address::from(1));
|
|
access_list.insert_storage_key(Address::from(2), H256::from(3));
|
|
|
|
let mut access_list_call = access_list.clone();
|
|
access_list_call.insert_address(Address::from(1));
|
|
access_list_call.insert_storage_key(Address::from(2), H256::from(3));
|
|
access_list_call.insert_address(Address::from(4));
|
|
|
|
let mut access_list_call_call = access_list.clone();
|
|
access_list_call_call.insert_address(Address::from(1));
|
|
access_list_call_call.insert_storage_key(Address::from(2), H256::from(3));
|
|
access_list_call_call.insert_address(Address::from(5));
|
|
access_list_call_call.insert_storage_key(Address::from(6), H256::from(7));
|
|
|
|
access_list_call.rollback();
|
|
|
|
assert_eq!(true, access_list.contains_address(&Address::from(1)));
|
|
assert_eq!(false, access_list.contains_address(&Address::from(4)));
|
|
assert_eq!(false, access_list.contains_address(&Address::from(5)));
|
|
assert_eq!(
|
|
true,
|
|
access_list.contains_storage_key(&Address::from(2), &H256::from(3))
|
|
);
|
|
assert_eq!(
|
|
false,
|
|
access_list.contains_storage_key(&Address::from(6), &H256::from(7))
|
|
);
|
|
}
|
|
}
|