From 33b6635d32f026854824b59424ae85bdfb6a1572 Mon Sep 17 00:00:00 2001 From: Gabriel Coutinho de Paula Date: Fri, 13 Jun 2025 16:55:08 -0300 Subject: [PATCH 1/3] wip --- prt/client-rs/core/src/tournament/reader.rs | 376 ++++++++++++------ .../core/src/tournament/tournament.rs | 300 ++++++++------ 2 files changed, 429 insertions(+), 247 deletions(-) diff --git a/prt/client-rs/core/src/tournament/reader.rs b/prt/client-rs/core/src/tournament/reader.rs index 486f6052..71531dd8 100644 --- a/prt/client-rs/core/src/tournament/reader.rs +++ b/prt/client-rs/core/src/tournament/reader.rs @@ -3,61 +3,247 @@ use anyhow::Result; use async_recursion::async_recursion; -use std::collections::HashMap; +use cartesi_machine::types::Hash; +use std::{collections::HashMap, sync::Arc}; use alloy::{ eips::BlockNumberOrTag::Latest, + primitives::U256, providers::{DynProvider, Provider}, sol_types::private::{Address, B256}, }; use crate::tournament::{ - ClockState, CommitmentState, MatchID, MatchState, TournamentState, TournamentStateMap, - TournamentWinner, + ClockState, CommitmentState, MatchID, MatchState, TournamentArgs, TournamentState, + TournamentStateMap, }; use cartesi_dave_merkle::Digest; -use cartesi_prt_contracts::{nonleaftournament, nonroottournament, roottournament, tournament}; +use cartesi_prt_contracts::{ + leaftournament::LeafTournament::LeafTournamentInstance, + nonleaftournament, nonroottournament, roottournament, + tournament::{self, Tournament::TournamentInstance}, +}; + +use super::{Divergence, MatchStatus, TournamentStatus}; #[derive(Clone)] pub struct StateReader { client: DynProvider, - block_created_number: u64, + root_tournament_address: Address, + latest: u64, + genesis: u64, } impl StateReader { - pub fn new(client: DynProvider, block_created_number: u64) -> Result { - Ok(Self { + pub fn new(client: DynProvider, root_tournament_address: Address, genesis: u64) -> Self { + Self { client, - block_created_number, - }) + root_tournament_address, + genesis, + } + } + + pub async fn read_state(&self) -> Result { + self.fetch_tournament( + TournamentState::new_root(self.root_tournament_address), + &mut states, + ) + .await?; + + Ok(states) } +} - async fn created_tournament( +// async fn divergence( +// match_id: B256, +// match_state: tournament::Match::State, +// tournament: TournamentInstance<(), impl Provider>, +// ) -> Result { +// let leaf_cycle = tournament.getMatchCycle(match_id).call().await?._0; +// Ok(Divergence { +// agree: match_state.otherParent.into(), +// p1_disagree: match_state.leftNode.into(), +// p2_disagree: match_state.rightNode.into(), +// agree_metacycle: leaf_cycle, +// }) +// } +// ($match_id:expr, $match_state:expr, $tournament:expr $(,)?) => {{ +// let leaf_cycle = $tournament.getMatchCycle($match_id).call().await?._0; +// Divergence { +// agree: $match_state.otherParent.into(), +// p1_disagree: $match_state.leftNode.into(), +// p2_disagree: $match_state.rightNode.into(), +// agree_metacycle: leaf_cycle, +// } +// }}; +macro_rules! build_divergence { + ($match_id:expr, $match_state:expr, $tournament:expr $(,)?) => {{ + let leaf_cycle = $tournament.getMatchCycle($match_id).call().await?._0; + Divergence { + agree: $match_state.otherParent.into(), + p1_disagree: $match_state.leftNode.into(), + p2_disagree: $match_state.rightNode.into(), + agree_metacycle: leaf_cycle, + } + }}; +} + +impl StateReader { + async fn commitments_joined( &self, tournament_address: Address, - match_id: MatchID, - ) -> Result> { - let tournament = - nonleaftournament::NonLeafTournament::new(tournament_address, &self.client); + ) -> Result>> { + let tournament = tournament::Tournament::new(tournament_address, &self.client); let events = tournament - .newInnerTournament_filter() + .commitmentJoined_filter() .address(tournament_address) - .topic1::(match_id.hash().into()) - .from_block(self.block_created_number) + .from_block(self.genesis) .to_block(Latest) .query() .await?; - if let Some(event) = events.last() { - Ok(Some(TournamentCreatedEvent { - parent_match_id_hash: match_id.hash(), - new_tournament_address: event.0._1, - })) - } else { - Ok(None) + + let mut joined = HashMap::with_capacity(events.len()); + for (commitment, _) in events { + let c = self + .read_commitment(tournament_address, commitment.root.into()) + .await?; + + joined.insert(c.root_hash, Arc::new(c)); + } + + Ok(joined) + } + + async fn read_commitment( + &self, + tournament_address: Address, + commitment_hash: Digest, + ) -> Result { + let tournament = tournament::Tournament::new(tournament_address, &self.client); + + let commitment = tournament + .getCommitment(commitment_hash.into()) + .block(self.latest.into()) + .call() + .await?; + + let clock = ClockState::new( + self.latest, + commitment._0.startInstant, + commitment._0.allowance, + ); + + Ok(CommitmentState { + root_hash: commitment_hash, + clock, + }) + } + + // async fn created_tournament( + // &self, + // tournament_address: Address, + // match_id: MatchID, + // ) -> Result> { + // let tournament = + // nonleaftournament::NonLeafTournament::new(tournament_address, &self.client); + // let events = tournament + // .newInnerTournament_filter() + // .address(tournament_address) + // .topic1::(match_id.hash().into()) + // .from_block(self.block_created_number) + // .to_block(Latest) + // .query() + // .await?; + // if let Some(event) = events.last() { + // Ok(Some(TournamentCreatedEvent { + // parent_match_id_hash: match_id.hash(), + // new_tournament_address: event.0._1, + // })) + // } else { + // Ok(None) + // } + // } + + async fn matches_created(&self, tournament_address: Address) -> Result> { + let tournament = tournament::Tournament::new(tournament_address, &self.client); + let events: Vec = tournament + .matchCreated_filter() + .address(tournament_address) + .from_block(self.genesis) + .to_block(self.latest) + .query() + .await? + .iter() + .map(|event| MatchCreatedEvent { + id: MatchID { + commitment_one: event.0.one.into(), + commitment_two: event.0.two.into(), + }, + left_hash: event.0.leftOfTwo.into(), + }) + .collect(); + Ok(events) + } + + async fn read_non_leaf_match( + &self, + tournament: LeafTournamentInstance<(), impl Provider>, + match_id: MatchID, + ) -> Result> { + let m = tournament.getMatch(match_id.hash()).call().await?._0; + if m.otherParent.is_zero() { + return Ok(None); + } + + Ok(Some(MatchState { + id: match_id, + status: if m.height == 0 { + let divergence = build_divergence!(match_id.hash().into(), m, tournament); + MatchStatus::FinishedNonLeaf { + divergence, + inner_tournament: todo!(), + } + } else { + MatchStatus::Ongoing { + other_parent: m.otherParent.into(), + left_node: m.leftNode.into(), + right_node: m.rightNode.into(), + current_height: m.height, + } + }, + })) + } + + async fn read_leaf_match( + &self, + tournament: LeafTournamentInstance<(), impl Provider>, + match_id: MatchID, + ) -> Result> { + let m = tournament.getMatch(match_id.hash()).call().await?._0; + if m.otherParent.is_zero() { + return Ok(None); } + + Ok(Some(MatchState { + id: match_id, + status: if m.height == 0 { + let divergence = build_divergence!(match_id.hash().into(), m, tournament); + MatchStatus::FinishedLeaf { divergence } + } else { + MatchStatus::Ongoing { + other_parent: m.otherParent.into(), + left_node: m.leftNode.into(), + right_node: m.rightNode.into(), + current_height: m.height, + } + }, + })) } - async fn capture_matches(&self, tournament_address: Address) -> Result> { + async fn read_matches( + &self, + tournament_address: Address, + ) -> Result> { let tournament = tournament::Tournament::new(tournament_address, &self.client); let created_matches = self.created_matches(tournament_address).await?; @@ -92,47 +278,6 @@ impl StateReader { Ok(matches) } - async fn created_matches(&self, tournament_address: Address) -> Result> { - let tournament = tournament::Tournament::new(tournament_address, &self.client); - let events: Vec = tournament - .matchCreated_filter() - .address(tournament_address) - .from_block(self.block_created_number) - .to_block(Latest) - .query() - .await? - .iter() - .map(|event| MatchCreatedEvent { - id: MatchID { - commitment_one: event.0.one.into(), - commitment_two: event.0.two.into(), - }, - left_hash: event.0.leftOfTwo.into(), - }) - .collect(); - Ok(events) - } - - async fn joined_commitments( - &self, - tournament_address: Address, - ) -> Result> { - let tournament = tournament::Tournament::new(tournament_address, &self.client); - let events = tournament - .commitmentJoined_filter() - .address(tournament_address) - .from_block(self.block_created_number) - .to_block(Latest) - .query() - .await? - .iter() - .map(|c| CommitmentJoinedEvent { - root: c.0.root.into(), - }) - .collect(); - Ok(events) - } - async fn get_commitment( &self, tournament_address: Address, @@ -163,51 +308,55 @@ impl StateReader { }) } - pub async fn fetch_from_root( - &self, - root_tournament_address: Address, - ) -> Result { - let mut states = HashMap::new(); - self.fetch_tournament( - TournamentState::new_root(root_tournament_address), - &mut states, - ) - .await?; - - Ok(states) - } - - #[async_recursion] - async fn fetch_tournament( - &self, - mut state: TournamentState, - states: &mut TournamentStateMap, - ) -> Result<()> { - let tournament_address = state.address; - let tournament = tournament::Tournament::new(tournament_address, &self.client); - let level_constants_return = tournament.tournamentLevelConstants().call().await?; - ( - state.max_level, - state.level, - state.log2_stride, - state.log2_stride_count, - ) = ( - level_constants_return._maxLevel, - level_constants_return._level, - level_constants_return._log2step, - level_constants_return._height, - ); + // #[async_recursion] + async fn read_tournament(&self, tournament_address: Address) -> Result { + let tournament_args = { + let tournament = tournament::Tournament::new(tournament_address, &self.client); + let level_constants_return = tournament.tournamentLevelConstants().call().await?; + TournamentArgs { + level: level_constants_return._level as u8, + start_metacycle: U256::ZERO, // TODO!! + log2_stride: level_constants_return._log2step, + log2_stride_count: level_constants_return._height, + } + }; - assert!(state.level < state.max_level, "level out of bounds"); + let commitments_joined = self.commitments_joined(tournament_address).await?; - if state.level > 0 { + if tournament_args.level > 0 { let tournament = nonroottournament::NonRootTournament::new(tournament_address, &self.client); - state.can_be_eliminated = tournament.canBeEliminated().call().await?._0; + let can_be_eliminated = tournament.canBeEliminated().call().await?._0; + + if can_be_eliminated { + return Ok(TournamentState { + address: tournament_address, + args: tournament_args, + commitments_joined, + status: TournamentStatus::Dead, + }); + } + } + + let winner = if tournament_args.level == 0 { + self.root_tournament_winner(tournament_address).await? + } else { + self.tournament_winner(tournament_address).await? + }; + + if let Some((winner_commitment, final_state)) = winner { + return Ok(TournamentState { + address: tournament_address, + args: tournament_args, + commitments_joined, + status: TournamentStatus::Finished { + winner_commitment, + final_state, + }, + }); } let mut captured_matches = self.capture_matches(tournament_address).await?; - let commitments_joined = self.joined_commitments(tournament_address).await?; let mut commitment_states = HashMap::new(); for commitment in commitments_joined { @@ -231,11 +380,6 @@ impl StateReader { .latest_match = Some(i); } - let winner = match state.parent { - Some(_) => self.tournament_winner(tournament_address).await?, - None => self.root_tournament_winner(tournament_address).await?, - }; - state.winner = winner; state.matches = captured_matches; state.commitment_states = commitment_states; @@ -274,7 +418,7 @@ impl StateReader { async fn root_tournament_winner( &self, root_tournament_address: Address, - ) -> Result> { + ) -> Result> { let root_tournament = roottournament::RootTournament::new(root_tournament_address, &self.client); let arbitration_result_return = root_tournament.arbitrationResult().call().await?; @@ -285,10 +429,7 @@ impl StateReader { ); if finished { - Ok(Some(TournamentWinner::Root( - commitment.into(), - state.into(), - ))) + Ok(Some((commitment.into(), state.into()))) } else { Ok(None) } @@ -297,7 +438,7 @@ impl StateReader { async fn tournament_winner( &self, tournament_address: Address, - ) -> Result> { + ) -> Result> { let tournament = nonroottournament::NonRootTournament::new(tournament_address, &self.client); let inner_tournament_winner_return = tournament.innerTournamentWinner().call().await?; @@ -308,10 +449,7 @@ impl StateReader { ); if finished { - Ok(Some(TournamentWinner::Inner( - parent_commitment.into(), - dangling_commitment.into(), - ))) + Ok(Some((parent_commitment.into(), dangling_commitment.into()))) } else { Ok(None) } diff --git a/prt/client-rs/core/src/tournament/tournament.rs b/prt/client-rs/core/src/tournament/tournament.rs index 9320d658..bf36e26b 100644 --- a/prt/client-rs/core/src/tournament/tournament.rs +++ b/prt/client-rs/core/src/tournament/tournament.rs @@ -2,170 +2,214 @@ use crate::machine::MachineCommitment; -use alloy::primitives::Address; +use alloy::primitives::{Address, B256}; use cartesi_dave_merkle::Digest; +use cartesi_machine::types::Hash; use ruint::aliases::U256; -use std::collections::HashMap; +use std::{collections::HashMap, sync::Arc}; pub type TournamentStateMap = HashMap; pub type CommitmentMap = HashMap; -/// Struct used to identify a match. -#[derive(Clone, Copy, Debug)] -pub struct MatchID { - pub commitment_one: Digest, - pub commitment_two: Digest, -} +//// Struct used to identify a match. +// #[derive(Clone, Copy, Debug)] +// pub struct MatchID { +// pub commitment_one: Digest, +// pub commitment_two: Digest, +// } +// +// impl MatchID { +// pub fn hash(&self) -> alloy::primitives::B256 { +// self.commitment_one.join(&self.commitment_two).into() +// } +// } -impl MatchID { - /// Generates a new [Digest] - pub fn hash(&self) -> Digest { - self.commitment_one.join(&self.commitment_two) - } +/// Struct used to communicate the state of a commitment. +#[derive(Clone, Debug)] +pub struct CommitmentState { + pub root_hash: Digest, + pub clock: ClockState, } -// TODO: this can be optimized if the bindings generated with only one shared `Id` struct -impl From for cartesi_prt_contracts::tournament::Match::Id { - fn from(match_id: MatchID) -> Self { - cartesi_prt_contracts::tournament::Match::Id { - commitmentOne: match_id.commitment_one.into(), - commitmentTwo: match_id.commitment_two.into(), - } - } +#[derive(Clone, Debug)] +pub enum ClockState { + Stopped { allowance: u64 }, + Ticking { deadline: u64, allowance: u64 }, + Dead { since: u64 }, } -impl From for cartesi_prt_contracts::nonleaftournament::Match::Id { - fn from(match_id: MatchID) -> Self { - cartesi_prt_contracts::nonleaftournament::Match::Id { - commitmentOne: match_id.commitment_one.into(), - commitmentTwo: match_id.commitment_two.into(), +impl ClockState { + pub fn new(block: u64, start_time: u64, allowance: u64) -> Self { + assert!(block >= start_time); + + if start_time == 0 { + Self::Stopped { allowance } + } else if start_time + allowance > block { + Self::Ticking { + deadline: start_time + allowance, + allowance: allowance - (block - start_time), + } + } else { + Self::Dead { + since: start_time + allowance, + } } } } -impl From for cartesi_prt_contracts::leaftournament::Match::Id { - fn from(match_id: MatchID) -> Self { - cartesi_prt_contracts::leaftournament::Match::Id { - commitmentOne: match_id.commitment_one.into(), - commitmentTwo: match_id.commitment_two.into(), - } - } +// impl ClockState { +// pub fn has_time(&self) -> bool { +// match self { +// ClockState::Stopped { allowance, since } => true, +// ClockState::Ticking { allowance, since } => self.start_instant + allowance > since, +// ClockState::Dead { since } => false, +// } +// // if self.start_instant == 0 { +// // true +// // } else { +// // self.deadline() > self.block_number +// // } +// } +// +// pub fn time_since_timeout(&self) -> u64 { +// if self.start_instant == 0 { +// 0 +// } else { +// self.block_number - self.deadline() +// } +// } +// +// // deadline of clock if it's ticking +// fn deadline(&self) -> u64 {} +// } + +// impl std::fmt::Display for ClockState { +// fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { +// if self.start_instant == 0 { +// write!(f, "clock paused, {} blocks left", self.allowance) +// } else { +// let time_elapsed = self.block_number - self.start_instant; +// if self.allowance >= time_elapsed { +// write!( +// f, +// "clock ticking, {} blocks left", +// self.allowance - time_elapsed +// ) +// } else { +// write!( +// f, +// "clock ticking, {} blocks overdue", +// time_elapsed - self.allowance +// ) +// } +// } +// } +// } + +/// Struct used to communicate the state of a tournament. +#[derive(Clone, Debug)] +pub struct TournamentState { + pub address: Address, + pub args: TournamentArgs, + pub commitments_joined: HashMap>, + pub status: TournamentStatus, } -/// Struct used to communicate the state of a commitment. -#[derive(Clone, Copy, Debug)] -pub struct CommitmentState { - pub clock: ClockState, - pub final_state: Digest, - pub latest_match: Option, +impl TournamentState { + fn is_root(&self) -> bool { + self.args.level == 0 + } } -/// Struct used to communicate the state of a clock. -#[derive(Clone, Copy, Debug)] -pub struct ClockState { - pub allowance: u64, - pub start_instant: u64, - pub block_number: u64, +#[derive(Clone, Debug)] +pub struct TournamentArgs { + pub level: u8, + pub start_metacycle: U256, + pub log2_stride: u64, + pub log2_stride_count: u64, } -impl ClockState { - pub fn has_time(&self) -> bool { - if self.start_instant == 0 { - true - } else { - self.deadline() > self.block_number - } - } +#[derive(Clone, Debug)] +pub enum TournamentStatus { + Finished { + winner_commitment: Digest, + final_state: cartesi_machine::types::Hash, + }, - pub fn time_since_timeout(&self) -> u64 { - if self.start_instant == 0 { - 0 - } else { - self.block_number - self.deadline() - } - } + Dead, - // deadline of clock if it's ticking - fn deadline(&self) -> u64 { - self.start_instant + self.allowance - } + Ongoing { + commitment_states: HashMap, + matches: Vec>, + }, } -impl std::fmt::Display for ClockState { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - if self.start_instant == 0 { - write!(f, "clock paused, {} blocks left", self.allowance) - } else { - let time_elapsed = self.block_number - self.start_instant; - if self.allowance >= time_elapsed { - write!( - f, - "clock ticking, {} blocks left", - self.allowance - time_elapsed - ) - } else { - write!( - f, - "clock ticking, {} blocks overdue", - time_elapsed - self.allowance - ) - } - } - } +/// Struct used to communicate the state of a match. +#[derive(Clone, Debug)] +pub struct MatchState { + pub commitment_one: Arc, + pub commitment_two: Arc, + pub status: MatchStatus, } -/// Enum used to represent the winner of a tournament. -#[derive(Clone, PartialEq, Debug)] -pub enum TournamentWinner { - Root(Digest, Digest), - Inner(Digest, Digest), +impl MatchState { + pub fn id(&self) -> Digest { + self.commitment_one + .root_hash + .join(&self.commitment_two.root_hash) + } } -/// Struct used to communicate the state of a tournament. -#[derive(Clone, Default, Debug)] -pub struct TournamentState { - pub address: Address, - pub base_cycle: U256, - pub level: u64, - pub log2_stride: u64, - pub log2_stride_count: u64, - pub max_level: u64, - pub parent: Option
, - pub commitment_states: HashMap, - pub matches: Vec, - pub winner: Option, - pub can_be_eliminated: bool, +impl From for cartesi_prt_contracts::tournament::Match::Id { + fn from(match_id: MatchState) -> Self { + cartesi_prt_contracts::tournament::Match::Id { + commitmentOne: match_id.commitment_one.root_hash.into(), + commitmentTwo: match_id.commitment_two.root_hash.into(), + } + } } -impl TournamentState { - pub fn new_root(address: Address) -> Self { - TournamentState { - address, - ..Default::default() +impl From for cartesi_prt_contracts::nonleaftournament::Match::Id { + fn from(match_id: MatchState) -> Self { + cartesi_prt_contracts::nonleaftournament::Match::Id { + commitmentOne: match_id.commitment_one.root_hash.into(), + commitmentTwo: match_id.commitment_two.root_hash.into(), } } +} - pub fn new_inner(address: Address, level: u64, base_cycle: U256, parent: Address) -> Self { - TournamentState { - address, - base_cycle, - level: level + 1, - parent: Some(parent), - ..Default::default() +impl From for cartesi_prt_contracts::leaftournament::Match::Id { + fn from(match_id: MatchState) -> Self { + cartesi_prt_contracts::leaftournament::Match::Id { + commitmentOne: match_id.commitment_one.root_hash.into(), + commitmentTwo: match_id.commitment_two.root_hash.into(), } } } -/// Struct used to communicate the state of a match. -#[derive(Clone, Copy, Debug)] -pub struct MatchState { - pub id: MatchID, - pub other_parent: Digest, - pub left_node: Digest, - pub right_node: Digest, - pub running_leaf_position: U256, - pub current_height: u64, - pub leaf_cycle: U256, - pub tournament_address: Address, - pub inner_tournament: Option
, +#[derive(Clone, Debug)] +pub struct Divergence { + pub agree: Hash, + pub p1_disagree: Hash, + pub p2_disagree: Hash, + pub agree_metacycle: U256, +} + +#[derive(Clone, Debug)] +pub enum MatchStatus { + Ongoing { + other_parent: Digest, + left_node: Digest, + right_node: Digest, + current_height: u64, + }, + + FinishedLeaf { + divergence: Divergence, + }, + + FinishedNonLeaf { + divergence: Divergence, + inner_tournament: Box, + }, } From c9e7e8782005968941c1c8bc10cc478f9bb52d9a Mon Sep 17 00:00:00 2001 From: Gabriel Coutinho de Paula Date: Tue, 24 Jun 2025 21:37:45 -0300 Subject: [PATCH 2/3] wip --- prt/client-rs/core/src/tournament/reader.rs | 2 +- prt/client-rs/core/src/tournament/tournament.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/prt/client-rs/core/src/tournament/reader.rs b/prt/client-rs/core/src/tournament/reader.rs index 71531dd8..87d405e8 100644 --- a/prt/client-rs/core/src/tournament/reader.rs +++ b/prt/client-rs/core/src/tournament/reader.rs @@ -245,7 +245,7 @@ impl StateReader { tournament_address: Address, ) -> Result> { let tournament = tournament::Tournament::new(tournament_address, &self.client); - let created_matches = self.created_matches(tournament_address).await?; + let created_matches = self.matches_created(tournament_address).await?; let mut matches = vec![]; for match_event in created_matches { diff --git a/prt/client-rs/core/src/tournament/tournament.rs b/prt/client-rs/core/src/tournament/tournament.rs index bf36e26b..e050574e 100644 --- a/prt/client-rs/core/src/tournament/tournament.rs +++ b/prt/client-rs/core/src/tournament/tournament.rs @@ -139,7 +139,6 @@ pub enum TournamentStatus { Dead, Ongoing { - commitment_states: HashMap, matches: Vec>, }, } From afeaed4ea535eb5c6f21d20ab8abb12577270418 Mon Sep 17 00:00:00 2001 From: Gabriel Coutinho de Paula Date: Thu, 26 Jun 2025 12:50:15 -0300 Subject: [PATCH 3/3] wip --- prt/client-rs/core/src/tournament/reader.rs | 480 ++++++------------ .../core/src/tournament/tournament.rs | 34 +- 2 files changed, 177 insertions(+), 337 deletions(-) diff --git a/prt/client-rs/core/src/tournament/reader.rs b/prt/client-rs/core/src/tournament/reader.rs index 87d405e8..c9ffa791 100644 --- a/prt/client-rs/core/src/tournament/reader.rs +++ b/prt/client-rs/core/src/tournament/reader.rs @@ -7,29 +7,24 @@ use cartesi_machine::types::Hash; use std::{collections::HashMap, sync::Arc}; use alloy::{ - eips::BlockNumberOrTag::Latest, - primitives::U256, - providers::{DynProvider, Provider}, - sol_types::private::{Address, B256}, + eips::BlockNumberOrTag::Latest, primitives::U256, providers::DynProvider, + sol_types::private::Address, }; -use crate::tournament::{ - ClockState, CommitmentState, MatchID, MatchState, TournamentArgs, TournamentState, - TournamentStateMap, -}; +use crate::tournament::{ClockState, CommitmentState, MatchState, TournamentArgs, TournamentState}; use cartesi_dave_merkle::Digest; use cartesi_prt_contracts::{ - leaftournament::LeafTournament::LeafTournamentInstance, - nonleaftournament, nonroottournament, roottournament, - tournament::{self, Tournament::TournamentInstance}, + nonroottournament, roottournament, + tournament::{self}, }; -use super::{Divergence, MatchStatus, TournamentStatus}; +use super::{Divergence, MatchStatus, Matchup, TournamentStatus}; #[derive(Clone)] pub struct StateReader { client: DynProvider, root_tournament_address: Address, + levels: u8, latest: u64, genesis: u64, } @@ -39,43 +34,17 @@ impl StateReader { Self { client, root_tournament_address, + levels: 3, // TODO hardcoded + latest: 0, genesis, } } pub async fn read_state(&self) -> Result { - self.fetch_tournament( - TournamentState::new_root(self.root_tournament_address), - &mut states, - ) - .await?; - - Ok(states) + self.read_tournament(self.root_tournament_address).await } } -// async fn divergence( -// match_id: B256, -// match_state: tournament::Match::State, -// tournament: TournamentInstance<(), impl Provider>, -// ) -> Result { -// let leaf_cycle = tournament.getMatchCycle(match_id).call().await?._0; -// Ok(Divergence { -// agree: match_state.otherParent.into(), -// p1_disagree: match_state.leftNode.into(), -// p2_disagree: match_state.rightNode.into(), -// agree_metacycle: leaf_cycle, -// }) -// } -// ($match_id:expr, $match_state:expr, $tournament:expr $(,)?) => {{ -// let leaf_cycle = $tournament.getMatchCycle($match_id).call().await?._0; -// Divergence { -// agree: $match_state.otherParent.into(), -// p1_disagree: $match_state.leftNode.into(), -// p2_disagree: $match_state.rightNode.into(), -// agree_metacycle: leaf_cycle, -// } -// }}; macro_rules! build_divergence { ($match_id:expr, $match_state:expr, $tournament:expr $(,)?) => {{ let leaf_cycle = $tournament.getMatchCycle($match_id).call().await?._0; @@ -89,231 +58,13 @@ macro_rules! build_divergence { } impl StateReader { - async fn commitments_joined( - &self, - tournament_address: Address, - ) -> Result>> { - let tournament = tournament::Tournament::new(tournament_address, &self.client); - let events = tournament - .commitmentJoined_filter() - .address(tournament_address) - .from_block(self.genesis) - .to_block(Latest) - .query() - .await?; - - let mut joined = HashMap::with_capacity(events.len()); - for (commitment, _) in events { - let c = self - .read_commitment(tournament_address, commitment.root.into()) - .await?; - - joined.insert(c.root_hash, Arc::new(c)); - } - - Ok(joined) - } - - async fn read_commitment( - &self, - tournament_address: Address, - commitment_hash: Digest, - ) -> Result { - let tournament = tournament::Tournament::new(tournament_address, &self.client); - - let commitment = tournament - .getCommitment(commitment_hash.into()) - .block(self.latest.into()) - .call() - .await?; - - let clock = ClockState::new( - self.latest, - commitment._0.startInstant, - commitment._0.allowance, - ); - - Ok(CommitmentState { - root_hash: commitment_hash, - clock, - }) - } - - // async fn created_tournament( - // &self, - // tournament_address: Address, - // match_id: MatchID, - // ) -> Result> { - // let tournament = - // nonleaftournament::NonLeafTournament::new(tournament_address, &self.client); - // let events = tournament - // .newInnerTournament_filter() - // .address(tournament_address) - // .topic1::(match_id.hash().into()) - // .from_block(self.block_created_number) - // .to_block(Latest) - // .query() - // .await?; - // if let Some(event) = events.last() { - // Ok(Some(TournamentCreatedEvent { - // parent_match_id_hash: match_id.hash(), - // new_tournament_address: event.0._1, - // })) - // } else { - // Ok(None) - // } - // } - - async fn matches_created(&self, tournament_address: Address) -> Result> { - let tournament = tournament::Tournament::new(tournament_address, &self.client); - let events: Vec = tournament - .matchCreated_filter() - .address(tournament_address) - .from_block(self.genesis) - .to_block(self.latest) - .query() - .await? - .iter() - .map(|event| MatchCreatedEvent { - id: MatchID { - commitment_one: event.0.one.into(), - commitment_two: event.0.two.into(), - }, - left_hash: event.0.leftOfTwo.into(), - }) - .collect(); - Ok(events) - } - - async fn read_non_leaf_match( - &self, - tournament: LeafTournamentInstance<(), impl Provider>, - match_id: MatchID, - ) -> Result> { - let m = tournament.getMatch(match_id.hash()).call().await?._0; - if m.otherParent.is_zero() { - return Ok(None); - } - - Ok(Some(MatchState { - id: match_id, - status: if m.height == 0 { - let divergence = build_divergence!(match_id.hash().into(), m, tournament); - MatchStatus::FinishedNonLeaf { - divergence, - inner_tournament: todo!(), - } - } else { - MatchStatus::Ongoing { - other_parent: m.otherParent.into(), - left_node: m.leftNode.into(), - right_node: m.rightNode.into(), - current_height: m.height, - } - }, - })) - } - - async fn read_leaf_match( - &self, - tournament: LeafTournamentInstance<(), impl Provider>, - match_id: MatchID, - ) -> Result> { - let m = tournament.getMatch(match_id.hash()).call().await?._0; - if m.otherParent.is_zero() { - return Ok(None); - } - - Ok(Some(MatchState { - id: match_id, - status: if m.height == 0 { - let divergence = build_divergence!(match_id.hash().into(), m, tournament); - MatchStatus::FinishedLeaf { divergence } - } else { - MatchStatus::Ongoing { - other_parent: m.otherParent.into(), - left_node: m.leftNode.into(), - right_node: m.rightNode.into(), - current_height: m.height, - } - }, - })) - } - - async fn read_matches( - &self, - tournament_address: Address, - ) -> Result> { - let tournament = tournament::Tournament::new(tournament_address, &self.client); - let created_matches = self.matches_created(tournament_address).await?; - - let mut matches = vec![]; - for match_event in created_matches { - let match_id = match_event.id; - let m = tournament.getMatch(match_id.hash().into()).call().await?._0; - - if !m.otherParent.is_zero() { - let leaf_cycle = tournament - .getMatchCycle(match_id.hash().into()) - .call() - .await? - ._0; - let running_leaf_position = m.runningLeafPosition; - - let match_state = MatchState { - id: match_id, - other_parent: m.otherParent.into(), - left_node: m.leftNode.into(), - right_node: m.rightNode.into(), - running_leaf_position, - current_height: m.currentHeight, - tournament_address, - leaf_cycle, - inner_tournament: None, - }; - matches.push(match_state); - } - } - - Ok(matches) - } - - async fn get_commitment( - &self, - tournament_address: Address, - commitment_hash: Digest, - ) -> Result { - let tournament = tournament::Tournament::new(tournament_address, &self.client); - let commitment_return = tournament - .getCommitment(commitment_hash.into()) - .call() - .await?; - - let block_number = self - .client - .get_block(Latest.into()) - .await? - .expect("cannot get last block") - .header - .number; - let clock_state = ClockState { - allowance: commitment_return._0.allowance, - start_instant: commitment_return._0.startInstant, - block_number, - }; - Ok(CommitmentState { - clock: clock_state, - final_state: commitment_return._1.into(), - latest_match: None, - }) - } - - // #[async_recursion] + #[async_recursion] async fn read_tournament(&self, tournament_address: Address) -> Result { let tournament_args = { let tournament = tournament::Tournament::new(tournament_address, &self.client); let level_constants_return = tournament.tournamentLevelConstants().call().await?; TournamentArgs { + max_level: level_constants_return._maxLevel as u8, level: level_constants_return._level as u8, start_metacycle: U256::ZERO, // TODO!! log2_stride: level_constants_return._log2step, @@ -356,63 +107,160 @@ impl StateReader { }); } - let mut captured_matches = self.capture_matches(tournament_address).await?; + let matches = self + .read_matches( + tournament_address, + tournament_args.level, + &commitments_joined, + ) + .await?; - let mut commitment_states = HashMap::new(); - for commitment in commitments_joined { - let commitment_state = self - .get_commitment(tournament_address, commitment.root) - .await?; - commitment_states.insert(commitment.root, commitment_state); - } + Ok(TournamentState { + address: tournament_address, + args: tournament_args, + commitments_joined, + status: TournamentStatus::Ongoing { matches }, + }) + } - for (i, captured_match) in captured_matches.iter_mut().enumerate() { - self.fetch_match(captured_match, states, state.level) - .await?; + // #[async_recursion] + async fn read_matches( + &self, + tournament_address: Address, + level: u8, + commitments_joined: &HashMap>, + ) -> Result> { + let tournament = tournament::Tournament::new(tournament_address, &self.client); + let created_matches = self.matches_created(tournament_address).await?; - commitment_states - .get_mut(&captured_match.id.commitment_one) - .expect("cannot find commitment one state") - .latest_match = Some(i); - commitment_states - .get_mut(&captured_match.id.commitment_two) - .expect("cannot find commitment two state") - .latest_match = Some(i); - } + let mut matches = vec![]; + for pair in created_matches { + let matchup = Matchup { + commitment_one: commitments_joined + .get(&pair.0) + .expect("commitment should always exist") + .clone(), + commitment_two: commitments_joined + .get(&pair.1) + .expect("commitment should always exist") + .clone(), + }; + + let id = matchup.id().into(); + let m = tournament.getMatch(id).call().await?._0; + + if m.otherParent.is_zero() { + continue; + } - state.winner = winner; - state.matches = captured_matches; - state.commitment_states = commitment_states; + let match_state = if level == self.levels { + // leaf + MatchState { + matchup, + status: if m.height == 0 { + let divergence = build_divergence!(id, m, tournament); + MatchStatus::FinishedLeaf { divergence } + } else { + MatchStatus::Ongoing { + other_parent: m.otherParent.into(), + left_node: m.leftNode.into(), + right_node: m.rightNode.into(), + current_height: m.height, + } + }, + } + } else { + // non leaf + MatchState { + matchup, + status: if m.height == 0 { + let divergence = build_divergence!(id, m, tournament); + MatchStatus::FinishedNonLeaf { + divergence, + inner_tournament: Box::new( + self.read_tournament(tournament_address).await?, + ), + } + } else { + MatchStatus::Ongoing { + other_parent: m.otherParent.into(), + left_node: m.leftNode.into(), + right_node: m.rightNode.into(), + current_height: m.height, + } + }, + } + }; - states.insert(tournament_address, state); + matches.push(match_state); + } - Ok(()) + Ok(matches) } - #[async_recursion] - async fn fetch_match( + async fn commitments_joined( &self, - match_state: &mut MatchState, - states: &mut TournamentStateMap, - tournament_level: u64, - ) -> Result<()> { - let created_tournament = self - .created_tournament(match_state.tournament_address, match_state.id) + tournament_address: Address, + ) -> Result>> { + let tournament = tournament::Tournament::new(tournament_address, &self.client); + let events = tournament + .commitmentJoined_filter() + .address(tournament_address) + .from_block(self.genesis) + .to_block(Latest) + .query() .await?; - if let Some(inner) = created_tournament { - let inner_tournament = TournamentState::new_inner( - inner.new_tournament_address, - tournament_level, - match_state.leaf_cycle, - match_state.tournament_address, - ); - self.fetch_tournament(inner_tournament, states).await?; - match_state.inner_tournament = Some(inner.new_tournament_address); - - return Ok(()); + + let mut joined = HashMap::with_capacity(events.len()); + for (commitment, _) in events { + let c = self + .read_commitment(tournament_address, commitment.root.into()) + .await?; + + joined.insert(c.root_hash, Arc::new(c)); } - Ok(()) + Ok(joined) + } + + async fn read_commitment( + &self, + tournament_address: Address, + commitment_hash: Digest, + ) -> Result { + let tournament = tournament::Tournament::new(tournament_address, &self.client); + + let commitment = tournament + .getCommitment(commitment_hash.into()) + .block(self.latest.into()) + .call() + .await?; + + let clock = ClockState::new( + self.latest, + commitment._0.startInstant, + commitment._0.allowance, + ); + + Ok(CommitmentState { + root_hash: commitment_hash, + clock, + }) + } + + async fn matches_created(&self, tournament_address: Address) -> Result> { + let tournament = tournament::Tournament::new(tournament_address, &self.client); + let pairs = tournament + .matchCreated_filter() + .address(tournament_address) + .from_block(self.genesis) + .to_block(self.latest) + .query() + .await? + .iter() + .map(|event| (event.0.one.into(), event.0.two.into())) + .collect(); + Ok(pairs) } async fn root_tournament_winner( @@ -455,23 +303,3 @@ impl StateReader { } } } - -/// This struct is used to communicate the creation of a new tournament. -#[derive(Clone, Copy)] -pub struct TournamentCreatedEvent { - pub parent_match_id_hash: Digest, - pub new_tournament_address: Address, -} - -/// This struct is used to communicate the enrollment of a new commitment. -#[derive(Clone, Copy)] -pub struct CommitmentJoinedEvent { - pub root: Digest, -} - -/// This struct is used to communicate the creation of a new match. -#[derive(Clone, Copy)] -pub struct MatchCreatedEvent { - pub id: MatchID, - pub left_hash: Digest, -} diff --git a/prt/client-rs/core/src/tournament/tournament.rs b/prt/client-rs/core/src/tournament/tournament.rs index e050574e..b80d90e9 100644 --- a/prt/client-rs/core/src/tournament/tournament.rs +++ b/prt/client-rs/core/src/tournament/tournament.rs @@ -124,6 +124,7 @@ impl TournamentState { #[derive(Clone, Debug)] pub struct TournamentArgs { pub level: u8, + pub max_level: u8, pub start_metacycle: U256, pub log2_stride: u64, pub log2_stride_count: u64, @@ -139,19 +140,17 @@ pub enum TournamentStatus { Dead, Ongoing { - matches: Vec>, + matches: Vec, }, } -/// Struct used to communicate the state of a match. #[derive(Clone, Debug)] -pub struct MatchState { +pub struct Matchup { pub commitment_one: Arc, pub commitment_two: Arc, - pub status: MatchStatus, } -impl MatchState { +impl Matchup { pub fn id(&self) -> Digest { self.commitment_one .root_hash @@ -159,8 +158,8 @@ impl MatchState { } } -impl From for cartesi_prt_contracts::tournament::Match::Id { - fn from(match_id: MatchState) -> Self { +impl From for cartesi_prt_contracts::tournament::Match::Id { + fn from(match_id: Matchup) -> Self { cartesi_prt_contracts::tournament::Match::Id { commitmentOne: match_id.commitment_one.root_hash.into(), commitmentTwo: match_id.commitment_two.root_hash.into(), @@ -168,8 +167,8 @@ impl From for cartesi_prt_contracts::tournament::Match::Id { } } -impl From for cartesi_prt_contracts::nonleaftournament::Match::Id { - fn from(match_id: MatchState) -> Self { +impl From for cartesi_prt_contracts::nonleaftournament::Match::Id { + fn from(match_id: Matchup) -> Self { cartesi_prt_contracts::nonleaftournament::Match::Id { commitmentOne: match_id.commitment_one.root_hash.into(), commitmentTwo: match_id.commitment_two.root_hash.into(), @@ -177,8 +176,8 @@ impl From for cartesi_prt_contracts::nonleaftournament::Match::Id { } } -impl From for cartesi_prt_contracts::leaftournament::Match::Id { - fn from(match_id: MatchState) -> Self { +impl From for cartesi_prt_contracts::leaftournament::Match::Id { + fn from(match_id: Matchup) -> Self { cartesi_prt_contracts::leaftournament::Match::Id { commitmentOne: match_id.commitment_one.root_hash.into(), commitmentTwo: match_id.commitment_two.root_hash.into(), @@ -186,6 +185,19 @@ impl From for cartesi_prt_contracts::leaftournament::Match::Id { } } +/// Struct used to communicate the state of a match. +#[derive(Clone, Debug)] +pub struct MatchState { + pub matchup: Matchup, + pub status: MatchStatus, +} + +impl MatchState { + pub fn id(&self) -> Digest { + self.matchup.id() + } +} + #[derive(Clone, Debug)] pub struct Divergence { pub agree: Hash,