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

Fuzzing tests for ledger rpc #403

Merged
merged 55 commits into from
Jul 3, 2023
Merged
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
f7b4a80
Adding test rpc
theochap May 31, 2023
c8fc175
Adding test rpc
theochap May 31, 2023
2e8cb70
Adding test rpc
theochap May 31, 2023
22373fc
Tests rpc
theochap Jun 6, 2023
b956809
Tests rpc
theochap Jun 6, 2023
8a7c5c9
Testing rpc
theochap Jun 6, 2023
39824cd
Finishing the ledger rpc tests
theochap Jun 6, 2023
b25af17
Reverting TODO commit
theochap Jun 6, 2023
f2e3b6d
fixing rollup-config
theochap Jun 6, 2023
b8edbda
fixing dependencies
theochap Jun 6, 2023
74c794d
Fixing comments
theochap Jun 6, 2023
5c87030
Moving data structures to mocks
theochap Jun 6, 2023
0668e4f
Fixing pr comments
theochap Jun 7, 2023
3b30048
Changing curl to reqwest
theochap Jun 7, 2023
a72b97c
Fix merging conflicts
theochap Jun 7, 2023
867a59e
Fixing tests
theochap Jun 7, 2023
b9c76d1
Merge branch 'main' of github.com:Sovereign-Labs/sovereign into theo/…
theochap Jun 7, 2023
6900d32
adding more tests
theochap Jun 9, 2023
6a30323
Merging with main
theochap Jun 9, 2023
d6c27e3
Refactor tests
theochap Jun 9, 2023
0db4882
Adding proptest for get_head
theochap Jun 12, 2023
8c6872e
Adding proptest regression
theochap Jun 12, 2023
d88c448
Ledger getHead test
theochap Jun 13, 2023
2fc27c3
Get_batches
theochap Jun 14, 2023
9862be3
Finishing get batch
theochap Jun 14, 2023
717eb3c
Get_txs
theochap Jun 14, 2023
e897619
Get_events
theochap Jun 14, 2023
850dd74
Finishing ledger rpc fuzzing
theochap Jun 14, 2023
c9de376
Simple lint
theochap Jun 14, 2023
36e67e8
Remove unused methods
theochap Jun 14, 2023
0a85f40
Modify workflows to save the regression
theochap Jun 14, 2023
f581e1a
Try to force temp folder deletion
theochap Jun 15, 2023
a8cf96b
Fix lint
theochap Jun 16, 2023
5d3c8d1
Merging with main
theochap Jun 16, 2023
3f6f127
nit
theochap Jun 16, 2023
ebebcaa
re-enable flaky test
preston-evans98 Jun 19, 2023
d173a44
Use Arbitrary for Event proptest gen
preston-evans98 Jun 19, 2023
6f740b0
Try to save proptest regressions on cov
preston-evans98 Jun 19, 2023
fd73ee6
Use arbitrary for tx receipt
preston-evans98 Jun 19, 2023
0f52473
Rpc tests
preston-evans98 Jun 20, 2023
bb39b90
Merge branch 'main' into preston/rpc-tests
preston-evans98 Jun 20, 2023
4d34573
Merge branch 'main' into preston/rpc-tests
preston-evans98 Jun 20, 2023
c9fc6d1
Implement Arbitrary for BatchReceipts
preston-evans98 Jun 20, 2023
7345c93
Merge branch 'preston/rpc-tests' into theo/tests-rpc
preston-evans98 Jun 20, 2023
c6ccf75
Add bytes/serde dep to mocks
preston-evans98 Jun 20, 2023
d1b095b
Migrate ledger rpc proptests to any() impl
preston-evans98 Jun 20, 2023
b8b27e3
Fix clippy warnings
preston-evans98 Jun 20, 2023
6e8cc55
Update examples/demo-rollup/Cargo.toml
citizen-stig Jun 28, 2023
effab90
Address comments after discussion
citizen-stig Jun 28, 2023
19acf49
Use big-endian for serializing `u64_wrapper` keys in RocksDB (#443)
neysofu Jul 3, 2023
8470c7b
Linting
neysofu Jul 3, 2023
8d0af4c
Replace &Vec<...> with &[...]
neysofu Jul 3, 2023
9aaac75
Revert changes to .github/workflows/rust.yml
neysofu Jul 3, 2023
d2cd0bf
Small improvements to fuzzing.rs
neysofu Jul 3, 2023
729f4f0
Move MerkleHasher trait into fuzzing.rs
neysofu Jul 3, 2023
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
8 changes: 4 additions & 4 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ name: Rust

on:
push:
branches: [ "main" ]
branches: ["main"]
paths-ignore:
- '**.md'
- "**.md"
pull_request:
branches: [ "main" ]
branches: ["main"]
paths-ignore:
- '**.md'
- "**.md"

env:
CARGO_TERM_COLOR: always
Expand Down
2 changes: 2 additions & 0 deletions examples/demo-rollup/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,5 @@ sha2 = { workspace = true }
reqwest = "0.11"
tendermint = "0.32"
tempfile = { workspace = true }
proptest = { workspace = true }
sov-rollup-interface = { path = "../../rollup-interface", features = ["fuzzing"] }
11 changes: 11 additions & 0 deletions examples/demo-rollup/proptest-regressions/test_rpc.txt

Large diffs are not rendered by default.

587 changes: 490 additions & 97 deletions examples/demo-rollup/src/test_rpc.rs

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions full-node/db/sov-db/src/ledger_db/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,24 @@ pub struct ItemNumbers {
pub event_number: u64,
}

#[derive(Debug)]
pub struct SlotCommit<S: SlotData, B, T> {
slot_data: S,
batch_receipts: Vec<BatchReceipt<B, T>>,
num_txs: usize,
num_events: usize,
}

impl<S: SlotData, B, T> SlotCommit<S, B, T> {
pub fn slot_data(&self) -> &S {
&self.slot_data
}

pub fn batch_receipts(&self) -> &[BatchReceipt<B, T>] {
&self.batch_receipts
}
}

impl<S: SlotData, B, T> SlotCommit<S, B, T> {
pub fn new(slot_data: S) -> Self {
Self {
Expand Down
92 changes: 78 additions & 14 deletions full-node/db/sov-db/src/schema/tables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use jmt::{
storage::{Node, NodeKey},
Version,
};
use sov_rollup_interface::db::errors::CodecError;
use sov_rollup_interface::db::{errors::CodecError, SeekKeyEncoder};
use sov_rollup_interface::{
db::{KeyDecoder, KeyEncoder, ValueCodec},
stf::{Event, EventKey},
Expand Down Expand Up @@ -56,7 +56,7 @@ pub const LEDGER_TABLES: &[&str] = &[
/// ```ignore
/// define_table_without_codec!(
/// /// A table storing keys and value
/// (MyTable) MyKey | MyValue
/// (MyTable) MyKey => MyValue
/// )
///
/// // This impl must be written by hand
Expand Down Expand Up @@ -98,14 +98,35 @@ macro_rules! define_table_without_codec {
};
}

macro_rules! impl_borsh_value_codec {
($table_name:ident, $value:ty) => {
impl ::sov_rollup_interface::db::ValueCodec<$table_name> for $value {
fn encode_value(
&self,
) -> ::std::result::Result<
::sov_rollup_interface::maybestd::vec::Vec<u8>,
::sov_rollup_interface::db::errors::CodecError,
> {
::borsh::BorshSerialize::try_to_vec(self).map_err(Into::into)
}

fn decode_value(
data: &[u8],
) -> ::std::result::Result<Self, ::sov_rollup_interface::db::errors::CodecError> {
::borsh::BorshDeserialize::deserialize_reader(&mut &data[..]).map_err(Into::into)
}
}
};
}

/// Macro to define a table that implements [`sov_rollup_interface::db::Schema`].
/// Automatically generates KeyCodec<...> and ValueCodec<...> implementations
/// using the Encode and Decode traits from sov_rollup_interface
///
/// ```ignore
/// define_table_with_default_codec!(
/// /// A table storing keys and value
/// (MyTable) MyKey | MyValue
/// (MyTable) MyKey => MyValue
/// )
/// ```
macro_rules! define_table_with_default_codec {
Expand All @@ -114,30 +135,67 @@ macro_rules! define_table_with_default_codec {

impl ::sov_rollup_interface::db::KeyEncoder<$table_name> for $key {
fn encode_key(&self) -> ::std::result::Result<::sov_rollup_interface::maybestd::vec::Vec<u8>, ::sov_rollup_interface::db::errors::CodecError> {
<Self as ::borsh::BorshSerialize>::try_to_vec(self).map_err(|e| e.into())
::borsh::BorshSerialize::try_to_vec(self).map_err(Into::into)
}
}

impl ::sov_rollup_interface::db::KeyDecoder<$table_name> for $key {
fn decode_key(data: &[u8]) -> ::std::result::Result<Self, ::sov_rollup_interface::db::errors::CodecError> {
<Self as ::borsh::BorshDeserialize>::deserialize_reader(&mut &data[..]).map_err(|e| e.into())
::borsh::BorshDeserialize::deserialize_reader(&mut &data[..]).map_err(Into::into)
}
}

impl ::sov_rollup_interface::db::ValueCodec<$table_name> for $value {
fn encode_value(&self) -> ::std::result::Result<::sov_rollup_interface::maybestd::vec::Vec<u8>, ::sov_rollup_interface::db::errors::CodecError> {
<Self as ::borsh::BorshSerialize>::try_to_vec(self).map_err(|e| e.into())
impl_borsh_value_codec!($table_name, $value);
};
}

/// Macro similar to [`define_table_with_default_codec`], but to be used when
/// your key type should be [`SeekKeyEncoder`]. Borsh serializes integers as
/// little-endian, but RocksDB uses lexigographic ordering which is only
/// compatible with big-endian, so we use [`bincode`] with the big-endian option
/// here.
macro_rules! define_table_with_seek_key_codec {
($(#[$docs:meta])+ ($table_name:ident) $key:ty => $value:ty) => {
define_table_without_codec!($(#[$docs])+ ( $table_name ) $key => $value);

impl ::sov_rollup_interface::db::KeyEncoder<$table_name> for $key {
fn encode_key(&self) -> ::std::result::Result<::sov_rollup_interface::maybestd::vec::Vec<u8>, ::sov_rollup_interface::db::errors::CodecError> {
use ::anyhow::Context;
use ::bincode::Options;

let bincode_options = ::bincode::options()
.with_fixint_encoding()
.with_big_endian();

bincode_options.serialize(self).context("Failed to serialize key").map_err(Into::into)
}
}

impl ::sov_rollup_interface::db::KeyDecoder<$table_name> for $key {
fn decode_key(data: &[u8]) -> ::std::result::Result<Self, ::sov_rollup_interface::db::errors::CodecError> {
use ::anyhow::Context;
use ::bincode::Options;

let bincode_options = ::bincode::options()
.with_fixint_encoding()
.with_big_endian();

bincode_options.deserialize_from(&mut &data[..]).context("Failed to deserialize key").map_err(Into::into)
}
}

fn decode_value(data: &[u8]) -> ::std::result::Result<Self, ::sov_rollup_interface::db::errors::CodecError> {
<Self as ::borsh::BorshDeserialize>::deserialize_reader(&mut &data[..]).map_err(|e| e.into())
impl ::sov_rollup_interface::db::SeekKeyEncoder<$table_name> for $key {
fn encode_seek_key(&self) -> ::std::result::Result<::sov_rollup_interface::maybestd::vec::Vec<u8>, ::sov_rollup_interface::db::errors::CodecError> {
<Self as ::sov_rollup_interface::db::KeyEncoder<$table_name>>::encode_key(self)
}
}

impl_borsh_value_codec!($table_name, $value);
};
}

// fn deser(target: &mut &[u8]) -> Result<Self, DeserializationError>;
define_table_with_default_codec!(
define_table_with_seek_key_codec!(
/// The primary source for slot data
(SlotByNumber) SlotNumber => StoredSlot
);
Expand All @@ -147,7 +205,7 @@ define_table_with_default_codec!(
(SlotByHash) DbHash => SlotNumber
);

define_table_with_default_codec!(
define_table_with_seek_key_codec!(
/// The primary source for batch data
(BatchByNumber) BatchNumber => StoredBatch
);
Expand All @@ -157,7 +215,7 @@ define_table_with_default_codec!(
(BatchByHash) DbHash => BatchNumber
);

define_table_with_default_codec!(
define_table_with_seek_key_codec!(
/// The primary source for transaction data
(TxByNumber) TxNumber => StoredTransaction
);
Expand All @@ -167,7 +225,7 @@ define_table_with_default_codec!(
(TxByHash) DbHash => TxNumber
);

define_table_with_default_codec!(
define_table_with_seek_key_codec!(
/// The primary store for event data
(EventByNumber) EventNumber => Event
);
Expand Down Expand Up @@ -223,6 +281,12 @@ impl<T: AsRef<[u8]> + PartialEq + core::fmt::Debug> KeyEncoder<JmtValues> for (T
}
}

impl<T: AsRef<[u8]> + PartialEq + core::fmt::Debug> SeekKeyEncoder<JmtValues> for (T, Version) {
fn encode_seek_key(&self) -> sov_rollup_interface::db::Result<Vec<u8>> {
self.encode_key()
}
}

impl KeyDecoder<JmtValues> for (StateKey, Version) {
fn decode_key(data: &[u8]) -> sov_rollup_interface::db::Result<Self> {
let mut cursor = maybestd::io::Cursor::new(data);
Expand Down
2 changes: 2 additions & 0 deletions full-node/db/sov-db/src/schema/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use sov_rollup_interface::{
Clone, PartialEq, PartialOrd, Eq, Ord, Debug, Default, BorshDeserialize, BorshSerialize,
)]
pub struct DbBytes(Arc<Vec<u8>>);

impl DbBytes {
pub fn new(contents: Vec<u8>) -> Self {
Self(Arc::new(contents))
Expand Down Expand Up @@ -58,6 +59,7 @@ pub struct StoredSlot {
pub extra_data: DbBytes,
pub batches: std::ops::Range<BatchNumber>,
}

/// The on-disk format for a batch. Stores the hash and identifies the range of transactions
/// included in the batch
#[derive(Debug, PartialEq, BorshDeserialize, BorshSerialize)]
Expand Down
6 changes: 6 additions & 0 deletions full-node/db/sov-schema-db/src/iterator_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ impl KeyDecoder<TestSchema> for TestKey {
}
}

impl SeekKeyEncoder<TestSchema> for TestKey {
fn encode_seek_key(&self) -> sov_rollup_interface::db::Result<Vec<u8>> {
self.encode_key()
}
}

impl ValueCodec<TestSchema> for TestValue {
fn encode_value(&self) -> Result<Vec<u8>, CodecError> {
Ok(self.0.to_be_bytes().to_vec())
Expand Down
8 changes: 5 additions & 3 deletions rollup-interface/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,20 @@ hex = { workspace = true, features = ["serde"] }
jmt = { workspace = true }

sha2 = { workspace = true, optional = true }
tendermint = "0.32"

anyhow = { workspace = true }
thiserror = { workspace = true }

# Proptest should be a dev-dependency, but those can't be optional
proptest = { workspace = true, optional = true }
proptest-derive = { workspace = true, optional = true }

[dev-dependencies]
serde_json = "1"
proptest = { workspace = true }
proptest-derive = { workspace = true }

[features]
default = []
fuzzing = ["proptest"]
mocks = ["sha2"]
fuzzing = ["proptest", "proptest-derive", "sha2"]
mocks = ["sha2", "bytes/serde"]
12 changes: 0 additions & 12 deletions rollup-interface/src/node/db/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,6 @@ pub trait SeekKeyEncoder<S: Schema + ?Sized>: Sized {
fn encode_seek_key(&self) -> Result<Vec<u8>>;
}

/// All keys can automatically be used as seek keys.
impl<S, K> SeekKeyEncoder<S> for K
where
S: Schema,
K: KeyEncoder<S>,
{
/// Delegates to [`KeyCodec::encode_key`].
fn encode_seek_key(&self) -> Result<Vec<u8>> {
<K as KeyEncoder<S>>::encode_key(self)
}
}

#[macro_export]
macro_rules! define_schema {
($schema_type:ident, $key_type:ty, $value_type:ty, $cf_name:expr) => {
Expand Down
1 change: 1 addition & 0 deletions rollup-interface/src/state_machine/crypto/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
mod simple_hasher;
pub mod simple_merkle;
pub use simple_hasher::{NoOpHasher, SimpleHasher};
57 changes: 57 additions & 0 deletions rollup-interface/src/state_machine/crypto/simple_merkle.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
pub const DEFAULT_LEAF_DOMAIN_SEPARATOR: [u8; 1] = [0];
pub const DEFAULT_INTERNAL_DOMAIN_SEPARATOR: [u8; 1] = [1];

pub trait MerkleHasher {
neysofu marked this conversation as resolved.
Show resolved Hide resolved
/// The root hash for an empty tree
fn empty_root(&mut self) -> [u8; 32];

/// Hash a leaf node to create an inner node. This function *should* prepend a domain separator to the input bytes.
fn leaf_hash(&mut self, bytes: impl AsRef<[u8]>) -> [u8; 32];

/// Hash two inner nodes. This function *should* prepend a domain separator to the input.
fn inner_hash(&mut self, left: &[u8; 32], right: &[u8; 32]) -> [u8; 32];

/// A trait describing how to build a merkle tree from a slice of bytes. The default implementation
/// returns an RFC6962-style tree for ease of updating.
fn build_merkle_tree(&mut self, byte_vecs: &[impl AsRef<[u8]>]) -> [u8; 32] {
let length = byte_vecs.len();
match length {
0 => self.empty_root(),
1 => self.leaf_hash(byte_vecs[0].as_ref()),
_ => {
let split = length.next_power_of_two() / 2;
let left = self.build_merkle_tree(&byte_vecs[..split]);
let right = self.build_merkle_tree(&byte_vecs[split..]);
self.inner_hash(&left, &right)
}
}
}
}

#[macro_export]
/// Implement the default `MerkleHasher` trait for a `SimpleHasher`, allowing it to build
/// domain-separated RC6962-style merkle trees.
macro_rules! impl_merkle_hasher(
($name:ty) => {
impl $crate::crypto::simple_merkle::MerkleHasher for $name {
fn empty_root(&mut self) -> [u8; 32] {
<$name as SimpleHasher>::new().finalize()
}

fn leaf_hash(&mut self, bytes: impl AsRef<[u8]>) -> [u8; 32] {
let mut hasher = <$name as SimpleHasher>::new();
hasher.update(& $crate::crypto::simple_merkle::DEFAULT_LEAF_DOMAIN_SEPARATOR);
hasher.update(bytes.as_ref());
hasher.finalize()
}

fn inner_hash(&mut self, left: &[u8; 32], right: &[u8; 32]) -> [u8; 32] {
let mut hasher = <$name as SimpleHasher>::new();
hasher.update(& $crate::crypto::simple_merkle::DEFAULT_INTERNAL_DOMAIN_SEPARATOR);
hasher.update(left);
hasher.update(right);
hasher.finalize()
}
}
}
);
neysofu marked this conversation as resolved.
Show resolved Hide resolved
4 changes: 2 additions & 2 deletions rollup-interface/src/state_machine/mocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ use crate::{
};
use anyhow::{ensure, Error};
use borsh::{BorshDeserialize, BorshSerialize};
use sha2::Digest;

use bytes::Bytes;
use serde::{Deserialize, Serialize};
use std::fmt::Display;
use std::io::Write;
use tendermint::crypto::Sha256;

#[derive(Debug, Clone, PartialEq, Eq, BorshDeserialize, BorshSerialize, Serialize, Deserialize)]
pub struct MockCodeCommitment(pub [u8; 32]);
Expand Down Expand Up @@ -196,7 +196,7 @@ impl CanonicalHash for TestBlockHeader {
type Output = TestHash;

fn hash(&self) -> Self::Output {
TestHash(sha2::Sha256::digest(self.prev_hash.0))
TestHash(sha2::Sha256::digest(self.prev_hash.0).into())
}
}

Expand Down
Loading