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 23 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
5 changes: 5 additions & 0 deletions Cargo.lock

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

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

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 +40,32 @@ 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,
}

/// 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(&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
111 changes: 90 additions & 21 deletions crates/client/rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,17 @@ 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 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;
pub use mc_rpc_core::{
Felt, MadaraRpcApiServer, PredeployedAccountWithBalance, StarknetReadRpcApiServer, StarknetTraceRpcApiServer,
StarknetWriteRpcApiServer,
Expand Down Expand Up @@ -50,11 +53,12 @@ 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::core::{ClassHash, Nonce};
use starknet_api::hash::StarkFelt;
use starknet_api::transaction::{Calldata, Fee, TransactionHash, TransactionVersion};
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 Down Expand Up @@ -220,9 +224,55 @@ 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,
{
fn _get_txn_hash(&self, transaction: DeclareTransactionV0V1) -> TransactionHash {
let txn = starknet_api::transaction::DeclareTransaction::V0(transaction);
txn.compute_hash(Felt252Wrapper::from(self.chain_id().unwrap().0), false)
}
tdelabro marked this conversation as resolved.
Show resolved Hide resolved

async fn declare_tx_common(
&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 {
log::debug!("Contract class already exists: {:?}", contract_class);
return Err(StarknetRpcApiError::ClassAlreadyDeclared);
}

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_hash, txn.class_hash())),
Err(e) => 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 +312,41 @@ where
})
.collect::<Vec<_>>())
}

async fn declare_v0_contract(&self, params: mc_rpc_core::CustomDeclareV0Transaction) -> RpcResult<DeclareV0Result> {
let txn_hash: TransactionHash = self._get_txn_hash(params.declare_transaction.clone());

let program_decoded = Program::decode_all(&mut params.program_vec.as_slice()).map_err(|e| {
log::debug!("error: {:?}", e);
StarknetRpcApiError::InternalServerError
})?;

let class_info = ClassInfo::new(
&blockifier::execution::contract_class::ContractClass::V0(ContractClassV0(Arc::from(
ContractClassV0Inner { program: program_decoded, entry_points_by_type: params.entrypoints },
))),
0,
params.abi_length,
)
.map_err(|e| {
log::debug!("error: {:?}", e);
StarknetRpcApiError::InternalServerError
})?;

let declare_transaction = DeclareTransaction::new(
starknet_api::transaction::DeclareTransaction::V0(params.declare_transaction),
txn_hash,
class_info,
)
.map_err(|e| {
log::debug!("error: {:?}", e);
StarknetRpcApiError::InternalServerError
})?;

let (txn_hash, class_hash) = self.declare_tx_common(declare_transaction).await?;

Ok(DeclareV0Result { txn_hash, class_hash })
}
}

#[async_trait]
Expand Down Expand Up @@ -290,8 +375,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 +384,11 @@ 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_tx_common(transaction).await?;

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
6 changes: 6 additions & 0 deletions crates/client/rpc/src/starknetrpcwrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::sync::Arc;

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 Down Expand Up @@ -36,6 +37,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 +53,10 @@ 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
12 changes: 12 additions & 0 deletions crates/client/rpc/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use std::num::ParseIntError;
use std::{fmt, u64};

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

pub struct RpcEventFilter {
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,
}

tdelabro marked this conversation as resolved.
Show resolved Hide resolved
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
Loading