From 063c83236c5eb28a72cf65b512a710a92ba49d5b Mon Sep 17 00:00:00 2001 From: ethanoroshiba Date: Wed, 26 Feb 2025 12:57:41 -0600 Subject: [PATCH 1/6] Squashed commit of the following: commit ead3a9b17c2fdad18cdf70e765af7814d463f956 Author: ethanoroshiba Date: Mon Feb 10 17:23:16 2025 -0600 make start height the first execution height instead of one before commit d8f2f3fa0eea51e8b00621076d9fde27d592d103 Author: ethanoroshiba Date: Mon Feb 10 16:48:39 2025 -0600 break proto changes into v2 commit dd16dc3e92f99ec58b96ad8aa84166871444d6f5 Author: ethanoroshiba Date: Mon Feb 10 12:30:34 2025 -0600 Fix evm restart test values, some verbiage changes for clarity commit 260e24c534c66fe98dd9a880f0de1d1ec32c930d Author: ethanoroshiba Date: Mon Feb 10 10:20:18 2025 -0600 fix geth genesis commit 3cee39820d04fd2a50a1e253f63fddf3a6429dbc Author: ethanoroshiba Date: Mon Feb 10 10:19:20 2025 -0600 fix values commit 0bfd35fb4e54684e8fc2fba880827757528cdaa3 Author: ethanoroshiba Date: Mon Feb 10 09:14:45 2025 -0600 fix rollup name in evm restart test dev values commit 8ade5174047ed2a7c7ad0d72bec072331e32025d Author: ethanoroshiba Date: Mon Feb 10 08:56:16 2025 -0600 give evm rollup restart test its own just command commit b2af17cc863202994b683734fbd856f3f08a0137 Merge: 1e1668ef 60eefa98 Author: Richard Janis Goldschmidt Date: Mon Feb 10 13:53:08 2025 +0100 Merge branch 'main' into superfluffy/forma-restart-logic commit 1e1668ef9a3ecaa540472c5b419a573ea956bc56 Author: ethanoroshiba Date: Fri Feb 7 13:40:56 2025 -0600 remove configmap values commit 782ee12f41f2c729217f76f899d1e406507cc0d3 Author: ethanoroshiba Date: Fri Feb 7 13:36:55 2025 -0600 fix evm restart test heights, add env vars back to non dev configmap commit f5051d5fd2cbd6e7b08b447229b84314218f3c32 Author: Richard Janis Goldschmidt Date: Tue Feb 4 20:05:27 2025 +0100 remove grpc mock changes after rebasing on main commit 98d04a855eeac16095d0574c80e10e175ee6c5eb Author: Richard Janis Goldschmidt Date: Tue Feb 4 18:16:02 2025 +0100 fix proto formatting commit 373aaa05d725bcf9d9f24a5481173a6d21c4e7d3 Author: Richard Janis Goldschmidt Date: Tue Feb 4 18:09:46 2025 +0100 rename halt_at_stop_height -> halt_at_rollup_stop_height commit 6b9b54ad8ffa5e2039dd57717d3fc78af838b916 Author: ethanoroshiba Date: Tue Feb 4 10:30:37 2025 -0600 bump charts, format core changelog commit 50010e97ca505c24a632819bf79a5b7055512776 Author: Richard Janis Goldschmidt Date: Tue Feb 4 14:43:14 2025 +0100 sequencer_end_height -> sequencer_stop_height commit 0be83abfbb4469215a256f0f54fb0424729eb049 Author: Richard Janis Goldschmidt Date: Tue Feb 4 14:36:50 2025 +0100 updated conductor, core changelogs commit faa42af68ac935fe55850aa8b293bb697a97bf82 Author: Richard Janis Goldschmidt Date: Tue Feb 4 14:32:09 2025 +0100 remove tendermint ID from public API of execution genesis commit 31caab9c73649dbe1cbc4bd7623e1481eceb9847 Author: Richard Janis Goldschmidt Date: Tue Feb 4 14:19:02 2025 +0100 rename last_height -> stop_height commit 7b5b5fcd263cf804dfbaafacadb5b3b7c77f3916 Author: Richard Janis Goldschmidt Date: Tue Feb 4 14:10:19 2025 +0100 bump charts commit 4eb7806bccd5e6f6837853c5ff3251834f896974 Author: Richard Janis Goldschmidt Date: Tue Feb 4 13:56:57 2025 +0100 remove confusing test helper commit c586b3315512cb2d4d32fb3c03591a214fc15394 Author: Richard Janis Goldschmidt Date: Tue Feb 4 13:53:18 2025 +0100 remove conductor_ prefix from tests, remove explicit sleep commit 42ae8646ebc7f00a5577de8e4745de4da70d5a1c Author: ethanoroshiba Date: Thu Jan 30 11:54:10 2025 -0600 bring back executor tests, make test utils commit 66315e7ffa1c4916f2a8ca62aceb56d6d8f95918 Author: ethanoroshiba Date: Thu Jan 30 11:36:14 2025 -0600 base stop height on rollup instead of sequencer commit 4ea3300d266297135639666fda8e4a597cff52f9 Author: ethanoroshiba Date: Tue Jan 28 12:33:39 2025 -0600 fix typos commit c72093124b96ab3a8139f7ebbd89737da07e722a Author: ethanoroshiba Date: Tue Jan 28 12:26:14 2025 -0600 update changelog, example config, test soft-first and firm-first scenarios separately commit add58bcb299d57209298791ae0c55e78078aa57d Author: ethanoroshiba Date: Mon Jan 27 12:29:55 2025 -0600 revert range changes commit d12ed9ad822192dabf467116585c75827cd77575 Author: ethanoroshiba Date: Mon Jan 27 12:21:22 2025 -0600 add necessary charts changes and smoke test, change stop height to be inclusive commit a45206c7a91558afa2d0c9ed7fb7fbcb69dc910d Author: Richard Janis Goldschmidt Date: Wed Jan 22 06:14:49 2025 +0100 feat(conductor): restart or stop at set height Co-authored-by: Ethan Oroshiba --- .github/workflows/docker-build.yml | 32 + charts/deploy.just | 20 + charts/evm-rollup/Chart.yaml | 2 +- .../files/genesis/geth-genesis.json | 102 +- charts/evm-rollup/templates/configmap.yaml | 4 +- charts/evm-rollup/values.yaml | 94 +- charts/evm-stack/values.yaml | 3 - crates/astria-conductor/CHANGELOG.md | 2 + crates/astria-conductor/local.env.example | 6 - crates/astria-conductor/src/block_cache.rs | 4 + .../astria-conductor/src/celestia/builder.rs | 6 - crates/astria-conductor/src/celestia/mod.rs | 31 +- .../astria-conductor/src/conductor/inner.rs | 303 +++++- crates/astria-conductor/src/config.rs | 6 - .../astria-conductor/src/executor/client.rs | 6 +- crates/astria-conductor/src/executor/mod.rs | 276 +++-- crates/astria-conductor/src/executor/state.rs | 344 ++++--- crates/astria-conductor/src/executor/tests.rs | 27 +- crates/astria-conductor/src/lib.rs | 2 + .../src/sequencer/block_stream.rs | 29 +- .../astria-conductor/src/sequencer/builder.rs | 3 - crates/astria-conductor/src/sequencer/mod.rs | 31 +- crates/astria-conductor/src/test_utils.rs | 66 ++ .../tests/blackbox/firm_only.rs | 202 +++- .../tests/blackbox/helpers/macros.rs | 138 ++- .../tests/blackbox/helpers/mock_grpc.rs | 2 +- .../tests/blackbox/helpers/mod.rs | 34 +- .../tests/blackbox/soft_and_firm.rs | 600 ++++++++++- .../tests/blackbox/soft_only.rs | 191 +++- crates/astria-core/src/execution/mod.rs | 1 + crates/astria-core/src/execution/v2/mod.rs | 545 ++++++++++ .../src/generated/astria.bundle.v1alpha1.rs | 762 ++++++++++++++ .../src/generated/astria.execution.v2.rs | 472 +++++++++ .../generated/astria.execution.v2.serde.rs | 954 +++++++++++++++++- dev/values/rollup/dev.yaml | 77 +- dev/values/rollup/evm-restart-test.yaml | 251 +++++ dev/values/rollup/ibc-bridge-test.yaml | 46 +- .../v1alpha1/optimistic_execution.proto | 38 + .../astria/execution/v2/execution.proto | 142 +++ specs/conductor.md | 5 + specs/execution-api.md | 41 +- 41 files changed, 5415 insertions(+), 485 deletions(-) create mode 100644 crates/astria-conductor/src/test_utils.rs create mode 100644 crates/astria-core/src/execution/v2/mod.rs create mode 100644 crates/astria-core/src/generated/astria.bundle.v1alpha1.rs create mode 100644 dev/values/rollup/evm-restart-test.yaml create mode 100644 proto/executionapis/astria/bundle/v1alpha1/optimistic_execution.proto create mode 100644 proto/executionapis/astria/execution/v2/execution.proto diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index fc9e3c4042..61d1400d5d 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -182,6 +182,37 @@ jobs: run: | TAG=sha-$(git rev-parse --short HEAD) just run-smoke-test $TAG + smoke-test-evm-rollup-restart: + needs: [run_checker, composer, conductor, sequencer, sequencer-relayer, evm-bridge-withdrawer, cli] + if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'astriaorg/astria') && (github.event_name == 'merge_group' || needs.run_checker.outputs.run_docker == 'true') + runs-on: buildjet-8vcpu-ubuntu-2204 + steps: + - uses: actions/checkout@v4 + - name: Install just + uses: taiki-e/install-action@just + - name: Install kind + uses: helm/kind-action@v1 + with: + install_only: true + - name: Log in to GHCR + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Setup Smoke Test Environment + timeout-minutes: 10 + run: | + TAG=sha-$(git rev-parse --short HEAD) + just deploy cluster + kubectl create secret generic regcred --from-file=.dockerconfigjson=$HOME/.docker/config.json --type=kubernetes.io/dockerconfigjson + echo -e "\n\nDeploying with astria images tagged $TAG" + just deploy evm-rollup-restart-test $TAG + - name: Run Smoke test + timeout-minutes: 3 + run: | + TAG=sha-$(git rev-parse --short HEAD) + just run-smoke-test $TAG smoke-cli: needs: [run_checker, composer, conductor, sequencer, sequencer-relayer, evm-bridge-withdrawer, cli] @@ -317,3 +348,4 @@ jobs: uses: ./.github/workflows/reusable-success.yml with: success: ${{ !contains(needs.*.result, 'failure') }} + \ No newline at end of file diff --git a/charts/deploy.just b/charts/deploy.just index 797554581b..0403ad61a1 100644 --- a/charts/deploy.just +++ b/charts/deploy.just @@ -211,6 +211,26 @@ deploy-smoke-test tag=defaultTag: --set evm-faucet.enabled=false > /dev/null @just wait-for-rollup > /dev/null +deploy-evm-rollup-restart-test tag=defaultTag: + @echo "Deploying ingress controller..." && just deploy ingress-controller > /dev/null + @just wait-for-ingress-controller > /dev/null + @echo "Deploying local celestia instance..." && just deploy celestia-local > /dev/null + @helm dependency update charts/sequencer > /dev/null + @helm dependency update charts/evm-stack > /dev/null + @echo "Setting up single astria sequencer..." && helm install \ + -n astria-validator-single single-sequencer-chart ./charts/sequencer \ + -f dev/values/validators/all.yml \ + -f dev/values/validators/single.yml \ + {{ if tag != '' { replace('--set images.sequencer.devTag=# --set sequencer-relayer.images.sequencerRelayer.devTag=#', '#', tag) } else { '' } }} \ + --create-namespace > /dev/null + @just wait-for-sequencer > /dev/null + @echo "Starting EVM rollup..." && helm install -n astria-dev-cluster astria-chain-chart ./charts/evm-stack -f dev/values/rollup/evm-restart-test.yaml \ + {{ if tag != '' { replace('--set evm-rollup.images.conductor.devTag=# --set composer.images.composer.devTag=# --set evm-bridge-withdrawer.images.evmBridgeWithdrawer.devTag=#', '#', tag) } else { '' } }} \ + --set blockscout-stack.enabled=false \ + --set postgresql.enabled=false \ + --set evm-faucet.enabled=false > /dev/null + @just wait-for-rollup > /dev/null + deploy-smoke-cli tag=defaultTag: @echo "Deploying ingress controller..." && just deploy ingress-controller > /dev/null @just wait-for-ingress-controller > /dev/null diff --git a/charts/evm-rollup/Chart.yaml b/charts/evm-rollup/Chart.yaml index 404198bebf..8f4274e665 100644 --- a/charts/evm-rollup/Chart.yaml +++ b/charts/evm-rollup/Chart.yaml @@ -21,7 +21,7 @@ version: 1.1.2 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "1.0.0" +appVersion: "1.0.2" maintainers: - name: wafflesvonmaple diff --git a/charts/evm-rollup/files/genesis/geth-genesis.json b/charts/evm-rollup/files/genesis/geth-genesis.json index 116aa9b88c..c77b1c3259 100644 --- a/charts/evm-rollup/files/genesis/geth-genesis.json +++ b/charts/evm-rollup/files/genesis/geth-genesis.json @@ -27,22 +27,98 @@ {{- range $key, $value := .Values.genesis.extra }} "{{ $key }}": {{ toPrettyJson $value | indent 8 | trim }}, {{- end }} - {{- if .Values.genesis.extraDataOverride }} - "astriaExtraDataOverride": "{{ .Values.genesis.extraDataOverride }}", - {{- end }} "astriaOverrideGenesisExtraData": {{ .Values.genesis.overrideGenesisExtraData }}, - "astriaSequencerInitialHeight": {{ toString .Values.genesis.sequencerInitialHeight | replace "\"" "" }}, "astriaRollupName": "{{ tpl .Values.genesis.rollupName . }}", - "astriaCelestiaInitialHeight": {{ toString .Values.genesis.celestiaInitialHeight | replace "\"" "" }}, - "astriaCelestiaHeightVariance": {{ toString .Values.genesis.celestiaHeightVariance | replace "\"" "" }}, - "astriaBridgeAddresses": {{ toPrettyJson .Values.genesis.bridgeAddresses | indent 8 | trim }}, - "astriaFeeCollectors": {{ toPrettyJson .Values.genesis.feeCollectors | indent 8 | trim }}, - "astriaEIP1559Params": {{ toPrettyJson .Values.genesis.eip1559Params | indent 8 | trim }}, - "astriaSequencerAddressPrefix": "{{ .Values.genesis.sequencerAddressPrefix }}" - {{- if not .Values.global.dev }} - {{- else }} - {{- end }} + "astriaForks": { + {{- $forks := .Values.genesis.forks }} + {{- $index := 0 }} + {{- $lastIndex := sub (len $forks) 1 }} + {{- range $key, $value := .Values.genesis.forks }} + "{{ $key }}": { + {{- $fields := list }} + {{- with $value }} + + {{- if .height }} + {{- $fields = append $fields (printf "\"height\": %s" (toString .height | replace "\"" "")) }} + {{- end }} + + {{- if .halt }} + {{- $fields = append $fields (printf "\"halt\": %s" (toString .halt | replace "\"" "")) }} + {{- end }} + + {{- if .snapshotChecksum }} + {{- $fields = append $fields (printf "\"snapshotChecksum\": %s" (toString .snapshotChecksum)) }} + {{- end }} + + {{- if .extraDataOverride }} + {{- $fields = append $fields (printf "\"extraDataOverride\": %s" (toString .extraDataOverride)) }} + {{- end }} + + {{- if .feeCollector }} + {{- $fields = append $fields (printf "\"feeCollector\": \"%s\"" (toString .feeCollector)) }} + {{- end }} + + {{- if .eip1559Params }} + {{- $fields = append $fields (printf "\"eip1559Params\": %s" (toPrettyJson .eip1559Params | indent 8 | trim)) }} + {{- end }} + + {{- if .sequencer }} + {{- $sequencerFields := list }} + + {{- if .sequencer.chainId }} + {{- $sequencerFields = append $sequencerFields (printf "\"chainId\": \"%s\"" (tpl .sequencer.chainId .)) }} + {{- end }} + + {{- if .sequencer.addressPrefix }} + {{- $sequencerFields = append $sequencerFields (printf "\"addressPrefix\": \"%s\"" .sequencer.addressPrefix) }} + {{- end }} + + {{- if .sequencer.startHeight }} + {{- $sequencerFields = append $sequencerFields (printf "\"startHeight\": %s" (toString .sequencer.startHeight | replace "\"" "")) }} + {{- end }} + + {{- if .sequencer.stopHeight }} + {{- $sequencerFields = append $sequencerFields (printf "\"stopHeight\": %s" (toString .sequencer.stopHeight | replace "\"" "")) }} + {{- end }} + + {{- $fields = append $fields (printf "\"sequencer\": {\n%s\n}" (join ",\n" $sequencerFields | indent 4)) }} + {{- end }} + + {{- if .celestia }} + {{- $celestiaFields := list }} + + {{- if .celestia.chainId }} + {{- $celestiaFields = append $celestiaFields (printf "\"chainId\": \"%s\"" (tpl .celestia.chainId .)) }} + {{- end }} + + {{- if .celestia.startHeight }} + {{- $celestiaFields = append $celestiaFields (printf "\"startHeight\": %s" (toString .celestia.startHeight | replace "\"" "")) }} + {{- end }} + + {{- if .celestia.heightVariance }} + {{- $celestiaFields = append $celestiaFields (printf "\"heightVariance\": %s" (toString .celestia.heightVariance | replace "\"" "")) }} + {{- end }} + + {{- if $celestiaFields | len }} + {{- $fields = append $fields (printf "\"celestia\": {\n%s\n}" (join ",\n" $celestiaFields | indent 4)) }} + {{- end }} + {{- end }} + + {{- if .bridgeAddresses }} + {{- $fields = append $fields (printf "\"bridgeAddresses\": %s" (toPrettyJson .bridgeAddresses | indent 4 | trim)) }} + {{- end }} + + {{- join ",\n" $fields | indent 16 }} + } + {{- if ne $index $lastIndex }},{{ end }} + {{- $index = add $index 1 }} + {{- end }} + {{- end }} + } }, + {{- if not .Values.global.dev }} + {{- else }} + {{- end }} "difficulty": "0", "gasLimit": "{{ toString .Values.genesis.gasLimit | replace "\"" "" }}", "alloc": { diff --git a/charts/evm-rollup/templates/configmap.yaml b/charts/evm-rollup/templates/configmap.yaml index f912d50bd0..4ec8e085d2 100644 --- a/charts/evm-rollup/templates/configmap.yaml +++ b/charts/evm-rollup/templates/configmap.yaml @@ -6,14 +6,12 @@ metadata: data: ASTRIA_CONDUCTOR_LOG: "astria_conductor={{ .Values.config.logLevel }}" ASTRIA_CONDUCTOR_CELESTIA_NODE_HTTP_URL: "{{ .Values.config.celestia.rpc }}" - ASTRIA_CONDUCTOR_EXPECTED_CELESTIA_CHAIN_ID: "{{ tpl .Values.config.conductor.celestiaChainId . }}" ASTRIA_CONDUCTOR_CELESTIA_BEARER_TOKEN: "{{ .Values.config.celestia.token }}" ASTRIA_CONDUCTOR_CELESTIA_BLOCK_TIME_MS: "{{ .Values.config.conductor.celestiaBlockTimeMs }}" ASTRIA_CONDUCTOR_EXECUTION_RPC_URL: "http://127.0.0.1:{{ .Values.ports.executionGRPC }}" ASTRIA_CONDUCTOR_EXECUTION_COMMIT_LEVEL: "{{ .Values.config.conductor.executionCommitLevel }}" ASTRIA_CONDUCTOR_SEQUENCER_GRPC_URL: "{{ tpl .Values.config.conductor.sequencerGrpc . }}" ASTRIA_CONDUCTOR_SEQUENCER_COMETBFT_URL: "{{ tpl .Values.config.conductor.sequencerRpc . }}" - ASTRIA_CONDUCTOR_EXPECTED_SEQUENCER_CHAIN_ID: "{{ tpl .Values.config.conductor.sequencerChainId . }}" ASTRIA_CONDUCTOR_SEQUENCER_BLOCK_TIME_MS: "{{ .Values.config.conductor.sequencerBlockTimeMs }}" ASTRIA_CONDUCTOR_NO_METRICS: "{{ not .Values.metrics.enabled }}" ASTRIA_CONDUCTOR_METRICS_HTTP_LISTENER_ADDR: "0.0.0.0:{{ .Values.ports.conductorMetrics }}" @@ -85,4 +83,4 @@ data: {{- end }} --- {{- end }} -{{- end }} \ No newline at end of file +{{- end }} diff --git a/charts/evm-rollup/values.yaml b/charts/evm-rollup/values.yaml index ec9cd7a9c4..5dd32105a9 100644 --- a/charts/evm-rollup/values.yaml +++ b/charts/evm-rollup/values.yaml @@ -11,12 +11,12 @@ images: repo: ghcr.io/astriaorg/astria-geth pullPolicy: IfNotPresent tag: 1.0.0 - devTag: latest + devTag: pr-59 overrideTag: "" conductor: repo: ghcr.io/astriaorg/conductor pullPolicy: IfNotPresent - tag: 1.0.0 + tag: 1.0.1 devTag: latest snapshot: repo: rclone/rclone @@ -24,20 +24,55 @@ images: tag: 1.69.0 genesis: - ## These values are used to configure the genesis block of the rollup chain - ## no defaults as they are unique to each chain - # The name of the rollup chain, used to generate the Rollup ID rollupName: "" - # Block height to start syncing rollup from, lowest possible is 2 - sequencerInitialHeight: "" - # The first Celestia height to utilize when looking for rollup data - celestiaInitialHeight: "" - # The variance in Celestia height to allow before halting the chain - celestiaHeightVariance: "" - # Will fill the extra data in each block, can be left empty - # can also fill with something unique for your chain. - extraDataOverride: "" + + # The "forks" for upgrading the chain. Contains necessary information for starting + # and, if desired, restarting the chain at a given height. The necessary fields + # for the genesis fork are provided, and additional forks can be added as needed. + forks: + launch: + # The rollup number to start executing blocks at, lowest possible is 1 + height: 1 + # Whether to halt the rollup chain at the given height + halt: "false" + # Checksum of the snapshot to use upon restart + snapshotChecksum: "" + # Will fill the extra data in each block, can be left empty + # can also fill with something unique for your chain. + extraDataOverride: "" + # Configure the fee collector for the evm tx fees, activated at block heights. + # If not configured, all tx fees will be burned. + feeCollector: "" + # 1: "0xaC21B97d35Bf75A7dAb16f35b111a50e78A72F30" + # Configure EIP-1559 params, activated at block heights. + eip1559Params: {} + # 1: + # minBaseFee: 0 + # elasticityMultiplier: 2 + # baseFeeChangeDenominator: 8 + sequencer: + # The chain id of the sequencer chain + chainId: "" + # The hrp for bech32m addresses, unlikely to be changed + addressPrefix: "astria" + # Block height to start syncing rollup from (inclusive), lowest possible is 2 + startHeight: "" + celestia: + # The chain id of the celestia chain + chainId: "" + # The first Celestia height to utilize when looking for rollup data + startHeight: "" + # The variance in Celestia height to allow before halting the chain + heightVariance: "" + # Configure the sequencer bridge addresses and allowed assets if using + # the astria canonical bridge. Recommend removing alloc values if so. + bridgeAddresses: [] + # - address: "684ae50c49a434199199c9c698115391152d7b3f" + # startHeight: 1 + # assetDenom: "nria" + # senderAddress: "0x0000000000000000000000000000000000000000" + # assetPrecision: 9 ## These are general configuration values with some recommended defaults @@ -45,34 +80,7 @@ genesis: gasLimit: "50000000" # If set to true the genesis block will contain extra data overrideGenesisExtraData: true - # The hrp for bech32m addresses, unlikely to be changed - sequencerAddressPrefix: "astria" - - ## These values are used to configure astria native bridging - ## Many of the fields have commented out example fields - - # Configure the sequencer bridge addresses and allowed assets if using - # the astria canonical bridge. Recommend removing alloc values if so. - bridgeAddresses: [] - # - address: "684ae50c49a434199199c9c698115391152d7b3f" - # startHeight: 1 - # assetDenom: "nria" - # senderAddress: "0x0000000000000000000000000000000000000000" - # assetPrecision: 9 - - - ## Fee configuration - # Configure the fee collector for the evm tx fees, activated at block heights. - # If not configured, all tx fees will be burned. - feeCollectors: {} - # 1: "0xaC21B97d35Bf75A7dAb16f35b111a50e78A72F30" - # Configure EIP-1559 params, activated at block heights - eip1559Params: {} - # 1: - # minBaseFee: 0 - # elasticityMultiplier: 2 - # baseFeeChangeDenominator: 8 ## Standard Eth Genesis config values # An EVM chain number id, different from the astria rollup name @@ -191,8 +199,6 @@ config: # - "FirmOnly" -> blocks are only pulled from DA # - "SoftAndFirm" -> blocks are pulled from both the sequencer and DA executionCommitLevel: 'SoftAndFirm' - # The chain id of the Astria sequencer chain conductor communicates with - sequencerChainId: "" # The expected fastest block time possible from sequencer, determines polling # rate. sequencerBlockTimeMs: 2000 @@ -204,8 +210,6 @@ config: sequencerGrpc: "" # The maximum number of requests to make to the sequencer per second sequencerRequestsPerSecond: 500 - # The chain id of the celestia network the conductor communicates with - celestiaChainId: "" celestia: # if config.rollup.executionLevel is NOT 'SoftOnly' AND celestia-node is not enabled diff --git a/charts/evm-stack/values.yaml b/charts/evm-stack/values.yaml index 18ad6e4350..770211aedd 100644 --- a/charts/evm-stack/values.yaml +++ b/charts/evm-stack/values.yaml @@ -13,7 +13,6 @@ global: rollupName: "" evmChainId: "" sequencerChainId: "" - celestiaChainId: "" otel: endpoint: "" tracesEndpoint: "" @@ -29,8 +28,6 @@ evm-rollup: chainId: "{{ .Values.global.evmChainId }}" config: conductor: - sequencerChainId: "{{ .Values.global.sequencerChainId }}" - celestiaChainId: "{{ .Values.global.celestiaChainId }}" sequencerRpc: "{{ .Values.global.sequencerRpc }}" sequencerGrpc: "{{ .Values.global.sequencerGrpc }}" otel: diff --git a/crates/astria-conductor/CHANGELOG.md b/crates/astria-conductor/CHANGELOG.md index 2210b7b33e..6e9294a3b2 100644 --- a/crates/astria-conductor/CHANGELOG.md +++ b/crates/astria-conductor/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Update `idna` dependency to resolve cargo audit warning [#1869](https://github.com/astriaorg/astria/pull/1869). - Remove panic source on shutdown [#1919](https://github.com/astriaorg/astria/pull/1919). +- Add stop height logic, remove chain id env vars, accomodate new genesis info +shape [#1928](https://github.com/astriaorg/astria/pull/1928). ## [1.0.0] - 2024-10-25 diff --git a/crates/astria-conductor/local.env.example b/crates/astria-conductor/local.env.example index 9a1eb3e43d..6237d3109b 100644 --- a/crates/astria-conductor/local.env.example +++ b/crates/astria-conductor/local.env.example @@ -73,12 +73,6 @@ ASTRIA_CONDUCTOR_SEQUENCER_BLOCK_TIME_MS=2000 # CometBFT node. ASTRIA_CONDUCTOR_SEQUENCER_REQUESTS_PER_SECOND=500 -# The chain ID of the sequencer network the conductor should be communicating with. -ASTRIA_CONDUCTOR_EXPECTED_SEQUENCER_CHAIN_ID="test-sequencer-1000" - -# The chain ID of the Celestia network the conductor should be communicating with. -ASTRIA_CONDUCTOR_EXPECTED_CELESTIA_CHAIN_ID="test-celestia-1000" - # Set to true to enable prometheus metrics. ASTRIA_CONDUCTOR_NO_METRICS=true diff --git a/crates/astria-conductor/src/block_cache.rs b/crates/astria-conductor/src/block_cache.rs index 799b31eb2d..3b3307d929 100644 --- a/crates/astria-conductor/src/block_cache.rs +++ b/crates/astria-conductor/src/block_cache.rs @@ -74,6 +74,10 @@ impl BlockCache { cache: self, } } + + pub(crate) fn next_height_to_pop(&self) -> u64 { + self.next_height + } } impl BlockCache { diff --git a/crates/astria-conductor/src/celestia/builder.rs b/crates/astria-conductor/src/celestia/builder.rs index eb03d9440c..aa200ab6d3 100644 --- a/crates/astria-conductor/src/celestia/builder.rs +++ b/crates/astria-conductor/src/celestia/builder.rs @@ -27,8 +27,6 @@ pub(crate) struct Builder { pub(crate) rollup_state: StateReceiver, pub(crate) sequencer_cometbft_client: SequencerClient, pub(crate) sequencer_requests_per_second: u32, - pub(crate) expected_celestia_chain_id: String, - pub(crate) expected_sequencer_chain_id: String, pub(crate) shutdown: CancellationToken, pub(crate) metrics: &'static Metrics, } @@ -42,8 +40,6 @@ impl Builder { celestia_token, sequencer_cometbft_client, sequencer_requests_per_second, - expected_celestia_chain_id, - expected_sequencer_chain_id, shutdown, metrics, firm_blocks, @@ -60,8 +56,6 @@ impl Builder { rollup_state, sequencer_cometbft_client, sequencer_requests_per_second, - expected_celestia_chain_id, - expected_sequencer_chain_id, shutdown, metrics, }) diff --git a/crates/astria-conductor/src/celestia/mod.rs b/crates/astria-conductor/src/celestia/mod.rs index 07b1633b96..3b76c19c5b 100644 --- a/crates/astria-conductor/src/celestia/mod.rs +++ b/crates/astria-conductor/src/celestia/mod.rs @@ -144,12 +144,6 @@ pub(crate) struct Reader { /// (usually to verify block data retrieved from Celestia blobs). sequencer_requests_per_second: u32, - /// The chain ID of the Celestia network the reader should be communicating with. - expected_celestia_chain_id: String, - - /// The chain ID of the Sequencer the reader should be communicating with. - expected_sequencer_chain_id: String, - /// Token to listen for Conductor being shut down. shutdown: CancellationToken, @@ -179,13 +173,13 @@ impl Reader { #[instrument(skip_all, err)] async fn initialize(&mut self) -> eyre::Result { + let expected_celestia_chain_id = self.rollup_state.celestia_chain_id(); let validate_celestia_chain_id = async { let actual_celestia_chain_id = get_celestia_chain_id(&self.celestia_client) .await .wrap_err("failed to fetch Celestia chain ID")?; - let expected_celestia_chain_id = &self.expected_celestia_chain_id; ensure!( - self.expected_celestia_chain_id == actual_celestia_chain_id.as_str(), + expected_celestia_chain_id == actual_celestia_chain_id.as_str(), "expected Celestia chain id `{expected_celestia_chain_id}` does not match actual: \ `{actual_celestia_chain_id}`" ); @@ -193,14 +187,14 @@ impl Reader { } .in_current_span(); + let expected_sequencer_chain_id = self.rollup_state.sequencer_chain_id(); let get_and_validate_sequencer_chain_id = async { let actual_sequencer_chain_id = get_sequencer_chain_id(self.sequencer_cometbft_client.clone()) .await .wrap_err("failed to get sequencer chain ID")?; - let expected_sequencer_chain_id = &self.expected_sequencer_chain_id; ensure!( - self.expected_sequencer_chain_id == actual_sequencer_chain_id.to_string(), + expected_sequencer_chain_id == actual_sequencer_chain_id.as_str(), "expected Celestia chain id `{expected_sequencer_chain_id}` does not match \ actual: `{actual_sequencer_chain_id}`" ); @@ -384,6 +378,10 @@ impl RunningReader { }); let reason = loop { + if self.has_reached_stop_height()? { + break Ok("stop height reached"); + } + self.schedule_new_blobs(); select!( @@ -449,6 +447,19 @@ impl RunningReader { } } + /// The stop height is reached if a) the next height to be forwarded would be greater + /// than the stop height, and b) there is no block currently in flight. + fn has_reached_stop_height(&self) -> eyre::Result { + Ok(self + .rollup_state + .sequencer_stop_height() + .wrap_err("failed to obtain sequencer stop height")? + .map_or(false, |height| { + self.block_cache.next_height_to_pop() > height.get() + && self.enqueued_block.is_terminated() + })) + } + #[instrument(skip_all)] fn cache_reconstructed_blocks(&mut self, reconstructed: ReconstructedBlocks) { for block in reconstructed.blocks { diff --git a/crates/astria-conductor/src/conductor/inner.rs b/crates/astria-conductor/src/conductor/inner.rs index 7f964a7bac..ba0cca42c8 100644 --- a/crates/astria-conductor/src/conductor/inner.rs +++ b/crates/astria-conductor/src/conductor/inner.rs @@ -28,7 +28,7 @@ use crate::{ /// Exit value of the inner conductor impl to signal to the outer task whether to restart or /// shutdown -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] pub(super) enum RestartOrShutdown { Restart, Shutdown, @@ -44,14 +44,14 @@ impl std::fmt::Display for RestartOrShutdown { } } -struct ShutdownSignalReceived; - /// The business logic of Conductur. pub(super) struct Inner { /// Token to signal to all tasks to shut down gracefully. shutdown_token: CancellationToken, - executor: Option>>, + config: Config, + + executor: Option>>>, } impl Inner { @@ -67,7 +67,7 @@ impl Inner { shutdown_token: CancellationToken, ) -> eyre::Result { let executor = executor::Builder { - config, + config: config.clone(), shutdown: shutdown_token.clone(), metrics, } @@ -76,6 +76,7 @@ impl Inner { Ok(Self { shutdown_token, + config, executor: Some(tokio::spawn(executor.run_until_stopped())), }) } @@ -87,25 +88,25 @@ impl Inner { pub(super) async fn run_until_stopped(mut self) -> eyre::Result { info_span!("Conductor::run_until_stopped").in_scope(|| info!("conductor is running")); - let exit_reason = select! { + let exit_status = select! { biased; () = self.shutdown_token.cancelled() => { - Ok(ShutdownSignalReceived) + Ok(None) }, res = self.executor.as_mut().expect("task must always be set at this point") => { // XXX: must Option::take the JoinHandle to avoid polling it in the shutdown logic. self.executor.take(); match res { - Ok(Ok(())) => Err(eyre!("executor exited unexpectedly")), + Ok(Ok(state)) => Ok(state), Ok(Err(err)) => Err(err.wrap_err("executor exited with error")), Err(err) => Err(Report::new(err).wrap_err("executor panicked")), } } }; - self.restart_or_shutdown(exit_reason).await + self.restart_or_shutdown(exit_status).await } /// Shuts down all tasks. @@ -116,20 +117,30 @@ impl Inner { #[instrument(skip_all, err, ret(Display))] async fn restart_or_shutdown( mut self, - exit_reason: eyre::Result, + exit_status: eyre::Result>, ) -> eyre::Result { - self.shutdown_token.cancel(); - let restart_or_shutdown = match exit_reason { - Ok(ShutdownSignalReceived) => Ok(RestartOrShutdown::Shutdown), - Err(error) => { - error!(%error, "executor failed; checking error chain if conductor should be restarted"); - if check_for_restart(&error) { - Ok(RestartOrShutdown::Restart) - } else { - Err(error) + let restart_or_shutdown = 'decide_restart: { + if self.shutdown_token.is_cancelled() { + break 'decide_restart Ok(RestartOrShutdown::Shutdown); + } + + match exit_status { + Ok(None) => Err(eyre!( + "executor exited with a success value but without rollup status even though \ + it was not explicitly cancelled; this shouldn't happen" + )), + Ok(Some(status)) => should_restart_or_shutdown(&self.config, &status), + Err(error) => { + error!(%error, "executor failed; checking error chain if conductor should be restarted"); + if should_restart_despite_error(&error) { + Ok(RestartOrShutdown::Restart) + } else { + Err(error) + } } } }; + self.shutdown_token.cancel(); if let Some(mut executor) = self.executor.take() { let wait_until_timeout = Duration::from_secs(25); @@ -149,7 +160,7 @@ impl Inner { } #[instrument(skip_all)] -fn check_for_restart(err: &eyre::Report) -> bool { +fn should_restart_despite_error(err: &eyre::Report) -> bool { let mut current = Some(err.as_ref() as &dyn std::error::Error); while let Some(err) = current { if let Some(status) = err.downcast_ref::() { @@ -162,17 +173,265 @@ fn check_for_restart(err: &eyre::Report) -> bool { false } +fn should_restart_or_shutdown( + config: &Config, + status: &crate::executor::State, +) -> eyre::Result { + let Some(rollup_stop_block_number) = status.rollup_stop_block_number() else { + return Err(eyre!( + "executor exited with a success value even though it was not configured to run with a \ + stop height and even though it received no shutdown signal; this should not happen" + )); + }; + + match config.execution_commit_level { + crate::config::CommitLevel::FirmOnly | crate::config::CommitLevel::SoftAndFirm => { + if status.has_firm_number_reached_stop_height() { + let restart_or_shutdown = if status.halt_at_rollup_stop_number() { + RestartOrShutdown::Shutdown + } else { + RestartOrShutdown::Restart + }; + Ok(restart_or_shutdown) + } else { + Err(eyre!( + "executor exited with a success value, but the stop height was not reached + (execution kind: `{}`, firm rollup block number: `{}`, mapped to sequencer \ + height: `{}`, rollup start height: `{}`, sequencer start height: `{}`, \ + sequencer stop height: `{}`)", + config.execution_commit_level, + status.firm_number(), + status.firm_block_number_as_sequencer_height(), + status.rollup_start_block_number(), + status.sequencer_start_height(), + rollup_stop_block_number, + )) + } + } + crate::config::CommitLevel::SoftOnly => { + if status.has_soft_number_reached_stop_height() { + let restart_or_shutdown = if status.halt_at_rollup_stop_number() { + RestartOrShutdown::Shutdown + } else { + RestartOrShutdown::Restart + }; + Ok(restart_or_shutdown) + } else { + Err(eyre!( + "executor exited with a success value, but the stop height was not reached + (execution kind: `{}`, soft rollup block number: `{}`, mapped to sequencer \ + height: `{}`, rollup start height: `{}`, sequencer start height: `{}`, \ + sequencer stop height: `{}`)", + config.execution_commit_level, + status.soft_number(), + status.soft_block_number_as_sequencer_height(), + status.rollup_start_block_number(), + status.sequencer_start_height(), + rollup_stop_block_number, + )) + } + } + } +} + #[cfg(test)] mod tests { + use astria_core::generated::astria::execution::v2::{ + Block, + CommitmentState, + GenesisInfo, + }; use astria_eyre::eyre::WrapErr as _; + use pbjson_types::Timestamp; + + use super::{ + executor::State, + RestartOrShutdown, + }; + use crate::{ + config::CommitLevel, + test_utils::{ + make_commitment_state, + make_genesis_info, + make_rollup_state, + }, + Config, + }; + + fn make_config() -> crate::Config { + crate::Config { + celestia_block_time_ms: 0, + celestia_node_http_url: String::new(), + no_celestia_auth: false, + celestia_bearer_token: String::new(), + sequencer_grpc_url: String::new(), + sequencer_cometbft_url: String::new(), + sequencer_block_time_ms: 0, + sequencer_requests_per_second: 0, + execution_rpc_url: String::new(), + log: String::new(), + execution_commit_level: CommitLevel::SoftAndFirm, + force_stdout: false, + no_otel: false, + no_metrics: false, + metrics_http_listener_addr: String::new(), + pretty_print: false, + } + } #[test] - fn check_for_restart_ok() { + fn should_restart_despite_error() { let tonic_error: Result<&str, tonic::Status> = Err(tonic::Status::new(tonic::Code::PermissionDenied, "error")); let err = tonic_error.wrap_err("wrapper_1"); let err = err.wrap_err("wrapper_2"); let err = err.wrap_err("wrapper_3"); - assert!(super::check_for_restart(&err.unwrap_err())); + assert!(super::should_restart_despite_error(&err.unwrap_err())); + } + + #[track_caller] + fn assert_restart_or_shutdown( + config: &Config, + state: &State, + restart_or_shutdown: &RestartOrShutdown, + ) { + assert_eq!( + &super::should_restart_or_shutdown(config, state).unwrap(), + restart_or_shutdown, + ); + } + + #[test] + fn restart_or_shutdown_on_firm_height_reached() { + assert_restart_or_shutdown( + &Config { + execution_commit_level: CommitLevel::SoftAndFirm, + ..make_config() + }, + &make_rollup_state( + GenesisInfo { + sequencer_start_height: 10, + rollup_start_block_number: 10, + rollup_stop_block_number: 99, + halt_at_rollup_stop_number: false, + ..make_genesis_info() + }, + CommitmentState { + firm: Some(Block { + number: 99, + hash: vec![0u8; 32].into(), + parent_block_hash: vec![].into(), + timestamp: Some(Timestamp::default()), + }), + soft: Some(Block { + number: 99, + hash: vec![0u8; 32].into(), + parent_block_hash: vec![].into(), + timestamp: Some(Timestamp::default()), + }), + ..make_commitment_state() + }, + ), + &RestartOrShutdown::Restart, + ); + + assert_restart_or_shutdown( + &Config { + execution_commit_level: CommitLevel::SoftAndFirm, + ..make_config() + }, + &make_rollup_state( + GenesisInfo { + sequencer_start_height: 10, + rollup_start_block_number: 10, + rollup_stop_block_number: 99, + halt_at_rollup_stop_number: true, + ..make_genesis_info() + }, + CommitmentState { + firm: Some(Block { + number: 99, + hash: vec![0u8; 32].into(), + parent_block_hash: vec![].into(), + timestamp: Some(Timestamp::default()), + }), + soft: Some(Block { + number: 99, + hash: vec![0u8; 32].into(), + parent_block_hash: vec![].into(), + timestamp: Some(Timestamp::default()), + }), + ..make_commitment_state() + }, + ), + &RestartOrShutdown::Shutdown, + ); + } + + #[test] + fn restart_or_shutdown_on_soft_height_reached() { + assert_restart_or_shutdown( + &Config { + execution_commit_level: CommitLevel::SoftOnly, + ..make_config() + }, + &make_rollup_state( + GenesisInfo { + sequencer_start_height: 10, + rollup_start_block_number: 10, + rollup_stop_block_number: 99, + halt_at_rollup_stop_number: false, + ..make_genesis_info() + }, + CommitmentState { + firm: Some(Block { + number: 99, + hash: vec![0u8; 32].into(), + parent_block_hash: vec![].into(), + timestamp: Some(Timestamp::default()), + }), + soft: Some(Block { + number: 99, + hash: vec![0u8; 32].into(), + parent_block_hash: vec![].into(), + timestamp: Some(Timestamp::default()), + }), + ..make_commitment_state() + }, + ), + &RestartOrShutdown::Restart, + ); + + assert_restart_or_shutdown( + &Config { + execution_commit_level: CommitLevel::SoftOnly, + ..make_config() + }, + &make_rollup_state( + GenesisInfo { + sequencer_start_height: 10, + rollup_start_block_number: 10, + rollup_stop_block_number: 99, + halt_at_rollup_stop_number: true, + ..make_genesis_info() + }, + CommitmentState { + firm: Some(Block { + number: 99, + hash: vec![0u8; 32].into(), + parent_block_hash: vec![].into(), + timestamp: Some(Timestamp::default()), + }), + soft: Some(Block { + number: 99, + hash: vec![0u8; 32].into(), + parent_block_hash: vec![].into(), + timestamp: Some(Timestamp::default()), + }), + ..make_commitment_state() + }, + ), + &RestartOrShutdown::Shutdown, + ); } } diff --git a/crates/astria-conductor/src/config.rs b/crates/astria-conductor/src/config.rs index cd48c4d1e0..974d65ae48 100644 --- a/crates/astria-conductor/src/config.rs +++ b/crates/astria-conductor/src/config.rs @@ -63,12 +63,6 @@ pub struct Config { /// The number of requests per second that will be sent to Sequencer. pub sequencer_requests_per_second: u32, - /// The chain ID of the sequencer network the conductor should be communiacting with. - pub expected_sequencer_chain_id: String, - - /// The chain ID of the Celestia network the conductor should be communicating with. - pub expected_celestia_chain_id: String, - /// Address of the RPC server for execution pub execution_rpc_url: String, diff --git a/crates/astria-conductor/src/executor/client.rs b/crates/astria-conductor/src/executor/client.rs index 1bab377d97..fd0bf6d9e2 100644 --- a/crates/astria-conductor/src/executor/client.rs +++ b/crates/astria-conductor/src/executor/client.rs @@ -1,15 +1,15 @@ use std::time::Duration; use astria_core::{ - execution::v1::{ + execution::v2::{ Block, CommitmentState, GenesisInfo, }, generated::astria::{ execution::{ - v1 as raw, - v1::execution_service_client::ExecutionServiceClient, + v2 as raw, + v2::execution_service_client::ExecutionServiceClient, }, sequencerblock::v1::RollupData, }, diff --git a/crates/astria-conductor/src/executor/mod.rs b/crates/astria-conductor/src/executor/mod.rs index ad475db85f..bcfb6dd669 100644 --- a/crates/astria-conductor/src/executor/mod.rs +++ b/crates/astria-conductor/src/executor/mod.rs @@ -4,7 +4,7 @@ use std::{ }; use astria_core::{ - execution::v1::{ + execution::v2::{ Block, CommitmentState, }, @@ -44,6 +44,7 @@ use tracing::{ debug_span, error, info, + info_span, instrument, warn, }; @@ -62,10 +63,11 @@ mod client; mod state; #[cfg(test)] mod tests; - pub(super) use client::Client; -use state::State; -pub(crate) use state::StateReceiver; +pub(crate) use state::{ + State, + StateReceiver, +}; use self::state::StateSender; @@ -83,17 +85,32 @@ pub(crate) struct Executor { metrics: &'static Metrics, } -impl Executor { - const CELESTIA: &'static str = "celestia"; - const SEQUENCER: &'static str = "sequencer"; +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +enum ReaderKind { + Firm, + Soft, +} + +impl std::fmt::Display for ReaderKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let msg = match self { + ReaderKind::Firm => "firm celestia reader", + ReaderKind::Soft => "soft sequencer reader", + }; + f.write_str(msg) + } +} - pub(crate) async fn run_until_stopped(self) -> eyre::Result<()> { +impl Executor { + pub(crate) async fn run_until_stopped(self) -> eyre::Result> { let initialized = select!( + biased; + () = self.shutdown.clone().cancelled_owned() => { - return report_exit(Ok( - "received shutdown signal while initializing task; \ - aborting intialization and exiting" - ), ""); + info_span!("shutdown signal on init").in_scope(|| { + info!("received shutdown signal while initializing executor; cancelling initialization"); + }); + return Ok(None); } res = self.init() => { res.wrap_err("initialization failed")? @@ -136,14 +153,12 @@ impl Executor { rollup_state: state.subscribe(), sequencer_cometbft_client: sequencer_cometbft_client.clone(), sequencer_requests_per_second: self.config.sequencer_requests_per_second, - expected_celestia_chain_id: self.config.expected_celestia_chain_id.clone(), - expected_sequencer_chain_id: self.config.expected_sequencer_chain_id.clone(), shutdown: reader_cancellation_token.child_token(), metrics: self.metrics, } .build() .wrap_err("failed to build Celestia Reader")?; - reader_tasks.spawn(Self::CELESTIA, reader.run_until_stopped()); + reader_tasks.spawn(ReaderKind::Firm, reader.run_until_stopped()); } if self.config.is_with_soft() { @@ -155,13 +170,12 @@ impl Executor { sequencer_grpc_client, sequencer_cometbft_client: sequencer_cometbft_client.clone(), sequencer_block_time: Duration::from_millis(self.config.sequencer_block_time_ms), - expected_sequencer_chain_id: self.config.expected_sequencer_chain_id.clone(), shutdown: reader_cancellation_token.child_token(), soft_blocks: soft_blocks_tx, rollup_state: state.subscribe(), } .build(); - reader_tasks.spawn(Self::SEQUENCER, sequencer_reader.run_until_stopped()); + reader_tasks.spawn(ReaderKind::Soft, sequencer_reader.run_until_stopped()); }; Ok(Initialized { @@ -178,7 +192,7 @@ impl Executor { }) } - #[instrument(skip_all, err)] + #[instrument(skip_all, err, ret(Display))] async fn create_initial_node_state(&self) -> eyre::Result { let genesis_info = { async { @@ -201,22 +215,21 @@ impl Executor { let (genesis_info, commitment_state) = tokio::try_join!(genesis_info, commitment_state)?; let (state, _) = state::channel( - State::try_from_genesis_info_and_commitment_state(genesis_info, commitment_state) - .wrap_err( - "failed to construct initial state gensis and commitment info received from \ - rollup", - )?, + State::try_from_genesis_info_and_commitment_state( + genesis_info, + commitment_state, + self.config.execution_commit_level, + ) + .wrap_err( + "failed to construct initial state gensis and commitment info received from rollup", + )?, ); self.metrics .absolute_set_executed_firm_block_number(state.firm_number()); self.metrics .absolute_set_executed_soft_block_number(state.soft_number()); - info!( - initial_state = serde_json::to_string(&*state.get()) - .expect("writing json to a string should not fail"), - "received genesis info from rollup", - ); + Ok(state) } } @@ -250,14 +263,14 @@ struct Initialized { metrics: &'static Metrics, /// The tasks reading block data off Celestia or Sequencer. - reader_tasks: JoinMap<&'static str, eyre::Result<()>>, + reader_tasks: JoinMap>, /// The cancellation token specifically for signaling the `reader_tasks` to shut down. reader_cancellation_token: CancellationToken, } impl Initialized { - async fn run(mut self) -> eyre::Result<()> { + async fn run(mut self) -> eyre::Result> { let reason = select!( biased; @@ -285,9 +298,7 @@ impl Initialized { block.hash = %block.block_hash(), "received block from celestia reader", )); - if let Err(error) = self.execute_firm(block).await { - break Err(error).wrap_err("failed executing firm block"); - } + self.execute_firm(block).await.wrap_err("failed executing firm block")?; } Some(block) = self.soft_blocks.recv(), if !self.is_spread_too_large() => @@ -297,13 +308,11 @@ impl Initialized { block.hash = %block.block_hash(), "received block from sequencer reader", )); - if let Err(error) = self.execute_soft(block).await { - break Err(error).wrap_err("failed executing soft block"); - } + self.execute_soft(block).await.wrap_err("failed executing soft block")?; } Some((task, res)) = self.reader_tasks.join_next() => { - break handle_task_exit(task, res); + self.handle_task_exit(task, res)?; } else => break Ok("all channels are closed") @@ -365,16 +374,21 @@ impl Initialized { std::cmp::Ordering::Equal => {} } - let genesis_height = self.state.sequencer_genesis_block_height(); - let block_height = executable_block.height; - let Some(block_number) = - state::map_sequencer_height_to_rollup_height(genesis_height, block_height) - else { + let sequencer_start_height = self.state.sequencer_start_height(); + let rollup_start_block_number = self.state.rollup_start_block_number(); + let current_block_height = executable_block.height; + let Some(block_number) = state::map_sequencer_height_to_rollup_height( + sequencer_start_height, + rollup_start_block_number, + current_block_height, + ) else { bail!( "failed to map block height rollup number. This means the operation - `sequencer_height - sequencer_genesis_height` underflowed or was not a valid - cometbft height. Sequencer height: `{block_height}`, sequencer genesis height: \ - `{genesis_height}`", + `sequencer_height - sequencer_start_height + rollup_start_block_number` \ + underflowed or was not a valid cometbft height. Sequencer height: \ + `{current_block_height}`, + sequencer start height: `{sequencer_start_height}`, + rollup start height: `{rollup_start_block_number}`" ) }; @@ -418,15 +432,20 @@ impl Initialized { "expected block at sequencer height {expected_height}, but got {block_height}", ); - let genesis_height = self.state.sequencer_genesis_block_height(); - let Some(block_number) = - state::map_sequencer_height_to_rollup_height(genesis_height, block_height) - else { + let sequencer_start_height = self.state.sequencer_start_height(); + let rollup_start_block_number = self.state.rollup_start_block_number(); + let Some(block_number) = state::map_sequencer_height_to_rollup_height( + sequencer_start_height, + rollup_start_block_number, + block_height, + ) else { bail!( "failed to map block height rollup number. This means the operation - `sequencer_height - sequencer_genesis_height` underflowed or was not a valid - cometbft height. Sequencer height: `{block_height}`, sequencer genesis height: \ - `{genesis_height}`", + `sequencer_height - sequencer_start_height + rollup_start_block_number` \ + underflowed or was not a valid cometbft height. Sequencer block height: \ + `{block_height}`, + sequencer start height: `{sequencer_start_height}`, + rollup start height: `{rollup_start_block_number}`" ) }; @@ -528,14 +547,27 @@ impl Initialized { OnlySoft, ToSame, }; - let (firm, soft, celestia_height) = match update { - OnlyFirm(firm, celestia_height) => (firm, self.state.soft(), celestia_height), + + use crate::config::CommitLevel; + let (firm, soft, celestia_height, commit_level) = match update { + OnlyFirm(firm, celestia_height) => ( + firm, + self.state.soft(), + celestia_height, + CommitLevel::FirmOnly, + ), OnlySoft(soft) => ( self.state.firm(), soft, self.state.celestia_base_block_height(), + CommitLevel::SoftOnly, + ), + ToSame(block, celestia_height) => ( + block.clone(), + block, + celestia_height, + CommitLevel::SoftAndFirm, ), - ToSame(block, celestia_height) => (block.clone(), block, celestia_height), }; let commitment_state = CommitmentState::builder() .firm(firm) @@ -556,7 +588,7 @@ impl Initialized { "updated commitment state", ); self.state - .try_update_commitment_state(new_state) + .try_update_commitment_state(new_state, commit_level) .wrap_err("failed updating internal state tracking rollup state; invalid?")?; Ok(()) } @@ -583,50 +615,106 @@ impl Initialized { } #[instrument(skip_all, err)] - async fn shutdown(mut self, reason: eyre::Result<&'static str>) -> eyre::Result<()> { - info!("signaling all reader tasks to exit"); - self.reader_cancellation_token.cancel(); - while let Some((task, exit_status)) = self.reader_tasks.join_next().await { - match crate::utils::flatten(exit_status) { - Ok(()) => info!(task, "task exited"), - Err(error) => warn!(task, %error, "task exited with error"), + fn handle_task_exit( + &mut self, + task: ReaderKind, + res: Result, JoinError>, + ) -> eyre::Result<()> { + match task { + ReaderKind::Firm if self.config.is_with_firm() => { + match ( + self.state.rollup_stop_block_number().is_some(), + self.state.has_firm_number_reached_stop_height(), + ) { + (true, true) => { + info!( + "firm number has reached stop height; signalling all readers to stop \ + and closing channels" + ); + self.reader_cancellation_token.cancel(); + self.firm_blocks.close(); + self.soft_blocks.close(); + Ok(()) + } + + (true, false) => match res { + Ok(Ok(())) => Err(eyre!("task exited with sucess value")), + Ok(Err(err)) => Err(err).wrap_err("task exited with error"), + Err(err) => Err(err).wrap_err("task panicked"), + } + .wrap_err_with(|| { + format!("task `{task}` exited unexpectedly before stop height was reached") + }), + + // fall-through case, no stop height was configured + (false, _) => match res { + Ok(Ok(())) => Err(eyre!("task exited with sucess value")), + Ok(Err(err)) => Err(err).wrap_err("task exited with error"), + Err(err) => Err(err).wrap_err("task panicked"), + } + .wrap_err_with(|| format!("task `{task}` exited unexpectedly")), + } } - } - report_exit(reason, "shutting down") - } -} -/// Wraps a task result to explain why it exited. -/// -/// Right now only the err-branch is populated because tasks should -/// never exit. Still returns an `eyre::Result` to line up with the -/// return type of [`Executor::run_until_stopped`]. -/// -/// Executor should `break handle_task_exit` immediately after calling -/// this method. -fn handle_task_exit( - task: &'static str, - res: Result, JoinError>, -) -> eyre::Result<&'static str> { - match res { - Ok(Ok(())) => Err(eyre!("task `{task}` finished unexpectedly")), - Ok(Err(err)) => Err(err).wrap_err_with(|| format!("task `{task}` exited with error")), - Err(err) => Err(err).wrap_err_with(|| format!("task `{task}` panicked")), + ReaderKind::Soft if self.config.is_with_soft() => { + match ( + self.state.rollup_stop_block_number().is_some(), + self.state.has_soft_number_reached_stop_height(), + ) { + (true, true) => { + info!("soft number has reached stop height"); + Ok(()) + } + (true, false) => match res { + Ok(Ok(())) => Err(eyre!("task exited with sucess value")), + Ok(Err(err)) => Err(err).wrap_err("task exited with error"), + Err(err) => Err(err).wrap_err("task panicked"), + } + .wrap_err_with(|| { + format!("task `{task}` exited unexpectedly before stop height was reached") + }), + + (false, _) => match res { + Ok(Ok(())) => Err(eyre!("task exited with sucess value")), + Ok(Err(err)) => Err(err).wrap_err("task exited with error"), + Err(err) => Err(err).wrap_err("task panicked"), + } + .wrap_err_with(|| format!("task `{task}` exited unexpectedly")), + } + } + + ReaderKind::Firm | ReaderKind::Soft => Err(eyre!( + "task `{task}` exited but it shouldn't have run in the first place because \ + because commit level is set to `{}`", + self.config.execution_commit_level + )), + } } -} -#[instrument(skip_all)] -fn report_exit(reason: eyre::Result<&str>, message: &str) -> eyre::Result<()> { - // XXX: explicitly setting the message (usually implicitly set by tracing) - match reason { - Ok(reason) => { - info!(%reason, message); - Ok(()) + #[instrument(skip_all, err)] + async fn shutdown(mut self, reason: eyre::Result<&'static str>) -> eyre::Result> { + let message = "shutting down"; + match &reason { + Ok(reason) => { + info!(%reason, message); + } + Err(reason) => { + error!(%reason, message); + } } - Err(error) => { - error!(%error, message); - Err(error) + + info!("signaling all reader tasks to exit, closing all channels"); + self.reader_cancellation_token.cancel(); + self.firm_blocks.close(); + self.soft_blocks.close(); + while let Some((task, exit_status)) = self.reader_tasks.join_next().await { + match crate::utils::flatten(exit_status) { + Ok(()) => info!(%task, "task exited"), + Err(error) => warn!(%task, %error, "task exited"), + } } + + reason.map(|_| (Some(self.state.get().clone()))) } } diff --git a/crates/astria-conductor/src/executor/state.rs b/crates/astria-conductor/src/executor/state.rs index 1f315b078b..37fc136122 100644 --- a/crates/astria-conductor/src/executor/state.rs +++ b/crates/astria-conductor/src/executor/state.rs @@ -2,14 +2,21 @@ //! the other methods can be used. Otherwise, they will panic. //! //! The inner state must not be unset after having been set. +use std::num::NonZeroU64; + use astria_core::{ - execution::v1::{ + execution::v2::{ Block, CommitmentState, GenesisInfo, }, primitive::v1::RollupId, }; +use astria_eyre::eyre::{ + self, + eyre, + WrapErr as _, +}; use bytes::Bytes; use sequencer_client::tendermint::block::Height as SequencerHeight; use tokio::sync::watch::{ @@ -31,13 +38,15 @@ pub(super) fn channel(state: State) -> (StateSender, StateReceiver) { #[derive(Debug, thiserror::Error)] #[error( - "adding sequencer genesis height `{sequencer_genesis_height}` and `{commitment_type}` rollup \ - number `{rollup_number}` overflowed unsigned u32::MAX, the maximum permissible cometbft \ - height" + "could not map rollup number to sequencer height for commitment type `{commitment_type}`: the \ + operation `{sequencer_start_height} + ({rollup_number} - {rollup_start_block_number})` \ + failed because `{issue}`" )] -pub(super) struct InvalidState { +pub(crate) struct InvalidState { commitment_type: &'static str, - sequencer_genesis_height: u64, + issue: &'static str, + sequencer_start_height: u64, + rollup_start_block_number: u64, rollup_number: u64, } @@ -74,44 +83,83 @@ impl StateReceiver { self.inner.changed().await?; Ok(self.next_expected_soft_sequencer_height()) } + + pub(crate) fn sequencer_stop_height(&self) -> eyre::Result> { + let Some(rollup_stop_block_number) = self.inner.borrow().rollup_stop_block_number() else { + return Ok(None); + }; + let sequencer_start_height = self.inner.borrow().sequencer_start_height(); + let rollup_start_block_number = self.inner.borrow().rollup_start_block_number(); + Ok(NonZeroU64::new( + map_rollup_number_to_sequencer_height( + sequencer_start_height, + rollup_start_block_number, + rollup_stop_block_number + .get() + .try_into() + .wrap_err("rollup stop block number overflows u32::MAX")?, + ) + .map_err(|e| { + eyre!(e).wrap_err("failed to map rollup stop block number to sequencer height") + })? + .into(), + )) + } } pub(super) struct StateSender { inner: watch::Sender, } -fn can_map_firm_to_sequencer_height( +impl std::fmt::Display for StateSender { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let s = serde_json::to_string(&*self.inner.borrow()).unwrap(); + f.write_str(&s) + } +} + +fn map_firm_to_sequencer_height( genesis_info: &GenesisInfo, commitment_state: &CommitmentState, -) -> Result<(), InvalidState> { - let sequencer_genesis_height = genesis_info.sequencer_genesis_block_height(); +) -> Result { + let sequencer_start_height = genesis_info.sequencer_start_height(); + let rollup_start_block_number = genesis_info.rollup_start_block_number(); let rollup_number = commitment_state.firm().number(); - if map_rollup_number_to_sequencer_height(sequencer_genesis_height, rollup_number).is_none() { - Err(InvalidState { - commitment_type: "firm", - sequencer_genesis_height: sequencer_genesis_height.value(), - rollup_number: rollup_number.into(), - }) - } else { - Ok(()) - } + + map_rollup_number_to_sequencer_height( + sequencer_start_height, + rollup_start_block_number, + rollup_number, + ) + .map_err(|issue| InvalidState { + commitment_type: "firm", + issue, + sequencer_start_height, + rollup_start_block_number, + rollup_number: rollup_number.into(), + }) } -fn can_map_soft_to_sequencer_height( +fn map_soft_to_sequencer_height( genesis_info: &GenesisInfo, commitment_state: &CommitmentState, -) -> Result<(), InvalidState> { - let sequencer_genesis_height = genesis_info.sequencer_genesis_block_height(); +) -> Result { + let sequencer_start_height = genesis_info.sequencer_start_height(); + let rollup_start_block_number = genesis_info.rollup_start_block_number(); let rollup_number = commitment_state.soft().number(); - if map_rollup_number_to_sequencer_height(sequencer_genesis_height, rollup_number).is_none() { - Err(InvalidState { - commitment_type: "soft", - sequencer_genesis_height: sequencer_genesis_height.value(), - rollup_number: rollup_number.into(), - }) - } else { - Ok(()) - } + + map_rollup_number_to_sequencer_height( + sequencer_start_height, + rollup_start_block_number, + rollup_number, + ) + .map_err(|issue| InvalidState { + commitment_type: "soft", + issue, + sequencer_start_height, + rollup_start_block_number, + rollup_number: rollup_number.into(), + }) } impl StateSender { @@ -143,10 +191,15 @@ impl StateSender { pub(super) fn try_update_commitment_state( &mut self, commitment_state: CommitmentState, + commit_level: crate::config::CommitLevel, ) -> Result<(), InvalidState> { let genesis_info = self.genesis_info(); - can_map_firm_to_sequencer_height(&genesis_info, &commitment_state)?; - can_map_soft_to_sequencer_height(&genesis_info, &commitment_state)?; + if commit_level.is_with_firm() { + let _ = map_firm_to_sequencer_height(&genesis_info, &commitment_state)?; + } + if commit_level.is_with_soft() { + let _ = map_soft_to_sequencer_height(&genesis_info, &commitment_state)?; + } self.inner.send_modify(move |state| { state.set_commitment_state(commitment_state); }); @@ -204,8 +257,12 @@ forward_impls!( [soft_hash -> Bytes], [celestia_block_variance -> u64], [rollup_id -> RollupId], - [sequencer_genesis_block_height -> SequencerHeight], + [sequencer_start_height -> u64], [celestia_base_block_height -> u64], + [rollup_start_block_number -> u64], + [rollup_stop_block_number -> Option], + [has_firm_number_reached_stop_height -> bool], + [has_soft_number_reached_stop_height -> bool], ); forward_impls!( @@ -213,35 +270,58 @@ forward_impls!( [celestia_base_block_height -> u64], [celestia_block_variance -> u64], [rollup_id -> RollupId], + [sequencer_chain_id -> String], + [celestia_chain_id -> String], ); /// `State` tracks the genesis info and commitment state of the remote rollup node. -#[derive(Debug, serde::Serialize)] -pub(super) struct State { +#[derive(Clone, Debug, serde::Serialize)] +pub(crate) struct State { commitment_state: CommitmentState, genesis_info: GenesisInfo, } impl State { - pub(super) fn try_from_genesis_info_and_commitment_state( + pub(crate) fn try_from_genesis_info_and_commitment_state( genesis_info: GenesisInfo, commitment_state: CommitmentState, + commit_level: crate::config::CommitLevel, ) -> Result { - can_map_firm_to_sequencer_height(&genesis_info, &commitment_state)?; - can_map_soft_to_sequencer_height(&genesis_info, &commitment_state)?; + if commit_level.is_with_firm() { + let _ = map_firm_to_sequencer_height(&genesis_info, &commitment_state)?; + } + if commit_level.is_with_soft() { + let _ = map_soft_to_sequencer_height(&genesis_info, &commitment_state)?; + } Ok(State { commitment_state, genesis_info, }) } + /// Returns if the tracked firm state of the rollup has reached the rollup stop block number. + pub(crate) fn has_firm_number_reached_stop_height(&self) -> bool { + let Some(rollup_stop_block_number) = self.rollup_stop_block_number() else { + return false; + }; + u64::from(self.commitment_state.firm().number()) >= rollup_stop_block_number.get() + } + + /// Returns if the tracked soft state of the rollup has reached the rollup stop block number. + pub(crate) fn has_soft_number_reached_stop_height(&self) -> bool { + let Some(rollup_stop_block_number) = self.rollup_stop_block_number() else { + return false; + }; + u64::from(self.commitment_state.soft().number()) >= rollup_stop_block_number.get() + } + /// Sets the inner commitment state. fn set_commitment_state(&mut self, commitment_state: CommitmentState) { self.commitment_state = commitment_state; } - fn genesis_info(&self) -> GenesisInfo { - self.genesis_info + fn genesis_info(&self) -> &GenesisInfo { + &self.genesis_info } fn firm(&self) -> &Block { @@ -252,11 +332,11 @@ impl State { self.commitment_state.soft() } - fn firm_number(&self) -> u32 { + pub(crate) fn firm_number(&self) -> u32 { self.commitment_state.firm().number() } - fn soft_number(&self) -> u32 { + pub(crate) fn soft_number(&self) -> u32 { self.commitment_state.soft().number() } @@ -276,124 +356,121 @@ impl State { self.genesis_info.celestia_block_variance() } - fn sequencer_genesis_block_height(&self) -> SequencerHeight { - self.genesis_info.sequencer_genesis_block_height() + pub(crate) fn sequencer_start_height(&self) -> u64 { + self.genesis_info.sequencer_start_height() + } + + pub(crate) fn halt_at_rollup_stop_number(&self) -> bool { + self.genesis_info.halt_at_rollup_stop_number() + } + + fn sequencer_chain_id(&self) -> String { + self.genesis_info.sequencer_chain_id().to_string() + } + + fn celestia_chain_id(&self) -> String { + self.genesis_info.celestia_chain_id().to_string() } fn rollup_id(&self) -> RollupId { self.genesis_info.rollup_id() } - fn next_expected_firm_sequencer_height(&self) -> Option { - map_rollup_number_to_sequencer_height( - self.sequencer_genesis_block_height(), - self.firm_number().saturating_add(1), + pub(crate) fn rollup_start_block_number(&self) -> u64 { + self.genesis_info.rollup_start_block_number() + } + + pub(crate) fn rollup_stop_block_number(&self) -> Option { + self.genesis_info.rollup_stop_block_number() + } + + pub(crate) fn firm_block_number_as_sequencer_height(&self) -> SequencerHeight { + map_firm_to_sequencer_height(&self.genesis_info, &self.commitment_state).expect( + "state must only contain numbers that can be mapped to sequencer heights; this is \ + enforced by its constructor and/or setter", ) } - fn next_expected_soft_sequencer_height(&self) -> Option { - map_rollup_number_to_sequencer_height( - self.sequencer_genesis_block_height(), - self.soft_number().saturating_add(1), + pub(crate) fn soft_block_number_as_sequencer_height(&self) -> SequencerHeight { + map_soft_to_sequencer_height(&self.genesis_info, &self.commitment_state).expect( + "state must only contain numbers that can be mapped to sequencer heights; this is \ + enforced by its constructor and/or setter", ) } + + fn next_expected_firm_sequencer_height(&self) -> Result { + map_firm_to_sequencer_height(&self.genesis_info, &self.commitment_state) + .map(SequencerHeight::increment) + } + + fn next_expected_soft_sequencer_height(&self) -> Result { + map_soft_to_sequencer_height(&self.genesis_info, &self.commitment_state) + .map(SequencerHeight::increment) + } } /// Maps a rollup height to a sequencer height. /// -/// Returns `None` if `sequencer_genesis_height + rollup_number` overflows -/// `u32::MAX`. +/// Returns error if `sequencer_start_height + (rollup_number - rollup_start_block_number)` +/// is out of range of `u32` or if `rollup_start_block_number` is more than 1 greater than +/// `rollup_number`. fn map_rollup_number_to_sequencer_height( - sequencer_genesis_height: SequencerHeight, + sequencer_start_height: u64, + rollup_start_block_number: u64, rollup_number: u32, -) -> Option { - let sequencer_genesis_height = sequencer_genesis_height.value(); - let rollup_number: u64 = rollup_number.into(); - let sequencer_height = sequencer_genesis_height.checked_add(rollup_number)?; - sequencer_height.try_into().ok() +) -> Result { + let rollup_number = u64::from(rollup_number); + if rollup_start_block_number > (rollup_number.checked_add(1).ok_or("overflows u64::MAX")?) { + return Err("rollup start height exceeds rollup number + 1"); + } + let sequencer_height = sequencer_start_height + .checked_add(rollup_number) + .ok_or("overflows u64::MAX")? + .checked_sub(rollup_start_block_number) + .ok_or("(sequencer height + rollup number - rollup start height) is negative")?; + sequencer_height + .try_into() + .map_err(|_| "overflows u32::MAX, the maximum cometbft height") } /// Maps a sequencer height to a rollup height. /// -/// Returns `None` if `sequencer_height - sequencer_genesis_height` underflows or if -/// the result does not fit in `u32`. +/// Returns `None` if `sequencer_height - sequencer_start_height + rollup_start_block_number` +/// underflows or if the result does not fit in `u32`. pub(super) fn map_sequencer_height_to_rollup_height( - sequencer_genesis_height: SequencerHeight, + sequencer_start_height: u64, + rollup_start_block_number: u64, sequencer_height: SequencerHeight, ) -> Option { sequencer_height .value() - .checked_sub(sequencer_genesis_height.value())? + .checked_sub(sequencer_start_height)? + .checked_add(rollup_start_block_number)? .try_into() .ok() } #[cfg(test)] mod tests { - use astria_core::{ - generated::astria::execution::v1 as raw, - Protobuf as _, - }; - use pbjson_types::Timestamp; - use super::*; + use crate::test_utils::{ + make_commitment_state, + make_genesis_info, + make_rollup_state, + }; - fn make_commitment_state() -> CommitmentState { - let firm = Block::try_from_raw(raw::Block { - number: 1, - hash: vec![42u8; 32].into(), - parent_block_hash: vec![41u8; 32].into(), - timestamp: Some(Timestamp { - seconds: 123_456, - nanos: 789, - }), - }) - .unwrap(); - let soft = Block::try_from_raw(raw::Block { - number: 2, - hash: vec![43u8; 32].into(), - parent_block_hash: vec![42u8; 32].into(), - timestamp: Some(Timestamp { - seconds: 123_456, - nanos: 789, - }), - }) - .unwrap(); - CommitmentState::builder() - .firm(firm) - .soft(soft) - .base_celestia_height(1u64) - .build() - .unwrap() - } - - fn make_genesis_info() -> GenesisInfo { - let rollup_id = RollupId::new([24; 32]); - GenesisInfo::try_from_raw(raw::GenesisInfo { - rollup_id: Some(rollup_id.to_raw()), - sequencer_genesis_block_height: 10, - celestia_block_variance: 0, - }) - .unwrap() - } - - fn make_state() -> State { - State::try_from_genesis_info_and_commitment_state( + fn make_channel() -> (StateSender, StateReceiver) { + super::channel(make_rollup_state( make_genesis_info(), make_commitment_state(), - ) - .unwrap() - } - - fn make_channel() -> (StateSender, StateReceiver) { - super::channel(make_state()) + )) } #[test] fn next_firm_sequencer_height_is_correct() { let (_, rx) = make_channel(); assert_eq!( - SequencerHeight::from(12u32), + SequencerHeight::from(11u32), rx.next_expected_firm_sequencer_height(), ); } @@ -402,24 +479,39 @@ mod tests { fn next_soft_sequencer_height_is_correct() { let (_, rx) = make_channel(); assert_eq!( - SequencerHeight::from(13u32), + SequencerHeight::from(12u32), rx.next_expected_soft_sequencer_height(), ); } #[track_caller] - fn assert_height_is_correct(left: u32, right: u32, expected: u32) { + fn assert_height_is_correct( + sequencer_start_height: u32, + rollup_start_number: u32, + rollup_number: u32, + expected_sequencer_height: u32, + ) { assert_eq!( - SequencerHeight::from(expected), - map_rollup_number_to_sequencer_height(SequencerHeight::from(left), right) - .expect("left + right is so small, they should never overflow"), + SequencerHeight::from(expected_sequencer_height), + map_rollup_number_to_sequencer_height( + sequencer_start_height.into(), + rollup_start_number.into(), + rollup_number, + ) + .unwrap() ); } + #[should_panic = "rollup start height exceeds rollup number"] + #[test] + fn is_error_if_rollup_start_exceeds_current_number_plus_one() { + map_rollup_number_to_sequencer_height(10, 11, 9).unwrap(); + } + #[test] fn mapping_rollup_height_to_sequencer_height_works() { - assert_height_is_correct(0, 0, 0); - assert_height_is_correct(0, 1, 1); - assert_height_is_correct(1, 0, 1); + assert_height_is_correct(0, 0, 0, 0); + assert_height_is_correct(0, 1, 1, 0); + assert_height_is_correct(1, 0, 1, 2); } } diff --git a/crates/astria-conductor/src/executor/tests.rs b/crates/astria-conductor/src/executor/tests.rs index a5206cb141..3c4db78be8 100644 --- a/crates/astria-conductor/src/executor/tests.rs +++ b/crates/astria-conductor/src/executor/tests.rs @@ -1,11 +1,11 @@ use astria_core::{ self, - execution::v1::{ + execution::v2::{ Block, CommitmentState, GenesisInfo, }, - generated::astria::execution::v1 as raw, + generated::astria::execution::v2 as raw, Protobuf as _, }; use bytes::Bytes; @@ -17,11 +17,11 @@ use super::{ StateReceiver, StateSender, }, - RollupId, }; -use crate::config::CommitLevel; - -const ROLLUP_ID: RollupId = RollupId::new([42u8; 32]); +use crate::{ + config::CommitLevel, + test_utils::make_genesis_info, +}; fn make_block(number: u32) -> raw::Block { raw::Block { @@ -46,20 +46,19 @@ fn make_state( soft, }: MakeState, ) -> (StateSender, StateReceiver) { - let genesis_info = GenesisInfo::try_from_raw(raw::GenesisInfo { - rollup_id: Some(ROLLUP_ID.to_raw()), - sequencer_genesis_block_height: 1, - celestia_block_variance: 1, - }) - .unwrap(); + let genesis_info = GenesisInfo::try_from_raw(make_genesis_info()).unwrap(); let commitment_state = CommitmentState::try_from_raw(raw::CommitmentState { firm: Some(make_block(firm)), soft: Some(make_block(soft)), base_celestia_height: 1, }) .unwrap(); - let state = - State::try_from_genesis_info_and_commitment_state(genesis_info, commitment_state).unwrap(); + let state = State::try_from_genesis_info_and_commitment_state( + genesis_info, + commitment_state, + crate::config::CommitLevel::SoftAndFirm, + ) + .unwrap(); super::state::channel(state) } diff --git a/crates/astria-conductor/src/lib.rs b/crates/astria-conductor/src/lib.rs index ee26570938..228a374f40 100644 --- a/crates/astria-conductor/src/lib.rs +++ b/crates/astria-conductor/src/lib.rs @@ -20,6 +20,8 @@ pub mod config; pub(crate) mod executor; pub(crate) mod metrics; pub(crate) mod sequencer; +#[cfg(test)] +pub(crate) mod test_utils; mod utils; pub use build_info::BUILD_INFO; diff --git a/crates/astria-conductor/src/sequencer/block_stream.rs b/crates/astria-conductor/src/sequencer/block_stream.rs index 490ae8f633..0d9f673370 100644 --- a/crates/astria-conductor/src/sequencer/block_stream.rs +++ b/crates/astria-conductor/src/sequencer/block_stream.rs @@ -1,5 +1,6 @@ use std::{ error::Error as StdError, + num::NonZeroU64, pin::Pin, task::Poll, }; @@ -35,6 +36,7 @@ struct Heights { rollup_expects: u64, greatest_requested_height: Option, latest_observed_sequencer_height: Option, + stop_height: Option, max_ahead: u64, } @@ -49,7 +51,11 @@ impl Heights { let not_too_far_ahead = potential_height < (self.rollup_expects.saturating_add(self.max_ahead)); let height_exists_on_sequencer = potential_height <= latest_observed_sequencer_height; - if not_too_far_ahead && height_exists_on_sequencer { + let stop_height_reached = self + .stop_height + .map_or(false, |stop_height| potential_height > stop_height.into()); + + if not_too_far_ahead && height_exists_on_sequencer && !stop_height_reached { Some(potential_height) } else { None @@ -148,12 +154,14 @@ impl BlocksFromHeightStream { pub(super) fn new( rollup_id: RollupId, first_height: Height, + last_height: Option, client: SequencerGrpcClient, ) -> Self { let heights = Heights { rollup_expects: first_height.value(), latest_observed_sequencer_height: None, greatest_requested_height: None, + stop_height: last_height, max_ahead: 128, }; Self { @@ -273,6 +281,8 @@ async fn fetch_block( #[cfg(test)] mod tests { + use std::num::NonZeroU64; + use super::Heights; #[test] @@ -281,6 +291,7 @@ mod tests { rollup_expects: 5, greatest_requested_height: None, latest_observed_sequencer_height: Some(6), + stop_height: None, max_ahead: 3, }; let next = heights.next_height_to_fetch(); @@ -306,18 +317,33 @@ mod tests { rollup_expects: 4, greatest_requested_height: Some(5), latest_observed_sequencer_height: Some(6), + stop_height: None, max_ahead: 2, }; let next = heights.next_height_to_fetch(); assert_eq!(None, next); } + #[test] + fn next_height_is_none_if_last_height_reached() { + let heights = Heights { + rollup_expects: 4, + greatest_requested_height: Some(6), + latest_observed_sequencer_height: Some(6), + stop_height: Some(NonZeroU64::new(6).unwrap()), + max_ahead: 5, + }; + let next = heights.next_height_to_fetch(); + assert_eq!(None, next); + } + #[test] fn next_height_is_none_if_at_sequencer_head() { let heights = Heights { rollup_expects: 4, greatest_requested_height: Some(5), latest_observed_sequencer_height: Some(5), + stop_height: None, max_ahead: 2, }; let next = heights.next_height_to_fetch(); @@ -330,6 +356,7 @@ mod tests { rollup_expects: 5, greatest_requested_height: None, latest_observed_sequencer_height: None, + stop_height: None, max_ahead: 3, }; let next = heights.next_height_to_fetch(); diff --git a/crates/astria-conductor/src/sequencer/builder.rs b/crates/astria-conductor/src/sequencer/builder.rs index a95b98e13a..c215074bc0 100644 --- a/crates/astria-conductor/src/sequencer/builder.rs +++ b/crates/astria-conductor/src/sequencer/builder.rs @@ -11,7 +11,6 @@ pub(crate) struct Builder { pub(crate) sequencer_grpc_client: SequencerGrpcClient, pub(crate) sequencer_cometbft_client: sequencer_client::HttpClient, pub(crate) sequencer_block_time: Duration, - pub(crate) expected_sequencer_chain_id: String, pub(crate) shutdown: CancellationToken, pub(crate) rollup_state: StateReceiver, pub(crate) soft_blocks: mpsc::Sender, @@ -23,7 +22,6 @@ impl Builder { sequencer_grpc_client, sequencer_cometbft_client, sequencer_block_time, - expected_sequencer_chain_id, shutdown, rollup_state, soft_blocks, @@ -34,7 +32,6 @@ impl Builder { sequencer_grpc_client, sequencer_cometbft_client, sequencer_block_time, - expected_sequencer_chain_id, shutdown, } } diff --git a/crates/astria-conductor/src/sequencer/mod.rs b/crates/astria-conductor/src/sequencer/mod.rs index c6006ecc50..49ff4ca5f9 100644 --- a/crates/astria-conductor/src/sequencer/mod.rs +++ b/crates/astria-conductor/src/sequencer/mod.rs @@ -15,6 +15,7 @@ use futures::{ self, BoxFuture, Fuse, + FusedFuture as _, }, FutureExt as _, StreamExt as _, @@ -74,9 +75,6 @@ pub(crate) struct Reader { /// height. sequencer_block_time: Duration, - /// The chain ID of the sequencer network the reader should be communicating with. - expected_sequencer_chain_id: String, - /// Token to listen for Conductor being shut down. shutdown: CancellationToken, } @@ -99,13 +97,13 @@ impl Reader { #[instrument(skip_all, err)] async fn initialize(&mut self) -> eyre::Result<()> { + let expected_sequencer_chain_id = self.rollup_state.sequencer_chain_id(); let actual_sequencer_chain_id = get_sequencer_chain_id(self.sequencer_cometbft_client.clone()) .await .wrap_err("failed to get chain ID from Sequencer")?; - let expected_sequencer_chain_id = &self.expected_sequencer_chain_id; ensure!( - self.expected_sequencer_chain_id == actual_sequencer_chain_id.as_str(), + expected_sequencer_chain_id == actual_sequencer_chain_id.as_str(), "expected chain id `{expected_sequencer_chain_id}` does not match actual: \ `{actual_sequencer_chain_id}`" ); @@ -152,6 +150,9 @@ impl RunningReader { } = reader; let next_expected_height = rollup_state.next_expected_soft_sequencer_height(); + let sequencer_stop_height = rollup_state + .sequencer_stop_height() + .wrap_err("failed to obtain sequencer stop height")?; let latest_height_stream = sequencer_cometbft_client.stream_latest_height(sequencer_block_time); @@ -162,6 +163,7 @@ impl RunningReader { let blocks_from_heights = BlocksFromHeightStream::new( rollup_state.rollup_id(), next_expected_height, + sequencer_stop_height, sequencer_grpc_client, ); @@ -186,9 +188,11 @@ impl RunningReader { } async fn run_loop(&mut self) -> eyre::Result<&'static str> { - use futures::future::FusedFuture as _; - loop { + if self.has_reached_stop_height()? { + return Ok("stop height reached"); + } + select! { biased; @@ -287,6 +291,19 @@ impl RunningReader { .set_next_expected_height_if_greater(next_height); self.block_cache.drop_obsolete(next_height); } + + /// The stop height is reached if a) the next height to be forwarded would be greater + /// than the stop height, and b) there is no block currently in flight. + fn has_reached_stop_height(&self) -> eyre::Result { + Ok(self + .rollup_state + .sequencer_stop_height() + .wrap_err("failed to obtain sequencer stop height")? + .map_or(false, |height| { + self.block_cache.next_height_to_pop() > height.get() + && self.enqueued_block.is_terminated() + })) + } } #[instrument(skip_all)] diff --git a/crates/astria-conductor/src/test_utils.rs b/crates/astria-conductor/src/test_utils.rs new file mode 100644 index 0000000000..a91efdd0c3 --- /dev/null +++ b/crates/astria-conductor/src/test_utils.rs @@ -0,0 +1,66 @@ +use astria_core::{ + generated::astria::execution::v2::{ + CommitmentState, + GenesisInfo, + }, + primitive::v1::RollupId, + Protobuf as _, +}; + +use crate::executor::State; + +pub(crate) fn make_commitment_state() -> CommitmentState { + let firm = astria_core::generated::astria::execution::v2::Block { + number: 1, + hash: vec![42u8; 32].into(), + parent_block_hash: vec![41u8; 32].into(), + timestamp: Some(pbjson_types::Timestamp { + seconds: 123_456, + nanos: 789, + }), + }; + let soft = astria_core::generated::astria::execution::v2::Block { + number: 2, + hash: vec![43u8; 32].into(), + parent_block_hash: vec![42u8; 32].into(), + timestamp: Some(pbjson_types::Timestamp { + seconds: 123_456, + nanos: 789, + }), + }; + + CommitmentState { + soft: Some(soft), + firm: Some(firm), + base_celestia_height: 1, + } +} + +pub(crate) fn make_genesis_info() -> GenesisInfo { + let rollup_id = RollupId::new([24; 32]); + GenesisInfo { + rollup_id: Some(rollup_id.to_raw()), + sequencer_start_height: 10, + celestia_block_variance: 0, + rollup_start_block_number: 1, + rollup_stop_block_number: 90, + sequencer_chain_id: "test-sequencer-0".to_string(), + celestia_chain_id: "test-celestia-0".to_string(), + halt_at_rollup_stop_number: false, + } +} + +pub(crate) fn make_rollup_state( + genesis_info: GenesisInfo, + commitment_state: CommitmentState, +) -> State { + let genesis_info = astria_core::execution::v2::GenesisInfo::try_from_raw(genesis_info).unwrap(); + let commitment_state = + astria_core::execution::v2::CommitmentState::try_from_raw(commitment_state).unwrap(); + State::try_from_genesis_info_and_commitment_state( + genesis_info, + commitment_state, + crate::config::CommitLevel::SoftAndFirm, + ) + .unwrap() +} diff --git a/crates/astria-conductor/tests/blackbox/firm_only.rs b/crates/astria-conductor/tests/blackbox/firm_only.rs index f08271ce43..a66060144e 100644 --- a/crates/astria-conductor/tests/blackbox/firm_only.rs +++ b/crates/astria-conductor/tests/blackbox/firm_only.rs @@ -5,7 +5,7 @@ use astria_conductor::{ Conductor, Config, }; -use astria_core::generated::astria::execution::v1::{ +use astria_core::generated::astria::execution::v2::{ GetCommitmentStateRequest, GetGenesisInfoRequest, }; @@ -54,8 +54,10 @@ async fn simple() { mount_get_genesis_info!( test_conductor, - sequencer_genesis_block_height: 1, + sequencer_start_height: 3, celestia_block_variance: 10, + rollup_start_block_number: 2, + rollup_stop_block_number: 9, ); mount_get_commitment_state!( @@ -125,7 +127,7 @@ async fn simple() { .await .expect( "conductor should have executed the firm block and updated the firm commitment state \ - within 2000ms", + within 1000ms", ); } @@ -135,8 +137,10 @@ async fn submits_two_heights_in_succession() { mount_get_genesis_info!( test_conductor, - sequencer_genesis_block_height: 1, + sequencer_start_height: 3, celestia_block_variance: 10, + rollup_start_block_number: 2, + rollup_stop_block_number: 9, ); mount_get_commitment_state!( @@ -236,7 +240,7 @@ async fn submits_two_heights_in_succession() { ) .await .expect( - "conductor should have executed the soft block and updated the soft commitment state \ + "conductor should have executed the firm block and updated the firm commitment state \ within 2000ms", ); } @@ -247,8 +251,10 @@ async fn skips_already_executed_heights() { mount_get_genesis_info!( test_conductor, - sequencer_genesis_block_height: 1, + sequencer_start_height: 3, celestia_block_variance: 10, + rollup_start_block_number: 2, + rollup_stop_block_number: 9, ); mount_get_commitment_state!( @@ -320,7 +326,7 @@ async fn skips_already_executed_heights() { ) .await .expect( - "conductor should have executed the soft block and updated the soft commitment state \ + "conductor should have executed the firm block and updated the firm commitment state \ within 1000ms", ); } @@ -331,8 +337,10 @@ async fn fetch_from_later_celestia_height() { mount_get_genesis_info!( test_conductor, - sequencer_genesis_block_height: 1, + sequencer_start_height: 3, celestia_block_variance: 10, + rollup_start_block_number: 2, + rollup_stop_block_number: 9, ); mount_get_commitment_state!( @@ -447,8 +455,11 @@ async fn exits_on_celestia_chain_id_mismatch() { matcher::message_type::(), ) .respond_with(GrpcResponse::constant_response( - genesis_info!(sequencer_genesis_block_height: 1, - celestia_block_variance: 10,), + genesis_info!(sequencer_start_height: 3, + celestia_block_variance: 10, + rollup_start_block_number: 2, + rollup_stop_block_number: 9 + ), )) .expect(0..) .mount(&mock_grpc.mock_server) @@ -513,3 +524,174 @@ async fn exits_on_celestia_chain_id_mismatch() { } } } + +/// Tests that the conductor correctly stops at the stop block height and executes the firm block +/// for that height before restarting and continuing after fetching new genesis info and commitment +/// state. +/// +/// It consists of the following steps: +/// 1. Mount commitment state and genesis info with a stop height of 3 for the first height, only +/// responding up to 1 time so that the same info is not provided after conductor restart. +/// 2. Mount sequencer genesis and celestia header network head. +/// 3. Mount firm blocks for heights 3 and 4. +/// 4. Mount `execute_block` and `update_commitment_state` for firm block 3, expecting only one call +/// since they should not be called after restarting. +/// 5. Wait ample time for conductor to restart before performing the next set of mounts. +/// 6. Mount new genesis info and updated commitment state with rollup start block height of 2 to +/// reflect that the first block has already been executed. +/// 7. Mount `execute_block` and `update_commitment_state` for firm block 4, awaiting their +/// satisfaction. +#[expect(clippy::too_many_lines, reason = "All lines reasonably necessary")] +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] +async fn restarts_after_reaching_stop_block_height() { + let test_conductor = spawn_conductor(CommitLevel::FirmOnly).await; + + mount_get_genesis_info!( + test_conductor, + sequencer_start_height: 3, + celestia_block_variance: 10, + rollup_start_block_number: 2, + rollup_stop_block_number: 2, + up_to_n_times: 1, // Only respond once, since updated information is needed after restart. + ); + + mount_get_commitment_state!( + test_conductor, + firm: ( + number: 1, + hash: [1; 64], + parent: [0; 64], + ), + soft: ( + number: 1, + hash: [1; 64], + parent: [0; 64], + ), + base_celestia_height: 1, + up_to_n_times: 1, + ); + + mount_sequencer_genesis!(test_conductor); + mount_celestia_header_network_head!( + test_conductor, + height: 2u32, + ); + + mount_celestia_blobs!( + test_conductor, + celestia_height: 1, + sequencer_heights: [3, 4], + ); + mount_sequencer_commit!( + test_conductor, + height: 3u32, + ); + mount_sequencer_commit!( + test_conductor, + height: 4u32, + ); + mount_sequencer_validator_set!(test_conductor, height: 2u32); + mount_sequencer_validator_set!(test_conductor, height: 3u32); + + let execute_block_1 = mount_executed_block!( + test_conductor, + mock_name: "execute_block_1", + number: 2, + hash: [2; 64], + parent: [1; 64], + expected_calls: 1, // should not be called again upon restart + ); + + let update_commitment_state_1 = mount_update_commitment_state!( + test_conductor, + mock_name: "update_commitment_state_1", + firm: ( + number: 2, + hash: [2; 64], + parent: [1; 64], + ), + soft: ( + number: 2, + hash: [2; 64], + parent: [1; 64], + ), + base_celestia_height: 1, + expected_calls: 1, // should not be called again upon restart + ); + + timeout( + Duration::from_millis(1000), + join( + execute_block_1.wait_until_satisfied(), + update_commitment_state_1.wait_until_satisfied(), + ), + ) + .await + .expect( + "conductor should have executed the first firm block and updated the first firm \ + commitment state twice within 1000ms", + ); + + // Mount new genesis info and commitment state with updated heights + mount_get_genesis_info!( + test_conductor, + sequencer_start_height: 4, + celestia_block_variance: 10, + rollup_start_block_number: 3, + rollup_stop_block_number: 9, + ); + + mount_get_commitment_state!( + test_conductor, + firm: ( + number: 2, + hash: [2; 64], + parent: [1; 64], + ), + soft: ( + number: 2, + hash: [2; 64], + parent: [1; 64], + ), + base_celestia_height: 1, + ); + + let execute_block_2 = mount_executed_block!( + test_conductor, + mock_name: "execute_block_2", + number: 3, + hash: [3; 64], + parent: [2; 64], + expected_calls: 1, + ); + + let update_commitment_state_2 = mount_update_commitment_state!( + test_conductor, + mock_name: "update_commitment_state_2", + firm: ( + number: 3, + hash: [3; 64], + parent: [2; 64], + ), + soft: ( + number: 3, + hash: [3; 64], + parent: [2; 64], + ), + base_celestia_height: 1, + expected_calls: 1, + ); + + timeout( + Duration::from_millis(2000), + join( + execute_block_2.wait_until_satisfied(), + update_commitment_state_2.wait_until_satisfied(), + ), + ) + .await + .expect( + "conductor should have executed the second firm block and updated the second firm \ + commitment state twice within 2000ms", + ); +} diff --git a/crates/astria-conductor/tests/blackbox/helpers/macros.rs b/crates/astria-conductor/tests/blackbox/helpers/macros.rs index 981f4c31dc..4b9fba3aca 100644 --- a/crates/astria-conductor/tests/blackbox/helpers/macros.rs +++ b/crates/astria-conductor/tests/blackbox/helpers/macros.rs @@ -1,7 +1,7 @@ #[macro_export] macro_rules! block { (number: $number:expr,hash: $hash:expr,parent: $parent:expr $(,)?) => { - ::astria_core::generated::astria::execution::v1::Block { + ::astria_core::generated::astria::execution::v2::Block { number: $number, hash: ::bytes::Bytes::from(Vec::from($hash)), parent_block_hash: ::bytes::Bytes::from(Vec::from($parent)), @@ -59,7 +59,7 @@ macro_rules! commitment_state { soft: (number: $soft_number:expr,hash: $soft_hash:expr,parent: $soft_parent:expr $(,)?), base_celestia_height: $base_celestia_height:expr $(,)? ) => { - ::astria_core::generated::astria::execution::v1::CommitmentState { + ::astria_core::generated::astria::execution::v2::CommitmentState { firm: Some($crate::block!( number: $firm_number, hash: $firm_hash, @@ -94,14 +94,37 @@ macro_rules! filtered_sequencer_block { #[macro_export] macro_rules! genesis_info { ( - sequencer_genesis_block_height: - $sequencer_height:expr,celestia_block_variance: - $variance:expr $(,)? + sequencer_start_height: + $start_height:expr,celestia_block_variance: + $variance:expr,rollup_start_block_number: + $rollup_start_block_number:expr, rollup_stop_block_number: + $rollup_stop_block_number:expr $(,)? ) => { - ::astria_core::generated::astria::execution::v1::GenesisInfo { + genesis_info!( + sequencer_start_height: $start_height, + celestia_block_variance: $variance, + rollup_start_block_number: $rollup_start_block_number, + rollup_stop_block_number: $rollup_stop_block_number, + halt_at_rollup_stop_number: false, + ) + }; + ( + sequencer_start_height: + $start_height:expr,celestia_block_variance: + $variance:expr,rollup_start_block_number: + $rollup_start_block_number:expr, + rollup_stop_block_number: $rollup_stop_block_number:expr, + halt_at_rollup_stop_number: $halt_at_rollup_stop_number:expr $(,)? + ) => { + ::astria_core::generated::astria::execution::v2::GenesisInfo { rollup_id: Some($crate::ROLLUP_ID.to_raw()), - sequencer_genesis_block_height: $sequencer_height, + sequencer_start_height: $start_height, celestia_block_variance: $variance, + rollup_start_block_number: $rollup_start_block_number, + rollup_stop_block_number: $rollup_stop_block_number, + sequencer_chain_id: $crate::SEQUENCER_CHAIN_ID.to_string(), + celestia_chain_id: $crate::helpers::CELESTIA_CHAIN_ID.to_string(), + halt_at_rollup_stop_number: $halt_at_rollup_stop_number, } }; } @@ -175,6 +198,22 @@ macro_rules! mount_get_commitment_state { soft: ( number: $soft_number:expr, hash: $soft_hash:expr, parent: $soft_parent:expr$(,)? ), base_celestia_height: $base_celestia_height:expr $(,)? + ) => { + mount_get_commitment_state!( + $test_env, + firm: ( number: $firm_number, hash: $firm_hash, parent: $firm_parent, ), + soft: ( number: $soft_number, hash: $soft_hash, parent: $soft_parent, ), + base_celestia_height: $base_celestia_height, + up_to_n_times: 1, + ) + }; + ( + $test_env:ident, + firm: ( number: $firm_number:expr, hash: $firm_hash:expr, parent: $firm_parent:expr$(,)? ), + soft: ( number: $soft_number:expr, hash: $soft_hash:expr, parent: $soft_parent:expr$(,)? ), + base_celestia_height: $base_celestia_height:expr, + up_to_n_times: $up_to_n_times:expr + $(,)? ) => { $test_env .mount_get_commitment_state($crate::commitment_state!( @@ -189,7 +228,7 @@ macro_rules! mount_get_commitment_state { parent: $soft_parent, ), base_celestia_height: $base_celestia_height, - )) + ), $up_to_n_times) .await }; } @@ -274,7 +313,8 @@ macro_rules! mount_executed_block { mock_name: $mock_name:expr, number: $number:expr, hash: $hash:expr, - parent: $parent:expr $(,)? + parent: $parent:expr, + expected_calls: $expected_calls:expr $(,)? ) => {{ use ::base64::prelude::*; $test_env.mount_execute_block( @@ -287,10 +327,27 @@ macro_rules! mount_executed_block { number: $number, hash: $hash, parent: $parent, - ) + ), + $expected_calls, ) .await }}; + ( + $test_env:ident, + mock_name: $mock_name:expr, + number: $number:expr, + hash: $hash:expr, + parent: $parent:expr, + ) => { + mount_executed_block!( + $test_env, + mock_name: None, + number: $number, + hash: $hash, + parent: $parent, + expected_calls: 1, + ) + }; ( $test_env:ident, number: $number:expr, @@ -334,15 +391,62 @@ macro_rules! mount_get_filtered_sequencer_block { macro_rules! mount_get_genesis_info { ( $test_env:ident, - sequencer_genesis_block_height: $sequencer_height:expr, - celestia_block_variance: $variance:expr + sequencer_start_height: $start_height:expr, + celestia_block_variance: $variance:expr, + rollup_start_block_number: $rollup_start_block_number:expr, + rollup_stop_block_number: $rollup_stop_block_number:expr + $(,)? + ) => { + mount_get_genesis_info!( + $test_env, + sequencer_start_height: $start_height, + celestia_block_variance: $variance, + rollup_start_block_number: $rollup_start_block_number, + rollup_stop_block_number: $rollup_stop_block_number, + up_to_n_times: 1, + ) + }; + ( + $test_env:ident, + sequencer_start_height: $start_height:expr, + celestia_block_variance: $variance:expr, + rollup_start_block_number: $rollup_start_block_number:expr, + rollup_stop_block_number: $rollup_stop_block_number:expr, + up_to_n_times: $up_to_n_times:expr + $(,)? + ) => { + mount_get_genesis_info!( + $test_env, + sequencer_start_height: $start_height, + celestia_block_variance: $variance, + rollup_start_block_number: $rollup_start_block_number, + rollup_stop_block_number: $rollup_stop_block_number, + up_to_n_times: $up_to_n_times, + halt_at_rollup_stop_number: false, + expected_calls: 1, + ) + }; + ( + $test_env:ident, + sequencer_start_height: $start_height:expr, + celestia_block_variance: $variance:expr, + rollup_start_block_number: $rollup_start_block_number:expr, + rollup_stop_block_number: $rollup_stop_block_number:expr, + up_to_n_times: $up_to_n_times:expr, + halt_at_rollup_stop_number: $halt_at_rollup_stop_number:expr, + expected_calls: $expected_calls:expr $(,)? ) => { $test_env.mount_get_genesis_info( $crate::genesis_info!( - sequencer_genesis_block_height: $sequencer_height, + sequencer_start_height: $start_height, celestia_block_variance: $variance, - ) + rollup_start_block_number: $rollup_start_block_number, + rollup_stop_block_number: $rollup_stop_block_number, + halt_at_rollup_stop_number: $halt_at_rollup_stop_number, + ), + $up_to_n_times, + $expected_calls, ).await; }; } @@ -385,12 +489,12 @@ macro_rules! mount_get_block { hash: $hash, parent: $parent, ); - let identifier = ::astria_core::generated::astria::execution::v1::BlockIdentifier { + let identifier = ::astria_core::generated::astria::execution::v2::BlockIdentifier { identifier: Some( - ::astria_core::generated::astria::execution::v1::block_identifier::Identifier::BlockNumber(block.number) + ::astria_core::generated::astria::execution::v2::block_identifier::Identifier::BlockNumber(block.number) )}; $test_env.mount_get_block( - ::astria_core::generated::astria::execution::v1::GetBlockRequest { + ::astria_core::generated::astria::execution::v2::GetBlockRequest { identifier: Some(identifier), }, block, diff --git a/crates/astria-conductor/tests/blackbox/helpers/mock_grpc.rs b/crates/astria-conductor/tests/blackbox/helpers/mock_grpc.rs index 457ef04e3d..fba86349c6 100644 --- a/crates/astria-conductor/tests/blackbox/helpers/mock_grpc.rs +++ b/crates/astria-conductor/tests/blackbox/helpers/mock_grpc.rs @@ -4,7 +4,7 @@ use std::{ }; use astria_core::generated::astria::{ - execution::v1::{ + execution::v2::{ execution_service_server::{ ExecutionService, ExecutionServiceServer, diff --git a/crates/astria-conductor/tests/blackbox/helpers/mod.rs b/crates/astria-conductor/tests/blackbox/helpers/mod.rs index c14ffff018..3933ddd418 100644 --- a/crates/astria-conductor/tests/blackbox/helpers/mod.rs +++ b/crates/astria-conductor/tests/blackbox/helpers/mod.rs @@ -13,7 +13,7 @@ use astria_conductor::{ use astria_core::{ brotli::compress_bytes, generated::astria::{ - execution::v1::{ + execution::v2::{ Block, CommitmentState, GenesisInfo, @@ -179,7 +179,7 @@ impl TestConductor { pub async fn mount_get_block( &self, expected_pbjson: S, - block: astria_core::generated::astria::execution::v1::Block, + block: astria_core::generated::astria::execution::v2::Block, ) { use astria_grpc_mock::{ matcher::message_partial_pbjson, @@ -305,20 +305,30 @@ impl TestConductor { mount_genesis(&self.mock_http, chain_id).await; } - pub async fn mount_get_genesis_info(&self, genesis_info: GenesisInfo) { - use astria_core::generated::astria::execution::v1::GetGenesisInfoRequest; + pub async fn mount_get_genesis_info( + &self, + genesis_info: GenesisInfo, + up_to_n_times: u64, + expected_calls: u64, + ) { + use astria_core::generated::astria::execution::v2::GetGenesisInfoRequest; astria_grpc_mock::Mock::for_rpc_given( "get_genesis_info", astria_grpc_mock::matcher::message_type::(), ) .respond_with(astria_grpc_mock::response::constant_response(genesis_info)) - .expect(1..) + .up_to_n_times(up_to_n_times) + .expect(expected_calls) .mount(&self.mock_grpc.mock_server) .await; } - pub async fn mount_get_commitment_state(&self, commitment_state: CommitmentState) { - use astria_core::generated::astria::execution::v1::GetCommitmentStateRequest; + pub async fn mount_get_commitment_state( + &self, + commitment_state: CommitmentState, + up_to_n_times: u64, + ) { + use astria_core::generated::astria::execution::v2::GetCommitmentStateRequest; astria_grpc_mock::Mock::for_rpc_given( "get_commitment_state", @@ -327,6 +337,7 @@ impl TestConductor { .respond_with(astria_grpc_mock::response::constant_response( commitment_state, )) + .up_to_n_times(up_to_n_times) .expect(1..) .mount(&self.mock_grpc.mock_server) .await; @@ -337,6 +348,7 @@ impl TestConductor { mock_name: Option<&str>, expected_pbjson: S, response: Block, + expected_calls: u64, ) -> astria_grpc_mock::MockGuard { use astria_grpc_mock::{ matcher::message_partial_pbjson, @@ -349,7 +361,7 @@ impl TestConductor { if let Some(name) = mock_name { mock = mock.with_name(name); } - mock.expect(1) + mock.expect(expected_calls) .mount_as_scoped(&self.mock_grpc.mock_server) .await } @@ -379,9 +391,9 @@ impl TestConductor { &self, mock_name: Option<&str>, commitment_state: CommitmentState, - expected_calls: u64, + expected_calls: impl Into, ) -> astria_grpc_mock::MockGuard { - use astria_core::generated::astria::execution::v1::UpdateCommitmentStateRequest; + use astria_core::generated::astria::execution::v2::UpdateCommitmentStateRequest; use astria_grpc_mock::{ matcher::message_partial_pbjson, response::constant_response, @@ -522,8 +534,6 @@ pub(crate) fn make_config() -> Config { sequencer_cometbft_url: "http://127.0.0.1:26657".into(), sequencer_requests_per_second: 500, sequencer_block_time_ms: 2000, - expected_celestia_chain_id: CELESTIA_CHAIN_ID.into(), - expected_sequencer_chain_id: SEQUENCER_CHAIN_ID.into(), execution_rpc_url: "http://127.0.0.1:50051".into(), log: "info".into(), execution_commit_level: astria_conductor::config::CommitLevel::SoftAndFirm, diff --git a/crates/astria-conductor/tests/blackbox/soft_and_firm.rs b/crates/astria-conductor/tests/blackbox/soft_and_firm.rs index 1054c43f80..b172b7de0f 100644 --- a/crates/astria-conductor/tests/blackbox/soft_and_firm.rs +++ b/crates/astria-conductor/tests/blackbox/soft_and_firm.rs @@ -40,8 +40,10 @@ async fn executes_soft_first_then_updates_firm() { mount_get_genesis_info!( test_conductor, - sequencer_genesis_block_height: 1, + sequencer_start_height: 3, celestia_block_variance: 10, + rollup_start_block_number: 2, + rollup_stop_block_number: 9, ); mount_get_commitment_state!( @@ -99,7 +101,7 @@ async fn executes_soft_first_then_updates_firm() { ); timeout( - Duration::from_millis(500), + Duration::from_millis(1000), join( execute_block.wait_until_satisfied(), update_commitment_state_soft.wait_until_satisfied(), @@ -108,7 +110,7 @@ async fn executes_soft_first_then_updates_firm() { .await .expect( "Conductor should have executed the block and updated the soft commitment state within \ - 500ms", + 1000ms", ); mount_celestia_blobs!( @@ -174,8 +176,10 @@ async fn executes_firm_then_soft_at_next_height() { mount_get_genesis_info!( test_conductor, - sequencer_genesis_block_height: 1, + sequencer_start_height: 3, celestia_block_variance: 10, + rollup_start_block_number: 2, + rollup_stop_block_number: 9, ); mount_get_commitment_state!( @@ -333,8 +337,10 @@ async fn missing_block_is_fetched_for_updating_firm_commitment() { mount_get_genesis_info!( test_conductor, - sequencer_genesis_block_height: 1, + sequencer_start_height: 3, celestia_block_variance: 10, + rollup_start_block_number: 2, + rollup_stop_block_number: 9, ); mount_get_commitment_state!( @@ -457,13 +463,18 @@ async fn missing_block_is_fetched_for_updating_firm_commitment() { reason = "all lines fairly necessary, and I don't think a test warrants a refactor" )] #[tokio::test(flavor = "multi_thread", worker_threads = 1)] -async fn conductor_restarts_on_permission_denied() { +async fn restarts_on_permission_denied() { let test_conductor = spawn_conductor(CommitLevel::SoftAndFirm).await; mount_get_genesis_info!( test_conductor, - sequencer_genesis_block_height: 1, + sequencer_start_height: 3, celestia_block_variance: 10, + rollup_start_block_number: 2, + rollup_stop_block_number: 9, + up_to_n_times: 2, + halt_at_rollup_stop_number: false, + expected_calls: 2, ); mount_get_commitment_state!( @@ -479,6 +490,7 @@ async fn conductor_restarts_on_permission_denied() { parent: [0; 64], ), base_celestia_height: 1, + up_to_n_times: 2, ); mount_sequencer_genesis!(test_conductor); @@ -578,3 +590,577 @@ async fn conductor_restarts_on_permission_denied() { within 1000ms", ); } + +/// Tests if the conductor correctly stops and procedes to restart after soft block height reaches +/// sequencer stop height (from genesis info provided by rollup). In `SoftAndFirm` mode executor +/// should execute both the soft and firm blocks at the stop height and then perform a restart. +/// +/// This test consists of the following steps: +/// 1. Mount commitment state and genesis info with a sequencer stop height of 3, only responding up +/// to 1 time so that Conductor will not receive the same response after restart. +/// 2. Mount Celestia network head and sequencer genesis. +/// 3. Mount ABCI info and sequencer blocks (soft blocks) for heights 3 and 4. +/// 4. Mount firm blocks at heights 3 and 4 with a slight delay to ensure that the soft blocks +/// arrive first. +/// 5. Mount `execute_block` and `update_commitment_state` for both soft and firm blocks at height 3 +/// 6. Await satisfaction of the `execute_block` and `update_commitment_state` for the soft and firm +/// blocks at height 3 with a timeout of 1000ms. +/// 7. Mount new genesis info with a sequencer stop height of 10 and a rollup start block height of +/// 2, along with corresponding commitment state, reflecting that block 1 has already been +/// executed and the commitment state updated. +/// 8. Mount `execute_block` and `update_commitment_state` for both soft and firm blocks at height 4 +/// and await their satisfaction. +#[expect( + clippy::too_many_lines, + reason = "All lines reasonably necessary for the thoroughness of this test" +)] +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] +async fn restarts_after_reaching_soft_stop_height_first() { + let test_conductor = spawn_conductor(CommitLevel::SoftAndFirm).await; + + mount_get_genesis_info!( + test_conductor, + sequencer_start_height: 3, + celestia_block_variance: 10, + rollup_start_block_number: 2, + rollup_stop_block_number: 2, + up_to_n_times: 1, // We only respond once since this needs to be updated after restart + ); + + mount_get_commitment_state!( + test_conductor, + firm: ( + number: 1, + hash: [1; 64], + parent: [0; 64], + ), + soft: ( + number: 1, + hash: [1; 64], + parent: [0; 64], + ), + base_celestia_height: 1, + up_to_n_times: 1, // We only respond once since this needs to be updated after restart + ); + + mount_sequencer_genesis!(test_conductor); + mount_celestia_header_network_head!( + test_conductor, + height: 1u32, + ); + mount_abci_info!( + test_conductor, + latest_sequencer_height: 4, + ); + + // Mount soft blocks for heights 3 and 4 + mount_get_filtered_sequencer_block!( + test_conductor, + sequencer_height: 3, + ); + mount_get_filtered_sequencer_block!( + test_conductor, + sequencer_height: 4, + ); + + // Mount firm blocks for heights 3 and 4 + mount_celestia_blobs!( + test_conductor, + celestia_height: 1, + sequencer_heights: [3, 4], + delay: Some(Duration::from_millis(200)) // short delay to ensure soft block at height 4 gets executed first after restart + ); + mount_sequencer_commit!( + test_conductor, + height: 3u32, + ); + mount_sequencer_commit!( + test_conductor, + height: 4u32, + ); + mount_sequencer_validator_set!(test_conductor, height: 2u32); + mount_sequencer_validator_set!(test_conductor, height: 3u32); + + let execute_block_1 = mount_executed_block!( + test_conductor, + mock_name: "execute_block_1", + number: 2, + hash: [2; 64], + parent: [1; 64], + expected_calls: 1, // This should not be called again after restart + ); + + let update_commitment_state_soft_1 = mount_update_commitment_state!( + test_conductor, + mock_name: "update_commitment_state_soft_1", + firm: ( + number: 1, + hash: [1; 64], + parent: [0; 64], + ), + soft: ( + number: 2, + hash: [2; 64], + parent: [1; 64], + ), + base_celestia_height: 1, + expected_calls: 1, + ); + + let update_commitment_state_firm_1 = mount_update_commitment_state!( + test_conductor, + mock_name: "update_commitment_state_firm_1", + firm: ( + number: 2, + hash: [2; 64], + parent: [1; 64], + ), + soft: ( + number: 2, + hash: [2; 64], + parent: [1; 64], + ), + base_celestia_height: 1, + expected_calls: 1, // Should not be called again after restart + ); + + timeout( + Duration::from_millis(1000), + join3( + execute_block_1.wait_until_satisfied(), + update_commitment_state_firm_1.wait_until_satisfied(), + update_commitment_state_soft_1.wait_until_satisfied(), + ), + ) + .await + .expect("conductor should have updated the firm commitment state within 1000ms"); + + mount_get_genesis_info!( + test_conductor, + sequencer_start_height: 4, + celestia_block_variance: 10, + rollup_start_block_number: 3, + rollup_stop_block_number: 9, + ); + + mount_get_commitment_state!( + test_conductor, + firm: ( + number: 2, + hash: [2; 64], + parent: [1; 64], + ), + soft: ( + number: 2, + hash: [2; 64], + parent: [1; 64], + ), + base_celestia_height: 1, + ); + + let execute_block_2 = mount_executed_block!( + test_conductor, + mock_name: "execute_block_2", + number: 3, + hash: [3; 64], + parent: [2; 64], + expected_calls: 1, + ); + + // This condition should be satisfied, since there is a delay on the firm block response + let update_commitment_state_soft_2 = mount_update_commitment_state!( + test_conductor, + mock_name: "update_commitment_state_soft_2", + firm: ( + number: 2, + hash: [2; 64], + parent: [1; 64], + ), + soft: ( + number: 3, + hash: [3; 64], + parent: [2; 64], + ), + base_celestia_height: 1, + expected_calls: 1, + ); + + let update_commitment_state_firm_2 = mount_update_commitment_state!( + test_conductor, + mock_name: "update_commitment_state_firm_2", + firm: ( + number: 3, + hash: [3; 64], + parent: [2; 64], + ), + soft: ( + number: 3, + hash: [3; 64], + parent: [2; 64], + ), + base_celestia_height: 1, + expected_calls: 1, + ); + + timeout( + Duration::from_millis(1000), + join3( + execute_block_2.wait_until_satisfied(), + update_commitment_state_firm_2.wait_until_satisfied(), + update_commitment_state_soft_2.wait_until_satisfied(), + ), + ) + .await + .expect("conductor should have updated the firm commitment state within 1000ms"); +} + +/// Tests if the conductor correctly stops and procedes to restart after firm height reaches +/// sequencer stop height, *without* updating soft commitment state, since the firm was received +/// first. +/// +/// This test consists of the following steps: +/// 1. Mount commitment state and genesis info with a sequencer stop height of 3, only responding up +/// to 1 time so that Conductor will not receive the same response after restart. +/// 2. Mount Celestia network head and sequencer genesis. +/// 3. Mount ABCI info and sequencer blocks (soft blocks) for heights 3 and 4 with a slight delay, +/// to ensure the firm blocks arrive first. +/// 4. Mount firm blocks at heights 3 and 4. +/// 5. Mount `update_commitment_state` for the soft block at height 3, expecting 0 calls since the +/// firm block will be received first. +/// 5. Mount `execute_block` and `update_commitment_state` for firm block at height 3. +/// 6. Await satisfaction of the `execute_block` and `update_commitment_state` for the firm block at +/// height 3 with a timeout of 1000ms. +/// 7. Mount new genesis info with a sequencer stop height of 10 and a rollup start block height of +/// 2, along with corresponding commitment state, reflecting that block 1 has already been +/// executed and the commitment state updated. +/// 8. Mount `execute_block` and `update_commitment_state` for both soft and firm blocks at height 4 +/// and await their satisfaction (the soft mount need not be satisfied in the case that the firm +/// block is received first; we are just looking to see that the conductor restarted properly). +#[expect( + clippy::too_many_lines, + reason = "All lines reasonably necessary for the thoroughness of this test" +)] +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] +async fn restarts_after_reaching_firm_stop_height_first() { + let test_conductor = spawn_conductor(CommitLevel::SoftAndFirm).await; + + mount_get_genesis_info!( + test_conductor, + sequencer_start_height: 3, + celestia_block_variance: 10, + rollup_start_block_number: 2, + rollup_stop_block_number: 2, + up_to_n_times: 1, // We only respond once since this needs to be updated after restart + ); + + mount_get_commitment_state!( + test_conductor, + firm: ( + number: 1, + hash: [1; 64], + parent: [0; 64], + ), + soft: ( + number: 1, + hash: [1; 64], + parent: [0; 64], + ), + base_celestia_height: 1, + up_to_n_times: 1, // We only respond once since this needs to be updated after restart + ); + + mount_sequencer_genesis!(test_conductor); + mount_celestia_header_network_head!( + test_conductor, + height: 1u32, + ); + mount_abci_info!( + test_conductor, + latest_sequencer_height: 4, + ); + + // Mount soft blocks for heights 3 and 4 + mount_get_filtered_sequencer_block!( + test_conductor, + sequencer_height: 3, + delay: Duration::from_millis(200), + ); + mount_get_filtered_sequencer_block!( + test_conductor, + sequencer_height: 4, + ); + + // Mount firm blocks for heights 3 and 4 + mount_celestia_blobs!( + test_conductor, + celestia_height: 1, + sequencer_heights: [3, 4], + ); + mount_sequencer_commit!( + test_conductor, + height: 3u32, + ); + mount_sequencer_commit!( + test_conductor, + height: 4u32, + ); + mount_sequencer_validator_set!(test_conductor, height: 2u32); + mount_sequencer_validator_set!(test_conductor, height: 3u32); + + let execute_block_1 = mount_executed_block!( + test_conductor, + mock_name: "execute_block_1", + number: 2, + hash: [2; 64], + parent: [1; 64], + expected_calls: 1, // This should not be called again after restart + ); + + // Should not be called since the firm block will be received first + let _update_commitment_state_soft_1 = mount_update_commitment_state!( + test_conductor, + mock_name: "update_commitment_state_soft_1", + firm: ( + number: 1, + hash: [1; 64], + parent: [0; 64], + ), + soft: ( + number: 2, + hash: [2; 64], + parent: [1; 64], + ), + base_celestia_height: 1, + expected_calls: 0, + ); + + let update_commitment_state_firm_1 = mount_update_commitment_state!( + test_conductor, + mock_name: "update_commitment_state_firm_1", + firm: ( + number: 2, + hash: [2; 64], + parent: [1; 64], + ), + soft: ( + number: 2, + hash: [2; 64], + parent: [1; 64], + ), + base_celestia_height: 1, + expected_calls: 1, // Should not be called again after restart + ); + + timeout( + Duration::from_millis(1000), + join( + execute_block_1.wait_until_satisfied(), + update_commitment_state_firm_1.wait_until_satisfied(), + ), + ) + .await + .expect("conductor should have updated the firm commitment state within 1000ms"); + + mount_get_genesis_info!( + test_conductor, + sequencer_start_height: 4, + celestia_block_variance: 10, + rollup_start_block_number: 3, + rollup_stop_block_number: 9, + ); + + mount_get_commitment_state!( + test_conductor, + firm: ( + number: 2, + hash: [2; 64], + parent: [1; 64], + ), + soft: ( + number: 2, + hash: [2; 64], + parent: [1; 64], + ), + base_celestia_height: 1, + ); + + let execute_block_2 = mount_executed_block!( + test_conductor, + mock_name: "execute_block_2", + number: 3, + hash: [3; 64], + parent: [2; 64], + expected_calls: 1, + ); + + // This condition does not need to be satisfied, since firm block may fire first after restart + let _update_commitment_state_soft_2 = mount_update_commitment_state!( + test_conductor, + mock_name: "update_commitment_state_soft_2", + firm: ( + number: 2, + hash: [2; 64], + parent: [1; 64], + ), + soft: ( + number: 3, + hash: [3; 64], + parent: [2; 64], + ), + base_celestia_height: 1, + expected_calls: 0..=1, + ); + + let update_commitment_state_firm_2 = mount_update_commitment_state!( + test_conductor, + mock_name: "update_commitment_state_firm_2", + firm: ( + number: 3, + hash: [3; 64], + parent: [2; 64], + ), + soft: ( + number: 3, + hash: [3; 64], + parent: [2; 64], + ), + base_celestia_height: 1, + expected_calls: 1, + ); + + timeout( + Duration::from_millis(1000), + join( + execute_block_2.wait_until_satisfied(), + update_commitment_state_firm_2.wait_until_satisfied(), + ), + ) + .await + .expect("conductor should have updated the firm commitment state within 1000ms"); +} + +/// Tests if the conductor correctly stops and does not restart after reaching the sequencer stop +/// height if genesis info's `halt_at_rollup_stop_number` is `true`. +/// +/// This test consists of the following steps: +/// 1. Mount commitment state and genesis info with a sequencer stop height of 3, expecting only 1 +/// response. +/// 2. Mount Celestia network head and sequencer genesis. +/// 3. Mount ABCI info and sequencer blocks (soft blocks) for height 3. +/// 4. Mount firm blocks at height 3. +/// 5. Mount `execute_block` and `update_commitment_state` for soft and firm blocks at height 3. +/// 6. Await satisfaction of the `execute_block` and `update_commitment_state` for the firm block +/// height 3 with a timeout of 1000ms. The soft mount need not be satisfied in the case that the +/// firm block is received first. The test case of ensuring the soft commitment state is updated +/// correctly in the case of receiving a soft block first is covered in +/// `conductor_restarts_after_reaching_soft_stop_height_first`. +/// 7. Allow ample time for the conductor to potentially restart erroneously. +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] +async fn stops_at_stop_height() { + let test_conductor = spawn_conductor(CommitLevel::SoftAndFirm).await; + + mount_get_genesis_info!( + test_conductor, + sequencer_start_height: 3, + celestia_block_variance: 10, + rollup_start_block_number: 2, + rollup_stop_block_number: 2, + up_to_n_times: 2, // allow for calls after an potential erroneous restart + halt_at_rollup_stop_number: true, + expected_calls: 1, + ); + + mount_get_commitment_state!( + test_conductor, + firm: ( + number: 1, + hash: [1; 64], + parent: [0; 64], + ), + soft: ( + number: 1, + hash: [1; 64], + parent: [0; 64], + ), + base_celestia_height: 1, + ); + + mount_sequencer_genesis!(test_conductor); + mount_celestia_header_network_head!( + test_conductor, + height: 1u32, + ); + mount_abci_info!( + test_conductor, + latest_sequencer_height: 3, + ); + + // Mount soft blocks for height 3 + mount_get_filtered_sequencer_block!( + test_conductor, + sequencer_height: 3, + ); + + // Mount firm blocks for height 3 + mount_celestia_blobs!( + test_conductor, + celestia_height: 1, + sequencer_heights: [3], + ); + mount_sequencer_commit!( + test_conductor, + height: 3u32, + ); + mount_sequencer_validator_set!(test_conductor, height: 2u32); + + let execute_block_1 = mount_executed_block!( + test_conductor, + mock_name: "execute_block_1", + number: 2, + hash: [2; 64], + parent: [1; 64], + ); + + let _update_commitment_state_soft_1 = mount_update_commitment_state!( + test_conductor, + mock_name: "update_commitment_state_soft_1", + firm: ( + number: 1, + hash: [1; 64], + parent: [0; 64], + ), + soft: ( + number: 2, + hash: [2; 64], + parent: [1; 64], + ), + base_celestia_height: 1, + expected_calls: 0..=1, + ); + + let update_commitment_state_firm_1 = mount_update_commitment_state!( + test_conductor, + mock_name: "update_commitment_state_firm_1", + firm: ( + number: 2, + hash: [2; 64], + parent: [1; 64], + ), + soft: ( + number: 2, + hash: [2; 64], + parent: [1; 64], + ), + base_celestia_height: 1, + ); + + timeout( + Duration::from_millis(1000), + join( + execute_block_1.wait_until_satisfied(), + update_commitment_state_firm_1.wait_until_satisfied(), + ), + ) + .await + .expect("conductor should have updated the firm commitment state within 1000ms"); +} diff --git a/crates/astria-conductor/tests/blackbox/soft_only.rs b/crates/astria-conductor/tests/blackbox/soft_only.rs index 8fff4e8c1c..1b51c6e4d9 100644 --- a/crates/astria-conductor/tests/blackbox/soft_only.rs +++ b/crates/astria-conductor/tests/blackbox/soft_only.rs @@ -5,7 +5,7 @@ use astria_conductor::{ Conductor, Config, }; -use astria_core::generated::astria::execution::v1::{ +use astria_core::generated::astria::execution::v2::{ GetCommitmentStateRequest, GetGenesisInfoRequest, }; @@ -41,8 +41,10 @@ async fn simple() { mount_get_genesis_info!( test_conductor, - sequencer_genesis_block_height: 1, + sequencer_start_height: 3, celestia_block_variance: 10, + rollup_start_block_number: 2, + rollup_stop_block_number: 9, ); mount_get_commitment_state!( @@ -114,8 +116,10 @@ async fn submits_two_heights_in_succession() { mount_get_genesis_info!( test_conductor, - sequencer_genesis_block_height: 1, + sequencer_start_height: 3, celestia_block_variance: 10, + rollup_start_block_number: 2, + rollup_stop_block_number: 9, ); mount_get_commitment_state!( @@ -220,8 +224,10 @@ async fn skips_already_executed_heights() { mount_get_genesis_info!( test_conductor, - sequencer_genesis_block_height: 1, + sequencer_start_height: 3, celestia_block_variance: 10, + rollup_start_block_number: 2, + rollup_stop_block_number: 9, ); mount_get_commitment_state!( @@ -293,8 +299,10 @@ async fn requests_from_later_genesis_height() { mount_get_genesis_info!( test_conductor, - sequencer_genesis_block_height: 10, + sequencer_start_height: 12, celestia_block_variance: 10, + rollup_start_block_number: 2, + rollup_stop_block_number: 10, ); mount_get_commitment_state!( @@ -401,8 +409,10 @@ async fn exits_on_sequencer_chain_id_mismatch() { matcher::message_type::(), ) .respond_with(GrpcResponse::constant_response( - genesis_info!(sequencer_genesis_block_height: 1, - celestia_block_variance: 10,), + genesis_info!(sequencer_start_height: 3, + celestia_block_variance: 10, + rollup_start_block_number: 2, + rollup_stop_block_number: 9), )) .expect(0..) .mount(&mock_grpc.mock_server) @@ -452,3 +462,170 @@ async fn exits_on_sequencer_chain_id_mismatch() { } } } + +/// Tests that the conductor correctly stops at the sequencer stop block height in soft only mode, +/// executing the soft block at that height. Then, tests that the conductor correctly restarts +/// and continues executing soft blocks after receiving updated genesis info and commitment state. +/// +/// It consists of the following steps: +/// 1. Mount commitment state and genesis info with a stop height of 3, responding only up to 1 time +/// so that the same information is not retrieved after restarting. +/// 2. Mount sequencer genesis, ABCI info, and sequencer blocks for heights 3 and 4. +/// 3. Mount `execute_block` and `update_commitment_state` mocks for the soft block at height 3, +/// expecting only 1 call and timing out after 1000ms. +/// 4. Mount updated commitment state and genesis info with a stop height of 10 (more than high +/// enough) and a rollup start block height of 2, reflecting that the first block has already +/// been executed. +/// 5. Mount `execute_block` and `update_commitment_state` mocks for the soft block at height 4, +/// awaiting their satisfaction. +#[expect( + clippy::too_many_lines, + reason = "All lines reasonably necessary for the thoroughness of this test" +)] +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] +async fn restarts_after_reaching_stop_block_height() { + let test_conductor = spawn_conductor(CommitLevel::SoftOnly).await; + + mount_get_genesis_info!( + test_conductor, + sequencer_start_height: 3, + celestia_block_variance: 10, + rollup_start_block_number: 2, + rollup_stop_block_number: 2, + up_to_n_times: 1, // We need to mount a new genesis info after restart + ); + + mount_get_commitment_state!( + test_conductor, + firm: ( + number: 1, + hash: [1; 64], + parent: [0; 64], + ), + soft: ( + number: 1, + hash: [1; 64], + parent: [0; 64], + ), + base_celestia_height: 1, + up_to_n_times: 1, // We need to mount a new commitment state after restart + ); + + mount_sequencer_genesis!(test_conductor); + + mount_abci_info!( + test_conductor, + latest_sequencer_height: 4, + ); + + mount_get_filtered_sequencer_block!( + test_conductor, + sequencer_height: 3, + ); + + mount_get_filtered_sequencer_block!( + test_conductor, + sequencer_height: 4, + ); + + let execute_block_1 = mount_executed_block!( + test_conductor, + mock_name: "execute_block_1", + number: 2, + hash: [2; 64], + parent: [1; 64], + expected_calls: 1, + ); + + let update_commitment_state_1 = mount_update_commitment_state!( + test_conductor, + mock_name: "update_commitment_state_1", + firm: ( + number: 1, + hash: [1; 64], + parent: [0; 64], + ), + soft: ( + number: 2, + hash: [2; 64], + parent: [1; 64], + ), + base_celestia_height: 1, + expected_calls: 1, + ); + + timeout( + Duration::from_millis(1000), + join( + execute_block_1.wait_until_satisfied(), + update_commitment_state_1.wait_until_satisfied(), + ), + ) + .await + .expect( + "conductor should have executed the first soft block and updated the first soft \ + commitment state within 1000ms", + ); + + mount_get_genesis_info!( + test_conductor, + sequencer_start_height: 4, + celestia_block_variance: 10, + rollup_start_block_number: 3, + rollup_stop_block_number: 9, + ); + + mount_get_commitment_state!( + test_conductor, + firm: ( + number: 1, + hash: [1; 64], + parent: [0; 64], + ), + soft: ( + number: 2, + hash: [2; 64], + parent: [1; 64], + ), + base_celestia_height: 1, + ); + + let execute_block_2 = mount_executed_block!( + test_conductor, + mock_name: "execute_block_2", + number: 3, + hash: [3; 64], + parent: [2; 64], + expected_calls: 1, + ); + + let update_commitment_state_2 = mount_update_commitment_state!( + test_conductor, + mock_name: "update_commitment_state_2", + firm: ( + number: 1, + hash: [1; 64], + parent: [0; 64], + ), + soft: ( + number: 3, + hash: [3; 64], + parent: [2; 64], + ), + base_celestia_height: 1, + expected_calls: 1, + ); + + timeout( + Duration::from_millis(1000), + join( + execute_block_2.wait_until_satisfied(), + update_commitment_state_2.wait_until_satisfied(), + ), + ) + .await + .expect( + "conductor should have executed the second soft block and updated the second soft \ + commitment state within 1000ms", + ); +} diff --git a/crates/astria-core/src/execution/mod.rs b/crates/astria-core/src/execution/mod.rs index a3a6d96c3f..ae6adc7cf4 100644 --- a/crates/astria-core/src/execution/mod.rs +++ b/crates/astria-core/src/execution/mod.rs @@ -1 +1,2 @@ pub mod v1; +pub mod v2; diff --git a/crates/astria-core/src/execution/v2/mod.rs b/crates/astria-core/src/execution/v2/mod.rs new file mode 100644 index 0000000000..baa694b834 --- /dev/null +++ b/crates/astria-core/src/execution/v2/mod.rs @@ -0,0 +1,545 @@ +use std::num::NonZeroU64; + +use bytes::Bytes; +use pbjson_types::Timestamp; + +use crate::{ + generated::astria::execution::v2 as raw, + primitive::v1::{ + IncorrectRollupIdLength, + RollupId, + }, + Protobuf, +}; + +// An error when transforming a [`raw::GenesisInfo`] into a [`GenesisInfo`]. +#[derive(Debug, thiserror::Error)] +#[error(transparent)] +pub struct GenesisInfoError(GenesisInfoErrorKind); + +impl GenesisInfoError { + fn incorrect_rollup_id_length(inner: IncorrectRollupIdLength) -> Self { + Self(GenesisInfoErrorKind::IncorrectRollupIdLength(inner)) + } + + fn no_rollup_id() -> Self { + Self(GenesisInfoErrorKind::NoRollupId) + } + + fn invalid_sequencer_id(source: tendermint::Error) -> Self { + Self(GenesisInfoErrorKind::InvalidSequencerId(source)) + } + + fn invalid_celestia_id(source: celestia_tendermint::Error) -> Self { + Self(GenesisInfoErrorKind::InvalidCelestiaId(source)) + } +} + +#[derive(Debug, thiserror::Error)] +enum GenesisInfoErrorKind { + #[error("`rollup_id` field contained an invalid rollup ID")] + IncorrectRollupIdLength(IncorrectRollupIdLength), + #[error("`rollup_id` was not set")] + NoRollupId, + #[error("field `sequencer_chain_id` was invalid")] + InvalidSequencerId(#[source] tendermint::Error), + #[error("field `celestia_chain_id` was invalid")] + InvalidCelestiaId(#[source] celestia_tendermint::Error), +} + +/// Genesis Info required from a rollup to start an execution client. +/// +/// Contains information about the rollup id, and base heights for both sequencer & celestia. +/// +/// Usually constructed its [`Protobuf`] implementation from a +/// [`raw::GenesisInfo`]. +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +#[cfg_attr( + feature = "serde", + serde(into = "crate::generated::astria::execution::v2::GenesisInfo") +)] +pub struct GenesisInfo { + /// The rollup id which is used to identify the rollup txs. + rollup_id: RollupId, + /// The Sequencer block height which contains the first block of the rollup. + sequencer_start_height: tendermint::block::Height, + /// The allowed variance in the block height of celestia when looking for sequencer blocks. + celestia_block_variance: u64, + /// The rollup block number to map to the sequencer start block height. + rollup_start_block_number: u64, + /// The rollup block number to restart/halt at after executing. + rollup_stop_block_number: Option, + /// The chain ID of the sequencer network. + sequencer_chain_id: tendermint::chain::Id, + /// The chain ID of the celestia network. + celestia_chain_id: celestia_tendermint::chain::Id, + /// Whether the conductor should halt at the stop height instead of restarting. + halt_at_rollup_stop_number: bool, +} + +impl GenesisInfo { + #[must_use] + pub fn rollup_id(&self) -> RollupId { + self.rollup_id + } + + #[must_use] + pub fn sequencer_start_height(&self) -> u64 { + self.sequencer_start_height.into() + } + + #[must_use] + pub fn celestia_block_variance(&self) -> u64 { + self.celestia_block_variance + } + + #[must_use] + pub fn sequencer_chain_id(&self) -> &str { + self.sequencer_chain_id.as_str() + } + + #[must_use] + pub fn celestia_chain_id(&self) -> &str { + self.celestia_chain_id.as_str() + } + + #[must_use] + pub fn rollup_start_block_number(&self) -> u64 { + self.rollup_start_block_number + } + + #[must_use] + pub fn rollup_stop_block_number(&self) -> Option { + self.rollup_stop_block_number + } + + #[must_use] + pub fn halt_at_rollup_stop_number(&self) -> bool { + self.halt_at_rollup_stop_number + } +} + +impl From for raw::GenesisInfo { + fn from(value: GenesisInfo) -> Self { + value.to_raw() + } +} + +impl Protobuf for GenesisInfo { + type Error = GenesisInfoError; + type Raw = raw::GenesisInfo; + + fn try_from_raw_ref(raw: &Self::Raw) -> Result { + let raw::GenesisInfo { + rollup_id, + sequencer_start_height, + celestia_block_variance, + rollup_start_block_number, + rollup_stop_block_number, + sequencer_chain_id, + celestia_chain_id, + halt_at_rollup_stop_number, + } = raw; + let Some(rollup_id) = rollup_id else { + return Err(Self::Error::no_rollup_id()); + }; + let rollup_id = RollupId::try_from_raw_ref(rollup_id) + .map_err(Self::Error::incorrect_rollup_id_length)?; + let sequencer_chain_id = sequencer_chain_id + .clone() + .try_into() + .map_err(Self::Error::invalid_sequencer_id)?; + let celestia_chain_id = celestia_chain_id + .clone() + .try_into() + .map_err(Self::Error::invalid_celestia_id)?; + + Ok(Self { + rollup_id, + sequencer_start_height: (*sequencer_start_height).into(), + celestia_block_variance: *celestia_block_variance, + rollup_start_block_number: *rollup_start_block_number, + rollup_stop_block_number: NonZeroU64::new(*rollup_stop_block_number), + sequencer_chain_id, + celestia_chain_id, + halt_at_rollup_stop_number: *halt_at_rollup_stop_number, + }) + } + + fn to_raw(&self) -> Self::Raw { + let Self { + rollup_id, + sequencer_start_height, + celestia_block_variance, + rollup_start_block_number, + rollup_stop_block_number, + sequencer_chain_id, + celestia_chain_id, + halt_at_rollup_stop_number, + } = self; + + let sequencer_start_height: u32 = (*sequencer_start_height).value().try_into().expect( + "block height overflow, this should not happen since tendermint heights are i64 under \ + the hood", + ); + + Self::Raw { + rollup_id: Some(rollup_id.to_raw()), + sequencer_start_height, + celestia_block_variance: *celestia_block_variance, + rollup_start_block_number: *rollup_start_block_number, + rollup_stop_block_number: rollup_stop_block_number.map(NonZeroU64::get).unwrap_or(0), + sequencer_chain_id: sequencer_chain_id.to_string(), + celestia_chain_id: celestia_chain_id.to_string(), + halt_at_rollup_stop_number: *halt_at_rollup_stop_number, + } + } +} + +/// An error when transforming a [`raw::Block`] into a [`Block`]. +#[derive(Debug, thiserror::Error)] +#[error(transparent)] +pub struct BlockError(BlockErrorKind); + +impl BlockError { + fn field_not_set(field: &'static str) -> Self { + Self(BlockErrorKind::FieldNotSet(field)) + } +} + +#[derive(Debug, thiserror::Error)] +enum BlockErrorKind { + #[error("{0} field not set")] + FieldNotSet(&'static str), +} + +/// An Astria execution block on a rollup. +/// +/// Contains information about the block number, its hash, +/// its parent block's hash, and timestamp. +/// +/// Usually constructed its [`Protobuf`] implementation from a +/// [`raw::Block`]. +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +#[cfg_attr( + feature = "serde", + serde(into = "crate::generated::astria::execution::v2::Block") +)] +pub struct Block { + /// The block number + number: u32, + /// The hash of the block + hash: Bytes, + /// The hash of the parent block + parent_block_hash: Bytes, + /// Timestamp on the block, standardized to google protobuf standard. + timestamp: Timestamp, +} + +impl Block { + #[must_use] + pub fn number(&self) -> u32 { + self.number + } + + #[must_use] + pub fn hash(&self) -> &Bytes { + &self.hash + } + + #[must_use] + pub fn parent_block_hash(&self) -> &Bytes { + &self.parent_block_hash + } + + #[must_use] + pub fn timestamp(&self) -> Timestamp { + // prost_types::Timestamp is a (i64, i32) tuple, so this is + // effectively just a copy + self.timestamp.clone() + } +} + +impl From for raw::Block { + fn from(value: Block) -> Self { + value.to_raw() + } +} + +impl Protobuf for Block { + type Error = BlockError; + type Raw = raw::Block; + + fn try_from_raw_ref(raw: &Self::Raw) -> Result { + let raw::Block { + number, + hash, + parent_block_hash, + timestamp, + } = raw; + // Cloning timestamp is effectively a copy because timestamp is just a (i32, i64) tuple + let timestamp = timestamp + .clone() + .ok_or(Self::Error::field_not_set(".timestamp"))?; + + Ok(Self { + number: *number, + hash: hash.clone(), + parent_block_hash: parent_block_hash.clone(), + timestamp, + }) + } + + fn to_raw(&self) -> Self::Raw { + let Self { + number, + hash, + parent_block_hash, + timestamp, + } = self; + Self::Raw { + number: *number, + hash: hash.clone(), + parent_block_hash: parent_block_hash.clone(), + // Cloning timestamp is effectively a copy because timestamp is just a (i32, i64) + // tuple + timestamp: Some(timestamp.clone()), + } + } +} + +#[derive(Debug, thiserror::Error)] +#[error(transparent)] +pub struct CommitmentStateError(CommitmentStateErrorKind); + +impl CommitmentStateError { + fn field_not_set(field: &'static str) -> Self { + Self(CommitmentStateErrorKind::FieldNotSet(field)) + } + + fn firm(source: BlockError) -> Self { + Self(CommitmentStateErrorKind::Firm(source)) + } + + fn soft(source: BlockError) -> Self { + Self(CommitmentStateErrorKind::Soft(source)) + } + + fn firm_exceeds_soft(source: FirmExceedsSoft) -> Self { + Self(CommitmentStateErrorKind::FirmExceedsSoft(source)) + } +} + +#[derive(Debug, thiserror::Error)] +enum CommitmentStateErrorKind { + #[error("{0} field not set")] + FieldNotSet(&'static str), + #[error(".firm field did not contain a valid block")] + Firm(#[source] BlockError), + #[error(".soft field did not contain a valid block")] + Soft(#[source] BlockError), + #[error(transparent)] + FirmExceedsSoft(FirmExceedsSoft), +} + +#[derive(Debug, thiserror::Error)] +#[error("firm commitment at `{firm} exceeds soft commitment at `{soft}")] +pub struct FirmExceedsSoft { + firm: u32, + soft: u32, +} + +pub struct NoFirm; +pub struct NoSoft; +pub struct NoBaseCelestiaHeight; +pub struct WithFirm(Block); +pub struct WithSoft(Block); +pub struct WithCelestiaBaseHeight(u64); +#[derive(Default)] +pub struct CommitmentStateBuilder< + TFirm = NoFirm, + TSoft = NoSoft, + TBaseCelestiaHeight = NoBaseCelestiaHeight, +> { + firm: TFirm, + soft: TSoft, + base_celestia_height: TBaseCelestiaHeight, +} + +impl CommitmentStateBuilder { + fn new() -> Self { + Self { + firm: NoFirm, + soft: NoSoft, + base_celestia_height: NoBaseCelestiaHeight, + } + } +} + +impl CommitmentStateBuilder { + pub fn firm(self, firm: Block) -> CommitmentStateBuilder { + let Self { + soft, + base_celestia_height, + .. + } = self; + CommitmentStateBuilder { + firm: WithFirm(firm), + soft, + base_celestia_height, + } + } + + pub fn soft(self, soft: Block) -> CommitmentStateBuilder { + let Self { + firm, + base_celestia_height, + .. + } = self; + CommitmentStateBuilder { + firm, + soft: WithSoft(soft), + base_celestia_height, + } + } + + pub fn base_celestia_height( + self, + base_celestia_height: u64, + ) -> CommitmentStateBuilder { + let Self { + firm, + soft, + .. + } = self; + CommitmentStateBuilder { + firm, + soft, + base_celestia_height: WithCelestiaBaseHeight(base_celestia_height), + } + } +} + +impl CommitmentStateBuilder { + /// Finalize the commitment state. + /// + /// # Errors + /// Returns an error if the firm block exceeds the soft one. + pub fn build(self) -> Result { + let Self { + firm: WithFirm(firm), + soft: WithSoft(soft), + base_celestia_height: WithCelestiaBaseHeight(base_celestia_height), + } = self; + if firm.number() > soft.number() { + return Err(FirmExceedsSoft { + firm: firm.number(), + soft: soft.number(), + }); + } + Ok(CommitmentState { + soft, + firm, + base_celestia_height, + }) + } +} + +/// Information about the [`Block`] at each sequencer commitment level. +/// +/// A commitment state is valid if: +/// - Block numbers are such that soft >= firm (upheld by this type). +/// - No blocks ever decrease in block number. +/// - The chain defined by soft is the head of the canonical chain the firm block must belong to. +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +#[cfg_attr( + feature = "serde", + serde(into = "crate::generated::astria::execution::v2::CommitmentState") +)] +pub struct CommitmentState { + /// Soft commitment is the rollup block matching latest sequencer block. + soft: Block, + /// Firm commitment is achieved when data has been seen in DA. + firm: Block, + /// The base height of celestia from which to search for blocks after this + /// commitment state. + base_celestia_height: u64, +} + +impl CommitmentState { + #[must_use = "a commitment state must be built to be useful"] + pub fn builder() -> CommitmentStateBuilder { + CommitmentStateBuilder::new() + } + + #[must_use] + pub fn firm(&self) -> &Block { + &self.firm + } + + #[must_use] + pub fn soft(&self) -> &Block { + &self.soft + } + + pub fn base_celestia_height(&self) -> u64 { + self.base_celestia_height + } +} + +impl From for raw::CommitmentState { + fn from(value: CommitmentState) -> Self { + value.to_raw() + } +} + +impl Protobuf for CommitmentState { + type Error = CommitmentStateError; + type Raw = raw::CommitmentState; + + fn try_from_raw_ref(raw: &Self::Raw) -> Result { + let Self::Raw { + soft, + firm, + base_celestia_height, + } = raw; + let soft = 'soft: { + let Some(soft) = soft else { + break 'soft Err(Self::Error::field_not_set(".soft")); + }; + Block::try_from_raw_ref(soft).map_err(Self::Error::soft) + }?; + let firm = 'firm: { + let Some(firm) = firm else { + break 'firm Err(Self::Error::field_not_set(".firm")); + }; + Block::try_from_raw_ref(firm).map_err(Self::Error::firm) + }?; + + Self::builder() + .firm(firm) + .soft(soft) + .base_celestia_height(*base_celestia_height) + .build() + .map_err(Self::Error::firm_exceeds_soft) + } + + fn to_raw(&self) -> Self::Raw { + let Self { + soft, + firm, + base_celestia_height, + } = self; + let soft = soft.to_raw(); + let firm = firm.to_raw(); + let base_celestia_height = *base_celestia_height; + Self::Raw { + soft: Some(soft), + firm: Some(firm), + base_celestia_height, + } + } +} diff --git a/crates/astria-core/src/generated/astria.bundle.v1alpha1.rs b/crates/astria-core/src/generated/astria.bundle.v1alpha1.rs new file mode 100644 index 0000000000..684ba5f9fa --- /dev/null +++ b/crates/astria-core/src/generated/astria.bundle.v1alpha1.rs @@ -0,0 +1,762 @@ +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetBundleStreamRequest {} +impl ::prost::Name for GetBundleStreamRequest { + const NAME: &'static str = "GetBundleStreamRequest"; + const PACKAGE: &'static str = "astria.bundle.v1alpha1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("astria.bundle.v1alpha1.{}", Self::NAME) + } +} +/// Information for the bundle submitter to know how to submit the bundle. +/// The fee and base_sequencer_block_hash are not necessarily strictly necessary +/// it allows for the case where the server doesn't always send the highest fee +/// bundles after the previous but could just stream any confirmed bundles. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Bundle { + /// The fee that can be expected to be received for submitting this bundle. + /// This allows the bundle producer to stream any confirmed bundles they would be ok + /// with submitting. Used to avoid race conditions in received bundle packets. Could + /// also be used by a bundle submitter to allow multiple entities to submit bundles. + #[prost(uint64, tag = "1")] + pub fee: u64, + /// The byte list of transactions to be included. + #[prost(bytes = "bytes", repeated, tag = "2")] + pub transactions: ::prost::alloc::vec::Vec<::prost::bytes::Bytes>, + /// The base_sequencer_block_hash is the hash from the base block this bundle + /// is based on. This is used to verify that the bundle is based on the correct + /// Sequencer block. + #[prost(bytes = "bytes", tag = "3")] + pub base_sequencer_block_hash: ::prost::bytes::Bytes, + /// The hash of previous rollup block, on top of which the bundle will be executed as ToB. + #[prost(bytes = "bytes", tag = "4")] + pub prev_rollup_block_hash: ::prost::bytes::Bytes, +} +impl ::prost::Name for Bundle { + const NAME: &'static str = "Bundle"; + const PACKAGE: &'static str = "astria.bundle.v1alpha1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("astria.bundle.v1alpha1.{}", Self::NAME) + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetBundleStreamResponse { + #[prost(message, optional, tag = "1")] + pub bundle: ::core::option::Option, +} +impl ::prost::Name for GetBundleStreamResponse { + const NAME: &'static str = "GetBundleStreamResponse"; + const PACKAGE: &'static str = "astria.bundle.v1alpha1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("astria.bundle.v1alpha1.{}", Self::NAME) + } +} +/// Generated client implementations. +#[cfg(feature = "client")] +pub mod bundle_service_client { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + use tonic::codegen::http::Uri; + #[derive(Debug, Clone)] + pub struct BundleServiceClient { + inner: tonic::client::Grpc, + } + impl BundleServiceClient { + /// Attempt to create a new client by connecting to a given endpoint. + pub async fn connect(dst: D) -> Result + where + D: TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl BundleServiceClient + where + T: tonic::client::GrpcService, + T::Error: Into, + T::ResponseBody: Body + Send + 'static, + ::Error: Into + Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_origin(inner: T, origin: Uri) -> Self { + let inner = tonic::client::Grpc::with_origin(inner, origin); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> BundleServiceClient> + where + F: tonic::service::Interceptor, + T::ResponseBody: Default, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + , + >>::Error: Into + Send + Sync, + { + BundleServiceClient::new(InterceptedService::new(inner, interceptor)) + } + /// Compress requests with the given encoding. + /// + /// This requires the server to support it otherwise it might respond with an + /// error. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.send_compressed(encoding); + self + } + /// Enable decompressing responses. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.accept_compressed(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_decoding_message_size(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_encoding_message_size(limit); + self + } + /// A bundle submitter requests bundles given a new optimistic Sequencer block, + /// and receives a stream of potential bundles for submission, until either a timeout + /// or the connection is closed by the client. + pub async fn get_bundle_stream( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response>, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/astria.bundle.v1alpha1.BundleService/GetBundleStream", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "astria.bundle.v1alpha1.BundleService", + "GetBundleStream", + ), + ); + self.inner.server_streaming(req, path, codec).await + } + } +} +/// Generated server implementations. +#[cfg(feature = "server")] +pub mod bundle_service_server { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + /// Generated trait containing gRPC methods that should be implemented for use with BundleServiceServer. + #[async_trait] + pub trait BundleService: Send + Sync + 'static { + /// Server streaming response type for the GetBundleStream method. + type GetBundleStreamStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result, + > + + Send + + 'static; + /// A bundle submitter requests bundles given a new optimistic Sequencer block, + /// and receives a stream of potential bundles for submission, until either a timeout + /// or the connection is closed by the client. + async fn get_bundle_stream( + self: std::sync::Arc, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + } + #[derive(Debug)] + pub struct BundleServiceServer { + inner: _Inner, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, + } + struct _Inner(Arc); + impl BundleServiceServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + let inner = _Inner(inner); + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.max_decoding_message_size = Some(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.max_encoding_message_size = Some(limit); + self + } + } + impl tonic::codegen::Service> for BundleServiceServer + where + T: BundleService, + B: Body + Send + 'static, + B::Error: Into + Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + let inner = self.inner.clone(); + match req.uri().path() { + "/astria.bundle.v1alpha1.BundleService/GetBundleStream" => { + #[allow(non_camel_case_types)] + struct GetBundleStreamSvc(pub Arc); + impl< + T: BundleService, + > tonic::server::ServerStreamingService< + super::GetBundleStreamRequest, + > for GetBundleStreamSvc { + type Response = super::GetBundleStreamResponse; + type ResponseStream = T::GetBundleStreamStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_bundle_stream(inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetBundleStreamSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.server_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + _ => { + Box::pin(async move { + Ok( + http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap(), + ) + }) + } + } + } + } + impl Clone for BundleServiceServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + max_decoding_message_size: self.max_decoding_message_size, + max_encoding_message_size: self.max_encoding_message_size, + } + } + } + impl Clone for _Inner { + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } + } + impl std::fmt::Debug for _Inner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } + } + impl tonic::server::NamedService for BundleServiceServer { + const NAME: &'static str = "astria.bundle.v1alpha1.BundleService"; + } +} +/// The "BaseBlock" is the information needed to simulate bundles on top of +/// a Sequencer block which may not have been committed yet. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BaseBlock { + /// This is the block hash for the proposed block. + #[prost(bytes = "bytes", tag = "1")] + pub sequencer_block_hash: ::prost::bytes::Bytes, + /// List of transactions to include in the new block. + #[prost(message, repeated, tag = "2")] + pub transactions: ::prost::alloc::vec::Vec< + super::super::sequencerblock::v1::RollupData, + >, + /// Timestamp to be used for new block. + #[prost(message, optional, tag = "3")] + pub timestamp: ::core::option::Option<::pbjson_types::Timestamp>, +} +impl ::prost::Name for BaseBlock { + const NAME: &'static str = "BaseBlock"; + const PACKAGE: &'static str = "astria.bundle.v1alpha1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("astria.bundle.v1alpha1.{}", Self::NAME) + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ExecuteOptimisticBlockStreamRequest { + #[prost(message, optional, tag = "1")] + pub base_block: ::core::option::Option, +} +impl ::prost::Name for ExecuteOptimisticBlockStreamRequest { + const NAME: &'static str = "ExecuteOptimisticBlockStreamRequest"; + const PACKAGE: &'static str = "astria.bundle.v1alpha1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("astria.bundle.v1alpha1.{}", Self::NAME) + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ExecuteOptimisticBlockStreamResponse { + /// Metadata identifying the block resulting from executing a block. Includes number, hash, + /// parent hash and timestamp. + #[prost(message, optional, tag = "1")] + pub block: ::core::option::Option, + /// The base_sequencer_block_hash is the hash from the base sequencer block this block + /// is based on. This is used to associate an optimistic execution result with the hash + /// received once a sequencer block is committed. + #[prost(bytes = "bytes", tag = "2")] + pub base_sequencer_block_hash: ::prost::bytes::Bytes, +} +impl ::prost::Name for ExecuteOptimisticBlockStreamResponse { + const NAME: &'static str = "ExecuteOptimisticBlockStreamResponse"; + const PACKAGE: &'static str = "astria.bundle.v1alpha1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("astria.bundle.v1alpha1.{}", Self::NAME) + } +} +/// Generated client implementations. +#[cfg(feature = "client")] +pub mod optimistic_execution_service_client { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + use tonic::codegen::http::Uri; + #[derive(Debug, Clone)] + pub struct OptimisticExecutionServiceClient { + inner: tonic::client::Grpc, + } + impl OptimisticExecutionServiceClient { + /// Attempt to create a new client by connecting to a given endpoint. + pub async fn connect(dst: D) -> Result + where + D: TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl OptimisticExecutionServiceClient + where + T: tonic::client::GrpcService, + T::Error: Into, + T::ResponseBody: Body + Send + 'static, + ::Error: Into + Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_origin(inner: T, origin: Uri) -> Self { + let inner = tonic::client::Grpc::with_origin(inner, origin); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> OptimisticExecutionServiceClient> + where + F: tonic::service::Interceptor, + T::ResponseBody: Default, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + , + >>::Error: Into + Send + Sync, + { + OptimisticExecutionServiceClient::new( + InterceptedService::new(inner, interceptor), + ) + } + /// Compress requests with the given encoding. + /// + /// This requires the server to support it otherwise it might respond with an + /// error. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.send_compressed(encoding); + self + } + /// Enable decompressing responses. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.accept_compressed(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_decoding_message_size(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_encoding_message_size(limit); + self + } + /// Stream blocks from the Auctioneer to Geth for optimistic execution. Geth will stream back + /// metadata from the executed blocks. + pub async fn execute_optimistic_block_stream( + &mut self, + request: impl tonic::IntoStreamingRequest< + Message = super::ExecuteOptimisticBlockStreamRequest, + >, + ) -> std::result::Result< + tonic::Response< + tonic::codec::Streaming, + >, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/astria.bundle.v1alpha1.OptimisticExecutionService/ExecuteOptimisticBlockStream", + ); + let mut req = request.into_streaming_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "astria.bundle.v1alpha1.OptimisticExecutionService", + "ExecuteOptimisticBlockStream", + ), + ); + self.inner.streaming(req, path, codec).await + } + } +} +/// Generated server implementations. +#[cfg(feature = "server")] +pub mod optimistic_execution_service_server { + #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] + use tonic::codegen::*; + /// Generated trait containing gRPC methods that should be implemented for use with OptimisticExecutionServiceServer. + #[async_trait] + pub trait OptimisticExecutionService: Send + Sync + 'static { + /// Server streaming response type for the ExecuteOptimisticBlockStream method. + type ExecuteOptimisticBlockStreamStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result< + super::ExecuteOptimisticBlockStreamResponse, + tonic::Status, + >, + > + + Send + + 'static; + /// Stream blocks from the Auctioneer to Geth for optimistic execution. Geth will stream back + /// metadata from the executed blocks. + async fn execute_optimistic_block_stream( + self: std::sync::Arc, + request: tonic::Request< + tonic::Streaming, + >, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + } + #[derive(Debug)] + pub struct OptimisticExecutionServiceServer { + inner: _Inner, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, + } + struct _Inner(Arc); + impl OptimisticExecutionServiceServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + let inner = _Inner(inner); + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.max_decoding_message_size = Some(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.max_encoding_message_size = Some(limit); + self + } + } + impl tonic::codegen::Service> + for OptimisticExecutionServiceServer + where + T: OptimisticExecutionService, + B: Body + Send + 'static, + B::Error: Into + Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + let inner = self.inner.clone(); + match req.uri().path() { + "/astria.bundle.v1alpha1.OptimisticExecutionService/ExecuteOptimisticBlockStream" => { + #[allow(non_camel_case_types)] + struct ExecuteOptimisticBlockStreamSvc< + T: OptimisticExecutionService, + >( + pub Arc, + ); + impl< + T: OptimisticExecutionService, + > tonic::server::StreamingService< + super::ExecuteOptimisticBlockStreamRequest, + > for ExecuteOptimisticBlockStreamSvc { + type Response = super::ExecuteOptimisticBlockStreamResponse; + type ResponseStream = T::ExecuteOptimisticBlockStreamStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request< + tonic::Streaming, + >, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::execute_optimistic_block_stream( + inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = ExecuteOptimisticBlockStreamSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + _ => { + Box::pin(async move { + Ok( + http::Response::builder() + .status(200) + .header("grpc-status", "12") + .header("content-type", "application/grpc") + .body(empty_body()) + .unwrap(), + ) + }) + } + } + } + } + impl Clone for OptimisticExecutionServiceServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + max_decoding_message_size: self.max_decoding_message_size, + max_encoding_message_size: self.max_encoding_message_size, + } + } + } + impl Clone for _Inner { + fn clone(&self) -> Self { + Self(Arc::clone(&self.0)) + } + } + impl std::fmt::Debug for _Inner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } + } + impl tonic::server::NamedService + for OptimisticExecutionServiceServer { + const NAME: &'static str = "astria.bundle.v1alpha1.OptimisticExecutionService"; + } +} diff --git a/crates/astria-core/src/generated/astria.execution.v2.rs b/crates/astria-core/src/generated/astria.execution.v2.rs index 184c751a96..18ee796137 100644 --- a/crates/astria-core/src/generated/astria.execution.v2.rs +++ b/crates/astria-core/src/generated/astria.execution.v2.rs @@ -1,7 +1,56 @@ +<<<<<<< HEAD +======= +/// GenesisInfo contains the information needed to start a rollup chain. +/// +/// This information is used to determine which sequencer & celestia data to +/// use from the Astria & Celestia networks. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GenesisInfo { + /// The rollup_id is the unique identifier for the rollup chain. + #[prost(message, optional, tag = "1")] + pub rollup_id: ::core::option::Option, + /// The first block height of sequencer chain to use for rollup transactions. + #[prost(uint32, tag = "2")] + pub sequencer_start_height: u32, + /// The allowed variance in celestia for sequencer blocks to have been posted. + #[prost(uint64, tag = "4")] + pub celestia_block_variance: u64, + /// The rollup block number to map to the sequencer start block height. + #[prost(uint64, tag = "5")] + pub rollup_start_block_number: u64, + /// The rollup block number to re-fetch the genesis info and continue executing with new data. + #[prost(uint64, tag = "6")] + pub rollup_stop_block_number: u64, + /// The ID of the Astria Sequencer network to retrieve Sequencer blocks from. + /// Conductor implementations should verify that the Sequencer network they are connected to + /// have this chain ID (if fetching soft Sequencer blocks), and verify that the Sequencer metadata + /// blobs retrieved from Celestia contain this chain ID (if extracting firm Sequencer blocks from + /// Celestia blobs). + #[prost(string, tag = "7")] + pub sequencer_chain_id: ::prost::alloc::string::String, + /// The ID of the Celestia network to retrieve blobs from. + /// Conductor implementations should verify that the Celestia network they are connected to have + /// this chain ID (if extracting firm Sequencer blocks from Celestia blobs). + #[prost(string, tag = "8")] + pub celestia_chain_id: ::prost::alloc::string::String, + /// Requests Conductor to halt at the stop number instead of re-fetching the genesis and continuing execution. + #[prost(bool, tag = "9")] + pub halt_at_rollup_stop_number: bool, +} +impl ::prost::Name for GenesisInfo { + const NAME: &'static str = "GenesisInfo"; + const PACKAGE: &'static str = "astria.execution.v2"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("astria.execution.v2.{}", Self::NAME) + } +} +>>>>>>> superfluffy/forma-restart-logic /// The set of information which deterministic driver of block production /// must know about a given rollup Block #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] +<<<<<<< HEAD pub struct ExecutedBlockMetadata { /// The block number #[prost(uint64, tag = "1")] @@ -20,6 +69,129 @@ pub struct ExecutedBlockMetadata { } impl ::prost::Name for ExecutedBlockMetadata { const NAME: &'static str = "ExecutedBlockMetadata"; +======= +pub struct Block { + /// The block number + #[prost(uint32, tag = "1")] + pub number: u32, + /// The hash of the block + #[prost(bytes = "bytes", tag = "2")] + pub hash: ::prost::bytes::Bytes, + /// The hash from the parent block + #[prost(bytes = "bytes", tag = "3")] + pub parent_block_hash: ::prost::bytes::Bytes, + /// Timestamp on the block, standardized to google protobuf standard. + #[prost(message, optional, tag = "4")] + pub timestamp: ::core::option::Option<::pbjson_types::Timestamp>, +} +impl ::prost::Name for Block { + const NAME: &'static str = "Block"; + const PACKAGE: &'static str = "astria.execution.v2"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("astria.execution.v2.{}", Self::NAME) + } +} +/// Fields which are indexed for finding blocks on a blockchain. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BlockIdentifier { + #[prost(oneof = "block_identifier::Identifier", tags = "1, 2")] + pub identifier: ::core::option::Option, +} +/// Nested message and enum types in `BlockIdentifier`. +pub mod block_identifier { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Identifier { + #[prost(uint32, tag = "1")] + BlockNumber(u32), + #[prost(bytes, tag = "2")] + BlockHash(::prost::bytes::Bytes), + } +} +impl ::prost::Name for BlockIdentifier { + const NAME: &'static str = "BlockIdentifier"; + const PACKAGE: &'static str = "astria.execution.v2"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("astria.execution.v2.{}", Self::NAME) + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetGenesisInfoRequest {} +impl ::prost::Name for GetGenesisInfoRequest { + const NAME: &'static str = "GetGenesisInfoRequest"; + const PACKAGE: &'static str = "astria.execution.v2"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("astria.execution.v2.{}", Self::NAME) + } +} +/// Used in GetBlock to find a single block. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetBlockRequest { + #[prost(message, optional, tag = "1")] + pub identifier: ::core::option::Option, +} +impl ::prost::Name for GetBlockRequest { + const NAME: &'static str = "GetBlockRequest"; + const PACKAGE: &'static str = "astria.execution.v2"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("astria.execution.v2.{}", Self::NAME) + } +} +/// Used in BatchGetBlocks, will find all or none based on the list of +/// identifiers. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BatchGetBlocksRequest { + #[prost(message, repeated, tag = "1")] + pub identifiers: ::prost::alloc::vec::Vec, +} +impl ::prost::Name for BatchGetBlocksRequest { + const NAME: &'static str = "BatchGetBlocksRequest"; + const PACKAGE: &'static str = "astria.execution.v2"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("astria.execution.v2.{}", Self::NAME) + } +} +/// The list of blocks in response to BatchGetBlocks. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BatchGetBlocksResponse { + #[prost(message, repeated, tag = "1")] + pub blocks: ::prost::alloc::vec::Vec, +} +impl ::prost::Name for BatchGetBlocksResponse { + const NAME: &'static str = "BatchGetBlocksResponse"; + const PACKAGE: &'static str = "astria.execution.v2"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("astria.execution.v2.{}", Self::NAME) + } +} +/// ExecuteBlockRequest contains all the information needed to create a new rollup +/// block. +/// +/// This information comes from previous rollup blocks, as well as from sequencer +/// blocks. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ExecuteBlockRequest { + /// The hash of previous block, which new block will be created on top of. + #[prost(bytes = "bytes", tag = "1")] + pub prev_block_hash: ::prost::bytes::Bytes, + /// List of transactions to include in the new block. + #[prost(message, repeated, tag = "2")] + pub transactions: ::prost::alloc::vec::Vec< + super::super::sequencerblock::v1::RollupData, + >, + /// Timestamp to be used for new block. + #[prost(message, optional, tag = "3")] + pub timestamp: ::core::option::Option<::pbjson_types::Timestamp>, +} +impl ::prost::Name for ExecuteBlockRequest { + const NAME: &'static str = "ExecuteBlockRequest"; +>>>>>>> superfluffy/forma-restart-logic const PACKAGE: &'static str = "astria.execution.v2"; fn full_name() -> ::prost::alloc::string::String { ::prost::alloc::format!("astria.execution.v2.{}", Self::NAME) @@ -36,6 +208,7 @@ impl ::prost::Name for ExecutedBlockMetadata { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct CommitmentState { +<<<<<<< HEAD /// Soft committed block metadata derived directly from an Astria sequencer block. #[prost(message, optional, tag = "1")] pub soft_executed_block_metadata: ::core::option::Option, @@ -49,6 +222,17 @@ pub struct CommitmentState { /// client will not need to search from Celestia genesis. #[prost(uint64, tag = "3")] pub lowest_celestia_search_height: u64, +======= + /// Soft commitment is the rollup block matching latest sequencer block. + #[prost(message, optional, tag = "1")] + pub soft: ::core::option::Option, + /// Firm commitment is achieved when data has been seen in DA. + #[prost(message, optional, tag = "2")] + pub firm: ::core::option::Option, + /// The lowest block number of celestia chain to be searched for rollup blocks given current state + #[prost(uint64, tag = "3")] + pub base_celestia_height: u64, +>>>>>>> superfluffy/forma-restart-logic } impl ::prost::Name for CommitmentState { const NAME: &'static str = "CommitmentState"; @@ -57,6 +241,7 @@ impl ::prost::Name for CommitmentState { ::prost::alloc::format!("astria.execution.v2.{}", Self::NAME) } } +<<<<<<< HEAD /// CreateExecutionSessionRequest is used to create a new execution session on the /// rollup. #[allow(clippy::derive_partial_eq_without_eq)] @@ -236,6 +421,14 @@ pub struct GetExecutedBlockMetadataRequest { } impl ::prost::Name for GetExecutedBlockMetadataRequest { const NAME: &'static str = "GetExecutedBlockMetadataRequest"; +======= +/// There is only one CommitmentState object, so the request is empty. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetCommitmentStateRequest {} +impl ::prost::Name for GetCommitmentStateRequest { + const NAME: &'static str = "GetCommitmentStateRequest"; +>>>>>>> superfluffy/forma-restart-logic const PACKAGE: &'static str = "astria.execution.v2"; fn full_name() -> ::prost::alloc::string::String { ::prost::alloc::format!("astria.execution.v2.{}", Self::NAME) @@ -245,11 +438,15 @@ impl ::prost::Name for GetExecutedBlockMetadataRequest { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct UpdateCommitmentStateRequest { +<<<<<<< HEAD /// The session which the commitment state is being updated within. #[prost(string, tag = "1")] pub session_id: ::prost::alloc::string::String, /// The new commitment state to set. #[prost(message, optional, tag = "2")] +======= + #[prost(message, optional, tag = "1")] +>>>>>>> superfluffy/forma-restart-logic pub commitment_state: ::core::option::Option, } impl ::prost::Name for UpdateCommitmentStateRequest { @@ -350,6 +547,7 @@ pub mod execution_service_client { self.inner = self.inner.max_encoding_message_size(limit); self } +<<<<<<< HEAD /// CreateExecutionSession returns the necessary information for mapping sequencer block /// height to rollup block number. pub async fn create_execution_session( @@ -359,6 +557,13 @@ pub mod execution_service_client { tonic::Response, tonic::Status, > { +======= + /// GetGenesisInfo returns the necessary genesis information for rollup chain. + pub async fn get_genesis_info( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result, tonic::Status> { +>>>>>>> superfluffy/forma-restart-logic self.inner .ready() .await @@ -370,24 +575,67 @@ pub mod execution_service_client { })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( +<<<<<<< HEAD "/astria.execution.v2.ExecutionService/CreateExecutionSession", +======= + "/astria.execution.v2.ExecutionService/GetGenesisInfo", +>>>>>>> superfluffy/forma-restart-logic ); let mut req = request.into_request(); req.extensions_mut() .insert( GrpcMethod::new( "astria.execution.v2.ExecutionService", +<<<<<<< HEAD "CreateExecutionSession", +======= + "GetGenesisInfo", +>>>>>>> superfluffy/forma-restart-logic ), ); self.inner.unary(req, path, codec).await } +<<<<<<< HEAD /// GetExecutedBlockMetadata will return a block given an identifier. pub async fn get_executed_block_metadata( &mut self, request: impl tonic::IntoRequest, ) -> std::result::Result< tonic::Response, +======= + /// GetBlock will return a block given an identifier. + pub async fn get_block( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result, tonic::Status> { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/astria.execution.v2.ExecutionService/GetBlock", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new("astria.execution.v2.ExecutionService", "GetBlock"), + ); + self.inner.unary(req, path, codec).await + } + /// BatchGetBlocks will return an array of Blocks given an array of block + /// identifiers. + pub async fn batch_get_blocks( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, +>>>>>>> superfluffy/forma-restart-logic tonic::Status, > { self.inner @@ -401,14 +649,22 @@ pub mod execution_service_client { })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( +<<<<<<< HEAD "/astria.execution.v2.ExecutionService/GetExecutedBlockMetadata", +======= + "/astria.execution.v2.ExecutionService/BatchGetBlocks", +>>>>>>> superfluffy/forma-restart-logic ); let mut req = request.into_request(); req.extensions_mut() .insert( GrpcMethod::new( "astria.execution.v2.ExecutionService", +<<<<<<< HEAD "GetExecutedBlockMetadata", +======= + "BatchGetBlocks", +>>>>>>> superfluffy/forma-restart-logic ), ); self.inner.unary(req, path, codec).await @@ -418,10 +674,14 @@ pub mod execution_service_client { pub async fn execute_block( &mut self, request: impl tonic::IntoRequest, +<<<<<<< HEAD ) -> std::result::Result< tonic::Response, tonic::Status, > { +======= + ) -> std::result::Result, tonic::Status> { +>>>>>>> superfluffy/forma-restart-logic self.inner .ready() .await @@ -445,6 +705,40 @@ pub mod execution_service_client { ); self.inner.unary(req, path, codec).await } +<<<<<<< HEAD +======= + /// GetCommitmentState fetches the current CommitmentState of the chain. + pub async fn get_commitment_state( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::new( + tonic::Code::Unknown, + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/astria.execution.v2.ExecutionService/GetCommitmentState", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "astria.execution.v2.ExecutionService", + "GetCommitmentState", + ), + ); + self.inner.unary(req, path, codec).await + } +>>>>>>> superfluffy/forma-restart-logic /// UpdateCommitmentState replaces the whole CommitmentState with a new /// CommitmentState. pub async fn update_commitment_state( @@ -487,6 +781,7 @@ pub mod execution_service_server { /// Generated trait containing gRPC methods that should be implemented for use with ExecutionServiceServer. #[async_trait] pub trait ExecutionService: Send + Sync + 'static { +<<<<<<< HEAD /// CreateExecutionSession returns the necessary information for mapping sequencer block /// height to rollup block number. async fn create_execution_session( @@ -502,6 +797,25 @@ pub mod execution_service_server { request: tonic::Request, ) -> std::result::Result< tonic::Response, +======= + /// GetGenesisInfo returns the necessary genesis information for rollup chain. + async fn get_genesis_info( + self: std::sync::Arc, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; + /// GetBlock will return a block given an identifier. + async fn get_block( + self: std::sync::Arc, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; + /// BatchGetBlocks will return an array of Blocks given an array of block + /// identifiers. + async fn batch_get_blocks( + self: std::sync::Arc, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, +>>>>>>> superfluffy/forma-restart-logic tonic::Status, >; /// ExecuteBlock is called to deterministically derive a rollup block from @@ -509,10 +823,19 @@ pub mod execution_service_server { async fn execute_block( self: std::sync::Arc, request: tonic::Request, +<<<<<<< HEAD ) -> std::result::Result< tonic::Response, tonic::Status, >; +======= + ) -> std::result::Result, tonic::Status>; + /// GetCommitmentState fetches the current CommitmentState of the chain. + async fn get_commitment_state( + self: std::sync::Arc, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; +>>>>>>> superfluffy/forma-restart-logic /// UpdateCommitmentState replaces the whole CommitmentState with a new /// CommitmentState. async fn update_commitment_state( @@ -604,6 +927,7 @@ pub mod execution_service_server { fn call(&mut self, req: http::Request) -> Self::Future { let inner = self.inner.clone(); match req.uri().path() { +<<<<<<< HEAD "/astria.execution.v2.ExecutionService/CreateExecutionSession" => { #[allow(non_camel_case_types)] struct CreateExecutionSessionSvc(pub Arc); @@ -612,12 +936,23 @@ pub mod execution_service_server { > tonic::server::UnaryService for CreateExecutionSessionSvc { type Response = super::ExecutionSession; +======= + "/astria.execution.v2.ExecutionService/GetGenesisInfo" => { + #[allow(non_camel_case_types)] + struct GetGenesisInfoSvc(pub Arc); + impl< + T: ExecutionService, + > tonic::server::UnaryService + for GetGenesisInfoSvc { + type Response = super::GenesisInfo; +>>>>>>> superfluffy/forma-restart-logic type Future = BoxFuture< tonic::Response, tonic::Status, >; fn call( &mut self, +<<<<<<< HEAD request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); @@ -626,6 +961,13 @@ pub mod execution_service_server { inner, request, ) +======= + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_genesis_info(inner, request) +>>>>>>> superfluffy/forma-restart-logic .await }; Box::pin(fut) @@ -638,7 +980,11 @@ pub mod execution_service_server { let inner = self.inner.clone(); let fut = async move { let inner = inner.0; +<<<<<<< HEAD let method = CreateExecutionSessionSvc(inner); +======= + let method = GetGenesisInfoSvc(inner); +>>>>>>> superfluffy/forma-restart-logic let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( @@ -654,6 +1000,7 @@ pub mod execution_service_server { }; Box::pin(fut) } +<<<<<<< HEAD "/astria.execution.v2.ExecutionService/GetExecutedBlockMetadata" => { #[allow(non_camel_case_types)] struct GetExecutedBlockMetadataSvc(pub Arc); @@ -662,12 +1009,23 @@ pub mod execution_service_server { > tonic::server::UnaryService for GetExecutedBlockMetadataSvc { type Response = super::ExecutedBlockMetadata; +======= + "/astria.execution.v2.ExecutionService/GetBlock" => { + #[allow(non_camel_case_types)] + struct GetBlockSvc(pub Arc); + impl< + T: ExecutionService, + > tonic::server::UnaryService + for GetBlockSvc { + type Response = super::Block; +>>>>>>> superfluffy/forma-restart-logic type Future = BoxFuture< tonic::Response, tonic::Status, >; fn call( &mut self, +<<<<<<< HEAD request: tonic::Request< super::GetExecutedBlockMetadataRequest, >, @@ -678,6 +1036,59 @@ pub mod execution_service_server { inner, request, ) +======= + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_block(inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetBlockSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/astria.execution.v2.ExecutionService/BatchGetBlocks" => { + #[allow(non_camel_case_types)] + struct BatchGetBlocksSvc(pub Arc); + impl< + T: ExecutionService, + > tonic::server::UnaryService + for BatchGetBlocksSvc { + type Response = super::BatchGetBlocksResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::batch_get_blocks(inner, request) +>>>>>>> superfluffy/forma-restart-logic .await }; Box::pin(fut) @@ -690,7 +1101,11 @@ pub mod execution_service_server { let inner = self.inner.clone(); let fut = async move { let inner = inner.0; +<<<<<<< HEAD let method = GetExecutedBlockMetadataSvc(inner); +======= + let method = BatchGetBlocksSvc(inner); +>>>>>>> superfluffy/forma-restart-logic let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( @@ -713,7 +1128,11 @@ pub mod execution_service_server { T: ExecutionService, > tonic::server::UnaryService for ExecuteBlockSvc { +<<<<<<< HEAD type Response = super::ExecuteBlockResponse; +======= + type Response = super::Block; +>>>>>>> superfluffy/forma-restart-logic type Future = BoxFuture< tonic::Response, tonic::Status, @@ -752,6 +1171,59 @@ pub mod execution_service_server { }; Box::pin(fut) } +<<<<<<< HEAD +======= + "/astria.execution.v2.ExecutionService/GetCommitmentState" => { + #[allow(non_camel_case_types)] + struct GetCommitmentStateSvc(pub Arc); + impl< + T: ExecutionService, + > tonic::server::UnaryService + for GetCommitmentStateSvc { + type Response = super::CommitmentState; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_commitment_state( + inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = GetCommitmentStateSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } +>>>>>>> superfluffy/forma-restart-logic "/astria.execution.v2.ExecutionService/UpdateCommitmentState" => { #[allow(non_camel_case_types)] struct UpdateCommitmentStateSvc(pub Arc); diff --git a/crates/astria-core/src/generated/astria.execution.v2.serde.rs b/crates/astria-core/src/generated/astria.execution.v2.serde.rs index 5329d0d55e..c9f78f0bc1 100644 --- a/crates/astria-core/src/generated/astria.execution.v2.serde.rs +++ b/crates/astria-core/src/generated/astria.execution.v2.serde.rs @@ -1,4 +1,8 @@ +<<<<<<< HEAD impl serde::Serialize for CommitmentState { +======= +impl serde::Serialize for BatchGetBlocksRequest { +>>>>>>> superfluffy/forma-restart-logic #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -6,6 +10,7 @@ impl serde::Serialize for CommitmentState { { use serde::ser::SerializeStruct; let mut len = 0; +<<<<<<< HEAD if self.soft_executed_block_metadata.is_some() { len += 1; } @@ -25,30 +30,50 @@ impl serde::Serialize for CommitmentState { if self.lowest_celestia_search_height != 0 { #[allow(clippy::needless_borrow)] struct_ser.serialize_field("lowestCelestiaSearchHeight", ToString::to_string(&self.lowest_celestia_search_height).as_str())?; +======= + if !self.identifiers.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("astria.execution.v2.BatchGetBlocksRequest", len)?; + if !self.identifiers.is_empty() { + struct_ser.serialize_field("identifiers", &self.identifiers)?; +>>>>>>> superfluffy/forma-restart-logic } struct_ser.end() } } +<<<<<<< HEAD impl<'de> serde::Deserialize<'de> for CommitmentState { +======= +impl<'de> serde::Deserialize<'de> for BatchGetBlocksRequest { +>>>>>>> superfluffy/forma-restart-logic #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ +<<<<<<< HEAD "soft_executed_block_metadata", "softExecutedBlockMetadata", "firm_executed_block_metadata", "firmExecutedBlockMetadata", "lowest_celestia_search_height", "lowestCelestiaSearchHeight", +======= + "identifiers", +>>>>>>> superfluffy/forma-restart-logic ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { +<<<<<<< HEAD SoftExecutedBlockMetadata, FirmExecutedBlockMetadata, LowestCelestiaSearchHeight, +======= + Identifiers, +>>>>>>> superfluffy/forma-restart-logic } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -70,9 +95,13 @@ impl<'de> serde::Deserialize<'de> for CommitmentState { E: serde::de::Error, { match value { +<<<<<<< HEAD "softExecutedBlockMetadata" | "soft_executed_block_metadata" => Ok(GeneratedField::SoftExecutedBlockMetadata), "firmExecutedBlockMetadata" | "firm_executed_block_metadata" => Ok(GeneratedField::FirmExecutedBlockMetadata), "lowestCelestiaSearchHeight" | "lowest_celestia_search_height" => Ok(GeneratedField::LowestCelestiaSearchHeight), +======= + "identifiers" => Ok(GeneratedField::Identifiers), +>>>>>>> superfluffy/forma-restart-logic _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -82,6 +111,7 @@ impl<'de> serde::Deserialize<'de> for CommitmentState { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { +<<<<<<< HEAD type Value = CommitmentState; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -113,45 +143,494 @@ impl<'de> serde::Deserialize<'de> for CommitmentState { if lowest_celestia_search_height__.is_some() { return Err(serde::de::Error::duplicate_field("lowestCelestiaSearchHeight")); } - lowest_celestia_search_height__ = - Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) - ; + lowest_celestia_search_height__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + } + } + Ok(CommitmentState { + soft_executed_block_metadata: soft_executed_block_metadata__, + firm_executed_block_metadata: firm_executed_block_metadata__, + lowest_celestia_search_height: lowest_celestia_search_height__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("astria.execution.v2.CommitmentState", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for CreateExecutionSessionRequest { +======= + type Value = BatchGetBlocksRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct astria.execution.v2.BatchGetBlocksRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut identifiers__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Identifiers => { + if identifiers__.is_some() { + return Err(serde::de::Error::duplicate_field("identifiers")); + } + identifiers__ = Some(map_.next_value()?); + } + } + } + Ok(BatchGetBlocksRequest { + identifiers: identifiers__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("astria.execution.v2.BatchGetBlocksRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for BatchGetBlocksResponse { +>>>>>>> superfluffy/forma-restart-logic + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; +<<<<<<< HEAD + let len = 0; + let struct_ser = serializer.serialize_struct("astria.execution.v2.CreateExecutionSessionRequest", len)?; + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for CreateExecutionSessionRequest { +======= + let mut len = 0; + if !self.blocks.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("astria.execution.v2.BatchGetBlocksResponse", len)?; + if !self.blocks.is_empty() { + struct_ser.serialize_field("blocks", &self.blocks)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for BatchGetBlocksResponse { +>>>>>>> superfluffy/forma-restart-logic + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ +<<<<<<< HEAD +======= + "blocks", +>>>>>>> superfluffy/forma-restart-logic + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { +<<<<<<< HEAD +======= + Blocks, +>>>>>>> superfluffy/forma-restart-logic + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { +<<<<<<< HEAD + Err(serde::de::Error::unknown_field(value, FIELDS)) +======= + match value { + "blocks" => Ok(GeneratedField::Blocks), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } +>>>>>>> superfluffy/forma-restart-logic + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { +<<<<<<< HEAD + type Value = CreateExecutionSessionRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct astria.execution.v2.CreateExecutionSessionRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + while map_.next_key::()?.is_some() { + let _ = map_.next_value::()?; + } + Ok(CreateExecutionSessionRequest { + }) + } + } + deserializer.deserialize_struct("astria.execution.v2.CreateExecutionSessionRequest", FIELDS, GeneratedVisitor) +======= + type Value = BatchGetBlocksResponse; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct astria.execution.v2.BatchGetBlocksResponse") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut blocks__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Blocks => { + if blocks__.is_some() { + return Err(serde::de::Error::duplicate_field("blocks")); + } + blocks__ = Some(map_.next_value()?); + } + } + } + Ok(BatchGetBlocksResponse { + blocks: blocks__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("astria.execution.v2.BatchGetBlocksResponse", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for Block { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.number != 0 { + len += 1; + } + if !self.hash.is_empty() { + len += 1; + } + if !self.parent_block_hash.is_empty() { + len += 1; + } + if self.timestamp.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("astria.execution.v2.Block", len)?; + if self.number != 0 { + struct_ser.serialize_field("number", &self.number)?; + } + if !self.hash.is_empty() { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("hash", pbjson::private::base64::encode(&self.hash).as_str())?; + } + if !self.parent_block_hash.is_empty() { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("parentBlockHash", pbjson::private::base64::encode(&self.parent_block_hash).as_str())?; + } + if let Some(v) = self.timestamp.as_ref() { + struct_ser.serialize_field("timestamp", v)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for Block { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "number", + "hash", + "parent_block_hash", + "parentBlockHash", + "timestamp", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Number, + Hash, + ParentBlockHash, + Timestamp, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "number" => Ok(GeneratedField::Number), + "hash" => Ok(GeneratedField::Hash), + "parentBlockHash" | "parent_block_hash" => Ok(GeneratedField::ParentBlockHash), + "timestamp" => Ok(GeneratedField::Timestamp), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = Block; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct astria.execution.v2.Block") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut number__ = None; + let mut hash__ = None; + let mut parent_block_hash__ = None; + let mut timestamp__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Number => { + if number__.is_some() { + return Err(serde::de::Error::duplicate_field("number")); + } + number__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::Hash => { + if hash__.is_some() { + return Err(serde::de::Error::duplicate_field("hash")); + } + hash__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; + } + GeneratedField::ParentBlockHash => { + if parent_block_hash__.is_some() { + return Err(serde::de::Error::duplicate_field("parentBlockHash")); + } + parent_block_hash__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; + } + GeneratedField::Timestamp => { + if timestamp__.is_some() { + return Err(serde::de::Error::duplicate_field("timestamp")); + } + timestamp__ = map_.next_value()?; + } + } + } + Ok(Block { + number: number__.unwrap_or_default(), + hash: hash__.unwrap_or_default(), + parent_block_hash: parent_block_hash__.unwrap_or_default(), + timestamp: timestamp__, + }) + } + } + deserializer.deserialize_struct("astria.execution.v2.Block", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for BlockIdentifier { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.identifier.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("astria.execution.v2.BlockIdentifier", len)?; + if let Some(v) = self.identifier.as_ref() { + match v { + block_identifier::Identifier::BlockNumber(v) => { + struct_ser.serialize_field("blockNumber", v)?; + } + block_identifier::Identifier::BlockHash(v) => { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("blockHash", pbjson::private::base64::encode(&v).as_str())?; + } + } + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for BlockIdentifier { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "block_number", + "blockNumber", + "block_hash", + "blockHash", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + BlockNumber, + BlockHash, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "blockNumber" | "block_number" => Ok(GeneratedField::BlockNumber), + "blockHash" | "block_hash" => Ok(GeneratedField::BlockHash), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = BlockIdentifier; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct astria.execution.v2.BlockIdentifier") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut identifier__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::BlockNumber => { + if identifier__.is_some() { + return Err(serde::de::Error::duplicate_field("blockNumber")); + } + identifier__ = map_.next_value::<::std::option::Option<::pbjson::private::NumberDeserialize<_>>>()?.map(|x| block_identifier::Identifier::BlockNumber(x.0)); + } + GeneratedField::BlockHash => { + if identifier__.is_some() { + return Err(serde::de::Error::duplicate_field("blockHash")); + } + identifier__ = map_.next_value::<::std::option::Option<::pbjson::private::BytesDeserialize<_>>>()?.map(|x| block_identifier::Identifier::BlockHash(x.0)); } } } - Ok(CommitmentState { - soft_executed_block_metadata: soft_executed_block_metadata__, - firm_executed_block_metadata: firm_executed_block_metadata__, - lowest_celestia_search_height: lowest_celestia_search_height__.unwrap_or_default(), + Ok(BlockIdentifier { + identifier: identifier__, }) } } - deserializer.deserialize_struct("astria.execution.v2.CommitmentState", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria.execution.v2.BlockIdentifier", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for CreateExecutionSessionRequest { +impl serde::Serialize for CommitmentState { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where S: serde::Serializer, { use serde::ser::SerializeStruct; - let len = 0; - let struct_ser = serializer.serialize_struct("astria.execution.v2.CreateExecutionSessionRequest", len)?; + let mut len = 0; + if self.soft.is_some() { + len += 1; + } + if self.firm.is_some() { + len += 1; + } + if self.base_celestia_height != 0 { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("astria.execution.v2.CommitmentState", len)?; + if let Some(v) = self.soft.as_ref() { + struct_ser.serialize_field("soft", v)?; + } + if let Some(v) = self.firm.as_ref() { + struct_ser.serialize_field("firm", v)?; + } + if self.base_celestia_height != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("baseCelestiaHeight", ToString::to_string(&self.base_celestia_height).as_str())?; + } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for CreateExecutionSessionRequest { +impl<'de> serde::Deserialize<'de> for CommitmentState { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ + "soft", + "firm", + "base_celestia_height", + "baseCelestiaHeight", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { + Soft, + Firm, + BaseCelestiaHeight, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -172,7 +651,12 @@ impl<'de> serde::Deserialize<'de> for CreateExecutionSessionRequest { where E: serde::de::Error, { - Err(serde::de::Error::unknown_field(value, FIELDS)) + match value { + "soft" => Ok(GeneratedField::Soft), + "firm" => Ok(GeneratedField::Firm), + "baseCelestiaHeight" | "base_celestia_height" => Ok(GeneratedField::BaseCelestiaHeight), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } } } deserializer.deserialize_identifier(GeneratedVisitor) @@ -180,24 +664,52 @@ impl<'de> serde::Deserialize<'de> for CreateExecutionSessionRequest { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = CreateExecutionSessionRequest; + type Value = CommitmentState; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria.execution.v2.CreateExecutionSessionRequest") + formatter.write_str("struct astria.execution.v2.CommitmentState") } - fn visit_map(self, mut map_: V) -> std::result::Result + fn visit_map(self, mut map_: V) -> std::result::Result where V: serde::de::MapAccess<'de>, { - while map_.next_key::()?.is_some() { - let _ = map_.next_value::()?; + let mut soft__ = None; + let mut firm__ = None; + let mut base_celestia_height__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Soft => { + if soft__.is_some() { + return Err(serde::de::Error::duplicate_field("soft")); + } + soft__ = map_.next_value()?; + } + GeneratedField::Firm => { + if firm__.is_some() { + return Err(serde::de::Error::duplicate_field("firm")); + } + firm__ = map_.next_value()?; + } + GeneratedField::BaseCelestiaHeight => { + if base_celestia_height__.is_some() { + return Err(serde::de::Error::duplicate_field("baseCelestiaHeight")); + } + base_celestia_height__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + } } - Ok(CreateExecutionSessionRequest { + Ok(CommitmentState { + soft: soft__, + firm: firm__, + base_celestia_height: base_celestia_height__.unwrap_or_default(), }) } } - deserializer.deserialize_struct("astria.execution.v2.CreateExecutionSessionRequest", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria.execution.v2.CommitmentState", FIELDS, GeneratedVisitor) +>>>>>>> superfluffy/forma-restart-logic } } impl serde::Serialize for ExecuteBlockRequest { @@ -208,10 +720,14 @@ impl serde::Serialize for ExecuteBlockRequest { { use serde::ser::SerializeStruct; let mut len = 0; +<<<<<<< HEAD if !self.session_id.is_empty() { len += 1; } if !self.parent_hash.is_empty() { +======= + if !self.prev_block_hash.is_empty() { +>>>>>>> superfluffy/forma-restart-logic len += 1; } if !self.transactions.is_empty() { @@ -221,11 +737,17 @@ impl serde::Serialize for ExecuteBlockRequest { len += 1; } let mut struct_ser = serializer.serialize_struct("astria.execution.v2.ExecuteBlockRequest", len)?; +<<<<<<< HEAD if !self.session_id.is_empty() { struct_ser.serialize_field("sessionId", &self.session_id)?; } if !self.parent_hash.is_empty() { struct_ser.serialize_field("parentHash", &self.parent_hash)?; +======= + if !self.prev_block_hash.is_empty() { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("prevBlockHash", pbjson::private::base64::encode(&self.prev_block_hash).as_str())?; +>>>>>>> superfluffy/forma-restart-logic } if !self.transactions.is_empty() { struct_ser.serialize_field("transactions", &self.transactions)?; @@ -243,18 +765,27 @@ impl<'de> serde::Deserialize<'de> for ExecuteBlockRequest { D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ +<<<<<<< HEAD "session_id", "sessionId", "parent_hash", "parentHash", +======= + "prev_block_hash", + "prevBlockHash", +>>>>>>> superfluffy/forma-restart-logic "transactions", "timestamp", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { +<<<<<<< HEAD SessionId, ParentHash, +======= + PrevBlockHash, +>>>>>>> superfluffy/forma-restart-logic Transactions, Timestamp, } @@ -278,8 +809,12 @@ impl<'de> serde::Deserialize<'de> for ExecuteBlockRequest { E: serde::de::Error, { match value { +<<<<<<< HEAD "sessionId" | "session_id" => Ok(GeneratedField::SessionId), "parentHash" | "parent_hash" => Ok(GeneratedField::ParentHash), +======= + "prevBlockHash" | "prev_block_hash" => Ok(GeneratedField::PrevBlockHash), +>>>>>>> superfluffy/forma-restart-logic "transactions" => Ok(GeneratedField::Transactions), "timestamp" => Ok(GeneratedField::Timestamp), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), @@ -301,12 +836,17 @@ impl<'de> serde::Deserialize<'de> for ExecuteBlockRequest { where V: serde::de::MapAccess<'de>, { +<<<<<<< HEAD let mut session_id__ = None; let mut parent_hash__ = None; +======= + let mut prev_block_hash__ = None; +>>>>>>> superfluffy/forma-restart-logic let mut transactions__ = None; let mut timestamp__ = None; while let Some(k) = map_.next_key()? { match k { +<<<<<<< HEAD GeneratedField::SessionId => { if session_id__.is_some() { return Err(serde::de::Error::duplicate_field("sessionId")); @@ -318,6 +858,15 @@ impl<'de> serde::Deserialize<'de> for ExecuteBlockRequest { return Err(serde::de::Error::duplicate_field("parentHash")); } parent_hash__ = Some(map_.next_value()?); +======= + GeneratedField::PrevBlockHash => { + if prev_block_hash__.is_some() { + return Err(serde::de::Error::duplicate_field("prevBlockHash")); + } + prev_block_hash__ = + Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) + ; +>>>>>>> superfluffy/forma-restart-logic } GeneratedField::Transactions => { if transactions__.is_some() { @@ -334,8 +883,12 @@ impl<'de> serde::Deserialize<'de> for ExecuteBlockRequest { } } Ok(ExecuteBlockRequest { +<<<<<<< HEAD session_id: session_id__.unwrap_or_default(), parent_hash: parent_hash__.unwrap_or_default(), +======= + prev_block_hash: prev_block_hash__.unwrap_or_default(), +>>>>>>> superfluffy/forma-restart-logic transactions: transactions__.unwrap_or_default(), timestamp: timestamp__, }) @@ -344,6 +897,7 @@ impl<'de> serde::Deserialize<'de> for ExecuteBlockRequest { deserializer.deserialize_struct("astria.execution.v2.ExecuteBlockRequest", FIELDS, GeneratedVisitor) } } +<<<<<<< HEAD impl serde::Serialize for ExecuteBlockResponse { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result @@ -819,6 +1373,9 @@ impl<'de> serde::Deserialize<'de> for ExecutionSession { } } impl serde::Serialize for ExecutionSessionParameters { +======= +impl serde::Serialize for GenesisInfo { +>>>>>>> superfluffy/forma-restart-logic #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -829,15 +1386,29 @@ impl serde::Serialize for ExecutionSessionParameters { if self.rollup_id.is_some() { len += 1; } +<<<<<<< HEAD if self.rollup_start_block_number != 0 { len += 1; } if self.rollup_end_block_number != 0 { +======= + if self.sequencer_start_height != 0 { + len += 1; + } + if self.celestia_block_variance != 0 { + len += 1; + } + if self.rollup_start_block_number != 0 { + len += 1; + } + if self.rollup_stop_block_number != 0 { +>>>>>>> superfluffy/forma-restart-logic len += 1; } if !self.sequencer_chain_id.is_empty() { len += 1; } +<<<<<<< HEAD if self.sequencer_start_block_height != 0 { len += 1; } @@ -851,17 +1422,43 @@ impl serde::Serialize for ExecutionSessionParameters { if let Some(v) = self.rollup_id.as_ref() { struct_ser.serialize_field("rollupId", v)?; } +======= + if !self.celestia_chain_id.is_empty() { + len += 1; + } + if self.halt_at_rollup_stop_number { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("astria.execution.v2.GenesisInfo", len)?; + if let Some(v) = self.rollup_id.as_ref() { + struct_ser.serialize_field("rollupId", v)?; + } + if self.sequencer_start_height != 0 { + struct_ser.serialize_field("sequencerStartHeight", &self.sequencer_start_height)?; + } + if self.celestia_block_variance != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("celestiaBlockVariance", ToString::to_string(&self.celestia_block_variance).as_str())?; + } +>>>>>>> superfluffy/forma-restart-logic if self.rollup_start_block_number != 0 { #[allow(clippy::needless_borrow)] struct_ser.serialize_field("rollupStartBlockNumber", ToString::to_string(&self.rollup_start_block_number).as_str())?; } +<<<<<<< HEAD if self.rollup_end_block_number != 0 { #[allow(clippy::needless_borrow)] struct_ser.serialize_field("rollupEndBlockNumber", ToString::to_string(&self.rollup_end_block_number).as_str())?; +======= + if self.rollup_stop_block_number != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("rollupStopBlockNumber", ToString::to_string(&self.rollup_stop_block_number).as_str())?; +>>>>>>> superfluffy/forma-restart-logic } if !self.sequencer_chain_id.is_empty() { struct_ser.serialize_field("sequencerChainId", &self.sequencer_chain_id)?; } +<<<<<<< HEAD if self.sequencer_start_block_height != 0 { #[allow(clippy::needless_borrow)] struct_ser.serialize_field("sequencerStartBlockHeight", ToString::to_string(&self.sequencer_start_block_height).as_str())?; @@ -872,11 +1469,22 @@ impl serde::Serialize for ExecutionSessionParameters { if self.celestia_search_height_max_look_ahead != 0 { #[allow(clippy::needless_borrow)] struct_ser.serialize_field("celestiaSearchHeightMaxLookAhead", ToString::to_string(&self.celestia_search_height_max_look_ahead).as_str())?; +======= + if !self.celestia_chain_id.is_empty() { + struct_ser.serialize_field("celestiaChainId", &self.celestia_chain_id)?; + } + if self.halt_at_rollup_stop_number { + struct_ser.serialize_field("haltAtRollupStopNumber", &self.halt_at_rollup_stop_number)?; +>>>>>>> superfluffy/forma-restart-logic } struct_ser.end() } } +<<<<<<< HEAD impl<'de> serde::Deserialize<'de> for ExecutionSessionParameters { +======= +impl<'de> serde::Deserialize<'de> for GenesisInfo { +>>>>>>> superfluffy/forma-restart-logic #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where @@ -885,6 +1493,7 @@ impl<'de> serde::Deserialize<'de> for ExecutionSessionParameters { const FIELDS: &[&str] = &[ "rollup_id", "rollupId", +<<<<<<< HEAD "rollup_start_block_number", "rollupStartBlockNumber", "rollup_end_block_number", @@ -897,17 +1506,43 @@ impl<'de> serde::Deserialize<'de> for ExecutionSessionParameters { "celestiaChainId", "celestia_search_height_max_look_ahead", "celestiaSearchHeightMaxLookAhead", +======= + "sequencer_start_height", + "sequencerStartHeight", + "celestia_block_variance", + "celestiaBlockVariance", + "rollup_start_block_number", + "rollupStartBlockNumber", + "rollup_stop_block_number", + "rollupStopBlockNumber", + "sequencer_chain_id", + "sequencerChainId", + "celestia_chain_id", + "celestiaChainId", + "halt_at_rollup_stop_number", + "haltAtRollupStopNumber", +>>>>>>> superfluffy/forma-restart-logic ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { RollupId, +<<<<<<< HEAD RollupStartBlockNumber, RollupEndBlockNumber, SequencerChainId, SequencerStartBlockHeight, CelestiaChainId, CelestiaSearchHeightMaxLookAhead, +======= + SequencerStartHeight, + CelestiaBlockVariance, + RollupStartBlockNumber, + RollupStopBlockNumber, + SequencerChainId, + CelestiaChainId, + HaltAtRollupStopNumber, +>>>>>>> superfluffy/forma-restart-logic } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -930,12 +1565,22 @@ impl<'de> serde::Deserialize<'de> for ExecutionSessionParameters { { match value { "rollupId" | "rollup_id" => Ok(GeneratedField::RollupId), +<<<<<<< HEAD "rollupStartBlockNumber" | "rollup_start_block_number" => Ok(GeneratedField::RollupStartBlockNumber), "rollupEndBlockNumber" | "rollup_end_block_number" => Ok(GeneratedField::RollupEndBlockNumber), "sequencerChainId" | "sequencer_chain_id" => Ok(GeneratedField::SequencerChainId), "sequencerStartBlockHeight" | "sequencer_start_block_height" => Ok(GeneratedField::SequencerStartBlockHeight), "celestiaChainId" | "celestia_chain_id" => Ok(GeneratedField::CelestiaChainId), "celestiaSearchHeightMaxLookAhead" | "celestia_search_height_max_look_ahead" => Ok(GeneratedField::CelestiaSearchHeightMaxLookAhead), +======= + "sequencerStartHeight" | "sequencer_start_height" => Ok(GeneratedField::SequencerStartHeight), + "celestiaBlockVariance" | "celestia_block_variance" => Ok(GeneratedField::CelestiaBlockVariance), + "rollupStartBlockNumber" | "rollup_start_block_number" => Ok(GeneratedField::RollupStartBlockNumber), + "rollupStopBlockNumber" | "rollup_stop_block_number" => Ok(GeneratedField::RollupStopBlockNumber), + "sequencerChainId" | "sequencer_chain_id" => Ok(GeneratedField::SequencerChainId), + "celestiaChainId" | "celestia_chain_id" => Ok(GeneratedField::CelestiaChainId), + "haltAtRollupStopNumber" | "halt_at_rollup_stop_number" => Ok(GeneratedField::HaltAtRollupStopNumber), +>>>>>>> superfluffy/forma-restart-logic _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -945,6 +1590,7 @@ impl<'de> serde::Deserialize<'de> for ExecutionSessionParameters { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { +<<<<<<< HEAD type Value = ExecutionSessionParameters; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -952,16 +1598,35 @@ impl<'de> serde::Deserialize<'de> for ExecutionSessionParameters { } fn visit_map(self, mut map_: V) -> std::result::Result +======= + type Value = GenesisInfo; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct astria.execution.v2.GenesisInfo") + } + + fn visit_map(self, mut map_: V) -> std::result::Result +>>>>>>> superfluffy/forma-restart-logic where V: serde::de::MapAccess<'de>, { let mut rollup_id__ = None; +<<<<<<< HEAD let mut rollup_start_block_number__ = None; let mut rollup_end_block_number__ = None; let mut sequencer_chain_id__ = None; let mut sequencer_start_block_height__ = None; let mut celestia_chain_id__ = None; let mut celestia_search_height_max_look_ahead__ = None; +======= + let mut sequencer_start_height__ = None; + let mut celestia_block_variance__ = None; + let mut rollup_start_block_number__ = None; + let mut rollup_stop_block_number__ = None; + let mut sequencer_chain_id__ = None; + let mut celestia_chain_id__ = None; + let mut halt_at_rollup_stop_number__ = None; +>>>>>>> superfluffy/forma-restart-logic while let Some(k) = map_.next_key()? { match k { GeneratedField::RollupId => { @@ -970,6 +1635,25 @@ impl<'de> serde::Deserialize<'de> for ExecutionSessionParameters { } rollup_id__ = map_.next_value()?; } +<<<<<<< HEAD +======= + GeneratedField::SequencerStartHeight => { + if sequencer_start_height__.is_some() { + return Err(serde::de::Error::duplicate_field("sequencerStartHeight")); + } + sequencer_start_height__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::CelestiaBlockVariance => { + if celestia_block_variance__.is_some() { + return Err(serde::de::Error::duplicate_field("celestiaBlockVariance")); + } + celestia_block_variance__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } +>>>>>>> superfluffy/forma-restart-logic GeneratedField::RollupStartBlockNumber => { if rollup_start_block_number__.is_some() { return Err(serde::de::Error::duplicate_field("rollupStartBlockNumber")); @@ -978,11 +1662,19 @@ impl<'de> serde::Deserialize<'de> for ExecutionSessionParameters { Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) ; } +<<<<<<< HEAD GeneratedField::RollupEndBlockNumber => { if rollup_end_block_number__.is_some() { return Err(serde::de::Error::duplicate_field("rollupEndBlockNumber")); } rollup_end_block_number__ = +======= + GeneratedField::RollupStopBlockNumber => { + if rollup_stop_block_number__.is_some() { + return Err(serde::de::Error::duplicate_field("rollupStopBlockNumber")); + } + rollup_stop_block_number__ = +>>>>>>> superfluffy/forma-restart-logic Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) ; } @@ -992,6 +1684,7 @@ impl<'de> serde::Deserialize<'de> for ExecutionSessionParameters { } sequencer_chain_id__ = Some(map_.next_value()?); } +<<<<<<< HEAD GeneratedField::SequencerStartBlockHeight => { if sequencer_start_block_height__.is_some() { return Err(serde::de::Error::duplicate_field("sequencerStartBlockHeight")); @@ -1000,12 +1693,15 @@ impl<'de> serde::Deserialize<'de> for ExecutionSessionParameters { Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) ; } +======= +>>>>>>> superfluffy/forma-restart-logic GeneratedField::CelestiaChainId => { if celestia_chain_id__.is_some() { return Err(serde::de::Error::duplicate_field("celestiaChainId")); } celestia_chain_id__ = Some(map_.next_value()?); } +<<<<<<< HEAD GeneratedField::CelestiaSearchHeightMaxLookAhead => { if celestia_search_height_max_look_ahead__.is_some() { return Err(serde::de::Error::duplicate_field("celestiaSearchHeightMaxLookAhead")); @@ -1031,6 +1727,32 @@ impl<'de> serde::Deserialize<'de> for ExecutionSessionParameters { } } impl serde::Serialize for GetExecutedBlockMetadataRequest { +======= + GeneratedField::HaltAtRollupStopNumber => { + if halt_at_rollup_stop_number__.is_some() { + return Err(serde::de::Error::duplicate_field("haltAtRollupStopNumber")); + } + halt_at_rollup_stop_number__ = Some(map_.next_value()?); + } + } + } + Ok(GenesisInfo { + rollup_id: rollup_id__, + sequencer_start_height: sequencer_start_height__.unwrap_or_default(), + celestia_block_variance: celestia_block_variance__.unwrap_or_default(), + rollup_start_block_number: rollup_start_block_number__.unwrap_or_default(), + rollup_stop_block_number: rollup_stop_block_number__.unwrap_or_default(), + sequencer_chain_id: sequencer_chain_id__.unwrap_or_default(), + celestia_chain_id: celestia_chain_id__.unwrap_or_default(), + halt_at_rollup_stop_number: halt_at_rollup_stop_number__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("astria.execution.v2.GenesisInfo", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for GetBlockRequest { +>>>>>>> superfluffy/forma-restart-logic #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -1041,14 +1763,22 @@ impl serde::Serialize for GetExecutedBlockMetadataRequest { if self.identifier.is_some() { len += 1; } +<<<<<<< HEAD let mut struct_ser = serializer.serialize_struct("astria.execution.v2.GetExecutedBlockMetadataRequest", len)?; +======= + let mut struct_ser = serializer.serialize_struct("astria.execution.v2.GetBlockRequest", len)?; +>>>>>>> superfluffy/forma-restart-logic if let Some(v) = self.identifier.as_ref() { struct_ser.serialize_field("identifier", v)?; } struct_ser.end() } } +<<<<<<< HEAD impl<'de> serde::Deserialize<'de> for GetExecutedBlockMetadataRequest { +======= +impl<'de> serde::Deserialize<'de> for GetBlockRequest { +>>>>>>> superfluffy/forma-restart-logic #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where @@ -1092,6 +1822,7 @@ impl<'de> serde::Deserialize<'de> for GetExecutedBlockMetadataRequest { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { +<<<<<<< HEAD type Value = GetExecutedBlockMetadataRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -1099,6 +1830,15 @@ impl<'de> serde::Deserialize<'de> for GetExecutedBlockMetadataRequest { } fn visit_map(self, mut map_: V) -> std::result::Result +======= + type Value = GetBlockRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct astria.execution.v2.GetBlockRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result +>>>>>>> superfluffy/forma-restart-logic where V: serde::de::MapAccess<'de>, { @@ -1113,12 +1853,162 @@ impl<'de> serde::Deserialize<'de> for GetExecutedBlockMetadataRequest { } } } +<<<<<<< HEAD Ok(GetExecutedBlockMetadataRequest { +======= + Ok(GetBlockRequest { +>>>>>>> superfluffy/forma-restart-logic identifier: identifier__, }) } } +<<<<<<< HEAD deserializer.deserialize_struct("astria.execution.v2.GetExecutedBlockMetadataRequest", FIELDS, GeneratedVisitor) +======= + deserializer.deserialize_struct("astria.execution.v2.GetBlockRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for GetCommitmentStateRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let len = 0; + let struct_ser = serializer.serialize_struct("astria.execution.v2.GetCommitmentStateRequest", len)?; + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for GetCommitmentStateRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + Err(serde::de::Error::unknown_field(value, FIELDS)) + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GetCommitmentStateRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct astria.execution.v2.GetCommitmentStateRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + while map_.next_key::()?.is_some() { + let _ = map_.next_value::()?; + } + Ok(GetCommitmentStateRequest { + }) + } + } + deserializer.deserialize_struct("astria.execution.v2.GetCommitmentStateRequest", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for GetGenesisInfoRequest { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let len = 0; + let struct_ser = serializer.serialize_struct("astria.execution.v2.GetGenesisInfoRequest", len)?; + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for GetGenesisInfoRequest { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + Err(serde::de::Error::unknown_field(value, FIELDS)) + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GetGenesisInfoRequest; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct astria.execution.v2.GetGenesisInfoRequest") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + while map_.next_key::()?.is_some() { + let _ = map_.next_value::()?; + } + Ok(GetGenesisInfoRequest { + }) + } + } + deserializer.deserialize_struct("astria.execution.v2.GetGenesisInfoRequest", FIELDS, GeneratedVisitor) +>>>>>>> superfluffy/forma-restart-logic } } impl serde::Serialize for UpdateCommitmentStateRequest { @@ -1129,16 +2019,22 @@ impl serde::Serialize for UpdateCommitmentStateRequest { { use serde::ser::SerializeStruct; let mut len = 0; +<<<<<<< HEAD if !self.session_id.is_empty() { len += 1; } +======= +>>>>>>> superfluffy/forma-restart-logic if self.commitment_state.is_some() { len += 1; } let mut struct_ser = serializer.serialize_struct("astria.execution.v2.UpdateCommitmentStateRequest", len)?; +<<<<<<< HEAD if !self.session_id.is_empty() { struct_ser.serialize_field("sessionId", &self.session_id)?; } +======= +>>>>>>> superfluffy/forma-restart-logic if let Some(v) = self.commitment_state.as_ref() { struct_ser.serialize_field("commitmentState", v)?; } @@ -1152,15 +2048,21 @@ impl<'de> serde::Deserialize<'de> for UpdateCommitmentStateRequest { D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ +<<<<<<< HEAD "session_id", "sessionId", +======= +>>>>>>> superfluffy/forma-restart-logic "commitment_state", "commitmentState", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { +<<<<<<< HEAD SessionId, +======= +>>>>>>> superfluffy/forma-restart-logic CommitmentState, } impl<'de> serde::Deserialize<'de> for GeneratedField { @@ -1183,7 +2085,10 @@ impl<'de> serde::Deserialize<'de> for UpdateCommitmentStateRequest { E: serde::de::Error, { match value { +<<<<<<< HEAD "sessionId" | "session_id" => Ok(GeneratedField::SessionId), +======= +>>>>>>> superfluffy/forma-restart-logic "commitmentState" | "commitment_state" => Ok(GeneratedField::CommitmentState), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } @@ -1204,6 +2109,7 @@ impl<'de> serde::Deserialize<'de> for UpdateCommitmentStateRequest { where V: serde::de::MapAccess<'de>, { +<<<<<<< HEAD let mut session_id__ = None; let mut commitment_state__ = None; while let Some(k) = map_.next_key()? { @@ -1214,6 +2120,11 @@ impl<'de> serde::Deserialize<'de> for UpdateCommitmentStateRequest { } session_id__ = Some(map_.next_value()?); } +======= + let mut commitment_state__ = None; + while let Some(k) = map_.next_key()? { + match k { +>>>>>>> superfluffy/forma-restart-logic GeneratedField::CommitmentState => { if commitment_state__.is_some() { return Err(serde::de::Error::duplicate_field("commitmentState")); @@ -1223,7 +2134,10 @@ impl<'de> serde::Deserialize<'de> for UpdateCommitmentStateRequest { } } Ok(UpdateCommitmentStateRequest { +<<<<<<< HEAD session_id: session_id__.unwrap_or_default(), +======= +>>>>>>> superfluffy/forma-restart-logic commitment_state: commitment_state__, }) } diff --git a/dev/values/rollup/dev.yaml b/dev/values/rollup/dev.yaml index 66eb3afb71..4e4d2e1ce6 100644 --- a/dev/values/rollup/dev.yaml +++ b/dev/values/rollup/dev.yaml @@ -10,18 +10,43 @@ global: evm-rollup: genesis: - ## These values are used to configure the genesis block of the rollup chain - ## no defaults as they are unique to each chain - - # Block height to start syncing rollup from, lowest possible is 2 - sequencerInitialHeight: 2 - # The first Celestia height to utilize when looking for rollup data - celestiaInitialHeight: 2 - # The variance in Celestia height to allow before halting the chain - celestiaHeightVariance: 10 - # Will fill the extra data in each block, can be left empty - # can also fill with something unique for your chain. - extraDataOverride: "" + # The name of the rollup chain, used to generate the Rollup ID + rollupName: "{{ .Values.global.rollupName }}" + + # The "forks" for upgrading the chain. Contains necessary information for starting + # and, if desired, restarting the chain at a given height. The necessary fields + # for the genesis fork are provided, and additional forks can be added as needed. + forks: + ## These values are used to configure the genesis block of the rollup chain + ## no defaults as they are unique to each chain + launch: + # The rollup number to start executing blocks at, lowest possible is 1 + height: 1 + # Configure the fee collector for the evm tx fees, activated at block heights. + # If not configured, all tx fees will be burned. + feeCollector: "0xaC21B97d35Bf75A7dAb16f35b111a50e78A72F30" + sequencer: + # The chain id of the sequencer chain + chainId: "sequencer-test-chain-0" + # The hrp for bech32m addresses, unlikely to be changed + addressPrefix: "astria" + # Block height to start syncing rollup from (inclusive), lowest possible is 2 + startHeight: 2 + celestia: + # The chain id of the celestia chain + chainId: "celestia-local-0" + # The first Celestia height to utilize when looking for rollup data + startHeight: 2 + # The variance in Celestia height to allow before halting the chain + heightVariance: 10 + # Configure the sequencer bridge addresses and allowed assets if using + # the astria canonical bridge. Recommend removing alloc values if so. + bridgeAddresses: + - bridgeAddress: "astria13ahqz4pjqfmynk9ylrqv4fwe4957x2p0h5782u" + startHeight: 1 + senderAddress: "0x0000000000000000000000000000000000000000" + assetDenom: "nria" + assetPrecision: 9 ## These are general configuration values with some recommended defaults @@ -29,34 +54,6 @@ evm-rollup: gasLimit: "50000000" # If set to true the genesis block will contain extra data overrideGenesisExtraData: true - # The hrp for bech32m addresses, unlikely to be changed - sequencerAddressPrefix: "astria" - - ## These values are used to configure astria native bridging - ## Many of the fields have commented out example fields - - # Configure the sequencer bridge addresses and allowed assets if using - # the astria canonical bridge. Recommend removing alloc values if so. - bridgeAddresses: - - bridgeAddress: "astria13ahqz4pjqfmynk9ylrqv4fwe4957x2p0h5782u" - startHeight: 1 - senderAddress: "0x0000000000000000000000000000000000000000" - assetDenom: "nria" - assetPrecision: 9 - - - ## Fee configuration - - # Configure the fee collector for the evm tx fees, activated at block heights. - # If not configured, all tx fees will be burned. - feeCollectors: - 1: "0xaC21B97d35Bf75A7dAb16f35b111a50e78A72F30" - # Configure EIP-1559 params, activated at block heights - eip1559Params: {} - # 1: - # minBaseFee: 0 - # elasticityMultiplier: 2 - # baseFeeChangeDenominator: 8 ## Standard Eth Genesis config values # Configuration of Eth forks, setting to 0 will enable from height, diff --git a/dev/values/rollup/evm-restart-test.yaml b/dev/values/rollup/evm-restart-test.yaml new file mode 100644 index 0000000000..e78414938f --- /dev/null +++ b/dev/values/rollup/evm-restart-test.yaml @@ -0,0 +1,251 @@ +global: + useTTY: true + dev: true + evmChainId: 1337 + rollupName: astria + sequencerRpc: http://node0-sequencer-rpc-service.astria-dev-cluster.svc.cluster.local:26657 + sequencerGrpc: http://node0-sequencer-grpc-service.astria-dev-cluster.svc.cluster.local:8080 + sequencerChainId: sequencer-test-chain-0 + celestiaChainId: celestia-local-0 + +evm-rollup: + genesis: + # The name of the rollup chain, used to generate the Rollup ID + rollupName: "{{ .Values.global.rollupName }}" + + # The "forks" for upgrading the chain. Contains necessary information for starting + # and, if desired, restarting the chain at a given height. The necessary fields + # for the genesis fork are provided, and additional forks can be added as needed. + forks: + ## These values are used to configure the genesis block of the rollup chain + ## no defaults as they are unique to each chain + launch: + # The rollup number to start executing blocks at, lowest possible is 1 + height: 1 + # Configure the fee collector for the evm tx fees, activated at block heights. + # If not configured, all tx fees will be burned. + feeCollector: "0xaC21B97d35Bf75A7dAb16f35b111a50e78A72F30" + sequencer: + # The chain id of the sequencer chain + chainId: "sequencer-test-chain-0" + # The hrp for bech32m addresses, unlikely to be changed + addressPrefix: "astria" + # Block height to start syncing rollup from (inclusive), lowest possible is 2 + startHeight: 2 + celestia: + # The chain id of the celestia chain + chainId: "celestia-local-0" + # The first Celestia height to utilize when looking for rollup data + startHeight: 2 + # The variance in Celestia height to allow before halting the chain + heightVariance: 10 + # Configure the sequencer bridge addresses and allowed assets if using + # the astria canonical bridge. Recommend removing alloc values if so. + bridgeAddresses: + - bridgeAddress: "astria13ahqz4pjqfmynk9ylrqv4fwe4957x2p0h5782u" + startHeight: 1 + senderAddress: "0x0000000000000000000000000000000000000000" + assetDenom: "nria" + assetPrecision: 9 + restart: + height: 19 + + ## These are general configuration values with some recommended defaults + + # Configure the gas Limit + gasLimit: "50000000" + # If set to true the genesis block will contain extra data + overrideGenesisExtraData: true + + ## Standard Eth Genesis config values + # Configuration of Eth forks, setting to 0 will enable from height, + # left as is these forks will not activate. + cancunTime: "" + pragueTime: "" + verkleTime: "" + # Can configure the genesis allocs for the chain + alloc: + # Deploying the deterministic deploy proxy contract in genesis + # Forge and other tools use this for their CREATE2 usage, but + # can only be included through the genesis block after EIP-155 + # https://github.com/Arachnid/deterministic-deployment-proxy + - address: "0x4e59b44847b379578588920cA78FbF26c0B4956C" + value: + balance: "0" + code: "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3" + - address: "0xA58639fB5458e65E4fA917FF951C390292C24A15" + value: + balance: "0" + code: "0x6080604052600436106100f35760003560e01c8063b6476c7e1161008a578063e74b981b11610059578063e74b981b1461027b578063ebd090541461029b578063f2fde38b146102bb578063fc88d31b146102db57600080fd5b8063b6476c7e1461021c578063bab916d01461023e578063d294f09314610251578063db97dc981461026657600080fd5b80638da5cb5b116100c65780638da5cb5b146101a1578063a7eaa739146101d3578063a996e020146101f3578063ad2282471461020657600080fd5b80636f46384a146100f8578063715018a6146101215780637eb6dec7146101385780638897397914610181575b600080fd5b34801561010457600080fd5b5061010e60035481565b6040519081526020015b60405180910390f35b34801561012d57600080fd5b506101366102f1565b005b34801561014457600080fd5b5061016c7f000000000000000000000000000000000000000000000000000000000000000981565b60405163ffffffff9091168152602001610118565b34801561018d57600080fd5b5061013661019c3660046107a6565b610305565b3480156101ad57600080fd5b506000546001600160a01b03165b6040516001600160a01b039091168152602001610118565b3480156101df57600080fd5b506101366101ee3660046107a6565b610312565b610136610201366004610808565b61031f565b34801561021257600080fd5b5061010e60065481565b34801561022857600080fd5b50610231610414565b6040516101189190610874565b61013661024c3660046108c3565b6104a2565b34801561025d57600080fd5b50610136610588565b34801561027257600080fd5b506102316106b4565b34801561028757600080fd5b50610136610296366004610905565b6106c1565b3480156102a757600080fd5b506005546101bb906001600160a01b031681565b3480156102c757600080fd5b506101366102d6366004610905565b6106eb565b3480156102e757600080fd5b5061010e60045481565b6102f9610729565b6103036000610756565b565b61030d610729565b600455565b61031a610729565b600355565b3460045480821161034b5760405162461bcd60e51b815260040161034290610935565b60405180910390fd5b60007f000000000000000000000000000000000000000000000000000000003b9aca006103788385610998565b61038291906109b1565b1161039f5760405162461bcd60e51b8152600401610342906109d3565b600454600660008282546103b39190610a61565b90915550506004546103c59034610998565b336001600160a01b03167f0c64e29a5254a71c7f4e52b3d2d236348c80e00a00ba2e1961962bd2827c03fb888888886040516104049493929190610a9d565b60405180910390a3505050505050565b6002805461042190610acf565b80601f016020809104026020016040519081016040528092919081815260200182805461044d90610acf565b801561049a5780601f1061046f5761010080835404028352916020019161049a565b820191906000526020600020905b81548152906001019060200180831161047d57829003601f168201915b505050505081565b346003548082116104c55760405162461bcd60e51b815260040161034290610935565b60007f000000000000000000000000000000000000000000000000000000003b9aca006104f28385610998565b6104fc91906109b1565b116105195760405162461bcd60e51b8152600401610342906109d3565b6003546006600082825461052d9190610a61565b909155505060035461053f9034610998565b336001600160a01b03167f0f4961cab7530804898499aa89f5ec81d1a73102e2e4a1f30f88e5ae3513ba2a868660405161057a929190610b09565b60405180910390a350505050565b6005546001600160a01b031633146105f45760405162461bcd60e51b815260206004820152602960248201527f41737472696142726964676561626c6545524332303a206f6e6c7920666565206044820152681c9958da5c1a595b9d60ba1b6064820152608401610342565b6005546006546040516000926001600160a01b031691908381818185875af1925050503d8060008114610643576040519150601f19603f3d011682016040523d82523d6000602084013e610648565b606091505b50509050806106ac5760405162461bcd60e51b815260206004820152602a60248201527f41737472696142726964676561626c6545524332303a20666565207472616e7360448201526919995c8819985a5b195960b21b6064820152608401610342565b506000600655565b6001805461042190610acf565b6106c9610729565b600580546001600160a01b0319166001600160a01b0392909216919091179055565b6106f3610729565b6001600160a01b03811661071d57604051631e4fbdf760e01b815260006004820152602401610342565b61072681610756565b50565b6000546001600160a01b031633146103035760405163118cdaa760e01b8152336004820152602401610342565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000602082840312156107b857600080fd5b5035919050565b60008083601f8401126107d157600080fd5b50813567ffffffffffffffff8111156107e957600080fd5b60208301915083602082850101111561080157600080fd5b9250929050565b6000806000806040858703121561081e57600080fd5b843567ffffffffffffffff8082111561083657600080fd5b610842888389016107bf565b9096509450602087013591508082111561085b57600080fd5b50610868878288016107bf565b95989497509550505050565b60006020808352835180602085015260005b818110156108a257858101830151858201604001528201610886565b506000604082860101526040601f19601f8301168501019250505092915050565b600080602083850312156108d657600080fd5b823567ffffffffffffffff8111156108ed57600080fd5b6108f9858286016107bf565b90969095509350505050565b60006020828403121561091757600080fd5b81356001600160a01b038116811461092e57600080fd5b9392505050565b6020808252602d908201527f417374726961576974686472617765723a20696e73756666696369656e74207760408201526c69746864726177616c2066656560981b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b818103818111156109ab576109ab610982565b92915050565b6000826109ce57634e487b7160e01b600052601260045260246000fd5b500490565b60208082526062908201527f417374726961576974686472617765723a20696e73756666696369656e74207660408201527f616c75652c206d7573742062652067726561746572207468616e203130202a2a60608201527f20283138202d20424153455f434841494e5f41535345545f505245434953494f6080820152614e2960f01b60a082015260c00190565b808201808211156109ab576109ab610982565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b604081526000610ab1604083018688610a74565b8281036020840152610ac4818587610a74565b979650505050505050565b600181811c90821680610ae357607f821691505b602082108103610b0357634e487b7160e01b600052602260045260246000fd5b50919050565b602081526000610b1d602083018486610a74565b94935050505056fea2646970667358221220842bd8104ffc1c611919341f64a8277f2fc808138b97720a6dc1382e5670099064736f6c63430008190033" + + + config: + # The level at which core astria components will log out + # Options are: error, warn, info, and debug + logLevel: "debug" + + conductor: + # Determines what will drive block execution, options are: + # - "SoftOnly" -> blocks are only pulled from the sequencer + # - "FirmOnly" -> blocks are only pulled from DA + # - "SoftAndFirm" -> blocks are pulled from both the sequencer and DA + executionCommitLevel: 'SoftAndFirm' + # The expected fastest block time possible from sequencer, determines polling + # rate. + sequencerBlockTimeMs: 2000 + # The maximum number of requests to make to the sequencer per second + sequencerRequestsPerSecond: 500 + + celestia: + rpc: "http://celestia-service.astria-dev-cluster.svc.cluster.local:26658" + token: "" + + resources: + conductor: + requests: + cpu: 0.01 + memory: 1Mi + limits: + cpu: 0.1 + memory: 20Mi + geth: + requests: + cpu: 0.25 + memory: 256Mi + limits: + cpu: 2 + memory: 1Gi + + storage: + enabled: false + + ingress: + enabled: true + services: + rpc: + enabled: true + ws: + enabled: true + +celestia-node: + enabled: false + +composer: + enabled: true + config: + privateKey: + devContent: "2bd806c97f0e00af1a1fc3328fa763a9269723c8db8fac4f93af71db186d6e90" + +evm-bridge-withdrawer: + enabled: true + config: + minExpectedFeeAssetBalance: "0" + sequencerBridgeAddress: "astria13ahqz4pjqfmynk9ylrqv4fwe4957x2p0h5782u" + feeAssetDenom: "nria" + rollupAssetDenom: "nria" + evmContractAddress: "0xA58639fB5458e65E4fA917FF951C390292C24A15" + sequencerPrivateKey: + devContent: "dfa7108e38ab71f89f356c72afc38600d5758f11a8c337164713e4471411d2e0" + +evm-faucet: + enabled: true + ingress: + enabled: true + config: + privateKey: + devContent: "8b3a7999072c9c9314c084044fe705db11714c6c4ed7cddb64da18ea270dd203" + +postgresql: + enabled: true + nameOverride: blockscout-postegres + primary: + persistence: + enabled: false + resourcesPreset: "medium" + auth: + enablePostgresUser: true + postgresPassword: bigsecretpassword + username: blockscout + password: blockscout + database: blockscout + audit: + logHostname: true + logConnections: true + logDisconnections: true +blockscout-stack: + enabled: true + config: + network: + id: 1337 + name: Astria + shortname: Astria + currency: + name: RIA + symbol: RIA + decimals: 18 + testnet: true + prometheus: + enabled: false + blockscout: + extraEnv: + - name: ECTO_USE_SSL + value: "false" + - name: DATABASE_URL + value: "postgres://postgres:bigsecretpassword@astria-chain-chart-blockscout-postegres.astria-dev-cluster.svc.cluster.local:5432/blockscout" + - name: ETHEREUM_JSONRPC_VARIANT + value: "geth" + - name: ETHEREUM_JSONRPC_HTTP_URL + value: "http://astria-evm-service.astria-dev-cluster.svc.cluster.local:8545/" + - name: ETHEREUM_JSONRPC_INSECURE + value: "true" + - name: ETHEREUM_JSONRPC_WS_URL + value: "ws://astria-evm-service.astria-dev-cluster.svc.cluster.local:8546/" + - name: INDEXER_DISABLE_BEACON_BLOB_FETCHER + value: "true" + - name: NETWORK + value: "Astria" + - name: SUBNETWORK + value: "Local" + - name: CONTRACT_VERIFICATION_ALLOWED_SOLIDITY_EVM_VERSIONS + value: "homestead,tangerineWhistle,spuriousDragon,byzantium,constantinople,petersburg,istanbul,berlin,london,paris,shanghai,default" + - name: CONTRACT_VERIFICATION_ALLOWED_VYPER_EVM_VERSIONS + value: "byzantium,constantinople,petersburg,istanbul,berlin,paris,shanghai,default" + - name: DISABLE_EXCHANGE_RATES + value: "true" + + ingress: + enabled: true + hostname: explorer.astria.localdev.me + paths: + - path: /api + pathType: Prefix + - path: /socket + pathType: Prefix + - path: /sitemap.xml + pathType: ImplementationSpecific + - path: /public-metrics + pathType: Prefix + - path: /auth/auth0 + pathType: Exact + - path: /auth/auth0/callback + pathType: Exact + - path: /auth/logout + pathType: Exact + + frontend: + extraEnv: + - name: NEXT_PUBLIC_NETWORK_VERIFICATION_TYPE + value: "validation" + - name: NEXT_PUBLIC_AD_BANNER_PROVIDER + value: "none" + - name: NEXT_PUBLIC_API_PROTOCOL + value: "http" + - name: NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL + value: "ws" + - name: NEXT_PUBLIC_NETWORK_CURRENCY_WEI_NAME + value: "aRia" + - name: NEXT_PUBLIC_AD_TEXT_PROVIDER + value: "none" + ingress: + enabled: true + hostname: explorer.astria.localdev.me diff --git a/dev/values/rollup/ibc-bridge-test.yaml b/dev/values/rollup/ibc-bridge-test.yaml index b5f198927c..83f8e17577 100644 --- a/dev/values/rollup/ibc-bridge-test.yaml +++ b/dev/values/rollup/ibc-bridge-test.yaml @@ -1,12 +1,48 @@ # this file contains overrides that are used for the ibc bridge tests +global: + rollupName: astria + sequencerChainId: sequencer-test-chain-0 + celestiaChainId: celestia-local-0 evm-rollup: genesis: - bridgeAddresses: - - bridgeAddress: "astria1d7zjjljc0dsmxa545xkpwxym86g8uvvwhtezcr" - startHeight: 1 - assetDenom: "transfer/channel-0/utia" - assetPrecision: 6 + # The name of the rollup chain, used to generate the Rollup ID + rollupName: "{{ .Values.global.rollupName }}" + + # The "forks" for upgrading the chain. Contains necessary information for starting + # and, if desired, restarting the chain at a given height. The necessary fields + # for the genesis fork are provided, and additional forks can be added as needed. + forks: + ## These values are used to configure the genesis block of the rollup chain + ## no defaults as they are unique to each chain + launch: + # The rollup number to start executing blocks at, lowest possible is 1 + height: 1 + # Configure the fee collector for the evm tx fees, activated at block heights. + # If not configured, all tx fees will be burned. + feeCollector: "0xaC21B97d35Bf75A7dAb16f35b111a50e78A72F30" + sequencer: + # The chain id of the sequencer chain + chainId: "sequencer-test-chain-0" + # The hrp for bech32m addresses, unlikely to be changed + addressPrefix: "astria" + # Block height to start syncing rollup from (inclusive), lowest possible is 2 + startHeight: 2 + # Block height (on sequencer) to stop syncing rollup at, continuing to next configuration fork + stopHeight: 0 + celestia: + # The chain id of the celestia chain + chainId: "celestia-local-0" + # The first Celestia height to utilize when looking for rollup data + startHeight: 2 + # The variance in Celestia height to allow before halting the chain + heightVariance: 10 + bridgeAddresses: + - bridgeAddress: "astria1d7zjjljc0dsmxa545xkpwxym86g8uvvwhtezcr" + startHeight: 1 + assetDenom: "transfer/channel-0/utia" + assetPrecision: 6 + alloc: - address: "0x4e59b44847b379578588920cA78FbF26c0B4956C" value: diff --git a/proto/executionapis/astria/bundle/v1alpha1/optimistic_execution.proto b/proto/executionapis/astria/bundle/v1alpha1/optimistic_execution.proto new file mode 100644 index 0000000000..c8fa7af943 --- /dev/null +++ b/proto/executionapis/astria/bundle/v1alpha1/optimistic_execution.proto @@ -0,0 +1,38 @@ +syntax = "proto3"; + +package astria.bundle.v1alpha1; + +import "astria/execution/v2/execution.proto"; +import "astria/sequencerblock/v1/block.proto"; +import "google/protobuf/timestamp.proto"; + +// The "BaseBlock" is the information needed to simulate bundles on top of +// a Sequencer block which may not have been committed yet. +message BaseBlock { + // This is the block hash for the proposed block. + bytes sequencer_block_hash = 1; + // List of transactions to include in the new block. + repeated astria.sequencerblock.v1.RollupData transactions = 2; + // Timestamp to be used for new block. + google.protobuf.Timestamp timestamp = 3; +} + +message ExecuteOptimisticBlockStreamRequest { + BaseBlock base_block = 1; +} + +message ExecuteOptimisticBlockStreamResponse { + // Metadata identifying the block resulting from executing a block. Includes number, hash, + // parent hash and timestamp. + astria.execution.v2.Block block = 1; + // The base_sequencer_block_hash is the hash from the base sequencer block this block + // is based on. This is used to associate an optimistic execution result with the hash + // received once a sequencer block is committed. + bytes base_sequencer_block_hash = 2; +} + +service OptimisticExecutionService { + // Stream blocks from the Auctioneer to Geth for optimistic execution. Geth will stream back + // metadata from the executed blocks. + rpc ExecuteOptimisticBlockStream(stream ExecuteOptimisticBlockStreamRequest) returns (stream ExecuteOptimisticBlockStreamResponse); +} diff --git a/proto/executionapis/astria/execution/v2/execution.proto b/proto/executionapis/astria/execution/v2/execution.proto new file mode 100644 index 0000000000..fddded00eb --- /dev/null +++ b/proto/executionapis/astria/execution/v2/execution.proto @@ -0,0 +1,142 @@ +syntax = 'proto3'; + +package astria.execution.v2; + +import "astria/primitive/v1/types.proto"; +import "astria/sequencerblock/v1/block.proto"; +import "google/protobuf/timestamp.proto"; + +// GenesisInfo contains the information needed to start a rollup chain. +// +// This information is used to determine which sequencer & celestia data to +// use from the Astria & Celestia networks. +message GenesisInfo { + // The rollup_id is the unique identifier for the rollup chain. + astria.primitive.v1.RollupId rollup_id = 1; + // The first block height of sequencer chain to use for rollup transactions. + uint32 sequencer_start_height = 2; + // The allowed variance in celestia for sequencer blocks to have been posted. + uint64 celestia_block_variance = 4; + // The rollup block number to map to the sequencer start block height. + uint64 rollup_start_block_number = 5; + // The rollup block number to re-fetch the genesis info and continue executing with new data. + uint64 rollup_stop_block_number = 6; + // The ID of the Astria Sequencer network to retrieve Sequencer blocks from. + // Conductor implementations should verify that the Sequencer network they are connected to + // have this chain ID (if fetching soft Sequencer blocks), and verify that the Sequencer metadata + // blobs retrieved from Celestia contain this chain ID (if extracting firm Sequencer blocks from + // Celestia blobs). + string sequencer_chain_id = 7; + // The ID of the Celestia network to retrieve blobs from. + // Conductor implementations should verify that the Celestia network they are connected to have + // this chain ID (if extracting firm Sequencer blocks from Celestia blobs). + string celestia_chain_id = 8; + // Requests Conductor to halt at the stop number instead of re-fetching the genesis and continuing execution. + bool halt_at_rollup_stop_number = 9; +} + +// The set of information which deterministic driver of block production +// must know about a given rollup Block +message Block { + // The block number + uint32 number = 1; + // The hash of the block + bytes hash = 2; + // The hash from the parent block + bytes parent_block_hash = 3; + // Timestamp on the block, standardized to google protobuf standard. + google.protobuf.Timestamp timestamp = 4; +} + +// Fields which are indexed for finding blocks on a blockchain. +message BlockIdentifier { + oneof identifier { + uint32 block_number = 1; + bytes block_hash = 2; + } +} + +message GetGenesisInfoRequest {} + +// Used in GetBlock to find a single block. +message GetBlockRequest { + BlockIdentifier identifier = 1; +} + +// Used in BatchGetBlocks, will find all or none based on the list of +// identifiers. +message BatchGetBlocksRequest { + repeated BlockIdentifier identifiers = 1; +} + +// The list of blocks in response to BatchGetBlocks. +message BatchGetBlocksResponse { + repeated Block blocks = 1; +} + +// ExecuteBlockRequest contains all the information needed to create a new rollup +// block. +// +// This information comes from previous rollup blocks, as well as from sequencer +// blocks. +message ExecuteBlockRequest { + // The hash of previous block, which new block will be created on top of. + bytes prev_block_hash = 1; + // List of transactions to include in the new block. + repeated astria.sequencerblock.v1.RollupData transactions = 2; + // Timestamp to be used for new block. + google.protobuf.Timestamp timestamp = 3; +} + +// The CommitmentState holds the block at each stage of sequencer commitment +// level +// +// A Valid CommitmentState: +// - Block numbers are such that soft >= firm. +// - No blocks ever decrease in block number. +// - The chain defined by soft is the head of the canonical chain the firm block +// must belong to. +message CommitmentState { + // Soft commitment is the rollup block matching latest sequencer block. + Block soft = 1; + // Firm commitment is achieved when data has been seen in DA. + Block firm = 2; + // The lowest block number of celestia chain to be searched for rollup blocks given current state + uint64 base_celestia_height = 3; +} + +// There is only one CommitmentState object, so the request is empty. +message GetCommitmentStateRequest {} + +// The CommitmentState to set, must include complete state. +message UpdateCommitmentStateRequest { + CommitmentState commitment_state = 1; +} + +// ExecutionService is used to drive deterministic production of blocks. +// +// The service can be implemented by any blockchain which wants to utilize the +// Astria Shared Sequencer, and will have block production driven via the Astria +// "Conductor". +service ExecutionService { + // GetGenesisInfo returns the necessary genesis information for rollup chain. + rpc GetGenesisInfo(GetGenesisInfoRequest) returns (GenesisInfo); + + // GetBlock will return a block given an identifier. + rpc GetBlock(GetBlockRequest) returns (Block); + + // BatchGetBlocks will return an array of Blocks given an array of block + // identifiers. + rpc BatchGetBlocks(BatchGetBlocksRequest) returns (BatchGetBlocksResponse); + + // ExecuteBlock is called to deterministically derive a rollup block from + // filtered sequencer block information. + rpc ExecuteBlock(ExecuteBlockRequest) returns (Block); + + // GetCommitmentState fetches the current CommitmentState of the chain. + rpc GetCommitmentState(GetCommitmentStateRequest) returns (CommitmentState); + + // UpdateCommitmentState replaces the whole CommitmentState with a new + // CommitmentState. + rpc UpdateCommitmentState(UpdateCommitmentStateRequest) returns (CommitmentState); +} diff --git a/specs/conductor.md b/specs/conductor.md index a2075922d9..6a6a1b364d 100644 --- a/specs/conductor.md +++ b/specs/conductor.md @@ -126,3 +126,8 @@ only `CommitmentState.firm.number += 1` is advanced). Soft being ahead of firm is the expected operation. In certain rare situations the numbers can match exactly, and step `firm-only.10` and `firm-only.11` are executed as written. + +## Startup, Restarts, Execution, and Commitments + +See [`astria.execution.v1alpha2` API documentation](./execution-api.md) for more +information on Conductor startup, restart, execution, and commitment logic. diff --git a/specs/execution-api.md b/specs/execution-api.md index c765229007..752134bb75 100644 --- a/specs/execution-api.md +++ b/specs/execution-api.md @@ -29,6 +29,32 @@ previous block data, Conductor must also track the block hash of any blocks between commitments, it will call `BatchGetBlocks` to get block information between commitments. +### Restart + +The conductor is able to gracefully restart under two scenarios: + +1. Conductor recieves a `PermissionDenied` status from the execution layer when +calling `ExecuteBlock`. This is meant to function seamlessly with [`astria-geth`](https://github.com/astriaorg/astria-geth)'s +behavior upon an unexpected restart. If `geth` receives a `ExecuteBlock` request +before receving *both* `GetGenesisInfo` and `GetCommitmentState` (which will be +the case if the execution layer has restarted), it responds with a `PremissionDenied` +status, prompting the conductor to restart. +2. `GenesisInfo` contains a `rollup_stop_block_number` and has a `halt_at_rollup_stop_number` +value of `false`, indicating a planned restart after execution of the given block. +Once the conductor reaches the stop height, it will perform a restart in one of the +following ways corresponding to its mode: + + - **Firm Only Mode**: Once the stop height is reached for the firm block stream, + the firm block at this height is executed (and commitment state updated) before + restarting the conductor, prompting the rollup for a new `GenesisInfo` with + new start/stop heights (and potentially chain IDs). + - **Soft and Firm Mode**: Once the stop height is reached for the soft block + stream, the block at this height will be executed. The conductor will then wait + for the firm block at the stop height, execute it, and restart. If the firm + block is executed first, conductor will restart immediately. + - **Soft Only Mode**: Once the stop height is reached for the soft block stream, + the block at this height is executed and Conductor restarts. + ### Execution & Commitments From the perspective of the sequencer: @@ -74,9 +100,18 @@ Note: For our EVM rollup, we map the `CommitmentState` to the `ForkchoiceRule`: ### GetGenesisInfo `GetGenesisInfo` returns information which is definitional to the rollup with -regards to how it serves data from the sequencer & celestia networks. This RPC -should ALWAYS succeed. The API is agnostic as to how the information is defined -in a rollups genesis, and used by the conductor as configuration on startup. +regards to how it serves data from the sequencer & celestia networks, along with +optional block heights for initiating a [conductor restart](#restart). This RPC +should ***always*** succeed. The API is agnostic as to how the information is defined +in a rollup's genesis, and used by the conductor as configuration on startup *or* +upon a restart. + +If the `GenesisInfo` provided by this RPC contains a `rollup_stop_block_number`, +the rollup should be prepared to provide an updated response when the conductor +restarts, including, at minimum, a new `rollup_start_block_number` and `sequencer_start_height`. +The updated response can also contain an updated `rollup_stop_block_number` (if +another restart is desired), `celestia_chain_id`, and/or `sequencer_chain_id` +(to facilitate network migration). ### ExecuteBlock From 959e08fc2362f678f4674cc23d078138f464c6fb Mon Sep 17 00:00:00 2001 From: ethanoroshiba Date: Thu, 27 Feb 2025 14:38:49 -0600 Subject: [PATCH 2/6] implement v2 execution api --- crates/astria-auctioneer/src/block/mod.rs | 12 +- crates/astria-conductor/src/celestia/mod.rs | 45 +- .../astria-conductor/src/conductor/inner.rs | 153 +-- .../astria-conductor/src/executor/client.rs | 118 +-- crates/astria-conductor/src/executor/mod.rs | 118 +-- crates/astria-conductor/src/executor/state.rs | 222 ++-- crates/astria-conductor/src/executor/tests.rs | 45 +- crates/astria-conductor/src/metrics.rs | 10 +- crates/astria-conductor/src/test_utils.rs | 47 +- .../tests/blackbox/firm_only.rs | 415 ++++---- .../tests/blackbox/helpers/macros.rs | 304 +++--- .../tests/blackbox/helpers/mock_grpc.rs | 20 +- .../tests/blackbox/helpers/mod.rs | 62 +- .../tests/blackbox/soft_and_firm.rs | 731 ++++++------- .../tests/blackbox/soft_only.rs | 427 ++++---- crates/astria-core/src/execution/mod.rs | 1 - crates/astria-core/src/execution/v1/mod.rs | 468 --------- crates/astria-core/src/execution/v2/mod.rs | 545 ++++++---- .../src/generated/astria.bundle.v1alpha1.rs | 762 -------------- .../src/generated/astria.execution.v2.rs | 472 --------- .../generated/astria.execution.v2.serde.rs | 960 +----------------- .../astria.optimistic_execution.v1alpha1.rs | 4 +- crates/astria-core/src/generated/mod.rs | 9 - .../v1alpha1/optimistic_execution.proto | 38 - .../astria/execution/v2/execution.proto | 142 --- ...ute_optimistic_block_stream_response.proto | 4 +- specs/conductor.md | 88 +- 27 files changed, 1717 insertions(+), 4505 deletions(-) delete mode 100644 crates/astria-core/src/execution/v1/mod.rs delete mode 100644 crates/astria-core/src/generated/astria.bundle.v1alpha1.rs delete mode 100644 proto/executionapis/astria/bundle/v1alpha1/optimistic_execution.proto delete mode 100644 proto/executionapis/astria/execution/v2/execution.proto diff --git a/crates/astria-auctioneer/src/block/mod.rs b/crates/astria-auctioneer/src/block/mod.rs index 53475a69d1..d0ba1de265 100644 --- a/crates/astria-auctioneer/src/block/mod.rs +++ b/crates/astria-auctioneer/src/block/mod.rs @@ -97,7 +97,7 @@ impl Proposed { #[derive(Debug, Clone)] pub(crate) struct Executed { /// The rollup block metadata that resulted from executing a proposed Sequencer block. - block: execution::v1::Block, + block_metadata: execution::v2::ExecutedBlockMetadata, /// The hash of the sequencer block that was executed optimistically. sequencer_block_hash: block::Hash, } @@ -106,8 +106,9 @@ impl Executed { pub(crate) fn try_from_raw( raw: optimistic_execution::ExecuteOptimisticBlockStreamResponse, ) -> eyre::Result { - let block = if let Some(raw_block) = raw.block { - execution::v1::Block::try_from_raw(raw_block).wrap_err("invalid rollup block")? + let block_metadata = if let Some(raw_block_metadata) = raw.block { + execution::v2::ExecutedBlockMetadata::try_from_raw(raw_block_metadata) + .wrap_err("invalid rollup block")? } else { return Err(eyre!("missing block")); }; @@ -119,7 +120,7 @@ impl Executed { .wrap_err("invalid block hash")?; Ok(Self { - block, + block_metadata, sequencer_block_hash, }) } @@ -129,6 +130,7 @@ impl Executed { } pub(crate) fn rollup_block_hash(&self) -> RollupBlockHash { - RollupBlockHash::new(self.block.hash().clone()) + let bytes = self.block_metadata.hash().to_string().into_bytes().into(); + RollupBlockHash::new(bytes) } } diff --git a/crates/astria-conductor/src/celestia/mod.rs b/crates/astria-conductor/src/celestia/mod.rs index 3b76c19c5b..1c1efd297b 100644 --- a/crates/astria-conductor/src/celestia/mod.rs +++ b/crates/astria-conductor/src/celestia/mod.rs @@ -275,18 +275,18 @@ struct RunningReader { /// The next Celestia height that will be fetched. celestia_next_height: u64, - /// The reference Celestia height. `celestia_reference_height` + `celestia_variance` = C is the - /// maximum Celestia height up to which Celestia's blobs will be fetched. - /// `celestia_reference_height` is initialized to the base Celestia height stored in the - /// rollup genesis. It is later advanced to that Celestia height from which the next block - /// is derived that will be executed against the rollup (only if greater than the current - /// value; it will never go down). + /// The reference Celestia height. `celestia_reference_height` + + /// `celestia_search_height_max_look_ahead` = C is the maximum Celestia height up to which + /// Celestia's blobs will be fetched. `celestia_reference_height` is initialized to the + /// base Celestia height stored in the rollup state. It is later advanced to that Celestia + /// height from which the next block is derived that will be executed against the rollup + /// (only if greater than the current value; it will never go down). celestia_reference_height: u64, - /// `celestia_variance` + `celestia_reference_height` define the maximum Celestia height from - /// Celestia blobs can be fetched. Set once during initialization to the value stored in - /// the rollup genesis. - celestia_variance: u64, + /// `celestia_search_height_max_look_ahead` + `celestia_reference_height` define the maximum + /// Celestia height from Celestia blobs that can be fetched. Set once during initialization + /// to the value stored in the rollup state. + celestia_search_height_max_look_ahead: u64, /// The rollup ID of the rollup that conductor is driving. Set once during initialization to /// the value stored in the @@ -332,9 +332,10 @@ impl RunningReader { let sequencer_namespace = astria_core::celestia::namespace_v0_from_sha256_of_bytes(sequencer_chain_id.as_bytes()); - let celestia_next_height = rollup_state.celestia_base_block_height(); - let celestia_reference_height = rollup_state.celestia_base_block_height(); - let celestia_variance = rollup_state.celestia_block_variance(); + let celestia_next_height = rollup_state.lowest_celestia_search_height(); + let celestia_reference_height = rollup_state.lowest_celestia_search_height(); + let celestia_search_height_max_look_ahead = + rollup_state.celestia_search_height_max_look_ahead(); Ok(Self { block_cache, @@ -353,7 +354,7 @@ impl RunningReader { celestia_head_height: None, celestia_next_height, celestia_reference_height, - celestia_variance, + celestia_search_height_max_look_ahead, rollup_id, rollup_namespace, @@ -368,7 +369,7 @@ impl RunningReader { info!( initial_celestia_height = self.celestia_next_height, initial_max_celestia_height = self.max_permitted_celestia_height(), - celestia_variance = self.celestia_variance, + celestia_search_height_max_look_ahead = self.celestia_search_height_max_look_ahead, rollup_namespace = %base64(&self.rollup_namespace.as_bytes()), rollup_id = %self.rollup_id, sequencer_chain_id = %self.sequencer_chain_id, @@ -544,14 +545,14 @@ impl RunningReader { /// Returns the maximum permitted Celestia height given the current state. /// - /// The maximum permitted Celestia height is calculated as `ref_height + 6 * variance`, with: + /// The maximum permitted Celestia height is calculated as `ref_height + + /// celestia_search_height_max_look_ahead`, with: /// /// - `ref_height` the height from which the last expected sequencer block was derived, - /// - `variance` the `celestia_block_variance` received from the connected rollup genesis info, - /// - and the factor 6 based on the assumption that there are up to 6 sequencer heights stored - /// per Celestia height. + /// - `celestia_search_height_max_look_ahead` received from the current rollup state, fn max_permitted_celestia_height(&self) -> u64 { - max_permitted_celestia_height(self.celestia_reference_height, self.celestia_variance) + self.celestia_reference_height + .saturating_add(self.celestia_search_height_max_look_ahead) } fn record_latest_celestia_height(&mut self, height: u64) { @@ -706,10 +707,6 @@ async fn get_sequencer_chain_id(client: SequencerClient) -> eyre::Result u64 { - reference.saturating_add(variance.saturating_mul(6)) -} - #[instrument(skip_all)] fn report_exit(exit_reason: eyre::Result<&str>, message: &str) -> eyre::Result<()> { match exit_reason { diff --git a/crates/astria-conductor/src/conductor/inner.rs b/crates/astria-conductor/src/conductor/inner.rs index ba0cca42c8..96856d16c5 100644 --- a/crates/astria-conductor/src/conductor/inner.rs +++ b/crates/astria-conductor/src/conductor/inner.rs @@ -109,7 +109,7 @@ impl Inner { self.restart_or_shutdown(exit_status).await } - /// Shuts down all tasks. + /// Shuts down all tasks and returns a token indicating whether to restart or not. /// /// Waits 25 seconds for all tasks to shut down before aborting them. 25 seconds /// because kubernetes issues SIGKILL 30 seconds after SIGTERM, giving 5 seconds @@ -164,7 +164,10 @@ fn should_restart_despite_error(err: &eyre::Report) -> bool { let mut current = Some(err.as_ref() as &dyn std::error::Error); while let Some(err) = current { if let Some(status) = err.downcast_ref::() { - if status.code() == tonic::Code::PermissionDenied { + if status.code() == tonic::Code::PermissionDenied + // Fallback in case execute block is called outside of execution session bounds + || status.code() == tonic::Code::OutOfRange + { return true; } } @@ -177,7 +180,7 @@ fn should_restart_or_shutdown( config: &Config, status: &crate::executor::State, ) -> eyre::Result { - let Some(rollup_stop_block_number) = status.rollup_stop_block_number() else { + let Some(rollup_stop_block_number) = status.rollup_end_block_number() else { return Err(eyre!( "executor exited with a success value even though it was not configured to run with a \ stop height and even though it received no shutdown signal; this should not happen" @@ -187,12 +190,7 @@ fn should_restart_or_shutdown( match config.execution_commit_level { crate::config::CommitLevel::FirmOnly | crate::config::CommitLevel::SoftAndFirm => { if status.has_firm_number_reached_stop_height() { - let restart_or_shutdown = if status.halt_at_rollup_stop_number() { - RestartOrShutdown::Shutdown - } else { - RestartOrShutdown::Restart - }; - Ok(restart_or_shutdown) + Ok(RestartOrShutdown::Restart) } else { Err(eyre!( "executor exited with a success value, but the stop height was not reached @@ -203,19 +201,14 @@ fn should_restart_or_shutdown( status.firm_number(), status.firm_block_number_as_sequencer_height(), status.rollup_start_block_number(), - status.sequencer_start_height(), + status.sequencer_start_block_height(), rollup_stop_block_number, )) } } crate::config::CommitLevel::SoftOnly => { if status.has_soft_number_reached_stop_height() { - let restart_or_shutdown = if status.halt_at_rollup_stop_number() { - RestartOrShutdown::Shutdown - } else { - RestartOrShutdown::Restart - }; - Ok(restart_or_shutdown) + Ok(RestartOrShutdown::Restart) } else { Err(eyre!( "executor exited with a success value, but the stop height was not reached @@ -226,7 +219,7 @@ fn should_restart_or_shutdown( status.soft_number(), status.soft_block_number_as_sequencer_height(), status.rollup_start_block_number(), - status.sequencer_start_height(), + status.sequencer_start_block_height(), rollup_stop_block_number, )) } @@ -237,9 +230,9 @@ fn should_restart_or_shutdown( #[cfg(test)] mod tests { use astria_core::generated::astria::execution::v2::{ - Block, CommitmentState, - GenesisInfo, + ExecutedBlockMetadata, + ExecutionSessionParameters, }; use astria_eyre::eyre::WrapErr as _; use pbjson_types::Timestamp; @@ -252,7 +245,7 @@ mod tests { config::CommitLevel, test_utils::{ make_commitment_state, - make_genesis_info, + make_execution_session_parameters, make_rollup_state, }, Config, @@ -279,15 +272,19 @@ mod tests { } } - #[test] - fn should_restart_despite_error() { - let tonic_error: Result<&str, tonic::Status> = - Err(tonic::Status::new(tonic::Code::PermissionDenied, "error")); + #[track_caller] + fn should_restart_despite_error_test(code: tonic::Code) { + let tonic_error: Result<&str, tonic::Status> = Err(tonic::Status::new(code, "error")); let err = tonic_error.wrap_err("wrapper_1"); let err = err.wrap_err("wrapper_2"); let err = err.wrap_err("wrapper_3"); assert!(super::should_restart_despite_error(&err.unwrap_err())); } + #[test] + fn should_restart_despite_error() { + should_restart_despite_error_test(tonic::Code::PermissionDenied); + should_restart_despite_error_test(tonic::Code::OutOfRange); + } #[track_caller] fn assert_restart_or_shutdown( @@ -309,24 +306,24 @@ mod tests { ..make_config() }, &make_rollup_state( - GenesisInfo { - sequencer_start_height: 10, + "test_execution_session".to_string(), + ExecutionSessionParameters { + sequencer_start_block_height: 10, rollup_start_block_number: 10, - rollup_stop_block_number: 99, - halt_at_rollup_stop_number: false, - ..make_genesis_info() + rollup_end_block_number: 99, + ..make_execution_session_parameters() }, CommitmentState { - firm: Some(Block { + firm_executed_block_metadata: Some(ExecutedBlockMetadata { number: 99, - hash: vec![0u8; 32].into(), - parent_block_hash: vec![].into(), + hash: hex::encode([0u8; 32]).to_string(), + parent_hash: String::new(), timestamp: Some(Timestamp::default()), }), - soft: Some(Block { + soft_executed_block_metadata: Some(ExecutedBlockMetadata { number: 99, - hash: vec![0u8; 32].into(), - parent_block_hash: vec![].into(), + hash: hex::encode([0u8; 32]).to_string(), + parent_hash: String::new(), timestamp: Some(Timestamp::default()), }), ..make_commitment_state() @@ -334,38 +331,6 @@ mod tests { ), &RestartOrShutdown::Restart, ); - - assert_restart_or_shutdown( - &Config { - execution_commit_level: CommitLevel::SoftAndFirm, - ..make_config() - }, - &make_rollup_state( - GenesisInfo { - sequencer_start_height: 10, - rollup_start_block_number: 10, - rollup_stop_block_number: 99, - halt_at_rollup_stop_number: true, - ..make_genesis_info() - }, - CommitmentState { - firm: Some(Block { - number: 99, - hash: vec![0u8; 32].into(), - parent_block_hash: vec![].into(), - timestamp: Some(Timestamp::default()), - }), - soft: Some(Block { - number: 99, - hash: vec![0u8; 32].into(), - parent_block_hash: vec![].into(), - timestamp: Some(Timestamp::default()), - }), - ..make_commitment_state() - }, - ), - &RestartOrShutdown::Shutdown, - ); } #[test] @@ -376,24 +341,24 @@ mod tests { ..make_config() }, &make_rollup_state( - GenesisInfo { - sequencer_start_height: 10, + "test_execution_session".to_string(), + ExecutionSessionParameters { + sequencer_start_block_height: 10, rollup_start_block_number: 10, - rollup_stop_block_number: 99, - halt_at_rollup_stop_number: false, - ..make_genesis_info() + rollup_end_block_number: 99, + ..make_execution_session_parameters() }, CommitmentState { - firm: Some(Block { + firm_executed_block_metadata: Some(ExecutedBlockMetadata { number: 99, - hash: vec![0u8; 32].into(), - parent_block_hash: vec![].into(), + hash: hex::encode([0u8; 32]).to_string(), + parent_hash: String::new(), timestamp: Some(Timestamp::default()), }), - soft: Some(Block { + soft_executed_block_metadata: Some(ExecutedBlockMetadata { number: 99, - hash: vec![0u8; 32].into(), - parent_block_hash: vec![].into(), + hash: hex::encode([0u8; 32]).to_string(), + parent_hash: String::new(), timestamp: Some(Timestamp::default()), }), ..make_commitment_state() @@ -401,37 +366,5 @@ mod tests { ), &RestartOrShutdown::Restart, ); - - assert_restart_or_shutdown( - &Config { - execution_commit_level: CommitLevel::SoftOnly, - ..make_config() - }, - &make_rollup_state( - GenesisInfo { - sequencer_start_height: 10, - rollup_start_block_number: 10, - rollup_stop_block_number: 99, - halt_at_rollup_stop_number: true, - ..make_genesis_info() - }, - CommitmentState { - firm: Some(Block { - number: 99, - hash: vec![0u8; 32].into(), - parent_block_hash: vec![].into(), - timestamp: Some(Timestamp::default()), - }), - soft: Some(Block { - number: 99, - hash: vec![0u8; 32].into(), - parent_block_hash: vec![].into(), - timestamp: Some(Timestamp::default()), - }), - ..make_commitment_state() - }, - ), - &RestartOrShutdown::Shutdown, - ); } } diff --git a/crates/astria-conductor/src/executor/client.rs b/crates/astria-conductor/src/executor/client.rs index fd0bf6d9e2..f2b0b180f8 100644 --- a/crates/astria-conductor/src/executor/client.rs +++ b/crates/astria-conductor/src/executor/client.rs @@ -2,14 +2,14 @@ use std::time::Duration; use astria_core::{ execution::v2::{ - Block, CommitmentState, - GenesisInfo, + ExecutedBlockMetadata, + ExecutionSession, }, generated::astria::{ - execution::{ - v2 as raw, - v2::execution_service_client::ExecutionServiceClient, + execution::v2::{ + self as raw, + execution_service_client::ExecutionServiceClient, }, sequencerblock::v1::RollupData, }, @@ -18,6 +18,7 @@ use astria_core::{ use astria_eyre::eyre::{ self, ensure, + OptionExt as _, WrapErr as _, }; use bytes::Bytes; @@ -59,15 +60,18 @@ impl Client { }) } - /// Calls RPC astria.execution.v1.GetBlock + /// Calls RPC astria.execution.v2.GetExecutedBlockMetadata #[instrument(skip_all, fields(block_number, uri = %self.uri), err)] - pub(crate) async fn get_block_with_retry(&mut self, block_number: u32) -> eyre::Result { - let raw_block = tryhard::retry_fn(|| { + pub(crate) async fn get_block_with_retry( + &mut self, + block_number: u64, + ) -> eyre::Result { + let raw_block_metadata = tryhard::retry_fn(|| { let mut client = self.inner.clone(); - let request = raw::GetBlockRequest { + let request = raw::GetExecutedBlockMetadataRequest { identifier: Some(block_identifier(block_number)), }; - async move { client.get_block(request).await } + async move { client.get_executed_block_metadata(request).await } }) .with_config(retry_config()) .in_current_span() @@ -78,48 +82,53 @@ impl Client { )? .into_inner(); ensure!( - block_number == raw_block.number, + block_number == raw_block_metadata.number, "requested block at number `{block_number}`, but received block contained `{}`", - raw_block.number + raw_block_metadata.number ); - Block::try_from_raw(raw_block).wrap_err("failed validating received block") + ExecutedBlockMetadata::try_from_raw(raw_block_metadata) + .wrap_err("failed validating received block") } - /// Calls remote procedure `astria.execution.v1.GetGenesisInfo` + /// Calls remote procedure `astria.execution.v2.CreateExecutionSession` #[instrument(skip_all, fields(uri = %self.uri), err)] - pub(crate) async fn get_genesis_info_with_retry(&mut self) -> eyre::Result { + pub(crate) async fn create_execution_session_with_retry( + &mut self, + ) -> eyre::Result { let response = tryhard::retry_fn(|| { let mut client = self.inner.clone(); - let request = raw::GetGenesisInfoRequest {}; - async move { client.get_genesis_info(request).await } + let request = raw::CreateExecutionSessionRequest {}; + async move { client.create_execution_session(request).await } }) .with_config(retry_config()) .in_current_span() .await .wrap_err( - "failed to execute astria.execution.v1.GetGenesisInfo RPC because of gRPC status code \ - or because number of retries were exhausted", + "failed to execute astria.execution.v2.CreateExecutionSession RPC because of gRPC \ + status code or because number of retries were exhausted", )? .into_inner(); - let genesis_info = GenesisInfo::try_from_raw(response) - .wrap_err("failed converting raw response to validated genesis info")?; - Ok(genesis_info) + let execution_session = ExecutionSession::try_from_raw(response) + .wrap_err("failed converting raw response to validated execution session")?; + Ok(execution_session) } - /// Calls remote procedure `astria.execution.v1.ExecuteBlock` + /// Calls remote procedure `astria.execution.v2.ExecuteBlock`. /// /// # Arguments /// - /// * `prev_block_hash` - Block hash of the parent block + /// * `session_id` - ID of the current execution session + /// * `parent_hash` - Hash of the parent block /// * `transactions` - List of transactions extracted from the sequencer block /// * `timestamp` - Optional timestamp of the sequencer block #[instrument(skip_all, fields(uri = %self.uri), err)] pub(super) async fn execute_block_with_retry( &mut self, - prev_block_hash: Bytes, + session_id: String, + parent_hash: String, transactions: Vec, timestamp: Timestamp, - ) -> eyre::Result { + ) -> eyre::Result { use prost::Message; let transactions = transactions @@ -129,7 +138,8 @@ impl Client { .wrap_err("failed to decode tx bytes as RollupData")?; let request = raw::ExecuteBlockRequest { - prev_block_hash, + session_id, + parent_hash, transactions, timestamp: Some(timestamp), }; @@ -142,52 +152,32 @@ impl Client { .in_current_span() .await .wrap_err( - "failed to execute astria.execution.v1.ExecuteBlock RPC because of gRPC status code \ + "failed to execute astria.execution.v2.ExecuteBlock RPC because of gRPC status code \ or because number of retries were exhausted", )? .into_inner(); - let block = Block::try_from_raw(response) - .wrap_err("failed converting raw response to validated block")?; - Ok(block) - } - - /// Calls remote procedure `astria.execution.v1.GetCommitmentState` - #[instrument(skip_all, fields(uri = %self.uri), err)] - pub(crate) async fn get_commitment_state_with_retry( - &mut self, - ) -> eyre::Result { - let response = tryhard::retry_fn(|| { - let mut client = self.inner.clone(); - async move { - let request = raw::GetCommitmentStateRequest {}; - client.get_commitment_state(request).await - } - }) - .with_config(retry_config()) - .in_current_span() - .await - .wrap_err( - "failed to execute astria.execution.v1.GetCommitmentState RPC because of gRPC status \ - code or because number of retries were exhausted", - )? - .into_inner(); - let commitment_state = CommitmentState::try_from_raw(response) - .wrap_err("failed converting raw response to validated commitment state")?; - Ok(commitment_state) + let response_metadata = response + .executed_block_metadata + .ok_or_eyre("response is missing executed block metadata")?; + let block_metadata = ExecutedBlockMetadata::try_from_raw(response_metadata) + .wrap_err("failed converting raw response to validated block metadata")?; + Ok(block_metadata) } - /// Calls remote procedure `astria.execution.v1.UpdateCommitmentState` + /// Calls remote procedure `astria.execution.v2.UpdateCommitmentState` /// /// # Arguments /// - /// * `firm` - The firm block - /// * `soft` - The soft block + /// * `session_id` - ID of the current execution session + /// * `commitment_state` - New commitment state to be updated with #[instrument(skip_all, fields(uri = %self.uri), err)] pub(super) async fn update_commitment_state_with_retry( &mut self, + session_id: String, commitment_state: CommitmentState, ) -> eyre::Result { let request = raw::UpdateCommitmentStateRequest { + session_id, commitment_state: Some(commitment_state.into_raw()), }; let response = tryhard::retry_fn(|| { @@ -199,7 +189,7 @@ impl Client { .in_current_span() .await .wrap_err( - "failed to execute astria.execution.v1.UpdateCommitmentState RPC because of gRPC \ + "failed to execute astria.execution.v2.UpdateCommitmentState RPC because of gRPC \ status code or because number of retries were exhausted", )? .into_inner(); @@ -209,11 +199,11 @@ impl Client { } } -/// Utility function to construct a `astria.execution.v1.BlockIdentifier` from `number` +/// Utility function to construct a `astria.execution.v2.ExecutedBlockIdentifier` from `number` /// to use in RPC requests. -fn block_identifier(number: u32) -> raw::BlockIdentifier { - raw::BlockIdentifier { - identifier: Some(raw::block_identifier::Identifier::BlockNumber(number)), +fn block_identifier(number: u64) -> raw::ExecutedBlockIdentifier { + raw::ExecutedBlockIdentifier { + identifier: Some(raw::executed_block_identifier::Identifier::Number(number)), } } diff --git a/crates/astria-conductor/src/executor/mod.rs b/crates/astria-conductor/src/executor/mod.rs index bcfb6dd669..feef682114 100644 --- a/crates/astria-conductor/src/executor/mod.rs +++ b/crates/astria-conductor/src/executor/mod.rs @@ -5,8 +5,8 @@ use std::{ use astria_core::{ execution::v2::{ - Block, CommitmentState, + ExecutedBlockMetadata, }, primitive::v1::RollupId, sequencerblock::v1::block::{ @@ -194,30 +194,18 @@ impl Executor { #[instrument(skip_all, err, ret(Display))] async fn create_initial_node_state(&self) -> eyre::Result { - let genesis_info = { - async { - self.client - .clone() - .get_genesis_info_with_retry() - .await - .wrap_err("failed getting genesis info") - } - }; - let commitment_state = { - async { - self.client - .clone() - .get_commitment_state_with_retry() - .await - .wrap_err("failed getting commitment state") - } - }; - let (genesis_info, commitment_state) = tokio::try_join!(genesis_info, commitment_state)?; + let execution_session = self + .client + .clone() + .create_execution_session_with_retry() + .await + .wrap_err("failed getting genesis info")?; let (state, _) = state::channel( - State::try_from_genesis_info_and_commitment_state( - genesis_info, - commitment_state, + State::try_from_execution_session_parameters_and_commitment_state( + execution_session.session_id().clone(), + execution_session.execution_session_parameters().clone(), + execution_session.commitment_state().clone(), self.config.execution_commit_level, ) .wrap_err( @@ -258,7 +246,7 @@ struct Initialized { /// /// Required to mark firm blocks received from celestia as executed /// without re-executing on top of the rollup node. - blocks_pending_finalization: HashMap, + blocks_pending_finalization: HashMap, metrics: &'static Metrics, @@ -374,28 +362,29 @@ impl Initialized { std::cmp::Ordering::Equal => {} } - let sequencer_start_height = self.state.sequencer_start_height(); + let sequencer_start_block_height = self.state.sequencer_start_block_height(); let rollup_start_block_number = self.state.rollup_start_block_number(); let current_block_height = executable_block.height; let Some(block_number) = state::map_sequencer_height_to_rollup_height( - sequencer_start_height, + sequencer_start_block_height, rollup_start_block_number, current_block_height, ) else { bail!( - "failed to map block height rollup number. This means the operation - `sequencer_height - sequencer_start_height + rollup_start_block_number` \ + "failed to map block height to rollup number. This means the operation + `sequencer_height - sequencer_start_block_height + rollup_start_block_number` \ underflowed or was not a valid cometbft height. Sequencer height: \ `{current_block_height}`, - sequencer start height: `{sequencer_start_height}`, - rollup start height: `{rollup_start_block_number}`" + sequencer start height: `{sequencer_start_block_height}`, + rollup start number: `{rollup_start_block_number}`" ) }; // The parent hash of the next block is the hash of the block at the current head. let parent_hash = self.state.soft_hash(); + let session_id = self.state.execution_session_id(); let executed_block = self - .execute_block(parent_hash, executable_block) + .execute_block(session_id, parent_hash, executable_block) .await .wrap_err("failed to execute block")?; @@ -432,27 +421,28 @@ impl Initialized { "expected block at sequencer height {expected_height}, but got {block_height}", ); - let sequencer_start_height = self.state.sequencer_start_height(); + let sequencer_start_block_height = self.state.sequencer_start_block_height(); let rollup_start_block_number = self.state.rollup_start_block_number(); let Some(block_number) = state::map_sequencer_height_to_rollup_height( - sequencer_start_height, + sequencer_start_block_height, rollup_start_block_number, block_height, ) else { bail!( - "failed to map block height rollup number. This means the operation - `sequencer_height - sequencer_start_height + rollup_start_block_number` \ + "failed to map block height to rollup number. This means the operation + `sequencer_height - sequencer_start_block_height + rollup_start_block_number` \ underflowed or was not a valid cometbft height. Sequencer block height: \ `{block_height}`, - sequencer start height: `{sequencer_start_height}`, - rollup start height: `{rollup_start_block_number}`" + sequencer start height: `{sequencer_start_block_height}`, + rollup start number: `{rollup_start_block_number}`" ) }; let update = if self.should_execute_firm_block() { let parent_hash = self.state.firm_hash(); + let session_id = self.state.execution_session_id(); let executed_block = self - .execute_block(parent_hash, executable_block) + .execute_block(session_id, parent_hash, executable_block) .await .wrap_err("failed to execute block")?; self.does_block_response_fulfill_contract(ExecutionKind::Firm, &executed_block) @@ -511,9 +501,10 @@ impl Initialized { ))] async fn execute_block( &mut self, - parent_hash: Bytes, + session_id: String, + parent_hash: String, block: ExecutableBlock, - ) -> eyre::Result { + ) -> eyre::Result { let ExecutableBlock { transactions, timestamp, @@ -522,9 +513,9 @@ impl Initialized { let n_transactions = transactions.len(); - let executed_block = self + let executed_block_metadata = self .client - .execute_block_with_retry(parent_hash, transactions, timestamp) + .execute_block_with_retry(session_id, parent_hash, transactions, timestamp) .await .wrap_err("failed to run execute_block RPC")?; @@ -532,12 +523,12 @@ impl Initialized { .record_transactions_per_executed_block(n_transactions); info!( - executed_block.hash = %telemetry::display::base64(&executed_block.hash()), - executed_block.number = executed_block.number(), + executed_block_metadata.hash = %telemetry::display::base64(&executed_block_metadata.hash()), + executed_block_metadata.number = executed_block_metadata.number(), "executed block", ); - Ok(executed_block) + Ok(executed_block_metadata) } #[instrument(skip_all, err)] @@ -559,7 +550,7 @@ impl Initialized { OnlySoft(soft) => ( self.state.firm(), soft, - self.state.celestia_base_block_height(), + self.state.lowest_celestia_search_height(), CommitLevel::SoftOnly, ), ToSame(block, celestia_height) => ( @@ -570,14 +561,15 @@ impl Initialized { ), }; let commitment_state = CommitmentState::builder() - .firm(firm) - .soft(soft) - .base_celestia_height(celestia_height) + .firm_executed_block_metadata(firm) + .soft_executed_block_metadata(soft) + .lowest_celestia_search_height(celestia_height) .build() .wrap_err("failed constructing commitment state")?; + let session_id = self.state.execution_session_id(); let new_state = self .client - .update_commitment_state_with_retry(commitment_state) + .update_commitment_state_with_retry(session_id, commitment_state) .await .wrap_err("failed updating remote commitment state")?; info!( @@ -596,9 +588,9 @@ impl Initialized { fn does_block_response_fulfill_contract( &mut self, kind: ExecutionKind, - block: &Block, + block_metadata: &ExecutedBlockMetadata, ) -> Result<(), ContractViolation> { - does_block_response_fulfill_contract(&mut self.state, kind, block) + does_block_response_fulfill_contract(&mut self.state, kind, block_metadata) } /// Returns whether a firm block should be executed. @@ -623,7 +615,7 @@ impl Initialized { match task { ReaderKind::Firm if self.config.is_with_firm() => { match ( - self.state.rollup_stop_block_number().is_some(), + self.state.rollup_end_block_number().is_some(), self.state.has_firm_number_reached_stop_height(), ) { (true, true) => { @@ -658,7 +650,7 @@ impl Initialized { ReaderKind::Soft if self.config.is_with_soft() => { match ( - self.state.rollup_stop_block_number().is_some(), + self.state.rollup_end_block_number().is_some(), self.state.has_soft_number_reached_stop_height(), ) { (true, true) => { @@ -719,9 +711,9 @@ impl Initialized { } enum Update { - OnlyFirm(Block, CelestiaHeight), - OnlySoft(Block), - ToSame(Block, CelestiaHeight), + OnlyFirm(ExecutedBlockMetadata, CelestiaHeight), + OnlySoft(ExecutedBlockMetadata), + ToSame(ExecutedBlockMetadata, CelestiaHeight), } #[derive(Debug)] @@ -807,24 +799,24 @@ enum ContractViolation { )] WrongBlock { kind: ExecutionKind, - current: u32, - expected: u32, - actual: u32, + current: u64, + expected: u64, + actual: u64, }, #[error("contract violated: current height cannot be incremented")] - CurrentBlockNumberIsMax { kind: ExecutionKind, actual: u32 }, + CurrentBlockNumberIsMax { kind: ExecutionKind, actual: u64 }, } fn does_block_response_fulfill_contract( state: &mut StateSender, kind: ExecutionKind, - block: &Block, + block_metadata: &ExecutedBlockMetadata, ) -> Result<(), ContractViolation> { let current = match kind { ExecutionKind::Firm => state.firm_number(), ExecutionKind::Soft => state.soft_number(), }; - let actual = block.number(); + let actual = block_metadata.number(); let expected = current .checked_add(1) .ok_or(ContractViolation::CurrentBlockNumberIsMax { diff --git a/crates/astria-conductor/src/executor/state.rs b/crates/astria-conductor/src/executor/state.rs index 37fc136122..43d79e45a6 100644 --- a/crates/astria-conductor/src/executor/state.rs +++ b/crates/astria-conductor/src/executor/state.rs @@ -6,18 +6,16 @@ use std::num::NonZeroU64; use astria_core::{ execution::v2::{ - Block, CommitmentState, - GenesisInfo, + ExecutedBlockMetadata, + ExecutionSessionParameters, }, primitive::v1::RollupId, }; use astria_eyre::eyre::{ self, eyre, - WrapErr as _, }; -use bytes::Bytes; use sequencer_client::tendermint::block::Height as SequencerHeight; use tokio::sync::watch::{ self, @@ -85,19 +83,16 @@ impl StateReceiver { } pub(crate) fn sequencer_stop_height(&self) -> eyre::Result> { - let Some(rollup_stop_block_number) = self.inner.borrow().rollup_stop_block_number() else { + let Some(rollup_end_block_number) = self.inner.borrow().rollup_end_block_number() else { return Ok(None); }; - let sequencer_start_height = self.inner.borrow().sequencer_start_height(); + let sequencer_start_height = self.inner.borrow().sequencer_start_block_height(); let rollup_start_block_number = self.inner.borrow().rollup_start_block_number(); Ok(NonZeroU64::new( map_rollup_number_to_sequencer_height( sequencer_start_height, rollup_start_block_number, - rollup_stop_block_number - .get() - .try_into() - .wrap_err("rollup stop block number overflows u32::MAX")?, + rollup_end_block_number.get(), ) .map_err(|e| { eyre!(e).wrap_err("failed to map rollup stop block number to sequencer height") @@ -119,11 +114,11 @@ impl std::fmt::Display for StateSender { } fn map_firm_to_sequencer_height( - genesis_info: &GenesisInfo, + execution_session_parameters: &ExecutionSessionParameters, commitment_state: &CommitmentState, ) -> Result { - let sequencer_start_height = genesis_info.sequencer_start_height(); - let rollup_start_block_number = genesis_info.rollup_start_block_number(); + let sequencer_start_height = execution_session_parameters.sequencer_start_block_height(); + let rollup_start_block_number = execution_session_parameters.rollup_start_block_number(); let rollup_number = commitment_state.firm().number(); map_rollup_number_to_sequencer_height( @@ -136,16 +131,16 @@ fn map_firm_to_sequencer_height( issue, sequencer_start_height, rollup_start_block_number, - rollup_number: rollup_number.into(), + rollup_number, }) } fn map_soft_to_sequencer_height( - genesis_info: &GenesisInfo, + execution_session_parameters: &ExecutionSessionParameters, commitment_state: &CommitmentState, ) -> Result { - let sequencer_start_height = genesis_info.sequencer_start_height(); - let rollup_start_block_number = genesis_info.rollup_start_block_number(); + let sequencer_start_height = execution_session_parameters.sequencer_start_block_height(); + let rollup_start_block_number = execution_session_parameters.rollup_start_block_number(); let rollup_number = commitment_state.soft().number(); map_rollup_number_to_sequencer_height( @@ -158,7 +153,7 @@ fn map_soft_to_sequencer_height( issue, sequencer_start_height, rollup_start_block_number, - rollup_number: rollup_number.into(), + rollup_number, }) } @@ -171,21 +166,21 @@ impl StateSender { /// Calculates the maximum allowed spread between firm and soft commitments heights. /// - /// The maximum allowed spread is taken as `max_spread = variance * 6`, where `variance` - /// is the `celestia_block_variance` as defined in the rollup node's genesis that this - /// executor/conductor talks to. + /// The maximum allowed spread is taken as `max_spread = max_look_ahead * 3`, where + /// `max_look_ahead` is the `celestia_search_height_max_look_ahead` as defined in the + /// current execution session. /// - /// The heuristic 6 is the largest number of Sequencer heights that will be found at + /// The heuristic 3 is the largest number of Sequencer heights that will be found at /// one Celestia height. /// /// # Panics - /// Panics if the `u32` underlying the celestia block variance tracked in the state could - /// not be converted to a `usize`. This should never happen on any reasonable architecture - /// that Conductor will run on. + /// Panics if the `u64` underlying the celestia search height max look ahead tracked in the + /// state could not be converted to a `usize`. This should never happen on any reasonable + /// architecture that Conductor will run on. pub(super) fn calculate_max_spread(&self) -> usize { - usize::try_from(self.celestia_block_variance()) - .expect("converting a u32 to usize should work on any architecture conductor runs on") - .saturating_mul(6) + usize::try_from(self.celestia_search_height_max_look_ahead()) + .expect("converting a u64 to usize should work on any architecture conductor runs on") + .saturating_mul(3) } pub(super) fn try_update_commitment_state( @@ -193,12 +188,12 @@ impl StateSender { commitment_state: CommitmentState, commit_level: crate::config::CommitLevel, ) -> Result<(), InvalidState> { - let genesis_info = self.genesis_info(); + let execution_session_parameters = self.execution_session_parameters(); if commit_level.is_with_firm() { - let _ = map_firm_to_sequencer_height(&genesis_info, &commitment_state)?; + let _ = map_firm_to_sequencer_height(&execution_session_parameters, &commitment_state)?; } if commit_level.is_with_soft() { - let _ = map_soft_to_sequencer_height(&genesis_info, &commitment_state)?; + let _ = map_soft_to_sequencer_height(&execution_session_parameters, &commitment_state)?; } self.inner.send_modify(move |state| { state.set_commitment_state(commitment_state); @@ -248,27 +243,28 @@ macro_rules! forward_impls { forward_impls!( StateSender: - [genesis_info -> GenesisInfo], - [firm -> Block], - [soft -> Block], - [firm_number -> u32], - [soft_number -> u32], - [firm_hash -> Bytes], - [soft_hash -> Bytes], - [celestia_block_variance -> u64], + [execution_session_parameters -> ExecutionSessionParameters], + [execution_session_id -> String], + [firm -> ExecutedBlockMetadata], + [soft -> ExecutedBlockMetadata], + [firm_number -> u64], + [soft_number -> u64], + [firm_hash -> String], + [soft_hash -> String], [rollup_id -> RollupId], - [sequencer_start_height -> u64], - [celestia_base_block_height -> u64], + [sequencer_start_block_height -> u64], + [lowest_celestia_search_height -> u64], + [celestia_search_height_max_look_ahead -> u64], [rollup_start_block_number -> u64], - [rollup_stop_block_number -> Option], + [rollup_end_block_number -> Option], [has_firm_number_reached_stop_height -> bool], [has_soft_number_reached_stop_height -> bool], ); forward_impls!( StateReceiver: - [celestia_base_block_height -> u64], - [celestia_block_variance -> u64], + [lowest_celestia_search_height -> u64], + [celestia_search_height_max_look_ahead -> u64], [rollup_id -> RollupId], [sequencer_chain_id -> String], [celestia_chain_id -> String], @@ -276,43 +272,50 @@ forward_impls!( /// `State` tracks the genesis info and commitment state of the remote rollup node. #[derive(Clone, Debug, serde::Serialize)] +#[expect( + clippy::struct_field_names, + reason = "`commitment_state` is the most accurate name" +)] pub(crate) struct State { + execution_session_id: String, + execution_session_parameters: ExecutionSessionParameters, commitment_state: CommitmentState, - genesis_info: GenesisInfo, } impl State { - pub(crate) fn try_from_genesis_info_and_commitment_state( - genesis_info: GenesisInfo, + pub(crate) fn try_from_execution_session_parameters_and_commitment_state( + execution_session_id: String, + execution_session_parameters: ExecutionSessionParameters, commitment_state: CommitmentState, commit_level: crate::config::CommitLevel, ) -> Result { if commit_level.is_with_firm() { - let _ = map_firm_to_sequencer_height(&genesis_info, &commitment_state)?; + let _ = map_firm_to_sequencer_height(&execution_session_parameters, &commitment_state)?; } if commit_level.is_with_soft() { - let _ = map_soft_to_sequencer_height(&genesis_info, &commitment_state)?; + let _ = map_soft_to_sequencer_height(&execution_session_parameters, &commitment_state)?; } Ok(State { + execution_session_id, + execution_session_parameters, commitment_state, - genesis_info, }) } /// Returns if the tracked firm state of the rollup has reached the rollup stop block number. pub(crate) fn has_firm_number_reached_stop_height(&self) -> bool { - let Some(rollup_stop_block_number) = self.rollup_stop_block_number() else { + let Some(rollup_end_block_number) = self.rollup_end_block_number() else { return false; }; - u64::from(self.commitment_state.firm().number()) >= rollup_stop_block_number.get() + self.commitment_state.firm().number() >= rollup_end_block_number.get() } /// Returns if the tracked soft state of the rollup has reached the rollup stop block number. pub(crate) fn has_soft_number_reached_stop_height(&self) -> bool { - let Some(rollup_stop_block_number) = self.rollup_stop_block_number() else { + let Some(rollup_end_block_number) = self.rollup_end_block_number() else { return false; }; - u64::from(self.commitment_state.soft().number()) >= rollup_stop_block_number.get() + self.commitment_state.soft().number() >= rollup_end_block_number.get() } /// Sets the inner commitment state. @@ -320,91 +323,100 @@ impl State { self.commitment_state = commitment_state; } - fn genesis_info(&self) -> &GenesisInfo { - &self.genesis_info + fn execution_session_parameters(&self) -> &ExecutionSessionParameters { + &self.execution_session_parameters } - fn firm(&self) -> &Block { + fn execution_session_id(&self) -> String { + self.execution_session_id.clone() + } + + fn firm(&self) -> &ExecutedBlockMetadata { self.commitment_state.firm() } - fn soft(&self) -> &Block { + fn soft(&self) -> &ExecutedBlockMetadata { self.commitment_state.soft() } - pub(crate) fn firm_number(&self) -> u32 { + pub(crate) fn firm_number(&self) -> u64 { self.commitment_state.firm().number() } - pub(crate) fn soft_number(&self) -> u32 { + pub(crate) fn soft_number(&self) -> u64 { self.commitment_state.soft().number() } - fn firm_hash(&self) -> Bytes { - self.firm().hash().clone() - } - - fn soft_hash(&self) -> Bytes { - self.soft().hash().clone() + fn firm_hash(&self) -> String { + self.firm().hash().to_string() } - fn celestia_base_block_height(&self) -> u64 { - self.commitment_state.base_celestia_height() + fn soft_hash(&self) -> String { + self.soft().hash().to_string() } - fn celestia_block_variance(&self) -> u64 { - self.genesis_info.celestia_block_variance() + fn lowest_celestia_search_height(&self) -> u64 { + self.commitment_state.lowest_celestia_search_height() } - pub(crate) fn sequencer_start_height(&self) -> u64 { - self.genesis_info.sequencer_start_height() + fn celestia_search_height_max_look_ahead(&self) -> u64 { + self.execution_session_parameters + .celestia_search_height_max_look_ahead() } - pub(crate) fn halt_at_rollup_stop_number(&self) -> bool { - self.genesis_info.halt_at_rollup_stop_number() + pub(crate) fn sequencer_start_block_height(&self) -> u64 { + self.execution_session_parameters + .sequencer_start_block_height() } fn sequencer_chain_id(&self) -> String { - self.genesis_info.sequencer_chain_id().to_string() + self.execution_session_parameters + .sequencer_chain_id() + .to_string() } fn celestia_chain_id(&self) -> String { - self.genesis_info.celestia_chain_id().to_string() + self.execution_session_parameters + .celestia_chain_id() + .to_string() } fn rollup_id(&self) -> RollupId { - self.genesis_info.rollup_id() + self.execution_session_parameters.rollup_id() } pub(crate) fn rollup_start_block_number(&self) -> u64 { - self.genesis_info.rollup_start_block_number() + self.execution_session_parameters + .rollup_start_block_number() } - pub(crate) fn rollup_stop_block_number(&self) -> Option { - self.genesis_info.rollup_stop_block_number() + pub(crate) fn rollup_end_block_number(&self) -> Option { + self.execution_session_parameters.rollup_end_block_number() } pub(crate) fn firm_block_number_as_sequencer_height(&self) -> SequencerHeight { - map_firm_to_sequencer_height(&self.genesis_info, &self.commitment_state).expect( - "state must only contain numbers that can be mapped to sequencer heights; this is \ - enforced by its constructor and/or setter", - ) + map_firm_to_sequencer_height(&self.execution_session_parameters, &self.commitment_state) + .expect( + "state must only contain numbers that can be mapped to sequencer heights; this is \ + enforced by its constructor and/or setter", + ) } pub(crate) fn soft_block_number_as_sequencer_height(&self) -> SequencerHeight { - map_soft_to_sequencer_height(&self.genesis_info, &self.commitment_state).expect( - "state must only contain numbers that can be mapped to sequencer heights; this is \ - enforced by its constructor and/or setter", - ) + map_soft_to_sequencer_height(&self.execution_session_parameters, &self.commitment_state) + .expect( + "state must only contain numbers that can be mapped to sequencer heights; this is \ + enforced by its constructor and/or setter", + ) } fn next_expected_firm_sequencer_height(&self) -> Result { - map_firm_to_sequencer_height(&self.genesis_info, &self.commitment_state) + map_firm_to_sequencer_height(&self.execution_session_parameters, &self.commitment_state) .map(SequencerHeight::increment) } fn next_expected_soft_sequencer_height(&self) -> Result { - map_soft_to_sequencer_height(&self.genesis_info, &self.commitment_state) + map_soft_to_sequencer_height(&self.execution_session_parameters, &self.commitment_state) .map(SequencerHeight::increment) } } @@ -412,14 +424,13 @@ impl State { /// Maps a rollup height to a sequencer height. /// /// Returns error if `sequencer_start_height + (rollup_number - rollup_start_block_number)` -/// is out of range of `u32` or if `rollup_start_block_number` is more than 1 greater than +/// is out of range of `u64` or if `rollup_start_block_number` is more than 1 greater than /// `rollup_number`. fn map_rollup_number_to_sequencer_height( sequencer_start_height: u64, rollup_start_block_number: u64, - rollup_number: u32, + rollup_number: u64, ) -> Result { - let rollup_number = u64::from(rollup_number); if rollup_start_block_number > (rollup_number.checked_add(1).ok_or("overflows u64::MAX")?) { return Err("rollup start height exceeds rollup number + 1"); } @@ -430,24 +441,22 @@ fn map_rollup_number_to_sequencer_height( .ok_or("(sequencer height + rollup number - rollup start height) is negative")?; sequencer_height .try_into() - .map_err(|_| "overflows u32::MAX, the maximum cometbft height") + .map_err(|_| "overflows u64::MAX") } /// Maps a sequencer height to a rollup height. /// /// Returns `None` if `sequencer_height - sequencer_start_height + rollup_start_block_number` -/// underflows or if the result does not fit in `u32`. +/// underflows or if the result does not fit in `u64`. pub(super) fn map_sequencer_height_to_rollup_height( sequencer_start_height: u64, rollup_start_block_number: u64, sequencer_height: SequencerHeight, -) -> Option { +) -> Option { sequencer_height .value() .checked_sub(sequencer_start_height)? - .checked_add(rollup_start_block_number)? - .try_into() - .ok() + .checked_add(rollup_start_block_number) } #[cfg(test)] @@ -455,13 +464,14 @@ mod tests { use super::*; use crate::test_utils::{ make_commitment_state, - make_genesis_info, + make_execution_session_parameters, make_rollup_state, }; fn make_channel() -> (StateSender, StateReceiver) { super::channel(make_rollup_state( - make_genesis_info(), + "test_session".to_string(), + make_execution_session_parameters(), make_commitment_state(), )) } @@ -486,16 +496,16 @@ mod tests { #[track_caller] fn assert_height_is_correct( - sequencer_start_height: u32, - rollup_start_number: u32, - rollup_number: u32, + sequencer_start_height: u64, + rollup_start_number: u64, + rollup_number: u64, expected_sequencer_height: u32, ) { assert_eq!( SequencerHeight::from(expected_sequencer_height), map_rollup_number_to_sequencer_height( - sequencer_start_height.into(), - rollup_start_number.into(), + sequencer_start_height, + rollup_start_number, rollup_number, ) .unwrap() diff --git a/crates/astria-conductor/src/executor/tests.rs b/crates/astria-conductor/src/executor/tests.rs index 3c4db78be8..8b55f9968f 100644 --- a/crates/astria-conductor/src/executor/tests.rs +++ b/crates/astria-conductor/src/executor/tests.rs @@ -1,14 +1,13 @@ use astria_core::{ self, execution::v2::{ - Block, CommitmentState, - GenesisInfo, + ExecutedBlockMetadata, + ExecutionSessionParameters, }, generated::astria::execution::v2 as raw, Protobuf as _, }; -use bytes::Bytes; use super::{ should_execute_firm_block, @@ -20,14 +19,14 @@ use super::{ }; use crate::{ config::CommitLevel, - test_utils::make_genesis_info, + test_utils::make_execution_session_parameters, }; -fn make_block(number: u32) -> raw::Block { - raw::Block { +fn make_block_metadata(number: u64) -> raw::ExecutedBlockMetadata { + raw::ExecutedBlockMetadata { number, - hash: Bytes::from_static(&[0u8; 32]), - parent_block_hash: Bytes::from_static(&[0u8; 32]), + hash: hex::encode([0u8; 32]).to_string(), + parent_hash: hex::encode([0u8; 32]).to_string(), timestamp: Some(pbjson_types::Timestamp { seconds: 0, nanos: 0, @@ -36,8 +35,8 @@ fn make_block(number: u32) -> raw::Block { } struct MakeState { - firm: u32, - soft: u32, + firm: u64, + soft: u64, } fn make_state( @@ -46,15 +45,17 @@ fn make_state( soft, }: MakeState, ) -> (StateSender, StateReceiver) { - let genesis_info = GenesisInfo::try_from_raw(make_genesis_info()).unwrap(); + let execution_session_parameters = + ExecutionSessionParameters::try_from_raw(make_execution_session_parameters()).unwrap(); let commitment_state = CommitmentState::try_from_raw(raw::CommitmentState { - firm: Some(make_block(firm)), - soft: Some(make_block(soft)), - base_celestia_height: 1, + firm_executed_block_metadata: Some(make_block_metadata(firm)), + soft_executed_block_metadata: Some(make_block_metadata(soft)), + lowest_celestia_search_height: 1, }) .unwrap(); - let state = State::try_from_genesis_info_and_commitment_state( - genesis_info, + let state = State::try_from_execution_session_parameters_and_commitment_state( + "test_execution_session".to_string(), + execution_session_parameters, commitment_state, crate::config::CommitLevel::SoftAndFirm, ) @@ -63,18 +64,18 @@ fn make_state( } #[track_caller] -fn assert_contract_fulfilled(kind: super::ExecutionKind, state: MakeState, number: u32) { - let block = Block::try_from_raw(make_block(number)).unwrap(); +fn assert_contract_fulfilled(kind: super::ExecutionKind, state: MakeState, number: u64) { + let block_metadata = ExecutedBlockMetadata::try_from_raw(make_block_metadata(number)).unwrap(); let mut state = make_state(state); - super::does_block_response_fulfill_contract(&mut state.0, kind, &block) + super::does_block_response_fulfill_contract(&mut state.0, kind, &block_metadata) .expect("number stored in response block must be one more than in tracked state"); } #[track_caller] -fn assert_contract_violated(kind: super::ExecutionKind, state: MakeState, number: u32) { - let block = Block::try_from_raw(make_block(number)).unwrap(); +fn assert_contract_violated(kind: super::ExecutionKind, state: MakeState, number: u64) { + let block_metadata = ExecutedBlockMetadata::try_from_raw(make_block_metadata(number)).unwrap(); let mut state = make_state(state); - super::does_block_response_fulfill_contract(&mut state.0, kind, &block).expect_err( + super::does_block_response_fulfill_contract(&mut state.0, kind, &block_metadata).expect_err( "number stored in response block must not be one more than in tracked state", ); diff --git a/crates/astria-conductor/src/metrics.rs b/crates/astria-conductor/src/metrics.rs index 208bc2bbc4..a132228e78 100644 --- a/crates/astria-conductor/src/metrics.rs +++ b/crates/astria-conductor/src/metrics.rs @@ -61,14 +61,12 @@ impl Metrics { .record(block_count); } - pub(crate) fn absolute_set_executed_firm_block_number(&self, block_number: u32) { - self.executed_firm_block_number - .absolute(u64::from(block_number)); + pub(crate) fn absolute_set_executed_firm_block_number(&self, block_number: u64) { + self.executed_firm_block_number.absolute(block_number); } - pub(crate) fn absolute_set_executed_soft_block_number(&self, block_number: u32) { - self.executed_soft_block_number - .absolute(u64::from(block_number)); + pub(crate) fn absolute_set_executed_soft_block_number(&self, block_number: u64) { + self.executed_soft_block_number.absolute(block_number); } pub(crate) fn record_transactions_per_executed_block(&self, tx_count: usize) { diff --git a/crates/astria-conductor/src/test_utils.rs b/crates/astria-conductor/src/test_utils.rs index a91efdd0c3..dadd2a7aa3 100644 --- a/crates/astria-conductor/src/test_utils.rs +++ b/crates/astria-conductor/src/test_utils.rs @@ -1,7 +1,7 @@ use astria_core::{ generated::astria::execution::v2::{ CommitmentState, - GenesisInfo, + ExecutionSessionParameters, }, primitive::v1::RollupId, Protobuf as _, @@ -10,19 +10,19 @@ use astria_core::{ use crate::executor::State; pub(crate) fn make_commitment_state() -> CommitmentState { - let firm = astria_core::generated::astria::execution::v2::Block { + let firm = astria_core::generated::astria::execution::v2::ExecutedBlockMetadata { number: 1, - hash: vec![42u8; 32].into(), - parent_block_hash: vec![41u8; 32].into(), + hash: hex::encode([42u8; 32]).to_string(), + parent_hash: hex::encode([41u8; 32]).to_string(), timestamp: Some(pbjson_types::Timestamp { seconds: 123_456, nanos: 789, }), }; - let soft = astria_core::generated::astria::execution::v2::Block { + let soft = astria_core::generated::astria::execution::v2::ExecutedBlockMetadata { number: 2, - hash: vec![43u8; 32].into(), - parent_block_hash: vec![42u8; 32].into(), + hash: hex::encode([43u8; 32]).to_string(), + parent_hash: hex::encode([42u8; 32]).to_string(), timestamp: Some(pbjson_types::Timestamp { seconds: 123_456, nanos: 789, @@ -30,35 +30,40 @@ pub(crate) fn make_commitment_state() -> CommitmentState { }; CommitmentState { - soft: Some(soft), - firm: Some(firm), - base_celestia_height: 1, + soft_executed_block_metadata: Some(soft), + firm_executed_block_metadata: Some(firm), + lowest_celestia_search_height: 1, } } -pub(crate) fn make_genesis_info() -> GenesisInfo { +pub(crate) fn make_execution_session_parameters() -> ExecutionSessionParameters { let rollup_id = RollupId::new([24; 32]); - GenesisInfo { - rollup_id: Some(rollup_id.to_raw()), - sequencer_start_height: 10, - celestia_block_variance: 0, + ExecutionSessionParameters { + rollup_id: Some(rollup_id.into_raw()), rollup_start_block_number: 1, - rollup_stop_block_number: 90, + rollup_end_block_number: 10, sequencer_chain_id: "test-sequencer-0".to_string(), + sequencer_start_block_height: 10, celestia_chain_id: "test-celestia-0".to_string(), - halt_at_rollup_stop_number: false, + celestia_search_height_max_look_ahead: 90, } } pub(crate) fn make_rollup_state( - genesis_info: GenesisInfo, + execution_session_id: String, + execution_session_parameters: ExecutionSessionParameters, commitment_state: CommitmentState, ) -> State { - let genesis_info = astria_core::execution::v2::GenesisInfo::try_from_raw(genesis_info).unwrap(); + let execution_session_parameters = + astria_core::execution::v2::ExecutionSessionParameters::try_from_raw( + execution_session_parameters, + ) + .unwrap(); let commitment_state = astria_core::execution::v2::CommitmentState::try_from_raw(commitment_state).unwrap(); - State::try_from_genesis_info_and_commitment_state( - genesis_info, + State::try_from_execution_session_parameters_and_commitment_state( + execution_session_id, + execution_session_parameters, commitment_state, crate::config::CommitLevel::SoftAndFirm, ) diff --git a/crates/astria-conductor/tests/blackbox/firm_only.rs b/crates/astria-conductor/tests/blackbox/firm_only.rs index a66060144e..48478ef6c6 100644 --- a/crates/astria-conductor/tests/blackbox/firm_only.rs +++ b/crates/astria-conductor/tests/blackbox/firm_only.rs @@ -5,10 +5,7 @@ use astria_conductor::{ Conductor, Config, }; -use astria_core::generated::astria::execution::v2::{ - GetCommitmentStateRequest, - GetGenesisInfoRequest, -}; +use astria_core::generated::astria::execution::v2::CreateExecutionSessionRequest; use futures::future::{ join, join4, @@ -27,8 +24,7 @@ use wiremock::{ use crate::{ celestia_network_head, - commitment_state, - genesis_info, + execution_session, helpers::{ make_config, spawn_conductor, @@ -39,9 +35,8 @@ use crate::{ }, mount_celestia_blobs, mount_celestia_header_network_head, - mount_executed_block, - mount_get_commitment_state, - mount_get_genesis_info, + mount_create_execution_session, + mount_execute_block, mount_sequencer_commit, mount_sequencer_genesis, mount_sequencer_validator_set, @@ -52,27 +47,27 @@ use crate::{ async fn simple() { let test_conductor = spawn_conductor(CommitLevel::FirmOnly).await; - mount_get_genesis_info!( - test_conductor, - sequencer_start_height: 3, - celestia_block_variance: 10, - rollup_start_block_number: 2, - rollup_stop_block_number: 9, - ); - - mount_get_commitment_state!( + mount_create_execution_session!( test_conductor, - firm: ( - number: 1, - hash: [1; 64], - parent: [0; 64], - ), - soft: ( - number: 1, - hash: [1; 64], - parent: [0; 64], - ), - base_celestia_height: 1, + execution_session_parameters: ( + rollup_start_block_number: 2, + rollup_end_block_number: 9, + sequencer_start_block_height: 3, + celestia_max_look_ahead: 10, + ), + commitment_state: ( + firm: ( + number: 1, + hash: "1", + parent: "0", + ), + soft: ( + number: 1, + hash: "1", + parent: "0", + ), + lowest_celestia_search_height: 1, + ) ); mount_sequencer_genesis!(test_conductor); @@ -95,26 +90,26 @@ async fn simple() { mount_sequencer_validator_set!(test_conductor, height: 2u32); - let execute_block = mount_executed_block!( + let execute_block = mount_execute_block!( test_conductor, number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ); let update_commitment_state = mount_update_commitment_state!( test_conductor, firm: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), soft: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, ); timeout( @@ -135,27 +130,27 @@ async fn simple() { async fn submits_two_heights_in_succession() { let test_conductor = spawn_conductor(CommitLevel::FirmOnly).await; - mount_get_genesis_info!( + mount_create_execution_session!( test_conductor, - sequencer_start_height: 3, - celestia_block_variance: 10, - rollup_start_block_number: 2, - rollup_stop_block_number: 9, - ); - - mount_get_commitment_state!( - test_conductor, - firm: ( - number: 1, - hash: [1; 64], - parent: [0; 64], + execution_session_parameters: ( + rollup_start_block_number: 2, + rollup_end_block_number: 9, + sequencer_start_block_height: 3, + celestia_max_look_ahead: 10, ), - soft: ( - number: 1, - hash: [1; 64], - parent: [0; 64], + commitment_state: ( + firm: ( + number: 1, + hash: "1", + parent: "0", + ), + soft: ( + number: 1, + hash: "1", + parent: "0", + ), + lowest_celestia_search_height: 1, ), - base_celestia_height: 1, ); mount_sequencer_genesis!(test_conductor); @@ -185,48 +180,48 @@ async fn submits_two_heights_in_succession() { mount_sequencer_validator_set!(test_conductor, height: 3u32); - let execute_block_number_2 = mount_executed_block!( + let execute_block_number_2 = mount_execute_block!( test_conductor, number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ); let update_commitment_state_number_2 = mount_update_commitment_state!( test_conductor, firm: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), soft: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, ); - let execute_block_number_3 = mount_executed_block!( + let execute_block_number_3 = mount_execute_block!( test_conductor, number: 3, - hash: [3; 64], - parent: [2; 64], + hash: "3", + parent: "2", ); let update_commitment_state_number_3 = mount_update_commitment_state!( test_conductor, firm: ( number: 3, - hash: [3; 64], - parent: [2; 64], + hash: "3", + parent: "2", ), soft: ( number: 3, - hash: [3; 64], - parent: [2; 64], + hash: "3", + parent: "2", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, ); timeout( @@ -249,28 +244,29 @@ async fn submits_two_heights_in_succession() { async fn skips_already_executed_heights() { let test_conductor = spawn_conductor(CommitLevel::FirmOnly).await; - mount_get_genesis_info!( - test_conductor, - sequencer_start_height: 3, - celestia_block_variance: 10, - rollup_start_block_number: 2, - rollup_stop_block_number: 9, - ); - - mount_get_commitment_state!( + mount_create_execution_session!( test_conductor, - firm: ( - number: 5, - hash: [1; 64], - parent: [0; 64], + execution_session_parameters: ( + rollup_start_block_number: 2, + rollup_end_block_number: 9, + sequencer_start_block_height: 3, + celestia_max_look_ahead: 10, ), - soft: ( - number: 5, - hash: [1; 64], - parent: [0; 64], + commitment_state: ( + firm: ( + number: 5, + hash: "1", + parent: "0", + ), + soft: ( + number: 5, + hash: "1", + parent: "0", + ), + lowest_celestia_search_height: 1, ), - base_celestia_height: 1, ); + mount_sequencer_genesis!(test_conductor); mount_celestia_header_network_head!( @@ -295,26 +291,26 @@ async fn skips_already_executed_heights() { mount_sequencer_validator_set!(test_conductor, height: 6u32); - let execute_block = mount_executed_block!( + let execute_block = mount_execute_block!( test_conductor, number: 6, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ); let update_commitment_state = mount_update_commitment_state!( test_conductor, firm: ( number: 6, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), soft: ( number: 6, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, ); timeout( @@ -335,27 +331,27 @@ async fn skips_already_executed_heights() { async fn fetch_from_later_celestia_height() { let test_conductor = spawn_conductor(CommitLevel::FirmOnly).await; - mount_get_genesis_info!( - test_conductor, - sequencer_start_height: 3, - celestia_block_variance: 10, - rollup_start_block_number: 2, - rollup_stop_block_number: 9, - ); - - mount_get_commitment_state!( + mount_create_execution_session!( test_conductor, - firm: ( - number: 1, - hash: [1; 64], - parent: [0; 64], + execution_session_parameters: ( + rollup_start_block_number: 2, + rollup_end_block_number: 9, + sequencer_start_block_height: 3, + celestia_max_look_ahead: 10, ), - soft: ( - number: 1, - hash: [1; 64], - parent: [0; 64], + commitment_state: ( + firm: ( + number: 1, + hash: "1", + parent: "0", + ), + soft: ( + number: 1, + hash: "1", + parent: "0", + ), + lowest_celestia_search_height: 4, ), - base_celestia_height: 4, ); mount_sequencer_genesis!(test_conductor); @@ -378,26 +374,26 @@ async fn fetch_from_later_celestia_height() { mount_sequencer_validator_set!(test_conductor, height: 2u32); - let execute_block = mount_executed_block!( + let execute_block = mount_execute_block!( test_conductor, number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ); let update_commitment_state = mount_update_commitment_state!( test_conductor, firm: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), soft: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), - base_celestia_height: 4, + lowest_celestia_search_height: 4, ); timeout( @@ -451,35 +447,30 @@ async fn exits_on_celestia_chain_id_mismatch() { }; GrpcMock::for_rpc_given( - "get_genesis_info", - matcher::message_type::(), + "create_execution_session", + matcher::message_type::(), ) - .respond_with(GrpcResponse::constant_response( - genesis_info!(sequencer_start_height: 3, - celestia_block_variance: 10, + .respond_with(GrpcResponse::constant_response(execution_session!( + execution_session_parameters: ( rollup_start_block_number: 2, - rollup_stop_block_number: 9 - ), - )) - .expect(0..) - .mount(&mock_grpc.mock_server) - .await; - - GrpcMock::for_rpc_given( - "get_commitment_state", - matcher::message_type::(), - ) - .respond_with(GrpcResponse::constant_response(commitment_state!(firm: ( - number: 1, - hash: [1; 64], - parent: [0; 64], - ), - soft: ( - number: 1, - hash: [1; 64], - parent: [0; 64], - ), - base_celestia_height: 1,))) + rollup_end_block_number: 9, + sequencer_start_block_height: 3, + celestia_max_look_ahead: 10, + ), + commitment_state: ( + firm: ( + number: 1, + hash: "1", + parent: "0", + ), + soft: ( + number: 1, + hash: "1", + parent: "0", + ), + lowest_celestia_search_height: 1, + ) + ))) .expect(0..) .mount(&mock_grpc.mock_server) .await; @@ -526,19 +517,18 @@ async fn exits_on_celestia_chain_id_mismatch() { } /// Tests that the conductor correctly stops at the stop block height and executes the firm block -/// for that height before restarting and continuing after fetching new genesis info and commitment -/// state. +/// for that height before restarting and continuing after requesting new execution session. /// /// It consists of the following steps: -/// 1. Mount commitment state and genesis info with a stop height of 3 for the first height, only -/// responding up to 1 time so that the same info is not provided after conductor restart. +/// 1. Mount execution session with a stop number of 2 for the first height (sequencer height 3), +/// only responding up to 1 time so that the same info is not provided after conductor restart. /// 2. Mount sequencer genesis and celestia header network head. /// 3. Mount firm blocks for heights 3 and 4. /// 4. Mount `execute_block` and `update_commitment_state` for firm block 3, expecting only one call /// since they should not be called after restarting. /// 5. Wait ample time for conductor to restart before performing the next set of mounts. -/// 6. Mount new genesis info and updated commitment state with rollup start block height of 2 to -/// reflect that the first block has already been executed. +/// 6. Mount new execution session with rollup start block number of 3 to reflect that the first +/// block has already been executed. /// 7. Mount `execute_block` and `update_commitment_state` for firm block 4, awaiting their /// satisfaction. #[expect(clippy::too_many_lines, reason = "All lines reasonably necessary")] @@ -546,29 +536,28 @@ async fn exits_on_celestia_chain_id_mismatch() { async fn restarts_after_reaching_stop_block_height() { let test_conductor = spawn_conductor(CommitLevel::FirmOnly).await; - mount_get_genesis_info!( + mount_create_execution_session!( test_conductor, - sequencer_start_height: 3, - celestia_block_variance: 10, - rollup_start_block_number: 2, - rollup_stop_block_number: 2, - up_to_n_times: 1, // Only respond once, since updated information is needed after restart. - ); - - mount_get_commitment_state!( - test_conductor, - firm: ( - number: 1, - hash: [1; 64], - parent: [0; 64], - ), - soft: ( - number: 1, - hash: [1; 64], - parent: [0; 64], - ), - base_celestia_height: 1, - up_to_n_times: 1, + execution_session_parameters: ( + rollup_start_block_number: 2, + rollup_end_block_number: 2, + sequencer_start_block_height: 3, + celestia_max_look_ahead: 10, + ), + commitment_state: ( + firm: ( + number: 1, + hash: "1", + parent: "0", + ), + soft: ( + number: 1, + hash: "1", + parent: "0", + ), + lowest_celestia_search_height: 1, + ), + up_to_n_times: 1, // Only respond once, since a new execution session is needed after restart. ); mount_sequencer_genesis!(test_conductor); @@ -593,12 +582,12 @@ async fn restarts_after_reaching_stop_block_height() { mount_sequencer_validator_set!(test_conductor, height: 2u32); mount_sequencer_validator_set!(test_conductor, height: 3u32); - let execute_block_1 = mount_executed_block!( + let execute_block_1 = mount_execute_block!( test_conductor, mock_name: "execute_block_1", number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", expected_calls: 1, // should not be called again upon restart ); @@ -607,15 +596,15 @@ async fn restarts_after_reaching_stop_block_height() { mock_name: "update_commitment_state_1", firm: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), soft: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, expected_calls: 1, // should not be called again upon restart ); @@ -632,36 +621,36 @@ async fn restarts_after_reaching_stop_block_height() { commitment state twice within 1000ms", ); - // Mount new genesis info and commitment state with updated heights - mount_get_genesis_info!( + // Mount new execution session with updated heights and commitment state. + mount_create_execution_session!( test_conductor, - sequencer_start_height: 4, - celestia_block_variance: 10, - rollup_start_block_number: 3, - rollup_stop_block_number: 9, - ); - - mount_get_commitment_state!( - test_conductor, - firm: ( - number: 2, - hash: [2; 64], - parent: [1; 64], + execution_session_parameters: ( + rollup_start_block_number: 3, + rollup_end_block_number: 9, + sequencer_start_block_height: 4, + celestia_max_look_ahead: 10, ), - soft: ( - number: 2, - hash: [2; 64], - parent: [1; 64], + commitment_state: ( + firm: ( + number: 2, + hash: "2", + parent: "1", + ), + soft: ( + number: 2, + hash: "2", + parent: "1", + ), + lowest_celestia_search_height: 1, ), - base_celestia_height: 1, ); - let execute_block_2 = mount_executed_block!( + let execute_block_2 = mount_execute_block!( test_conductor, mock_name: "execute_block_2", number: 3, - hash: [3; 64], - parent: [2; 64], + hash: "3", + parent: "2", expected_calls: 1, ); @@ -670,15 +659,15 @@ async fn restarts_after_reaching_stop_block_height() { mock_name: "update_commitment_state_2", firm: ( number: 3, - hash: [3; 64], - parent: [2; 64], + hash: "3", + parent: "2", ), soft: ( number: 3, - hash: [3; 64], - parent: [2; 64], + hash: "3", + parent: "2", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, expected_calls: 1, ); diff --git a/crates/astria-conductor/tests/blackbox/helpers/macros.rs b/crates/astria-conductor/tests/blackbox/helpers/macros.rs index 4b9fba3aca..71cb6cf668 100644 --- a/crates/astria-conductor/tests/blackbox/helpers/macros.rs +++ b/crates/astria-conductor/tests/blackbox/helpers/macros.rs @@ -1,10 +1,10 @@ #[macro_export] -macro_rules! block { +macro_rules! block_metadata { (number: $number:expr,hash: $hash:expr,parent: $parent:expr $(,)?) => { - ::astria_core::generated::astria::execution::v2::Block { + ::astria_core::generated::astria::execution::v2::ExecutedBlockMetadata { number: $number, - hash: ::bytes::Bytes::from(Vec::from($hash)), - parent_block_hash: ::bytes::Bytes::from(Vec::from($parent)), + hash: $hash.to_string(), + parent_hash: $parent.to_string(), timestamp: Some(::pbjson_types::Timestamp { seconds: 1, nanos: 1, @@ -52,29 +52,6 @@ macro_rules! celestia_network_head { }; } -#[macro_export] -macro_rules! commitment_state { - ( - firm: (number: $firm_number:expr,hash: $firm_hash:expr,parent: $firm_parent:expr $(,)?), - soft: (number: $soft_number:expr,hash: $soft_hash:expr,parent: $soft_parent:expr $(,)?), - base_celestia_height: $base_celestia_height:expr $(,)? - ) => { - ::astria_core::generated::astria::execution::v2::CommitmentState { - firm: Some($crate::block!( - number: $firm_number, - hash: $firm_hash, - parent: $firm_parent, - )), - soft: Some($crate::block!( - number: $soft_number, - hash: $soft_hash, - parent: $soft_parent, - )), - base_celestia_height: $base_celestia_height, - } - }; -} - #[macro_export] macro_rules! filtered_sequencer_block { (sequencer_height: $height:expr) => {{ @@ -92,39 +69,46 @@ macro_rules! filtered_sequencer_block { // 1. applying #[rustfmt::skip] on the macro or on the containing module triggers issue 52234. // 2. applying #![rustfmt::skip] triggers issue 64266. #[macro_export] -macro_rules! genesis_info { - ( - sequencer_start_height: - $start_height:expr,celestia_block_variance: - $variance:expr,rollup_start_block_number: - $rollup_start_block_number:expr, rollup_stop_block_number: - $rollup_stop_block_number:expr $(,)? - ) => { - genesis_info!( - sequencer_start_height: $start_height, - celestia_block_variance: $variance, - rollup_start_block_number: $rollup_start_block_number, - rollup_stop_block_number: $rollup_stop_block_number, - halt_at_rollup_stop_number: false, - ) - }; +macro_rules! execution_session { ( - sequencer_start_height: - $start_height:expr,celestia_block_variance: - $variance:expr,rollup_start_block_number: - $rollup_start_block_number:expr, - rollup_stop_block_number: $rollup_stop_block_number:expr, - halt_at_rollup_stop_number: $halt_at_rollup_stop_number:expr $(,)? + execution_session_parameters: ( + rollup_start_block_number: $rollup_start_block_number:expr, + rollup_end_block_number: $rollup_end_block_number:expr, + sequencer_start_block_height: $start_height:expr, + celestia_max_look_ahead: $celestia_max_look_ahead:expr$(,)? + ), + commitment_state: ( + firm: ( number: $firm_number:expr, hash: $firm_hash:expr, parent: $firm_parent:expr$(,)? ), + soft: ( number: $soft_number:expr, hash: $soft_hash:expr, parent: $soft_parent:expr$(,)? ), + lowest_celestia_search_height: $lowest_celestia_search_height:expr$(,)? + )$(,)? ) => { - ::astria_core::generated::astria::execution::v2::GenesisInfo { - rollup_id: Some($crate::ROLLUP_ID.to_raw()), - sequencer_start_height: $start_height, - celestia_block_variance: $variance, - rollup_start_block_number: $rollup_start_block_number, - rollup_stop_block_number: $rollup_stop_block_number, - sequencer_chain_id: $crate::SEQUENCER_CHAIN_ID.to_string(), - celestia_chain_id: $crate::helpers::CELESTIA_CHAIN_ID.to_string(), - halt_at_rollup_stop_number: $halt_at_rollup_stop_number, + ::astria_core::generated::astria::execution::v2::ExecutionSession { + session_id: $crate::helpers::EXECUTION_SESSION_ID.to_string(), + execution_session_parameters: Some( + ::astria_core::generated::astria::execution::v2::ExecutionSessionParameters { + rollup_id: Some($crate::ROLLUP_ID.to_raw()), + rollup_start_block_number: $rollup_start_block_number, + rollup_end_block_number: $rollup_end_block_number, + sequencer_start_block_height: $start_height, + sequencer_chain_id: $crate::SEQUENCER_CHAIN_ID.to_string(), + celestia_chain_id: $crate::helpers::CELESTIA_CHAIN_ID.to_string(), + celestia_search_height_max_look_ahead: $celestia_max_look_ahead, + } + ), + commitment_state: Some(::astria_core::generated::astria::execution::v2::CommitmentState { + firm_executed_block_metadata: Some($crate::block_metadata!( + number: $firm_number, + hash: $firm_hash, + parent: $firm_parent, + )), + soft_executed_block_metadata: Some($crate::block_metadata!( + number: $soft_number, + hash: $soft_hash, + parent: $soft_parent, + )), + lowest_celestia_search_height: $lowest_celestia_search_height, + }), } }; } @@ -190,56 +174,13 @@ macro_rules! mount_celestia_header_network_head { } } -#[macro_export] -macro_rules! mount_get_commitment_state { - ( - $test_env:ident, - firm: ( number: $firm_number:expr, hash: $firm_hash:expr, parent: $firm_parent:expr$(,)? ), - soft: ( number: $soft_number:expr, hash: $soft_hash:expr, parent: $soft_parent:expr$(,)? ), - base_celestia_height: $base_celestia_height:expr - $(,)? - ) => { - mount_get_commitment_state!( - $test_env, - firm: ( number: $firm_number, hash: $firm_hash, parent: $firm_parent, ), - soft: ( number: $soft_number, hash: $soft_hash, parent: $soft_parent, ), - base_celestia_height: $base_celestia_height, - up_to_n_times: 1, - ) - }; - ( - $test_env:ident, - firm: ( number: $firm_number:expr, hash: $firm_hash:expr, parent: $firm_parent:expr$(,)? ), - soft: ( number: $soft_number:expr, hash: $soft_hash:expr, parent: $soft_parent:expr$(,)? ), - base_celestia_height: $base_celestia_height:expr, - up_to_n_times: $up_to_n_times:expr - $(,)? - ) => { - $test_env - .mount_get_commitment_state($crate::commitment_state!( - firm: ( - number: $firm_number, - hash: $firm_hash, - parent: $firm_parent, - ), - soft: ( - number: $soft_number, - hash: $soft_hash, - parent: $soft_parent, - ), - base_celestia_height: $base_celestia_height, - ), $up_to_n_times) - .await - }; -} - #[macro_export] macro_rules! mount_update_commitment_state { ( $test_env:ident, firm: ( number: $firm_number:expr, hash: $firm_hash:expr, parent: $firm_parent:expr$(,)? ), soft: ( number: $soft_number:expr, hash: $soft_hash:expr, parent: $soft_parent:expr$(,)? ), - base_celestia_height: $base_celestia_height:expr + lowest_celestia_search_height: $lowest_celestia_search_height:expr $(,)? ) => { mount_update_commitment_state!( @@ -247,7 +188,7 @@ macro_rules! mount_update_commitment_state { mock_name: None, firm: ( number: $firm_number, hash: $firm_hash, parent: $firm_parent, ), soft: ( number: $soft_number, hash: $soft_hash, parent: $soft_parent, ), - base_celestia_height: $base_celestia_height, + lowest_celestia_search_height: $lowest_celestia_search_height, expected_calls: 1, ) }; @@ -256,7 +197,7 @@ macro_rules! mount_update_commitment_state { mock_name: $mock_name:expr, firm: ( number: $firm_number:expr, hash: $firm_hash:expr, parent: $firm_parent:expr$(,)? ), soft: ( number: $soft_number:expr, hash: $soft_hash:expr, parent: $soft_parent:expr$(,)? ), - base_celestia_height: $base_celestia_height:expr + lowest_celestia_search_height: $lowest_celestia_search_height:expr $(,)? ) => { mount_update_commitment_state!( @@ -264,7 +205,7 @@ macro_rules! mount_update_commitment_state { mock_name: $mock_name, firm: ( number: $firm_number, hash: $firm_hash, parent: $firm_parent, ), soft: ( number: $soft_number, hash: $soft_hash, parent: $soft_parent, ), - base_celestia_height: $base_celestia_height, + lowest_celestia_search_height: $lowest_celestia_search_height, expected_calls: 1, ) }; @@ -273,26 +214,26 @@ macro_rules! mount_update_commitment_state { mock_name: $mock_name:expr, firm: ( number: $firm_number:expr, hash: $firm_hash:expr, parent: $firm_parent:expr$(,)? ), soft: ( number: $soft_number:expr, hash: $soft_hash:expr, parent: $soft_parent:expr$(,)? ), - base_celestia_height: $base_celestia_height:expr, + lowest_celestia_search_height: $lowest_celestia_search_height:expr, expected_calls: $expected_calls:expr $(,)? ) => { $test_env .mount_update_commitment_state( $mock_name.into(), - $crate::commitment_state!( - firm: ( + ::astria_core::generated::astria::execution::v2::CommitmentState { + firm_executed_block_metadata: Some($crate::block_metadata!( number: $firm_number, hash: $firm_hash, parent: $firm_parent, - ), - soft: ( + )), + soft_executed_block_metadata: Some($crate::block_metadata!( number: $soft_number, hash: $soft_hash, parent: $soft_parent, - ), - base_celestia_height: $base_celestia_height, - ), + )), + lowest_celestia_search_height: $lowest_celestia_search_height, + }, $expected_calls, ) .await @@ -307,7 +248,7 @@ macro_rules! mount_abci_info { } #[macro_export] -macro_rules! mount_executed_block { +macro_rules! mount_execute_block { ( $test_env:ident, mock_name: $mock_name:expr, @@ -320,10 +261,11 @@ macro_rules! mount_executed_block { $test_env.mount_execute_block( $mock_name.into(), ::serde_json::json!({ - "prevBlockHash": BASE64_STANDARD.encode($parent), + "sessionId": $crate::helpers::EXECUTION_SESSION_ID, + "parentHash": $parent, "transactions": [{"sequencedData": BASE64_STANDARD.encode($crate::helpers::data())}], }), - $crate::block!( + $crate::block_metadata!( number: $number, hash: $hash, parent: $parent, @@ -339,7 +281,7 @@ macro_rules! mount_executed_block { hash: $hash:expr, parent: $parent:expr, ) => { - mount_executed_block!( + mount_execute_block!( $test_env, mock_name: None, number: $number, @@ -354,7 +296,7 @@ macro_rules! mount_executed_block { hash: $hash:expr, parent: $parent:expr $(,)? ) => { - mount_executed_block!( + mount_execute_block!( $test_env, mock_name: None, number: $number, @@ -388,62 +330,101 @@ macro_rules! mount_get_filtered_sequencer_block { } #[macro_export] -macro_rules! mount_get_genesis_info { +macro_rules! mount_create_execution_session { ( $test_env:ident, - sequencer_start_height: $start_height:expr, - celestia_block_variance: $variance:expr, - rollup_start_block_number: $rollup_start_block_number:expr, - rollup_stop_block_number: $rollup_stop_block_number:expr + execution_session_parameters: ( + rollup_start_block_number: $rollup_start_block_number:expr, + rollup_end_block_number: $rollup_end_block_number:expr, + sequencer_start_block_height: $start_height:expr, + celestia_max_look_ahead: $celestia_max_look_ahead:expr $(,)? + ), + commitment_state: ( + firm: ( number: $firm_number:expr, hash: $firm_hash:expr, parent: $firm_parent:expr$(,)? ), + soft: ( number: $soft_number:expr, hash: $soft_hash:expr, parent: $soft_parent:expr$(,)? ), + lowest_celestia_search_height: $lowest_celestia_search_height:expr$(,)? + ) $(,)? ) => { - mount_get_genesis_info!( + mount_create_execution_session!( $test_env, - sequencer_start_height: $start_height, - celestia_block_variance: $variance, - rollup_start_block_number: $rollup_start_block_number, - rollup_stop_block_number: $rollup_stop_block_number, + execution_session_parameters: ( + rollup_start_block_number: $rollup_start_block_number, + rollup_end_block_number: $rollup_end_block_number, + sequencer_start_block_height: $start_height, + celestia_max_look_ahead: $celestia_max_look_ahead, + ), + commitment_state: ( + firm: ( number: $firm_number, hash: $firm_hash, parent: $firm_parent, ), + soft: ( number: $soft_number, hash: $soft_hash, parent: $soft_parent, ), + lowest_celestia_search_height: $lowest_celestia_search_height, + ), + expected_calls: 1, up_to_n_times: 1, ) }; ( $test_env:ident, - sequencer_start_height: $start_height:expr, - celestia_block_variance: $variance:expr, - rollup_start_block_number: $rollup_start_block_number:expr, - rollup_stop_block_number: $rollup_stop_block_number:expr, + execution_session_parameters: ( + rollup_start_block_number: $rollup_start_block_number:expr, + rollup_end_block_number: $rollup_end_block_number:expr, + sequencer_start_block_height: $start_height:expr, + celestia_max_look_ahead: $celestia_max_look_ahead:expr $(,)? + ), + commitment_state: ( + firm: ( number: $firm_number:expr, hash: $firm_hash:expr, parent: $firm_parent:expr$(,)? ), + soft: ( number: $soft_number:expr, hash: $soft_hash:expr, parent: $soft_parent:expr$(,)? ), + lowest_celestia_search_height: $lowest_celestia_search_height:expr$(,)? + ), up_to_n_times: $up_to_n_times:expr $(,)? ) => { - mount_get_genesis_info!( + mount_create_execution_session!( $test_env, - sequencer_start_height: $start_height, - celestia_block_variance: $variance, - rollup_start_block_number: $rollup_start_block_number, - rollup_stop_block_number: $rollup_stop_block_number, - up_to_n_times: $up_to_n_times, - halt_at_rollup_stop_number: false, + execution_session_parameters: ( + rollup_start_block_number: $rollup_start_block_number, + rollup_end_block_number: $rollup_end_block_number, + sequencer_start_block_height: $start_height, + celestia_max_look_ahead: $celestia_max_look_ahead, + ), + commitment_state: ( + firm: ( number: $firm_number, hash: $firm_hash, parent: $firm_parent, ), + soft: ( number: $soft_number, hash: $soft_hash, parent: $soft_parent, ), + lowest_celestia_search_height: $lowest_celestia_search_height, + ), expected_calls: 1, + up_to_n_times: $up_to_n_times, ) }; ( $test_env:ident, - sequencer_start_height: $start_height:expr, - celestia_block_variance: $variance:expr, - rollup_start_block_number: $rollup_start_block_number:expr, - rollup_stop_block_number: $rollup_stop_block_number:expr, - up_to_n_times: $up_to_n_times:expr, - halt_at_rollup_stop_number: $halt_at_rollup_stop_number:expr, - expected_calls: $expected_calls:expr - $(,)? + execution_session_parameters: ( + rollup_start_block_number: $rollup_start_block_number:expr, + rollup_end_block_number: $rollup_end_block_number:expr, + sequencer_start_block_height: $start_height:expr, + celestia_max_look_ahead: $celestia_max_look_ahead:expr $(,)? + ), + commitment_state: ( + firm: ( number: $firm_number:expr, hash: $firm_hash:expr, parent: $firm_parent:expr$(,)? ), + soft: ( number: $soft_number:expr, hash: $soft_hash:expr, parent: $soft_parent:expr$(,)? ), + lowest_celestia_search_height: $lowest_celestia_search_height:expr$(,)? + ), + expected_calls: $expected_calls:expr, + up_to_n_times: $up_to_n_times:expr $(,)? ) => { - $test_env.mount_get_genesis_info( - $crate::genesis_info!( - sequencer_start_height: $start_height, - celestia_block_variance: $variance, - rollup_start_block_number: $rollup_start_block_number, - rollup_stop_block_number: $rollup_stop_block_number, - halt_at_rollup_stop_number: $halt_at_rollup_stop_number, + $test_env.mount_create_execution_session( + $crate::execution_session!( + execution_session_parameters: ( + rollup_start_block_number: $rollup_start_block_number, + rollup_end_block_number: $rollup_end_block_number, + sequencer_start_block_height: $start_height, + celestia_max_look_ahead: $celestia_max_look_ahead, + ), + commitment_state: ( + firm: ( number: $firm_number, hash: $firm_hash, parent: $firm_parent), + soft: ( number: $soft_number, hash: $soft_hash, parent: $soft_parent), + lowest_celestia_search_height: $lowest_celestia_search_height, + ), ), $up_to_n_times, $expected_calls, @@ -477,24 +458,24 @@ macro_rules! mount_sequencer_genesis { } #[macro_export] -macro_rules! mount_get_block { +macro_rules! mount_get_executed_block_metadata { ( $test_env:ident, number: $number:expr, hash: $hash:expr, parent: $parent:expr $(,)? ) => {{ - let block = $crate::block!( + let block = $crate::block_metadata!( number: $number, hash: $hash, parent: $parent, ); - let identifier = ::astria_core::generated::astria::execution::v2::BlockIdentifier { + let identifier = ::astria_core::generated::astria::execution::v2::ExecutedBlockIdentifier { identifier: Some( - ::astria_core::generated::astria::execution::v2::block_identifier::Identifier::BlockNumber(block.number) + ::astria_core::generated::astria::execution::v2::executed_block_identifier::Identifier::Number(block.number) )}; - $test_env.mount_get_block( - ::astria_core::generated::astria::execution::v2::GetBlockRequest { + $test_env.mount_get_executed_block_metadata( + ::astria_core::generated::astria::execution::v2::GetExecutedBlockMetadataRequest { identifier: Some(identifier), }, block, @@ -513,7 +494,8 @@ macro_rules! mount_execute_block_tonic_code { use ::base64::prelude::*; $test_env.mount_tonic_status_code( ::serde_json::json!({ - "prevBlockHash": BASE64_STANDARD.encode($parent), + "sessionId": $crate::helpers::EXECUTION_SESSION_ID, + "parentHash": $parent, "transactions": [{"sequencedData": BASE64_STANDARD.encode($crate::helpers::data())}], }), $status_code diff --git a/crates/astria-conductor/tests/blackbox/helpers/mock_grpc.rs b/crates/astria-conductor/tests/blackbox/helpers/mock_grpc.rs index fba86349c6..97298637f9 100644 --- a/crates/astria-conductor/tests/blackbox/helpers/mock_grpc.rs +++ b/crates/astria-conductor/tests/blackbox/helpers/mock_grpc.rs @@ -9,15 +9,13 @@ use astria_core::generated::astria::{ ExecutionService, ExecutionServiceServer, }, - BatchGetBlocksRequest, - BatchGetBlocksResponse, - Block, CommitmentState, + CreateExecutionSessionRequest, ExecuteBlockRequest, - GenesisInfo, - GetBlockRequest, - GetCommitmentStateRequest, - GetGenesisInfoRequest, + ExecuteBlockResponse, + ExecutedBlockMetadata, + ExecutionSession, + GetExecutedBlockMetadataRequest, UpdateCommitmentStateRequest, }, sequencerblock::v1::{ @@ -145,10 +143,8 @@ macro_rules! define_and_impl_service { } define_and_impl_service!(impl ExecutionService for ExecutionServiceImpl { - (get_block: GetBlockRequest => Block) - (get_genesis_info: GetGenesisInfoRequest => GenesisInfo) - (batch_get_blocks: BatchGetBlocksRequest => BatchGetBlocksResponse) - (execute_block: ExecuteBlockRequest => Block) - (get_commitment_state: GetCommitmentStateRequest => CommitmentState) + (get_executed_block_metadata: GetExecutedBlockMetadataRequest => ExecutedBlockMetadata) + (create_execution_session: CreateExecutionSessionRequest => ExecutionSession) + (execute_block: ExecuteBlockRequest => ExecuteBlockResponse) (update_commitment_state: UpdateCommitmentStateRequest => CommitmentState) }); diff --git a/crates/astria-conductor/tests/blackbox/helpers/mod.rs b/crates/astria-conductor/tests/blackbox/helpers/mod.rs index 3933ddd418..31a90c2ecd 100644 --- a/crates/astria-conductor/tests/blackbox/helpers/mod.rs +++ b/crates/astria-conductor/tests/blackbox/helpers/mod.rs @@ -14,9 +14,9 @@ use astria_core::{ brotli::compress_bytes, generated::astria::{ execution::v2::{ - Block, CommitmentState, - GenesisInfo, + ExecutedBlockMetadata, + ExecutionSession, }, sequencerblock::v1::FilteredSequencerBlock, }, @@ -52,6 +52,7 @@ pub static ROLLUP_ID_BYTES: Bytes = Bytes::from_static(ROLLUP_ID.as_bytes()); pub const SEQUENCER_CHAIN_ID: &str = "test_sequencer-1000"; pub const CELESTIA_CHAIN_ID: &str = "test_celestia-1000"; +pub const EXECUTION_SESSION_ID: &str = "test_execution_session"; pub const INITIAL_SOFT_HASH: [u8; 64] = [1; 64]; pub const INITIAL_FIRM_HASH: [u8; 64] = [1; 64]; @@ -176,21 +177,24 @@ impl TestConductor { .await; } - pub async fn mount_get_block( + pub async fn mount_get_executed_block_metadata( &self, expected_pbjson: S, - block: astria_core::generated::astria::execution::v2::Block, + block: ExecutedBlockMetadata, ) { use astria_grpc_mock::{ matcher::message_partial_pbjson, response::constant_response, Mock, }; - Mock::for_rpc_given("get_block", message_partial_pbjson(&expected_pbjson)) - .respond_with(constant_response(block)) - .expect(1..) - .mount(&self.mock_grpc.mock_server) - .await; + Mock::for_rpc_given( + "get_executed_block_metadata", + message_partial_pbjson(&expected_pbjson), + ) + .respond_with(constant_response(block)) + .expect(1..) + .mount(&self.mock_grpc.mock_server) + .await; } pub async fn mount_celestia_blob_get_all( @@ -305,40 +309,22 @@ impl TestConductor { mount_genesis(&self.mock_http, chain_id).await; } - pub async fn mount_get_genesis_info( + pub async fn mount_create_execution_session( &self, - genesis_info: GenesisInfo, + execution_session: ExecutionSession, up_to_n_times: u64, expected_calls: u64, ) { - use astria_core::generated::astria::execution::v2::GetGenesisInfoRequest; + use astria_core::generated::astria::execution::v2::CreateExecutionSessionRequest; astria_grpc_mock::Mock::for_rpc_given( - "get_genesis_info", - astria_grpc_mock::matcher::message_type::(), - ) - .respond_with(astria_grpc_mock::response::constant_response(genesis_info)) - .up_to_n_times(up_to_n_times) - .expect(expected_calls) - .mount(&self.mock_grpc.mock_server) - .await; - } - - pub async fn mount_get_commitment_state( - &self, - commitment_state: CommitmentState, - up_to_n_times: u64, - ) { - use astria_core::generated::astria::execution::v2::GetCommitmentStateRequest; - - astria_grpc_mock::Mock::for_rpc_given( - "get_commitment_state", - astria_grpc_mock::matcher::message_type::(), + "create_execution_session", + astria_grpc_mock::matcher::message_type::(), ) .respond_with(astria_grpc_mock::response::constant_response( - commitment_state, + execution_session, )) .up_to_n_times(up_to_n_times) - .expect(1..) + .expect(expected_calls) .mount(&self.mock_grpc.mock_server) .await; } @@ -347,9 +333,10 @@ impl TestConductor { &self, mock_name: Option<&str>, expected_pbjson: S, - response: Block, + block_metadata: ExecutedBlockMetadata, expected_calls: u64, ) -> astria_grpc_mock::MockGuard { + use astria_core::generated::astria::execution::v2::ExecuteBlockResponse; use astria_grpc_mock::{ matcher::message_partial_pbjson, response::constant_response, @@ -357,7 +344,9 @@ impl TestConductor { }; let mut mock = Mock::for_rpc_given("execute_block", message_partial_pbjson(&expected_pbjson)) - .respond_with(constant_response(response)); + .respond_with(constant_response(ExecuteBlockResponse { + executed_block_metadata: Some(block_metadata.clone()), + })); if let Some(name) = mock_name { mock = mock.with_name(name); } @@ -402,6 +391,7 @@ impl TestConductor { let mut mock = Mock::for_rpc_given( "update_commitment_state", message_partial_pbjson(&UpdateCommitmentStateRequest { + session_id: EXECUTION_SESSION_ID.to_string(), commitment_state: Some(commitment_state.clone()), }), ) diff --git a/crates/astria-conductor/tests/blackbox/soft_and_firm.rs b/crates/astria-conductor/tests/blackbox/soft_and_firm.rs index b172b7de0f..2386331246 100644 --- a/crates/astria-conductor/tests/blackbox/soft_and_firm.rs +++ b/crates/astria-conductor/tests/blackbox/soft_and_firm.rs @@ -12,12 +12,11 @@ use crate::{ mount_abci_info, mount_celestia_blobs, mount_celestia_header_network_head, + mount_create_execution_session, + mount_execute_block, mount_execute_block_tonic_code, - mount_executed_block, - mount_get_block, - mount_get_commitment_state, + mount_get_executed_block_metadata, mount_get_filtered_sequencer_block, - mount_get_genesis_info, mount_sequencer_commit, mount_sequencer_genesis, mount_sequencer_validator_set, @@ -38,27 +37,27 @@ use crate::{ async fn executes_soft_first_then_updates_firm() { let test_conductor = spawn_conductor(CommitLevel::SoftAndFirm).await; - mount_get_genesis_info!( - test_conductor, - sequencer_start_height: 3, - celestia_block_variance: 10, - rollup_start_block_number: 2, - rollup_stop_block_number: 9, - ); - - mount_get_commitment_state!( - test_conductor, - firm: ( - number: 1, - hash: [1; 64], - parent: [0; 64], - ), - soft: ( - number: 1, - hash: [1; 64], - parent: [0; 64], - ), - base_celestia_height: 1, + mount_create_execution_session!( + test_conductor, + execution_session_parameters: ( + rollup_start_block_number: 2, + rollup_end_block_number: 9, + sequencer_start_block_height: 3, + celestia_max_look_ahead: 10, + ), + commitment_state: ( + firm: ( + number: 1, + hash: "1", + parent: "0", + ), + soft: ( + number: 1, + hash: "1", + parent: "0", + ), + lowest_celestia_search_height: 1, + ) ); mount_abci_info!( @@ -78,26 +77,26 @@ async fn executes_soft_first_then_updates_firm() { sequencer_height: 3, ); - let execute_block = mount_executed_block!( + let execute_block = mount_execute_block!( test_conductor, number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ); let update_commitment_state_soft = mount_update_commitment_state!( test_conductor, firm: ( number: 1, - hash: [1; 64], - parent: [0; 64], + hash: "1", + parent: "0", ), soft: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, ); timeout( @@ -131,15 +130,15 @@ async fn executes_soft_first_then_updates_firm() { test_conductor, firm: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), soft: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, ); timeout( @@ -174,27 +173,27 @@ async fn executes_soft_first_then_updates_firm() { async fn executes_firm_then_soft_at_next_height() { let test_conductor = spawn_conductor(CommitLevel::SoftAndFirm).await; - mount_get_genesis_info!( - test_conductor, - sequencer_start_height: 3, - celestia_block_variance: 10, - rollup_start_block_number: 2, - rollup_stop_block_number: 9, - ); - - mount_get_commitment_state!( - test_conductor, - firm: ( - number: 1, - hash: [1; 64], - parent: [0; 64], - ), - soft: ( - number: 1, - hash: [1; 64], - parent: [0; 64], - ), - base_celestia_height: 1, + mount_create_execution_session!( + test_conductor, + execution_session_parameters: ( + rollup_start_block_number: 2, + rollup_end_block_number: 9, + sequencer_start_block_height: 3, + celestia_max_look_ahead: 10, + ), + commitment_state: ( + firm: ( + number: 1, + hash: "1", + parent: "0", + ), + soft: ( + number: 1, + hash: "1", + parent: "0", + ), + lowest_celestia_search_height: 1, + ) ); mount_abci_info!( @@ -222,11 +221,11 @@ async fn executes_firm_then_soft_at_next_height() { mount_sequencer_validator_set!(test_conductor, height: 2u32); - let execute_block = mount_executed_block!( + let execute_block = mount_execute_block!( test_conductor, number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ); // Mount soft block at current height with a slight delay @@ -247,15 +246,15 @@ async fn executes_firm_then_soft_at_next_height() { test_conductor, firm: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), soft: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, ); // This guard's conditions will be checked when it is dropped, ensuring that there have been 0 @@ -269,15 +268,15 @@ async fn executes_firm_then_soft_at_next_height() { mock_name: "should_be_ignored_update_commitment_state_soft", firm: ( number: 1, - hash: [1; 64], - parent: [0; 64], + hash: "1", + parent: "0", ), soft: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, expected_calls: 0, ); @@ -294,26 +293,26 @@ async fn executes_firm_then_soft_at_next_height() { 1000ms", ); - let execute_block = mount_executed_block!( + let execute_block = mount_execute_block!( test_conductor, number: 3, - hash: [3; 64], - parent: [2; 64], + hash: "3", + parent: "2", ); let update_commitment_state_soft = mount_update_commitment_state!( test_conductor, firm: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), soft: ( number: 3, - hash: [3; 64], - parent: [2; 64], + hash: "3", + parent: "2", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, ); timeout( @@ -335,27 +334,27 @@ async fn executes_firm_then_soft_at_next_height() { async fn missing_block_is_fetched_for_updating_firm_commitment() { let test_conductor = spawn_conductor(CommitLevel::SoftAndFirm).await; - mount_get_genesis_info!( - test_conductor, - sequencer_start_height: 3, - celestia_block_variance: 10, - rollup_start_block_number: 2, - rollup_stop_block_number: 9, - ); - - mount_get_commitment_state!( - test_conductor, - firm: ( - number: 1, - hash: [1; 64], - parent: [0; 64], - ), - soft: ( - number: 2, - hash: [2; 64], - parent: [1; 64], - ), - base_celestia_height: 1, + mount_create_execution_session!( + test_conductor, + execution_session_parameters: ( + rollup_start_block_number: 2, + rollup_end_block_number: 9, + sequencer_start_block_height: 3, + celestia_max_look_ahead: 10, + ), + commitment_state: ( + firm: ( + number: 1, + hash: "1", + parent: "0", + ), + soft: ( + number: 2, + hash: "2", + parent: "1", + ), + lowest_celestia_search_height: 1, + ) ); mount_abci_info!( @@ -365,11 +364,11 @@ async fn missing_block_is_fetched_for_updating_firm_commitment() { mount_sequencer_genesis!(test_conductor); - mount_get_block!( + mount_get_executed_block_metadata!( test_conductor, number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ); mount_celestia_header_network_head!( @@ -394,15 +393,15 @@ async fn missing_block_is_fetched_for_updating_firm_commitment() { test_conductor, firm: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), soft: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, ); timeout( @@ -417,26 +416,26 @@ async fn missing_block_is_fetched_for_updating_firm_commitment() { sequencer_height: 4, ); - let execute_block_soft = mount_executed_block!( + let execute_block_soft = mount_execute_block!( test_conductor, number: 3, - hash: [3; 64], - parent: [2; 64], + hash: "3", + parent: "2", ); let update_commitment_state_soft = mount_update_commitment_state!( test_conductor, firm: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), soft: ( number: 3, - hash: [3; 64], - parent: [2; 64], + hash: "3", + parent: "2", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, ); timeout( @@ -466,30 +465,28 @@ async fn missing_block_is_fetched_for_updating_firm_commitment() { async fn restarts_on_permission_denied() { let test_conductor = spawn_conductor(CommitLevel::SoftAndFirm).await; - mount_get_genesis_info!( - test_conductor, - sequencer_start_height: 3, - celestia_block_variance: 10, - rollup_start_block_number: 2, - rollup_stop_block_number: 9, - up_to_n_times: 2, - halt_at_rollup_stop_number: false, - expected_calls: 2, - ); - - mount_get_commitment_state!( - test_conductor, - firm: ( - number: 1, - hash: [1; 64], - parent: [0; 64], - ), - soft: ( - number: 1, - hash: [1; 64], - parent: [0; 64], + mount_create_execution_session!( + test_conductor, + execution_session_parameters: ( + rollup_start_block_number: 2, + rollup_end_block_number: 9, + sequencer_start_block_height: 3, + celestia_max_look_ahead: 10, + ), + commitment_state: ( + firm: ( + number: 1, + hash: "1", + parent: "0", + ), + soft: ( + number: 1, + hash: "1", + parent: "0", + ), + lowest_celestia_search_height: 1, ), - base_celestia_height: 1, + expected_calls: 2, up_to_n_times: 2, ); @@ -528,7 +525,7 @@ async fn restarts_on_permission_denied() { // This mock can only be called up to 1 time, allowing a normal `execute_block` call after. let execute_block_tonic_code = mount_execute_block_tonic_code!( test_conductor, - parent: [1; 64], + parent: "1", status_code: tonic::Code::PermissionDenied, ); @@ -539,41 +536,41 @@ async fn restarts_on_permission_denied() { .await .expect("conductor should have restarted after a permission denied error within 1000ms"); - let execute_block = mount_executed_block!( + let execute_block = mount_execute_block!( test_conductor, number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ); let update_commitment_state_soft = mount_update_commitment_state!( test_conductor, firm: ( number: 1, - hash: [1; 64], - parent: [0; 64], + hash: "1", + parent: "0", ), soft: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, ); let update_commitment_state_firm = mount_update_commitment_state!( test_conductor, firm: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), soft: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, ); timeout( @@ -596,8 +593,8 @@ async fn restarts_on_permission_denied() { /// should execute both the soft and firm blocks at the stop height and then perform a restart. /// /// This test consists of the following steps: -/// 1. Mount commitment state and genesis info with a sequencer stop height of 3, only responding up -/// to 1 time so that Conductor will not receive the same response after restart. +/// 1. Mount execution session with a rollup stop number of 2 (sequencer height 3), only responding +/// up to 1 time so that Conductor will not receive the same response after restart. /// 2. Mount Celestia network head and sequencer genesis. /// 3. Mount ABCI info and sequencer blocks (soft blocks) for heights 3 and 4. /// 4. Mount firm blocks at heights 3 and 4 with a slight delay to ensure that the soft blocks @@ -605,9 +602,8 @@ async fn restarts_on_permission_denied() { /// 5. Mount `execute_block` and `update_commitment_state` for both soft and firm blocks at height 3 /// 6. Await satisfaction of the `execute_block` and `update_commitment_state` for the soft and firm /// blocks at height 3 with a timeout of 1000ms. -/// 7. Mount new genesis info with a sequencer stop height of 10 and a rollup start block height of -/// 2, along with corresponding commitment state, reflecting that block 1 has already been -/// executed and the commitment state updated. +/// 7. Mount new execution session with a rollup stop number of 9 and a start block number of 2, +/// reflecting that block 1 has already been executed and the commitment state updated. /// 8. Mount `execute_block` and `update_commitment_state` for both soft and firm blocks at height 4 /// and await their satisfaction. #[expect( @@ -618,29 +614,28 @@ async fn restarts_on_permission_denied() { async fn restarts_after_reaching_soft_stop_height_first() { let test_conductor = spawn_conductor(CommitLevel::SoftAndFirm).await; - mount_get_genesis_info!( - test_conductor, - sequencer_start_height: 3, - celestia_block_variance: 10, - rollup_start_block_number: 2, - rollup_stop_block_number: 2, - up_to_n_times: 1, // We only respond once since this needs to be updated after restart - ); - - mount_get_commitment_state!( + mount_create_execution_session!( test_conductor, - firm: ( - number: 1, - hash: [1; 64], - parent: [0; 64], + execution_session_parameters: ( + rollup_start_block_number: 2, + rollup_end_block_number: 2, + sequencer_start_block_height: 3, + celestia_max_look_ahead: 10, ), - soft: ( - number: 1, - hash: [1; 64], - parent: [0; 64], + commitment_state: ( + firm: ( + number: 1, + hash: "1", + parent: "0", + ), + soft: ( + number: 1, + hash: "1", + parent: "0", + ), + lowest_celestia_search_height: 1, ), - base_celestia_height: 1, - up_to_n_times: 1, // We only respond once since this needs to be updated after restart + up_to_n_times: 1, // We only respond once since a new execution session is needed after restart ); mount_sequencer_genesis!(test_conductor); @@ -681,12 +676,12 @@ async fn restarts_after_reaching_soft_stop_height_first() { mount_sequencer_validator_set!(test_conductor, height: 2u32); mount_sequencer_validator_set!(test_conductor, height: 3u32); - let execute_block_1 = mount_executed_block!( + let execute_block_1 = mount_execute_block!( test_conductor, mock_name: "execute_block_1", number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", expected_calls: 1, // This should not be called again after restart ); @@ -695,15 +690,15 @@ async fn restarts_after_reaching_soft_stop_height_first() { mock_name: "update_commitment_state_soft_1", firm: ( number: 1, - hash: [1; 64], - parent: [0; 64], + hash: "1", + parent: "0", ), soft: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, expected_calls: 1, ); @@ -712,15 +707,15 @@ async fn restarts_after_reaching_soft_stop_height_first() { mock_name: "update_commitment_state_firm_1", firm: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), soft: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, expected_calls: 1, // Should not be called again after restart ); @@ -735,35 +730,35 @@ async fn restarts_after_reaching_soft_stop_height_first() { .await .expect("conductor should have updated the firm commitment state within 1000ms"); - mount_get_genesis_info!( + mount_create_execution_session!( test_conductor, - sequencer_start_height: 4, - celestia_block_variance: 10, - rollup_start_block_number: 3, - rollup_stop_block_number: 9, - ); - - mount_get_commitment_state!( - test_conductor, - firm: ( - number: 2, - hash: [2; 64], - parent: [1; 64], + execution_session_parameters: ( + rollup_start_block_number: 3, + rollup_end_block_number: 9, + sequencer_start_block_height: 4, + celestia_max_look_ahead: 10, ), - soft: ( - number: 2, - hash: [2; 64], - parent: [1; 64], + commitment_state: ( + firm: ( + number: 2, + hash: "2", + parent: "1", + ), + soft: ( + number: 2, + hash: "2", + parent: "1", + ), + lowest_celestia_search_height: 1, ), - base_celestia_height: 1, ); - let execute_block_2 = mount_executed_block!( + let execute_block_2 = mount_execute_block!( test_conductor, mock_name: "execute_block_2", number: 3, - hash: [3; 64], - parent: [2; 64], + hash: "3", + parent: "2", expected_calls: 1, ); @@ -773,15 +768,15 @@ async fn restarts_after_reaching_soft_stop_height_first() { mock_name: "update_commitment_state_soft_2", firm: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), soft: ( number: 3, - hash: [3; 64], - parent: [2; 64], + hash: "3", + parent: "2", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, expected_calls: 1, ); @@ -790,15 +785,15 @@ async fn restarts_after_reaching_soft_stop_height_first() { mock_name: "update_commitment_state_firm_2", firm: ( number: 3, - hash: [3; 64], - parent: [2; 64], + hash: "3", + parent: "2", ), soft: ( number: 3, - hash: [3; 64], - parent: [2; 64], + hash: "3", + parent: "2", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, expected_calls: 1, ); @@ -819,8 +814,8 @@ async fn restarts_after_reaching_soft_stop_height_first() { /// first. /// /// This test consists of the following steps: -/// 1. Mount commitment state and genesis info with a sequencer stop height of 3, only responding up -/// to 1 time so that Conductor will not receive the same response after restart. +/// 1. Mount execution session with a rollup stop number of 2 (sequencer height 3), only responding +/// up to 1 time so that Conductor will not receive the same response after restart. /// 2. Mount Celestia network head and sequencer genesis. /// 3. Mount ABCI info and sequencer blocks (soft blocks) for heights 3 and 4 with a slight delay, /// to ensure the firm blocks arrive first. @@ -830,9 +825,8 @@ async fn restarts_after_reaching_soft_stop_height_first() { /// 5. Mount `execute_block` and `update_commitment_state` for firm block at height 3. /// 6. Await satisfaction of the `execute_block` and `update_commitment_state` for the firm block at /// height 3 with a timeout of 1000ms. -/// 7. Mount new genesis info with a sequencer stop height of 10 and a rollup start block height of -/// 2, along with corresponding commitment state, reflecting that block 1 has already been -/// executed and the commitment state updated. +/// 7. Mount new genesis info with a rollup stop number of 9 and a start block number of 2, +/// reflecting that block 1 has already been executed and the commitment state updated. /// 8. Mount `execute_block` and `update_commitment_state` for both soft and firm blocks at height 4 /// and await their satisfaction (the soft mount need not be satisfied in the case that the firm /// block is received first; we are just looking to see that the conductor restarted properly). @@ -844,29 +838,28 @@ async fn restarts_after_reaching_soft_stop_height_first() { async fn restarts_after_reaching_firm_stop_height_first() { let test_conductor = spawn_conductor(CommitLevel::SoftAndFirm).await; - mount_get_genesis_info!( - test_conductor, - sequencer_start_height: 3, - celestia_block_variance: 10, - rollup_start_block_number: 2, - rollup_stop_block_number: 2, - up_to_n_times: 1, // We only respond once since this needs to be updated after restart - ); - - mount_get_commitment_state!( + mount_create_execution_session!( test_conductor, - firm: ( - number: 1, - hash: [1; 64], - parent: [0; 64], + execution_session_parameters: ( + rollup_start_block_number: 2, + rollup_end_block_number: 2, + sequencer_start_block_height: 3, + celestia_max_look_ahead: 10, ), - soft: ( - number: 1, - hash: [1; 64], - parent: [0; 64], + commitment_state: ( + firm: ( + number: 1, + hash: "1", + parent: "0", + ), + soft: ( + number: 1, + hash: "1", + parent: "0", + ), + lowest_celestia_search_height: 1, ), - base_celestia_height: 1, - up_to_n_times: 1, // We only respond once since this needs to be updated after restart + up_to_n_times: 1, // We only respond once since a new execution session is needed after restart ); mount_sequencer_genesis!(test_conductor); @@ -907,12 +900,12 @@ async fn restarts_after_reaching_firm_stop_height_first() { mount_sequencer_validator_set!(test_conductor, height: 2u32); mount_sequencer_validator_set!(test_conductor, height: 3u32); - let execute_block_1 = mount_executed_block!( + let execute_block_1 = mount_execute_block!( test_conductor, mock_name: "execute_block_1", number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", expected_calls: 1, // This should not be called again after restart ); @@ -922,15 +915,15 @@ async fn restarts_after_reaching_firm_stop_height_first() { mock_name: "update_commitment_state_soft_1", firm: ( number: 1, - hash: [1; 64], - parent: [0; 64], + hash: "1", + parent: "0", ), soft: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, expected_calls: 0, ); @@ -939,15 +932,15 @@ async fn restarts_after_reaching_firm_stop_height_first() { mock_name: "update_commitment_state_firm_1", firm: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), soft: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, expected_calls: 1, // Should not be called again after restart ); @@ -961,35 +954,35 @@ async fn restarts_after_reaching_firm_stop_height_first() { .await .expect("conductor should have updated the firm commitment state within 1000ms"); - mount_get_genesis_info!( + mount_create_execution_session!( test_conductor, - sequencer_start_height: 4, - celestia_block_variance: 10, - rollup_start_block_number: 3, - rollup_stop_block_number: 9, - ); - - mount_get_commitment_state!( - test_conductor, - firm: ( - number: 2, - hash: [2; 64], - parent: [1; 64], + execution_session_parameters: ( + rollup_start_block_number: 3, + rollup_end_block_number: 9, + sequencer_start_block_height: 4, + celestia_max_look_ahead: 10, ), - soft: ( - number: 2, - hash: [2; 64], - parent: [1; 64], + commitment_state: ( + firm: ( + number: 2, + hash: "2", + parent: "1", + ), + soft: ( + number: 2, + hash: "2", + parent: "1", + ), + lowest_celestia_search_height: 1, ), - base_celestia_height: 1, ); - let execute_block_2 = mount_executed_block!( + let execute_block_2 = mount_execute_block!( test_conductor, mock_name: "execute_block_2", number: 3, - hash: [3; 64], - parent: [2; 64], + hash: "3", + parent: "2", expected_calls: 1, ); @@ -999,15 +992,15 @@ async fn restarts_after_reaching_firm_stop_height_first() { mock_name: "update_commitment_state_soft_2", firm: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), soft: ( number: 3, - hash: [3; 64], - parent: [2; 64], + hash: "3", + parent: "2", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, expected_calls: 0..=1, ); @@ -1016,15 +1009,15 @@ async fn restarts_after_reaching_firm_stop_height_first() { mock_name: "update_commitment_state_firm_2", firm: ( number: 3, - hash: [3; 64], - parent: [2; 64], + hash: "3", + parent: "2", ), soft: ( number: 3, - hash: [3; 64], - parent: [2; 64], + hash: "3", + parent: "2", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, expected_calls: 1, ); @@ -1039,128 +1032,138 @@ async fn restarts_after_reaching_firm_stop_height_first() { .expect("conductor should have updated the firm commitment state within 1000ms"); } -/// Tests if the conductor correctly stops and does not restart after reaching the sequencer stop -/// height if genesis info's `halt_at_rollup_stop_number` is `true`. +/// Tests if conductor restarts internal services if rollup response with `OUT_OF_RANGE`. /// -/// This test consists of the following steps: -/// 1. Mount commitment state and genesis info with a sequencer stop height of 3, expecting only 1 -/// response. -/// 2. Mount Celestia network head and sequencer genesis. -/// 3. Mount ABCI info and sequencer blocks (soft blocks) for height 3. -/// 4. Mount firm blocks at height 3. -/// 5. Mount `execute_block` and `update_commitment_state` for soft and firm blocks at height 3. -/// 6. Await satisfaction of the `execute_block` and `update_commitment_state` for the firm block -/// height 3 with a timeout of 1000ms. The soft mount need not be satisfied in the case that the -/// firm block is received first. The test case of ensuring the soft commitment state is updated -/// correctly in the case of receiving a soft block first is covered in -/// `conductor_restarts_after_reaching_soft_stop_height_first`. -/// 7. Allow ample time for the conductor to potentially restart erroneously. +/// Astria Geth will return an `OutOfRange` status if `execute_block` is called for a block outside +/// of the bounds of the given session. If this happens, Conductor should restart and attempt to +/// create a new session. +#[expect( + clippy::too_many_lines, + reason = "all lines fairly necessary, and I don't think a test warrants a refactor" +)] #[tokio::test(flavor = "multi_thread", worker_threads = 1)] -async fn stops_at_stop_height() { +async fn restarts_on_out_of_range() { let test_conductor = spawn_conductor(CommitLevel::SoftAndFirm).await; - mount_get_genesis_info!( - test_conductor, - sequencer_start_height: 3, - celestia_block_variance: 10, - rollup_start_block_number: 2, - rollup_stop_block_number: 2, - up_to_n_times: 2, // allow for calls after an potential erroneous restart - halt_at_rollup_stop_number: true, - expected_calls: 1, - ); - - mount_get_commitment_state!( - test_conductor, - firm: ( - number: 1, - hash: [1; 64], - parent: [0; 64], + mount_create_execution_session!( + test_conductor, + execution_session_parameters: ( + rollup_start_block_number: 2, + rollup_end_block_number: 9, + sequencer_start_block_height: 3, + celestia_max_look_ahead: 10, + ), + commitment_state: ( + firm: ( + number: 1, + hash: "1", + parent: "0", + ), + soft: ( + number: 1, + hash: "1", + parent: "0", + ), + lowest_celestia_search_height: 1, ), - soft: ( - number: 1, - hash: [1; 64], - parent: [0; 64], - ), - base_celestia_height: 1, + expected_calls: 2, + up_to_n_times: 2, ); mount_sequencer_genesis!(test_conductor); + mount_celestia_header_network_head!( test_conductor, height: 1u32, ); + + mount_celestia_blobs!( + test_conductor, + celestia_height: 1, + sequencer_heights: [3], + delay: Some(Duration::from_millis(250)) + ); + + mount_sequencer_commit!( + test_conductor, + height: 3u32, + ); + + mount_sequencer_validator_set!(test_conductor, height: 2u32); + mount_abci_info!( test_conductor, latest_sequencer_height: 3, ); - // Mount soft blocks for height 3 mount_get_filtered_sequencer_block!( test_conductor, sequencer_height: 3, ); - // Mount firm blocks for height 3 - mount_celestia_blobs!( - test_conductor, - celestia_height: 1, - sequencer_heights: [3], - ); - mount_sequencer_commit!( + // mount tonic `OutOfRange` error to cause the conductor to restart. + // This mock can only be called up to 1 time, allowing a normal `execute_block` call after. + let execute_block_tonic_code = mount_execute_block_tonic_code!( test_conductor, - height: 3u32, + parent: "1", + status_code: tonic::Code::OutOfRange, ); - mount_sequencer_validator_set!(test_conductor, height: 2u32); - let execute_block_1 = mount_executed_block!( + timeout( + Duration::from_millis(1000), + execute_block_tonic_code.wait_until_satisfied(), + ) + .await + .expect("conductor should have restarted after a permission denied error within 1000ms"); + + let execute_block = mount_execute_block!( test_conductor, - mock_name: "execute_block_1", number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ); - let _update_commitment_state_soft_1 = mount_update_commitment_state!( + let update_commitment_state_soft = mount_update_commitment_state!( test_conductor, - mock_name: "update_commitment_state_soft_1", firm: ( number: 1, - hash: [1; 64], - parent: [0; 64], + hash: "1", + parent: "0", ), soft: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), - base_celestia_height: 1, - expected_calls: 0..=1, + lowest_celestia_search_height: 1, ); - let update_commitment_state_firm_1 = mount_update_commitment_state!( + let update_commitment_state_firm = mount_update_commitment_state!( test_conductor, - mock_name: "update_commitment_state_firm_1", firm: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), soft: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, ); timeout( Duration::from_millis(1000), - join( - execute_block_1.wait_until_satisfied(), - update_commitment_state_firm_1.wait_until_satisfied(), + join3( + execute_block.wait_until_satisfied(), + update_commitment_state_soft.wait_until_satisfied(), + update_commitment_state_firm.wait_until_satisfied(), ), ) .await - .expect("conductor should have updated the firm commitment state within 1000ms"); + .expect( + "conductor should have executed the block and updated the soft and firm commitment states \ + within 1000ms", + ); } diff --git a/crates/astria-conductor/tests/blackbox/soft_only.rs b/crates/astria-conductor/tests/blackbox/soft_only.rs index 1b51c6e4d9..0bf44c11f7 100644 --- a/crates/astria-conductor/tests/blackbox/soft_only.rs +++ b/crates/astria-conductor/tests/blackbox/soft_only.rs @@ -5,10 +5,7 @@ use astria_conductor::{ Conductor, Config, }; -use astria_core::generated::astria::execution::v2::{ - GetCommitmentStateRequest, - GetGenesisInfoRequest, -}; +use astria_core::generated::astria::execution::v2::CreateExecutionSessionRequest; use futures::future::{ join, join4, @@ -17,8 +14,7 @@ use telemetry::metrics; use tokio::time::timeout; use crate::{ - commitment_state, - genesis_info, + execution_session, helpers::{ make_config, mount_genesis, @@ -26,10 +22,9 @@ use crate::{ MockGrpc, }, mount_abci_info, - mount_executed_block, - mount_get_commitment_state, + mount_create_execution_session, + mount_execute_block, mount_get_filtered_sequencer_block, - mount_get_genesis_info, mount_sequencer_genesis, mount_update_commitment_state, SEQUENCER_CHAIN_ID, @@ -39,27 +34,27 @@ use crate::{ async fn simple() { let test_conductor = spawn_conductor(CommitLevel::SoftOnly).await; - mount_get_genesis_info!( - test_conductor, - sequencer_start_height: 3, - celestia_block_variance: 10, - rollup_start_block_number: 2, - rollup_stop_block_number: 9, - ); - - mount_get_commitment_state!( - test_conductor, - firm: ( - number: 1, - hash: [1; 64], - parent: [0; 64], - ), - soft: ( - number: 1, - hash: [1; 64], - parent: [0; 64], - ), - base_celestia_height: 1, + mount_create_execution_session!( + test_conductor, + execution_session_parameters: ( + rollup_start_block_number: 2, + rollup_end_block_number: 9, + sequencer_start_block_height: 3, + celestia_max_look_ahead: 10, + ), + commitment_state: ( + firm: ( + number: 1, + hash: "1", + parent: "0", + ), + soft: ( + number: 1, + hash: "1", + parent: "0", + ), + lowest_celestia_search_height: 1, + ) ); mount_sequencer_genesis!(test_conductor); @@ -74,26 +69,26 @@ async fn simple() { sequencer_height: 3, ); - let execute_block = mount_executed_block!( + let execute_block = mount_execute_block!( test_conductor, number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ); let update_commitment_state = mount_update_commitment_state!( test_conductor, firm: ( number: 1, - hash: [1; 64], - parent: [0; 64], + hash: "1", + parent: "0", ), soft: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, ); timeout( @@ -114,27 +109,27 @@ async fn simple() { async fn submits_two_heights_in_succession() { let test_conductor = spawn_conductor(CommitLevel::SoftOnly).await; - mount_get_genesis_info!( - test_conductor, - sequencer_start_height: 3, - celestia_block_variance: 10, - rollup_start_block_number: 2, - rollup_stop_block_number: 9, - ); - - mount_get_commitment_state!( - test_conductor, - firm: ( - number: 1, - hash: [1; 64], - parent: [0; 64], - ), - soft: ( - number: 1, - hash: [1; 64], - parent: [0; 64], - ), - base_celestia_height: 1, + mount_create_execution_session!( + test_conductor, + execution_session_parameters: ( + rollup_start_block_number: 2, + rollup_end_block_number: 9, + sequencer_start_block_height: 3, + celestia_max_look_ahead: 10, + ), + commitment_state: ( + firm: ( + number: 1, + hash: "1", + parent: "0", + ), + soft: ( + number: 1, + hash: "1", + parent: "0", + ), + lowest_celestia_search_height: 1, + ) ); mount_sequencer_genesis!(test_conductor); @@ -154,12 +149,12 @@ async fn submits_two_heights_in_succession() { sequencer_height: 4, ); - let execute_block_number_2 = mount_executed_block!( + let execute_block_number_2 = mount_execute_block!( test_conductor, mock_name: "first_execute", number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ); let update_commitment_state_number_2 = mount_update_commitment_state!( @@ -167,23 +162,23 @@ async fn submits_two_heights_in_succession() { mock_name: "first_update", firm: ( number: 1, - hash: [1; 64], - parent: [0; 64], + hash: "1", + parent: "0", ), soft: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, ); - let execute_block_number_3 = mount_executed_block!( + let execute_block_number_3 = mount_execute_block!( test_conductor, mock_name: "second_execute", number: 3, - hash: [3; 64], - parent: [2; 64], + hash: "3", + parent: "2", ); let update_commitment_state_number_3 = mount_update_commitment_state!( @@ -191,15 +186,15 @@ async fn submits_two_heights_in_succession() { mock_name: "second_update", firm: ( number: 1, - hash: [1; 64], - parent: [0; 64], + hash: "1", + parent: "0", ), soft: ( number: 3, - hash: [3; 64], - parent: [2; 64], + hash: "3", + parent: "2", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, ); timeout( @@ -222,27 +217,27 @@ async fn submits_two_heights_in_succession() { async fn skips_already_executed_heights() { let test_conductor = spawn_conductor(CommitLevel::SoftOnly).await; - mount_get_genesis_info!( - test_conductor, - sequencer_start_height: 3, - celestia_block_variance: 10, - rollup_start_block_number: 2, - rollup_stop_block_number: 9, - ); - - mount_get_commitment_state!( - test_conductor, - firm: ( - number: 1, - hash: [1; 64], - parent: [0; 64], - ), - soft: ( - number: 5, - hash: [1; 64], - parent: [0; 64], - ), - base_celestia_height: 1, + mount_create_execution_session!( + test_conductor, + execution_session_parameters: ( + rollup_start_block_number: 2, + rollup_end_block_number: 9, + sequencer_start_block_height: 3, + celestia_max_look_ahead: 10, + ), + commitment_state: ( + firm: ( + number: 1, + hash: "1", + parent: "0", + ), + soft: ( + number: 5, + hash: "1", + parent: "0", + ), + lowest_celestia_search_height: 1, + ) ); mount_sequencer_genesis!(test_conductor); @@ -257,26 +252,26 @@ async fn skips_already_executed_heights() { sequencer_height: 7, ); - let execute_block = mount_executed_block!( + let execute_block = mount_execute_block!( test_conductor, number: 6, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ); let update_commitment_state = mount_update_commitment_state!( test_conductor, firm: ( number: 1, - hash: [1; 64], - parent: [0; 64], + hash: "1", + parent: "0", ), soft: ( number: 6, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, ); timeout( @@ -297,27 +292,27 @@ async fn skips_already_executed_heights() { async fn requests_from_later_genesis_height() { let test_conductor = spawn_conductor(CommitLevel::SoftOnly).await; - mount_get_genesis_info!( - test_conductor, - sequencer_start_height: 12, - celestia_block_variance: 10, - rollup_start_block_number: 2, - rollup_stop_block_number: 10, - ); - - mount_get_commitment_state!( - test_conductor, - firm: ( - number: 1, - hash: [1; 64], - parent: [0; 64], - ), - soft: ( - number: 1, - hash: [1; 64], - parent: [0; 64], - ), - base_celestia_height: 1, + mount_create_execution_session!( + test_conductor, + execution_session_parameters: ( + rollup_start_block_number: 2, + rollup_end_block_number: 10, + sequencer_start_block_height: 12, + celestia_max_look_ahead: 10, + ), + commitment_state: ( + firm: ( + number: 1, + hash: "1", + parent: "0", + ), + soft: ( + number: 1, + hash: "1", + parent: "0", + ), + lowest_celestia_search_height: 1, + ) ); mount_sequencer_genesis!(test_conductor); @@ -332,26 +327,26 @@ async fn requests_from_later_genesis_height() { sequencer_height: 12, ); - let execute_block = mount_executed_block!( + let execute_block = mount_execute_block!( test_conductor, number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ); let update_commitment_state = mount_update_commitment_state!( test_conductor, firm: ( number: 1, - hash: [1; 64], - parent: [0; 64], + hash: "1", + parent: "0", ), soft: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), - base_celestia_height: 1 + lowest_celestia_search_height: 1 ); timeout( @@ -405,34 +400,30 @@ async fn exits_on_sequencer_chain_id_mismatch() { }; GrpcMock::for_rpc_given( - "get_genesis_info", - matcher::message_type::(), - ) - .respond_with(GrpcResponse::constant_response( - genesis_info!(sequencer_start_height: 3, - celestia_block_variance: 10, - rollup_start_block_number: 2, - rollup_stop_block_number: 9), - )) - .expect(0..) - .mount(&mock_grpc.mock_server) - .await; - - GrpcMock::for_rpc_given( - "get_commitment_state", - matcher::message_type::(), + "create_execution_session", + matcher::message_type::(), ) - .respond_with(GrpcResponse::constant_response(commitment_state!(firm: ( - number: 1, - hash: [1; 64], - parent: [0; 64], - ), - soft: ( - number: 1, - hash: [1; 64], - parent: [0; 64], - ), - base_celestia_height: 1,))) + .respond_with(GrpcResponse::constant_response(execution_session!( + execution_session_parameters: ( + rollup_start_block_number: 2, + rollup_end_block_number: 9, + sequencer_start_block_height: 3, + celestia_max_look_ahead: 10, + ), + commitment_state: ( + firm: ( + number: 1, + hash: "1", + parent: "0", + ), + soft: ( + number: 1, + hash: "1", + parent: "0", + ), + lowest_celestia_search_height: 1, + ), + ))) .expect(0..) .mount(&mock_grpc.mock_server) .await; @@ -468,14 +459,13 @@ async fn exits_on_sequencer_chain_id_mismatch() { /// and continues executing soft blocks after receiving updated genesis info and commitment state. /// /// It consists of the following steps: -/// 1. Mount commitment state and genesis info with a stop height of 3, responding only up to 1 time -/// so that the same information is not retrieved after restarting. +/// 1. Mount execution session with a rollup stop number of 2 (sequencer height 3), responding only +/// up to 1 time so that the same information is not retrieved after restarting. /// 2. Mount sequencer genesis, ABCI info, and sequencer blocks for heights 3 and 4. /// 3. Mount `execute_block` and `update_commitment_state` mocks for the soft block at height 3, /// expecting only 1 call and timing out after 1000ms. -/// 4. Mount updated commitment state and genesis info with a stop height of 10 (more than high -/// enough) and a rollup start block height of 2, reflecting that the first block has already -/// been executed. +/// 4. Mount updated execution session with a rollup stop number of 9 (more than high enough) and a +/// start block number of 2, reflecting that the first block has already been executed. /// 5. Mount `execute_block` and `update_commitment_state` mocks for the soft block at height 4, /// awaiting their satisfaction. #[expect( @@ -486,29 +476,28 @@ async fn exits_on_sequencer_chain_id_mismatch() { async fn restarts_after_reaching_stop_block_height() { let test_conductor = spawn_conductor(CommitLevel::SoftOnly).await; - mount_get_genesis_info!( - test_conductor, - sequencer_start_height: 3, - celestia_block_variance: 10, - rollup_start_block_number: 2, - rollup_stop_block_number: 2, - up_to_n_times: 1, // We need to mount a new genesis info after restart - ); - - mount_get_commitment_state!( + mount_create_execution_session!( test_conductor, - firm: ( - number: 1, - hash: [1; 64], - parent: [0; 64], + execution_session_parameters: ( + rollup_start_block_number: 2, + rollup_end_block_number: 2, + sequencer_start_block_height: 3, + celestia_max_look_ahead: 10, ), - soft: ( - number: 1, - hash: [1; 64], - parent: [0; 64], + commitment_state: ( + firm: ( + number: 1, + hash: "1", + parent: "0", + ), + soft: ( + number: 1, + hash: "1", + parent: "0", + ), + lowest_celestia_search_height: 1, ), - base_celestia_height: 1, - up_to_n_times: 1, // We need to mount a new commitment state after restart + up_to_n_times: 1, // We need a new execution session after restart ); mount_sequencer_genesis!(test_conductor); @@ -528,12 +517,12 @@ async fn restarts_after_reaching_stop_block_height() { sequencer_height: 4, ); - let execute_block_1 = mount_executed_block!( + let execute_block_1 = mount_execute_block!( test_conductor, mock_name: "execute_block_1", number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", expected_calls: 1, ); @@ -542,15 +531,15 @@ async fn restarts_after_reaching_stop_block_height() { mock_name: "update_commitment_state_1", firm: ( number: 1, - hash: [1; 64], - parent: [0; 64], + hash: "1", + parent: "0", ), soft: ( number: 2, - hash: [2; 64], - parent: [1; 64], + hash: "2", + parent: "1", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, expected_calls: 1, ); @@ -567,35 +556,35 @@ async fn restarts_after_reaching_stop_block_height() { commitment state within 1000ms", ); - mount_get_genesis_info!( - test_conductor, - sequencer_start_height: 4, - celestia_block_variance: 10, - rollup_start_block_number: 3, - rollup_stop_block_number: 9, - ); - - mount_get_commitment_state!( + mount_create_execution_session!( test_conductor, - firm: ( - number: 1, - hash: [1; 64], - parent: [0; 64], + execution_session_parameters: ( + rollup_start_block_number: 3, + rollup_end_block_number: 9, + sequencer_start_block_height: 4, + celestia_max_look_ahead: 10, ), - soft: ( - number: 2, - hash: [2; 64], - parent: [1; 64], + commitment_state: ( + firm: ( + number: 1, + hash: "1", + parent: "0", + ), + soft: ( + number: 2, + hash: "2", + parent: "1", + ), + lowest_celestia_search_height: 1, ), - base_celestia_height: 1, ); - let execute_block_2 = mount_executed_block!( + let execute_block_2 = mount_execute_block!( test_conductor, mock_name: "execute_block_2", number: 3, - hash: [3; 64], - parent: [2; 64], + hash: "3", + parent: "2", expected_calls: 1, ); @@ -604,15 +593,15 @@ async fn restarts_after_reaching_stop_block_height() { mock_name: "update_commitment_state_2", firm: ( number: 1, - hash: [1; 64], - parent: [0; 64], + hash: "1", + parent: "0", ), soft: ( number: 3, - hash: [3; 64], - parent: [2; 64], + hash: "3", + parent: "2", ), - base_celestia_height: 1, + lowest_celestia_search_height: 1, expected_calls: 1, ); diff --git a/crates/astria-core/src/execution/mod.rs b/crates/astria-core/src/execution/mod.rs index ae6adc7cf4..7083bd82d0 100644 --- a/crates/astria-core/src/execution/mod.rs +++ b/crates/astria-core/src/execution/mod.rs @@ -1,2 +1 @@ -pub mod v1; pub mod v2; diff --git a/crates/astria-core/src/execution/v1/mod.rs b/crates/astria-core/src/execution/v1/mod.rs deleted file mode 100644 index 3327cd0edc..0000000000 --- a/crates/astria-core/src/execution/v1/mod.rs +++ /dev/null @@ -1,468 +0,0 @@ -use bytes::Bytes; -use pbjson_types::Timestamp; - -use crate::{ - generated::astria::execution::v1 as raw, - primitive::v1::{ - IncorrectRollupIdLength, - RollupId, - }, - Protobuf, -}; - -// An error when transforming a [`raw::GenesisInfo`] into a [`GenesisInfo`]. -#[derive(Debug, thiserror::Error)] -#[error(transparent)] -pub struct GenesisInfoError(GenesisInfoErrorKind); - -impl GenesisInfoError { - fn incorrect_rollup_id_length(inner: IncorrectRollupIdLength) -> Self { - Self(GenesisInfoErrorKind::IncorrectRollupIdLength(inner)) - } - - fn no_rollup_id() -> Self { - Self(GenesisInfoErrorKind::NoRollupId) - } -} - -#[derive(Debug, thiserror::Error)] -enum GenesisInfoErrorKind { - #[error("`rollup_id` field contained an invalid rollup ID")] - IncorrectRollupIdLength(IncorrectRollupIdLength), - #[error("`rollup_id` was not set")] - NoRollupId, -} - -/// Genesis Info required from a rollup to start an execution client. -/// -/// Contains information about the rollup id, and base heights for both sequencer & celestia. -/// -/// Usually constructed its [`Protobuf`] implementation from a -/// [`raw::GenesisInfo`]. -#[derive(Clone, Copy, Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] -#[cfg_attr( - feature = "serde", - serde(into = "crate::generated::astria::execution::v1::GenesisInfo") -)] -pub struct GenesisInfo { - /// The rollup id which is used to identify the rollup txs. - rollup_id: RollupId, - /// The Sequencer block height which contains the first block of the rollup. - sequencer_genesis_block_height: tendermint::block::Height, - /// The allowed variance in the block height of celestia when looking for sequencer blocks. - celestia_block_variance: u64, -} - -impl GenesisInfo { - #[must_use] - pub fn rollup_id(&self) -> RollupId { - self.rollup_id - } - - #[must_use] - pub fn sequencer_genesis_block_height(&self) -> tendermint::block::Height { - self.sequencer_genesis_block_height - } - - #[must_use] - pub fn celestia_block_variance(&self) -> u64 { - self.celestia_block_variance - } -} - -impl From for raw::GenesisInfo { - fn from(value: GenesisInfo) -> Self { - value.to_raw() - } -} - -impl Protobuf for GenesisInfo { - type Error = GenesisInfoError; - type Raw = raw::GenesisInfo; - - fn try_from_raw_ref(raw: &Self::Raw) -> Result { - let raw::GenesisInfo { - rollup_id, - sequencer_genesis_block_height, - celestia_block_variance, - } = raw; - let Some(rollup_id) = rollup_id else { - return Err(Self::Error::no_rollup_id()); - }; - let rollup_id = RollupId::try_from_raw_ref(rollup_id) - .map_err(Self::Error::incorrect_rollup_id_length)?; - - Ok(Self { - rollup_id, - sequencer_genesis_block_height: (*sequencer_genesis_block_height).into(), - celestia_block_variance: *celestia_block_variance, - }) - } - - fn to_raw(&self) -> Self::Raw { - let Self { - rollup_id, - sequencer_genesis_block_height, - celestia_block_variance, - } = self; - - let sequencer_genesis_block_height: u32 = - (*sequencer_genesis_block_height).value().try_into().expect( - "block height overflow, this should not happen since tendermint heights are i64 \ - under the hood", - ); - Self::Raw { - rollup_id: Some(rollup_id.to_raw()), - sequencer_genesis_block_height, - celestia_block_variance: *celestia_block_variance, - } - } -} - -/// An error when transforming a [`raw::Block`] into a [`Block`]. -#[derive(Debug, thiserror::Error)] -#[error(transparent)] -pub struct BlockError(BlockErrorKind); - -impl BlockError { - fn field_not_set(field: &'static str) -> Self { - Self(BlockErrorKind::FieldNotSet(field)) - } -} - -#[derive(Debug, thiserror::Error)] -enum BlockErrorKind { - #[error("{0} field not set")] - FieldNotSet(&'static str), -} - -/// An Astria execution block on a rollup. -/// -/// Contains information about the block number, its hash, -/// its parent block's hash, and timestamp. -/// -/// Usually constructed its [`Protobuf`] implementation from a -/// [`raw::Block`]. -#[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] -#[cfg_attr( - feature = "serde", - serde(into = "crate::generated::astria::execution::v1::Block") -)] -pub struct Block { - /// The block number - number: u32, - /// The hash of the block - hash: Bytes, - /// The hash of the parent block - parent_block_hash: Bytes, - /// Timestamp on the block, standardized to google protobuf standard. - timestamp: Timestamp, -} - -impl Block { - #[must_use] - pub fn number(&self) -> u32 { - self.number - } - - #[must_use] - pub fn hash(&self) -> &Bytes { - &self.hash - } - - #[must_use] - pub fn parent_block_hash(&self) -> &Bytes { - &self.parent_block_hash - } - - #[must_use] - pub fn timestamp(&self) -> Timestamp { - // prost_types::Timestamp is a (i64, i32) tuple, so this is - // effectively just a copy - self.timestamp.clone() - } -} - -impl From for raw::Block { - fn from(value: Block) -> Self { - value.to_raw() - } -} - -impl Protobuf for Block { - type Error = BlockError; - type Raw = raw::Block; - - fn try_from_raw_ref(raw: &Self::Raw) -> Result { - let raw::Block { - number, - hash, - parent_block_hash, - timestamp, - } = raw; - // Cloning timestamp is effectively a copy because timestamp is just a (i32, i64) tuple - let timestamp = timestamp - .clone() - .ok_or(Self::Error::field_not_set(".timestamp"))?; - - Ok(Self { - number: *number, - hash: hash.clone(), - parent_block_hash: parent_block_hash.clone(), - timestamp, - }) - } - - fn to_raw(&self) -> Self::Raw { - let Self { - number, - hash, - parent_block_hash, - timestamp, - } = self; - Self::Raw { - number: *number, - hash: hash.clone(), - parent_block_hash: parent_block_hash.clone(), - // Cloning timestamp is effectively a copy because timestamp is just a (i32, i64) - // tuple - timestamp: Some(timestamp.clone()), - } - } -} - -#[derive(Debug, thiserror::Error)] -#[error(transparent)] -pub struct CommitmentStateError(CommitmentStateErrorKind); - -impl CommitmentStateError { - fn field_not_set(field: &'static str) -> Self { - Self(CommitmentStateErrorKind::FieldNotSet(field)) - } - - fn firm(source: BlockError) -> Self { - Self(CommitmentStateErrorKind::Firm(source)) - } - - fn soft(source: BlockError) -> Self { - Self(CommitmentStateErrorKind::Soft(source)) - } - - fn firm_exceeds_soft(source: FirmExceedsSoft) -> Self { - Self(CommitmentStateErrorKind::FirmExceedsSoft(source)) - } -} - -#[derive(Debug, thiserror::Error)] -enum CommitmentStateErrorKind { - #[error("{0} field not set")] - FieldNotSet(&'static str), - #[error(".firm field did not contain a valid block")] - Firm(#[source] BlockError), - #[error(".soft field did not contain a valid block")] - Soft(#[source] BlockError), - #[error(transparent)] - FirmExceedsSoft(FirmExceedsSoft), -} - -#[derive(Debug, thiserror::Error)] -#[error("firm commitment at `{firm} exceeds soft commitment at `{soft}")] -pub struct FirmExceedsSoft { - firm: u32, - soft: u32, -} - -pub struct NoFirm; -pub struct NoSoft; -pub struct NoBaseCelestiaHeight; -pub struct WithFirm(Block); -pub struct WithSoft(Block); -pub struct WithCelestiaBaseHeight(u64); -#[derive(Default)] -pub struct CommitmentStateBuilder< - TFirm = NoFirm, - TSoft = NoSoft, - TBaseCelestiaHeight = NoBaseCelestiaHeight, -> { - firm: TFirm, - soft: TSoft, - base_celestia_height: TBaseCelestiaHeight, -} - -impl CommitmentStateBuilder { - fn new() -> Self { - Self { - firm: NoFirm, - soft: NoSoft, - base_celestia_height: NoBaseCelestiaHeight, - } - } -} - -impl CommitmentStateBuilder { - pub fn firm(self, firm: Block) -> CommitmentStateBuilder { - let Self { - soft, - base_celestia_height, - .. - } = self; - CommitmentStateBuilder { - firm: WithFirm(firm), - soft, - base_celestia_height, - } - } - - pub fn soft(self, soft: Block) -> CommitmentStateBuilder { - let Self { - firm, - base_celestia_height, - .. - } = self; - CommitmentStateBuilder { - firm, - soft: WithSoft(soft), - base_celestia_height, - } - } - - pub fn base_celestia_height( - self, - base_celestia_height: u64, - ) -> CommitmentStateBuilder { - let Self { - firm, - soft, - .. - } = self; - CommitmentStateBuilder { - firm, - soft, - base_celestia_height: WithCelestiaBaseHeight(base_celestia_height), - } - } -} - -impl CommitmentStateBuilder { - /// Finalize the commitment state. - /// - /// # Errors - /// Returns an error if the firm block exceeds the soft one. - pub fn build(self) -> Result { - let Self { - firm: WithFirm(firm), - soft: WithSoft(soft), - base_celestia_height: WithCelestiaBaseHeight(base_celestia_height), - } = self; - if firm.number() > soft.number() { - return Err(FirmExceedsSoft { - firm: firm.number(), - soft: soft.number(), - }); - } - Ok(CommitmentState { - soft, - firm, - base_celestia_height, - }) - } -} - -/// Information about the [`Block`] at each sequencer commitment level. -/// -/// A commitment state is valid if: -/// - Block numbers are such that soft >= firm (upheld by this type). -/// - No blocks ever decrease in block number. -/// - The chain defined by soft is the head of the canonical chain the firm block must belong to. -#[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize))] -#[cfg_attr( - feature = "serde", - serde(into = "crate::generated::astria::execution::v1::CommitmentState") -)] -pub struct CommitmentState { - /// Soft commitment is the rollup block matching latest sequencer block. - soft: Block, - /// Firm commitment is achieved when data has been seen in DA. - firm: Block, - /// The base height of celestia from which to search for blocks after this - /// commitment state. - base_celestia_height: u64, -} - -impl CommitmentState { - #[must_use = "a commitment state must be built to be useful"] - pub fn builder() -> CommitmentStateBuilder { - CommitmentStateBuilder::new() - } - - #[must_use] - pub fn firm(&self) -> &Block { - &self.firm - } - - #[must_use] - pub fn soft(&self) -> &Block { - &self.soft - } - - pub fn base_celestia_height(&self) -> u64 { - self.base_celestia_height - } -} - -impl From for raw::CommitmentState { - fn from(value: CommitmentState) -> Self { - value.to_raw() - } -} - -impl Protobuf for CommitmentState { - type Error = CommitmentStateError; - type Raw = raw::CommitmentState; - - fn try_from_raw_ref(raw: &Self::Raw) -> Result { - let Self::Raw { - soft, - firm, - base_celestia_height, - } = raw; - let soft = 'soft: { - let Some(soft) = soft else { - break 'soft Err(Self::Error::field_not_set(".soft")); - }; - Block::try_from_raw_ref(soft).map_err(Self::Error::soft) - }?; - let firm = 'firm: { - let Some(firm) = firm else { - break 'firm Err(Self::Error::field_not_set(".firm")); - }; - Block::try_from_raw_ref(firm).map_err(Self::Error::firm) - }?; - - Self::builder() - .firm(firm) - .soft(soft) - .base_celestia_height(*base_celestia_height) - .build() - .map_err(Self::Error::firm_exceeds_soft) - } - - fn to_raw(&self) -> Self::Raw { - let Self { - soft, - firm, - base_celestia_height, - } = self; - let soft = soft.to_raw(); - let firm = firm.to_raw(); - let base_celestia_height = *base_celestia_height; - Self::Raw { - soft: Some(soft), - firm: Some(firm), - base_celestia_height, - } - } -} diff --git a/crates/astria-core/src/execution/v2/mod.rs b/crates/astria-core/src/execution/v2/mod.rs index baa694b834..7172644862 100644 --- a/crates/astria-core/src/execution/v2/mod.rs +++ b/crates/astria-core/src/execution/v2/mod.rs @@ -1,6 +1,5 @@ use std::num::NonZeroU64; -use bytes::Bytes; use pbjson_types::Timestamp; use crate::{ @@ -12,39 +11,148 @@ use crate::{ Protobuf, }; -// An error when transforming a [`raw::GenesisInfo`] into a [`GenesisInfo`]. #[derive(Debug, thiserror::Error)] #[error(transparent)] -pub struct GenesisInfoError(GenesisInfoErrorKind); +pub struct ExecutionSessionError(ExecutionSessionErrorKind); -impl GenesisInfoError { - fn incorrect_rollup_id_length(inner: IncorrectRollupIdLength) -> Self { - Self(GenesisInfoErrorKind::IncorrectRollupIdLength(inner)) +impl ExecutionSessionError { + fn execution_session_parameters(inner: ExecutionSessionParametersError) -> Self { + Self(ExecutionSessionErrorKind::InvalidExecutionSessionParameters(inner)) } - fn no_rollup_id() -> Self { - Self(GenesisInfoErrorKind::NoRollupId) + fn commitment_state(inner: CommitmentStateError) -> Self { + Self(ExecutionSessionErrorKind::InvalidCommitmentState(inner)) + } + + fn missing_execution_session_parameters() -> Self { + Self(ExecutionSessionErrorKind::MissingExecutionSessionParameters) + } + + fn missing_commitment_state() -> Self { + Self(ExecutionSessionErrorKind::MissingCommitmentState) + } +} + +#[derive(Debug, thiserror::Error)] +enum ExecutionSessionErrorKind { + #[error("invalid execution session parameters")] + InvalidExecutionSessionParameters(#[from] ExecutionSessionParametersError), + #[error("invalid commitment state")] + InvalidCommitmentState(#[from] CommitmentStateError), + #[error("execution session parameters missing")] + MissingExecutionSessionParameters, + #[error("commitment state missing")] + MissingCommitmentState, +} + +/// `ExecutionSession` contains the information needed to drive the full execution +/// of a rollup chain in the rollup. +/// +/// The execution session is only valid for the execution config params with +/// which it was created. Once all blocks within the session have been executed, +/// the execution client must request a new session. The `session_id` is used to +/// to track which session is being used. +#[derive(Debug)] +pub struct ExecutionSession { + /// An ID for the session. + session_id: String, + /// The configuration for the execution session. + execution_session_parameters: ExecutionSessionParameters, + /// The commitment state for executing client to start from. + commitment_state: CommitmentState, +} + +impl ExecutionSession { + #[must_use] + pub fn session_id(&self) -> &String { + &self.session_id + } + + #[must_use] + pub fn execution_session_parameters(&self) -> &ExecutionSessionParameters { + &self.execution_session_parameters } - fn invalid_sequencer_id(source: tendermint::Error) -> Self { - Self(GenesisInfoErrorKind::InvalidSequencerId(source)) + #[must_use] + pub fn commitment_state(&self) -> &CommitmentState { + &self.commitment_state } +} + +impl Protobuf for ExecutionSession { + type Error = ExecutionSessionError; + type Raw = raw::ExecutionSession; - fn invalid_celestia_id(source: celestia_tendermint::Error) -> Self { - Self(GenesisInfoErrorKind::InvalidCelestiaId(source)) + fn try_from_raw_ref(raw: &Self::Raw) -> Result { + let raw::ExecutionSession { + session_id, + execution_session_parameters, + commitment_state, + } = raw; + let execution_session_parameters = execution_session_parameters + .as_ref() + .ok_or_else(Self::Error::missing_execution_session_parameters)?; + let execution_session_parameters = + ExecutionSessionParameters::try_from_raw_ref(execution_session_parameters) + .map_err(Self::Error::execution_session_parameters)?; + let commitment_state = commitment_state + .as_ref() + .ok_or_else(Self::Error::missing_commitment_state)?; + let commitment_state = CommitmentState::try_from_raw_ref(commitment_state) + .map_err(Self::Error::commitment_state)?; + Ok(Self { + session_id: session_id.clone(), + execution_session_parameters, + commitment_state, + }) + } + + fn to_raw(&self) -> Self::Raw { + let Self { + session_id, + execution_session_parameters, + commitment_state, + } = self; + let execution_session_parameters = execution_session_parameters.to_raw(); + let commitment_state = commitment_state.to_raw(); + Self::Raw { + session_id: session_id.clone(), + execution_session_parameters: Some(execution_session_parameters), + commitment_state: Some(commitment_state), + } } } +// An error when transforming a [`raw::ExecutionSessionParameters`] into a +// [`ExecutionSessionParameters`]. #[derive(Debug, thiserror::Error)] -enum GenesisInfoErrorKind { +#[error(transparent)] +pub struct ExecutionSessionParametersError(ExecutionSessionParametersErrorKind); + +impl ExecutionSessionParametersError { + fn incorrect_rollup_id_length(inner: IncorrectRollupIdLength) -> Self { + Self(ExecutionSessionParametersErrorKind::IncorrectRollupIdLength(inner)) + } + + fn no_rollup_id() -> Self { + Self(ExecutionSessionParametersErrorKind::NoRollupId) + } + + fn invalid_sequencer_start_block_height(inner: tendermint::Error) -> Self { + Self(ExecutionSessionParametersErrorKind::InvalidSequencerStartBlockHeight(inner)) + } +} + +#[derive(Debug, thiserror::Error)] +enum ExecutionSessionParametersErrorKind { #[error("`rollup_id` field contained an invalid rollup ID")] IncorrectRollupIdLength(IncorrectRollupIdLength), #[error("`rollup_id` was not set")] NoRollupId, - #[error("field `sequencer_chain_id` was invalid")] - InvalidSequencerId(#[source] tendermint::Error), - #[error("field `celestia_chain_id` was invalid")] - InvalidCelestiaId(#[source] celestia_tendermint::Error), + #[error( + "`tendermint::block::Height` could not be constructed from `sequencer_start_block_height`" + )] + InvalidSequencerStartBlockHeight(#[from] tendermint::Error), } /// Genesis Info required from a rollup to start an execution client. @@ -52,164 +160,184 @@ enum GenesisInfoErrorKind { /// Contains information about the rollup id, and base heights for both sequencer & celestia. /// /// Usually constructed its [`Protobuf`] implementation from a -/// [`raw::GenesisInfo`]. +/// [`raw::ExecutionSessionParameters`]. #[derive(Clone, Debug)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr( feature = "serde", - serde(into = "crate::generated::astria::execution::v2::GenesisInfo") + serde(into = "crate::generated::astria::execution::v2::ExecutionSessionParameters") )] -pub struct GenesisInfo { - /// The rollup id which is used to identify the rollup txs. +pub struct ExecutionSessionParameters { + // The rollup_id is the unique identifier for the rollup chain. rollup_id: RollupId, - /// The Sequencer block height which contains the first block of the rollup. - sequencer_start_height: tendermint::block::Height, - /// The allowed variance in the block height of celestia when looking for sequencer blocks. - celestia_block_variance: u64, - /// The rollup block number to map to the sequencer start block height. + // The first rollup block number to be executed. This is mapped to + // `sequencer_first_block_height`. The minimum first block number is 1, since 0 represents + // the genesis block. Implementors should reject a value of 0. + // + // Servers implementing this API should reject execution of blocks below this + // value with an OUT_OF_RANGE error code. rollup_start_block_number: u64, - /// The rollup block number to restart/halt at after executing. - rollup_stop_block_number: Option, - /// The chain ID of the sequencer network. - sequencer_chain_id: tendermint::chain::Id, - /// The chain ID of the celestia network. - celestia_chain_id: celestia_tendermint::chain::Id, - /// Whether the conductor should halt at the stop height instead of restarting. - halt_at_rollup_stop_number: bool, + // The final rollup block number to execute as part of a session. + // + // If not set or set to 0, the execution session does not have an upper bound. + // + // Servers implementing this API should reject execution of blocks past this + // value with an OUT_OF_RANGE error code. + rollup_end_block_number: Option, + // The ID of the Astria Sequencer network to retrieve Sequencer blocks from. + // Conductor implementations should verify that the Sequencer network they are + // connected to have this chain ID (if fetching soft Sequencer blocks), and verify + // that the Sequencer metadata blobs retrieved from Celestia contain this chain + // ID (if extracting firm Sequencer blocks from Celestia blobs). + sequencer_chain_id: String, + // The first block height on the sequencer chain to use for rollup transactions. + // This is mapped to `rollup_start_block_number`. + sequencer_start_block_height: tendermint::block::Height, + // The ID of the Celestia network to retrieve blobs from. + // Conductor implementations should verify that the Celestia network they are + // connected to have this chain ID (if extracting firm Sequencer blocks from + // Celestia blobs). + celestia_chain_id: String, + // The maximum number of Celestia blocks which can be read above + // `CommitmentState.lowest_celestia_search_height` in search of the next firm + // block. + celestia_search_height_max_look_ahead: u64, } -impl GenesisInfo { +impl ExecutionSessionParameters { #[must_use] - pub fn rollup_id(&self) -> RollupId { - self.rollup_id + pub fn new( + rollup_id: RollupId, + rollup_start_block_number: u64, + rollup_end_block_number: u64, + sequencer_chain_id: String, + sequencer_start_block_height: tendermint::block::Height, + celestia_chain_id: String, + celestia_search_height_max_look_ahead: u64, + ) -> Self { + Self { + rollup_id, + rollup_start_block_number, + rollup_end_block_number: NonZeroU64::new(rollup_end_block_number), + sequencer_chain_id, + sequencer_start_block_height, + celestia_chain_id, + celestia_search_height_max_look_ahead, + } } #[must_use] - pub fn sequencer_start_height(&self) -> u64 { - self.sequencer_start_height.into() + pub fn rollup_id(&self) -> RollupId { + self.rollup_id } #[must_use] - pub fn celestia_block_variance(&self) -> u64 { - self.celestia_block_variance + pub fn rollup_start_block_number(&self) -> u64 { + self.rollup_start_block_number } #[must_use] - pub fn sequencer_chain_id(&self) -> &str { - self.sequencer_chain_id.as_str() + pub fn rollup_end_block_number(&self) -> Option { + self.rollup_end_block_number } #[must_use] - pub fn celestia_chain_id(&self) -> &str { - self.celestia_chain_id.as_str() + pub fn sequencer_start_block_height(&self) -> u64 { + self.sequencer_start_block_height.into() } #[must_use] - pub fn rollup_start_block_number(&self) -> u64 { - self.rollup_start_block_number + pub fn sequencer_chain_id(&self) -> &String { + &self.sequencer_chain_id } #[must_use] - pub fn rollup_stop_block_number(&self) -> Option { - self.rollup_stop_block_number + pub fn celestia_chain_id(&self) -> &String { + &self.celestia_chain_id } #[must_use] - pub fn halt_at_rollup_stop_number(&self) -> bool { - self.halt_at_rollup_stop_number + pub fn celestia_search_height_max_look_ahead(&self) -> u64 { + self.celestia_search_height_max_look_ahead } } -impl From for raw::GenesisInfo { - fn from(value: GenesisInfo) -> Self { +impl From for raw::ExecutionSessionParameters { + fn from(value: ExecutionSessionParameters) -> Self { value.to_raw() } } -impl Protobuf for GenesisInfo { - type Error = GenesisInfoError; - type Raw = raw::GenesisInfo; +impl Protobuf for ExecutionSessionParameters { + type Error = ExecutionSessionParametersError; + type Raw = raw::ExecutionSessionParameters; fn try_from_raw_ref(raw: &Self::Raw) -> Result { - let raw::GenesisInfo { + let raw::ExecutionSessionParameters { rollup_id, - sequencer_start_height, - celestia_block_variance, rollup_start_block_number, - rollup_stop_block_number, + rollup_end_block_number, sequencer_chain_id, + sequencer_start_block_height, celestia_chain_id, - halt_at_rollup_stop_number, + celestia_search_height_max_look_ahead, } = raw; let Some(rollup_id) = rollup_id else { return Err(Self::Error::no_rollup_id()); }; let rollup_id = RollupId::try_from_raw_ref(rollup_id) .map_err(Self::Error::incorrect_rollup_id_length)?; - let sequencer_chain_id = sequencer_chain_id - .clone() - .try_into() - .map_err(Self::Error::invalid_sequencer_id)?; - let celestia_chain_id = celestia_chain_id - .clone() - .try_into() - .map_err(Self::Error::invalid_celestia_id)?; + let sequencer_start_block_height = + tendermint::block::Height::try_from(*sequencer_start_block_height) + .map_err(Self::Error::invalid_sequencer_start_block_height)?; Ok(Self { rollup_id, - sequencer_start_height: (*sequencer_start_height).into(), - celestia_block_variance: *celestia_block_variance, rollup_start_block_number: *rollup_start_block_number, - rollup_stop_block_number: NonZeroU64::new(*rollup_stop_block_number), - sequencer_chain_id, - celestia_chain_id, - halt_at_rollup_stop_number: *halt_at_rollup_stop_number, + rollup_end_block_number: NonZeroU64::new(*rollup_end_block_number), + sequencer_chain_id: sequencer_chain_id.clone(), + sequencer_start_block_height, + celestia_chain_id: celestia_chain_id.clone(), + celestia_search_height_max_look_ahead: *celestia_search_height_max_look_ahead, }) } fn to_raw(&self) -> Self::Raw { let Self { rollup_id, - sequencer_start_height, - celestia_block_variance, rollup_start_block_number, - rollup_stop_block_number, + rollup_end_block_number, sequencer_chain_id, + sequencer_start_block_height, celestia_chain_id, - halt_at_rollup_stop_number, + celestia_search_height_max_look_ahead, } = self; - let sequencer_start_height: u32 = (*sequencer_start_height).value().try_into().expect( - "block height overflow, this should not happen since tendermint heights are i64 under \ - the hood", - ); - Self::Raw { rollup_id: Some(rollup_id.to_raw()), - sequencer_start_height, - celestia_block_variance: *celestia_block_variance, rollup_start_block_number: *rollup_start_block_number, - rollup_stop_block_number: rollup_stop_block_number.map(NonZeroU64::get).unwrap_or(0), - sequencer_chain_id: sequencer_chain_id.to_string(), - celestia_chain_id: celestia_chain_id.to_string(), - halt_at_rollup_stop_number: *halt_at_rollup_stop_number, + rollup_end_block_number: rollup_end_block_number.map(NonZeroU64::get).unwrap_or(0), + sequencer_chain_id: sequencer_chain_id.clone(), + sequencer_start_block_height: sequencer_start_block_height.value(), + celestia_chain_id: celestia_chain_id.clone(), + celestia_search_height_max_look_ahead: *celestia_search_height_max_look_ahead, } } } -/// An error when transforming a [`raw::Block`] into a [`Block`]. +/// An error when transforming a [`raw::ExecutedBlockMetadata`] into a [`ExecutedBlockMetadata`]. #[derive(Debug, thiserror::Error)] #[error(transparent)] -pub struct BlockError(BlockErrorKind); +pub struct ExecutedBlockMetadataError(ExecutedBlockMetadataErrorKind); -impl BlockError { +impl ExecutedBlockMetadataError { fn field_not_set(field: &'static str) -> Self { - Self(BlockErrorKind::FieldNotSet(field)) + Self(ExecutedBlockMetadataErrorKind::FieldNotSet(field)) } } #[derive(Debug, thiserror::Error)] -enum BlockErrorKind { +enum ExecutedBlockMetadataErrorKind { #[error("{0} field not set")] FieldNotSet(&'static str), } @@ -220,38 +348,39 @@ enum BlockErrorKind { /// its parent block's hash, and timestamp. /// /// Usually constructed its [`Protobuf`] implementation from a -/// [`raw::Block`]. +/// [`raw::ExecutedBlockMetadata`]. #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] #[cfg_attr( feature = "serde", - serde(into = "crate::generated::astria::execution::v2::Block") + serde(into = "crate::generated::astria::execution::v2::ExecutedBlockMetadata") )] -pub struct Block { +pub struct ExecutedBlockMetadata { /// The block number - number: u32, + number: u64, /// The hash of the block - hash: Bytes, + hash: String, /// The hash of the parent block - parent_block_hash: Bytes, - /// Timestamp on the block, standardized to google protobuf standard. + parent_hash: String, + /// Timestamp of the block, taken from the sequencer block that this rollup block + /// was constructed from. timestamp: Timestamp, } -impl Block { +impl ExecutedBlockMetadata { #[must_use] - pub fn number(&self) -> u32 { + pub fn number(&self) -> u64 { self.number } #[must_use] - pub fn hash(&self) -> &Bytes { + pub fn hash(&self) -> &str { &self.hash } #[must_use] - pub fn parent_block_hash(&self) -> &Bytes { - &self.parent_block_hash + pub fn parent_hash(&self) -> &str { + &self.parent_hash } #[must_use] @@ -262,21 +391,21 @@ impl Block { } } -impl From for raw::Block { - fn from(value: Block) -> Self { +impl From for raw::ExecutedBlockMetadata { + fn from(value: ExecutedBlockMetadata) -> Self { value.to_raw() } } -impl Protobuf for Block { - type Error = BlockError; - type Raw = raw::Block; +impl Protobuf for ExecutedBlockMetadata { + type Error = ExecutedBlockMetadataError; + type Raw = raw::ExecutedBlockMetadata; fn try_from_raw_ref(raw: &Self::Raw) -> Result { - let raw::Block { + let raw::ExecutedBlockMetadata { number, hash, - parent_block_hash, + parent_hash, timestamp, } = raw; // Cloning timestamp is effectively a copy because timestamp is just a (i32, i64) tuple @@ -287,7 +416,7 @@ impl Protobuf for Block { Ok(Self { number: *number, hash: hash.clone(), - parent_block_hash: parent_block_hash.clone(), + parent_hash: parent_hash.clone(), timestamp, }) } @@ -296,13 +425,13 @@ impl Protobuf for Block { let Self { number, hash, - parent_block_hash, + parent_hash, timestamp, } = self; Self::Raw { number: *number, hash: hash.clone(), - parent_block_hash: parent_block_hash.clone(), + parent_hash: parent_hash.clone(), // Cloning timestamp is effectively a copy because timestamp is just a (i32, i64) // tuple timestamp: Some(timestamp.clone()), @@ -319,11 +448,11 @@ impl CommitmentStateError { Self(CommitmentStateErrorKind::FieldNotSet(field)) } - fn firm(source: BlockError) -> Self { + fn firm(source: ExecutedBlockMetadataError) -> Self { Self(CommitmentStateErrorKind::Firm(source)) } - fn soft(source: BlockError) -> Self { + fn soft(source: ExecutedBlockMetadataError) -> Self { Self(CommitmentStateErrorKind::Soft(source)) } @@ -336,10 +465,10 @@ impl CommitmentStateError { enum CommitmentStateErrorKind { #[error("{0} field not set")] FieldNotSet(&'static str), - #[error(".firm field did not contain a valid block")] - Firm(#[source] BlockError), - #[error(".soft field did not contain a valid block")] - Soft(#[source] BlockError), + #[error(".firm field did not contain valid executed block metadata")] + Firm(#[source] ExecutedBlockMetadataError), + #[error(".soft field did not contain valid executed block metadata")] + Soft(#[source] ExecutedBlockMetadataError), #[error(transparent)] FirmExceedsSoft(FirmExceedsSoft), } @@ -347,110 +476,120 @@ enum CommitmentStateErrorKind { #[derive(Debug, thiserror::Error)] #[error("firm commitment at `{firm} exceeds soft commitment at `{soft}")] pub struct FirmExceedsSoft { - firm: u32, - soft: u32, + firm: u64, + soft: u64, } pub struct NoFirm; pub struct NoSoft; pub struct NoBaseCelestiaHeight; -pub struct WithFirm(Block); -pub struct WithSoft(Block); -pub struct WithCelestiaBaseHeight(u64); +pub struct WithFirm(ExecutedBlockMetadata); +pub struct WithSoft(ExecutedBlockMetadata); +pub struct WithLowestCelestiaSearchHeight(u64); #[derive(Default)] pub struct CommitmentStateBuilder< TFirm = NoFirm, TSoft = NoSoft, TBaseCelestiaHeight = NoBaseCelestiaHeight, > { - firm: TFirm, - soft: TSoft, - base_celestia_height: TBaseCelestiaHeight, + firm_executed_block_metadata: TFirm, + soft_executed_block_metadata: TSoft, + lowest_celestia_search_height: TBaseCelestiaHeight, } impl CommitmentStateBuilder { fn new() -> Self { Self { - firm: NoFirm, - soft: NoSoft, - base_celestia_height: NoBaseCelestiaHeight, + firm_executed_block_metadata: NoFirm, + soft_executed_block_metadata: NoSoft, + lowest_celestia_search_height: NoBaseCelestiaHeight, } } } impl CommitmentStateBuilder { - pub fn firm(self, firm: Block) -> CommitmentStateBuilder { + pub fn firm_executed_block_metadata( + self, + firm_executed_block_metadata: ExecutedBlockMetadata, + ) -> CommitmentStateBuilder { let Self { - soft, - base_celestia_height, + soft_executed_block_metadata, + lowest_celestia_search_height, .. } = self; CommitmentStateBuilder { - firm: WithFirm(firm), - soft, - base_celestia_height, + firm_executed_block_metadata: WithFirm(firm_executed_block_metadata), + soft_executed_block_metadata, + lowest_celestia_search_height, } } - pub fn soft(self, soft: Block) -> CommitmentStateBuilder { + pub fn soft_executed_block_metadata( + self, + soft_executed_block_metadata: ExecutedBlockMetadata, + ) -> CommitmentStateBuilder { let Self { - firm, - base_celestia_height, + firm_executed_block_metadata, + lowest_celestia_search_height, .. } = self; CommitmentStateBuilder { - firm, - soft: WithSoft(soft), - base_celestia_height, + firm_executed_block_metadata, + soft_executed_block_metadata: WithSoft(soft_executed_block_metadata), + lowest_celestia_search_height, } } - pub fn base_celestia_height( + pub fn lowest_celestia_search_height( self, - base_celestia_height: u64, - ) -> CommitmentStateBuilder { + lowest_celestia_search_height: u64, + ) -> CommitmentStateBuilder { let Self { - firm, - soft, + firm_executed_block_metadata, + soft_executed_block_metadata, .. } = self; CommitmentStateBuilder { - firm, - soft, - base_celestia_height: WithCelestiaBaseHeight(base_celestia_height), + firm_executed_block_metadata, + soft_executed_block_metadata, + lowest_celestia_search_height: WithLowestCelestiaSearchHeight( + lowest_celestia_search_height, + ), } } } -impl CommitmentStateBuilder { +impl CommitmentStateBuilder { /// Finalize the commitment state. /// /// # Errors /// Returns an error if the firm block exceeds the soft one. pub fn build(self) -> Result { let Self { - firm: WithFirm(firm), - soft: WithSoft(soft), - base_celestia_height: WithCelestiaBaseHeight(base_celestia_height), + firm_executed_block_metadata: WithFirm(firm_executed_block_metadata), + soft_executed_block_metadata: WithSoft(soft_executed_block_metadata), + lowest_celestia_search_height: + WithLowestCelestiaSearchHeight(lowest_celestia_search_height), } = self; - if firm.number() > soft.number() { + if firm_executed_block_metadata.number() > soft_executed_block_metadata.number() { return Err(FirmExceedsSoft { - firm: firm.number(), - soft: soft.number(), + firm: firm_executed_block_metadata.number(), + soft: soft_executed_block_metadata.number(), }); } Ok(CommitmentState { - soft, - firm, - base_celestia_height, + soft_executed_block_metadata, + firm_executed_block_metadata, + lowest_celestia_search_height, }) } } -/// Information about the [`Block`] at each sequencer commitment level. +/// The `CommitmentState` holds the block at each stage of sequencer commitment +/// level /// -/// A commitment state is valid if: -/// - Block numbers are such that soft >= firm (upheld by this type). +/// A Valid `CommitmentState`: +/// - Block numbers are such that soft >= firm. /// - No blocks ever decrease in block number. /// - The chain defined by soft is the head of the canonical chain the firm block must belong to. #[derive(Clone, Debug, PartialEq)] @@ -460,13 +599,16 @@ impl CommitmentStateBuilder { serde(into = "crate::generated::astria::execution::v2::CommitmentState") )] pub struct CommitmentState { - /// Soft commitment is the rollup block matching latest sequencer block. - soft: Block, - /// Firm commitment is achieved when data has been seen in DA. - firm: Block, - /// The base height of celestia from which to search for blocks after this - /// commitment state. - base_celestia_height: u64, + /// Soft committed block metadata derived directly from an Astria sequencer block. + soft_executed_block_metadata: ExecutedBlockMetadata, + /// Firm committed block metadata derived from a Sequencer block that has been + /// written to the data availability layer (Celestia). + firm_executed_block_metadata: ExecutedBlockMetadata, + /// The lowest Celestia height that will be searched for the next firm block. + /// This information is stored as part of `CommitmentState` so that it will be + /// routinely updated as new firm blocks are received, and so that the execution + /// client will not need to search from Celestia genesis. + lowest_celestia_search_height: u64, } impl CommitmentState { @@ -476,17 +618,18 @@ impl CommitmentState { } #[must_use] - pub fn firm(&self) -> &Block { - &self.firm + pub fn firm(&self) -> &ExecutedBlockMetadata { + &self.firm_executed_block_metadata } #[must_use] - pub fn soft(&self) -> &Block { - &self.soft + pub fn soft(&self) -> &ExecutedBlockMetadata { + &self.soft_executed_block_metadata } - pub fn base_celestia_height(&self) -> u64 { - self.base_celestia_height + #[must_use] + pub fn lowest_celestia_search_height(&self) -> u64 { + self.lowest_celestia_search_height } } @@ -502,44 +645,44 @@ impl Protobuf for CommitmentState { fn try_from_raw_ref(raw: &Self::Raw) -> Result { let Self::Raw { - soft, - firm, - base_celestia_height, + soft_executed_block_metadata, + firm_executed_block_metadata, + lowest_celestia_search_height, } = raw; - let soft = 'soft: { - let Some(soft) = soft else { + let soft_executed_block_metadata = 'soft: { + let Some(soft) = soft_executed_block_metadata else { break 'soft Err(Self::Error::field_not_set(".soft")); }; - Block::try_from_raw_ref(soft).map_err(Self::Error::soft) + ExecutedBlockMetadata::try_from_raw_ref(soft).map_err(Self::Error::soft) }?; - let firm = 'firm: { - let Some(firm) = firm else { + let firm_executed_block_metadata = 'firm: { + let Some(firm) = firm_executed_block_metadata else { break 'firm Err(Self::Error::field_not_set(".firm")); }; - Block::try_from_raw_ref(firm).map_err(Self::Error::firm) + ExecutedBlockMetadata::try_from_raw_ref(firm).map_err(Self::Error::firm) }?; Self::builder() - .firm(firm) - .soft(soft) - .base_celestia_height(*base_celestia_height) + .firm_executed_block_metadata(firm_executed_block_metadata) + .soft_executed_block_metadata(soft_executed_block_metadata) + .lowest_celestia_search_height(*lowest_celestia_search_height) .build() .map_err(Self::Error::firm_exceeds_soft) } fn to_raw(&self) -> Self::Raw { let Self { - soft, - firm, - base_celestia_height, + soft_executed_block_metadata, + firm_executed_block_metadata, + lowest_celestia_search_height, } = self; - let soft = soft.to_raw(); - let firm = firm.to_raw(); - let base_celestia_height = *base_celestia_height; + let soft_executed_block_metadata = soft_executed_block_metadata.to_raw(); + let firm_executed_block_metadata = firm_executed_block_metadata.to_raw(); + let lowest_celestia_search_height = *lowest_celestia_search_height; Self::Raw { - soft: Some(soft), - firm: Some(firm), - base_celestia_height, + soft_executed_block_metadata: Some(soft_executed_block_metadata), + firm_executed_block_metadata: Some(firm_executed_block_metadata), + lowest_celestia_search_height, } } } diff --git a/crates/astria-core/src/generated/astria.bundle.v1alpha1.rs b/crates/astria-core/src/generated/astria.bundle.v1alpha1.rs deleted file mode 100644 index 684ba5f9fa..0000000000 --- a/crates/astria-core/src/generated/astria.bundle.v1alpha1.rs +++ /dev/null @@ -1,762 +0,0 @@ -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct GetBundleStreamRequest {} -impl ::prost::Name for GetBundleStreamRequest { - const NAME: &'static str = "GetBundleStreamRequest"; - const PACKAGE: &'static str = "astria.bundle.v1alpha1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria.bundle.v1alpha1.{}", Self::NAME) - } -} -/// Information for the bundle submitter to know how to submit the bundle. -/// The fee and base_sequencer_block_hash are not necessarily strictly necessary -/// it allows for the case where the server doesn't always send the highest fee -/// bundles after the previous but could just stream any confirmed bundles. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct Bundle { - /// The fee that can be expected to be received for submitting this bundle. - /// This allows the bundle producer to stream any confirmed bundles they would be ok - /// with submitting. Used to avoid race conditions in received bundle packets. Could - /// also be used by a bundle submitter to allow multiple entities to submit bundles. - #[prost(uint64, tag = "1")] - pub fee: u64, - /// The byte list of transactions to be included. - #[prost(bytes = "bytes", repeated, tag = "2")] - pub transactions: ::prost::alloc::vec::Vec<::prost::bytes::Bytes>, - /// The base_sequencer_block_hash is the hash from the base block this bundle - /// is based on. This is used to verify that the bundle is based on the correct - /// Sequencer block. - #[prost(bytes = "bytes", tag = "3")] - pub base_sequencer_block_hash: ::prost::bytes::Bytes, - /// The hash of previous rollup block, on top of which the bundle will be executed as ToB. - #[prost(bytes = "bytes", tag = "4")] - pub prev_rollup_block_hash: ::prost::bytes::Bytes, -} -impl ::prost::Name for Bundle { - const NAME: &'static str = "Bundle"; - const PACKAGE: &'static str = "astria.bundle.v1alpha1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria.bundle.v1alpha1.{}", Self::NAME) - } -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct GetBundleStreamResponse { - #[prost(message, optional, tag = "1")] - pub bundle: ::core::option::Option, -} -impl ::prost::Name for GetBundleStreamResponse { - const NAME: &'static str = "GetBundleStreamResponse"; - const PACKAGE: &'static str = "astria.bundle.v1alpha1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria.bundle.v1alpha1.{}", Self::NAME) - } -} -/// Generated client implementations. -#[cfg(feature = "client")] -pub mod bundle_service_client { - #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] - use tonic::codegen::*; - use tonic::codegen::http::Uri; - #[derive(Debug, Clone)] - pub struct BundleServiceClient { - inner: tonic::client::Grpc, - } - impl BundleServiceClient { - /// Attempt to create a new client by connecting to a given endpoint. - pub async fn connect(dst: D) -> Result - where - D: TryInto, - D::Error: Into, - { - let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; - Ok(Self::new(conn)) - } - } - impl BundleServiceClient - where - T: tonic::client::GrpcService, - T::Error: Into, - T::ResponseBody: Body + Send + 'static, - ::Error: Into + Send, - { - pub fn new(inner: T) -> Self { - let inner = tonic::client::Grpc::new(inner); - Self { inner } - } - pub fn with_origin(inner: T, origin: Uri) -> Self { - let inner = tonic::client::Grpc::with_origin(inner, origin); - Self { inner } - } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> BundleServiceClient> - where - F: tonic::service::Interceptor, - T::ResponseBody: Default, - T: tonic::codegen::Service< - http::Request, - Response = http::Response< - >::ResponseBody, - >, - >, - , - >>::Error: Into + Send + Sync, - { - BundleServiceClient::new(InterceptedService::new(inner, interceptor)) - } - /// Compress requests with the given encoding. - /// - /// This requires the server to support it otherwise it might respond with an - /// error. - #[must_use] - pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.inner = self.inner.send_compressed(encoding); - self - } - /// Enable decompressing responses. - #[must_use] - pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.inner = self.inner.accept_compressed(encoding); - self - } - /// Limits the maximum size of a decoded message. - /// - /// Default: `4MB` - #[must_use] - pub fn max_decoding_message_size(mut self, limit: usize) -> Self { - self.inner = self.inner.max_decoding_message_size(limit); - self - } - /// Limits the maximum size of an encoded message. - /// - /// Default: `usize::MAX` - #[must_use] - pub fn max_encoding_message_size(mut self, limit: usize) -> Self { - self.inner = self.inner.max_encoding_message_size(limit); - self - } - /// A bundle submitter requests bundles given a new optimistic Sequencer block, - /// and receives a stream of potential bundles for submission, until either a timeout - /// or the connection is closed by the client. - pub async fn get_bundle_stream( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response>, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/astria.bundle.v1alpha1.BundleService/GetBundleStream", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert( - GrpcMethod::new( - "astria.bundle.v1alpha1.BundleService", - "GetBundleStream", - ), - ); - self.inner.server_streaming(req, path, codec).await - } - } -} -/// Generated server implementations. -#[cfg(feature = "server")] -pub mod bundle_service_server { - #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] - use tonic::codegen::*; - /// Generated trait containing gRPC methods that should be implemented for use with BundleServiceServer. - #[async_trait] - pub trait BundleService: Send + Sync + 'static { - /// Server streaming response type for the GetBundleStream method. - type GetBundleStreamStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result, - > - + Send - + 'static; - /// A bundle submitter requests bundles given a new optimistic Sequencer block, - /// and receives a stream of potential bundles for submission, until either a timeout - /// or the connection is closed by the client. - async fn get_bundle_stream( - self: std::sync::Arc, - request: tonic::Request, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; - } - #[derive(Debug)] - pub struct BundleServiceServer { - inner: _Inner, - accept_compression_encodings: EnabledCompressionEncodings, - send_compression_encodings: EnabledCompressionEncodings, - max_decoding_message_size: Option, - max_encoding_message_size: Option, - } - struct _Inner(Arc); - impl BundleServiceServer { - pub fn new(inner: T) -> Self { - Self::from_arc(Arc::new(inner)) - } - pub fn from_arc(inner: Arc) -> Self { - let inner = _Inner(inner); - Self { - inner, - accept_compression_encodings: Default::default(), - send_compression_encodings: Default::default(), - max_decoding_message_size: None, - max_encoding_message_size: None, - } - } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> InterceptedService - where - F: tonic::service::Interceptor, - { - InterceptedService::new(Self::new(inner), interceptor) - } - /// Enable decompressing requests with the given encoding. - #[must_use] - pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.accept_compression_encodings.enable(encoding); - self - } - /// Compress responses with the given encoding, if the client supports it. - #[must_use] - pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.send_compression_encodings.enable(encoding); - self - } - /// Limits the maximum size of a decoded message. - /// - /// Default: `4MB` - #[must_use] - pub fn max_decoding_message_size(mut self, limit: usize) -> Self { - self.max_decoding_message_size = Some(limit); - self - } - /// Limits the maximum size of an encoded message. - /// - /// Default: `usize::MAX` - #[must_use] - pub fn max_encoding_message_size(mut self, limit: usize) -> Self { - self.max_encoding_message_size = Some(limit); - self - } - } - impl tonic::codegen::Service> for BundleServiceServer - where - T: BundleService, - B: Body + Send + 'static, - B::Error: Into + Send + 'static, - { - type Response = http::Response; - type Error = std::convert::Infallible; - type Future = BoxFuture; - fn poll_ready( - &mut self, - _cx: &mut Context<'_>, - ) -> Poll> { - Poll::Ready(Ok(())) - } - fn call(&mut self, req: http::Request) -> Self::Future { - let inner = self.inner.clone(); - match req.uri().path() { - "/astria.bundle.v1alpha1.BundleService/GetBundleStream" => { - #[allow(non_camel_case_types)] - struct GetBundleStreamSvc(pub Arc); - impl< - T: BundleService, - > tonic::server::ServerStreamingService< - super::GetBundleStreamRequest, - > for GetBundleStreamSvc { - type Response = super::GetBundleStreamResponse; - type ResponseStream = T::GetBundleStreamStream; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::get_bundle_stream(inner, request) - .await - }; - Box::pin(fut) - } - } - let accept_compression_encodings = self.accept_compression_encodings; - let send_compression_encodings = self.send_compression_encodings; - let max_decoding_message_size = self.max_decoding_message_size; - let max_encoding_message_size = self.max_encoding_message_size; - let inner = self.inner.clone(); - let fut = async move { - let inner = inner.0; - let method = GetBundleStreamSvc(inner); - let codec = tonic::codec::ProstCodec::default(); - let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); - let res = grpc.server_streaming(method, req).await; - Ok(res) - }; - Box::pin(fut) - } - _ => { - Box::pin(async move { - Ok( - http::Response::builder() - .status(200) - .header("grpc-status", "12") - .header("content-type", "application/grpc") - .body(empty_body()) - .unwrap(), - ) - }) - } - } - } - } - impl Clone for BundleServiceServer { - fn clone(&self) -> Self { - let inner = self.inner.clone(); - Self { - inner, - accept_compression_encodings: self.accept_compression_encodings, - send_compression_encodings: self.send_compression_encodings, - max_decoding_message_size: self.max_decoding_message_size, - max_encoding_message_size: self.max_encoding_message_size, - } - } - } - impl Clone for _Inner { - fn clone(&self) -> Self { - Self(Arc::clone(&self.0)) - } - } - impl std::fmt::Debug for _Inner { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.0) - } - } - impl tonic::server::NamedService for BundleServiceServer { - const NAME: &'static str = "astria.bundle.v1alpha1.BundleService"; - } -} -/// The "BaseBlock" is the information needed to simulate bundles on top of -/// a Sequencer block which may not have been committed yet. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct BaseBlock { - /// This is the block hash for the proposed block. - #[prost(bytes = "bytes", tag = "1")] - pub sequencer_block_hash: ::prost::bytes::Bytes, - /// List of transactions to include in the new block. - #[prost(message, repeated, tag = "2")] - pub transactions: ::prost::alloc::vec::Vec< - super::super::sequencerblock::v1::RollupData, - >, - /// Timestamp to be used for new block. - #[prost(message, optional, tag = "3")] - pub timestamp: ::core::option::Option<::pbjson_types::Timestamp>, -} -impl ::prost::Name for BaseBlock { - const NAME: &'static str = "BaseBlock"; - const PACKAGE: &'static str = "astria.bundle.v1alpha1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria.bundle.v1alpha1.{}", Self::NAME) - } -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ExecuteOptimisticBlockStreamRequest { - #[prost(message, optional, tag = "1")] - pub base_block: ::core::option::Option, -} -impl ::prost::Name for ExecuteOptimisticBlockStreamRequest { - const NAME: &'static str = "ExecuteOptimisticBlockStreamRequest"; - const PACKAGE: &'static str = "astria.bundle.v1alpha1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria.bundle.v1alpha1.{}", Self::NAME) - } -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ExecuteOptimisticBlockStreamResponse { - /// Metadata identifying the block resulting from executing a block. Includes number, hash, - /// parent hash and timestamp. - #[prost(message, optional, tag = "1")] - pub block: ::core::option::Option, - /// The base_sequencer_block_hash is the hash from the base sequencer block this block - /// is based on. This is used to associate an optimistic execution result with the hash - /// received once a sequencer block is committed. - #[prost(bytes = "bytes", tag = "2")] - pub base_sequencer_block_hash: ::prost::bytes::Bytes, -} -impl ::prost::Name for ExecuteOptimisticBlockStreamResponse { - const NAME: &'static str = "ExecuteOptimisticBlockStreamResponse"; - const PACKAGE: &'static str = "astria.bundle.v1alpha1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria.bundle.v1alpha1.{}", Self::NAME) - } -} -/// Generated client implementations. -#[cfg(feature = "client")] -pub mod optimistic_execution_service_client { - #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] - use tonic::codegen::*; - use tonic::codegen::http::Uri; - #[derive(Debug, Clone)] - pub struct OptimisticExecutionServiceClient { - inner: tonic::client::Grpc, - } - impl OptimisticExecutionServiceClient { - /// Attempt to create a new client by connecting to a given endpoint. - pub async fn connect(dst: D) -> Result - where - D: TryInto, - D::Error: Into, - { - let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; - Ok(Self::new(conn)) - } - } - impl OptimisticExecutionServiceClient - where - T: tonic::client::GrpcService, - T::Error: Into, - T::ResponseBody: Body + Send + 'static, - ::Error: Into + Send, - { - pub fn new(inner: T) -> Self { - let inner = tonic::client::Grpc::new(inner); - Self { inner } - } - pub fn with_origin(inner: T, origin: Uri) -> Self { - let inner = tonic::client::Grpc::with_origin(inner, origin); - Self { inner } - } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> OptimisticExecutionServiceClient> - where - F: tonic::service::Interceptor, - T::ResponseBody: Default, - T: tonic::codegen::Service< - http::Request, - Response = http::Response< - >::ResponseBody, - >, - >, - , - >>::Error: Into + Send + Sync, - { - OptimisticExecutionServiceClient::new( - InterceptedService::new(inner, interceptor), - ) - } - /// Compress requests with the given encoding. - /// - /// This requires the server to support it otherwise it might respond with an - /// error. - #[must_use] - pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.inner = self.inner.send_compressed(encoding); - self - } - /// Enable decompressing responses. - #[must_use] - pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.inner = self.inner.accept_compressed(encoding); - self - } - /// Limits the maximum size of a decoded message. - /// - /// Default: `4MB` - #[must_use] - pub fn max_decoding_message_size(mut self, limit: usize) -> Self { - self.inner = self.inner.max_decoding_message_size(limit); - self - } - /// Limits the maximum size of an encoded message. - /// - /// Default: `usize::MAX` - #[must_use] - pub fn max_encoding_message_size(mut self, limit: usize) -> Self { - self.inner = self.inner.max_encoding_message_size(limit); - self - } - /// Stream blocks from the Auctioneer to Geth for optimistic execution. Geth will stream back - /// metadata from the executed blocks. - pub async fn execute_optimistic_block_stream( - &mut self, - request: impl tonic::IntoStreamingRequest< - Message = super::ExecuteOptimisticBlockStreamRequest, - >, - ) -> std::result::Result< - tonic::Response< - tonic::codec::Streaming, - >, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/astria.bundle.v1alpha1.OptimisticExecutionService/ExecuteOptimisticBlockStream", - ); - let mut req = request.into_streaming_request(); - req.extensions_mut() - .insert( - GrpcMethod::new( - "astria.bundle.v1alpha1.OptimisticExecutionService", - "ExecuteOptimisticBlockStream", - ), - ); - self.inner.streaming(req, path, codec).await - } - } -} -/// Generated server implementations. -#[cfg(feature = "server")] -pub mod optimistic_execution_service_server { - #![allow(unused_variables, dead_code, missing_docs, clippy::let_unit_value)] - use tonic::codegen::*; - /// Generated trait containing gRPC methods that should be implemented for use with OptimisticExecutionServiceServer. - #[async_trait] - pub trait OptimisticExecutionService: Send + Sync + 'static { - /// Server streaming response type for the ExecuteOptimisticBlockStream method. - type ExecuteOptimisticBlockStreamStream: tonic::codegen::tokio_stream::Stream< - Item = std::result::Result< - super::ExecuteOptimisticBlockStreamResponse, - tonic::Status, - >, - > - + Send - + 'static; - /// Stream blocks from the Auctioneer to Geth for optimistic execution. Geth will stream back - /// metadata from the executed blocks. - async fn execute_optimistic_block_stream( - self: std::sync::Arc, - request: tonic::Request< - tonic::Streaming, - >, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - >; - } - #[derive(Debug)] - pub struct OptimisticExecutionServiceServer { - inner: _Inner, - accept_compression_encodings: EnabledCompressionEncodings, - send_compression_encodings: EnabledCompressionEncodings, - max_decoding_message_size: Option, - max_encoding_message_size: Option, - } - struct _Inner(Arc); - impl OptimisticExecutionServiceServer { - pub fn new(inner: T) -> Self { - Self::from_arc(Arc::new(inner)) - } - pub fn from_arc(inner: Arc) -> Self { - let inner = _Inner(inner); - Self { - inner, - accept_compression_encodings: Default::default(), - send_compression_encodings: Default::default(), - max_decoding_message_size: None, - max_encoding_message_size: None, - } - } - pub fn with_interceptor( - inner: T, - interceptor: F, - ) -> InterceptedService - where - F: tonic::service::Interceptor, - { - InterceptedService::new(Self::new(inner), interceptor) - } - /// Enable decompressing requests with the given encoding. - #[must_use] - pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.accept_compression_encodings.enable(encoding); - self - } - /// Compress responses with the given encoding, if the client supports it. - #[must_use] - pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { - self.send_compression_encodings.enable(encoding); - self - } - /// Limits the maximum size of a decoded message. - /// - /// Default: `4MB` - #[must_use] - pub fn max_decoding_message_size(mut self, limit: usize) -> Self { - self.max_decoding_message_size = Some(limit); - self - } - /// Limits the maximum size of an encoded message. - /// - /// Default: `usize::MAX` - #[must_use] - pub fn max_encoding_message_size(mut self, limit: usize) -> Self { - self.max_encoding_message_size = Some(limit); - self - } - } - impl tonic::codegen::Service> - for OptimisticExecutionServiceServer - where - T: OptimisticExecutionService, - B: Body + Send + 'static, - B::Error: Into + Send + 'static, - { - type Response = http::Response; - type Error = std::convert::Infallible; - type Future = BoxFuture; - fn poll_ready( - &mut self, - _cx: &mut Context<'_>, - ) -> Poll> { - Poll::Ready(Ok(())) - } - fn call(&mut self, req: http::Request) -> Self::Future { - let inner = self.inner.clone(); - match req.uri().path() { - "/astria.bundle.v1alpha1.OptimisticExecutionService/ExecuteOptimisticBlockStream" => { - #[allow(non_camel_case_types)] - struct ExecuteOptimisticBlockStreamSvc< - T: OptimisticExecutionService, - >( - pub Arc, - ); - impl< - T: OptimisticExecutionService, - > tonic::server::StreamingService< - super::ExecuteOptimisticBlockStreamRequest, - > for ExecuteOptimisticBlockStreamSvc { - type Response = super::ExecuteOptimisticBlockStreamResponse; - type ResponseStream = T::ExecuteOptimisticBlockStreamStream; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request< - tonic::Streaming, - >, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::execute_optimistic_block_stream( - inner, - request, - ) - .await - }; - Box::pin(fut) - } - } - let accept_compression_encodings = self.accept_compression_encodings; - let send_compression_encodings = self.send_compression_encodings; - let max_decoding_message_size = self.max_decoding_message_size; - let max_encoding_message_size = self.max_encoding_message_size; - let inner = self.inner.clone(); - let fut = async move { - let inner = inner.0; - let method = ExecuteOptimisticBlockStreamSvc(inner); - let codec = tonic::codec::ProstCodec::default(); - let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); - let res = grpc.streaming(method, req).await; - Ok(res) - }; - Box::pin(fut) - } - _ => { - Box::pin(async move { - Ok( - http::Response::builder() - .status(200) - .header("grpc-status", "12") - .header("content-type", "application/grpc") - .body(empty_body()) - .unwrap(), - ) - }) - } - } - } - } - impl Clone for OptimisticExecutionServiceServer { - fn clone(&self) -> Self { - let inner = self.inner.clone(); - Self { - inner, - accept_compression_encodings: self.accept_compression_encodings, - send_compression_encodings: self.send_compression_encodings, - max_decoding_message_size: self.max_decoding_message_size, - max_encoding_message_size: self.max_encoding_message_size, - } - } - } - impl Clone for _Inner { - fn clone(&self) -> Self { - Self(Arc::clone(&self.0)) - } - } - impl std::fmt::Debug for _Inner { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.0) - } - } - impl tonic::server::NamedService - for OptimisticExecutionServiceServer { - const NAME: &'static str = "astria.bundle.v1alpha1.OptimisticExecutionService"; - } -} diff --git a/crates/astria-core/src/generated/astria.execution.v2.rs b/crates/astria-core/src/generated/astria.execution.v2.rs index 18ee796137..184c751a96 100644 --- a/crates/astria-core/src/generated/astria.execution.v2.rs +++ b/crates/astria-core/src/generated/astria.execution.v2.rs @@ -1,56 +1,7 @@ -<<<<<<< HEAD -======= -/// GenesisInfo contains the information needed to start a rollup chain. -/// -/// This information is used to determine which sequencer & celestia data to -/// use from the Astria & Celestia networks. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct GenesisInfo { - /// The rollup_id is the unique identifier for the rollup chain. - #[prost(message, optional, tag = "1")] - pub rollup_id: ::core::option::Option, - /// The first block height of sequencer chain to use for rollup transactions. - #[prost(uint32, tag = "2")] - pub sequencer_start_height: u32, - /// The allowed variance in celestia for sequencer blocks to have been posted. - #[prost(uint64, tag = "4")] - pub celestia_block_variance: u64, - /// The rollup block number to map to the sequencer start block height. - #[prost(uint64, tag = "5")] - pub rollup_start_block_number: u64, - /// The rollup block number to re-fetch the genesis info and continue executing with new data. - #[prost(uint64, tag = "6")] - pub rollup_stop_block_number: u64, - /// The ID of the Astria Sequencer network to retrieve Sequencer blocks from. - /// Conductor implementations should verify that the Sequencer network they are connected to - /// have this chain ID (if fetching soft Sequencer blocks), and verify that the Sequencer metadata - /// blobs retrieved from Celestia contain this chain ID (if extracting firm Sequencer blocks from - /// Celestia blobs). - #[prost(string, tag = "7")] - pub sequencer_chain_id: ::prost::alloc::string::String, - /// The ID of the Celestia network to retrieve blobs from. - /// Conductor implementations should verify that the Celestia network they are connected to have - /// this chain ID (if extracting firm Sequencer blocks from Celestia blobs). - #[prost(string, tag = "8")] - pub celestia_chain_id: ::prost::alloc::string::String, - /// Requests Conductor to halt at the stop number instead of re-fetching the genesis and continuing execution. - #[prost(bool, tag = "9")] - pub halt_at_rollup_stop_number: bool, -} -impl ::prost::Name for GenesisInfo { - const NAME: &'static str = "GenesisInfo"; - const PACKAGE: &'static str = "astria.execution.v2"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria.execution.v2.{}", Self::NAME) - } -} ->>>>>>> superfluffy/forma-restart-logic /// The set of information which deterministic driver of block production /// must know about a given rollup Block #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -<<<<<<< HEAD pub struct ExecutedBlockMetadata { /// The block number #[prost(uint64, tag = "1")] @@ -69,129 +20,6 @@ pub struct ExecutedBlockMetadata { } impl ::prost::Name for ExecutedBlockMetadata { const NAME: &'static str = "ExecutedBlockMetadata"; -======= -pub struct Block { - /// The block number - #[prost(uint32, tag = "1")] - pub number: u32, - /// The hash of the block - #[prost(bytes = "bytes", tag = "2")] - pub hash: ::prost::bytes::Bytes, - /// The hash from the parent block - #[prost(bytes = "bytes", tag = "3")] - pub parent_block_hash: ::prost::bytes::Bytes, - /// Timestamp on the block, standardized to google protobuf standard. - #[prost(message, optional, tag = "4")] - pub timestamp: ::core::option::Option<::pbjson_types::Timestamp>, -} -impl ::prost::Name for Block { - const NAME: &'static str = "Block"; - const PACKAGE: &'static str = "astria.execution.v2"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria.execution.v2.{}", Self::NAME) - } -} -/// Fields which are indexed for finding blocks on a blockchain. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct BlockIdentifier { - #[prost(oneof = "block_identifier::Identifier", tags = "1, 2")] - pub identifier: ::core::option::Option, -} -/// Nested message and enum types in `BlockIdentifier`. -pub mod block_identifier { - #[allow(clippy::derive_partial_eq_without_eq)] - #[derive(Clone, PartialEq, ::prost::Oneof)] - pub enum Identifier { - #[prost(uint32, tag = "1")] - BlockNumber(u32), - #[prost(bytes, tag = "2")] - BlockHash(::prost::bytes::Bytes), - } -} -impl ::prost::Name for BlockIdentifier { - const NAME: &'static str = "BlockIdentifier"; - const PACKAGE: &'static str = "astria.execution.v2"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria.execution.v2.{}", Self::NAME) - } -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct GetGenesisInfoRequest {} -impl ::prost::Name for GetGenesisInfoRequest { - const NAME: &'static str = "GetGenesisInfoRequest"; - const PACKAGE: &'static str = "astria.execution.v2"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria.execution.v2.{}", Self::NAME) - } -} -/// Used in GetBlock to find a single block. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct GetBlockRequest { - #[prost(message, optional, tag = "1")] - pub identifier: ::core::option::Option, -} -impl ::prost::Name for GetBlockRequest { - const NAME: &'static str = "GetBlockRequest"; - const PACKAGE: &'static str = "astria.execution.v2"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria.execution.v2.{}", Self::NAME) - } -} -/// Used in BatchGetBlocks, will find all or none based on the list of -/// identifiers. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct BatchGetBlocksRequest { - #[prost(message, repeated, tag = "1")] - pub identifiers: ::prost::alloc::vec::Vec, -} -impl ::prost::Name for BatchGetBlocksRequest { - const NAME: &'static str = "BatchGetBlocksRequest"; - const PACKAGE: &'static str = "astria.execution.v2"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria.execution.v2.{}", Self::NAME) - } -} -/// The list of blocks in response to BatchGetBlocks. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct BatchGetBlocksResponse { - #[prost(message, repeated, tag = "1")] - pub blocks: ::prost::alloc::vec::Vec, -} -impl ::prost::Name for BatchGetBlocksResponse { - const NAME: &'static str = "BatchGetBlocksResponse"; - const PACKAGE: &'static str = "astria.execution.v2"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("astria.execution.v2.{}", Self::NAME) - } -} -/// ExecuteBlockRequest contains all the information needed to create a new rollup -/// block. -/// -/// This information comes from previous rollup blocks, as well as from sequencer -/// blocks. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ExecuteBlockRequest { - /// The hash of previous block, which new block will be created on top of. - #[prost(bytes = "bytes", tag = "1")] - pub prev_block_hash: ::prost::bytes::Bytes, - /// List of transactions to include in the new block. - #[prost(message, repeated, tag = "2")] - pub transactions: ::prost::alloc::vec::Vec< - super::super::sequencerblock::v1::RollupData, - >, - /// Timestamp to be used for new block. - #[prost(message, optional, tag = "3")] - pub timestamp: ::core::option::Option<::pbjson_types::Timestamp>, -} -impl ::prost::Name for ExecuteBlockRequest { - const NAME: &'static str = "ExecuteBlockRequest"; ->>>>>>> superfluffy/forma-restart-logic const PACKAGE: &'static str = "astria.execution.v2"; fn full_name() -> ::prost::alloc::string::String { ::prost::alloc::format!("astria.execution.v2.{}", Self::NAME) @@ -208,7 +36,6 @@ impl ::prost::Name for ExecuteBlockRequest { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct CommitmentState { -<<<<<<< HEAD /// Soft committed block metadata derived directly from an Astria sequencer block. #[prost(message, optional, tag = "1")] pub soft_executed_block_metadata: ::core::option::Option, @@ -222,17 +49,6 @@ pub struct CommitmentState { /// client will not need to search from Celestia genesis. #[prost(uint64, tag = "3")] pub lowest_celestia_search_height: u64, -======= - /// Soft commitment is the rollup block matching latest sequencer block. - #[prost(message, optional, tag = "1")] - pub soft: ::core::option::Option, - /// Firm commitment is achieved when data has been seen in DA. - #[prost(message, optional, tag = "2")] - pub firm: ::core::option::Option, - /// The lowest block number of celestia chain to be searched for rollup blocks given current state - #[prost(uint64, tag = "3")] - pub base_celestia_height: u64, ->>>>>>> superfluffy/forma-restart-logic } impl ::prost::Name for CommitmentState { const NAME: &'static str = "CommitmentState"; @@ -241,7 +57,6 @@ impl ::prost::Name for CommitmentState { ::prost::alloc::format!("astria.execution.v2.{}", Self::NAME) } } -<<<<<<< HEAD /// CreateExecutionSessionRequest is used to create a new execution session on the /// rollup. #[allow(clippy::derive_partial_eq_without_eq)] @@ -421,14 +236,6 @@ pub struct GetExecutedBlockMetadataRequest { } impl ::prost::Name for GetExecutedBlockMetadataRequest { const NAME: &'static str = "GetExecutedBlockMetadataRequest"; -======= -/// There is only one CommitmentState object, so the request is empty. -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct GetCommitmentStateRequest {} -impl ::prost::Name for GetCommitmentStateRequest { - const NAME: &'static str = "GetCommitmentStateRequest"; ->>>>>>> superfluffy/forma-restart-logic const PACKAGE: &'static str = "astria.execution.v2"; fn full_name() -> ::prost::alloc::string::String { ::prost::alloc::format!("astria.execution.v2.{}", Self::NAME) @@ -438,15 +245,11 @@ impl ::prost::Name for GetCommitmentStateRequest { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct UpdateCommitmentStateRequest { -<<<<<<< HEAD /// The session which the commitment state is being updated within. #[prost(string, tag = "1")] pub session_id: ::prost::alloc::string::String, /// The new commitment state to set. #[prost(message, optional, tag = "2")] -======= - #[prost(message, optional, tag = "1")] ->>>>>>> superfluffy/forma-restart-logic pub commitment_state: ::core::option::Option, } impl ::prost::Name for UpdateCommitmentStateRequest { @@ -547,7 +350,6 @@ pub mod execution_service_client { self.inner = self.inner.max_encoding_message_size(limit); self } -<<<<<<< HEAD /// CreateExecutionSession returns the necessary information for mapping sequencer block /// height to rollup block number. pub async fn create_execution_session( @@ -557,13 +359,6 @@ pub mod execution_service_client { tonic::Response, tonic::Status, > { -======= - /// GetGenesisInfo returns the necessary genesis information for rollup chain. - pub async fn get_genesis_info( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result, tonic::Status> { ->>>>>>> superfluffy/forma-restart-logic self.inner .ready() .await @@ -575,67 +370,24 @@ pub mod execution_service_client { })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( -<<<<<<< HEAD "/astria.execution.v2.ExecutionService/CreateExecutionSession", -======= - "/astria.execution.v2.ExecutionService/GetGenesisInfo", ->>>>>>> superfluffy/forma-restart-logic ); let mut req = request.into_request(); req.extensions_mut() .insert( GrpcMethod::new( "astria.execution.v2.ExecutionService", -<<<<<<< HEAD "CreateExecutionSession", -======= - "GetGenesisInfo", ->>>>>>> superfluffy/forma-restart-logic ), ); self.inner.unary(req, path, codec).await } -<<<<<<< HEAD /// GetExecutedBlockMetadata will return a block given an identifier. pub async fn get_executed_block_metadata( &mut self, request: impl tonic::IntoRequest, ) -> std::result::Result< tonic::Response, -======= - /// GetBlock will return a block given an identifier. - pub async fn get_block( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result, tonic::Status> { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/astria.execution.v2.ExecutionService/GetBlock", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert( - GrpcMethod::new("astria.execution.v2.ExecutionService", "GetBlock"), - ); - self.inner.unary(req, path, codec).await - } - /// BatchGetBlocks will return an array of Blocks given an array of block - /// identifiers. - pub async fn batch_get_blocks( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, ->>>>>>> superfluffy/forma-restart-logic tonic::Status, > { self.inner @@ -649,22 +401,14 @@ pub mod execution_service_client { })?; let codec = tonic::codec::ProstCodec::default(); let path = http::uri::PathAndQuery::from_static( -<<<<<<< HEAD "/astria.execution.v2.ExecutionService/GetExecutedBlockMetadata", -======= - "/astria.execution.v2.ExecutionService/BatchGetBlocks", ->>>>>>> superfluffy/forma-restart-logic ); let mut req = request.into_request(); req.extensions_mut() .insert( GrpcMethod::new( "astria.execution.v2.ExecutionService", -<<<<<<< HEAD "GetExecutedBlockMetadata", -======= - "BatchGetBlocks", ->>>>>>> superfluffy/forma-restart-logic ), ); self.inner.unary(req, path, codec).await @@ -674,14 +418,10 @@ pub mod execution_service_client { pub async fn execute_block( &mut self, request: impl tonic::IntoRequest, -<<<<<<< HEAD ) -> std::result::Result< tonic::Response, tonic::Status, > { -======= - ) -> std::result::Result, tonic::Status> { ->>>>>>> superfluffy/forma-restart-logic self.inner .ready() .await @@ -705,40 +445,6 @@ pub mod execution_service_client { ); self.inner.unary(req, path, codec).await } -<<<<<<< HEAD -======= - /// GetCommitmentState fetches the current CommitmentState of the chain. - pub async fn get_commitment_state( - &mut self, - request: impl tonic::IntoRequest, - ) -> std::result::Result< - tonic::Response, - tonic::Status, - > { - self.inner - .ready() - .await - .map_err(|e| { - tonic::Status::new( - tonic::Code::Unknown, - format!("Service was not ready: {}", e.into()), - ) - })?; - let codec = tonic::codec::ProstCodec::default(); - let path = http::uri::PathAndQuery::from_static( - "/astria.execution.v2.ExecutionService/GetCommitmentState", - ); - let mut req = request.into_request(); - req.extensions_mut() - .insert( - GrpcMethod::new( - "astria.execution.v2.ExecutionService", - "GetCommitmentState", - ), - ); - self.inner.unary(req, path, codec).await - } ->>>>>>> superfluffy/forma-restart-logic /// UpdateCommitmentState replaces the whole CommitmentState with a new /// CommitmentState. pub async fn update_commitment_state( @@ -781,7 +487,6 @@ pub mod execution_service_server { /// Generated trait containing gRPC methods that should be implemented for use with ExecutionServiceServer. #[async_trait] pub trait ExecutionService: Send + Sync + 'static { -<<<<<<< HEAD /// CreateExecutionSession returns the necessary information for mapping sequencer block /// height to rollup block number. async fn create_execution_session( @@ -797,25 +502,6 @@ pub mod execution_service_server { request: tonic::Request, ) -> std::result::Result< tonic::Response, -======= - /// GetGenesisInfo returns the necessary genesis information for rollup chain. - async fn get_genesis_info( - self: std::sync::Arc, - request: tonic::Request, - ) -> std::result::Result, tonic::Status>; - /// GetBlock will return a block given an identifier. - async fn get_block( - self: std::sync::Arc, - request: tonic::Request, - ) -> std::result::Result, tonic::Status>; - /// BatchGetBlocks will return an array of Blocks given an array of block - /// identifiers. - async fn batch_get_blocks( - self: std::sync::Arc, - request: tonic::Request, - ) -> std::result::Result< - tonic::Response, ->>>>>>> superfluffy/forma-restart-logic tonic::Status, >; /// ExecuteBlock is called to deterministically derive a rollup block from @@ -823,19 +509,10 @@ pub mod execution_service_server { async fn execute_block( self: std::sync::Arc, request: tonic::Request, -<<<<<<< HEAD ) -> std::result::Result< tonic::Response, tonic::Status, >; -======= - ) -> std::result::Result, tonic::Status>; - /// GetCommitmentState fetches the current CommitmentState of the chain. - async fn get_commitment_state( - self: std::sync::Arc, - request: tonic::Request, - ) -> std::result::Result, tonic::Status>; ->>>>>>> superfluffy/forma-restart-logic /// UpdateCommitmentState replaces the whole CommitmentState with a new /// CommitmentState. async fn update_commitment_state( @@ -927,7 +604,6 @@ pub mod execution_service_server { fn call(&mut self, req: http::Request) -> Self::Future { let inner = self.inner.clone(); match req.uri().path() { -<<<<<<< HEAD "/astria.execution.v2.ExecutionService/CreateExecutionSession" => { #[allow(non_camel_case_types)] struct CreateExecutionSessionSvc(pub Arc); @@ -936,23 +612,12 @@ pub mod execution_service_server { > tonic::server::UnaryService for CreateExecutionSessionSvc { type Response = super::ExecutionSession; -======= - "/astria.execution.v2.ExecutionService/GetGenesisInfo" => { - #[allow(non_camel_case_types)] - struct GetGenesisInfoSvc(pub Arc); - impl< - T: ExecutionService, - > tonic::server::UnaryService - for GetGenesisInfoSvc { - type Response = super::GenesisInfo; ->>>>>>> superfluffy/forma-restart-logic type Future = BoxFuture< tonic::Response, tonic::Status, >; fn call( &mut self, -<<<<<<< HEAD request: tonic::Request, ) -> Self::Future { let inner = Arc::clone(&self.0); @@ -961,13 +626,6 @@ pub mod execution_service_server { inner, request, ) -======= - request: tonic::Request, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::get_genesis_info(inner, request) ->>>>>>> superfluffy/forma-restart-logic .await }; Box::pin(fut) @@ -980,11 +638,7 @@ pub mod execution_service_server { let inner = self.inner.clone(); let fut = async move { let inner = inner.0; -<<<<<<< HEAD let method = CreateExecutionSessionSvc(inner); -======= - let method = GetGenesisInfoSvc(inner); ->>>>>>> superfluffy/forma-restart-logic let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( @@ -1000,7 +654,6 @@ pub mod execution_service_server { }; Box::pin(fut) } -<<<<<<< HEAD "/astria.execution.v2.ExecutionService/GetExecutedBlockMetadata" => { #[allow(non_camel_case_types)] struct GetExecutedBlockMetadataSvc(pub Arc); @@ -1009,23 +662,12 @@ pub mod execution_service_server { > tonic::server::UnaryService for GetExecutedBlockMetadataSvc { type Response = super::ExecutedBlockMetadata; -======= - "/astria.execution.v2.ExecutionService/GetBlock" => { - #[allow(non_camel_case_types)] - struct GetBlockSvc(pub Arc); - impl< - T: ExecutionService, - > tonic::server::UnaryService - for GetBlockSvc { - type Response = super::Block; ->>>>>>> superfluffy/forma-restart-logic type Future = BoxFuture< tonic::Response, tonic::Status, >; fn call( &mut self, -<<<<<<< HEAD request: tonic::Request< super::GetExecutedBlockMetadataRequest, >, @@ -1036,59 +678,6 @@ pub mod execution_service_server { inner, request, ) -======= - request: tonic::Request, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::get_block(inner, request).await - }; - Box::pin(fut) - } - } - let accept_compression_encodings = self.accept_compression_encodings; - let send_compression_encodings = self.send_compression_encodings; - let max_decoding_message_size = self.max_decoding_message_size; - let max_encoding_message_size = self.max_encoding_message_size; - let inner = self.inner.clone(); - let fut = async move { - let inner = inner.0; - let method = GetBlockSvc(inner); - let codec = tonic::codec::ProstCodec::default(); - let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); - let res = grpc.unary(method, req).await; - Ok(res) - }; - Box::pin(fut) - } - "/astria.execution.v2.ExecutionService/BatchGetBlocks" => { - #[allow(non_camel_case_types)] - struct BatchGetBlocksSvc(pub Arc); - impl< - T: ExecutionService, - > tonic::server::UnaryService - for BatchGetBlocksSvc { - type Response = super::BatchGetBlocksResponse; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::batch_get_blocks(inner, request) ->>>>>>> superfluffy/forma-restart-logic .await }; Box::pin(fut) @@ -1101,11 +690,7 @@ pub mod execution_service_server { let inner = self.inner.clone(); let fut = async move { let inner = inner.0; -<<<<<<< HEAD let method = GetExecutedBlockMetadataSvc(inner); -======= - let method = BatchGetBlocksSvc(inner); ->>>>>>> superfluffy/forma-restart-logic let codec = tonic::codec::ProstCodec::default(); let mut grpc = tonic::server::Grpc::new(codec) .apply_compression_config( @@ -1128,11 +713,7 @@ pub mod execution_service_server { T: ExecutionService, > tonic::server::UnaryService for ExecuteBlockSvc { -<<<<<<< HEAD type Response = super::ExecuteBlockResponse; -======= - type Response = super::Block; ->>>>>>> superfluffy/forma-restart-logic type Future = BoxFuture< tonic::Response, tonic::Status, @@ -1171,59 +752,6 @@ pub mod execution_service_server { }; Box::pin(fut) } -<<<<<<< HEAD -======= - "/astria.execution.v2.ExecutionService/GetCommitmentState" => { - #[allow(non_camel_case_types)] - struct GetCommitmentStateSvc(pub Arc); - impl< - T: ExecutionService, - > tonic::server::UnaryService - for GetCommitmentStateSvc { - type Response = super::CommitmentState; - type Future = BoxFuture< - tonic::Response, - tonic::Status, - >; - fn call( - &mut self, - request: tonic::Request, - ) -> Self::Future { - let inner = Arc::clone(&self.0); - let fut = async move { - ::get_commitment_state( - inner, - request, - ) - .await - }; - Box::pin(fut) - } - } - let accept_compression_encodings = self.accept_compression_encodings; - let send_compression_encodings = self.send_compression_encodings; - let max_decoding_message_size = self.max_decoding_message_size; - let max_encoding_message_size = self.max_encoding_message_size; - let inner = self.inner.clone(); - let fut = async move { - let inner = inner.0; - let method = GetCommitmentStateSvc(inner); - let codec = tonic::codec::ProstCodec::default(); - let mut grpc = tonic::server::Grpc::new(codec) - .apply_compression_config( - accept_compression_encodings, - send_compression_encodings, - ) - .apply_max_message_size_config( - max_decoding_message_size, - max_encoding_message_size, - ); - let res = grpc.unary(method, req).await; - Ok(res) - }; - Box::pin(fut) - } ->>>>>>> superfluffy/forma-restart-logic "/astria.execution.v2.ExecutionService/UpdateCommitmentState" => { #[allow(non_camel_case_types)] struct UpdateCommitmentStateSvc(pub Arc); diff --git a/crates/astria-core/src/generated/astria.execution.v2.serde.rs b/crates/astria-core/src/generated/astria.execution.v2.serde.rs index c9f78f0bc1..5329d0d55e 100644 --- a/crates/astria-core/src/generated/astria.execution.v2.serde.rs +++ b/crates/astria-core/src/generated/astria.execution.v2.serde.rs @@ -1,8 +1,4 @@ -<<<<<<< HEAD impl serde::Serialize for CommitmentState { -======= -impl serde::Serialize for BatchGetBlocksRequest { ->>>>>>> superfluffy/forma-restart-logic #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -10,7 +6,6 @@ impl serde::Serialize for BatchGetBlocksRequest { { use serde::ser::SerializeStruct; let mut len = 0; -<<<<<<< HEAD if self.soft_executed_block_metadata.is_some() { len += 1; } @@ -30,50 +25,30 @@ impl serde::Serialize for BatchGetBlocksRequest { if self.lowest_celestia_search_height != 0 { #[allow(clippy::needless_borrow)] struct_ser.serialize_field("lowestCelestiaSearchHeight", ToString::to_string(&self.lowest_celestia_search_height).as_str())?; -======= - if !self.identifiers.is_empty() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("astria.execution.v2.BatchGetBlocksRequest", len)?; - if !self.identifiers.is_empty() { - struct_ser.serialize_field("identifiers", &self.identifiers)?; ->>>>>>> superfluffy/forma-restart-logic } struct_ser.end() } } -<<<<<<< HEAD impl<'de> serde::Deserialize<'de> for CommitmentState { -======= -impl<'de> serde::Deserialize<'de> for BatchGetBlocksRequest { ->>>>>>> superfluffy/forma-restart-logic #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ -<<<<<<< HEAD "soft_executed_block_metadata", "softExecutedBlockMetadata", "firm_executed_block_metadata", "firmExecutedBlockMetadata", "lowest_celestia_search_height", "lowestCelestiaSearchHeight", -======= - "identifiers", ->>>>>>> superfluffy/forma-restart-logic ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { -<<<<<<< HEAD SoftExecutedBlockMetadata, FirmExecutedBlockMetadata, LowestCelestiaSearchHeight, -======= - Identifiers, ->>>>>>> superfluffy/forma-restart-logic } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -95,13 +70,9 @@ impl<'de> serde::Deserialize<'de> for BatchGetBlocksRequest { E: serde::de::Error, { match value { -<<<<<<< HEAD "softExecutedBlockMetadata" | "soft_executed_block_metadata" => Ok(GeneratedField::SoftExecutedBlockMetadata), "firmExecutedBlockMetadata" | "firm_executed_block_metadata" => Ok(GeneratedField::FirmExecutedBlockMetadata), "lowestCelestiaSearchHeight" | "lowest_celestia_search_height" => Ok(GeneratedField::LowestCelestiaSearchHeight), -======= - "identifiers" => Ok(GeneratedField::Identifiers), ->>>>>>> superfluffy/forma-restart-logic _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -111,7 +82,6 @@ impl<'de> serde::Deserialize<'de> for BatchGetBlocksRequest { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { -<<<<<<< HEAD type Value = CommitmentState; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -139,498 +109,49 @@ impl<'de> serde::Deserialize<'de> for BatchGetBlocksRequest { } firm_executed_block_metadata__ = map_.next_value()?; } - GeneratedField::LowestCelestiaSearchHeight => { - if lowest_celestia_search_height__.is_some() { - return Err(serde::de::Error::duplicate_field("lowestCelestiaSearchHeight")); - } - lowest_celestia_search_height__ = - Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) - ; - } - } - } - Ok(CommitmentState { - soft_executed_block_metadata: soft_executed_block_metadata__, - firm_executed_block_metadata: firm_executed_block_metadata__, - lowest_celestia_search_height: lowest_celestia_search_height__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("astria.execution.v2.CommitmentState", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for CreateExecutionSessionRequest { -======= - type Value = BatchGetBlocksRequest; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria.execution.v2.BatchGetBlocksRequest") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut identifiers__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Identifiers => { - if identifiers__.is_some() { - return Err(serde::de::Error::duplicate_field("identifiers")); - } - identifiers__ = Some(map_.next_value()?); - } - } - } - Ok(BatchGetBlocksRequest { - identifiers: identifiers__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("astria.execution.v2.BatchGetBlocksRequest", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for BatchGetBlocksResponse { ->>>>>>> superfluffy/forma-restart-logic - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; -<<<<<<< HEAD - let len = 0; - let struct_ser = serializer.serialize_struct("astria.execution.v2.CreateExecutionSessionRequest", len)?; - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for CreateExecutionSessionRequest { -======= - let mut len = 0; - if !self.blocks.is_empty() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("astria.execution.v2.BatchGetBlocksResponse", len)?; - if !self.blocks.is_empty() { - struct_ser.serialize_field("blocks", &self.blocks)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for BatchGetBlocksResponse { ->>>>>>> superfluffy/forma-restart-logic - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ -<<<<<<< HEAD -======= - "blocks", ->>>>>>> superfluffy/forma-restart-logic - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { -<<<<<<< HEAD -======= - Blocks, ->>>>>>> superfluffy/forma-restart-logic - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GeneratedField; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(formatter, "expected one of: {:?}", &FIELDS) - } - - #[allow(unused_variables)] - fn visit_str(self, value: &str) -> std::result::Result - where - E: serde::de::Error, - { -<<<<<<< HEAD - Err(serde::de::Error::unknown_field(value, FIELDS)) -======= - match value { - "blocks" => Ok(GeneratedField::Blocks), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } ->>>>>>> superfluffy/forma-restart-logic - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { -<<<<<<< HEAD - type Value = CreateExecutionSessionRequest; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria.execution.v2.CreateExecutionSessionRequest") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - while map_.next_key::()?.is_some() { - let _ = map_.next_value::()?; - } - Ok(CreateExecutionSessionRequest { - }) - } - } - deserializer.deserialize_struct("astria.execution.v2.CreateExecutionSessionRequest", FIELDS, GeneratedVisitor) -======= - type Value = BatchGetBlocksResponse; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria.execution.v2.BatchGetBlocksResponse") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut blocks__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Blocks => { - if blocks__.is_some() { - return Err(serde::de::Error::duplicate_field("blocks")); - } - blocks__ = Some(map_.next_value()?); - } - } - } - Ok(BatchGetBlocksResponse { - blocks: blocks__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("astria.execution.v2.BatchGetBlocksResponse", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for Block { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if self.number != 0 { - len += 1; - } - if !self.hash.is_empty() { - len += 1; - } - if !self.parent_block_hash.is_empty() { - len += 1; - } - if self.timestamp.is_some() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("astria.execution.v2.Block", len)?; - if self.number != 0 { - struct_ser.serialize_field("number", &self.number)?; - } - if !self.hash.is_empty() { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("hash", pbjson::private::base64::encode(&self.hash).as_str())?; - } - if !self.parent_block_hash.is_empty() { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("parentBlockHash", pbjson::private::base64::encode(&self.parent_block_hash).as_str())?; - } - if let Some(v) = self.timestamp.as_ref() { - struct_ser.serialize_field("timestamp", v)?; - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for Block { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "number", - "hash", - "parent_block_hash", - "parentBlockHash", - "timestamp", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - Number, - Hash, - ParentBlockHash, - Timestamp, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GeneratedField; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(formatter, "expected one of: {:?}", &FIELDS) - } - - #[allow(unused_variables)] - fn visit_str(self, value: &str) -> std::result::Result - where - E: serde::de::Error, - { - match value { - "number" => Ok(GeneratedField::Number), - "hash" => Ok(GeneratedField::Hash), - "parentBlockHash" | "parent_block_hash" => Ok(GeneratedField::ParentBlockHash), - "timestamp" => Ok(GeneratedField::Timestamp), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = Block; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria.execution.v2.Block") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut number__ = None; - let mut hash__ = None; - let mut parent_block_hash__ = None; - let mut timestamp__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Number => { - if number__.is_some() { - return Err(serde::de::Error::duplicate_field("number")); - } - number__ = - Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) - ; - } - GeneratedField::Hash => { - if hash__.is_some() { - return Err(serde::de::Error::duplicate_field("hash")); - } - hash__ = - Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) - ; - } - GeneratedField::ParentBlockHash => { - if parent_block_hash__.is_some() { - return Err(serde::de::Error::duplicate_field("parentBlockHash")); - } - parent_block_hash__ = - Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) - ; - } - GeneratedField::Timestamp => { - if timestamp__.is_some() { - return Err(serde::de::Error::duplicate_field("timestamp")); - } - timestamp__ = map_.next_value()?; - } - } - } - Ok(Block { - number: number__.unwrap_or_default(), - hash: hash__.unwrap_or_default(), - parent_block_hash: parent_block_hash__.unwrap_or_default(), - timestamp: timestamp__, - }) - } - } - deserializer.deserialize_struct("astria.execution.v2.Block", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for BlockIdentifier { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let mut len = 0; - if self.identifier.is_some() { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("astria.execution.v2.BlockIdentifier", len)?; - if let Some(v) = self.identifier.as_ref() { - match v { - block_identifier::Identifier::BlockNumber(v) => { - struct_ser.serialize_field("blockNumber", v)?; - } - block_identifier::Identifier::BlockHash(v) => { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("blockHash", pbjson::private::base64::encode(&v).as_str())?; - } - } - } - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for BlockIdentifier { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - "block_number", - "blockNumber", - "block_hash", - "blockHash", - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - BlockNumber, - BlockHash, - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GeneratedField; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(formatter, "expected one of: {:?}", &FIELDS) - } - - #[allow(unused_variables)] - fn visit_str(self, value: &str) -> std::result::Result - where - E: serde::de::Error, - { - match value { - "blockNumber" | "block_number" => Ok(GeneratedField::BlockNumber), - "blockHash" | "block_hash" => Ok(GeneratedField::BlockHash), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = BlockIdentifier; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria.execution.v2.BlockIdentifier") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - let mut identifier__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::BlockNumber => { - if identifier__.is_some() { - return Err(serde::de::Error::duplicate_field("blockNumber")); - } - identifier__ = map_.next_value::<::std::option::Option<::pbjson::private::NumberDeserialize<_>>>()?.map(|x| block_identifier::Identifier::BlockNumber(x.0)); - } - GeneratedField::BlockHash => { - if identifier__.is_some() { - return Err(serde::de::Error::duplicate_field("blockHash")); + GeneratedField::LowestCelestiaSearchHeight => { + if lowest_celestia_search_height__.is_some() { + return Err(serde::de::Error::duplicate_field("lowestCelestiaSearchHeight")); } - identifier__ = map_.next_value::<::std::option::Option<::pbjson::private::BytesDeserialize<_>>>()?.map(|x| block_identifier::Identifier::BlockHash(x.0)); + lowest_celestia_search_height__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; } } } - Ok(BlockIdentifier { - identifier: identifier__, + Ok(CommitmentState { + soft_executed_block_metadata: soft_executed_block_metadata__, + firm_executed_block_metadata: firm_executed_block_metadata__, + lowest_celestia_search_height: lowest_celestia_search_height__.unwrap_or_default(), }) } } - deserializer.deserialize_struct("astria.execution.v2.BlockIdentifier", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("astria.execution.v2.CommitmentState", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for CommitmentState { +impl serde::Serialize for CreateExecutionSessionRequest { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where S: serde::Serializer, { use serde::ser::SerializeStruct; - let mut len = 0; - if self.soft.is_some() { - len += 1; - } - if self.firm.is_some() { - len += 1; - } - if self.base_celestia_height != 0 { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("astria.execution.v2.CommitmentState", len)?; - if let Some(v) = self.soft.as_ref() { - struct_ser.serialize_field("soft", v)?; - } - if let Some(v) = self.firm.as_ref() { - struct_ser.serialize_field("firm", v)?; - } - if self.base_celestia_height != 0 { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("baseCelestiaHeight", ToString::to_string(&self.base_celestia_height).as_str())?; - } + let len = 0; + let struct_ser = serializer.serialize_struct("astria.execution.v2.CreateExecutionSessionRequest", len)?; struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for CommitmentState { +impl<'de> serde::Deserialize<'de> for CreateExecutionSessionRequest { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ - "soft", - "firm", - "base_celestia_height", - "baseCelestiaHeight", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { - Soft, - Firm, - BaseCelestiaHeight, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -651,12 +172,7 @@ impl<'de> serde::Deserialize<'de> for CommitmentState { where E: serde::de::Error, { - match value { - "soft" => Ok(GeneratedField::Soft), - "firm" => Ok(GeneratedField::Firm), - "baseCelestiaHeight" | "base_celestia_height" => Ok(GeneratedField::BaseCelestiaHeight), - _ => Err(serde::de::Error::unknown_field(value, FIELDS)), - } + Err(serde::de::Error::unknown_field(value, FIELDS)) } } deserializer.deserialize_identifier(GeneratedVisitor) @@ -664,52 +180,24 @@ impl<'de> serde::Deserialize<'de> for CommitmentState { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = CommitmentState; + type Value = CreateExecutionSessionRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria.execution.v2.CommitmentState") + formatter.write_str("struct astria.execution.v2.CreateExecutionSessionRequest") } - fn visit_map(self, mut map_: V) -> std::result::Result + fn visit_map(self, mut map_: V) -> std::result::Result where V: serde::de::MapAccess<'de>, { - let mut soft__ = None; - let mut firm__ = None; - let mut base_celestia_height__ = None; - while let Some(k) = map_.next_key()? { - match k { - GeneratedField::Soft => { - if soft__.is_some() { - return Err(serde::de::Error::duplicate_field("soft")); - } - soft__ = map_.next_value()?; - } - GeneratedField::Firm => { - if firm__.is_some() { - return Err(serde::de::Error::duplicate_field("firm")); - } - firm__ = map_.next_value()?; - } - GeneratedField::BaseCelestiaHeight => { - if base_celestia_height__.is_some() { - return Err(serde::de::Error::duplicate_field("baseCelestiaHeight")); - } - base_celestia_height__ = - Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) - ; - } - } + while map_.next_key::()?.is_some() { + let _ = map_.next_value::()?; } - Ok(CommitmentState { - soft: soft__, - firm: firm__, - base_celestia_height: base_celestia_height__.unwrap_or_default(), + Ok(CreateExecutionSessionRequest { }) } } - deserializer.deserialize_struct("astria.execution.v2.CommitmentState", FIELDS, GeneratedVisitor) ->>>>>>> superfluffy/forma-restart-logic + deserializer.deserialize_struct("astria.execution.v2.CreateExecutionSessionRequest", FIELDS, GeneratedVisitor) } } impl serde::Serialize for ExecuteBlockRequest { @@ -720,14 +208,10 @@ impl serde::Serialize for ExecuteBlockRequest { { use serde::ser::SerializeStruct; let mut len = 0; -<<<<<<< HEAD if !self.session_id.is_empty() { len += 1; } if !self.parent_hash.is_empty() { -======= - if !self.prev_block_hash.is_empty() { ->>>>>>> superfluffy/forma-restart-logic len += 1; } if !self.transactions.is_empty() { @@ -737,17 +221,11 @@ impl serde::Serialize for ExecuteBlockRequest { len += 1; } let mut struct_ser = serializer.serialize_struct("astria.execution.v2.ExecuteBlockRequest", len)?; -<<<<<<< HEAD if !self.session_id.is_empty() { struct_ser.serialize_field("sessionId", &self.session_id)?; } if !self.parent_hash.is_empty() { struct_ser.serialize_field("parentHash", &self.parent_hash)?; -======= - if !self.prev_block_hash.is_empty() { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("prevBlockHash", pbjson::private::base64::encode(&self.prev_block_hash).as_str())?; ->>>>>>> superfluffy/forma-restart-logic } if !self.transactions.is_empty() { struct_ser.serialize_field("transactions", &self.transactions)?; @@ -765,27 +243,18 @@ impl<'de> serde::Deserialize<'de> for ExecuteBlockRequest { D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ -<<<<<<< HEAD "session_id", "sessionId", "parent_hash", "parentHash", -======= - "prev_block_hash", - "prevBlockHash", ->>>>>>> superfluffy/forma-restart-logic "transactions", "timestamp", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { -<<<<<<< HEAD SessionId, ParentHash, -======= - PrevBlockHash, ->>>>>>> superfluffy/forma-restart-logic Transactions, Timestamp, } @@ -809,12 +278,8 @@ impl<'de> serde::Deserialize<'de> for ExecuteBlockRequest { E: serde::de::Error, { match value { -<<<<<<< HEAD "sessionId" | "session_id" => Ok(GeneratedField::SessionId), "parentHash" | "parent_hash" => Ok(GeneratedField::ParentHash), -======= - "prevBlockHash" | "prev_block_hash" => Ok(GeneratedField::PrevBlockHash), ->>>>>>> superfluffy/forma-restart-logic "transactions" => Ok(GeneratedField::Transactions), "timestamp" => Ok(GeneratedField::Timestamp), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), @@ -836,17 +301,12 @@ impl<'de> serde::Deserialize<'de> for ExecuteBlockRequest { where V: serde::de::MapAccess<'de>, { -<<<<<<< HEAD let mut session_id__ = None; let mut parent_hash__ = None; -======= - let mut prev_block_hash__ = None; ->>>>>>> superfluffy/forma-restart-logic let mut transactions__ = None; let mut timestamp__ = None; while let Some(k) = map_.next_key()? { match k { -<<<<<<< HEAD GeneratedField::SessionId => { if session_id__.is_some() { return Err(serde::de::Error::duplicate_field("sessionId")); @@ -858,15 +318,6 @@ impl<'de> serde::Deserialize<'de> for ExecuteBlockRequest { return Err(serde::de::Error::duplicate_field("parentHash")); } parent_hash__ = Some(map_.next_value()?); -======= - GeneratedField::PrevBlockHash => { - if prev_block_hash__.is_some() { - return Err(serde::de::Error::duplicate_field("prevBlockHash")); - } - prev_block_hash__ = - Some(map_.next_value::<::pbjson::private::BytesDeserialize<_>>()?.0) - ; ->>>>>>> superfluffy/forma-restart-logic } GeneratedField::Transactions => { if transactions__.is_some() { @@ -883,12 +334,8 @@ impl<'de> serde::Deserialize<'de> for ExecuteBlockRequest { } } Ok(ExecuteBlockRequest { -<<<<<<< HEAD session_id: session_id__.unwrap_or_default(), parent_hash: parent_hash__.unwrap_or_default(), -======= - prev_block_hash: prev_block_hash__.unwrap_or_default(), ->>>>>>> superfluffy/forma-restart-logic transactions: transactions__.unwrap_or_default(), timestamp: timestamp__, }) @@ -897,7 +344,6 @@ impl<'de> serde::Deserialize<'de> for ExecuteBlockRequest { deserializer.deserialize_struct("astria.execution.v2.ExecuteBlockRequest", FIELDS, GeneratedVisitor) } } -<<<<<<< HEAD impl serde::Serialize for ExecuteBlockResponse { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result @@ -1373,9 +819,6 @@ impl<'de> serde::Deserialize<'de> for ExecutionSession { } } impl serde::Serialize for ExecutionSessionParameters { -======= -impl serde::Serialize for GenesisInfo { ->>>>>>> superfluffy/forma-restart-logic #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -1386,29 +829,15 @@ impl serde::Serialize for GenesisInfo { if self.rollup_id.is_some() { len += 1; } -<<<<<<< HEAD if self.rollup_start_block_number != 0 { len += 1; } if self.rollup_end_block_number != 0 { -======= - if self.sequencer_start_height != 0 { - len += 1; - } - if self.celestia_block_variance != 0 { - len += 1; - } - if self.rollup_start_block_number != 0 { - len += 1; - } - if self.rollup_stop_block_number != 0 { ->>>>>>> superfluffy/forma-restart-logic len += 1; } if !self.sequencer_chain_id.is_empty() { len += 1; } -<<<<<<< HEAD if self.sequencer_start_block_height != 0 { len += 1; } @@ -1422,43 +851,17 @@ impl serde::Serialize for GenesisInfo { if let Some(v) = self.rollup_id.as_ref() { struct_ser.serialize_field("rollupId", v)?; } -======= - if !self.celestia_chain_id.is_empty() { - len += 1; - } - if self.halt_at_rollup_stop_number { - len += 1; - } - let mut struct_ser = serializer.serialize_struct("astria.execution.v2.GenesisInfo", len)?; - if let Some(v) = self.rollup_id.as_ref() { - struct_ser.serialize_field("rollupId", v)?; - } - if self.sequencer_start_height != 0 { - struct_ser.serialize_field("sequencerStartHeight", &self.sequencer_start_height)?; - } - if self.celestia_block_variance != 0 { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("celestiaBlockVariance", ToString::to_string(&self.celestia_block_variance).as_str())?; - } ->>>>>>> superfluffy/forma-restart-logic if self.rollup_start_block_number != 0 { #[allow(clippy::needless_borrow)] struct_ser.serialize_field("rollupStartBlockNumber", ToString::to_string(&self.rollup_start_block_number).as_str())?; } -<<<<<<< HEAD if self.rollup_end_block_number != 0 { #[allow(clippy::needless_borrow)] struct_ser.serialize_field("rollupEndBlockNumber", ToString::to_string(&self.rollup_end_block_number).as_str())?; -======= - if self.rollup_stop_block_number != 0 { - #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("rollupStopBlockNumber", ToString::to_string(&self.rollup_stop_block_number).as_str())?; ->>>>>>> superfluffy/forma-restart-logic } if !self.sequencer_chain_id.is_empty() { struct_ser.serialize_field("sequencerChainId", &self.sequencer_chain_id)?; } -<<<<<<< HEAD if self.sequencer_start_block_height != 0 { #[allow(clippy::needless_borrow)] struct_ser.serialize_field("sequencerStartBlockHeight", ToString::to_string(&self.sequencer_start_block_height).as_str())?; @@ -1469,22 +872,11 @@ impl serde::Serialize for GenesisInfo { if self.celestia_search_height_max_look_ahead != 0 { #[allow(clippy::needless_borrow)] struct_ser.serialize_field("celestiaSearchHeightMaxLookAhead", ToString::to_string(&self.celestia_search_height_max_look_ahead).as_str())?; -======= - if !self.celestia_chain_id.is_empty() { - struct_ser.serialize_field("celestiaChainId", &self.celestia_chain_id)?; - } - if self.halt_at_rollup_stop_number { - struct_ser.serialize_field("haltAtRollupStopNumber", &self.halt_at_rollup_stop_number)?; ->>>>>>> superfluffy/forma-restart-logic } struct_ser.end() } } -<<<<<<< HEAD impl<'de> serde::Deserialize<'de> for ExecutionSessionParameters { -======= -impl<'de> serde::Deserialize<'de> for GenesisInfo { ->>>>>>> superfluffy/forma-restart-logic #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where @@ -1493,7 +885,6 @@ impl<'de> serde::Deserialize<'de> for GenesisInfo { const FIELDS: &[&str] = &[ "rollup_id", "rollupId", -<<<<<<< HEAD "rollup_start_block_number", "rollupStartBlockNumber", "rollup_end_block_number", @@ -1506,43 +897,17 @@ impl<'de> serde::Deserialize<'de> for GenesisInfo { "celestiaChainId", "celestia_search_height_max_look_ahead", "celestiaSearchHeightMaxLookAhead", -======= - "sequencer_start_height", - "sequencerStartHeight", - "celestia_block_variance", - "celestiaBlockVariance", - "rollup_start_block_number", - "rollupStartBlockNumber", - "rollup_stop_block_number", - "rollupStopBlockNumber", - "sequencer_chain_id", - "sequencerChainId", - "celestia_chain_id", - "celestiaChainId", - "halt_at_rollup_stop_number", - "haltAtRollupStopNumber", ->>>>>>> superfluffy/forma-restart-logic ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { RollupId, -<<<<<<< HEAD RollupStartBlockNumber, RollupEndBlockNumber, SequencerChainId, SequencerStartBlockHeight, CelestiaChainId, CelestiaSearchHeightMaxLookAhead, -======= - SequencerStartHeight, - CelestiaBlockVariance, - RollupStartBlockNumber, - RollupStopBlockNumber, - SequencerChainId, - CelestiaChainId, - HaltAtRollupStopNumber, ->>>>>>> superfluffy/forma-restart-logic } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -1565,22 +930,12 @@ impl<'de> serde::Deserialize<'de> for GenesisInfo { { match value { "rollupId" | "rollup_id" => Ok(GeneratedField::RollupId), -<<<<<<< HEAD "rollupStartBlockNumber" | "rollup_start_block_number" => Ok(GeneratedField::RollupStartBlockNumber), "rollupEndBlockNumber" | "rollup_end_block_number" => Ok(GeneratedField::RollupEndBlockNumber), "sequencerChainId" | "sequencer_chain_id" => Ok(GeneratedField::SequencerChainId), "sequencerStartBlockHeight" | "sequencer_start_block_height" => Ok(GeneratedField::SequencerStartBlockHeight), "celestiaChainId" | "celestia_chain_id" => Ok(GeneratedField::CelestiaChainId), "celestiaSearchHeightMaxLookAhead" | "celestia_search_height_max_look_ahead" => Ok(GeneratedField::CelestiaSearchHeightMaxLookAhead), -======= - "sequencerStartHeight" | "sequencer_start_height" => Ok(GeneratedField::SequencerStartHeight), - "celestiaBlockVariance" | "celestia_block_variance" => Ok(GeneratedField::CelestiaBlockVariance), - "rollupStartBlockNumber" | "rollup_start_block_number" => Ok(GeneratedField::RollupStartBlockNumber), - "rollupStopBlockNumber" | "rollup_stop_block_number" => Ok(GeneratedField::RollupStopBlockNumber), - "sequencerChainId" | "sequencer_chain_id" => Ok(GeneratedField::SequencerChainId), - "celestiaChainId" | "celestia_chain_id" => Ok(GeneratedField::CelestiaChainId), - "haltAtRollupStopNumber" | "halt_at_rollup_stop_number" => Ok(GeneratedField::HaltAtRollupStopNumber), ->>>>>>> superfluffy/forma-restart-logic _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -1590,7 +945,6 @@ impl<'de> serde::Deserialize<'de> for GenesisInfo { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { -<<<<<<< HEAD type Value = ExecutionSessionParameters; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -1598,35 +952,16 @@ impl<'de> serde::Deserialize<'de> for GenesisInfo { } fn visit_map(self, mut map_: V) -> std::result::Result -======= - type Value = GenesisInfo; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria.execution.v2.GenesisInfo") - } - - fn visit_map(self, mut map_: V) -> std::result::Result ->>>>>>> superfluffy/forma-restart-logic where V: serde::de::MapAccess<'de>, { let mut rollup_id__ = None; -<<<<<<< HEAD let mut rollup_start_block_number__ = None; let mut rollup_end_block_number__ = None; let mut sequencer_chain_id__ = None; let mut sequencer_start_block_height__ = None; let mut celestia_chain_id__ = None; let mut celestia_search_height_max_look_ahead__ = None; -======= - let mut sequencer_start_height__ = None; - let mut celestia_block_variance__ = None; - let mut rollup_start_block_number__ = None; - let mut rollup_stop_block_number__ = None; - let mut sequencer_chain_id__ = None; - let mut celestia_chain_id__ = None; - let mut halt_at_rollup_stop_number__ = None; ->>>>>>> superfluffy/forma-restart-logic while let Some(k) = map_.next_key()? { match k { GeneratedField::RollupId => { @@ -1635,25 +970,6 @@ impl<'de> serde::Deserialize<'de> for GenesisInfo { } rollup_id__ = map_.next_value()?; } -<<<<<<< HEAD -======= - GeneratedField::SequencerStartHeight => { - if sequencer_start_height__.is_some() { - return Err(serde::de::Error::duplicate_field("sequencerStartHeight")); - } - sequencer_start_height__ = - Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) - ; - } - GeneratedField::CelestiaBlockVariance => { - if celestia_block_variance__.is_some() { - return Err(serde::de::Error::duplicate_field("celestiaBlockVariance")); - } - celestia_block_variance__ = - Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) - ; - } ->>>>>>> superfluffy/forma-restart-logic GeneratedField::RollupStartBlockNumber => { if rollup_start_block_number__.is_some() { return Err(serde::de::Error::duplicate_field("rollupStartBlockNumber")); @@ -1662,19 +978,11 @@ impl<'de> serde::Deserialize<'de> for GenesisInfo { Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) ; } -<<<<<<< HEAD GeneratedField::RollupEndBlockNumber => { if rollup_end_block_number__.is_some() { return Err(serde::de::Error::duplicate_field("rollupEndBlockNumber")); } rollup_end_block_number__ = -======= - GeneratedField::RollupStopBlockNumber => { - if rollup_stop_block_number__.is_some() { - return Err(serde::de::Error::duplicate_field("rollupStopBlockNumber")); - } - rollup_stop_block_number__ = ->>>>>>> superfluffy/forma-restart-logic Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) ; } @@ -1684,7 +992,6 @@ impl<'de> serde::Deserialize<'de> for GenesisInfo { } sequencer_chain_id__ = Some(map_.next_value()?); } -<<<<<<< HEAD GeneratedField::SequencerStartBlockHeight => { if sequencer_start_block_height__.is_some() { return Err(serde::de::Error::duplicate_field("sequencerStartBlockHeight")); @@ -1693,15 +1000,12 @@ impl<'de> serde::Deserialize<'de> for GenesisInfo { Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) ; } -======= ->>>>>>> superfluffy/forma-restart-logic GeneratedField::CelestiaChainId => { if celestia_chain_id__.is_some() { return Err(serde::de::Error::duplicate_field("celestiaChainId")); } celestia_chain_id__ = Some(map_.next_value()?); } -<<<<<<< HEAD GeneratedField::CelestiaSearchHeightMaxLookAhead => { if celestia_search_height_max_look_ahead__.is_some() { return Err(serde::de::Error::duplicate_field("celestiaSearchHeightMaxLookAhead")); @@ -1727,32 +1031,6 @@ impl<'de> serde::Deserialize<'de> for GenesisInfo { } } impl serde::Serialize for GetExecutedBlockMetadataRequest { -======= - GeneratedField::HaltAtRollupStopNumber => { - if halt_at_rollup_stop_number__.is_some() { - return Err(serde::de::Error::duplicate_field("haltAtRollupStopNumber")); - } - halt_at_rollup_stop_number__ = Some(map_.next_value()?); - } - } - } - Ok(GenesisInfo { - rollup_id: rollup_id__, - sequencer_start_height: sequencer_start_height__.unwrap_or_default(), - celestia_block_variance: celestia_block_variance__.unwrap_or_default(), - rollup_start_block_number: rollup_start_block_number__.unwrap_or_default(), - rollup_stop_block_number: rollup_stop_block_number__.unwrap_or_default(), - sequencer_chain_id: sequencer_chain_id__.unwrap_or_default(), - celestia_chain_id: celestia_chain_id__.unwrap_or_default(), - halt_at_rollup_stop_number: halt_at_rollup_stop_number__.unwrap_or_default(), - }) - } - } - deserializer.deserialize_struct("astria.execution.v2.GenesisInfo", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for GetBlockRequest { ->>>>>>> superfluffy/forma-restart-logic #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -1763,22 +1041,14 @@ impl serde::Serialize for GetBlockRequest { if self.identifier.is_some() { len += 1; } -<<<<<<< HEAD let mut struct_ser = serializer.serialize_struct("astria.execution.v2.GetExecutedBlockMetadataRequest", len)?; -======= - let mut struct_ser = serializer.serialize_struct("astria.execution.v2.GetBlockRequest", len)?; ->>>>>>> superfluffy/forma-restart-logic if let Some(v) = self.identifier.as_ref() { struct_ser.serialize_field("identifier", v)?; } struct_ser.end() } } -<<<<<<< HEAD impl<'de> serde::Deserialize<'de> for GetExecutedBlockMetadataRequest { -======= -impl<'de> serde::Deserialize<'de> for GetBlockRequest { ->>>>>>> superfluffy/forma-restart-logic #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where @@ -1822,7 +1092,6 @@ impl<'de> serde::Deserialize<'de> for GetBlockRequest { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { -<<<<<<< HEAD type Value = GetExecutedBlockMetadataRequest; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -1830,15 +1099,6 @@ impl<'de> serde::Deserialize<'de> for GetBlockRequest { } fn visit_map(self, mut map_: V) -> std::result::Result -======= - type Value = GetBlockRequest; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria.execution.v2.GetBlockRequest") - } - - fn visit_map(self, mut map_: V) -> std::result::Result ->>>>>>> superfluffy/forma-restart-logic where V: serde::de::MapAccess<'de>, { @@ -1853,162 +1113,12 @@ impl<'de> serde::Deserialize<'de> for GetBlockRequest { } } } -<<<<<<< HEAD Ok(GetExecutedBlockMetadataRequest { -======= - Ok(GetBlockRequest { ->>>>>>> superfluffy/forma-restart-logic identifier: identifier__, }) } } -<<<<<<< HEAD deserializer.deserialize_struct("astria.execution.v2.GetExecutedBlockMetadataRequest", FIELDS, GeneratedVisitor) -======= - deserializer.deserialize_struct("astria.execution.v2.GetBlockRequest", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for GetCommitmentStateRequest { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let len = 0; - let struct_ser = serializer.serialize_struct("astria.execution.v2.GetCommitmentStateRequest", len)?; - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for GetCommitmentStateRequest { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GeneratedField; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(formatter, "expected one of: {:?}", &FIELDS) - } - - #[allow(unused_variables)] - fn visit_str(self, value: &str) -> std::result::Result - where - E: serde::de::Error, - { - Err(serde::de::Error::unknown_field(value, FIELDS)) - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GetCommitmentStateRequest; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria.execution.v2.GetCommitmentStateRequest") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - while map_.next_key::()?.is_some() { - let _ = map_.next_value::()?; - } - Ok(GetCommitmentStateRequest { - }) - } - } - deserializer.deserialize_struct("astria.execution.v2.GetCommitmentStateRequest", FIELDS, GeneratedVisitor) - } -} -impl serde::Serialize for GetGenesisInfoRequest { - #[allow(deprecated)] - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - use serde::ser::SerializeStruct; - let len = 0; - let struct_ser = serializer.serialize_struct("astria.execution.v2.GetGenesisInfoRequest", len)?; - struct_ser.end() - } -} -impl<'de> serde::Deserialize<'de> for GetGenesisInfoRequest { - #[allow(deprecated)] - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - const FIELDS: &[&str] = &[ - ]; - - #[allow(clippy::enum_variant_names)] - enum GeneratedField { - } - impl<'de> serde::Deserialize<'de> for GeneratedField { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - struct GeneratedVisitor; - - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GeneratedField; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(formatter, "expected one of: {:?}", &FIELDS) - } - - #[allow(unused_variables)] - fn visit_str(self, value: &str) -> std::result::Result - where - E: serde::de::Error, - { - Err(serde::de::Error::unknown_field(value, FIELDS)) - } - } - deserializer.deserialize_identifier(GeneratedVisitor) - } - } - struct GeneratedVisitor; - impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = GetGenesisInfoRequest; - - fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct astria.execution.v2.GetGenesisInfoRequest") - } - - fn visit_map(self, mut map_: V) -> std::result::Result - where - V: serde::de::MapAccess<'de>, - { - while map_.next_key::()?.is_some() { - let _ = map_.next_value::()?; - } - Ok(GetGenesisInfoRequest { - }) - } - } - deserializer.deserialize_struct("astria.execution.v2.GetGenesisInfoRequest", FIELDS, GeneratedVisitor) ->>>>>>> superfluffy/forma-restart-logic } } impl serde::Serialize for UpdateCommitmentStateRequest { @@ -2019,22 +1129,16 @@ impl serde::Serialize for UpdateCommitmentStateRequest { { use serde::ser::SerializeStruct; let mut len = 0; -<<<<<<< HEAD if !self.session_id.is_empty() { len += 1; } -======= ->>>>>>> superfluffy/forma-restart-logic if self.commitment_state.is_some() { len += 1; } let mut struct_ser = serializer.serialize_struct("astria.execution.v2.UpdateCommitmentStateRequest", len)?; -<<<<<<< HEAD if !self.session_id.is_empty() { struct_ser.serialize_field("sessionId", &self.session_id)?; } -======= ->>>>>>> superfluffy/forma-restart-logic if let Some(v) = self.commitment_state.as_ref() { struct_ser.serialize_field("commitmentState", v)?; } @@ -2048,21 +1152,15 @@ impl<'de> serde::Deserialize<'de> for UpdateCommitmentStateRequest { D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ -<<<<<<< HEAD "session_id", "sessionId", -======= ->>>>>>> superfluffy/forma-restart-logic "commitment_state", "commitmentState", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { -<<<<<<< HEAD SessionId, -======= ->>>>>>> superfluffy/forma-restart-logic CommitmentState, } impl<'de> serde::Deserialize<'de> for GeneratedField { @@ -2085,10 +1183,7 @@ impl<'de> serde::Deserialize<'de> for UpdateCommitmentStateRequest { E: serde::de::Error, { match value { -<<<<<<< HEAD "sessionId" | "session_id" => Ok(GeneratedField::SessionId), -======= ->>>>>>> superfluffy/forma-restart-logic "commitmentState" | "commitment_state" => Ok(GeneratedField::CommitmentState), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } @@ -2109,7 +1204,6 @@ impl<'de> serde::Deserialize<'de> for UpdateCommitmentStateRequest { where V: serde::de::MapAccess<'de>, { -<<<<<<< HEAD let mut session_id__ = None; let mut commitment_state__ = None; while let Some(k) = map_.next_key()? { @@ -2120,11 +1214,6 @@ impl<'de> serde::Deserialize<'de> for UpdateCommitmentStateRequest { } session_id__ = Some(map_.next_value()?); } -======= - let mut commitment_state__ = None; - while let Some(k) = map_.next_key()? { - match k { ->>>>>>> superfluffy/forma-restart-logic GeneratedField::CommitmentState => { if commitment_state__.is_some() { return Err(serde::de::Error::duplicate_field("commitmentState")); @@ -2134,10 +1223,7 @@ impl<'de> serde::Deserialize<'de> for UpdateCommitmentStateRequest { } } Ok(UpdateCommitmentStateRequest { -<<<<<<< HEAD session_id: session_id__.unwrap_or_default(), -======= ->>>>>>> superfluffy/forma-restart-logic commitment_state: commitment_state__, }) } diff --git a/crates/astria-core/src/generated/astria.optimistic_execution.v1alpha1.rs b/crates/astria-core/src/generated/astria.optimistic_execution.v1alpha1.rs index 606000e263..10e31d0a86 100644 --- a/crates/astria-core/src/generated/astria.optimistic_execution.v1alpha1.rs +++ b/crates/astria-core/src/generated/astria.optimistic_execution.v1alpha1.rs @@ -41,7 +41,9 @@ pub struct ExecuteOptimisticBlockStreamResponse { /// Metadata identifying the block resulting from executing a block. Includes number, hash, /// parent hash and timestamp. #[prost(message, optional, tag = "1")] - pub block: ::core::option::Option, + pub block: ::core::option::Option< + super::super::execution::v2::ExecutedBlockMetadata, + >, /// The base_sequencer_block_hash is the hash from the base sequencer block this block /// is based on. This is used to associate an optimistic execution result with the hash /// received once a sequencer block is committed. diff --git a/crates/astria-core/src/generated/mod.rs b/crates/astria-core/src/generated/mod.rs index f9b4ffeece..79eb648f3c 100644 --- a/crates/astria-core/src/generated/mod.rs +++ b/crates/astria-core/src/generated/mod.rs @@ -55,15 +55,6 @@ pub mod astria { #[path = ""] pub mod execution { - pub mod v1 { - include!("astria.execution.v1.rs"); - - #[cfg(feature = "serde")] - mod _serde_impl { - use super::*; - include!("astria.execution.v1.serde.rs"); - } - } pub mod v2 { include!("astria.execution.v2.rs"); diff --git a/proto/executionapis/astria/bundle/v1alpha1/optimistic_execution.proto b/proto/executionapis/astria/bundle/v1alpha1/optimistic_execution.proto deleted file mode 100644 index c8fa7af943..0000000000 --- a/proto/executionapis/astria/bundle/v1alpha1/optimistic_execution.proto +++ /dev/null @@ -1,38 +0,0 @@ -syntax = "proto3"; - -package astria.bundle.v1alpha1; - -import "astria/execution/v2/execution.proto"; -import "astria/sequencerblock/v1/block.proto"; -import "google/protobuf/timestamp.proto"; - -// The "BaseBlock" is the information needed to simulate bundles on top of -// a Sequencer block which may not have been committed yet. -message BaseBlock { - // This is the block hash for the proposed block. - bytes sequencer_block_hash = 1; - // List of transactions to include in the new block. - repeated astria.sequencerblock.v1.RollupData transactions = 2; - // Timestamp to be used for new block. - google.protobuf.Timestamp timestamp = 3; -} - -message ExecuteOptimisticBlockStreamRequest { - BaseBlock base_block = 1; -} - -message ExecuteOptimisticBlockStreamResponse { - // Metadata identifying the block resulting from executing a block. Includes number, hash, - // parent hash and timestamp. - astria.execution.v2.Block block = 1; - // The base_sequencer_block_hash is the hash from the base sequencer block this block - // is based on. This is used to associate an optimistic execution result with the hash - // received once a sequencer block is committed. - bytes base_sequencer_block_hash = 2; -} - -service OptimisticExecutionService { - // Stream blocks from the Auctioneer to Geth for optimistic execution. Geth will stream back - // metadata from the executed blocks. - rpc ExecuteOptimisticBlockStream(stream ExecuteOptimisticBlockStreamRequest) returns (stream ExecuteOptimisticBlockStreamResponse); -} diff --git a/proto/executionapis/astria/execution/v2/execution.proto b/proto/executionapis/astria/execution/v2/execution.proto deleted file mode 100644 index fddded00eb..0000000000 --- a/proto/executionapis/astria/execution/v2/execution.proto +++ /dev/null @@ -1,142 +0,0 @@ -syntax = 'proto3'; - -package astria.execution.v2; - -import "astria/primitive/v1/types.proto"; -import "astria/sequencerblock/v1/block.proto"; -import "google/protobuf/timestamp.proto"; - -// GenesisInfo contains the information needed to start a rollup chain. -// -// This information is used to determine which sequencer & celestia data to -// use from the Astria & Celestia networks. -message GenesisInfo { - // The rollup_id is the unique identifier for the rollup chain. - astria.primitive.v1.RollupId rollup_id = 1; - // The first block height of sequencer chain to use for rollup transactions. - uint32 sequencer_start_height = 2; - // The allowed variance in celestia for sequencer blocks to have been posted. - uint64 celestia_block_variance = 4; - // The rollup block number to map to the sequencer start block height. - uint64 rollup_start_block_number = 5; - // The rollup block number to re-fetch the genesis info and continue executing with new data. - uint64 rollup_stop_block_number = 6; - // The ID of the Astria Sequencer network to retrieve Sequencer blocks from. - // Conductor implementations should verify that the Sequencer network they are connected to - // have this chain ID (if fetching soft Sequencer blocks), and verify that the Sequencer metadata - // blobs retrieved from Celestia contain this chain ID (if extracting firm Sequencer blocks from - // Celestia blobs). - string sequencer_chain_id = 7; - // The ID of the Celestia network to retrieve blobs from. - // Conductor implementations should verify that the Celestia network they are connected to have - // this chain ID (if extracting firm Sequencer blocks from Celestia blobs). - string celestia_chain_id = 8; - // Requests Conductor to halt at the stop number instead of re-fetching the genesis and continuing execution. - bool halt_at_rollup_stop_number = 9; -} - -// The set of information which deterministic driver of block production -// must know about a given rollup Block -message Block { - // The block number - uint32 number = 1; - // The hash of the block - bytes hash = 2; - // The hash from the parent block - bytes parent_block_hash = 3; - // Timestamp on the block, standardized to google protobuf standard. - google.protobuf.Timestamp timestamp = 4; -} - -// Fields which are indexed for finding blocks on a blockchain. -message BlockIdentifier { - oneof identifier { - uint32 block_number = 1; - bytes block_hash = 2; - } -} - -message GetGenesisInfoRequest {} - -// Used in GetBlock to find a single block. -message GetBlockRequest { - BlockIdentifier identifier = 1; -} - -// Used in BatchGetBlocks, will find all or none based on the list of -// identifiers. -message BatchGetBlocksRequest { - repeated BlockIdentifier identifiers = 1; -} - -// The list of blocks in response to BatchGetBlocks. -message BatchGetBlocksResponse { - repeated Block blocks = 1; -} - -// ExecuteBlockRequest contains all the information needed to create a new rollup -// block. -// -// This information comes from previous rollup blocks, as well as from sequencer -// blocks. -message ExecuteBlockRequest { - // The hash of previous block, which new block will be created on top of. - bytes prev_block_hash = 1; - // List of transactions to include in the new block. - repeated astria.sequencerblock.v1.RollupData transactions = 2; - // Timestamp to be used for new block. - google.protobuf.Timestamp timestamp = 3; -} - -// The CommitmentState holds the block at each stage of sequencer commitment -// level -// -// A Valid CommitmentState: -// - Block numbers are such that soft >= firm. -// - No blocks ever decrease in block number. -// - The chain defined by soft is the head of the canonical chain the firm block -// must belong to. -message CommitmentState { - // Soft commitment is the rollup block matching latest sequencer block. - Block soft = 1; - // Firm commitment is achieved when data has been seen in DA. - Block firm = 2; - // The lowest block number of celestia chain to be searched for rollup blocks given current state - uint64 base_celestia_height = 3; -} - -// There is only one CommitmentState object, so the request is empty. -message GetCommitmentStateRequest {} - -// The CommitmentState to set, must include complete state. -message UpdateCommitmentStateRequest { - CommitmentState commitment_state = 1; -} - -// ExecutionService is used to drive deterministic production of blocks. -// -// The service can be implemented by any blockchain which wants to utilize the -// Astria Shared Sequencer, and will have block production driven via the Astria -// "Conductor". -service ExecutionService { - // GetGenesisInfo returns the necessary genesis information for rollup chain. - rpc GetGenesisInfo(GetGenesisInfoRequest) returns (GenesisInfo); - - // GetBlock will return a block given an identifier. - rpc GetBlock(GetBlockRequest) returns (Block); - - // BatchGetBlocks will return an array of Blocks given an array of block - // identifiers. - rpc BatchGetBlocks(BatchGetBlocksRequest) returns (BatchGetBlocksResponse); - - // ExecuteBlock is called to deterministically derive a rollup block from - // filtered sequencer block information. - rpc ExecuteBlock(ExecuteBlockRequest) returns (Block); - - // GetCommitmentState fetches the current CommitmentState of the chain. - rpc GetCommitmentState(GetCommitmentStateRequest) returns (CommitmentState); - - // UpdateCommitmentState replaces the whole CommitmentState with a new - // CommitmentState. - rpc UpdateCommitmentState(UpdateCommitmentStateRequest) returns (CommitmentState); -} diff --git a/proto/executionapis/astria/optimistic_execution/v1alpha1/execute_optimistic_block_stream_response.proto b/proto/executionapis/astria/optimistic_execution/v1alpha1/execute_optimistic_block_stream_response.proto index e3291478ff..57a03c9a01 100644 --- a/proto/executionapis/astria/optimistic_execution/v1alpha1/execute_optimistic_block_stream_response.proto +++ b/proto/executionapis/astria/optimistic_execution/v1alpha1/execute_optimistic_block_stream_response.proto @@ -2,12 +2,12 @@ syntax = "proto3"; package astria.optimistic_execution.v1alpha1; -import "astria/execution/v1/execution.proto"; +import "astria/execution/v2/executed_block_metadata.proto"; message ExecuteOptimisticBlockStreamResponse { // Metadata identifying the block resulting from executing a block. Includes number, hash, // parent hash and timestamp. - astria.execution.v1.Block block = 1; + astria.execution.v2.ExecutedBlockMetadata block = 1; // The base_sequencer_block_hash is the hash from the base sequencer block this block // is based on. This is used to associate an optimistic execution result with the hash // received once a sequencer block is committed. diff --git a/specs/conductor.md b/specs/conductor.md index 6a6a1b364d..81464a4ae5 100644 --- a/specs/conductor.md +++ b/specs/conductor.md @@ -8,7 +8,7 @@ against a rollup (currently geth). It does this by: 1. reading data specific to the rollup from Sequencer or from a data availability provider (currently Celestia); 2. and then executing that data against the rollup implementing the - [`astria.execution.v1alpha2` API](./execution-api.md). + [`astria.execution.v2` API](./execution-api.md). Executed rollup data that is read directly from Sequencer is referred to *soft*-committed, while rollup data read from the data availability provder @@ -30,54 +30,50 @@ not the data availability provider. It connects to a At a high level, it followed the following steps (all remote procedure calls are gRPC): -1. Call `astria.execution.v1alpha2.GetGenesisInfo` to get the rollup's genesis - information (call this `G`). -2. Call `astria.execution.v1alpha2.GetCommitmentState` to get the rollup's most - recent commitment state (call this `C`). -3. Map the current rollup's soft number/height to the next expected Sequencer's - height using `S = G.sequencer_genesis_block_height + C.soft.number`. -4. Call `astria.sequencerblock.v1alpha1.GetFilteredSequencerBlock` with - arguments `S` and `G.rollup_id` to get Sequencer block metadata and data +1. Call `astria.execution.v2.CreateExecutionSession` to initiate a new execution + session (call this `E`). +2. Map the current rollup's soft number/height to the next expected Sequencer's + height using `S = E.sequencer_start_block_height + (C.soft.number - E.rollup_start_block_number`. +3. Call `astria.sequencerblock.v1.GetFilteredSequencerBlock` with + arguments `S` and `E.rollup_id` to get Sequencer block metadata and data specific to Conductor's rollup node. -5. Call `astria.execution.v1alpha2.ExecuteBlock` with the result of step 4. -6. Call `astria.execution.v1alpha2.UpdateCommitmentState` with the result of - step 5, specifically updating the tracked commitment state +4. Call `astria.execution.v2.ExecuteBlock` with the result of step 3. +5. Call `astria.execution.v2.UpdateCommitmentState` with the result of + step 4, specifically updating the tracked commitment state `C.soft.number += 1`. -7. Go to step 3. +6. Go to step 2. ### Firm-only mode In firm-only mode, Conductor only reads rollup information from Celestia but not from Sequencer. Because Sequencer blocks are both batched and split by namespaces (see the [Sequencer-Relayer spec](./sequencer-relayer.md)), -Conductor must read, verify and match Sequencer block metadata to rollup data +Conductor must read, verify, and match Sequencer block metadata to rollup data for a given Sequencer height. At a high level, it followed the following steps (all remote procedure calls are gRPC): -1. Call `astria.execution.v1alpha2.GetGenesisInfo` to get the rollup's genesis - information (call this `G`). -2. Call `astria.execution.v1alpha2.GetCommitmentState` to get the rollup's most - recent commitment state (call this `C`). -3. Call Sequencer's CometBFT JSONRPC endpoint with arguments +1. Call `astria.execution.v2.CreateExecutionSession` to initiate a new execution + session (call this `E`). +2. Call Sequencer's CometBFT JSONRPC endpoint with arguments `{ "method": "genesis", "params": null }` to get its genesis - state (call this `Gs`). -4. Determine the rollup's [Celestia v0 namespace] from the first 10 bytes of its + state (call this `G`). +3. Determine the rollup's [Celestia v0 namespace] from the first 10 bytes of its ID, `G.rollup_id[0..10]` (call this Celestia namespace `Nr`) -5. Determine the Sequencer's [Celestia v0 namespace] from the first 10 bytes of +4. Determine the Sequencer's [Celestia v0 namespace] from the first 10 bytes of the Sha256 hash of its chain ID, `Sha256(Gs.chain_id)[0..10]` (call this Celestia namespace `Ns`). -6. Map the current rollup's firm number/height to the Sequencer's height using - `F = G.sequencer_genesis_block_height + C.soft.number`. -7. Determine the permissible Celestia height window that Conductor is allowed - to read from `H_start = C.base_celestia_height` and - `H_end = H_start + G.celestia_block_variance * 6`[^1]. -8. For every height `H` in the range `[H_start, H_end]` (inclusive): +5. Map the current rollup's firm number/height to the Sequencer's height using + `F = E.sequencer_start_block_height + (C.firm.number - E.rollup_start_block_number`. +6. Determine the permissible Celestia height window that Conductor is allowed + to read from `H_start = C.lowest_celestia_search_height` and + `H_end = H_start + E.celestia_search_height_max_look_ahead * 6`[^1]. +7. For every height `H` in the range `[H_start, H_end]` (inclusive): 1. Call Celestia-Node JSONRPC with arguments to get Sequencer block metadata `{"method": "blob.GetAll", "params": [, []]}`. 2. Decompress the result of 1. as brotli, decode as protobuf - `astria.sequencerblock.v1alpha1.SubmittedMetadataList`. + `astria.sequencerblock.v1.SubmittedMetadataList`. 3. For each metadata element found in the previous step: 1. Call the Sequencer CometBFT JSONRPC with the following arguments to get the commitment at the metadata sequencer height `M` @@ -86,23 +82,23 @@ are gRPC): get the set of validators at the metadata sequencer height `M-1` (the validators for height `M` are found at height `M-1`): `{"method": "validators", "params": { "height": }}`. - 3. validate the metadata using the commitment and validators + 3. Validate the metadata using the commitment and validator's information. 4. Call Celestia-Node JSONRPC with arguments to get Rollup data `{"method": "blob.GetAll", "params": [, []]}`. - 5. Decompress the result of 6. as brotli, decode as protobuf - `astria.sequencerblock.v1alpha1.SubmittedRollupDataList`. - 6. Match pairs `P = (metadata, rollup data)` found in the previous steps - using `rollup.block_hash` and `metadata.block_hash`. -9. Get that pair `P` with metadata sequencer height matching the next expected - firm Sequencer height `M == F` (as determined in step 6). If it exists, go to - step 10. If no such pair exists, exit. -10. Call `astria.execution.v1alpha2.ExecuteBlock` with the result of step 9. -11. Call `astria.execution.v1alpha2.UpdateCommitmentState` with the result of - step 10, specifically updating the tracked commitment state - `C.firm.number == C.soft.number += 1`[^2] and `C.base_celestia_height = H`, + 5. Decompress the result of 4. as brotli, decode as protobuf + `astria.sequencerblock.v1.SubmittedRollupDataList`. + 6. Match pairs `P = (metadata, rollup_data)` found in the previous steps + using `rollup_data.block_hash` and `metadata.block_hash`. +8. Get that pair `P` with metadata sequencer height matching the next expected + firm Sequencer height `M == F` (as determined in step 5). If it exists, go to + step 9. If no such pair exists, exit. +9. Call `astria.execution.v2.ExecuteBlock` with the result of step 9. +10. Call `astria.execution.v2.UpdateCommitmentState` with the result of + step 9, specifically updating the tracked commitment state + `C.firm.number == C.soft.number += 1`[^2] and `C.lowest_celestia_search_height = H`, with `H` the source Celestia height of the just executed pair `P`. -12. Go to step 6. +11. Go to step 5. [Celestia v0 namespace]: https://celestiaorg.github.io/celestia-app/specs/namespace.html#version-0 [^1]: It is assumed that on average 6 Sequencer heights will fit into 1 @@ -119,15 +115,15 @@ exception of the execution and update-commitment steps: If the soft commitment is ahead of firm, `CommitmentState.soft.number > CommitmentState.firm.number`, then step -`firm-only.10` is skipped (i.e. the data is not executed against the rollup), -but only step `firm-only.11` is ran *without updating the soft number (i.e. +`firm-only.9` is skipped (i.e. the data is not executed against the rollup), +but only step `firm-only.10` is ran *without updating the soft number (i.e. only `CommitmentState.firm.number += 1` is advanced). Soft being ahead of firm is the expected operation. In certain rare situations -the numbers can match exactly, and step `firm-only.10` and `firm-only.11` are +the numbers can match exactly, and step `firm-only.9` and `firm-only.10` are executed as written. ## Startup, Restarts, Execution, and Commitments -See [`astria.execution.v1alpha2` API documentation](./execution-api.md) for more +See [`astria.execution.v2` API documentation](./execution-api.md) for more information on Conductor startup, restart, execution, and commitment logic. From 5819f9baa9ee958210df5a75d9daff998dddc27a Mon Sep 17 00:00:00 2001 From: ethanoroshiba Date: Thu, 27 Feb 2025 14:51:02 -0600 Subject: [PATCH 3/6] update charts, changelog, and spec --- charts/evm-rollup/Chart.yaml | 2 +- charts/evm-rollup/values.yaml | 2 +- crates/astria-conductor/CHANGELOG.md | 4 ++-- specs/conductor.md | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/charts/evm-rollup/Chart.yaml b/charts/evm-rollup/Chart.yaml index 8f4274e665..452adfd05e 100644 --- a/charts/evm-rollup/Chart.yaml +++ b/charts/evm-rollup/Chart.yaml @@ -21,7 +21,7 @@ version: 1.1.2 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "1.0.2" +appVersion: "1.1.0" maintainers: - name: wafflesvonmaple diff --git a/charts/evm-rollup/values.yaml b/charts/evm-rollup/values.yaml index 5dd32105a9..b7f6f701b5 100644 --- a/charts/evm-rollup/values.yaml +++ b/charts/evm-rollup/values.yaml @@ -16,7 +16,7 @@ images: conductor: repo: ghcr.io/astriaorg/conductor pullPolicy: IfNotPresent - tag: 1.0.1 + tag: 1.0.0 devTag: latest snapshot: repo: rclone/rclone diff --git a/crates/astria-conductor/CHANGELOG.md b/crates/astria-conductor/CHANGELOG.md index 6e9294a3b2..e31d3dd595 100644 --- a/crates/astria-conductor/CHANGELOG.md +++ b/crates/astria-conductor/CHANGELOG.md @@ -13,8 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Update `idna` dependency to resolve cargo audit warning [#1869](https://github.com/astriaorg/astria/pull/1869). - Remove panic source on shutdown [#1919](https://github.com/astriaorg/astria/pull/1919). -- Add stop height logic, remove chain id env vars, accomodate new genesis info -shape [#1928](https://github.com/astriaorg/astria/pull/1928). +- Bump to v2 Execution API, using execution sessions and removing chain id env +vars [#2006](https://github.com/astriaorg/astria/pull/2006). ## [1.0.0] - 2024-10-25 diff --git a/specs/conductor.md b/specs/conductor.md index 81464a4ae5..43438850f4 100644 --- a/specs/conductor.md +++ b/specs/conductor.md @@ -96,7 +96,8 @@ are gRPC): 9. Call `astria.execution.v2.ExecuteBlock` with the result of step 9. 10. Call `astria.execution.v2.UpdateCommitmentState` with the result of step 9, specifically updating the tracked commitment state - `C.firm.number == C.soft.number += 1`[^2] and `C.lowest_celestia_search_height = H`, + `C.firm.number == C.soft.number += 1`[^2] and `C.lowest_celestia_search_height + = H`, with `H` the source Celestia height of the just executed pair `P`. 11. Go to step 5. From 93d0ebe8fa523c26256c04f4d8bf7b8cdcdb4e94 Mon Sep 17 00:00:00 2001 From: ethanoroshiba Date: Thu, 27 Feb 2025 15:08:06 -0600 Subject: [PATCH 4/6] bump chart versions --- charts/evm-rollup/Chart.yaml | 2 +- charts/evm-stack/Chart.lock | 6 +++--- charts/evm-stack/Chart.yaml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/charts/evm-rollup/Chart.yaml b/charts/evm-rollup/Chart.yaml index 452adfd05e..d266d88086 100644 --- a/charts/evm-rollup/Chart.yaml +++ b/charts/evm-rollup/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.1.2 +version: 1.1.3 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/evm-stack/Chart.lock b/charts/evm-stack/Chart.lock index a8e13b2518..3181331c69 100644 --- a/charts/evm-stack/Chart.lock +++ b/charts/evm-stack/Chart.lock @@ -4,7 +4,7 @@ dependencies: version: 0.4.0 - name: evm-rollup repository: file://../evm-rollup - version: 1.1.2 + version: 1.1.3 - name: flame-rollup repository: file://../flame-rollup version: 0.0.2 @@ -26,5 +26,5 @@ dependencies: - name: blockscout-stack repository: https://blockscout.github.io/helm-charts version: 1.6.8 -digest: sha256:ec55e7e1427dd7af6b3764d7cedc7b5b168ec2443fa140cb3000c8ed68d711ac -generated: "2025-02-20T10:44:04.460576+02:00" +digest: sha256:85329bcb82e89b366cb2e9c64e7b74207c7f2b9fb0a479b8f1a6f3a3123b91b5 +generated: "2025-02-27T15:06:50.890389-06:00" diff --git a/charts/evm-stack/Chart.yaml b/charts/evm-stack/Chart.yaml index 640f1f9658..59db76012b 100644 --- a/charts/evm-stack/Chart.yaml +++ b/charts/evm-stack/Chart.yaml @@ -15,14 +15,14 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.0.12 +version: 1.0.13 dependencies: - name: celestia-node version: 0.4.0 repository: "file://../celestia-node" condition: celestia-node.enabled - name: evm-rollup - version: 1.1.2 + version: 1.1.3 repository: "file://../evm-rollup" condition: evm-rollup.enabled - name: flame-rollup From 8dfc8931f1e1420718f09be87b3db829828061ad Mon Sep 17 00:00:00 2001 From: ethanoroshiba Date: Thu, 27 Feb 2025 15:12:15 -0600 Subject: [PATCH 5/6] revert execution api spec changes --- specs/execution-api.md | 41 +++-------------------------------------- 1 file changed, 3 insertions(+), 38 deletions(-) diff --git a/specs/execution-api.md b/specs/execution-api.md index 752134bb75..c765229007 100644 --- a/specs/execution-api.md +++ b/specs/execution-api.md @@ -29,32 +29,6 @@ previous block data, Conductor must also track the block hash of any blocks between commitments, it will call `BatchGetBlocks` to get block information between commitments. -### Restart - -The conductor is able to gracefully restart under two scenarios: - -1. Conductor recieves a `PermissionDenied` status from the execution layer when -calling `ExecuteBlock`. This is meant to function seamlessly with [`astria-geth`](https://github.com/astriaorg/astria-geth)'s -behavior upon an unexpected restart. If `geth` receives a `ExecuteBlock` request -before receving *both* `GetGenesisInfo` and `GetCommitmentState` (which will be -the case if the execution layer has restarted), it responds with a `PremissionDenied` -status, prompting the conductor to restart. -2. `GenesisInfo` contains a `rollup_stop_block_number` and has a `halt_at_rollup_stop_number` -value of `false`, indicating a planned restart after execution of the given block. -Once the conductor reaches the stop height, it will perform a restart in one of the -following ways corresponding to its mode: - - - **Firm Only Mode**: Once the stop height is reached for the firm block stream, - the firm block at this height is executed (and commitment state updated) before - restarting the conductor, prompting the rollup for a new `GenesisInfo` with - new start/stop heights (and potentially chain IDs). - - **Soft and Firm Mode**: Once the stop height is reached for the soft block - stream, the block at this height will be executed. The conductor will then wait - for the firm block at the stop height, execute it, and restart. If the firm - block is executed first, conductor will restart immediately. - - **Soft Only Mode**: Once the stop height is reached for the soft block stream, - the block at this height is executed and Conductor restarts. - ### Execution & Commitments From the perspective of the sequencer: @@ -100,18 +74,9 @@ Note: For our EVM rollup, we map the `CommitmentState` to the `ForkchoiceRule`: ### GetGenesisInfo `GetGenesisInfo` returns information which is definitional to the rollup with -regards to how it serves data from the sequencer & celestia networks, along with -optional block heights for initiating a [conductor restart](#restart). This RPC -should ***always*** succeed. The API is agnostic as to how the information is defined -in a rollup's genesis, and used by the conductor as configuration on startup *or* -upon a restart. - -If the `GenesisInfo` provided by this RPC contains a `rollup_stop_block_number`, -the rollup should be prepared to provide an updated response when the conductor -restarts, including, at minimum, a new `rollup_start_block_number` and `sequencer_start_height`. -The updated response can also contain an updated `rollup_stop_block_number` (if -another restart is desired), `celestia_chain_id`, and/or `sequencer_chain_id` -(to facilitate network migration). +regards to how it serves data from the sequencer & celestia networks. This RPC +should ALWAYS succeed. The API is agnostic as to how the information is defined +in a rollups genesis, and used by the conductor as configuration on startup. ### ExecuteBlock From 13399973021d8022ce48da8cd1b017780b05247f Mon Sep 17 00:00:00 2001 From: ethanoroshiba Date: Tue, 4 Mar 2025 13:59:41 -0600 Subject: [PATCH 6/6] fix tests and conductor logic --- .../files/genesis/geth-genesis.json | 4 +- charts/evm-rollup/values.yaml | 8 +- .../astria-conductor/src/conductor/inner.rs | 6 +- crates/astria-conductor/src/executor/mod.rs | 25 ++-- crates/astria-conductor/src/executor/state.rs | 38 ++--- crates/astria-conductor/src/executor/tests.rs | 18 +-- crates/astria-conductor/src/test_utils.rs | 23 ++- .../tests/blackbox/soft_and_firm.rs | 136 ------------------ dev/values/rollup/dev.yaml | 8 +- dev/values/rollup/evm-restart-test.yaml | 6 +- dev/values/rollup/ibc-bridge-test.yaml | 8 +- 11 files changed, 60 insertions(+), 220 deletions(-) diff --git a/charts/evm-rollup/files/genesis/geth-genesis.json b/charts/evm-rollup/files/genesis/geth-genesis.json index c77b1c3259..d733a5038b 100644 --- a/charts/evm-rollup/files/genesis/geth-genesis.json +++ b/charts/evm-rollup/files/genesis/geth-genesis.json @@ -95,8 +95,8 @@ {{- $celestiaFields = append $celestiaFields (printf "\"startHeight\": %s" (toString .celestia.startHeight | replace "\"" "")) }} {{- end }} - {{- if .celestia.heightVariance }} - {{- $celestiaFields = append $celestiaFields (printf "\"heightVariance\": %s" (toString .celestia.heightVariance | replace "\"" "")) }} + {{- if .celestia.searchHeightMaxLookAhead }} + {{- $celestiaFields = append $celestiaFields (printf "\"searchHeightMaxLookAhead\": %s" (toString .celestia.searchHeightMaxLookAhead | replace "\"" "")) }} {{- end }} {{- if $celestiaFields | len }} diff --git a/charts/evm-rollup/values.yaml b/charts/evm-rollup/values.yaml index b7f6f701b5..3050cecb78 100644 --- a/charts/evm-rollup/values.yaml +++ b/charts/evm-rollup/values.yaml @@ -12,7 +12,7 @@ images: pullPolicy: IfNotPresent tag: 1.0.0 devTag: pr-59 - overrideTag: "" + overrideTag: pr-59 conductor: repo: ghcr.io/astriaorg/conductor pullPolicy: IfNotPresent @@ -63,13 +63,13 @@ genesis: chainId: "" # The first Celestia height to utilize when looking for rollup data startHeight: "" - # The variance in Celestia height to allow before halting the chain - heightVariance: "" + # The maximum number of blocks ahead of the lowest Celestia search height + # to search for a firm commitment + searchHeightMaxLookAhead: "" # Configure the sequencer bridge addresses and allowed assets if using # the astria canonical bridge. Recommend removing alloc values if so. bridgeAddresses: [] # - address: "684ae50c49a434199199c9c698115391152d7b3f" - # startHeight: 1 # assetDenom: "nria" # senderAddress: "0x0000000000000000000000000000000000000000" # assetPrecision: 9 diff --git a/crates/astria-conductor/src/conductor/inner.rs b/crates/astria-conductor/src/conductor/inner.rs index 96856d16c5..ba593270d3 100644 --- a/crates/astria-conductor/src/conductor/inner.rs +++ b/crates/astria-conductor/src/conductor/inner.rs @@ -164,10 +164,7 @@ fn should_restart_despite_error(err: &eyre::Report) -> bool { let mut current = Some(err.as_ref() as &dyn std::error::Error); while let Some(err) = current { if let Some(status) = err.downcast_ref::() { - if status.code() == tonic::Code::PermissionDenied - // Fallback in case execute block is called outside of execution session bounds - || status.code() == tonic::Code::OutOfRange - { + if status.code() == tonic::Code::PermissionDenied { return true; } } @@ -283,7 +280,6 @@ mod tests { #[test] fn should_restart_despite_error() { should_restart_despite_error_test(tonic::Code::PermissionDenied); - should_restart_despite_error_test(tonic::Code::OutOfRange); } #[track_caller] diff --git a/crates/astria-conductor/src/executor/mod.rs b/crates/astria-conductor/src/executor/mod.rs index feef682114..1a786f5200 100644 --- a/crates/astria-conductor/src/executor/mod.rs +++ b/crates/astria-conductor/src/executor/mod.rs @@ -134,8 +134,14 @@ impl Executor { let reader_cancellation_token = self.shutdown.child_token(); let (firm_blocks_tx, firm_blocks_rx) = tokio::sync::mpsc::channel(16); - let (soft_blocks_tx, soft_blocks_rx) = - tokio::sync::mpsc::channel(state.calculate_max_spread()); + let (soft_blocks_tx, soft_blocks_rx) = tokio::sync::mpsc::channel( + state + .celestia_search_height_max_look_ahead() + .try_into() + .expect( + "converting a u64 to usize should work on any architecture conductor runs on", + ), + ); let mut reader_tasks = JoinMap::new(); if self.config.is_with_firm() { @@ -199,17 +205,15 @@ impl Executor { .clone() .create_execution_session_with_retry() .await - .wrap_err("failed getting genesis info")?; + .wrap_err("failed creating execution session")?; let (state, _) = state::channel( - State::try_from_execution_session_parameters_and_commitment_state( - execution_session.session_id().clone(), - execution_session.execution_session_parameters().clone(), - execution_session.commitment_state().clone(), + State::try_from_execution_session( + &execution_session, self.config.execution_commit_level, ) .wrap_err( - "failed to construct initial state gensis and commitment info received from rollup", + "failed to construct initial state using execution session received from rollup", )?, ); @@ -326,9 +330,8 @@ impl Initialized { (next_firm, next_soft) }; - let is_too_far_ahead = usize::try_from(next_soft.saturating_sub(next_firm)) - .map(|spread| spread >= self.state.calculate_max_spread()) - .unwrap_or(false); + let is_too_far_ahead = next_soft.saturating_sub(next_firm) + >= self.state.celestia_search_height_max_look_ahead(); if is_too_far_ahead { debug!("soft blocks are too far ahead of firm; skipping soft blocks"); diff --git a/crates/astria-conductor/src/executor/state.rs b/crates/astria-conductor/src/executor/state.rs index 43d79e45a6..d03b6706d4 100644 --- a/crates/astria-conductor/src/executor/state.rs +++ b/crates/astria-conductor/src/executor/state.rs @@ -8,6 +8,7 @@ use astria_core::{ execution::v2::{ CommitmentState, ExecutedBlockMetadata, + ExecutionSession, ExecutionSessionParameters, }, primitive::v1::RollupId, @@ -164,25 +165,6 @@ impl StateSender { } } - /// Calculates the maximum allowed spread between firm and soft commitments heights. - /// - /// The maximum allowed spread is taken as `max_spread = max_look_ahead * 3`, where - /// `max_look_ahead` is the `celestia_search_height_max_look_ahead` as defined in the - /// current execution session. - /// - /// The heuristic 3 is the largest number of Sequencer heights that will be found at - /// one Celestia height. - /// - /// # Panics - /// Panics if the `u64` underlying the celestia search height max look ahead tracked in the - /// state could not be converted to a `usize`. This should never happen on any reasonable - /// architecture that Conductor will run on. - pub(super) fn calculate_max_spread(&self) -> usize { - usize::try_from(self.celestia_search_height_max_look_ahead()) - .expect("converting a u64 to usize should work on any architecture conductor runs on") - .saturating_mul(3) - } - pub(super) fn try_update_commitment_state( &mut self, commitment_state: CommitmentState, @@ -283,22 +265,22 @@ pub(crate) struct State { } impl State { - pub(crate) fn try_from_execution_session_parameters_and_commitment_state( - execution_session_id: String, - execution_session_parameters: ExecutionSessionParameters, - commitment_state: CommitmentState, + pub(crate) fn try_from_execution_session( + execution_session: &ExecutionSession, commit_level: crate::config::CommitLevel, ) -> Result { + let execution_session_parameters = execution_session.execution_session_parameters(); + let commitment_state = execution_session.commitment_state(); if commit_level.is_with_firm() { - let _ = map_firm_to_sequencer_height(&execution_session_parameters, &commitment_state)?; + let _ = map_firm_to_sequencer_height(execution_session_parameters, commitment_state)?; } if commit_level.is_with_soft() { - let _ = map_soft_to_sequencer_height(&execution_session_parameters, &commitment_state)?; + let _ = map_soft_to_sequencer_height(execution_session_parameters, commitment_state)?; } Ok(State { - execution_session_id, - execution_session_parameters, - commitment_state, + execution_session_id: execution_session.session_id().clone(), + execution_session_parameters: execution_session_parameters.clone(), + commitment_state: commitment_state.clone(), }) } diff --git a/crates/astria-conductor/src/executor/tests.rs b/crates/astria-conductor/src/executor/tests.rs index 8b55f9968f..12e9a6e3ef 100644 --- a/crates/astria-conductor/src/executor/tests.rs +++ b/crates/astria-conductor/src/executor/tests.rs @@ -1,9 +1,8 @@ use astria_core::{ self, execution::v2::{ - CommitmentState, ExecutedBlockMetadata, - ExecutionSessionParameters, + ExecutionSession, }, generated::astria::execution::v2 as raw, Protobuf as _, @@ -45,18 +44,19 @@ fn make_state( soft, }: MakeState, ) -> (StateSender, StateReceiver) { - let execution_session_parameters = - ExecutionSessionParameters::try_from_raw(make_execution_session_parameters()).unwrap(); - let commitment_state = CommitmentState::try_from_raw(raw::CommitmentState { + let commitment_state = raw::CommitmentState { firm_executed_block_metadata: Some(make_block_metadata(firm)), soft_executed_block_metadata: Some(make_block_metadata(soft)), lowest_celestia_search_height: 1, + }; + let execution_session = ExecutionSession::try_from_raw(raw::ExecutionSession { + session_id: "test_execution_session".to_string(), + execution_session_parameters: Some(make_execution_session_parameters()), + commitment_state: Some(commitment_state), }) .unwrap(); - let state = State::try_from_execution_session_parameters_and_commitment_state( - "test_execution_session".to_string(), - execution_session_parameters, - commitment_state, + let state = State::try_from_execution_session( + &execution_session, crate::config::CommitLevel::SoftAndFirm, ) .unwrap(); diff --git a/crates/astria-conductor/src/test_utils.rs b/crates/astria-conductor/src/test_utils.rs index dadd2a7aa3..9e81d25c6a 100644 --- a/crates/astria-conductor/src/test_utils.rs +++ b/crates/astria-conductor/src/test_utils.rs @@ -1,4 +1,5 @@ use astria_core::{ + execution::v2::ExecutionSession, generated::astria::execution::v2::{ CommitmentState, ExecutionSessionParameters, @@ -54,18 +55,14 @@ pub(crate) fn make_rollup_state( execution_session_parameters: ExecutionSessionParameters, commitment_state: CommitmentState, ) -> State { - let execution_session_parameters = - astria_core::execution::v2::ExecutionSessionParameters::try_from_raw( - execution_session_parameters, - ) - .unwrap(); - let commitment_state = - astria_core::execution::v2::CommitmentState::try_from_raw(commitment_state).unwrap(); - State::try_from_execution_session_parameters_and_commitment_state( - execution_session_id, - execution_session_parameters, - commitment_state, - crate::config::CommitLevel::SoftAndFirm, + let execution_session = ExecutionSession::try_from_raw( + astria_core::generated::astria::execution::v2::ExecutionSession { + session_id: execution_session_id, + execution_session_parameters: Some(execution_session_parameters), + commitment_state: Some(commitment_state), + }, ) - .unwrap() + .unwrap(); + State::try_from_execution_session(&execution_session, crate::config::CommitLevel::SoftAndFirm) + .unwrap() } diff --git a/crates/astria-conductor/tests/blackbox/soft_and_firm.rs b/crates/astria-conductor/tests/blackbox/soft_and_firm.rs index 2386331246..00d0a75ea9 100644 --- a/crates/astria-conductor/tests/blackbox/soft_and_firm.rs +++ b/crates/astria-conductor/tests/blackbox/soft_and_firm.rs @@ -1031,139 +1031,3 @@ async fn restarts_after_reaching_firm_stop_height_first() { .await .expect("conductor should have updated the firm commitment state within 1000ms"); } - -/// Tests if conductor restarts internal services if rollup response with `OUT_OF_RANGE`. -/// -/// Astria Geth will return an `OutOfRange` status if `execute_block` is called for a block outside -/// of the bounds of the given session. If this happens, Conductor should restart and attempt to -/// create a new session. -#[expect( - clippy::too_many_lines, - reason = "all lines fairly necessary, and I don't think a test warrants a refactor" -)] -#[tokio::test(flavor = "multi_thread", worker_threads = 1)] -async fn restarts_on_out_of_range() { - let test_conductor = spawn_conductor(CommitLevel::SoftAndFirm).await; - - mount_create_execution_session!( - test_conductor, - execution_session_parameters: ( - rollup_start_block_number: 2, - rollup_end_block_number: 9, - sequencer_start_block_height: 3, - celestia_max_look_ahead: 10, - ), - commitment_state: ( - firm: ( - number: 1, - hash: "1", - parent: "0", - ), - soft: ( - number: 1, - hash: "1", - parent: "0", - ), - lowest_celestia_search_height: 1, - ), - expected_calls: 2, - up_to_n_times: 2, - ); - - mount_sequencer_genesis!(test_conductor); - - mount_celestia_header_network_head!( - test_conductor, - height: 1u32, - ); - - mount_celestia_blobs!( - test_conductor, - celestia_height: 1, - sequencer_heights: [3], - delay: Some(Duration::from_millis(250)) - ); - - mount_sequencer_commit!( - test_conductor, - height: 3u32, - ); - - mount_sequencer_validator_set!(test_conductor, height: 2u32); - - mount_abci_info!( - test_conductor, - latest_sequencer_height: 3, - ); - - mount_get_filtered_sequencer_block!( - test_conductor, - sequencer_height: 3, - ); - - // mount tonic `OutOfRange` error to cause the conductor to restart. - // This mock can only be called up to 1 time, allowing a normal `execute_block` call after. - let execute_block_tonic_code = mount_execute_block_tonic_code!( - test_conductor, - parent: "1", - status_code: tonic::Code::OutOfRange, - ); - - timeout( - Duration::from_millis(1000), - execute_block_tonic_code.wait_until_satisfied(), - ) - .await - .expect("conductor should have restarted after a permission denied error within 1000ms"); - - let execute_block = mount_execute_block!( - test_conductor, - number: 2, - hash: "2", - parent: "1", - ); - - let update_commitment_state_soft = mount_update_commitment_state!( - test_conductor, - firm: ( - number: 1, - hash: "1", - parent: "0", - ), - soft: ( - number: 2, - hash: "2", - parent: "1", - ), - lowest_celestia_search_height: 1, - ); - - let update_commitment_state_firm = mount_update_commitment_state!( - test_conductor, - firm: ( - number: 2, - hash: "2", - parent: "1", - ), - soft: ( - number: 2, - hash: "2", - parent: "1", - ), - lowest_celestia_search_height: 1, - ); - - timeout( - Duration::from_millis(1000), - join3( - execute_block.wait_until_satisfied(), - update_commitment_state_soft.wait_until_satisfied(), - update_commitment_state_firm.wait_until_satisfied(), - ), - ) - .await - .expect( - "conductor should have executed the block and updated the soft and firm commitment states \ - within 1000ms", - ); -} diff --git a/dev/values/rollup/dev.yaml b/dev/values/rollup/dev.yaml index 4e4d2e1ce6..99ae70c1e7 100644 --- a/dev/values/rollup/dev.yaml +++ b/dev/values/rollup/dev.yaml @@ -15,7 +15,7 @@ evm-rollup: # The "forks" for upgrading the chain. Contains necessary information for starting # and, if desired, restarting the chain at a given height. The necessary fields - # for the genesis fork are provided, and additional forks can be added as needed. + # for the first fork are provided, and additional forks can be added as needed. forks: ## These values are used to configure the genesis block of the rollup chain ## no defaults as they are unique to each chain @@ -37,13 +37,13 @@ evm-rollup: chainId: "celestia-local-0" # The first Celestia height to utilize when looking for rollup data startHeight: 2 - # The variance in Celestia height to allow before halting the chain - heightVariance: 10 + # The maximum number of blocks ahead of the lowest Celestia search height + # to search for a firm commitment + searchHeightMaxLookAhead: 50 # Configure the sequencer bridge addresses and allowed assets if using # the astria canonical bridge. Recommend removing alloc values if so. bridgeAddresses: - bridgeAddress: "astria13ahqz4pjqfmynk9ylrqv4fwe4957x2p0h5782u" - startHeight: 1 senderAddress: "0x0000000000000000000000000000000000000000" assetDenom: "nria" assetPrecision: 9 diff --git a/dev/values/rollup/evm-restart-test.yaml b/dev/values/rollup/evm-restart-test.yaml index e78414938f..77535b16ea 100644 --- a/dev/values/rollup/evm-restart-test.yaml +++ b/dev/values/rollup/evm-restart-test.yaml @@ -37,13 +37,13 @@ evm-rollup: chainId: "celestia-local-0" # The first Celestia height to utilize when looking for rollup data startHeight: 2 - # The variance in Celestia height to allow before halting the chain - heightVariance: 10 + # The maximum number of blocks ahead of the lowest Celestia search height + # to search for a firm commitment + searchHeightMaxLookAhead: 50 # Configure the sequencer bridge addresses and allowed assets if using # the astria canonical bridge. Recommend removing alloc values if so. bridgeAddresses: - bridgeAddress: "astria13ahqz4pjqfmynk9ylrqv4fwe4957x2p0h5782u" - startHeight: 1 senderAddress: "0x0000000000000000000000000000000000000000" assetDenom: "nria" assetPrecision: 9 diff --git a/dev/values/rollup/ibc-bridge-test.yaml b/dev/values/rollup/ibc-bridge-test.yaml index 83f8e17577..d9bfb95479 100644 --- a/dev/values/rollup/ibc-bridge-test.yaml +++ b/dev/values/rollup/ibc-bridge-test.yaml @@ -28,18 +28,16 @@ evm-rollup: addressPrefix: "astria" # Block height to start syncing rollup from (inclusive), lowest possible is 2 startHeight: 2 - # Block height (on sequencer) to stop syncing rollup at, continuing to next configuration fork - stopHeight: 0 celestia: # The chain id of the celestia chain chainId: "celestia-local-0" # The first Celestia height to utilize when looking for rollup data startHeight: 2 - # The variance in Celestia height to allow before halting the chain - heightVariance: 10 + # The maximum number of blocks ahead of the lowest Celestia search height + # to search for a firm commitment + searchHeightMaxLookAhead: 100 bridgeAddresses: - bridgeAddress: "astria1d7zjjljc0dsmxa545xkpwxym86g8uvvwhtezcr" - startHeight: 1 assetDenom: "transfer/channel-0/utia" assetPrecision: 6