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(node): consistent genesis #1016

Merged
merged 40 commits into from
Aug 29, 2024
Merged

feat(node): consistent genesis #1016

merged 40 commits into from
Aug 29, 2024

Conversation

cryptoAtwill
Copy link
Contributor

@cryptoAtwill cryptoAtwill commented May 30, 2024

This PR follows this discussion to address #1002.

The flow is simpler now with:

  • Addition of SealGenesis command that runs the previous genesis creation logic:
    • Load builtin/custom actor and deploy ipc contracts using memory store. Implementation not changed, just shifted location.
    • Write memory store to Car file.
  • InitChain just reads the car file bytes into the StateStore

This PR is just an implementation of the flow, it's a quick way to show if the flow actually works. Pending items to make it ready for review:

  • Integration testing. The current state of the PR might have missing steps (unlikely, but possible), will actually perform integration testing to ensure it actually works.
  • Unit tests.
  • Some further code clean up and simplification.
  • Fix broken CI due to this change.

To actually try it out, perform the following:

# generate the fendermint genesis first, then seal the genesis state
cargo run -p fendermint_app --release -- genesis --genesis-file test-network/genesis.json \
  ipc \
    seal-state \
    --builtin-actors-path <FULL PATH TO YOUR BUILT_IN BUNDLE> \
    --custom-actors-path  <FULL PATH TO YOUR CUSTOM BUNDLE> \
    --artifacts-path  <FULL PATH TO YOUR CONTRACT OUT FOLDER> \
    --output-path <FULL PATH TO YOUR SEALED STATE OUTPUT PATH>

# convert to cometbft genesis
cargo run -p fendermint_app --release -- \
  genesis --genesis-file test-network/genesis.json \
  into-tendermint --out ~/.cometbft/config/genesis.json --sealed  <FULL PATH TO YOUR SEALED STATE OUTPUT PATH>

Copy link
Contributor

@raulk raulk left a comment

Choose a reason for hiding this comment

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

Will do a deeper review tomorrow, but this is headed in the right direction!

In the meantime, can you work on the command to extract the state tree from Fluence's genesis?

@folex can give you an endpoint if you need one.

Comment on lines 156 to 167
/// The built in actors bundle path
#[arg(long, short)]
pub builtin_actors_path: PathBuf,

/// The custom actors bundle path
#[arg(long, short)]
pub custom_actors_path: PathBuf,

/// The ipc artifacts output path. If you are using ipc-monorepo, it should be the `out` folder
/// of `make build`
#[arg(long, short)]
pub ipc_artifacts_path: PathBuf,
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if we can embed these artifacts in the build, and load them from the target directory instead?

Copy link
Contributor

Choose a reason for hiding this comment

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

@cryptoAtwill, any thoughts here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oops, missed this. I think we have discussed this before, if I recall correctly, it's because ipc_contracts can be updated and compile it will result in a different artifacts build. Using a path gives more flexibility on which artifacts should be imported.


/// The sealed, i.e. finalized genesis state CAR file dump path
#[arg(long, short)]
pub sealed_genesis_path: PathBuf,
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's simply call this output_path.

@@ -7,6 +7,7 @@ use anyhow::anyhow;
use fvm_shared::bigint::{BigInt, Integer};
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use std::path::PathBuf;
Copy link
Contributor

Choose a reason for hiding this comment

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

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.

removed alr


/// The output of genesis creation
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FvmGenesisOutput {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can drop Fvm from the name.

@cryptoAtwill cryptoAtwill marked this pull request as ready for review June 5, 2024 07:20
cryptoAtwill and others added 5 commits June 6, 2024 21:20
Co-authored-by: Eric Tu <6364934+ec2@users.noreply.github.com>
Co-authored-by: Mikers <snissn@gmail.com>
Co-authored-by: raulk <raul@protocol.ai>
Co-authored-by: Shashank Trivedi <100513286+lordshashank@users.noreply.github.com>
@cryptoAtwill cryptoAtwill requested a review from a team as a code owner August 5, 2024 12:18
@cryptoAtwill cryptoAtwill changed the title Consistent genesis feat(node): Consistent genesis Aug 5, 2024
@cryptoAtwill cryptoAtwill changed the title feat(node): Consistent genesis feat(node): consistent genesis Aug 5, 2024
@@ -94,6 +95,8 @@ cmd! {
set_ipc_gateway(&genesis_file, args),
GenesisIpcCommands::FromParent(args) =>
new_genesis_from_parent(&genesis_file, args).await,
GenesisIpcCommands::SealState(args) =>
Copy link
Contributor

@karlem karlem Aug 5, 2024

Choose a reason for hiding this comment

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

I wonder if it makes sense to have this under IPC? I understand that it does deploy IPC-related contracts, but it also deploys built-in contracts that might be useful without IPC. Maybe it can be moved as a root genesis command, with FEVM contracts as an optional parameter?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Currently the separation btw ipc and non-ipc is blurred. I would actually argue we track this in another issue, because it's the downstream implementation that assumes an artifacts folder which makes it difficult to split upstream.

}

/// Deploy a library contract with a dynamic ID and no constructor.
pub fn deploy_library(
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 for it to be pub?

}

/// Construct the bytecode of a top-level contract and deploy it with some constructor parameters.
pub fn deploy_contract<T>(
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 for it to be pub?

}

/// Collect Facet Cuts for the diamond pattern, where the facet address comes from already deployed library facets.
pub fn facets(&self, contract_name: &str) -> anyhow::Result<Vec<FacetCut>> {
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 for it to be pub?

.fold(TokenAmount::zero(), |s, a| s + a.balance.clone())
}
//
// #[cfg(test)]
Copy link
Contributor

Choose a reason for hiding this comment

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

We probably want to either re-write the test so that they work with the new implementation or just delete them at least?

Copy link
Contributor Author

@cryptoAtwill cryptoAtwill Aug 7, 2024

Choose a reason for hiding this comment

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

I will remove them first, rewrite them later

/// The custom actors bundle path
custom_actors_path: PathBuf,
/// The CAR path to flush the sealed genesis state
sealed_out_path: PathBuf,
Copy link
Contributor

@karlem karlem Aug 6, 2024

Choose a reason for hiding this comment

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

We don't really mention that it creates sealed genesis anywhere so I guess out_path would do?

@cryptoAtwill
Copy link
Contributor Author

@karlem I have updated based on the review feedback, please take another look. I will remove old implementation in a follow up PR.

Copy link
Contributor

@raulk raulk left a comment

Choose a reason for hiding this comment

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

Initial comments.

@@ -71,8 +71,7 @@ jobs:
id: docker-build
working-directory: ipc/fendermint
run: |
export PATH="$PATH:/home/runner/.config/.foundry/bin"
docker pull ghcr.io/consensus-shipyard/fendermint:latest
export PATH="$PATH:/home/runner/.config/.foundry/bin" && make docker-build
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't this gonna crazy expensive? IIRC we build a Docker image in another job, can't we reuse that Docker image?

args.output_path.clone(),
);

genesis_creator.create(genesis).await
Copy link
Contributor

Choose a reason for hiding this comment

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

These APIs are weird. We have a Genesis and the GenesisCreator. You would imagine the GenesisCreator creates a Genesis, but in reality the Genesis already exists and the GenesisCreator wants it as an input.

I think we need to adjust this a little.

  • We should have a GenesisBuilder.
  • The genesis_file is poorly named. These are genesis parameters, so let's name it genesis_params_path.
  • You can then have a GenesisBuilder::from_params to construct a new GenesisBuilder from the parameters file.
  • We then have a method for each artifact we need to take: with_builtin_actors, with_custom_actors, with_system_contracts.
  • We then have a write_into<W: io::Write>(w: &mut W) method that writes the genesis file to a writer.
  • The file is managed by seal_genesis.

In practice:

let mut output = // create the file

let gen = GenesisBuilder::from_params(genesis_params_path)
    .with_builtin_actors(...)
    .with_custom_actors(...)
    .with_system_contracts(...)
    .write_into(&output)

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 works for me. Just a side note that probably

.with_builtin_actors(...)
.with_custom_actors(...)
.with_system_contracts(...)

should be added to constructor as they are required parameters, otherwise it feels it's not really required.

Comment on lines 229 to +230
--out /cometbft/config/genesis.json \
--app-state /cometbft/config/sealed.json \
Copy link
Contributor

Choose a reason for hiding this comment

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

Why both an out and an app-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.

The app-state is practically the genesis state, but the terminology cometbft is using is app-state, just took that term here.

@@ -0,0 +1,795 @@
// Copyright 2022-2024 Protocol Labs
Copy link
Contributor

Choose a reason for hiding this comment

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

Good question.

/// The solidity artifacts output path. If you are using ipc-monorepo, it should be the `out` folder
/// of `make build`
#[arg(long, short)]
pub artifacts_path: Option<PathBuf>,
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this an Option? It does not add up with the comments here? Are we loading them as embedded resources?

.context("failed to init from genesis")?;

let state_root = state.commit().context("failed to commit genesis state")?;
let (validators, state_params) = read_genesis_car(genesis_bytes, &self.state_store).await?;
Copy link
Contributor

Choose a reason for hiding this comment

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

Hm, ideally we should be able to get validators by calling a getter on the gateway, so that we don't have to carry them separately. But the problem is that the Gateway doesn't store pubkeys; we should extend it for that.

}

match bytes[0] {
0 => {
Copy link
Contributor

Choose a reason for hiding this comment

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

We should make these versions/format identifiers constants somewhere.

pub fn compress_and_encode(&self) -> anyhow::Result<String> {
let bytes = match self {
GenesisAppState::V1(ref bytes) => {
let mut wtr = snap::write::FrameEncoder::new(vec![0]);
Copy link
Contributor

Choose a reason for hiding this comment

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

@cryptoAtwill this looks like a bug? We write the version discriminator (0) under snappy compression, but we expect to find it uncompressed below?

@raulk raulk merged commit dd73ac2 into main Aug 29, 2024
15 checks passed
@raulk raulk deleted the simple-genesis branch August 29, 2024 10:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

3 participants