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

feat(db): codec encoding/decoding #51

Merged
merged 43 commits into from
Oct 17, 2022
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
b77281b
wip
joshieDo Oct 5, 2022
2426fbf
add table macro
joshieDo Oct 5, 2022
70eb292
add simple put get test with Address
joshieDo Oct 5, 2022
0159538
add Env.view and Env.update
joshieDo Oct 6, 2022
14d7c06
docs
joshieDo Oct 6, 2022
2458373
slightly change the test
joshieDo Oct 6, 2022
5b9dc7c
add initial table initialization and placeholders
joshieDo Oct 6, 2022
ef76ca2
lint & some
joshieDo Oct 6, 2022
c130ad3
replace String with str
joshieDo Oct 6, 2022
a7f2f8f
Merge branch 'main' into joshie/kv_mdbx
joshieDo Oct 7, 2022
a36cb71
add error.rs
joshieDo Oct 7, 2022
940cb87
add docs to encode
joshieDo Oct 10, 2022
3cdc13d
add docs
joshieDo Oct 10, 2022
86c216a
clamp
joshieDo Oct 10, 2022
bd5c9b9
add source on libmdbx_max_page_size
joshieDo Oct 10, 2022
ce9c505
Merge branch 'main' into joshie/kv_mdbx
joshieDo Oct 10, 2022
7a2bf87
add BlockNumer_BlockHash
joshieDo Oct 10, 2022
837ce40
add scale
joshieDo Oct 12, 2022
d5b0b4d
Merge branch 'main' into joshie/scale
joshieDo Oct 12, 2022
2534e1c
set header filed to bytes Bytes
joshieDo Oct 12, 2022
5167017
remove unwrap
joshieDo Oct 12, 2022
31b06f7
restrict scale to chosen types
joshieDo Oct 12, 2022
88549df
into bytes
joshieDo Oct 13, 2022
af9614e
add postcard
joshieDo Oct 13, 2022
f892996
Merge branch 'main' into joshie/scale
joshieDo Oct 13, 2022
c8dfdc5
changed to BlockNumHash
joshieDo Oct 13, 2022
bb5d63a
add proc_macro_attribute codecs
joshieDo Oct 13, 2022
aac1e6f
fix feature flagging
joshieDo Oct 13, 2022
52e83e5
set a version for postcard
joshieDo Oct 13, 2022
18b4345
cleanup
joshieDo Oct 13, 2022
d7e23d6
seal ScaleOnly
joshieDo Oct 13, 2022
c96f421
remove unnecessary dependencies
joshieDo Oct 13, 2022
957fa17
properly encode/decode blocknumhash
joshieDo Oct 14, 2022
b37cab2
change Account codec to scale
joshieDo Oct 14, 2022
e427d78
add missing feature to scale
joshieDo Oct 14, 2022
34dc2bb
add codec to a couple more types
joshieDo Oct 14, 2022
58ceb34
silence clippy
joshieDo Oct 14, 2022
0ad054b
add docs about table encoding
joshieDo Oct 14, 2022
5cb39a3
move and add reth-codecs
joshieDo Oct 14, 2022
6a82be9
clippy
joshieDo Oct 14, 2022
e0e5813
make proc-macro visible
joshieDo Oct 17, 2022
78863d9
add README.md
joshieDo Oct 17, 2022
bc56452
Merge branch 'main' into joshie/scale
joshieDo Oct 17, 2022
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
239 changes: 235 additions & 4 deletions Cargo.lock

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions crates/codecs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "reth-codecs"
version = "0.1.0"
edition = "2021"

[features]
default = ["scale"]
scale = ["codecs-derive/scale"]
postcard = ["codecs-derive/postcard"]
no_codec = ["codecs-derive/no_codec"]

[dependencies]
codecs-derive = { version = "0.1.0", path = "./derive", default-features = false }
22 changes: 22 additions & 0 deletions crates/codecs/derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "codecs-derive"
version = "0.1.0"
edition = "2021"

gakonst marked this conversation as resolved.
Show resolved Hide resolved
[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "1.0", features = ["full"] }

# codecs
serde = { version = "1.0.*", default-features = false }
parity-scale-codec = { version = "3.2.1", features = ["derive", "bytes"] }

[lib]
proc-macro = true

[features]
default = ["scale"]
scale = []
postcard = []
no_codec = []
66 changes: 66 additions & 0 deletions crates/codecs/derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use proc_macro::{self, TokenStream};
use quote::quote;
use syn::{parse_macro_input, DeriveInput};

#[proc_macro_attribute]
#[rustfmt::skip]
#[allow(unreachable_code)]
pub fn main_codec(args: TokenStream, input: TokenStream) -> TokenStream {
#[cfg(feature = "scale")]
return use_scale(args, input);

#[cfg(feature = "postcard")]
return use_postcard(args, input);

#[cfg(feature = "no_codec")]
return no_codec(args, input);

// no features
no_codec(args, input)
}

#[proc_macro_attribute]
pub fn use_scale(_args: TokenStream, input: TokenStream) -> TokenStream {
let mut ast = parse_macro_input!(input as DeriveInput);
let compactable_types = ["u8", "u16", "u32", "i32", "i64", "u64", "f32", "f64"];

if let syn::Data::Struct(ref mut data) = &mut ast.data {
if let syn::Fields::Named(fields) = &mut data.fields {
for field in fields.named.iter_mut() {
if let syn::Type::Path(ref path) = field.ty {
if !path.path.segments.is_empty() {
let _type = format!("{}", path.path.segments[0].ident);
if compactable_types.contains(&_type.as_str()) {
field.attrs.push(syn::parse_quote! {
#[codec(compact)]
});
}
}
}
}
}
}

quote! {
#[derive(parity_scale_codec::Encode, parity_scale_codec::Decode)]
#ast
}
.into()
}

#[proc_macro_attribute]
pub fn use_postcard(_args: TokenStream, input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);

quote! {
#[derive(serde::Serialize, serde::Deserialize)]
#ast
}
.into()
}

#[proc_macro_attribute]
pub fn no_codec(_args: TokenStream, input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
quote! { #ast }.into()
}
1 change: 1 addition & 0 deletions crates/codecs/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub use codecs_derive::*;
6 changes: 6 additions & 0 deletions crates/db/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ description = "Staged syncing primitives used in reth."
# reth
reth-primitives = { path = "../primitives" }

# codecs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't these be optional?

serde = { version = "1.0.*", default-features = false }
postcard = { version = "1.0.2", features = ["heapless"] }
heapless = "0.7.16"
parity-scale-codec = { version = "3.2.1", features = ["bytes"] }

# misc
bytes = "1.2.1"
libmdbx = "0.1.8"
Expand Down
2 changes: 2 additions & 0 deletions crates/db/src/kv/codecs/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
mod postcard;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these be feature gated?

mod scale;
35 changes: 35 additions & 0 deletions crates/db/src/kv/codecs/postcard.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#![allow(unused)]

use crate::kv::{Decode, Encode, KVError};
use heapless::Vec;
use postcard::{from_bytes, to_vec};
use reth_primitives::Account;

// Just add `Serialize` and `Deserialize`, and set impl_heapless_postcard!(T, MaxSize(T))
//
//
// use serde::{Deserialize, Serialize};
//
// #[derive(Serialize, Deserialize )]
// pub struct T {
// }
//
// impl_heapless_postcard!(T, MaxSize(T))

macro_rules! impl_heapless_postcard {
($name:tt, $static_size:tt) => {
impl Encode for $name {
type Encoded = Vec<u8, $static_size>;

fn encode(self) -> Self::Encoded {
to_vec(&self).expect("Failed to encode")
}
}

impl Decode for $name {
fn decode<B: Into<bytes::Bytes>>(value: B) -> Result<Self, KVError> {
from_bytes(&value.into()).map_err(|_| KVError::InvalidValue)
}
}
};
}
43 changes: 43 additions & 0 deletions crates/db/src/kv/codecs/scale.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use crate::kv::{Decode, Encode, KVError};
use parity_scale_codec::decode_from_bytes;
use reth_primitives::*;

mod sealed {
pub trait Sealed {}
}
/// Marker trait type to restrict the TableEncode and TableDecode with scale to chosen types.
pub trait ScaleOnly: sealed::Sealed {}

impl<T> Encode for T
where
T: ScaleOnly + parity_scale_codec::Encode + Sync + Send + std::fmt::Debug,
{
type Encoded = Vec<u8>;

fn encode(self) -> Self::Encoded {
parity_scale_codec::Encode::encode(&self)
}
}

impl<T> Decode for T
where
T: ScaleOnly + parity_scale_codec::Decode + Sync + Send + std::fmt::Debug,
{
fn decode<B: Into<bytes::Bytes>>(value: B) -> Result<T, KVError> {
decode_from_bytes(value.into()).map_err(|_| KVError::InvalidValue)
}
}

macro_rules! impl_scale {
($($name:tt),+) => {
$(
impl ScaleOnly for $name {}
impl sealed::Sealed for $name {}
)+
};
}

impl_scale!(u16, H256, U256, H160, u8, u64, Header, Account, Log, Receipt, TxType);

impl ScaleOnly for Vec<u8> {}
impl sealed::Sealed for Vec<u8> {}
4 changes: 2 additions & 2 deletions crates/db/src/kv/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,6 @@ pub enum KVError {
#[error("{0:?}")]
InitTransaction(Error),
/// Failed to decode or encode a key or value coming from a table..
#[error("{0:?}")]
InvalidValue(Option<String>),
#[error("Error decoding value.")]
InvalidValue,
}
29 changes: 20 additions & 9 deletions crates/db/src/kv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,17 @@ use tables::TABLES;

pub mod cursor;

pub mod models;
pub use models::*;

pub mod tx;
use tx::Tx;

mod error;
pub use error::KVError;

mod codecs;

/// Environment used when opening a MDBX environment. RO/RW.
#[derive(Debug)]
pub enum EnvKind {
Expand Down Expand Up @@ -164,9 +169,12 @@ pub mod test_utils {

#[cfg(test)]
mod tests {
use super::{tables::PlainState, test_utils, Env, EnvKind};
use super::{
tables::{Headers, PlainState},
test_utils, Env, EnvKind,
};
use libmdbx::{NoWriteMap, WriteMap};
use reth_primitives::Address;
use reth_primitives::{Account, Address, Header, H256, U256};
use std::str::FromStr;
use tempfile::TempDir;

Expand All @@ -187,18 +195,17 @@ mod tests {
fn db_manual_put_get() {
let env = test_utils::create_test_db::<NoWriteMap>(EnvKind::RW);

let value = vec![1, 3, 3, 7];
let key = Address::from_str("0xa2c122be93b0074270ebee7f6b7292c7deb45047")
.expect(ERROR_ETH_ADDRESS);
let value = Header::default();
let key = (1u64, H256::zero());

// PUT
let tx = env.begin_mut_tx().expect(ERROR_INIT_TX);
tx.put::<PlainState>(key, value.clone()).expect(ERROR_PUT);
tx.put::<Headers>(key.into(), value.clone()).expect(ERROR_PUT);
tx.commit().expect(ERROR_COMMIT);

// GET
let tx = env.begin_tx().expect(ERROR_INIT_TX);
let result = tx.get::<PlainState>(key).expect(ERROR_GET);
let result = tx.get::<Headers>(key.into()).expect(ERROR_GET);
assert!(result.expect(ERROR_RETURN_VALUE) == value);
tx.commit().expect(ERROR_COMMIT);
}
Expand All @@ -207,7 +214,11 @@ mod tests {
fn db_closure_put_get() {
let path = TempDir::new().expect(test_utils::ERROR_TEMPDIR).into_path();

let value = vec![1, 3, 3, 7];
let value = Account {
nonce: 18446744073709551615,
bytecode_hash: H256::random(),
balance: U256::max_value(),
};
let key = Address::from_str("0xa2c122be93b0074270ebee7f6b7292c7deb45047")
.expect(ERROR_ETH_ADDRESS);

Expand All @@ -216,7 +227,7 @@ mod tests {

// PUT
let result = env.update(|tx| {
tx.put::<PlainState>(key, value.clone()).expect(ERROR_PUT);
tx.put::<PlainState>(key, value).expect(ERROR_PUT);
200
});
assert!(result.expect(ERROR_RETURN_VALUE) == 200);
Expand Down
64 changes: 64 additions & 0 deletions crates/db/src/kv/models/blocks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//! Block related models and types.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's also that similar XForStorage types that Akula has, which we maybe could adopt as a pattern


use crate::kv::{
table::{Decode, Encode},
KVError,
};
use bytes::Bytes;
use reth_primitives::{BlockHash, BlockNumber, H256};

/// Total chain number of transactions. Key for [`CumulativeTxCount`].
pub type NumTransactions = u64;

/// Number of transactions in the block. Value for [`BlockBodies`].
pub type NumTxesInBlock = u16;

/// Hash of the block header. Value for [`CanonicalHeaders`]
pub type HeaderHash = H256;

/// BlockNumber concatenated with BlockHash. Used as a key for multiple tables. Having the first
/// element as BlockNumber, helps out with querying/sorting.
///
/// Since it's used as a key, the `BlockNumber` is not compressed when encoding it.
#[derive(Debug)]
#[allow(non_camel_case_types)]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we want non camel case here?

Copy link
Collaborator Author

@joshieDo joshieDo Oct 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#29 (comment)

I wanted some representation of a concatenation of (BlockNumber, BlockHash, TxNumber), open to change it man_shrugging

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imo just using camel case is sufficient

pub struct BlockNumHash((BlockNumber, BlockHash));

impl BlockNumHash {
/// Consumes `Self` and returns [`BlockNumber`], [`BlockHash`]
pub fn take(self) -> (BlockNumber, BlockHash) {
(self.0 .0, self.0 .1)
}
}

impl From<(u64, H256)> for BlockNumHash {
fn from(tpl: (u64, H256)) -> Self {
BlockNumHash(tpl)
}
}

impl Encode for BlockNumHash {
type Encoded = [u8; 40];

fn encode(self) -> Self::Encoded {
let number = self.0 .0;
let hash = self.0 .1;

let mut rnum = [0; 40];

rnum[..8].copy_from_slice(&number.to_be_bytes());
rnum[8..].copy_from_slice(&hash.encode());
rnum
}
}

impl Decode for BlockNumHash {
fn decode<B: Into<Bytes>>(value: B) -> Result<Self, KVError> {
let value: bytes::Bytes = value.into();

let num = u64::from_be_bytes(value.as_ref().try_into().map_err(|_| KVError::InvalidValue)?);
let hash = H256::decode(value.slice(8..))?;

Ok(BlockNumHash((num, hash)))
}
}
3 changes: 3 additions & 0 deletions crates/db/src/kv/models/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
//! Implements data structures specific to the database

pub mod blocks;
Loading