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

Archival state support for state_db and native_db #1170

Draft
wants to merge 15 commits into
base: nightly
Choose a base branch
from
4 changes: 2 additions & 2 deletions examples/demo-rollup/src/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use sov_ethereum::GasPriceOracleConfig;
use sov_modules_api::default_context::DefaultContext;
use sov_modules_api::default_signature::private_key::DefaultPrivateKey;
use sov_rollup_interface::services::da::DaService;
use sov_state::ProverStorage;
use sov_state::Storages;

const TX_SIGNER_PRIV_KEY_PATH: &str = "../test-data/keys/tx_signer_private_key.json";

Expand All @@ -25,7 +25,7 @@ fn read_sov_tx_signer_priv_key() -> Result<DefaultPrivateKey, anyhow::Error> {
// register ethereum methods.
pub(crate) fn register_ethereum<Da: DaService>(
da_service: Da,
storage: ProverStorage<sov_state::DefaultStorageSpec>,
storage: Storages<sov_state::DefaultStorageSpec>,
methods: &mut jsonrpsee::RpcModule<()>,
) -> Result<(), anyhow::Error> {
let eth_rpc_config = {
Expand Down
42 changes: 31 additions & 11 deletions full-node/db/sov-db/src/native_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ use sov_schema_db::{SchemaBatch, DB};

use crate::rocks_db_config::gen_rocksdb_options;
use crate::schema::tables::{ModuleAccessoryState, NATIVE_TABLES};
use crate::schema::types::StateKey;
use crate::schema::types::AccessoryKey;

/// Specifies a particular version of the Accessory state.
pub type Version = u64;

/// A typed wrapper around RocksDB for storing native-only accessory state.
/// Internally, this is roughly just an [`Arc<SchemaDB>`].
Expand Down Expand Up @@ -37,20 +40,37 @@ impl NativeDB {
}

/// Queries for a value in the [`NativeDB`], given a key.
pub fn get_value_option(&self, key: &StateKey) -> anyhow::Result<Option<Vec<u8>>> {
self.db
.get::<ModuleAccessoryState>(key)
.map(Option::flatten)
pub fn get_value_option(
&self,
key: &AccessoryKey,
version: Version,
) -> anyhow::Result<Option<Vec<u8>>> {
let mut iter = self.db.iter::<ModuleAccessoryState>()?;
Copy link
Member

Choose a reason for hiding this comment

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

With this change, we use iterator on every access to the database.

Do we want to measure performance impact on regular execution?

Maybe it is too late to discuss it, but it looks like archival state taxes on regular execution.

I am raising this point, because together with ForkManagement our storage can become bottleneck.

iter.seek_for_prev(&(key.to_vec(), version))?;
let found = iter.next();
match found {
Some(result) => {
let ((found_key, found_version), value) = result?;
if &found_key == key {
anyhow::ensure!(found_version <= version, "Bug! iterator isn't returning expected values. expected a version <= {version:} but found {found_version:}");
Ok(value)
} else {
Ok(None)
}
}
None => Ok(None),
}
}

/// Sets a sequence of key-value pairs in the [`NativeDB`]. The write is atomic.
pub fn set_values(
&self,
key_value_pairs: impl IntoIterator<Item = (Vec<u8>, Option<Vec<u8>>)>,
version: Version,
) -> anyhow::Result<()> {
let mut batch = SchemaBatch::default();
for (key, value) in key_value_pairs {
batch.put::<ModuleAccessoryState>(&key, &value)?;
batch.put::<ModuleAccessoryState>(&(key, version), &value)?;
}
self.db.write_schemas(batch)
}
Expand Down Expand Up @@ -144,9 +164,9 @@ mod tests {

let key = b"foo".to_vec();
let value = b"bar".to_vec();
db.set_values(vec![(key.clone(), Some(value.clone()))])
db.set_values(vec![(key.clone(), Some(value.clone()))], 0)
.unwrap();
assert_eq!(db.get_value_option(&key).unwrap(), Some(value));
assert_eq!(db.get_value_option(&key, 0).unwrap(), Some(value));
}

#[test]
Expand All @@ -155,8 +175,8 @@ mod tests {
let db = NativeDB::with_path(tmpdir.path()).unwrap();

let key = b"deleted".to_vec();
db.set_values(vec![(key.clone(), None)]).unwrap();
assert_eq!(db.get_value_option(&key).unwrap(), None);
db.set_values(vec![(key.clone(), None)], 0).unwrap();
assert_eq!(db.get_value_option(&key, 0).unwrap(), None);
}

#[test]
Expand All @@ -165,6 +185,6 @@ mod tests {
let db = NativeDB::with_path(tmpdir.path()).unwrap();

let key = b"spam".to_vec();
assert_eq!(db.get_value_option(&key).unwrap(), None);
assert_eq!(db.get_value_option(&key, 0).unwrap(), None);
}
}
49 changes: 44 additions & 5 deletions full-node/db/sov-db/src/schema/tables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,11 +219,6 @@ define_table_with_default_codec!(
(SlotByHash) DbHash => SlotNumber
);

define_table_with_default_codec!(
/// Non-JMT state stored by a module for JSON-RPC use.
(ModuleAccessoryState) AccessoryKey => AccessoryStateValue
);

define_table_with_seek_key_codec!(
/// The primary source for batch data
(BatchByNumber) BatchNumber => StoredBatch
Expand Down Expand Up @@ -332,3 +327,47 @@ define_table_with_default_codec!(
/// which requires the ability to fetch values by hash.
(KeyHashToKey) [u8;32] => StateKey
);

define_table_without_codec!(
/// Non-JMT state stored by a module for JSON-RPC use.
(ModuleAccessoryState) (AccessoryKey, Version) => AccessoryStateValue
);

impl KeyEncoder<ModuleAccessoryState> for (AccessoryKey, Version) {
fn encode_key(&self) -> sov_schema_db::schema::Result<Vec<u8>> {
let mut out = Vec::with_capacity(self.0.len() + std::mem::size_of::<Version>() + 8);
self.0
.as_slice()
.serialize(&mut out)
.map_err(CodecError::from)?;
// Write the version in big-endian order so that sorting order is based on the most-significant bytes of the key
out.write_u64::<BigEndian>(self.1)
.expect("serialization to vec is infallible");
Ok(out)
}
}

impl SeekKeyEncoder<ModuleAccessoryState> for (AccessoryKey, Version) {
fn encode_seek_key(&self) -> sov_schema_db::schema::Result<Vec<u8>> {
<(Vec<u8>, u64) as KeyEncoder<ModuleAccessoryState>>::encode_key(self)
}
}

impl KeyDecoder<ModuleAccessoryState> for (AccessoryKey, Version) {
fn decode_key(data: &[u8]) -> sov_schema_db::schema::Result<Self> {
let mut cursor = maybestd::io::Cursor::new(data);
let key = Vec::<u8>::deserialize_reader(&mut cursor)?;
let version = cursor.read_u64::<BigEndian>()?;
Ok((key, version))
}
}

impl ValueCodec<ModuleAccessoryState> for AccessoryStateValue {
fn encode_value(&self) -> sov_schema_db::schema::Result<Vec<u8>> {
self.try_to_vec().map_err(CodecError::from)
}

fn decode_value(data: &[u8]) -> sov_schema_db::schema::Result<Self> {
Ok(Self::deserialize_reader(&mut &data[..])?)
}
}
6 changes: 3 additions & 3 deletions full-node/sov-sequencer/src/batch_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ mod tests {
use sov_modules_api::transaction::Transaction;
use sov_modules_api::{Context, DispatchCall, EncodeCall, Genesis, MessageCodec, PrivateKey};
use sov_rollup_interface::services::batch_builder::BatchBuilder;
use sov_state::{DefaultStorageSpec, ProverStorage, Storage};
use sov_state::{DefaultStorageSpec, ProverStorage, Storage, Storages};
use sov_value_setter::{CallMessage, ValueSetter, ValueSetterConfig};
use tempfile::TempDir;

Expand Down Expand Up @@ -235,7 +235,7 @@ mod tests {
tmpdir: &TempDir,
) -> (
FiFoStrictBatchBuilder<C, TestRuntime<C>>,
ProverStorage<DefaultStorageSpec>,
Storages<DefaultStorageSpec>,
) {
let storage = ProverStorage::<DefaultStorageSpec>::with_path(tmpdir.path()).unwrap();

Expand All @@ -248,7 +248,7 @@ mod tests {
(batch_builder, storage)
}

fn setup_runtime(storage: ProverStorage<DefaultStorageSpec>, admin: Option<DefaultPublicKey>) {
fn setup_runtime(storage: Storages<DefaultStorageSpec>, admin: Option<DefaultPublicKey>) {
let runtime = TestRuntime::<C>::default();
let mut working_set = WorkingSet::new(storage.clone());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use sov_modules_api::hooks::SlotHooks;
use sov_modules_api::utils::generate_address;
use sov_modules_api::{Address, Genesis, Spec, ValidityConditionChecker, WorkingSet};
use sov_state::storage::{NativeStorage, Storage, StorageProof};
use sov_state::{DefaultStorageSpec, ProverStorage};
use sov_state::{DefaultStorageSpec, Storages};

use crate::AttesterIncentives;

Expand All @@ -25,7 +25,7 @@ pub const INIT_HEIGHT: u64 = 0;
/// Consumes and commit the existing working set on the underlying storage
/// `storage` must be the underlying storage defined on the working set for this method to work.
pub(crate) fn commit_get_new_working_set(
storage: &ProverStorage<DefaultStorageSpec>,
storage: &Storages<DefaultStorageSpec>,
working_set: WorkingSet<C>,
) -> (jmt::RootHash, WorkingSet<C>) {
let (reads_writes, witness) = working_set.checkpoint().freeze();
Expand Down Expand Up @@ -141,7 +141,7 @@ pub(crate) struct ExecutionSimulationVars {
pub(crate) fn execution_simulation<Checker: ValidityConditionChecker<MockValidityCond>>(
rounds: u8,
module: &AttesterIncentives<C, MockZkvm, MockDaSpec, Checker>,
storage: &ProverStorage<DefaultStorageSpec>,
storage: &Storages<DefaultStorageSpec>,
attester_address: <C as Spec>::Address,
mut working_set: WorkingSet<C>,
) -> (
Expand Down
Loading