Skip to content

Commit

Permalink
Add katana RPC namespace (#50)
Browse files Browse the repository at this point in the history
  • Loading branch information
kariy authored May 12, 2023
1 parent 559242c commit 0bddc87
Show file tree
Hide file tree
Showing 12 changed files with 856 additions and 658 deletions.
87 changes: 43 additions & 44 deletions katana-cli/src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
use std::{path::PathBuf, process::exit, sync::Arc};
use std::{process::exit, sync::Arc};

use clap::Parser;
use env_logger::Env;
use katana_core::{sequencer::KatanaSequencer, starknet::StarknetConfig};
use katana_rpc::{config::RpcConfig, KatanaRpc};
use katana_core::sequencer::KatanaSequencer;
use katana_rpc::KatanaNodeRpc;
use log::error;
use tokio::sync::RwLock;
use yansi::Paint;

mod config;

use config::Cli;

#[tokio::main]
async fn main() {
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
Expand All @@ -19,17 +23,27 @@ async fn main() {
let sequencer = Arc::new(RwLock::new(KatanaSequencer::new(starknet_config)));
sequencer.write().await.start();

let predeployed_accounts = sequencer
.read()
.await
.starknet
.predeployed_accounts
.display();
let predeployed_accounts = if config.hide_predeployed_accounts {
None
} else {
Some(
sequencer
.read()
.await
.starknet
.predeployed_accounts
.display(),
)
};

match KatanaRpc::new(sequencer.clone(), rpc_config).run().await {
match KatanaNodeRpc::new(sequencer.clone(), rpc_config)
.run()
.await
{
Ok((addr, server_handle)) => {
print_intro(
predeployed_accounts,
config.seed,
format!(
"🚀 JSON-RPC server started: {}",
Paint::red(format!("http://{addr}"))
Expand All @@ -45,36 +59,7 @@ async fn main() {
};
}

#[derive(Parser, Debug)]
struct Cli {
#[arg(short, long)]
#[arg(default_value = "5050")]
#[arg(help = "Port number to listen on.")]
port: u16,

#[arg(long)]
#[arg(default_value = "10")]
#[arg(help = "Number of pre-funded accounts to generate.")]
accounts: u8,

#[arg(long)]
account_path: Option<PathBuf>,
}

impl Cli {
fn rpc_config(&self) -> RpcConfig {
RpcConfig { port: self.port }
}

fn starknet_config(&self) -> StarknetConfig {
StarknetConfig {
total_accounts: self.accounts,
account_path: self.account_path.clone(),
}
}
}

fn print_intro(accounts: String, address: String) {
fn print_intro(accounts: Option<String>, seed: Option<String>, address: String) {
println!(
"{}",
Paint::red(
Expand All @@ -91,12 +76,26 @@ fn print_intro(accounts: String, address: String) {
"
)
);
println!(
r"

if let Some(accounts) = accounts {
println!(
r"
PREFUNDED ACCOUNTS
==================
{accounts}
"
);
"
);
}

if let Some(seed) = seed {
println!(
r"
ACCOUNTS SEED
=============
{seed}
"
);
}

println!("\n{address}\n\n");
}
75 changes: 75 additions & 0 deletions katana-cli/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use std::path::PathBuf;

use clap::Parser;
use katana_core::starknet::StarknetConfig;
use katana_rpc::config::RpcConfig;

#[derive(Parser, Debug)]
pub struct Cli {
#[arg(short, long)]
#[arg(default_value = "5050")]
#[arg(help = "Port number to listen on.")]
pub port: u16,

#[arg(long)]
#[arg(help = "Allow transaction max fee to be zero.")]
pub allow_max_fee_zero: bool,

#[arg(long)]
#[arg(help = "Specify the seed for randomness of accounts to be predeployed.")]
pub seed: Option<String>,

#[arg(long)]
#[arg(default_value = "10")]
#[arg(help = "Number of pre-funded accounts to generate.")]
pub accounts: u8,

#[arg(long)]
#[arg(help = "Prevents from printing the predeployed accounts details.")]
pub hide_predeployed_accounts: bool,

#[arg(long)]
#[arg(help = "Block generation on demand via an endpoint.")]
pub blocks_on_demand: bool,

#[arg(long)]
#[arg(help = "The account implementation for the predeployed accounts.")]
#[arg(
long_help = "Specify the account implementation to be used for the predeployed accounts; should be a path to the
compiled JSON artifact."
)]
pub account_class: Option<PathBuf>,
}

impl Cli {
pub fn rpc_config(&self) -> RpcConfig {
RpcConfig { port: self.port }
}

pub fn starknet_config(&self) -> StarknetConfig {
StarknetConfig {
total_accounts: self.accounts,
seed: parse_seed(self.seed.clone()),
block_on_demand: self.blocks_on_demand,
account_path: self.account_class.clone(),
allow_zero_max_fee: self.allow_max_fee_zero,
}
}
}

fn parse_seed(seed: Option<String>) -> [u8; 32] {
seed.map(|seed| {
let seed = seed.as_bytes();

if seed.len() >= 32 {
unsafe { *(seed[..32].as_ptr() as *const [u8; 32]) }
} else {
let mut actual_seed = [0u8; 32];
seed.iter()
.enumerate()
.for_each(|(i, b)| actual_seed[i] = *b);
actual_seed
}
})
.unwrap_or_default()
}
8 changes: 8 additions & 0 deletions katana-core/src/sequencer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,11 +320,19 @@ impl Sequencer for KatanaSequencer {
)),
)
}

fn generate_new_block(&mut self) -> Result<()> {
self.starknet.generate_latest_block()?;
self.starknet.generate_pending_block();
Ok(())
}
}

pub trait Sequencer {
fn chain_id(&self) -> ChainId;

fn generate_new_block(&mut self) -> Result<()>;

fn nonce_at(
&mut self,
block_id: BlockId,
Expand Down
38 changes: 32 additions & 6 deletions katana-core/src/starknet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ use blockifier::{
state_api::State,
},
transaction::{
objects::AccountTransactionContext, transaction_execution::Transaction,
transactions::ExecutableTransaction,
account_transaction::AccountTransaction,
objects::AccountTransactionContext,
transaction_execution::Transaction,
transactions::{DeclareTransaction, ExecutableTransaction},
},
};
use starknet::{
Expand Down Expand Up @@ -45,7 +47,10 @@ use transaction::{StarknetTransaction, StarknetTransactions};
use self::transaction::ExternalFunctionCall;

pub struct StarknetConfig {
pub seed: [u8; 32],
pub total_accounts: u8,
pub block_on_demand: bool,
pub allow_zero_max_fee: bool,
pub account_path: Option<PathBuf>,
}

Expand All @@ -67,7 +72,7 @@ impl StarknetWrapper {

let predeployed_accounts = PredeployedAccounts::generate(
config.total_accounts,
[0u8; 32],
config.seed,
stark_felt!(DEFAULT_PREFUNDED_ACCOUNT_BALANCE),
config
.account_path
Expand Down Expand Up @@ -97,7 +102,10 @@ impl StarknetWrapper {
);

let res = match transaction {
Transaction::AccountTransaction(tx) => tx.execute(&mut self.state, &self.block_context),
Transaction::AccountTransaction(tx) => {
self.check_tx_fee(&tx);
tx.execute(&mut self.state, &self.block_context)
}
Transaction::L1HandlerTransaction(tx) => {
tx.execute(&mut self.state, &self.block_context)
}
Expand All @@ -120,9 +128,11 @@ impl StarknetWrapper {
.insert_transaction(api_tx);

self.store_transaction(starknet_tx);
self.generate_latest_block()?;

self.generate_pending_block();
if !self.config.block_on_demand {
self.generate_latest_block()?;
self.generate_pending_block();
}
}

Err(exec_err) => {
Expand Down Expand Up @@ -236,6 +246,22 @@ impl StarknetWrapper {
unimplemented!("StarknetWrapper::state")
}

fn check_tx_fee(&self, transaction: &AccountTransaction) {
let max_fee = match transaction {
AccountTransaction::Invoke(tx) => tx.max_fee(),
AccountTransaction::DeployAccount(tx) => tx.max_fee,
AccountTransaction::Declare(DeclareTransaction { tx, .. }) => match tx {
starknet_api::transaction::DeclareTransaction::V0(tx) => tx.max_fee,
starknet_api::transaction::DeclareTransaction::V1(tx) => tx.max_fee,
starknet_api::transaction::DeclareTransaction::V2(tx) => tx.max_fee,
},
};

if !self.config.allow_zero_max_fee && max_fee.0 == 0 {
panic!("max fee == 0 is not supported")
}
}

fn create_new_empty_block(&self) -> StarknetBlock {
let block_number = self.block_context.block_number;

Expand Down
3 changes: 3 additions & 0 deletions katana-core/tests/starknet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ fn create_test_starknet() -> StarknetWrapper {
.collect();

StarknetWrapper::new(StarknetConfig {
seed: [0u8; 32],
total_accounts: 2,
block_on_demand: false,
allow_zero_max_fee: true,
account_path: Some(test_account_path),
})
}
Expand Down
1 change: 1 addition & 0 deletions katana-rpc/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#[derive(Debug, Clone)]
pub struct RpcConfig {
pub port: u16,
}
24 changes: 24 additions & 0 deletions katana-rpc/src/katana/api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use jsonrpsee::{
core::Error,
proc_macros::rpc,
types::{error::CallError, ErrorObject},
};

#[derive(thiserror::Error, Clone, Copy, Debug)]
pub enum KatanaApiError {}

impl From<KatanaApiError> for Error {
fn from(err: KatanaApiError) -> Self {
Error::Call(CallError::Custom(ErrorObject::owned(
err as i32,
err.to_string(),
None::<()>,
)))
}
}

#[rpc(server, client, namespace = "katana")]
pub trait KatanaApi {
#[method(name = "generateBlock")]
async fn generate_block(&self) -> Result<(), Error>;
}
27 changes: 27 additions & 0 deletions katana-rpc/src/katana/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use std::sync::Arc;

use jsonrpsee::core::{async_trait, Error};
use katana_core::sequencer::Sequencer;
use tokio::sync::RwLock;

use self::api::KatanaApiServer;

pub mod api;

pub struct KatanaRpc<S> {
sequencer: Arc<RwLock<S>>,
}

impl<S: Sequencer + Send + Sync + 'static> KatanaRpc<S> {
pub fn new(sequencer: Arc<RwLock<S>>) -> Self {
Self { sequencer }
}
}

#[async_trait]
impl<S: Sequencer + Send + Sync + 'static> KatanaApiServer for KatanaRpc<S> {
async fn generate_block(&self) -> Result<(), Error> {
self.sequencer.write().await.generate_new_block()?;
Ok(())
}
}
Loading

0 comments on commit 0bddc87

Please sign in to comment.