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

feat: Starknet "add_invoke_transaction" #175

Merged
merged 2 commits into from
Jan 16, 2023
Merged
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
226 changes: 94 additions & 132 deletions Cargo.lock

Large diffs are not rendered by default.

29 changes: 28 additions & 1 deletion beerus_cli/src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ use ethers::types::{H256, U256};
use helios::types::ExecutionBlock;
use serde_json::json;
use starknet::core::types::FieldElement;
use starknet::providers::jsonrpc::models::{BlockHashAndNumber, ContractClass, SyncStatusType};
use starknet::providers::jsonrpc::models::{
BlockHashAndNumber, ContractClass, InvokeTransactionResult, SyncStatusType,
};
use std::{fmt::Display, path::PathBuf};

/// Main struct for the Beerus CLI args.
Expand Down Expand Up @@ -239,6 +241,26 @@ pub enum StarkNetSubCommands {
block_id: String,
},
QuerySyncing {},
AddInvokeTransaction {
/// Max fee
#[arg(short, long, value_name = "MAX_FEE")]
max_fee: String,
/// The signature
#[arg(short, long, value_name = "SIGNATURE", value_delimiter = ',')]
signature: Vec<String>,
/// The nonce
#[arg(short, long, value_name = "NONCE")]
nonce: String,
/// The contract address
#[arg(short, long, value_name = "CONTRACT_ADDRESS")]
contract_address: String,
// The entry point selector
#[arg(short, long, value_name = "CONTRACT_ADDRESS")]
entry_point_selector: String,
/// The calldata
#[arg(short, long, value_name = "CALLDATA", value_delimiter = ',')]
calldata: Vec<String>,
},
}

/// The response from a CLI command.
Expand Down Expand Up @@ -270,6 +292,7 @@ pub enum CommandResponse {
StarknetQueryGetClassAt(ContractClass),
StarknetQueryGetBlockTransactionCount(u64),
StarknetQuerySyncing(SyncStatusType),
StarknetAddInvokeTransaction(InvokeTransactionResult),
StarkNetL1ToL2MessageCancellations(U256),
StarkNetL1ToL2Messages(U256),
StarkNetL1ToL2MessageNonce(U256),
Expand Down Expand Up @@ -535,6 +558,10 @@ impl Display for CommandResponse {
write!(f, "{json_response}")
}
},

CommandResponse::StarknetAddInvokeTransaction(response) => {
write!(f, "{response:?}")
}
}
}
}
19 changes: 19 additions & 0 deletions beerus_cli/src/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,25 @@ pub async fn run(beerus: BeerusLightClient, cli: Cli) -> Result<CommandResponse>
.await
}
StarkNetSubCommands::QuerySyncing {} => starknet::query_starknet_syncing(beerus).await,
StarkNetSubCommands::AddInvokeTransaction {
max_fee,
signature,
nonce,
contract_address,
entry_point_selector,
calldata,
} => {
starknet::add_invoke_transaction(
beerus,
max_fee.to_string(),
signature.to_owned(),
nonce.to_string(),
contract_address.to_string(),
entry_point_selector.to_string(),
calldata.to_owned(),
)
.await
}
},
}
}
60 changes: 58 additions & 2 deletions beerus_cli/src/starknet/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use std::str::FromStr;

use crate::model::CommandResponse;
use beerus_core::lightclient::beerus::BeerusLightClient;
use ethers::types::U256;
use eyre::Result;
use starknet::core::types::FieldElement;

use crate::model::CommandResponse;
use starknet::providers::jsonrpc::models::{
BroadcastedInvokeTransaction, BroadcastedInvokeTransactionV0,
};

/// Query the StarkNet state root.
/// # Arguments
Expand Down Expand Up @@ -293,3 +295,57 @@ pub async fn query_starknet_syncing(beerus: BeerusLightClient) -> Result<Command
beerus.starknet_lightclient.syncing().await?,
))
}

/// Add an Invoke transaction to the StarkNet network.
/// # Arguments
/// * `beerus` - The Beerus light client.
/// * `max_fee` - The maximum fee.
/// * `signature` - The signature.
/// * `nonce` - The nonce.
/// * `contract_address` - The contract address.
/// * `entry_point_selector` - The entry point selector.
/// * `calldata` - The calldata.
///
/// # Returns
///
/// * `Result<CommandResponse>` - If the node is synchronized on the StarkNet network.
pub async fn add_invoke_transaction(
beerus: BeerusLightClient,
max_fee: String,
signature: Vec<String>,
nonce: String,
contract_address: String,
entry_point_selector: String,
calldata: Vec<String>,
) -> Result<CommandResponse> {
let max_fee: FieldElement = FieldElement::from_str(&max_fee).unwrap();
let signature = signature
.iter()
.map(|x| FieldElement::from_str(x).unwrap())
.collect();
let nonce: FieldElement = FieldElement::from_str(&nonce).unwrap();
let contract_address: FieldElement = FieldElement::from_str(&contract_address).unwrap();
let entry_point_selector: FieldElement = FieldElement::from_str(&entry_point_selector).unwrap();
let calldata = calldata
.iter()
.map(|x| FieldElement::from_str(x).unwrap())
.collect();

let transaction_data = BroadcastedInvokeTransactionV0 {
max_fee,
signature,
nonce,
contract_address,
entry_point_selector,
calldata,
};

let invoke_transaction = BroadcastedInvokeTransaction::V0(transaction_data);

Ok(CommandResponse::StarknetAddInvokeTransaction(
beerus
.starknet_lightclient
.add_invoke_transaction(&invoke_transaction)
.await?,
))
}
93 changes: 92 additions & 1 deletion beerus_cli/tests/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ mod test {
};
use ethers::types::{Address, Transaction, H256, U256};
use helios::types::{ExecutionBlock, Transactions};
use starknet::{core::types::FieldElement, providers::jsonrpc::models::BlockHashAndNumber};
use starknet::{
core::types::FieldElement,
providers::jsonrpc::models::{BlockHashAndNumber, InvokeTransactionResult},
};

/// Test the `send_raw_transaction` CLI command.
/// Given normal conditions, when sending raw transaction, then ok.
Expand Down Expand Up @@ -2322,6 +2325,94 @@ mod test {
}
}

/// Test the `add_invoke_transaction` CLI command.
/// Given normal conditions, when query add_invoke_transaction, then ok.
#[tokio::test]
async fn given_normal_conditions_when_starknet_add_invoke_transaction_then_ok() {
// Build mocks.
let (config, ethereum_lightclient, mut starknet_lightclient) = config_and_mocks();

// Given
let expected_result = InvokeTransactionResult {
transaction_hash: FieldElement::from_str("0x01").unwrap(),
};
// Set the expected return value for the StarkNet light client mock.
starknet_lightclient
.expect_add_invoke_transaction()
.return_once(move |_| Ok(expected_result));

let beerus = BeerusLightClient::new(
config,
Box::new(ethereum_lightclient),
Box::new(starknet_lightclient),
);

let params = StarkNetSubCommands::AddInvokeTransaction {
max_fee: "0".to_string(),
signature: vec![10.to_string()],
nonce: "0".to_string(),
contract_address: "0".to_string(),
entry_point_selector: "0".to_string(),
calldata: vec![10.to_string()],
};

// Mock the command line arguments.
let cli = Cli {
config: None,
command: Commands::StarkNet(StarkNetCommands { command: params }),
};
// When
let result = runner::run(beerus, cli).await.unwrap();

// Then
assert_eq!("InvokeTransactionResult { transaction_hash: FieldElement { inner: 0x0000000000000000000000000000000000000000000000000000000000000001 } }", result.to_string());
}

/// Test the `add_invoke_transaction` CLI command.
/// Given starknet lightclient returns an error, when query add_invoke_transaction, then the error is propagated.
/// Error case.
#[tokio::test]
async fn given_starknet_lightclient_returns_error_when_starknet_add_invoke_transaction_then_error_is_propagated(
) {
// Build mocks.
let (config, ethereum_lightclient, mut starknet_lightclient) = config_and_mocks();

// Given
// Set the expected return value for the StarkNet light client mock.
starknet_lightclient
.expect_add_invoke_transaction()
.return_once(move |_| Err(eyre::eyre!("starknet_lightclient_error")));

let beerus = BeerusLightClient::new(
config,
Box::new(ethereum_lightclient),
Box::new(starknet_lightclient),
);

// Mock the command line arguments.
let cli = Cli {
config: None,
command: Commands::StarkNet(StarkNetCommands {
command: StarkNetSubCommands::AddInvokeTransaction {
max_fee: "0".to_string(),
signature: vec![],
nonce: "0".to_string(),
contract_address: "0".to_string(),
entry_point_selector: "0".to_string(),
calldata: vec![],
},
}),
};
// When
let result = runner::run(beerus, cli).await;

// Then
match result {
Err(e) => assert_eq!("starknet_lightclient_error", e.to_string()),
Ok(_) => panic!("Expected error, got ok"),
}
}

fn config_and_mocks() -> (Config, MockEthereumLightClient, MockStarkNetLightClient) {
let config = Config {
ethereum_network: "mainnet".to_string(),
Expand Down
32 changes: 31 additions & 1 deletion beerus_core/src/lightclient/starknet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ use starknet::{
core::types::FieldElement,
providers::jsonrpc::{
models::FunctionCall,
models::{BlockHashAndNumber, BlockId, ContractClass, SyncStatusType},
models::{
BlockHashAndNumber, BlockId, BroadcastedInvokeTransaction, ContractClass,
InvokeTransactionResult, SyncStatusType,
},
HttpTransport, JsonRpcClient,
},
};
Expand Down Expand Up @@ -41,6 +44,10 @@ pub trait StarkNetLightClient: Send + Sync {
) -> Result<ContractClass>;
async fn get_block_transaction_count(&self, block_id: &BlockId) -> Result<u64>;
async fn syncing(&self) -> Result<SyncStatusType>;
async fn add_invoke_transaction(
&self,
invoke_transaction: &BroadcastedInvokeTransaction,
) -> Result<InvokeTransactionResult>;
}

pub struct StarkNetLightClientImpl {
Expand Down Expand Up @@ -227,4 +234,27 @@ impl StarkNetLightClient for StarkNetLightClientImpl {
async fn syncing(&self) -> Result<SyncStatusType> {
self.client.syncing().await.map_err(|e| eyre::eyre!(e))
}

/// Add an invoke transaction
///
/// # Arguments
///
/// invoke_transaction : Transaction data
///
///
/// # Returns
///
/// Result : Invoke Transaction Result
///
/// `Ok(InvokeTransactionResult)` if the operation was successful.
/// `Err(eyre::Report)` if the operation failed.
async fn add_invoke_transaction(
&self,
invoke_transaction: &BroadcastedInvokeTransaction,
) -> Result<InvokeTransactionResult> {
self.client
.add_invoke_transaction(invoke_transaction)
.await
.map_err(|e| eyre::eyre!(e))
}
}
Loading