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

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

2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ RUN apt-get -y update && \
# Set the working directory
WORKDIR /usr/src/madara/
# Copy the source code into the container
COPY Cargo.toml Cargo.lock ./
COPY Cargo.toml Cargo.lock .db-versions.yml ./
COPY crates crates
COPY .db-versions.yml .db-versions.yml ./
COPY cairo-artifacts cairo-artifacts
Expand Down
4 changes: 2 additions & 2 deletions configs/presets/devnet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ gateway_url: "http://localhost:8080/gateway/"
native_fee_token_address: "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d"
parent_fee_token_address: "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"
latest_protocol_version: "0.13.2"
block_time: "10s"
pending_block_update_time: "2s"
block_time: "1h"
pending_block_update_time: "1s"
execution_batch_size: 16
bouncer_config:
block_max_capacity:
Expand Down
4 changes: 3 additions & 1 deletion crates/madara/client/analytics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ impl Analytics {
self.meter_provider = Some(self.init_metric_provider()?);

let layer = OpenTelemetryTracingBridge::new(&logger_provider);
tracing_subscriber.with(OpenTelemetryLayer::new(tracer)).with(layer).init();
tracing_subscriber.with(OpenTelemetryLayer::new(tracer)).init();

tracing::info!("OTEL initialized");
Ok(())
}

Expand Down
7 changes: 6 additions & 1 deletion crates/madara/client/block_production/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ blockifier = { workspace = true, features = ["testing"] }
mockall.workspace = true
assert_matches.workspace = true
lazy_static.workspace = true
serde = { workspace = true, default-features = true }
serde_json.workspace = true

[features]
Expand All @@ -37,6 +38,7 @@ testing = ["blockifier/testing", "mc-db/testing", "mockall"]
mc-analytics.workspace = true
mc-block-import.workspace = true
mc-db.workspace = true
mc-devnet.workspace = true
mc-exec.workspace = true
mc-mempool.workspace = true
mp-block.workspace = true
Expand All @@ -53,7 +55,8 @@ blockifier.workspace = true
starknet-core.workspace = true
starknet-types-core.workspace = true
starknet_api.workspace = true

starknet.workspace = true
starknet-types-rpc = { workspace = true }
# Other
anyhow.workspace = true
mockall = { workspace = true, optional = true }
Expand All @@ -76,3 +79,5 @@ tracing = { workspace = true }
tracing-core = { workspace = true, default-features = false }
tracing-opentelemetry = { workspace = true }
tracing-subscriber = { workspace = true, features = ["env-filter"] }
rand.workspace = true
dotenv.workspace = true
433 changes: 428 additions & 5 deletions crates/madara/client/block_production/src/lib.rs

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions crates/madara/client/db/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ librocksdb-sys = { workspace = true }
rayon = { workspace = true }
rocksdb.workspace = true
serde = { workspace = true }
serde_json = { workspace = true }
tempfile = { workspace = true, optional = true }
thiserror = { workspace = true }
tokio = { workspace = true, features = [
Expand Down
234 changes: 234 additions & 0 deletions crates/madara/client/db/src/game_db.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
use crate::DatabaseExt;
use crate::{Column, MadaraBackend};
use rocksdb::WriteBatch;
use serde::{Deserialize, Serialize};
use std::env;

#[derive(Serialize, Deserialize)]
pub struct AddressNode {
address: String,
next: Option<String>, // Next address in sequence
previous: Option<String>, // Previous address in sequence
}

#[derive(Serialize, Deserialize, Debug, Default)]
pub struct ListMetadata {
game_address: String,
head: Option<String>, // First address
tail: Option<String>, // Last address
pub next_iter_addr: Option<String>, // Next address for iterator
pub length: u64, // Number of addresses in list
pub tiles_mined: u64, // Number of tiles mined
}

const METADATA_KEY: &[u8] = b"list_metadata";

impl MadaraBackend {
pub fn game_get_metadata(&self) -> Result<ListMetadata, rocksdb::Error> {
let col = self.db.get_column(Column::Game);
let meta = self
.db
.get_cf(&col, METADATA_KEY)?
.map(|bytes| serde_json::from_slice(&bytes).unwrap())
.unwrap_or(ListMetadata::default());
Ok(meta)
}

pub fn game_update_metadata(&self, updates: impl FnOnce(&mut ListMetadata)) -> Result<(), rocksdb::Error> {
let col = self.db.get_column(Column::Game);
let mut batch = WriteBatch::default();

// Get current metadata or create default if none exists
let mut metadata = self
.db
.get_cf(&col, METADATA_KEY)?
.map(|bytes| serde_json::from_slice(&bytes).unwrap())
.unwrap_or(ListMetadata::default());

// Apply the updates to the metadata
updates(&mut metadata);

// Serialize and write the updated metadata
let serialized = serde_json::to_vec(&metadata).unwrap();
batch.put_cf(&col, METADATA_KEY, serialized);

// Write the batch to the database
self.db.write(batch)?;

Ok(())
}

/// Add new bot address to the linked list
/// 1. Create new node with current address, with previous pointing to old tail
/// 2. Add new node to the database
/// 3. If it's first address, set head & tail to this address
/// 4. Update old tail's next field to point to new address
/// 5. Update tail to the new address
/// 6. Update metadata, including tail and length
/// 7. Write batch to the database
pub fn game_add_bot_address(&self, address: &str) -> Result<(), rocksdb::Error> {
let col = self.db.get_column(Column::Game);
let mut meta = self.game_get_metadata()?;

let new_node = AddressNode {
address: address.to_string(),
next: None,
previous: meta.tail.clone(), // Point to old tail
};

let mut batch = WriteBatch::default();

// If list is empty
if meta.head.is_none() {
meta.head = Some(address.to_string());
meta.tail = Some(address.to_string());
} else {
// Update old tail to point to new address
if let Some(old_tail) = &meta.tail {
let mut old_tail_node: AddressNode =
serde_json::from_slice(&self.db.get_cf(&col, old_tail.as_bytes())?.unwrap()).unwrap();
old_tail_node.next = Some(address.to_string());
batch.put_cf(&col, old_tail.as_bytes(), serde_json::to_vec(&old_tail_node).unwrap());
}
meta.tail = Some(address.to_string());
}

meta.length += 1;

batch.put_cf(&col, address.as_bytes(), serde_json::to_vec(&new_node).unwrap());
batch.put_cf(&col, METADATA_KEY, serde_json::to_vec(&meta).unwrap());

// Added New Node
// Updated old tail to point to new node
// Incremented metadata length by 1

self.db.write(batch)
}

/// Delete a game address from the list
/// 1. Modify next pointer of previous node to point to node after this one
/// 2. Modify previous pointer of next node to point to node before this one
/// 3. Delete provided node. Reduce length by 1 in metadata
/// 4. Write batch to the database
pub fn game_delete_bot_address(&self, address: &str) -> Result<(), rocksdb::Error> {
let col = self.db.get_column(Column::Game);
let mut meta = self.game_get_metadata()?;

// Get the node to delete
if let Some(node_bytes) = self.db.get_cf(&col, address.as_bytes())? {
let node: AddressNode = serde_json::from_slice(&node_bytes).unwrap();
let mut batch = WriteBatch::default();

// Update previous node's next pointer
if let Some(prev_addr) = &node.previous {
let mut prev_node: AddressNode =
serde_json::from_slice(&self.db.get_cf(&col, prev_addr.as_bytes())?.unwrap()).unwrap();
prev_node.next = node.next.clone();
batch.put_cf(&col, prev_addr.as_bytes(), serde_json::to_vec(&prev_node).unwrap());
} else {
// Deleting head
meta.head = node.next.clone();
}

// Update next node's previous pointer
if let Some(next_addr) = &node.next {
let mut next_node: AddressNode =
serde_json::from_slice(&self.db.get_cf(&col, next_addr.as_bytes())?.unwrap()).unwrap();
next_node.previous = node.previous;
batch.put_cf(&col, next_addr.as_bytes(), serde_json::to_vec(&next_node).unwrap());
} else {
// Deleting tail
meta.tail = node.previous;
}

meta.length -= 1;

// If the deleted node was the one being iterated over, update the next iteration pointer
if let Some(next_iter_addr) = meta.next_iter_addr.as_ref() {
if next_iter_addr == &address {
meta.next_iter_addr = node.next;
}
}

batch.delete_cf(&col, address.as_bytes());
batch.put_cf(&col, METADATA_KEY, serde_json::to_vec(&meta).unwrap());

// Delete the node
// Updated previous node to point to next node
// Decremented metadata length by 1

return self.db.write(batch);
}

Ok(()) // Address not found
}

pub fn game_get_all_bot_addresses(&self) -> Result<Vec<String>, rocksdb::Error> {
let col = self.db.get_column(Column::Game);
let meta = self.game_get_metadata()?;
let mut addresses = Vec::new();

let mut current = meta.head;
while let Some(addr) = current {
addresses.push(addr.clone());
let node: AddressNode = serde_json::from_slice(&self.db.get_cf(&col, addr.as_bytes())?.unwrap()).unwrap();
current = node.next;
}

assert!(
addresses.len() == meta.length as usize,
"fetched addresses list != stored addresses length in metadata"
);

Ok(addresses)
}

pub fn game_get_bots_list(&self) -> Result<Vec<String>, rocksdb::Error> {
let mines_per_transaction =
env::var("MADARA_GAME_TPS").expect("MADARA_GAME_TPS not set").parse::<i64>().unwrap();

let col = self.db.get_column(Column::Game);
let meta = self.game_get_metadata()?;

let next_start_address = meta.next_iter_addr;

// Handle empty list case
if meta.length == 0 {
return Ok(vec![]);
}
let mut return_bots = Vec::with_capacity(mines_per_transaction as usize);

// Start from the given address or head if None
let mut current_address = match next_start_address {
Some(addr) => Some(addr),
None => meta.head.clone(),
};

// Keep adding bots until we reach mines_per_transaction
while return_bots.len() < mines_per_transaction as usize {
if let Some(addr) = &current_address {
// Add current address
return_bots.push(addr.clone());

// Get next address, if we reach end, start from head
let node: AddressNode =
serde_json::from_slice(&self.db.get_cf(&col, addr.as_bytes())?.unwrap()).unwrap();

current_address = match node.next {
Some(next) => Some(next),
None => meta.head.clone(), // Wrap around to start
};
} else {
break; // Should never happen if meta.length > 0
}
}

let mut meta = self.game_get_metadata()?;
// Update metadata with next start address
meta.next_iter_addr = current_address;
self.db.put_cf(&col, METADATA_KEY, serde_json::to_vec(&meta).unwrap())?;
// The next start address will be current_address

Ok(return_bots)
}
}
6 changes: 6 additions & 0 deletions crates/madara/client/db/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub mod contract_db;
pub mod db_block_id;
pub mod db_metrics;
pub mod devnet_db;
pub mod game_db;
pub mod l1_db;
pub mod mempool_db;
pub mod storage_updates;
Expand Down Expand Up @@ -154,6 +155,8 @@ pub enum Column {
Devnet,

MempoolTransactions,
/// Game DB for Gridy
Game,
}

impl fmt::Debug for Column {
Expand All @@ -168,6 +171,7 @@ impl fmt::Display for Column {
}
}

// TODO: why do I need to explicitly add Column memeber here, can't we fetch it dynamically from Column
impl Column {
pub const ALL: &'static [Self] = {
use Column::*;
Expand Down Expand Up @@ -201,6 +205,7 @@ impl Column {
PendingContractStorage,
Devnet,
MempoolTransactions,
Game,
]
};
pub const NUM_COLUMNS: usize = Self::ALL.len();
Expand Down Expand Up @@ -237,6 +242,7 @@ impl Column {
PendingContractStorage => "pending_contract_storage",
Devnet => "devnet",
MempoolTransactions => "mempool_transactions",
Game => "game",
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions crates/madara/client/devnet/src/entrypoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ impl Multicall {
self
}

pub fn with_vec(calls: Vec<Call>) -> Self {
Multicall(calls)
}

pub fn flatten(&self) -> impl Iterator<Item = Felt> + '_ {
[self.0.len().into()].into_iter().chain(self.0.iter().flat_map(|c| c.flatten()))
}
Expand Down
Loading