Skip to content
This repository was archived by the owner on Jul 22, 2024. It is now read-only.

Make contract caches shared. #940

Merged
merged 70 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
e634b27
Unify deprecated and casm contract caches.
azteca1998 Aug 23, 2023
63e4bc4
Fix formatting and clippy.
azteca1998 Aug 23, 2023
3efec30
Remove unused code.
azteca1998 Aug 24, 2023
a4afabb
Unify contract classes in the state traits too.
azteca1998 Aug 24, 2023
ae4750b
Fix typos. Make cache shared.
azteca1998 Aug 24, 2023
08506e0
Minor fixes.
azteca1998 Aug 24, 2023
01e0164
Fix testing state generation to make it work with shared caches.
azteca1998 Aug 24, 2023
3ec883b
Fix formatting.
azteca1998 Aug 24, 2023
1847cf1
Add cache diff.
azteca1998 Aug 24, 2023
a8e7c0d
Add suggestion.
azteca1998 Aug 24, 2023
a4e493d
Fix stuff.
azteca1998 Aug 29, 2023
a91efdc
Implement cache trait. Add a null and a permanent cache. Fix everythi…
azteca1998 Aug 30, 2023
3be1d0b
Merge branch 'main' into make-contract-caches-shared
azteca1998 Aug 30, 2023
661a76a
Add documentation.
azteca1998 Aug 31, 2023
c968717
Convert the cache trait into immutable (aka. move the lock into them).
azteca1998 Aug 31, 2023
f10bb1d
Remove external cache lock (no longer needed).
azteca1998 Aug 31, 2023
395f5de
Fix stuff.
azteca1998 Aug 31, 2023
3908f6f
Add LRU cache example.
azteca1998 Aug 31, 2023
73957f4
Run `cargo fmt`.
azteca1998 Aug 31, 2023
7854c71
Merge branch 'main' into make-contract-caches-shared
azteca1998 Aug 31, 2023
5425ca9
Fix LRU example.
azteca1998 Aug 31, 2023
9127d27
Fix the other example.
azteca1998 Aug 31, 2023
3225bec
Merge branch 'main' into make-contract-caches-shared
azteca1998 Sep 6, 2023
adf5672
Fix after merge.
azteca1998 Sep 6, 2023
7071a03
Add private cache drain method.
azteca1998 Sep 6, 2023
e693316
Temporarily disable `max_fee` checks.
azteca1998 Sep 6, 2023
c76073d
Add comment on `RefCell::get_mut()`.
azteca1998 Sep 7, 2023
b14c0bb
Remove `extend` from trait. Remove unused code.
azteca1998 Sep 7, 2023
ec6e516
Fix duplicated `CachedState` issue.
azteca1998 Sep 8, 2023
0b13b49
Add missing comments.
azteca1998 Sep 8, 2023
5ba9720
Merge branch 'main' into make-contract-caches-shared
azteca1998 Sep 8, 2023
abd8bcb
Update `README.md`.
azteca1998 Sep 8, 2023
8f89d0a
Merge branch 'main' into make-contract-caches-shared
azteca1998 Sep 12, 2023
35b5239
Fix after merge.
azteca1998 Sep 12, 2023
fcb114b
Remove obsolete comment.
azteca1998 Sep 12, 2023
204ff74
Fix test after merge.
azteca1998 Sep 12, 2023
dd282e7
Fix borrows.
azteca1998 Sep 12, 2023
ce39605
Merge branch 'main' into make-contract-caches-shared
Oppen Sep 12, 2023
e95eaff
Merge branch 'main' into make-contract-caches-shared
azteca1998 Sep 14, 2023
eae0b0c
Fix after merge.
azteca1998 Sep 14, 2023
3c64761
Fix erc20 test.
azteca1998 Sep 18, 2023
0a6dee1
Remove unused feature.
azteca1998 Sep 18, 2023
8f64391
Update `coverage-helper` to support `#[coverage(off)]`.
azteca1998 Sep 18, 2023
6694d01
Add `coverage` attribute feature on testing.
azteca1998 Sep 18, 2023
91f11b6
Merge branch 'main' into make-contract-caches-shared
azteca1998 Sep 18, 2023
5941f49
Update `README.md` and example.
azteca1998 Sep 19, 2023
13150c8
Fix `README.md`.
azteca1998 Sep 19, 2023
115476a
Merge branch 'main' into make-contract-caches-shared
azteca1998 Sep 19, 2023
dab48c2
Improve `README.md`.
azteca1998 Sep 19, 2023
a30fbb2
Remove references to `StarknetState` in `README.md`.
azteca1998 Sep 19, 2023
f6b550b
Remove debug print.
azteca1998 Sep 19, 2023
01b528d
Remove commented block of code.
azteca1998 Sep 20, 2023
6e4eb3c
Merge branch 'main' into make-contract-caches-shared
azteca1998 Sep 21, 2023
0b85f31
Merge branch 'main' into make-contract-caches-shared
azteca1998 Sep 22, 2023
b287800
Fix after merging.
azteca1998 Sep 22, 2023
57c52f9
Fix formatting.
azteca1998 Sep 22, 2023
8a11259
Update Pr: Make contract caches shared (#1071)
fmoletta Oct 31, 2023
d265c76
Revert "Update Pr: Make contract caches shared (#1071)" (#1116)
Oct 31, 2023
2b9d94f
Update contract caches (#1117)
Nov 13, 2023
967ae8f
Merge
fmoletta Nov 14, 2023
498c4a6
Fix code
fmoletta Nov 14, 2023
d3e3339
clippy
fmoletta Nov 14, 2023
f4b1743
Merge branch 'main' of github.com:lambdaclass/starknet_in_rust into m…
fmoletta Nov 14, 2023
9cf7d7c
Remove old file
fmoletta Nov 14, 2023
1502671
Remove old file
fmoletta Nov 14, 2023
3872008
Avoid cloning private cache
fmoletta Nov 16, 2023
5b17da0
Fix locks
fmoletta Nov 16, 2023
849699b
Uncomment metrics
fmoletta Nov 16, 2023
b784202
Merge branch 'main' of github.com:lambdaclass/starknet_in_rust into m…
fmoletta Nov 16, 2023
d65adff
Merge branch 'main' of github.com:lambdaclass/starknet_in_rust into m…
fmoletta Nov 17, 2023
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
12 changes: 11 additions & 1 deletion Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ tracing = "0.1.37"
[dev-dependencies]
assert_matches = "1.5.0"
coverage-helper = "0.2.0"
lru = "0.11.0"
pretty_assertions_sorted = "1.2.3"
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }

Expand Down
48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,54 @@ You can find an example on how to use the CLI [here](/docs/CLI_USAGE_EXAMPLE.md)

### Customization

#### Contract class cache behavior

`starknet_in_rust` supports caching contracts in memory. Caching the contracts is useful for
avoiding excessive RPC API usage and keeping the contract class deserialization overhead to the
minimum. The project provides two builtin cache policies: null and permanent. The null cache behaves
as if there was no cache at all. The permanent cache caches everything in memory forever.

In addition to those two, an example is provided that implements and uses an LRU cache policy.
Long-running applications should ideally implement a cache algorithm suited to their needs or
alternatively use our example's implementation to avoid spamming the API when using the null cache
or blowing the memory usage when running with the permanent cache.

Customized cache policies may be used by implementing the `ContractClassCache` trait. Check out our
[LRU cache example](examples/lru_cache/main.rs) for more details. Updating the cache requires
manually merging the local state cache into the shared cache manually. This can be done by calling
the `drain_private_contract_class_cache` on the `CachedState` instance.

```rs
// To use the null cache (aka. no cache at all), create the state as follows:
let cache = Arc::new(NullContractClassCache::default());
let state1 = CachedState::new(state_reader.clone(), cache.clone());
let state2 = CachedState::new(state_reader.clone(), cache.clone()); // Cache is reused.

// Insert state usage here.

// The null cache doesn't have any method to extend it since it has no data.
```

```rs
// If the permanent cache is preferred, then use `PermanentContractClassCache` instead:
let cache = Arc::new(PermanentContractClassCache::default());
let state1 = CachedState::new(state_reader.clone(), cache.clone());
let state2 = CachedState::new(state_reader.clone(), cache.clone()); // Cache is reused.

// Insert state usage here.

// Extend the shared cache with the states' contracts after using them.
cache.extend(state1.state.drain_private_contract_class_cache());
cache.extend(state2.state.drain_private_contract_class_cache());
```

#### Logging configuration

This project uses the [`tracing`](https://crates.io/crates/tracing) crate as a library. Check out
its documentation for more information.

### Testing

#### Logging configuration

This project uses the [`tracing`](https://crates.io/crates/tracing) crate as a library. Check out
Expand Down
35 changes: 25 additions & 10 deletions bench/internals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@ use starknet_in_rust::{
services::api::contract_classes::{
compiled_class::CompiledClass, deprecated_contract_class::ContractClass,
},
state::in_memory_state_reader::InMemoryStateReader,
state::{cached_state::CachedState, state_api::State},
state::{
contract_class_cache::PermanentContractClassCache,
in_memory_state_reader::InMemoryStateReader,
},
transaction::{declare::Declare, Deploy, DeployAccount, InvokeFunction},
utils::{Address, ClassHash},
};
use std::{collections::HashMap, hint::black_box, sync::Arc};
use std::{hint::black_box, sync::Arc};

#[cfg(feature = "cairo-native")]
use std::{cell::RefCell, rc::Rc};
Expand Down Expand Up @@ -74,7 +77,10 @@ fn deploy_account(
const RUNS: usize = 500;

let state_reader = Arc::new(InMemoryStateReader::default());
let mut state = CachedState::new(state_reader, HashMap::new());
let mut state = CachedState::new(
state_reader,
Arc::new(PermanentContractClassCache::default()),
);

state
.set_contract_class(
Expand All @@ -86,7 +92,7 @@ fn deploy_account(
let block_context = &Default::default();

for _ in 0..RUNS {
let mut state_copy = state.clone();
let mut state_copy = state.clone_for_testing();
let class_hash = *CLASS_HASH;
let signature = SIGNATURE.clone();
scope(|| {
Expand Down Expand Up @@ -118,12 +124,15 @@ fn declare(#[cfg(feature = "cairo-native")] program_cache: Rc<RefCell<ProgramCac
const RUNS: usize = 5;

let state_reader = Arc::new(InMemoryStateReader::default());
let state = CachedState::new(state_reader, HashMap::new());
let state = CachedState::new(
state_reader,
Arc::new(PermanentContractClassCache::default()),
);

let block_context = &Default::default();

for _ in 0..RUNS {
let mut cloned_state = state.clone();
let mut cloned_state = state.clone_for_testing();
let class = CONTRACT_CLASS.clone();
let address = CONTRACT_ADDRESS.clone();
scope(|| {
Expand Down Expand Up @@ -155,7 +164,10 @@ fn deploy(#[cfg(feature = "cairo-native")] program_cache: Rc<RefCell<ProgramCach
const RUNS: usize = 8;

let state_reader = Arc::new(InMemoryStateReader::default());
let mut state = CachedState::new(state_reader, HashMap::new());
let mut state = CachedState::new(
state_reader,
Arc::new(PermanentContractClassCache::default()),
);

state
.set_contract_class(
Expand All @@ -167,7 +179,7 @@ fn deploy(#[cfg(feature = "cairo-native")] program_cache: Rc<RefCell<ProgramCach
let block_context = &Default::default();

for _ in 0..RUNS {
let mut state_copy = state.clone();
let mut state_copy = state.clone_for_testing();
let salt = felt_str!(
"2669425616857739096022668060305620640217901643963991674344872184515580705509"
);
Expand Down Expand Up @@ -198,7 +210,10 @@ fn invoke(#[cfg(feature = "cairo-native")] program_cache: Rc<RefCell<ProgramCach
const RUNS: usize = 100;

let state_reader = Arc::new(InMemoryStateReader::default());
let mut state = CachedState::new(state_reader, HashMap::new());
let mut state = CachedState::new(
state_reader,
Arc::new(PermanentContractClassCache::default()),
);

state
.set_contract_class(
Expand Down Expand Up @@ -231,7 +246,7 @@ fn invoke(#[cfg(feature = "cairo-native")] program_cache: Rc<RefCell<ProgramCach
.unwrap();

for _ in 0..RUNS {
let mut state_copy = state.clone();
let mut state_copy = state.clone_for_testing();
let address = CONTRACT_ADDRESS.clone();
let selector = VALIDATE_ENTRY_POINT_SELECTOR.clone();
let signature = SIGNATURE.clone();
Expand Down
44 changes: 25 additions & 19 deletions bench/native_bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ use num_traits::Zero;
use starknet_in_rust::definitions::block_context::BlockContext;
use starknet_in_rust::definitions::block_context::StarknetChainId;
use starknet_in_rust::services::api::contract_classes::compiled_class::CompiledClass;
use starknet_in_rust::state::contract_class_cache::ContractClassCache;
use starknet_in_rust::state::contract_class_cache::PermanentContractClassCache;
use starknet_in_rust::state::state_api::State;
use starknet_in_rust::transaction::DeployAccount;
use starknet_in_rust::utils::calculate_sn_keccak;
Expand All @@ -32,7 +34,6 @@ use starknet_in_rust::{
utils::{Address, ClassHash},
};
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use std::sync::Arc;

Expand Down Expand Up @@ -62,7 +63,7 @@ pub fn main() {

fn bench_fibo(executions: usize, native: bool) {
// Create state reader with class hash data
let mut contract_class_cache = HashMap::new();
let contract_class_cache = PermanentContractClassCache::default();
static CASM_CLASS_HASH: ClassHash = ClassHash([2; 32]);

let (contract_class, constructor_selector) = match native {
Expand Down Expand Up @@ -96,7 +97,7 @@ fn bench_fibo(executions: usize, native: bool) {

let caller_address = Address(123456789.into());

contract_class_cache.insert(CASM_CLASS_HASH, contract_class);
contract_class_cache.set_contract_class(CASM_CLASS_HASH, contract_class);
let mut state_reader = InMemoryStateReader::default();
let nonce = Felt252::zero();

Expand All @@ -109,7 +110,7 @@ fn bench_fibo(executions: usize, native: bool) {

// Create state from the state_reader and contract cache.
let state_reader = Arc::new(state_reader);
let state = CachedState::new(state_reader, contract_class_cache);
let state = CachedState::new(state_reader, Arc::new(contract_class_cache));

/* f0, f1, N */
let mut calldata = [1.into(), 1.into(), 2000000.into()];
Expand All @@ -120,7 +121,7 @@ fn bench_fibo(executions: usize, native: bool) {
for _ in 0..executions {
calldata[2] = &calldata[2] + 1usize;
let result = execute(
&mut state.clone(),
&mut state.clone_for_testing(),
&caller_address,
&caller_address,
&Felt252::new(constructor_selector.clone()),
Expand All @@ -136,7 +137,7 @@ fn bench_fibo(executions: usize, native: bool) {

fn bench_fact(executions: usize, native: bool) {
// Create state reader with class hash data
let mut contract_class_cache = HashMap::new();
let contract_class_cache = PermanentContractClassCache::default();
static CASM_CLASS_HASH: ClassHash = ClassHash([2; 32]);

let (contract_class, constructor_selector) = match native {
Expand Down Expand Up @@ -172,7 +173,7 @@ fn bench_fact(executions: usize, native: bool) {
// FACT 1M
// FIBO 2M

contract_class_cache.insert(CASM_CLASS_HASH, contract_class);
contract_class_cache.set_contract_class(CASM_CLASS_HASH, contract_class);
let mut state_reader = InMemoryStateReader::default();
let nonce = Felt252::zero();

Expand All @@ -185,7 +186,7 @@ fn bench_fact(executions: usize, native: bool) {

// Create state from the state_reader and contract cache.
let state_reader = Arc::new(state_reader);
let state = CachedState::new(state_reader, contract_class_cache);
let state = CachedState::new(state_reader, Arc::new(contract_class_cache));

/* N */
let mut calldata = [2000000.into()];
Expand All @@ -196,7 +197,7 @@ fn bench_fact(executions: usize, native: bool) {
for _ in 0..executions {
calldata[0] = &calldata[0] + 1usize;
let result = execute(
&mut state.clone(),
&mut state.clone_for_testing(),
&caller_address,
&caller_address,
&Felt252::new(constructor_selector.clone()),
Expand All @@ -213,7 +214,7 @@ fn bench_fact(executions: usize, native: bool) {
fn bench_erc20(executions: usize, native: bool) {
// 1. setup ERC20 contract and state.
// Create state reader and preload the contract classes.
let mut contract_class_cache = HashMap::new();
let contract_class_cache = PermanentContractClassCache::default();

lazy_static! {
static ref ERC20_CLASS_HASH: ClassHash = ClassHash::from(felt_str!("2"));
Expand All @@ -239,7 +240,10 @@ fn bench_erc20(executions: usize, native: bool) {
}

let program_cache = Rc::new(RefCell::new(ProgramCache::new(get_native_context())));
let (erc20_address, mut state): (Address, CachedState<InMemoryStateReader>) = match native {
let (erc20_address, mut state): (
Address,
CachedState<InMemoryStateReader, PermanentContractClassCache>,
) = match native {
true => {
let erc20_sierra_class = include_bytes!("../starknet_programs/cairo2/erc20.sierra");
let sierra_contract_class: cairo_lang_starknet::contract_class::ContractClass =
Expand All @@ -259,11 +263,11 @@ fn bench_erc20(executions: usize, native: bool) {
let deploy_entrypoint_selector = &entrypoints.external.get(0).unwrap().selector;

// insert deployer and erc20 classes into the cache.
contract_class_cache.insert(
contract_class_cache.set_contract_class(
*DEPLOYER_CLASS_HASH,
CompiledClass::Casm(Arc::new(erc20_deployer_class)),
);
contract_class_cache.insert(*ERC20_CLASS_HASH, erc20_contract_class);
contract_class_cache.set_contract_class(*ERC20_CLASS_HASH, erc20_contract_class);

let mut state_reader = InMemoryStateReader::default();
// setup deployer nonce and address into the state reader
Expand All @@ -275,7 +279,8 @@ fn bench_erc20(executions: usize, native: bool) {
.insert(DEPLOYER_ADDRESS.clone(), Felt252::zero());

// Create state from the state_reader and contract cache.
let mut state = CachedState::new(Arc::new(state_reader), contract_class_cache);
let mut state =
CachedState::new(Arc::new(state_reader), Arc::new(contract_class_cache));

// deploy the erc20 contract by calling the deployer contract.

Expand Down Expand Up @@ -338,11 +343,11 @@ fn bench_erc20(executions: usize, native: bool) {
let deploy_entrypoint_selector = &entrypoints.external.get(0).unwrap().selector;

// insert deployer and erc20 classes into the cache.
contract_class_cache.insert(
contract_class_cache.set_contract_class(
*DEPLOYER_CLASS_HASH,
CompiledClass::Casm(Arc::new(erc20_deployer_class)),
);
contract_class_cache.insert(*ERC20_CLASS_HASH, erc20_contract_class);
contract_class_cache.set_contract_class(*ERC20_CLASS_HASH, erc20_contract_class);

let mut state_reader = InMemoryStateReader::default();
// setup deployer nonce and address into the state reader
Expand All @@ -354,7 +359,8 @@ fn bench_erc20(executions: usize, native: bool) {
.insert(DEPLOYER_ADDRESS.clone(), Felt252::zero());

// Create state from the state_reader and contract cache.
let mut state = CachedState::new(Arc::new(state_reader), contract_class_cache);
let mut state =
CachedState::new(Arc::new(state_reader), Arc::new(contract_class_cache));

// deploy the erc20 contract by calling the deployer contract.

Expand Down Expand Up @@ -493,7 +499,7 @@ fn bench_erc20(executions: usize, native: bool) {

for _ in 0..executions {
let result = execute(
&mut state.clone(),
&mut state.clone_for_testing(),
&account1_address,
&erc20_address,
&transfer_entrypoint_selector.clone(),
Expand All @@ -510,7 +516,7 @@ fn bench_erc20(executions: usize, native: bool) {
#[inline(never)]
#[allow(clippy::too_many_arguments)]
fn execute(
state: &mut CachedState<InMemoryStateReader>,
state: &mut CachedState<InMemoryStateReader, PermanentContractClassCache>,
caller_address: &Address,
callee_address: &Address,
selector: &Felt252,
Expand Down
Loading