Skip to content

Commit 7eee89c

Browse files
committed
feat(node): add output merkle and proof for settle
1 parent b82f883 commit 7eee89c

File tree

6 files changed

+141
-31
lines changed

6 files changed

+141
-31
lines changed

cartesi-rollups/node/epoch-manager/src/lib.rs

+27-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
use alloy::{hex::ToHexExt, primitives::Address, providers::DynProvider};
1+
use alloy::{
2+
hex::ToHexExt,
3+
primitives::{Address, B256},
4+
providers::DynProvider,
5+
};
26
use anyhow::Result;
37
use log::{error, info, trace};
48
use num_traits::cast::ToPrimitive;
59
use std::{sync::Arc, time::Duration};
610

711
use cartesi_dave_contracts::daveconsensus;
8-
use rollups_state_manager::StateManager;
12+
use rollups_state_manager::{Proof, StateManager};
913

1014
pub struct EpochManager<SM: StateManager> {
1115
client: DynProvider,
@@ -38,13 +42,13 @@ where
3842
let can_settle = dave_consensus.canSettle().call().await?;
3943

4044
if can_settle.isFinished {
41-
match self.state_manager.computation_hash(
45+
match self.state_manager.settlement_info(
4246
can_settle
4347
.epochNumber
4448
.to_u64()
4549
.expect("fail to convert epoch number to u64"),
4650
)? {
47-
Some(computation_hash) => {
51+
Some((computation_hash, output_merkle, output_proof)) => {
4852
assert_eq!(
4953
computation_hash,
5054
can_settle.winnerCommitment.to_vec(),
@@ -55,7 +59,15 @@ where
5559
can_settle.epochNumber,
5660
computation_hash.encode_hex()
5761
);
58-
match dave_consensus.settle(can_settle.epochNumber).send().await {
62+
match dave_consensus
63+
.settle(
64+
can_settle.epochNumber,
65+
Self::vec_u8_to_bytes_32(output_merkle),
66+
Self::to_bytes_32_vec(output_proof),
67+
)
68+
.send()
69+
.await
70+
{
5971
Ok(tx_builder) => {
6072
let _ = tx_builder.watch().await.inspect_err(|e| error!("{}", e));
6173
}
@@ -73,4 +85,14 @@ where
7385
tokio::time::sleep(self.sleep_duration).await;
7486
}
7587
}
88+
89+
fn to_bytes_32_vec(proof: Proof) -> Vec<B256> {
90+
proof.inner().iter().map(|hash| B256::from(hash)).collect()
91+
}
92+
93+
fn vec_u8_to_bytes_32(vec: Vec<u8>) -> B256 {
94+
let mut array = [0u8; 32];
95+
array.copy_from_slice(&vec[..32]);
96+
B256::from(array)
97+
}
7698
}

cartesi-rollups/node/machine-runner/src/lib.rs

+25-5
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ use std::{
1414
use cartesi_dave_merkle::{Digest, DigestError, MerkleBuilder};
1515
use cartesi_machine::{
1616
config::runtime::RuntimeConfig,
17-
constants::break_reason,
17+
constants::{break_reason, pma::TX_START},
1818
machine::Machine,
1919
types::{cmio::CmioResponseReason, Hash},
2020
};
2121
use cartesi_prt_core::machine::constants::{
2222
LOG2_BARCH_SPAN_TO_INPUT, LOG2_INPUT_SPAN_TO_EPOCH, LOG2_UARCH_SPAN_TO_BARCH,
2323
};
24-
use rollups_state_manager::{InputId, StateManager};
24+
use rollups_state_manager::{InputId, Proof, StateManager};
2525

2626
// gap of each leaf in the commitment tree, should use the same value as CanonicalConstants.sol:log2step(0)
2727
const LOG2_STRIDE: u64 = 44;
@@ -101,7 +101,9 @@ where
101101
assert!(self.epoch_number < latest_epoch);
102102

103103
let commitment = self.build_commitment()?;
104-
self.save_commitment(&commitment)?;
104+
let (output_merkle, output_proof) = self.get_output_merkle_and_proof()?;
105+
106+
self.save_settlement_info(&commitment, &output_merkle, &output_proof)?;
105107

106108
// end of current epoch
107109
self.epoch_number += 1;
@@ -159,9 +161,27 @@ where
159161
Ok(computation_hash)
160162
}
161163

162-
fn save_commitment(&self, computation_hash: &[u8]) -> Result<(), SM> {
164+
/// get output merkle and output proof
165+
fn get_output_merkle_and_proof(&mut self) -> Result<(Vec<u8>, Proof), SM> {
166+
let proof = self.machine.proof(TX_START, 5)?;
167+
let output_merkle = self.machine.read_memory(TX_START, 32)?;
168+
169+
Ok((output_merkle, Proof::new(proof.sibling_hashes)))
170+
}
171+
172+
fn save_settlement_info(
173+
&self,
174+
computation_hash: &[u8],
175+
output_merkle: &[u8],
176+
output_proof: &Proof,
177+
) -> Result<(), SM> {
163178
self.state_manager
164-
.add_computation_hash(computation_hash, self.epoch_number)
179+
.add_settlement_info(
180+
computation_hash,
181+
output_merkle,
182+
output_proof,
183+
self.epoch_number,
184+
)
165185
.map_err(|e| MachineRunnerError::StateManagerError(e))?;
166186

167187
Ok(())

cartesi-rollups/node/prt-runner/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ where
4545
if let Some(last_sealed_epoch) = self.state_manager.last_sealed_epoch()? {
4646
match self
4747
.state_manager
48-
.computation_hash(last_sealed_epoch.epoch_number)?
48+
.settlement_info(last_sealed_epoch.epoch_number)?
4949
{
5050
Some(_) => {
5151
if let Some(snapshot) = self

cartesi-rollups/node/state-manager/src/lib.rs

+47-3
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,48 @@ pub mod persistent_state_access;
66
pub(crate) mod sql;
77

88
use std::error::Error;
9-
109
pub type Blob = Vec<u8>;
1110

11+
#[derive(Debug, PartialEq)]
12+
pub struct Proof(Vec<[u8; 32]>);
13+
impl From<Vec<u8>> for Proof {
14+
fn from(input: Vec<u8>) -> Self {
15+
// Ensure the length is a multiple of 32
16+
assert!(
17+
input.len() % 32 == 0,
18+
"Input length must be a multiple of 32"
19+
);
20+
21+
let mut result = Vec::new();
22+
23+
for chunk in input.chunks(32) {
24+
let mut array = [0u8; 32];
25+
array.copy_from_slice(chunk);
26+
result.push(array);
27+
}
28+
29+
Proof(result)
30+
}
31+
}
32+
33+
impl Proof {
34+
pub fn new(siblings: Vec<[u8; 32]>) -> Self {
35+
Self(siblings)
36+
}
37+
38+
pub fn inner(&self) -> Vec<[u8; 32]> {
39+
self.0.clone()
40+
}
41+
42+
fn flatten(&self) -> Vec<u8> {
43+
self.0
44+
.iter()
45+
.flat_map(|array| array.iter())
46+
.copied()
47+
.collect()
48+
}
49+
}
50+
1251
#[derive(Clone, Debug, Default)]
1352
pub struct InputId {
1453
pub epoch_number: u64,
@@ -104,10 +143,15 @@ pub trait StateManager {
104143
state_hash_index_in_epoch: u64,
105144
) -> Result<(Vec<u8>, u64), Self::Error>;
106145
fn machine_state_hashes(&self, epoch_number: u64) -> Result<Vec<(Vec<u8>, u64)>, Self::Error>;
107-
fn computation_hash(&self, epoch_number: u64) -> Result<Option<Vec<u8>>, Self::Error>;
108-
fn add_computation_hash(
146+
fn settlement_info(
147+
&self,
148+
epoch_number: u64,
149+
) -> Result<Option<(Vec<u8>, Vec<u8>, Proof)>, Self::Error>;
150+
fn add_settlement_info(
109151
&self,
110152
computation_hash: &[u8],
153+
output_merkle: &[u8],
154+
output_proof: &Proof,
111155
epoch_number: u64,
112156
) -> Result<(), Self::Error>;
113157
fn add_snapshot(

cartesi-rollups/node/state-manager/src/persistent_state_access.rs

+37-15
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0 (see LICENSE)
33

44
use crate::sql::{consensus_data, error::*, migrations};
5-
use crate::{Epoch, Input, InputId, StateManager};
5+
use crate::{Epoch, Input, InputId, Proof, StateManager};
66

77
use rusqlite::{Connection, OptionalExtension};
88
use std::sync::Mutex;
@@ -179,25 +179,35 @@ impl StateManager for PersistentStateAccess {
179179
Ok(())
180180
}
181181

182-
fn computation_hash(&self, epoch_number: u64) -> Result<Option<Vec<u8>>> {
182+
fn settlement_info(&self, epoch_number: u64) -> Result<Option<(Vec<u8>, Vec<u8>, Proof)>> {
183183
let conn = self.connection.lock().unwrap();
184184
let mut stmt = conn.prepare(
185185
"\
186-
SELECT computation_hash FROM computation_hashes
186+
SELECT computation_hash, output_merkle, output_proof FROM settlement_info
187187
WHERE epoch_number = ?1
188188
",
189189
)?;
190190

191191
Ok(stmt
192-
.query_row([epoch_number], |row| Ok(row.get(0)?))
192+
.query_row([epoch_number], |row| {
193+
Ok((row.get(0)?, row.get(1)?, row.get::<_, Vec<u8>>(2)?.into()))
194+
})
193195
.optional()?)
194196
}
195197

196-
fn add_computation_hash(&self, computation_hash: &[u8], epoch_number: u64) -> Result<()> {
197-
match self.computation_hash(epoch_number)? {
198-
Some(c) => {
199-
// previous row with same key found, all values should match
198+
fn add_settlement_info(
199+
&self,
200+
computation_hash: &[u8],
201+
output_merkle: &[u8],
202+
output_proof: &Proof,
203+
epoch_number: u64,
204+
) -> Result<()> {
205+
match self.settlement_info(epoch_number)? {
206+
Some((c, o_m, o_p)) => {
207+
// a row with same key found, all values should match
200208
assert!(c == computation_hash.to_vec());
209+
assert!(o_m == output_merkle.to_vec());
210+
assert!(o_p == *output_proof);
201211

202212
return Ok(());
203213
}
@@ -206,10 +216,16 @@ impl StateManager for PersistentStateAccess {
206216

207217
let conn = self.connection.lock().unwrap();
208218
let mut sttm = conn.prepare(
209-
"INSERT INTO computation_hashes (epoch_number, computation_hash) VALUES (?1, ?2)",
219+
"INSERT INTO settlement_info (epoch_number, computation_hash, output_merkle, output_proof) VALUES (?1, ?2, ?3, ?4)",
210220
)?;
211221

212-
if sttm.execute((epoch_number, computation_hash))? != 1 {
222+
if sttm.execute((
223+
epoch_number,
224+
computation_hash,
225+
output_merkle,
226+
output_proof.flatten(),
227+
))? != 1
228+
{
213229
return Err(PersistentStateAccessError::InsertionFailed {
214230
description: "machine computation_hash insertion failed".to_owned(),
215231
});
@@ -577,17 +593,23 @@ mod tests {
577593
);
578594

579595
assert!(
580-
access.computation_hash(0)?.is_none(),
596+
access.settlement_info(0)?.is_none(),
581597
"computation_hash shouldn't exist"
582598
);
583599

584600
let computation_hash_1 = vec![1, 2, 3, 4, 5];
585-
access.add_computation_hash(&computation_hash_1, 0)?;
601+
let output_merkle_1 = vec![1, 2, 3, 4, 4];
602+
let output_proof_1: Proof = vec![
603+
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
604+
25, 26, 27, 28, 29, 30, 31, 32,
605+
]
606+
.into();
607+
access.add_settlement_info(&computation_hash_1, &output_merkle_1, &output_proof_1, 0)?;
586608

587609
assert_eq!(
588-
access.computation_hash(0)?,
589-
Some(computation_hash_1),
590-
"computation_hash 1 should match"
610+
access.settlement_info(0)?,
611+
Some((computation_hash_1, output_merkle_1, output_proof_1)),
612+
"settlement info of epoch 0 should match"
591613
);
592614

593615
Ok(())

cartesi-rollups/node/state-manager/src/sql/migrations.sql

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
CREATE TABLE computation_hashes (
1+
CREATE TABLE settlement_info (
22
epoch_number INTEGER NOT NULL PRIMARY KEY,
3-
computation_hash BLOB NOT NULL
3+
computation_hash BLOB NOT NULL,
4+
output_merkle BLOB NOT NULL,
5+
output_proof BLOB NOT NULL
46
);
57

68
CREATE TABLE epochs (

0 commit comments

Comments
 (0)