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

Persistent mode for saya using mock of SNOS #2253

Merged
merged 61 commits into from
Oct 9, 2024

Conversation

matzayonc
Copy link
Collaborator

@matzayonc matzayonc commented Aug 2, 2024

Description

I added a Cairo 1 program simulating SNOS, committing to piltover.

Related issue

Tests

The full development flow is in this script: bin/saya/saya.sh.

  • Yes
  • No, because they aren't needed
  • No, because I need help

Added to documentation?

  • README.md
  • Dojo Book
  • No documentation needed

Checklist

  • I've formatted my code (scripts/prettier.sh, scripts/rust_fmt.sh, scripts/cairo_fmt.sh)
  • I've linted my code (scripts/clippy.sh, scripts/docs.sh)
  • I've commented my code
  • I've requested a review after addressing the comments

Summary by CodeRabbit

  • New Features

    • Introduced flexible dependency management for improved maintenance and future updates.
    • Enhanced command-line interface with new and modified flags for better clarity and usability, including options for settlement modes.
    • Implemented structured options for operational modes and contract addresses.
    • Added batch processing capabilities and improved transaction handling for StarkNet interactions.
    • Introduced new configuration parameters for block processing and proof generation.
    • Streamlined deployment process with updated manifest files reflecting new deployment addresses.
    • Added support for new proving programs and enhanced handling of Cairo versions.
    • Introduced a new error handling structure for better reporting and debugging.
    • Added a new method for publishing checkpoints to the data availability layer.
    • Enhanced documentation for Saya CLI, detailing usage and setup instructions.
    • Added a new asynchronous function for monitoring transaction status until completion.
  • Bug Fixes

    • Resolved previous workaround regarding world_address, enhancing deployment instructions.
  • Documentation

    • Updated README with expanded deployment instructions and command-line options.
    • Revised configuration files to reflect new operational parameters and batch processing settings.
  • Chores

    • Cleaned up unused imports and improved code readability across various modules.
    • Restructured and clarified documentation for better user guidance.

@matzayonc matzayonc requested a review from glihm August 2, 2024 11:43
bin/saya/README.md Outdated Show resolved Hide resolved
pub struct SayaModeArg(pub SayaMode);

#[derive(Debug, Args, Clone)]
pub struct ShardOptions {
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should think how the terminology will be separated, as shard is mostly for ephemeral execution and persistent execution is done in a different manner at some point.
Or the name shouldn't be piltover but settlement_contract which can be the world for shards and piltover for persistency.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sure, that makes sense.

&[
SayaModeArg(SayaMode::Ephemeral),
SayaModeArg(SayaMode::Persistent),
SayaModeArg(SayaMode::PersistentMerging),
Copy link
Collaborator

Choose a reason for hiding this comment

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

What is this one? sovereign?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

So the one using the merger program recursively should be soverein?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Hum, then no. It's something unrelated. I wasn't sure why we had a persistent and persistent merging. Seems to be only at provable program layer, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It changes the program flow in both merging and updating state. It is a different program, so it produces a different output. I think at this point PersistentMerging should be treated as a sort of legacy program, as it is still useful, but Persistent is a newer, "sounder" way to prove state.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's remove PersistentMerging if it's not used anymore?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Removed

@@ -23,7 +22,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
saya.start().await?;

// Wait until Ctrl + C is pressed, then shutdown
ctrl_c().await?;
// ctrl_c().await?;
Copy link
Collaborator

Choose a reason for hiding this comment

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

not required anymore to be able to kill the program? Or it's handled by the .start() function?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not required, as the ephemeral shard will shut itself down after reaching the shutdown event.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah but we may want to kill saya. :) In this case it would be better keeping it to ensure graceful shutdown in case of manual interruption.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It is the opposite, this line simply prevents saya from closing until ctrl+c is pressed. Now we do the same thing by simply not returning from the start function until all the blocks are prover (for ephemeral shard).
A graceful shutdown would have to be implemented there.

use crate::{SayaStarknetAccount, LOG_TARGET};

#[derive(Debug, Serialize)]
pub struct PiltoverCalldata {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not sure if the cainome bindgen is already integrated to piltover. But if yes, we should use it to already have concrete types in rust that are automatically serializable in cairo.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Looks to me like it is integrated, will use it as a dependcy.

By the way those are automatically serializable, as they use serde_felt from this PR: cartridge-gg/cairo-proof-parser#10.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It doesn't seem to be working at the moment. Create a bug report here: keep-starknet-strange/piltover#39

Copy link
Collaborator

Choose a reason for hiding this comment

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

Thanks for that, I'll have a look at it to ensure it works as it will be better to rely on generated code.

Are we using a fork of piltover or the original contract?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

to: c.to,
selector: c.selector,
calldata: c.calldata,
starknet_messages: Vec::new(), // TODO: Fill messages.
Copy link
Collaborator

Choose a reason for hiding this comment

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

This should be done in a subsequent PR, or could be done in this one? The messages are or L1HandlerTransaction or available into the receipts.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Should be trivial to implement, but might be less so to test it, that why I left it for now to avoid a false positive.
This PR is quite big already so I would leave it for the following out, unless you think otherwise.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Sounds great in a follow up PR. 👍

) -> anyhow::Result<(String, Felt)> {
if serialized_proof.len() > 2000 {
Copy link
Collaborator

Choose a reason for hiding this comment

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

isn't 4000 the limit?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I exprienced that some transactions failed when greater than 3000, but can test further now that it's in a more mature state.

let mut nonce = account.get_nonce().await?;
let mut hashes = Vec::new();

for fragment in serialized_proof.into_iter().chunks(2000).into_iter() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

If a fragment upload fail, do we have a way that saya can make a call on the chain to resume from where it was?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not at the moment, but should be pretty straight forward to implement.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Same then let's keep that in the track for a follow up PR to be implemented to ensure more reliable settlement.

Copy link

codecov bot commented Aug 29, 2024

Codecov Report

Attention: Patch coverage is 19.20821% with 551 lines in your changes missing coverage. Please review.

Project coverage is 68.54%. Comparing base (e591364) to head (e47f0d1).
Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
crates/saya/core/src/lib.rs 0.00% 189 Missing ⚠️
crates/saya/core/src/prover/client.rs 0.00% 56 Missing ⚠️
crates/saya/core/src/verifier/starknet.rs 0.00% 52 Missing ⚠️
crates/saya/core/src/prover/mod.rs 0.00% 41 Missing ⚠️
crates/saya/core/src/verifier/utils.rs 0.00% 30 Missing ⚠️
bin/saya/src/args/mod.rs 75.47% 26 Missing ⚠️
...es/saya/core/src/data_availability/celestia/mod.rs 0.00% 25 Missing ⚠️
bin/saya/src/args/settlement.rs 0.00% 24 Missing ⚠️
crates/saya/core/src/dojo_os/piltover.rs 0.00% 24 Missing ⚠️
crates/saya/core/src/dojo_os/mod.rs 0.00% 22 Missing ⚠️
... and 7 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2253      +/-   ##
==========================================
+ Coverage   67.73%   68.54%   +0.80%     
==========================================
  Files         388      387       -1     
  Lines       50421    49902     -519     
==========================================
+ Hits        34153    34203      +50     
+ Misses      16268    15699     -569     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@matzayonc matzayonc requested a review from glihm August 29, 2024 16:31
@matzayonc matzayonc self-assigned this Aug 29, 2024
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between f5a9e80 and a65bb40.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (2)
  • crates/saya/core/Cargo.toml (1 hunks)
  • crates/saya/core/src/macros.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/saya/core/Cargo.toml
🧰 Additional context used

Comment on lines +1 to +4
#[macro_export]
macro_rules! retry {
// The macro takes an async block as an input
($func:expr) => {{
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Ohayo, sensei! Consider parameterizing retry values for greater flexibility.

The macro currently uses hardcoded values for MAX_RETRIES and RETRY_DELAY_MS. To make it more versatile, consider allowing these to be specified as parameters.

Here's a suggested improvement:

macro_rules! retry {
    ($func:expr, $max_retries:expr, $retry_delay_ms:expr) => {{
        let max_retries = $max_retries;
        let retry_delay_ms = $retry_delay_ms;
        // ... rest of the macro implementation ...
    }};
}

This change would allow users to customize the retry behavior based on their specific needs.

Comment on lines +6 to +9
const MAX_RETRIES: usize = 20;

// Set the delay between retries in milliseconds (adjust as needed)
const RETRY_DELAY_MS: u64 = 1000;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ohayo, sensei! Be wary of constant declarations inside macros.

Defining MAX_RETRIES and RETRY_DELAY_MS as constants inside the macro could lead to name conflicts if the macro is invoked multiple times within the same scope.

Consider using let bindings instead:

let max_retries: usize = 20;
let retry_delay_ms: u64 = 1000;

This approach avoids potential name conflicts and aligns with the parameterization suggestion from the previous comment.

Comment on lines +13 to +32
loop {
match $func.await {
Ok(result) => break Ok(result), // If the function succeeds, break the loop and
// return the result
Err(err) => {
tracing::warn!("Error: {}", err);

// Check if the maximum number of retries has been reached
if retry_count >= MAX_RETRIES {
break Err(err);
}

// Increment the retry count
retry_count += 1;
tracing::info!("Retrying... ({}/{})", retry_count, MAX_RETRIES);
// Wait before retrying
tokio::time::sleep(tokio::time::Duration::from_millis(RETRY_DELAY_MS)).await;
}
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Ohayo, sensei! Consider enhancing the retry strategy for more robustness.

The current implementation is solid, but there's room for improvement to make the macro more powerful and flexible.

Consider these enhancements:

  1. Implement different backoff strategies (e.g., exponential backoff).
  2. Allow users to provide a custom condition for retrying.

Here's a conceptual example:

macro_rules! retry {
    ($func:expr, $max_retries:expr, $backoff_strategy:expr, $should_retry:expr) => {{
        let mut retry_count = 0;
        loop {
            match $func.await {
                Ok(result) => break Ok(result),
                Err(err) => {
                    if !$should_retry(&err) || retry_count >= $max_retries {
                        break Err(err);
                    }
                    retry_count += 1;
                    let delay = $backoff_strategy(retry_count);
                    tokio::time::sleep(tokio::time::Duration::from_millis(delay)).await;
                }
            }
        }
    }};
}

This version allows users to specify a backoff strategy and a custom retry condition, providing greater control over the retry behavior.

Ok(result) => break Ok(result), // If the function succeeds, break the loop and
// return the result
Err(err) => {
tracing::warn!("Error: {}", err);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Ohayo, sensei! Consider enhancing error handling and logging.

The current implementation uses tracing effectively for logging errors and retry attempts. However, there's room for improvement in error handling and the information provided in logs.

Consider these enhancements:

  1. Implement structured error handling to categorize different types of errors.
  2. Include more context in error logs, such as the function name or a unique identifier.
  3. Add debug logging for successful attempts to aid in troubleshooting.

Here's an example of how you might implement these suggestions:

use std::fmt::Debug;

macro_rules! retry {
    ($func:expr, $max_retries:expr, $retry_delay_ms:expr) => {{
        let max_retries = $max_retries;
        let retry_delay_ms = $retry_delay_ms;
        let func_name = stringify!($func);

        let mut retry_count = 0;
        loop {
            match $func.await {
                Ok(result) => {
                    tracing::debug!("Function {} succeeded after {} attempts", func_name, retry_count + 1);
                    break Ok(result)
                },
                Err(err) => {
                    tracing::warn!("Error in {}: {:?}", func_name, err);
                    if retry_count >= max_retries {
                        tracing::error!("Max retries reached for {}", func_name);
                        break Err(err);
                    }
                    retry_count += 1;
                    tracing::info!("Retrying {} ({}/{})", func_name, retry_count, max_retries);
                    tokio::time::sleep(tokio::time::Duration::from_millis(retry_delay_ms)).await;
                }
            }
        }
    }};
}

This version provides more context in logs and adds debug logging for successful attempts, which can be valuable for troubleshooting and monitoring.

Also applies to: 27-27

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (4)
crates/saya/core/src/lib.rs (4)

14-14: Ohayo, sensei! The new imports look great!

The additions from itertools, prover, and starknet crates align well with the new functionality. The public re-export of ProverAccessKey is a nice touch for API usability.

Consider grouping related imports together for better readability. For example, you could group all starknet-related imports.

Also applies to: 19-22, 26-32


60-63: Ohayo, sensei! The SayaConfig struct has evolved nicely!

The new mode and settlement_contract fields, along with the more flexible block_range, align well with the PR objectives. These changes enhance the configurability of the Saya system.

Consider adding documentation comments for the new fields to explain their purpose and expected values. This will help users understand how to configure Saya correctly.


93-96: Ohayo, sensei! The Saya struct has gained some new superpowers!

The addition of prover_identifier, prev_commitment, and prev_height fields enhances Saya's capabilities for proving and state tracking. The initialization in the new method looks good.

Consider adding comments to explain the purpose of prev_commitment and prev_height, as their roles might not be immediately clear to newcomers.

Also applies to: 126-139


Line range hint 151-279: Ohayo, sensei! The start method has become a true master of ceremonies!

The refactoring to support different modes of operation and batch processing is impressive. The error handling and logging improvements are also great additions.

However, the method has grown quite complex. Consider breaking it down into smaller, more focused methods to improve readability and maintainability. For example:

  1. Extract the mode-specific logic into separate methods.
  2. Create a helper method for block fetching and processing.
  3. Move the proof generation and processing logic into a separate method.

This refactoring would make the start method easier to understand and maintain.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between a65bb40 and 5356d15.

📒 Files selected for processing (1)
  • crates/saya/core/src/lib.rs (13 hunks)
🧰 Additional context used
🔇 Additional comments (1)
crates/saya/core/src/lib.rs (1)

Line range hint 1-573: Ohayo, sensei! Overall, the changes to the Saya core library are impressive!

The introduction of different operational modes, enhanced StarkNet integration, and improved block processing align well with the PR objectives. The code structure and new functionality demonstrate a significant step forward for the Saya system.

However, there are a few areas that could benefit from further attention:

  1. The start method has become quite complex and could be refactored for better maintainability.
  2. There are some TODOs and commented-out code that need to be addressed.
  3. The StarknetAccountData struct could be improved for better security.
  4. Some methods could benefit from additional error handling and documentation.

Addressing these points will further enhance the quality and robustness of the codebase. Great work on this substantial update, sensei!

Comment on lines +426 to +522
// felt.to_bytes()).collect::<Vec<_>>());
let (commitment, height) = if self.config.mode != SayaMode::Ephemeral {
da.publish_checkpoint(checkpoint).await?
} else if self.config.skip_publishing_proof {
da.publish_state_diff_felts(&state_diff).await?
} else {
da.publish_state_diff_and_proof_felts(&world_da, &serialized_proof).await?;
}
da.publish_state_diff_and_proof_felts(&state_diff, &serialized_proof).await?
};
self.prev_commitment = Some(commitment);
self.prev_height = Some(height);

info!(target: LOG_TARGET,"commitment: {:?}, height: {:?}", commitment.0, height);
}

let program_hash = proof.program_hash;
let program_output_hash = proof.program_output_hash;
let program_output = proof.program_output;

let program_hash_string = program_hash;
let program_output_hash_string = program_output_hash;

info!(target: LOG_TARGET,"Extracted program hash and output hash. {:?} {:?}", program_hash_string, program_output_hash_string);
let expected_fact = poseidon_hash_many(&[program_hash, program_output_hash]).to_string();
let program = program_hash.to_string();
info!(target: LOG_TARGET, expected_fact, program, "Expected fact.");

let starknet_account = self.config.starknet_account.get_starknet_account()?;

// Verify the proof and register fact.
trace!(target: LOG_TARGET, last_block, "Verifying block.");
let (transaction_hash, nonce_after) = verifier::verify(
let (transaction_hash, _nonce) = verifier::verify(
VerifierIdentifier::HerodotusStarknetSepolia(self.config.fact_registry_address),
serialized_proof,
self.config.starknet_account.clone(),
&starknet_account,
self.config.mode.to_program().cairo_version(),
)
.await?;
info!(target: LOG_TARGET, last_block, transaction_hash, "Block verified.");

let ExtractProgramResult { program: _, program_hash } = extract_program(&proof)?;
let ExtractOutputResult { program_output, program_output_hash } = extract_output(&proof)?;
let expected_fact = poseidon_hash_many(&[program_hash, program_output_hash]).to_string();
info!(target: LOG_TARGET, expected_fact, "Expected fact.");

// When not waiting for couple of second `apply_diffs` will sometimes fail due to reliance
// on registered fact
tokio::time::sleep(std::time::Duration::from_secs(2)).await;

trace!(target: LOG_TARGET, last_block, "Applying diffs.");
let transaction_hash = dojo_os::starknet_apply_diffs(
self.config.world_address,
world_da,
program_output,
program_hash,
nonce_after + Felt::ONE,
self.config.starknet_account.clone(),
)
.await?;
info!(target: LOG_TARGET, last_block, transaction_hash, "Diffs applied.");

// Apply the diffs to the world state.
match self.config.mode {
SayaMode::Ephemeral => {
// Needs checker program to be verified, and set as the upgrade_state authority
todo!("Ephemeral mode does not support publishing updated state yet.");
}
SayaMode::Persistent => {
let serialized_output = program_output.iter().copied().collect_vec();
println!("serialized_output: {:?}", serialized_output);

// todo!("Persistent mode does not support publishing updated state with SNOS
// yet.");

let deduplicated_output =
serialized_output[1..serialized_output.len() / 2].to_vec();
let batcher_output = from_felts::<StarknetOsOutput>(&deduplicated_output).unwrap();
let piltover_calldata = PiltoverCalldata {
program_output: serialized_output,
// onchain_data_hash: batcher_output.new_state_root,
onchain_data_hash: batcher_output.new_block_hash,
onchain_data_size: (Felt::ZERO, Felt::ZERO),
};

let expected_state_root = batcher_output.new_block_hash.to_string();
let expected_block_number =
(batcher_output.new_block_number - Felt::ONE).to_string();
info!(target: LOG_TARGET, last_block, expected_state_root, expected_block_number, "Applying snos to piltover.");

starknet_apply_piltover(
piltover_calldata,
self.config.settlement_contract,
&starknet_account,
)
.await?;
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ohayo, sensei! The process_proven method is a true proof master!

The implementation for publishing state differences and verifying proofs looks solid. Great job on supporting different modes of operation!

However, there are a few areas that need attention:

  1. There's a TODO comment for the Ephemeral mode (line 491). This needs to be implemented.
  2. There's a commented-out line for onchain_data_hash (line 505). Decide whether to use this or remove it.
  3. The error handling for from_felts (line 502) uses unwrap(), which could lead to panics. Consider using ? or proper error handling.

Please address these issues to ensure the robustness of the process_proven method.

Comment on lines +548 to +573
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct StarknetAccountData {
pub starknet_url: Url,
#[serde(deserialize_with = "felt_string_deserializer")]
pub chain_id: Felt,
pub signer_address: Felt,
pub signer_key: Felt,
}

impl StarknetAccountData {
pub fn get_starknet_account(&self) -> anyhow::Result<SayaStarknetAccount> {
let provider = JsonRpcClient::new(HttpTransport::new(self.starknet_url.clone()));
let signer = LocalWallet::from(SigningKey::from_secret_scalar(self.signer_key));

let mut account = SingleOwnerAccount::new(
provider,
signer,
self.signer_address,
self.chain_id,
ExecutionEncoding::New,
);

account.set_block_id(BlockId::Tag(BlockTag::Pending));
Ok(account)
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ohayo, sensei! The StarknetAccountData struct is a nice addition, but let's talk security!

The implementation of StarknetAccountData and its get_starknet_account method improves the handling of StarkNet account information. However, there's a potential security concern:

Storing the signer_key directly in the struct might expose sensitive information. Consider using a more secure method for handling the signer key, such as:

  1. Using a separate, encrypted storage for the key.
  2. Implementing a method to securely retrieve the key when needed, possibly requiring user input for decryption.

Here's a conceptual example:

pub struct StarknetAccountData {
    pub starknet_url: Url,
    #[serde(deserialize_with = "felt_string_deserializer")]
    pub chain_id: Felt,
    pub signer_address: Felt,
    // Remove the signer_key field
}

impl StarknetAccountData {
    pub fn get_starknet_account(&self, key_provider: &dyn SecureKeyProvider) -> anyhow::Result<SayaStarknetAccount> {
        let provider = JsonRpcClient::new(HttpTransport::new(self.starknet_url.clone()));
        let signer_key = key_provider.get_signer_key()?; // Securely retrieve the key
        let signer = LocalWallet::from(SigningKey::from_secret_scalar(signer_key));

        // ... rest of the method ...
    }
}

This approach would enhance the security of the sensitive signer key.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (4)
bin/saya/src/args/mod.rs (4)

49-50: Ohayo! The SayaArgs struct is leveling up, sensei!

The new end_block field and the settlement options are great additions that provide more flexibility in controlling block processing and settlement. The conflict between batch_size and end_block is a smart move to prevent inconsistencies.

One small suggestion:

Consider adding a comment explaining why batch_size and end_block are mutually exclusive. This will help future developers understand the reasoning behind this constraint.

/// The number of blocks to be merged into a single proof.
/// This option conflicts with `end_block` to ensure consistent batch processing.
#[arg(conflicts_with = "end_block")]
pub batch_size: usize,

Also applies to: 54-60


100-193: Ohayo, sensei! The try_from implementation is looking sharp!

The new logic for handling DataAvailabilityConfig, private keys, and settlement options is well-implemented. The validation for batch_size in persistent mode is a crucial addition to ensure correct behavior.

However, I have a small suggestion to improve error handling consistency:

Consider using a custom error type instead of Box<dyn std::error::Error> for more specific error handling. This would make it easier to handle different error cases and provide more informative error messages to users.

For example:

#[derive(thiserror::Error, Debug)]
enum SayaConfigError {
    #[error("Celestia config: {0}")]
    CelestiaConfig(String),
    #[error("Invalid input: {0}")]
    InvalidInput(String),
    // Add more error variants as needed
}

impl TryFrom<SayaArgs> for SayaConfig {
    type Error = SayaConfigError;

    fn try_from(args: SayaArgs) -> Result<Self, Self::Error> {
        // Use the custom error type in your implementation
        // For example:
        // Err(SayaConfigError::CelestiaConfig("Node url is required".to_string()))
    }
}

This approach would make error handling more robust and easier to maintain as the codebase grows.


Line range hint 199-297: Ohayo! The test case is looking good, sensei!

The updates to the test case accurately reflect the changes in the structs and implementation. Good job on maintaining test coverage!

To make your tests even more robust, consider adding these additional test cases:

  1. Test the validation of batch_size in persistent mode:
#[test]
fn test_batch_size_validation_in_persistent_mode() {
    let args = SayaArgs {
        // ... other fields ...
        batch_size: 2,
        settlement: SettlementOptions {
            saya_mode: settlement::SayaModeArg(SayaMode::Persistent),
            // ... other fields ...
        },
        // ... other fields ...
    };

    let result: Result<SayaConfig, _> = args.try_into();
    assert!(result.is_err());
    assert!(result.unwrap_err().to_string().contains("Batch size must be 1 for persistent mode"));
}
  1. Test the mutual exclusivity of batch_size and end_block:
#[test]
fn test_batch_size_and_end_block_conflict() {
    let args = SayaArgs {
        // ... other fields ...
        batch_size: 10,
        end_block: Some(100),
        // ... other fields ...
    };

    let result: Result<SayaConfig, _> = args.try_into();
    assert!(result.is_err());
    assert!(result.unwrap_err().to_string().contains("The argument '--batch-size <BATCH_SIZE>' cannot be used with '--end-block <END_BLOCK>'"));
}

These additional tests will help ensure that the new constraints and validations are working as expected.


169-177: Ohayo, sensei! The settlement_contract validation is on point!

The addition of the settlement_contract field and its validation for persistent mode is crucial. Great job on ensuring this important requirement is met!

To make the error message even more helpful:

Consider updating the error message to be more specific about the requirement:

return Err(SayaConfigError::InvalidInput(
    "A `settlement_contract` must be specified when using persistent mode.".to_string()
));

This more detailed message will help users understand exactly what they need to provide when using persistent mode.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 3d6be35 and 9ac3bdf.

📒 Files selected for processing (1)
  • bin/saya/src/args/mod.rs (6 hunks)
🧰 Additional context used
🔇 Additional comments (2)
bin/saya/src/args/mod.rs (2)

6-7: Ohayo, sensei! The new imports and module look good!

The addition of SayaMode and SettlementOptions imports, along with the new settlement module, aligns well with the introduction of settlement-related functionality. Nice work on keeping the code structure organized!

Also applies to: 21-21


Line range hint 1-297: Ohayo, sensei! Your code is evolving beautifully!

Overall, the changes to this file significantly enhance the Saya configuration process. The new settlement options, block range controls, and data availability configurations provide great flexibility for users. The added validations, especially for the persistent mode and settlement_contract, help prevent misconfigurations.

Here's a summary of the improvements:

  1. Enhanced SayaArgs struct with new options
  2. Improved try_from implementation with robust error checking
  3. Updated test cases to cover new functionality

To take your code to the next level, consider:

  1. Implementing a custom error type for more specific error handling
  2. Adding a few more test cases to cover edge cases
  3. Improving some error messages for clarity

Great work on evolving the Saya tool, sensei! These changes will definitely improve the user experience and make the tool more powerful.

Comment on lines +49 to +50
#[arg(short, long)]
pub end_block: Option<u64>,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ohayo, sensei! Let's add a quick check for end_block!

The addition of the end_block field is a great improvement for flexibility. However, to prevent potential issues, we should ensure that end_block is greater than or equal to start_block when it's provided.

Consider adding this validation in the try_from implementation:

if let Some(end_block) = args.end_block {
    if end_block < args.start_block {
        return Err(SayaConfigError::InvalidInput(
            "`end_block` must be greater than or equal to `start_block`".to_string()
        ));
    }
}

This check will help prevent invalid block ranges and potential runtime errors.

Also applies to: 184-184

@glihm glihm merged commit 4fb8f3f into dojoengine:main Oct 9, 2024
14 of 15 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Oct 23, 2024
10 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants