Skip to content

Commit

Permalink
Anvil dump and restore
Browse files Browse the repository at this point in the history
  • Loading branch information
xavier-romero committed Feb 3, 2025
1 parent c8e0b48 commit cff5aab
Show file tree
Hide file tree
Showing 11 changed files with 236 additions and 76 deletions.
4 changes: 4 additions & 0 deletions .github/tests/anvil/rollup.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
args:
verbosity: debug
l1_engine: anvil
consensus_contract_type: rollup
42 changes: 42 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -636,3 +636,45 @@ jobs:
with:
name: dump_deploy_to_external_l1_${{ github.run_id }}
path: ./dump

deploy-to-anvil_l1:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# This step will only execute if the necessary secrets are available, preventing failures
# on pull requests from forked repositories.
if: ${{ env.DOCKERHUB_USERNAME && env.DOCKERHUB_TOKEN }}
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Install Kurtosis CDK tools
uses: ./.github/actions/setup-kurtosis-cdk

- name: Deploy stack with anvil
run: kurtosis run --enclave=${{ env.ENCLAVE_NAME }} --args-file=./.github/tests/anvil/rollup.yml .

- name: Monitor CDK chain verified batches (CDK Erigon Permissionless RPC)
working-directory: .github/scripts
run: |
./monitor-cdk-chain.sh \
--enclave ${{ env.ENCLAVE_NAME }} \
--rpc-url "$(kurtosis port print ${{ env.ENCLAVE_NAME }} cdk-erigon-rpc-001 rpc)"
- name: Dump enclave
if: ${{ !cancelled() }}
run: kurtosis enclave dump ${{ env.ENCLAVE_NAME }} ./dump

- name: Upload enclave dump
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v4
with:
name: dump_deploy_to_external_l1_${{ github.run_id }}
path: ./dump
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,11 @@ package-lock.json
package.json

# prod values
*secret*
*secret*

# Files used for existing L1
templates/contract-deploy/combined.json
templates/contract-deploy/dynamic-kurtosis-allocs.json
templates/contract-deploy/dynamic-kurtosis-conf.json
templates/contract-deploy/genesis.json
anvil_state.json
73 changes: 37 additions & 36 deletions anvil.star
Original file line number Diff line number Diff line change
Expand Up @@ -4,53 +4,54 @@ ANVIL_BLOCK_TIME = 1
ANVIL_SLOTS_IN_AN_EPOCH = (
2 # Setting to X leads to block N-(X+1) being finalized, being N latest block
)
STATE_PATH = "/tmp"


def run(plan, args):
chain_id = str(args["l1_chain_id"])
service_files = {}
mnemonic = args.get("l1_preallocated_mnemonic")

cmd = (
"anvil --block-time "
+ str(ANVIL_BLOCK_TIME)
+ " --slots-in-an-epoch "
+ str(ANVIL_SLOTS_IN_AN_EPOCH)
+ " --chain-id "
+ chain_id
+ " --host 0.0.0.0 --port "
+ str(ANVIL_PORT)
+ " --dump-state "
+ STATE_PATH
+ "/state_out.json"
+ " --balance 1000000000"
+ ' --mnemonic "'
+ mnemonic
+ '"'
)

load_state = bool(args.get("anvil_state_file"))

if load_state:
anvil_state = plan.upload_files(
name="anvil-state",
src=args["anvil_state_file"],
description="Uploading Anvil State",
)
service_files = {
STATE_PATH: anvil_state,
}
cmd += " --load-state " + STATE_PATH + "/" + args["anvil_state_file"]

plan.add_service(
name="anvil",
name="anvil" + args["deployment_suffix"],
config=ServiceConfig(
image=ANVIL_IMAGE,
ports={
"rpc": PortSpec(ANVIL_PORT, application_protocol="http"),
},
cmd=[
"anvil --block-time "
+ str(ANVIL_BLOCK_TIME)
+ " --slots-in-an-epoch "
+ str(ANVIL_SLOTS_IN_AN_EPOCH)
+ " --chain-id "
+ chain_id
+ " --host 0.0.0.0 --port "
+ str(ANVIL_PORT)
],
files=service_files,
cmd=[cmd],
),
description="Anvil",
)

mnemonic = args.get("l1_preallocated_mnemonic")
cmd = (
'cast rpc anvil_setBalance $(cast wallet addr --mnemonic "'
+ mnemonic
+ '") 0x33b2e3c9fd0803ce8000000'
)
plan.exec(
description="Funding L1 account",
service_name="anvil",
recipe=ExecRecipe(command=["/bin/sh", "-c", cmd]),
)

# Check balance
plan.exec(
description="Checking L1 account balance",
service_name="anvil",
recipe=ExecRecipe(
command=[
"/bin/sh",
"-c",
'cast balance $(cast wallet addr --mnemonic "' + mnemonic + '")',
]
),
)
53 changes: 30 additions & 23 deletions cdk_erigon.star
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,29 @@ zkevm_prover_package = import_module("./lib/zkevm_prover.star")


def run_sequencer(plan, args, contract_setup_addresses):
# Start the zkevm stateless executor if strict mode is enabled.
if args["erigon_strict_mode"]:
stateless_configs = {}
stateless_configs["stateless_executor"] = True
stateless_executor_config_template = read_file(
src="./templates/trusted-node/prover-config.json"
)
stateless_executor_config_artifact = plan.render_templates(
name="stateless-executor-config-artifact",
config={
"stateless-executor-config.json": struct(
template=stateless_executor_config_template,
data=args | stateless_configs,
)
},
)
zkevm_prover_package.start_stateless_executor(
plan,
args,
stateless_executor_config_artifact,
"zkevm_stateless_executor_start_port",
)

cdk_erigon_config_template = read_file(src="./templates/cdk-erigon/config.yml")
cdk_erigon_sequencer_config_artifact = plan.render_templates(
name="cdk-erigon-sequencer-config-artifact",
Expand All @@ -13,6 +36,7 @@ def run_sequencer(plan, args, contract_setup_addresses):
"zkevm_data_stream_port": args["zkevm_data_streamer_port"],
"is_sequencer": True,
"consensus_contract_type": args["consensus_contract_type"],
"l1_sync_start_block": 1 if args["anvil_state_file"] else 0,
}
| args
| contract_setup_addresses,
Expand Down Expand Up @@ -49,42 +73,24 @@ def run_sequencer(plan, args, contract_setup_addresses):
name="cdk-erigon-chain-first-batch",
)

cdk_erigon_datadir = Directory(
persistent_key="cdk-erigon-datadir" + args["deployment_suffix"],
)

config_artifacts = struct(
config=cdk_erigon_sequencer_config_artifact,
chain_spec=cdk_erigon_chain_spec_artifact,
chain_config=cdk_erigon_chain_config_artifact,
chain_allocs=cdk_erigon_chain_allocs_artifact,
chain_first_batch=cdk_erigon_chain_first_batch_artifact,
datadir=cdk_erigon_datadir,
)
cdk_erigon_package.start_cdk_erigon_sequencer(
plan, args, config_artifacts, "cdk_erigon_sequencer_start_port"
)


def run_rpc(plan, args, contract_setup_addresses):
# Start the zkevm stateless executor if strict mode is enabled.
if args["erigon_strict_mode"]:
stateless_configs = {}
stateless_configs["stateless_executor"] = True
stateless_executor_config_template = read_file(
src="./templates/trusted-node/prover-config.json"
)
stateless_executor_config_artifact = plan.render_templates(
name="stateless-executor-config-artifact",
config={
"stateless-executor-config.json": struct(
template=stateless_executor_config_template,
data=args | stateless_configs,
)
},
)
zkevm_prover_package.start_stateless_executor(
plan,
args,
stateless_executor_config_artifact,
"zkevm_stateless_executor_start_port",
)

zkevm_sequencer_service = plan.get_service(
name=args["sequencer_name"] + args["deployment_suffix"]
)
Expand Down Expand Up @@ -115,6 +121,7 @@ def run_rpc(plan, args, contract_setup_addresses):
"is_sequencer": False,
"pool_manager_url": pool_manager_url,
"consensus_contract_type": args["consensus_contract_type"],
"l1_sync_start_block": 0,
}
| args
| contract_setup_addresses,
Expand Down
5 changes: 1 addition & 4 deletions deploy_zkevm_contracts.star
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,7 @@ def run(plan, args):
artifact_paths = list(ARTIFACTS)
# If we are configured to use a previous deployment, we'll
# dynamically add artifacts for the genesis and combined outputs.
if (
"use_previously_deployed_contracts" in args
and args["use_previously_deployed_contracts"]
):
if args.get("use_previously_deployed_contracts"):
artifact_paths.append(
{
"name": "genesis.json",
Expand Down
91 changes: 91 additions & 0 deletions docs/anvil.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Anvil L1

You can configure the stack to use Anvil as L1 by setting
```
l1_engine: anvil
```

Please, understand that by doing:
- l1_chain_id is taken into account
- l1_rpc_url is automatically set to http://anvil-001:8545"
- l1_ws_url is automatically set to ws://anvil-001:8545
- These params are ignored:
- l1_beacon_url
- l1_additional_services
- l1_preset
- l1_seconds_per_slot
- l1_participants_count


## State dump and recover
By using Anvil as L1, you can dump L1 network state, totally remove the network, and recreate again with the same L1 state.
This procedure has been tested with zkEVM rollup mode, for other scenarios you could need to perform additional steps (like dumping/restoring DAC database) or could be even not supported.

### Procedure
Deploy the network.
```bash
ENCLAVE=cdk
kurtosis run --enclave $ENCLAVE . '{
"args": {
"l1_engine": "anvil",
"consensus_contract_type": "rollup",
}
}'
```

Once deployed, save the required files to recreate again later. These are the files you need:
- ./anvil_state.json
- ./templates/contract-deploy/
- ./templates/contract-deploy/combined.json
- ./templates/contract-deploy/genesis.json
- ./templates/contract-deploy/dynamic-kurtosis-conf.json
- ./templates/contract-deploy/dynamic-kurtosis-allocs.json

Let's get them:

```bash
STATE_FILE=anvil_state.json
DEPLOYMENT_FILES="combined.json genesis.json dynamic-kurtosis-conf.json dynamic-kurtosis-allocs.json"

contracts_uuid=$(kurtosis enclave inspect --full-uuids $ENCLAVE | grep contracts | awk '{ print $1 }')
for file in $DEPLOYMENT_FILES; do
# Save each file on ./templates/contract-deploy, as they are expecte there for use_previously_deployed_contracts=True
docker cp contracts-001--$contracts_uuid:/opt/zkevm/$file ./templates/contract-deploy/$file
done

# Dump Anvilstate (L1)
anvil_uuid=$(kurtosis enclave inspect --full-uuids $ENCLAVE | grep anvil | awk '{ print $1 }')
docker cp anvil--001--$anvil_uuid:/tmp/state_out.json $STATE_FILE
```

At that point you have all you need, you can totally remove the network.
```bash
kurtosis enclave stop $ENCLAVE
kurtosis enclave rm $ENCLAVE
```

To recreate the network, run kurtosis from scratch like this:
```bash
time kurtosis run --enclave $ENCLAVE . '{
"args": {
"anvil_state_file": '$STATE_FILE',
"use_previously_deployed_contracts": true,
"consensus_contract_type": "rollup",
}
}'
```

This will perform the required steps to load the previous state, however, the sequencer needs to recover the state from L1 so it has been set with a specific param, that won't allow it to resume generating new blocks. So you need to manually unlock it:

```bash
# Check cdk-erigon-sequencer logs until you see lines like this before proceeding.
# "L1 block sync recovery has completed!""

# Disable L1 recovery mode
kurtosis service exec $ENCLAVE cdk-erigon-sequencer-001 \
"sed -i 's/zkevm.l1-sync-start-block: 1/zkevm.l1-sync-start-block: 0/' /etc/cdk-erigon/config.yaml"

# Restart sequenccer
kurtosis service stop $ENCLAVE cdk-erigon-sequencer-001
kurtosis service start $ENCLAVE cdk-erigon-sequencer-001
```
8 changes: 6 additions & 2 deletions input_parser.star
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ DEFAULT_L1_ARGS = {
# TODO at some point it would be nice if erigon could recover itself, but this is not going to be easy if there's a DAC
"use_previously_deployed_contracts": False,
"erigon_datadir_archive": None,
"anvil_state_file": None,
}

DEFAULT_L2_ARGS = {
Expand Down Expand Up @@ -391,9 +392,12 @@ def parse_args(plan, args):
op_stack_args = args.get("optimism_package", {})
args = DEFAULT_ARGS | args.get("args", {})

if args["anvil_state_file"] != None:
args["l1_engine"] = "anvil"

if args["l1_engine"] == "anvil":
args["l1_rpc_url"] = "http://anvil:8545"
args["l1_ws_url"] = "ws://anvil:8545"
args["l1_rpc_url"] = "http://anvil" + args["deployment_suffix"] + ":8545"
args["l1_ws_url"] = "ws://anvil" + args["deployment_suffix"] + ":8545"
elif args["l1_engine"] != "geth":
fail(
"Unsupported L1 engine: '{}', please use 'geth' or 'anvil'".format(
Expand Down
Loading

0 comments on commit cff5aab

Please sign in to comment.