Skip to content
This repository has been archived by the owner on Aug 2, 2024. It is now read-only.

feat: Declare V0 RPC call #1617

Merged
merged 30 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
aa8745c
feat : added declare v0
May 26, 2024
ecf3596
feat : added declare common function
May 27, 2024
8a05ffd
feat : fixed mismatch types
May 28, 2024
d77d90a
feat : added declare v0 deps and testing done
May 31, 2024
9342f36
removed setup dir
May 31, 2024
69f2ee0
feat : removed uneccessary logs
May 31, 2024
2a7f977
added pr title in changelog
May 31, 2024
a6c9126
fix : fixed prettier issues
May 31, 2024
0d1185f
feat : added requested changes
May 31, 2024
205151f
feat : added requested changes 2
Jun 1, 2024
73a0f05
feat : added requested changes 2
Jun 1, 2024
e848d97
Merge branch 'main' into main
ocdbytes Jun 1, 2024
3c2f7ef
lint test fix
Jun 1, 2024
d8df267
lint test fix
Jun 1, 2024
3767dbb
feat : added new requested changes : formatting and redundant code re…
Jun 3, 2024
6b9615f
Merge pull request #1 from ocdbytes/declareV0/feat
ocdbytes Jun 3, 2024
840f38b
lint issue fixed
Jun 3, 2024
62b6cd9
feat : rpc test fix & declare v0 rpc test added
Jun 3, 2024
bd5dec7
feat : fixed lint issues
Jun 3, 2024
90951ee
fix declare V0 test
Jun 3, 2024
e73623e
feat : fix lint issues
Jun 3, 2024
90c7620
feat : fix lint issues
Jun 3, 2024
f7d0f59
fix : added rpc test fixes
Jun 3, 2024
96c529c
feat : refactoring and comments added
Jun 17, 2024
f68fd03
Merge branch 'main' into main
ocdbytes Jun 17, 2024
0fb2574
refactor : changed program_vec to program_bytes
Jun 19, 2024
c0e504a
add_declare_transaction_v0 e2e
tdelabro Jun 19, 2024
ee50d2d
Merge pull request #2 from tdelabro/add-declare-transaction-v0
ocdbytes Jun 19, 2024
ea34185
fix : lint
Jun 19, 2024
54033cf
feat : refactor and removed unused deps
Jun 20, 2024
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

## v0.8.0

- feat: Declare V0 RPC call
- feat: add `TransactionFilter<TxType>` to pallet-starknet `Config`
- chore: remove `ignore` from
`storage_changes_should_revert_on_transaction_revert` test
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

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

37 changes: 36 additions & 1 deletion crates/client/rpc-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,15 @@
#[cfg(test)]
mod tests;

use jsonrpsee::core::RpcResult;
use blockifier::transaction::transactions::DeclareTransaction;
use indexmap::IndexMap;
use jsonrpsee::core::{RpcResult};
use jsonrpsee::proc_macros::rpc;
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use starknet_api::core::ClassHash;
use starknet_api::deprecated_contract_class::{EntryPoint, EntryPointType};
use starknet_api::transaction::{DeclareTransactionV0V1, TransactionHash};

pub mod utils;

Expand All @@ -36,11 +41,41 @@ pub struct PredeployedAccountWithBalance {
pub balance: FieldElement,
}

#[serde_as]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DeclareV0Result {
pub txn_hash: TransactionHash,
pub class_hash: ClassHash,
}

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
tdelabro marked this conversation as resolved.
Show resolved Hide resolved
pub struct CustomDeclareV0Transaction {
pub declare_transaction: DeclareTransactionV0V1,
pub program_vec: Vec<u8>,
tdelabro marked this conversation as resolved.
Show resolved Hide resolved
pub entrypoints: IndexMap<EntryPointType, Vec<EntryPoint>>,
tdelabro marked this conversation as resolved.
Show resolved Hide resolved
pub abi_length: usize
}

#[derive(PartialEq, Eq, Debug)]
pub enum DeclareTransactionWithV0 {
tdelabro marked this conversation as resolved.
Show resolved Hide resolved
V0(DeclareTransactionV0V1, Vec<u8>, IndexMap<EntryPointType, Vec<EntryPoint>>, usize),
tdelabro marked this conversation as resolved.
Show resolved Hide resolved
V1(DeclareTransaction),
}

/// Madara rpc interface for additional features.
#[rpc(server, namespace = "madara")]
pub trait MadaraRpcApi: StarknetReadRpcApi {
#[method(name = "predeployedAccounts")]
fn predeployed_accounts(&self) -> RpcResult<Vec<PredeployedAccountWithBalance>>;

// There is an issue in deserialisation when we try to send the class info directly
// That's why we are sending the components saperately here and then building the
// transaction here in madara and executing the function in the pallet.
#[method(name = "declareV0")]
async fn declare_v0_contract(
apoorvsadana marked this conversation as resolved.
Show resolved Hide resolved
&self,
params: CustomDeclareV0Transaction
) -> RpcResult<DeclareV0Result>;
}

/// Starknet write rpc interface.
Expand Down
1 change: 1 addition & 0 deletions crates/client/rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ sc-network-sync = { workspace = true }
# Starknet
blockifier = { workspace = true }
cairo-vm = { workspace = true }
indexmap = { workspace = true }
tdelabro marked this conversation as resolved.
Show resolved Hide resolved
jsonrpsee = { workspace = true, features = ["server", "macros"] }
log = { workspace = true }
mp-block = { workspace = true }
Expand Down
144 changes: 121 additions & 23 deletions crates/client/rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,18 @@ use std::collections::HashMap;
use std::marker::PhantomData;
use std::sync::Arc;

use blockifier::execution::contract_class::{ClassInfo, ContractClassV0, ContractClassV0Inner};
use blockifier::transaction::account_transaction::AccountTransaction;
use blockifier::transaction::objects::{ResourcesMapping, TransactionExecutionInfo};
use blockifier::transaction::transactions::L1HandlerTransaction;
use blockifier::transaction::transactions::{DeclareTransaction, L1HandlerTransaction};
use cairo_vm::types::program::Program;
use errors::StarknetRpcApiError;
use indexmap::IndexMap;
use jsonrpsee::core::{async_trait, RpcResult};
use log::error;
use mc_genesis_data_provider::GenesisProvider;
pub use mc_rpc_core::utils::*;
use mc_rpc_core::{DeclareV0Result, DeclareTransactionWithV0};
pub use mc_rpc_core::{
Felt, MadaraRpcApiServer, PredeployedAccountWithBalance, StarknetReadRpcApiServer, StarknetTraceRpcApiServer,
StarknetWriteRpcApiServer,
Expand Down Expand Up @@ -50,11 +54,13 @@ use sp_api::ProvideRuntimeApi;
use sp_arithmetic::traits::UniqueSaturatedInto;
use sp_blockchain::HeaderBackend;
use sp_core::H256;
use sp_runtime::codec::DecodeAll;
use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
use sp_runtime::transaction_validity::InvalidTransaction;
use starknet_api::core::Nonce;
use starknet_api::hash::StarkFelt;
use starknet_api::transaction::{Calldata, Fee, TransactionHash, TransactionVersion};
use starknet_api::core::{ClassHash, Nonce};
use starknet_api::deprecated_contract_class::{EntryPoint, EntryPointType};
use starknet_api::hash::{StarkFelt, StarkHash};
use starknet_api::transaction::{Calldata, DeclareTransactionV0V1, Fee, TransactionHash, TransactionVersion};
use starknet_core::types::{
BlockHashAndNumber, BlockId, BlockStatus, BlockTag, BlockWithTxHashes, BlockWithTxs, BroadcastedDeclareTransaction,
BroadcastedDeployAccountTransaction, BroadcastedInvokeTransaction, BroadcastedTransaction, ContractClass,
Expand All @@ -72,7 +78,7 @@ use starknet_core::utils::get_selector_from_name;
use trace_api::get_previous_block_substrate_hash;

use crate::constants::{MAX_EVENTS_CHUNK_SIZE, MAX_EVENTS_KEYS};
use crate::types::RpcEventFilter;
use crate::types::{RpcEventFilter};

/// A Starknet RPC server for Madara
pub struct Starknet<A: ChainApi, B: BlockT, BE, G, C, P, H> {
Expand Down Expand Up @@ -220,9 +226,97 @@ where
}
}

impl<A, B, BE, G, C, P, H> Starknet<A, B, BE, G, C, P, H>
where
A: ChainApi<Block = B> + 'static,
B: BlockT,
P: TransactionPool<Block = B> + 'static,
BE: Backend<B> + 'static,
C: HeaderBackend<B> + BlockBackend<B> + StorageProvider<B, BE> + 'static,
C: ProvideRuntimeApi<B>,
C::Api: StarknetRuntimeApi<B> + ConvertTransactionRuntimeApi<B>,
G: GenesisProvider + Send + Sync + 'static,
H: HasherT + Send + Sync + 'static,
{
async fn declare_txn_common(
&self,
transaction_inputs: DeclareTransactionWithV0,
) -> Result<(TransactionHash, ClassHash), StarknetRpcApiError> {
match transaction_inputs {
tdelabro marked this conversation as resolved.
Show resolved Hide resolved
DeclareTransactionWithV0::V1(transaction) => {
self._declare_tx(transaction).await
}
DeclareTransactionWithV0::V0(declare_txn, program_vec, entrypoints, abi_length) => {
let txn_hash: TransactionHash = TransactionHash(StarkHash { 0: FieldElement::ONE.to_bytes_be() });
tdelabro marked this conversation as resolved.
Show resolved Hide resolved

let program_decoded = Program::decode_all(&mut &*program_vec).unwrap();
tdelabro marked this conversation as resolved.
Show resolved Hide resolved

let class_info;
let declare_transaction;

let try_class_info = ClassInfo::new(
&blockifier::execution::contract_class::ContractClass::V0(ContractClassV0(Arc::from(
ContractClassV0Inner { program: program_decoded, entry_points_by_type: entrypoints },
))),
0,
abi_length,
);

match try_class_info {
Ok(val) => {
class_info = val;
}
Err(_e) => return Err(StarknetRpcApiError::InternalServerError),
tdelabro marked this conversation as resolved.
Show resolved Hide resolved
}

let try_declare_transaction = DeclareTransaction::new(
starknet_api::transaction::DeclareTransaction::V0(declare_txn),
txn_hash,
class_info,
);

match try_declare_transaction {
tdelabro marked this conversation as resolved.
Show resolved Hide resolved
Ok(val) => {
declare_transaction = val;
}
Err(e) => return Err(StarknetRpcApiError::from(e)),
}

self._declare_tx(declare_transaction).await
}
}
}

async fn _declare_tx(&self ,txn: DeclareTransaction) -> Result<(TransactionHash, ClassHash), StarknetRpcApiError> {
let best_block_hash = self.get_best_block_hash();
let current_block_hash = self.get_best_block_hash();
let contract_class = self
.overrides
.for_block_hash(self.client.as_ref(), current_block_hash)
.contract_class_by_class_hash(current_block_hash, txn.class_hash());

if let Some(contract_class) = contract_class {
error!("Contract class already exists: {:?}", contract_class);
return Err(StarknetRpcApiError::ClassAlreadyDeclared.into());
}

let extrinsic = self
.convert_tx_to_extrinsic(best_block_hash, AccountTransaction::Declare(txn.clone()))
.unwrap();

let res = submit_extrinsic(self.pool.clone(), best_block_hash, extrinsic).await;

match res {
Ok(_val) => Ok((txn.tx.compute_hash(Felt252Wrapper::from(self.chain_id().unwrap().0), false), txn.class_hash())),
tdelabro marked this conversation as resolved.
Show resolved Hide resolved
Err(e) => return Err(e),
}
}
}

/// Taken from https://github.com/paritytech/substrate/blob/master/client/rpc/src/author/mod.rs#L78
const TX_SOURCE: TransactionSource = TransactionSource::External;

#[async_trait]
impl<A, B, BE, G, C, P, H> MadaraRpcApiServer for Starknet<A, B, BE, G, C, P, H>
where
A: ChainApi<Block = B> + 'static,
Expand Down Expand Up @@ -262,6 +356,25 @@ where
})
.collect::<Vec<_>>())
}

async fn declare_v0_contract(
&self,
params: mc_rpc_core::CustomDeclareV0Transaction
) -> RpcResult<DeclareV0Result> {
let try_declare = self
.declare_txn_common(DeclareTransactionWithV0::V0(
params.declare_transaction,
params.program_vec,
params.entrypoints,
params.abi_length,
))
.await;

match try_declare {
Ok((txn_hash, class_hash)) => Ok(DeclareV0Result { class_hash, txn_hash }),
Err(e) => Err(e.into()),
}
}
}

#[async_trait]
Expand Down Expand Up @@ -290,8 +403,6 @@ where
&self,
declare_transaction: BroadcastedDeclareTransaction,
) -> RpcResult<DeclareTransactionResult> {
let best_block_hash = self.get_best_block_hash();

let opt_sierra_contract_class = if let BroadcastedDeclareTransaction::V2(ref tx) = declare_transaction {
Some(flattened_sierra_to_sierra_contract_class(tx.contract_class.clone()))
} else {
Expand All @@ -301,25 +412,12 @@ where
let chain_id = Felt252Wrapper(self.chain_id()?.0);

let transaction = try_declare_tx_from_broadcasted_declare_tx(declare_transaction, chain_id).map_err(|e| {
error!("Failed to convert BroadcastedDeclareTransaction to DeclareTransaction, error: {e}");
error!("Failed to convert Broadcasted DeclareTransaction to DeclareTransaction, error: {e}");
StarknetRpcApiError::InternalServerError
})?;
let (class_hash, tx_hash) = (transaction.class_hash(), transaction.tx_hash());

let current_block_hash = self.get_best_block_hash();
let contract_class = self
.overrides
.for_block_hash(self.client.as_ref(), current_block_hash)
.contract_class_by_class_hash(current_block_hash, class_hash);

if let Some(contract_class) = contract_class {
error!("Contract class already exists: {:?}", contract_class);
return Err(StarknetRpcApiError::ClassAlreadyDeclared.into());
}

let extrinsic = self.convert_tx_to_extrinsic(best_block_hash, AccountTransaction::Declare(transaction))?;

submit_extrinsic(self.pool.clone(), best_block_hash, extrinsic).await?;
let (tx_hash, class_hash) =
self.declare_txn_common(mc_rpc_core::DeclareTransactionWithV0::V1(transaction)).await.unwrap();

if let Some(sierra_contract_class) = opt_sierra_contract_class {
if let Some(e) = self.backend.sierra_classes().store_sierra_class(class_hash, sierra_contract_class).err() {
Expand Down
12 changes: 12 additions & 0 deletions crates/client/rpc/src/starknetrpcwrapper.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use std::sync::Arc;

use indexmap::IndexMap;
use jsonrpsee::core::{async_trait, RpcResult};
use mc_genesis_data_provider::GenesisProvider;
use mc_rpc_core::DeclareV0Result;
pub use mc_rpc_core::{
Felt, MadaraRpcApiServer, PredeployedAccountWithBalance, StarknetReadRpcApiServer, StarknetTraceRpcApiServer,
StarknetWriteRpcApiServer,
Expand All @@ -16,6 +18,8 @@ use sc_transaction_pool_api::TransactionPool;
use sp_api::ProvideRuntimeApi;
use sp_blockchain::HeaderBackend;
use sp_runtime::traits::Block as BlockT;
use starknet_api::deprecated_contract_class::{EntryPoint, EntryPointType};
use starknet_api::transaction::DeclareTransactionV0V1;
use starknet_core::types::{
BlockHashAndNumber, BlockId, BroadcastedDeclareTransaction, BroadcastedDeployAccountTransaction,
BroadcastedInvokeTransaction, BroadcastedTransaction, ContractClass, DeclareTransactionResult,
Expand All @@ -36,6 +40,7 @@ impl<A: ChainApi, B: BlockT, BE, G, C, P, H> Clone for StarknetRpcWrapper<A, B,
}
}

#[async_trait]
impl<A, B, BE, G, C, P, H> MadaraRpcApiServer for StarknetRpcWrapper<A, B, BE, G, C, P, H>
where
A: ChainApi<Block = B> + 'static,
Expand All @@ -51,6 +56,13 @@ where
fn predeployed_accounts(&self) -> RpcResult<Vec<PredeployedAccountWithBalance>> {
self.0.predeployed_accounts()
}

async fn declare_v0_contract(
&self,
params: mc_rpc_core::CustomDeclareV0Transaction
) -> RpcResult<DeclareV0Result> {
self.0.declare_v0_contract(params).await
}
}

#[async_trait]
Expand Down
14 changes: 13 additions & 1 deletion crates/client/rpc/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
use std::num::ParseIntError;
use std::{fmt, u64};

use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
use starknet_api::deprecated_contract_class::{EntryPoint, EntryPointType};
use starknet_api::transaction::DeclareTransactionV0V1;
use mp_felt::Felt252Wrapper;
use starknet_ff::FieldElement;
use mc_db::MappingCommitment;

pub struct RpcEventFilter {
pub from_block: u64,
Expand All @@ -25,6 +29,14 @@ pub enum ParseTokenError {
ParseFailed(ParseIntError),
}

#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
pub struct CustomDeclareV0Transaction {
pub declare_transaction: DeclareTransactionV0V1,
pub program_vec: Vec<u8>,
pub entrypoints: IndexMap<EntryPointType, Vec<EntryPoint>>,
pub abi_length: usize
}

impl fmt::Display for ContinuationToken {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:x},{:x}", self.block_n, self.event_n)
Expand Down
16 changes: 11 additions & 5 deletions crates/pallets/starknet/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -581,11 +581,17 @@ pub mod pallet {
!ContractClasses::<T>::contains_key(transaction.tx().class_hash().0),
Error::<T>::ClassHashAlreadyDeclared
);
// Check if contract is deployed
ensure!(
ContractClassHashes::<T>::contains_key(transaction.tx().sender_address()),
Error::<T>::AccountNotDeployed
);

let tx_version = transaction.tx().version();

// Avoiding account checks for V0 declares as they don't need to originate from an existing account
if tx_version != TransactionVersion::ZERO {
// Check if contract is deployed
ensure!(
ContractClassHashes::<T>::contains_key(transaction.tx().sender_address()),
Error::<T>::AccountNotDeployed
);
}
tdelabro marked this conversation as resolved.
Show resolved Hide resolved

let mut state = BlockifierStateAdapter::<T>::default();
let charge_fee = !<T as Config>::DisableTransactionFee::get();
Expand Down
1 change: 1 addition & 0 deletions crates/pallets/starknet/src/transaction_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ impl<T: Config> Pallet<T> {
AccountTransaction::DeployAccount(tx) => tx.contract_address,
AccountTransaction::Invoke(tx) => tx.tx.sender_address(),
};

if address == sender_address
&& account_nonce == Nonce(StarkFelt::ZERO)
&& incoming_tx_nonce == Nonce(StarkFelt::ONE)
Expand Down
Loading