Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add MerkleStorage trait to data #11

Merged
merged 20 commits into from
Aug 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions src/consts.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use fuel_tx::ContractId;
use fuel_asm::Word;
use fuel_tx::consts::*;
use fuel_tx::{Bytes32, Color};

use std::mem;

/* MEMORY TYPES */

Expand Down Expand Up @@ -80,4 +84,8 @@ pub const FUEL_MAX_PROGRAM_SIZE: u8 = 16;
// used for serder
pub const VM_REGISTER_WIDTH: u8 = 6;

pub const VM_CONTRACT_ID_BASE: ContractId = ContractId::new([0xab; ContractId::size_of()]);
pub const VM_TX_MEMORY: usize = Bytes32::size_of() // Tx ID
+ mem::size_of::<Word>() // Tx size
+ MAX_INPUTS as usize * (
Color::size_of() + mem::size_of::<Word>()
); // Color/Balance coin input pairs
156 changes: 152 additions & 4 deletions src/crypto.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use fuel_tx::{crypto as tx_crypto, Bytes32};
use fuel_tx::crypto::Hasher;
use fuel_tx::Bytes32;
use secp256k1::recovery::{RecoverableSignature, RecoveryId};
use secp256k1::Error as Secp256k1Error;
use secp256k1::{Message, Secp256k1, SecretKey};

use std::convert::TryFrom;
use std::mem;

/// Sign a given message and compress the `v` to the signature
///
Expand Down Expand Up @@ -40,7 +42,153 @@ pub fn secp256k1_sign_compact_recover(signature: &[u8], message: &[u8]) -> Resul
<[u8; 64]>::try_from(&pk[1..]).map_err(|_| Secp256k1Error::InvalidPublicKey)
}

pub fn merkle_root(data: &[u8]) -> Bytes32 {
// TODO implement merkle root
tx_crypto::hash(data)
/// Calculate a binary merkle root
///
/// The space complexity of this operation is O(n). This means it expects small
/// sets. For bigger sets (e.g. blockchain state), use a storage backed merkle
/// implementation
pub fn ephemeral_merkle_root<L, I>(mut leaves: I) -> Bytes32
where
L: AsRef<[u8]>,
I: Iterator<Item = L> + ExactSizeIterator,
{
let mut hasher = Hasher::default();
let mut width = leaves.len().next_power_of_two();
let mut len = leaves.len() as f32;

if width <= 2 {
return leaves.collect::<Hasher>().digest();
}

width /= 2;
len /= 2.0;

let mut current = vec![Bytes32::default(); width];

// Drain the leaves
current.iter_mut().for_each(|l| {
hasher.reset();

leaves.next().map(|a| hasher.input(a));
leaves.next().map(|b| hasher.input(b));

*l = hasher.digest();
});

let mut next = current.clone();

// Cheap loop with no realloc
while width > 1 {
mem::swap(&mut current, &mut next);

let mut c = current.iter().take(len.ceil() as usize);

width /= 2;
len /= 2.0;

next.iter_mut().take(width).for_each(|n| {
hasher.reset();

c.next().map(|a| hasher.input(a));
c.next().map(|b| hasher.input(b));

*n = hasher.digest();
});
}

next[0]
}

#[cfg(test)]
mod tests {
use super::*;
use crate::prelude::*;

use fuel_tx::crypto::Hasher;
use rand::rngs::StdRng;
use rand::{Rng, RngCore, SeedableRng};
use secp256k1::PublicKey;

use std::convert::TryFrom;

#[test]
fn ecrecover() {
let secp = Secp256k1::new();
let mut rng = StdRng::seed_from_u64(2322u64);
let mut secret_seed = [0u8; 32];
let mut message = [0u8; 95];

for _ in 0..10 {
rng.fill_bytes(&mut message);
rng.fill_bytes(&mut secret_seed);

let secret = SecretKey::from_slice(&secret_seed).expect("Failed to generate random secret!");
let public = PublicKey::from_secret_key(&secp, &secret).serialize_uncompressed();
let public = <[u8; 64]>::try_from(&public[1..]).expect("Failed to parse public key!");

let e = Hasher::hash(&message);

let sig =
secp256k1_sign_compact_recoverable(secret.as_ref(), e.as_ref()).expect("Failed to generate signature");
let pk_p = secp256k1_sign_compact_recover(&sig, e.as_ref()).expect("Failed to recover PK from signature");

assert_eq!(public, pk_p);
}
}

#[test]
fn ephemeral_merkle_root_works() {
let mut rng = StdRng::seed_from_u64(2322u64);

// Test for 0 leaves
let empty: Vec<Address> = vec![];

let root = ephemeral_merkle_root(empty.iter());
let empty = Hasher::default().digest();

assert_eq!(empty, root);

// Test for 5 leaves
let a: Address = rng.gen();
let b: Address = rng.gen();
let c: Address = rng.gen();
let d: Address = rng.gen();
let e: Address = rng.gen();

let initial = [a, b, c, d, e];

let a = [a, b].iter().collect::<Hasher>().digest();
let b = [c, d].iter().collect::<Hasher>().digest();
let c = [e].iter().collect::<Hasher>().digest();

let a = [a, b].iter().collect::<Hasher>().digest();
let b = [c].iter().collect::<Hasher>().digest();

let root = [a, b].iter().collect::<Hasher>().digest();
let root_p = ephemeral_merkle_root(initial.iter());

assert_eq!(root, root_p);

// Test for n leaves
let mut inputs = vec![Address::default(); 64];

inputs.iter_mut().for_each(|i| *i = rng.gen());

(0..65).into_iter().for_each(|w| {
let initial: Vec<&Address> = inputs.iter().take(w).collect();
let mut level: Vec<Bytes32> = initial
.chunks(2)
.map(|c| c.iter().collect::<Hasher>().digest())
.collect();

while level.len() > 1 {
level = level.chunks(2).map(|c| c.iter().collect::<Hasher>().digest()).collect();
}

let root = level.first().copied().unwrap_or(empty);
let root_p = ephemeral_merkle_root(initial.iter());

assert_eq!(root, root_p);
});
}
}
88 changes: 79 additions & 9 deletions src/data.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::interpreter::Contract;

use fuel_asm::Word;
use fuel_tx::{Bytes32, Color, ContractId};
use fuel_tx::{Address, Bytes32, Color, ContractId, Salt};

use std::ops::DerefMut;

Expand All @@ -12,15 +12,14 @@ pub use error::DataError;
pub use memory::MemoryStorage;

pub trait Key {}

pub trait Value {}

pub trait Storage<K, V>
where
K: Key,
V: Value,
{
fn insert(&mut self, key: K, value: V) -> Result<Option<V>, DataError>;
fn insert(&mut self, key: &K, value: &V) -> Result<Option<V>, DataError>;
fn remove(&mut self, key: &K) -> Result<Option<V>, DataError>;

// This initial implementation safeguard from the complex scenarios when a
Expand All @@ -37,7 +36,7 @@ where
S: Storage<K, V>,
I: DerefMut<Target = S>,
{
fn insert(&mut self, key: K, value: V) -> Result<Option<V>, DataError> {
fn insert(&mut self, key: &K, value: &V) -> Result<Option<V>, DataError> {
<S as Storage<K, V>>::insert(self.deref_mut(), key, value)
}

Expand All @@ -54,25 +53,96 @@ where
}
}

pub trait MerkleStorage<P, K, V>
where
P: Key,
K: Key,
V: Value,
{
fn insert(&mut self, parent: &P, key: &K, value: &V) -> Result<Option<V>, DataError>;
fn remove(&mut self, parent: &P, key: &K) -> Result<Option<V>, DataError>;
fn get(&self, parent: &P, key: &K) -> Result<Option<V>, DataError>;
fn contains_key(&self, parent: &P, key: &K) -> Result<bool, DataError>;
fn root(&mut self, parent: &P) -> Result<Bytes32, DataError>;
}

impl<P, K, V, X, I> MerkleStorage<P, K, V> for I
where
P: Key,
K: Key,
V: Value,
X: MerkleStorage<P, K, V>,
I: DerefMut<Target = X>,
{
fn insert(&mut self, parent: &P, key: &K, value: &V) -> Result<Option<V>, DataError> {
<X as MerkleStorage<P, K, V>>::insert(self.deref_mut(), parent, key, value)
}

fn remove(&mut self, parent: &P, key: &K) -> Result<Option<V>, DataError> {
<X as MerkleStorage<P, K, V>>::remove(self.deref_mut(), parent, key)
}

fn get(&self, parent: &P, key: &K) -> Result<Option<V>, DataError> {
<X as MerkleStorage<P, K, V>>::get(self.deref(), parent, key)
}

fn contains_key(&self, parent: &P, key: &K) -> Result<bool, DataError> {
<X as MerkleStorage<P, K, V>>::contains_key(self.deref(), parent, key)
}

fn root(&mut self, parent: &P) -> Result<Bytes32, DataError> {
<X as MerkleStorage<P, K, V>>::root(self.deref_mut(), parent)
}
}

// TODO use trait aliases after stable release
// https://github.com/rust-lang/rust/issues/41517

/// When this trait is implemented, the underlying interpreter is guaranteed to
/// have full functionality
pub trait InterpreterStorage:
Storage<ContractId, Contract> + Storage<(ContractId, Color), Word> + Storage<(ContractId, Bytes32), Bytes32>
Storage<ContractId, (Salt, Bytes32)>
+ Storage<ContractId, Contract>
+ MerkleStorage<ContractId, Color, Word>
+ MerkleStorage<ContractId, Bytes32, Bytes32>
{
fn block_height(&self) -> Result<u32, DataError>;
fn block_hash(&self, block_height: u32) -> Result<Bytes32, DataError>;
fn coinbase(&self) -> Result<Address, DataError>;
}

impl<S, I> InterpreterStorage for I
where
S: InterpreterStorage,
I: DerefMut<Target = S>,
{
fn block_height(&self) -> Result<u32, DataError> {
<S as InterpreterStorage>::block_height(self.deref())
}

fn block_hash(&self, block_height: u32) -> Result<Bytes32, DataError> {
<S as InterpreterStorage>::block_hash(self.deref(), block_height)
}

fn coinbase(&self) -> Result<Address, DataError> {
<S as InterpreterStorage>::coinbase(self.deref())
}
}

// Provisory implementation that will cover ID definitions until client backend
// is implemented
impl Key for Bytes32 {}
impl Key for Color {}
impl Key for ContractId {}
impl Key for (ContractId, Color) {}
impl Key for (ContractId, Bytes32) {}
impl Value for Word {}
impl Value for Contract {}

impl Value for Bytes32 {}
impl Value for Contract {}
impl Value for Salt {}
impl Value for Word {}

impl<A, B> Value for (A, B)
where
A: Value,
B: Value,
{
}
Loading