Skip to content
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
11 changes: 0 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ tokio = { version = "1", features = ["full"] }
# sqlite
lazy_static = "1.4"
rusqlite = { version = "0.31.0", features = ["bundled", "functions"] }
rusqlite_migration = "1.2.0"

clap = { version = "4.5", features = ["derive", "env"] }
hex = "0.4"
Expand Down
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,14 +153,15 @@ smaller build and test prerequisites as well.

At any point, run:
```bash
just doctor # build and pre-commit-check readiness
just doctor-e2e # machine images, devnet, and E2E state
just doctor # build/check readiness, including echo and yield images
just doctor-e2e # full E2E images, devnet, and E2E state
just doctor-all # both scopes
```
Each command prints the fix for anything missing. Keeping E2E artifacts out of
the base doctor prevents an otherwise build-ready checkout from appearing
permanently unhealthy. `just --list` shows every available recipe, and
`just check` is the pre-commit gate.
the base doctor, except for the echo/yield images used by standard Rust tests,
prevents an otherwise check-ready checkout from appearing permanently
unhealthy. `just --list` shows every available recipe, and `just check` is the
pre-commit gate.

### Running Examples

Expand Down
1 change: 0 additions & 1 deletion cartesi-rollups/node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ tokio = { workspace = true }
lazy_static = { workspace = true }
log = { workspace = true }
rusqlite = { workspace = true }
rusqlite_migration = { workspace = true }
hex = { workspace = true }
serde_json = "1.0"
tempfile = "3"
Expand Down
2 changes: 1 addition & 1 deletion cartesi-rollups/node/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ impl NodeConfig {
.web3_submit_rpc_url
.unwrap_or_else(|| args.web3_rpc_url.clone());

let mut storage = Storage::migrate(
let mut storage = Storage::initialize(
&args.state_dir,
&args.machine_path,
address_book.genesis_block_number,
Expand Down
2 changes: 1 addition & 1 deletion cartesi-rollups/node/src/bin/measure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ fn bench_quartets(
let mut results = Vec::new();
for (index, (label, log2_stride, height)) in spans.into_iter().enumerate() {
let state_dir = scratch(scratch_root, &format!("quartet-{index}"))?;
let mut storage = Storage::migrate(&state_dir, image, 0, Address::ZERO)?;
let mut storage = Storage::initialize(&state_dir, image, 0, Address::ZERO)?;
let rows: Vec<StorageInput> = inputs
.iter()
.enumerate()
Expand Down
2 changes: 1 addition & 1 deletion cartesi-rollups/node/src/blockchain_reader/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ mod blockchain_reader_tests {
.unwrap();
machine.store(&machine_path).unwrap();

let acc = Storage::migrate(state_dir, &machine_path, 0, Address::ZERO).unwrap();
let acc = Storage::initialize(state_dir, &machine_path, 0, Address::ZERO).unwrap();

(state_dir_, acc)
}
Expand Down
8 changes: 4 additions & 4 deletions cartesi-rollups/node/src/engine/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
//! The write-once configuration: everything contextual the cache rows
//! deliberately do not carry.
//!
//! This is migration-time state: the node's migration owns the DDL
//! (storage/sql/migrations.sql) and `pin` writes the row exactly once
//! This is initialization-time state: the node's schema owns the DDL
//! (storage/sql/schema.sql) and `pin` writes the row exactly once
//! at database creation; the dispute module only reads and asserts
//! (`assert_compatible`).

Expand All @@ -23,7 +23,7 @@ pub struct EngineConfig {
}

/// Pins the configuration, once per database; the schema comes from
/// the node migration. Idempotent for an identical configuration; any
/// node initialization. Idempotent for an identical configuration; any
/// drift is refused.
pub fn pin(connection: &Connection, config: &EngineConfig) -> Result<()> {
config.structure.assert_valid();
Expand Down Expand Up @@ -111,7 +111,7 @@ mod tests {
fn config_is_write_once() -> Result<()> {
let dir = tempfile::tempdir()?;
let path = dir.path().join("cache.db");
crate::storage::sql::migrations::migrate_to_latest(&mut Connection::open(&path)?)?;
crate::storage::sql::schema::initialize(&Connection::open(&path)?)?;
let structure = Structure {
log2_input_span: 1,
log2_barch_span: 1,
Expand Down
114 changes: 95 additions & 19 deletions cartesi-rollups/node/src/engine/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,38 @@ pub use cartesi_machine::constants::ar::SHADOW_REVERT_ROOT_HASH_START as CHECKPO
#[cfg(test)]
mod tests {
use super::{CHECKPOINT_ADDRESS, LOG2_EPOCH_RULER_SPAN};
use cartesi_machine::constants::rollup::{
LOG2_MAX_ADVANCE_STATES_PER_EPOCH, LOG2_MAX_MCYCLES_PER_ADVANCE_STATE,
LOG2_MAX_UARCH_CYCLES_PER_MCYCLE,
use cartesi_machine::{
Machine,
cartesi_machine_sys::{
CM_HTIF_CMD_MASK, CM_HTIF_CMD_SHIFT, CM_HTIF_DEV_MASK, CM_HTIF_DEV_SHIFT,
CM_HTIF_DEV_YIELD, CM_HTIF_REASON_MASK, CM_HTIF_REASON_SHIFT, CM_HTIF_YIELD_CMD_MANUAL,
CM_HTIF_YIELD_MANUAL_REASON_RX_ACCEPTED, CM_REG_HTIF_TOHOST, CM_REG_IFLAGS_Y,
},
constants::{
ar::TX_START,
machine::{HASH_TREE_LOG2_ROOT_SIZE, HASH_TREE_LOG2_WORD_SIZE},
rollup::{
LOG2_MAX_ADVANCE_STATES_PER_EPOCH, LOG2_MAX_MCYCLES_PER_ADVANCE_STATE,
LOG2_MAX_UARCH_CYCLES_PER_MCYCLE,
},
},
};

fn solidity_constant(source: &str, marker: &str) -> u64 {
let pos = source
.find(marker)
.unwrap_or_else(|| panic!("{marker} not found in contract source"));
let after = &source[pos + marker.len()..];
let eq = after.find('=').expect("expected `=` after constant name");
let semi = after.find(';').expect("expected `;` after constant value");
let value = after[eq + 1..semi].trim();
value
.strip_prefix("0x")
.map(|hex| u64::from_str_radix(hex, 16))
.unwrap_or_else(|| value.parse())
.unwrap_or_else(|_| panic!("{marker} is not a numeric Solidity constant"))
}

/// Guardrail: step's `EmulatorConstants.sol` is auto-generated from the
/// emulator C++ source, and `REVERT_ROOT_HASH_ADDRESS` must equal the
/// emulator's `CM_AR_SHADOW_REVERT_ROOT_HASH_START` - otherwise the
Expand All @@ -60,22 +87,7 @@ mod tests {
let source = std::fs::read_to_string(&emulator_constants_sol)
.unwrap_or_else(|e| panic!("failed to read {}: {e}", emulator_constants_sol.display()));

// Find: `uint64 constant REVERT_ROOT_HASH_ADDRESS = 0x<hex>;`
let marker = "REVERT_ROOT_HASH_ADDRESS";
let pos = source.find(marker).unwrap_or_else(|| {
panic!("{marker} not found in {}", emulator_constants_sol.display())
});
let after = &source[pos + marker.len()..];
let eq = after.find('=').expect("expected `=` after constant name");
let semi = after.find(';').expect("expected `;` after constant value");
let value_str = after[eq + 1..semi].trim();
let step_value = if let Some(hex) = value_str.strip_prefix("0x") {
u64::from_str_radix(hex, 16).expect("REVERT_ROOT_HASH_ADDRESS not valid hex")
} else {
value_str
.parse::<u64>()
.expect("REVERT_ROOT_HASH_ADDRESS not valid decimal")
};
let step_value = solidity_constant(&source, "REVERT_ROOT_HASH_ADDRESS");

assert_eq!(
CHECKPOINT_ADDRESS, step_value,
Expand All @@ -85,6 +97,70 @@ mod tests {
);
}

#[test]
fn machine_validity_proof_geometry_matches_solidity() {
let manifest_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let root = manifest_dir.join("../..");
let emulator_constants = std::fs::read_to_string(root.join(
"cartesi-rollups/contracts/dependencies/\
cartesi-rollups-contracts-3.0.0-alpha.9/dependencies/\
cartesi-machine-solidity-step-0.15.0/src/EmulatorConstants.sol",
))
.expect("read DaveConsensus's vendored EmulatorConstants.sol");
let memory = std::fs::read_to_string(root.join(
"cartesi-rollups/contracts/dependencies/\
cartesi-rollups-contracts-3.0.0-alpha.9/dependencies/\
cartesi-machine-solidity-step-0.15.0/src/Memory.sol",
))
.expect("read DaveConsensus's vendored Memory.sol");
let canonical_machine = std::fs::read_to_string(root.join(
"cartesi-rollups/contracts/dependencies/\
cartesi-rollups-contracts-3.0.0-alpha.9/src/common/CanonicalMachine.sol",
))
.expect("read CanonicalMachine.sol");

assert_eq!(
Machine::reg_address(CM_REG_IFLAGS_Y).unwrap(),
solidity_constant(&emulator_constants, "IFLAGS_Y_ADDRESS")
);
assert_eq!(
Machine::reg_address(CM_REG_HTIF_TOHOST).unwrap(),
solidity_constant(&emulator_constants, "HTIF_TOHOST_ADDRESS")
);
assert_eq!(
TX_START,
solidity_constant(&emulator_constants, "AR_CMIO_TX_BUFFER_START")
);
assert_eq!(
u64::from(HASH_TREE_LOG2_WORD_SIZE),
solidity_constant(&memory, "uint8 constant LOG2_LEAF")
);
assert_eq!(
u64::from(HASH_TREE_LOG2_ROOT_SIZE),
solidity_constant(&canonical_machine, "LOG2_MEMORY_SIZE")
);
for (marker, emulator_value) in [
("HTIF_DEV_MASK", CM_HTIF_DEV_MASK),
("HTIF_CMD_MASK", CM_HTIF_CMD_MASK),
("HTIF_REASON_MASK", CM_HTIF_REASON_MASK),
("HTIF_DEV_SHIFT", u64::from(CM_HTIF_DEV_SHIFT)),
("HTIF_CMD_SHIFT", u64::from(CM_HTIF_CMD_SHIFT)),
("HTIF_REASON_SHIFT", u64::from(CM_HTIF_REASON_SHIFT)),
("HTIF_DEV_YIELD", u64::from(CM_HTIF_DEV_YIELD)),
("HTIF_YIELD_CMD_MANUAL", u64::from(CM_HTIF_YIELD_CMD_MANUAL)),
(
"HTIF_YIELD_MANUAL_REASON_RX_ACCEPTED",
u64::from(CM_HTIF_YIELD_MANUAL_REASON_RX_ACCEPTED),
),
] {
assert_eq!(
emulator_value,
solidity_constant(&emulator_constants, marker),
"Cartesi Machine and DaveConsensus disagree on {marker}"
);
}
}

/// The first number appearing after `marker` in `source` (digits
/// only, delimiters skipped): dumb but loud, like the parser above.
fn first_number_after(source: &str, marker: &str) -> u64 {
Expand Down
2 changes: 1 addition & 1 deletion cartesi-rollups/node/src/engine/dispute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ impl<F: RulerFactory> DisputeSource<F> {
// window's root stores its fanout there - and counting those
// once bricked reconstruction after the hero's own join. A
// store the runner never processed (the engine harnesses; a
// freshly migrated node) has an empty prefix and the machine
// freshly initialized node) has an empty prefix and the machine
// serves everything - the pre-frontier full-replay behavior.
// A nonzero prefix must match the closed epoch's input count
// exactly; anything else is corruption. The padding value is
Expand Down
4 changes: 2 additions & 2 deletions cartesi-rollups/node/src/engine/machine_stf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ impl ProvingStf for MachineStf {
/// from the boundary store's nearest stored machine and advancing
/// the remainder. The store is live: boundaries recorded by any
/// writer (the open regime's gap fill, a future dispute write-back)
/// shorten the next positioning. On a freshly migrated store only
/// shorten the next positioning. On a freshly initialized store only
/// the epoch start exists, which is the full-replay behavior the
/// prototype had. Constructed only by [`DisputeSource::on_store`];
/// the type is public for signatures alone.
Expand All @@ -488,7 +488,7 @@ pub struct Positioner {
/// engine pieces.
impl DisputeSource<Positioner> {
pub fn on_store(mut storage: Storage, epoch: u64, work_dir: PathBuf) -> Result<Self> {
// The migration pinned the config; assert engine
// Initialization pinned the config; assert engine
// compatibility before serving any quartet.
let structure = Structure::PRODUCTION;
super::config::assert_compatible(
Expand Down
4 changes: 2 additions & 2 deletions cartesi-rollups/node/src/engine/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,8 @@ pub(crate) fn toy_storage(structure: Structure) -> Storage {
emulator_version: "toy".into(),
};
let dir = tempfile::tempdir().unwrap().keep();
let mut connection = Connection::open(dir.join("db.sqlite3")).unwrap();
crate::storage::sql::migrations::migrate_to_latest(&mut connection).unwrap();
let connection = Connection::open(dir.join("db.sqlite3")).unwrap();
crate::storage::sql::schema::initialize(&connection).unwrap();
super::config::pin(&connection, &config).unwrap();
Storage::new(&dir).unwrap()
}
Expand Down
Loading
Loading