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: add state dump and loading for the sequencer #159

Closed
wants to merge 8 commits into from

Conversation

bajpai244
Copy link
Contributor

Resolves: #119

Pull Request type

Please check the type of change your PR introduces:

  • Bugfix
  • Feature
  • Code style update (formatting, renaming)
  • Refactoring (no functional changes, no API changes)
  • Build-related changes
  • Documentation content changes
  • Testing

What is the new behavior?

This PR adds the ability to dump and load sequencer state, the serialisable types have been borrowed from dojo's katana.

Does this introduce a breaking change?

  • Yes
  • No

@bajpai244 bajpai244 force-pushed the feat/dump-load-seqeuencer branch 2 times, most recently from 469bc32 to 5ca62c5 Compare September 22, 2023 07:56
@bajpai244 bajpai244 marked this pull request as ready for review September 22, 2023 10:07

use super::program::SerializableProgram;

#[derive(Debug, Clone, Serialize, Deserialize)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you annotate which code is taken from katana? for credit but also to know which code is technically reviewed, tested and works in katana currently

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, completed via this commit.

}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SerializableEntryPointOffset(pub usize);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been taken from Katana directly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but can it be changed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is required for the serialization.

fn from(value: SerializableEntryPointV1) -> Self {
blockifier::execution::contract_class::EntryPointV1 {
selector: value.selector,
offset: value.offset.into(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it looks like the only problem is offset!? that sounds cursed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been taken from Katana directly.

@@ -143,6 +170,27 @@ impl BlockifierStateReader for &mut State {
}
}

impl From<&SerializableState> for State {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we implementing the trait on a reference?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So that we want to create a State from SerializableState, we can do so without moving the value and rather than borrowing it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really see the need for this, specially as the implementation itself just copies everything? So in the end we are copying values

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But if you will do it directly on type SerializableState, then this copying will happen twice right?

Firstly you have to call this method on a clone, and then there will another copying inside the implementation. I think the reference approach is hence more optimizied.

Lemme know what you think.

nonces: serializable_state.nonces.clone(),
};

serializable_state.classes.iter().for_each(|(class_hash, contract_class)|{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need to do this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The contract_class is of type SerializableClassRecord, hence we need a conversion from this to ContractClass
{ which is not seriaizable, and hence we have a seriaizable type for it } which is what we store in a normal state of the seqeuncer.

image

serializable_state.classes.iter().for_each(|(class_hash, contract_class)|{
state.classes.insert(
*class_hash,
contract_class.class.clone().try_into().unwrap_or_else(|error| panic!("failed to convert SerializableClassRecord to ContractClass for class_hash {},\n error {}", class_hash, error))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

must we panic here?
can't we implement TryFrom and return a result?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think panic here is fine right? Because this situation is a bug, it means that there is an error in our serialisation for contract class { which is already tested by dojo! }, and from my understanding, panics are reserved for bugs, hence, from our assumption, this should never happen!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should never have a panic! in our code, would make a lot more sense to rely the error upstream if we have one

let contract_class = get_contract_class(include_str!(
"./test_data/contracts/compiled/universal_deployer.json"
));
let compiled_class_hash = CompiledClassHash(stark_felt!("0x1"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you use @greged93 's constants? I think he wrote a similar flow

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the constant.rs file

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can, I have almost all values in there, and we can add if missing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense, done via this commit.


/// This will read a dump from a file and initialize the state from it
pub fn load_state_from_file(path: &PathBuf) -> Result<Self, SerializationError> {
let serilizable_state = SerializableState::load_state(path)?;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let serilizable_state = SerializableState::load_state(path)?;
let serializable_state = SerializableState::load_state(path)?;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved via this commit.

pub fn load_state_from_file(path: &PathBuf) -> Result<Self, SerializationError> {
let serilizable_state = SerializableState::load_state(path)?;

Ok(Self::from(&serilizable_state))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Ok(Self::from(&serilizable_state))
Ok(Self::from(&serializable_state))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved via this commit.


/// Converts `starknet-rs` RPC [FlattenedSierraClass] type to Cairo's
/// [ContractClass](cairo_lang_starknet::contract_class::ContractClass) type.
pub fn rpc_to_cairo_contract_class(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

required in the serialization of contract class, required from a dojo borrowed file for contract serialization.

image

})
}

pub fn get_contract_class(contract_class_str: &str) -> ContractClass {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this doesn't look like a getter but rather an into_contract_class

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also we should avoid get prefix

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been borrowed directly from katana and it is also used from the dojo borrowed file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have made a commit resolving it.

Copy link
Contributor

@greged93 greged93 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple of comments, great work!

# Starknet
# TODO: remove the blockifier patch on the workspace once we can remove Katana.
blockifier = { package = "blockifier", git = "https://github.com/starkware-libs/blockifier.git", tag = "v0.3.0-rc0" }
cairo-lang-starknet = { workspace = true }
cairo-vm = { workspace = true }
serde.workspace = true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you use the same syntax here please? { workspace = true }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved here.


# Other
rustc-hash = "1.1.0"

[dev-dependencies]
lazy_static = { workspace = true }
starknet = { workspace = true }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to depend on it for dev, we have in the crate deps

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved here.

impl TryFrom<SerializableContractClass> for blockifier::execution::contract_class::ContractClass {
type Error = anyhow::Error;

fn try_from(value: SerializableContractClass) -> Result<Self, Self::Error> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you rework this? You are using a TryFrom trait but I don't see any error being thrown and I see an unwrap in the implementation.

Copy link
Contributor Author

@bajpai244 bajpai244 Sep 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@greged93 this whole file is borrowed from Dojo, LamdbaClass said they are working on serialization for these types, if we still need this file in sometime even after merging this, then might be we can refactor this otherwise we will probably not need these serde types in sometime!

use starknet::core::types::contract::SierraClass;

use super::*;
use crate::serde::utils::{get_contract_class, rpc_to_inner_class};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added some cairo 0 and cairo 1 contracts already, can you use these instead? In order to avoid adding more and more test files.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

required by dojo tests, I don't think we should change this, in sometime we should not need it mostly as discussed once we have serialization enabled for ContractClass.


/// TODO: wrap the underlying errors in them
#[derive(Error, Debug)]
pub enum SerializationError {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can rework this a little to use more the syntax from thiserror examples?

#[derive(Error, Debug)]
pub enum MyError {
    Io {
        #[from]
        source: io::Error,
        backtrace: Backtrace,
    },
}

source

}

impl SerializableState {
pub fn dump_state(&self) -> Result<Vec<u8>, SerializationError> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering: should we rename this? This converts the SerializableState into a Vec, so naming it dump_state sounds too specific.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about serialize_state?

Ok(serialized_state_buffer)
}

pub fn load_state(path: &PathBuf) -> Result<Self, SerializationError> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we change this to new? new could take an Option and load the file if Option is not None.

Copy link
Contributor Author

@bajpai244 bajpai244 Sep 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, from_file_path is a better approach, it should be a static method and do one single thing!

We can see this pattern in other rust codebases as well, where there is options like from_str, etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lemme know what you think about it.

let contract_class = get_contract_class(include_str!(
"./test_data/contracts/compiled/universal_deployer.json"
));
let compiled_class_hash = CompiledClassHash(stark_felt!("0x1"));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can, I have almost all values in there, and we can add if missing.

Comment on lines 358 to 386
assert_eq!(
state.classes.get(&class_hash),
loaded_state.classes.get(&class_hash)
);
assert_eq!(
state.compiled_class_hashes.get(&class_hash),
loaded_state.compiled_class_hashes.get(&class_hash)
);
assert_eq!(
state.contracts.get(&contract_address),
loaded_state.contracts.get(&contract_address)
);
assert_eq!(
state.storage.get(&contract_storage_key),
loaded_state.storage.get(&contract_storage_key)
);
assert_eq!(
state.nonces.get(&contract_address),
loaded_state.nonces.get(&contract_address)
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we derive PartialEq and just have one assert_eq!?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great suggestion! Implemented via this commit.

@@ -0,0 +1,2534 @@
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can remove both these contracts and use the ones I have added.

@greged93
Copy link
Contributor

We will fork cairo-vm and blockifier in order to implement serialize on the necessary types.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

feat: add the ability to be able to dump and load sequencer state
3 participants