Commit 846cec96 authored by smartcontracts's avatar smartcontracts Committed by GitHub

feat: update op-e2e for FPAC (#9366)

parent 9a71fed9
...@@ -178,7 +178,13 @@ jobs: ...@@ -178,7 +178,13 @@ jobs:
FOUNDRY_PROFILE: ci FOUNDRY_PROFILE: ci
command: pnpm build command: pnpm build
- run: - run:
name: Generate allocs name: Generate FPAC allocs
command: DEVNET_FPAC="true" make devnet-allocs
- run:
name: Copy FPAC allocs to .devnet-fpac
command: cp -r .devnet/ .devnet-fault-proofs/
- run:
name: Generate non-FPAC allocs
command: make devnet-allocs command: make devnet-allocs
- persist_to_workspace: - persist_to_workspace:
root: "." root: "."
...@@ -191,6 +197,8 @@ jobs: ...@@ -191,6 +197,8 @@ jobs:
- "packages/contracts-bedrock/tsconfig.build.tsbuildinfo" - "packages/contracts-bedrock/tsconfig.build.tsbuildinfo"
- ".devnet/allocs-l1.json" - ".devnet/allocs-l1.json"
- ".devnet/addresses.json" - ".devnet/addresses.json"
- ".devnet-fault-proofs/allocs-l1.json"
- ".devnet-fault-proofs/addresses.json"
- "packages/contracts-bedrock/deploy-config/devnetL1.json" - "packages/contracts-bedrock/deploy-config/devnetL1.json"
- "packages/contracts-bedrock/deployments/devnetL1" - "packages/contracts-bedrock/deployments/devnetL1"
...@@ -799,6 +807,9 @@ jobs: ...@@ -799,6 +807,9 @@ jobs:
go-e2e-test: go-e2e-test:
parameters: parameters:
fpac:
type: string
default: ''
module: module:
description: Go Module Name description: Go Module Name
type: string type: string
...@@ -813,6 +824,9 @@ jobs: ...@@ -813,6 +824,9 @@ jobs:
description: Whether to notify on failure description: Whether to notify on failure
type: boolean type: boolean
default: false default: false
environment:
DEVNET_FPAC: 'false'
OP_E2E_USE_FPAC: 'false'
docker: docker:
- image: <<pipeline.parameters.ci_builder_image>> - image: <<pipeline.parameters.ci_builder_image>>
resource_class: xlarge resource_class: xlarge
...@@ -824,6 +838,16 @@ jobs: ...@@ -824,6 +838,16 @@ jobs:
parallelism: <<parameters.parallelism>> parallelism: <<parameters.parallelism>>
steps: steps:
- checkout - checkout
- when:
condition:
equal: ['-fault-proofs', <<parameters.fpac>>]
steps:
- run:
name: Set DEVNET_FPAC = true
command: echo 'export DEVNET_FPAC=true' >> $BASH_ENV
- run:
name: Set OP_E2E_USE_FPAC = true
command: echo 'export OP_E2E_USE_FPAC=true' >> $BASH_ENV
- check-changed: - check-changed:
patterns: op-(.+),cannon,contracts-bedrock patterns: op-(.+),cannon,contracts-bedrock
- run: - run:
...@@ -841,8 +865,8 @@ jobs: ...@@ -841,8 +865,8 @@ jobs:
name: Load devnet-allocs name: Load devnet-allocs
command: | command: |
mkdir -p .devnet mkdir -p .devnet
cp /tmp/workspace/.devnet/allocs-l1.json .devnet/allocs-l1.json cp /tmp/workspace/.devnet<<parameters.fpac>>/allocs-l1.json .devnet/allocs-l1.json
cp /tmp/workspace/.devnet/addresses.json .devnet/addresses.json cp /tmp/workspace/.devnet<<parameters.fpac>>/addresses.json .devnet/addresses.json
cp /tmp/workspace/packages/contracts-bedrock/deploy-config/devnetL1.json packages/contracts-bedrock/deploy-config/devnetL1.json cp /tmp/workspace/packages/contracts-bedrock/deploy-config/devnetL1.json packages/contracts-bedrock/deploy-config/devnetL1.json
cp -r /tmp/workspace/packages/contracts-bedrock/deployments/devnetL1 packages/contracts-bedrock/deployments/devnetL1 cp -r /tmp/workspace/packages/contracts-bedrock/deployments/devnetL1 packages/contracts-bedrock/deployments/devnetL1
- run: - run:
...@@ -917,12 +941,29 @@ jobs: ...@@ -917,12 +941,29 @@ jobs:
working_directory: <<parameters.working_directory>> working_directory: <<parameters.working_directory>>
indexer-tests: indexer-tests:
parameters:
fpac:
type: string
default: ''
environment:
DEVNET_FPAC: 'false'
OP_E2E_USE_FPAC: 'false'
docker: docker:
- image: <<pipeline.parameters.ci_builder_image>> - image: <<pipeline.parameters.ci_builder_image>>
- image: cimg/postgres:14.1 - image: cimg/postgres:14.1
resource_class: xlarge resource_class: xlarge
steps: steps:
- checkout - checkout
- when:
condition:
equal: ['fault-proofs', <<parameters.fpac>>]
steps:
- run:
name: Set DEVNET_FPAC = true
command: echo 'export DEVNET_FPAC=true' >> $BASH_ENV
- run:
name: Set OP_E2E_USE_FPAC = true
command: echo 'export OP_E2E_USE_FPAC=true' >> $BASH_ENV
- check-changed: - check-changed:
patterns: indexer patterns: indexer
- run: - run:
...@@ -1021,7 +1062,7 @@ jobs: ...@@ -1021,7 +1062,7 @@ jobs:
machine: machine:
image: <<pipeline.parameters.base_image>> image: <<pipeline.parameters.base_image>>
parameters: parameters:
devnet_fpac: fpac:
type: string type: string
environment: environment:
DOCKER_BUILDKIT: 1 DOCKER_BUILDKIT: 1
...@@ -1032,7 +1073,7 @@ jobs: ...@@ -1032,7 +1073,7 @@ jobs:
- checkout - checkout
- when: - when:
condition: condition:
equal: ['fault-proofs', <<parameters.devnet_fpac>>] equal: ['-fault-proofs', <<parameters.fpac>>]
steps: steps:
- run: - run:
name: Set DEVNET_FPAC = true name: Set DEVNET_FPAC = true
...@@ -1460,7 +1501,11 @@ workflows: ...@@ -1460,7 +1501,11 @@ workflows:
name: proxyd-tests name: proxyd-tests
binary_name: proxyd binary_name: proxyd
working_directory: proxyd working_directory: proxyd
- indexer-tests - indexer-tests:
name: indexer-tests<< matrix.fpac >>
matrix:
parameters:
fpac: ["", "-fault-proofs"]
- semgrep-scan - semgrep-scan
- go-mod-download - go-mod-download
- fuzz-golang: - fuzz-golang:
...@@ -1533,7 +1578,10 @@ workflows: ...@@ -1533,7 +1578,10 @@ workflows:
module: op-service module: op-service
requires: ["go-mod-download"] requires: ["go-mod-download"]
- go-e2e-test: - go-e2e-test:
name: op-e2e-HTTP-tests name: op-e2e-HTTP-tests<< matrix.fpac >>
matrix:
parameters:
fpac: ["", "-fault-proofs"]
module: op-e2e module: op-e2e
target: test-http target: test-http
parallelism: 4 parallelism: 4
...@@ -1541,7 +1589,10 @@ workflows: ...@@ -1541,7 +1589,10 @@ workflows:
- go-mod-download - go-mod-download
- pnpm-monorepo - pnpm-monorepo
- go-e2e-test: - go-e2e-test:
name: op-e2e-action-tests name: op-e2e-action-tests<< matrix.fpac >>
matrix:
parameters:
fpac: ["", "-fault-proofs"]
module: op-e2e module: op-e2e
target: test-actions target: test-actions
parallelism: 1 parallelism: 1
...@@ -1557,7 +1608,10 @@ workflows: ...@@ -1557,7 +1608,10 @@ workflows:
- pnpm-monorepo - pnpm-monorepo
- cannon-prestate - cannon-prestate
- go-e2e-test: - go-e2e-test:
name: op-e2e-ext-geth-tests name: op-e2e-ext-geth-tests<< matrix.fpac >>
matrix:
parameters:
fpac: ["", "-fault-proofs"]
module: op-e2e module: op-e2e
target: test-external-geth target: test-external-geth
requires: requires:
...@@ -1649,7 +1703,7 @@ workflows: ...@@ -1649,7 +1703,7 @@ workflows:
- devnet: - devnet:
matrix: matrix:
parameters: parameters:
devnet_fpac: ["legacy", "fault-proofs"] fpac: ["legacy", "fault-proofs"]
requires: requires:
- pnpm-monorepo - pnpm-monorepo
- op-batcher-docker-build - op-batcher-docker-build
......
...@@ -31,8 +31,9 @@ type L1Contracts struct { ...@@ -31,8 +31,9 @@ type L1Contracts struct {
SystemConfigProxy common.Address `toml:"system-config"` SystemConfigProxy common.Address `toml:"system-config"`
// rollup state // rollup state
OptimismPortalProxy common.Address `toml:"optimism-portal"` OptimismPortalProxy common.Address `toml:"optimism-portal"`
L2OutputOracleProxy common.Address `toml:"l2-output-oracle"` L2OutputOracleProxy common.Address `toml:"l2-output-oracle"`
DisputeGameFactoryProxy common.Address `toml:"dispute-game-factory"`
// bridging // bridging
L1CrossDomainMessengerProxy common.Address `toml:"l1-cross-domain-messenger"` L1CrossDomainMessengerProxy common.Address `toml:"l1-cross-domain-messenger"`
......
...@@ -98,6 +98,7 @@ func TestLoadConfigWithoutPreset(t *testing.T) { ...@@ -98,6 +98,7 @@ func TestLoadConfigWithoutPreset(t *testing.T) {
l2-output-oracle = "0x42097868233d1aa22e815a266982f2cf17685a27" l2-output-oracle = "0x42097868233d1aa22e815a266982f2cf17685a27"
l1-cross-domain-messenger = "0x420ce71c97B33Cc4729CF772ae268934F7ab5fA1" l1-cross-domain-messenger = "0x420ce71c97B33Cc4729CF772ae268934F7ab5fA1"
l1-standard-bridge = "0x4209fc46f92E8a1c0deC1b1747d010903E884bE1" l1-standard-bridge = "0x4209fc46f92E8a1c0deC1b1747d010903E884bE1"
dispute-game-factory = "0x4209fc46f92E8a1c0deC1b1747d010903E884bE1"
[rpcs] [rpcs]
l1-rpc = "https://l1.example.com" l1-rpc = "https://l1.example.com"
......
...@@ -25,6 +25,7 @@ var Presets = map[int]Preset{ ...@@ -25,6 +25,7 @@ var Presets = map[int]Preset{
L1CrossDomainMessengerProxy: common.HexToAddress("0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1"), L1CrossDomainMessengerProxy: common.HexToAddress("0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1"),
L1StandardBridgeProxy: common.HexToAddress("0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1"), L1StandardBridgeProxy: common.HexToAddress("0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1"),
L1ERC721BridgeProxy: common.HexToAddress("0x5a7749f83b81B301cAb5f48EB8516B986DAef23D"), L1ERC721BridgeProxy: common.HexToAddress("0x5a7749f83b81B301cAb5f48EB8516B986DAef23D"),
DisputeGameFactoryProxy: common.HexToAddress("0x1111111111111111111111111111111111111111"),
// pre-bedrock // pre-bedrock
LegacyCanonicalTransactionChain: common.HexToAddress("0x5e4e65926ba27467555eb562121fac00d24e9dd2"), LegacyCanonicalTransactionChain: common.HexToAddress("0x5e4e65926ba27467555eb562121fac00d24e9dd2"),
...@@ -49,6 +50,7 @@ var Presets = map[int]Preset{ ...@@ -49,6 +50,7 @@ var Presets = map[int]Preset{
L1CrossDomainMessengerProxy: common.HexToAddress("0x5086d1eEF304eb5284A0f6720f79403b4e9bE294"), L1CrossDomainMessengerProxy: common.HexToAddress("0x5086d1eEF304eb5284A0f6720f79403b4e9bE294"),
L1StandardBridgeProxy: common.HexToAddress("0x636Af16bf2f682dD3109e60102b8E1A089FedAa8"), L1StandardBridgeProxy: common.HexToAddress("0x636Af16bf2f682dD3109e60102b8E1A089FedAa8"),
L1ERC721BridgeProxy: common.HexToAddress("0x8DD330DdE8D9898d43b4dc840Da27A07dF91b3c9"), L1ERC721BridgeProxy: common.HexToAddress("0x8DD330DdE8D9898d43b4dc840Da27A07dF91b3c9"),
DisputeGameFactoryProxy: common.HexToAddress("0x1111111111111111111111111111111111111111"),
// pre-bedrock // pre-bedrock
LegacyCanonicalTransactionChain: common.HexToAddress("0x607F755149cFEB3a14E1Dc3A4E2450Cde7dfb04D"), LegacyCanonicalTransactionChain: common.HexToAddress("0x607F755149cFEB3a14E1Dc3A4E2450Cde7dfb04D"),
...@@ -73,6 +75,7 @@ var Presets = map[int]Preset{ ...@@ -73,6 +75,7 @@ var Presets = map[int]Preset{
L1CrossDomainMessengerProxy: common.HexToAddress("0x58Cc85b8D04EA49cC6DBd3CbFFd00B4B8D6cb3ef"), L1CrossDomainMessengerProxy: common.HexToAddress("0x58Cc85b8D04EA49cC6DBd3CbFFd00B4B8D6cb3ef"),
L1StandardBridgeProxy: common.HexToAddress("0xFBb0621E0B23b5478B630BD55a5f21f67730B0F1"), L1StandardBridgeProxy: common.HexToAddress("0xFBb0621E0B23b5478B630BD55a5f21f67730B0F1"),
L1ERC721BridgeProxy: common.HexToAddress("0xd83e03D576d23C9AEab8cC44Fa98d058D2176D1f"), L1ERC721BridgeProxy: common.HexToAddress("0xd83e03D576d23C9AEab8cC44Fa98d058D2176D1f"),
DisputeGameFactoryProxy: common.HexToAddress("0x1111111111111111111111111111111111111111"),
}, },
L1StartingHeight: 4071408, L1StartingHeight: 4071408,
L1ConfirmationDepth: 10, L1ConfirmationDepth: 10,
...@@ -91,6 +94,7 @@ var Presets = map[int]Preset{ ...@@ -91,6 +94,7 @@ var Presets = map[int]Preset{
L1CrossDomainMessengerProxy: common.HexToAddress("0x866E82a600A1414e583f7F13623F1aC5d58b0Afa"), L1CrossDomainMessengerProxy: common.HexToAddress("0x866E82a600A1414e583f7F13623F1aC5d58b0Afa"),
L1StandardBridgeProxy: common.HexToAddress("0x3154Cf16ccdb4C6d922629664174b904d80F2C35"), L1StandardBridgeProxy: common.HexToAddress("0x3154Cf16ccdb4C6d922629664174b904d80F2C35"),
L1ERC721BridgeProxy: common.HexToAddress("0x608d94945A64503E642E6370Ec598e519a2C1E53"), L1ERC721BridgeProxy: common.HexToAddress("0x608d94945A64503E642E6370Ec598e519a2C1E53"),
DisputeGameFactoryProxy: common.HexToAddress("0x1111111111111111111111111111111111111111"),
}, },
L1StartingHeight: 17481768, L1StartingHeight: 17481768,
L1ConfirmationDepth: 10, L1ConfirmationDepth: 10,
...@@ -109,6 +113,7 @@ var Presets = map[int]Preset{ ...@@ -109,6 +113,7 @@ var Presets = map[int]Preset{
L1CrossDomainMessengerProxy: common.HexToAddress("0x8e5693140eA606bcEB98761d9beB1BC87383706D"), L1CrossDomainMessengerProxy: common.HexToAddress("0x8e5693140eA606bcEB98761d9beB1BC87383706D"),
L1StandardBridgeProxy: common.HexToAddress("0xfA6D8Ee5BE770F84FC001D098C4bD604Fe01284a"), L1StandardBridgeProxy: common.HexToAddress("0xfA6D8Ee5BE770F84FC001D098C4bD604Fe01284a"),
L1ERC721BridgeProxy: common.HexToAddress("0x5E0c967457347D5175bF82E8CCCC6480FCD7e568"), L1ERC721BridgeProxy: common.HexToAddress("0x5E0c967457347D5175bF82E8CCCC6480FCD7e568"),
DisputeGameFactoryProxy: common.HexToAddress("0x1111111111111111111111111111111111111111"),
}, },
L1StartingHeight: 8410981, L1StartingHeight: 8410981,
L1ConfirmationDepth: 10, L1ConfirmationDepth: 10,
...@@ -127,6 +132,7 @@ var Presets = map[int]Preset{ ...@@ -127,6 +132,7 @@ var Presets = map[int]Preset{
L1CrossDomainMessengerProxy: common.HexToAddress("0xC34855F4De64F1840e5686e64278da901e261f20"), L1CrossDomainMessengerProxy: common.HexToAddress("0xC34855F4De64F1840e5686e64278da901e261f20"),
L1StandardBridgeProxy: common.HexToAddress("0xfd0Bf71F60660E2f608ed56e1659C450eB113120"), L1StandardBridgeProxy: common.HexToAddress("0xfd0Bf71F60660E2f608ed56e1659C450eB113120"),
L1ERC721BridgeProxy: common.HexToAddress("0x21eFD066e581FA55Ef105170Cc04d74386a09190"), L1ERC721BridgeProxy: common.HexToAddress("0x21eFD066e581FA55Ef105170Cc04d74386a09190"),
DisputeGameFactoryProxy: common.HexToAddress("0x1111111111111111111111111111111111111111"),
}, },
L1StartingHeight: 4370868, L1StartingHeight: 4370868,
L1ConfirmationDepth: 10, L1ConfirmationDepth: 10,
...@@ -145,6 +151,7 @@ var Presets = map[int]Preset{ ...@@ -145,6 +151,7 @@ var Presets = map[int]Preset{
L1CrossDomainMessengerProxy: common.HexToAddress("0xdC40a14d9abd6F410226f1E6de71aE03441ca506"), L1CrossDomainMessengerProxy: common.HexToAddress("0xdC40a14d9abd6F410226f1E6de71aE03441ca506"),
L1StandardBridgeProxy: common.HexToAddress("0x3e2Ea9B92B7E48A52296fD261dc26fd995284631"), L1StandardBridgeProxy: common.HexToAddress("0x3e2Ea9B92B7E48A52296fD261dc26fd995284631"),
L1ERC721BridgeProxy: common.HexToAddress("0x83A4521A3573Ca87f3a971B169C5A0E1d34481c3"), L1ERC721BridgeProxy: common.HexToAddress("0x83A4521A3573Ca87f3a971B169C5A0E1d34481c3"),
DisputeGameFactoryProxy: common.HexToAddress("0x1111111111111111111111111111111111111111"),
}, },
L1StartingHeight: 17473923, L1StartingHeight: 17473923,
L1ConfirmationDepth: 10, L1ConfirmationDepth: 10,
...@@ -163,6 +170,7 @@ var Presets = map[int]Preset{ ...@@ -163,6 +170,7 @@ var Presets = map[int]Preset{
L1CrossDomainMessengerProxy: common.HexToAddress("0xD87342e16352D33170557A7dA1e5fB966a60FafC"), L1CrossDomainMessengerProxy: common.HexToAddress("0xD87342e16352D33170557A7dA1e5fB966a60FafC"),
L1StandardBridgeProxy: common.HexToAddress("0x7CC09AC2452D6555d5e0C213Ab9E2d44eFbFc956"), L1StandardBridgeProxy: common.HexToAddress("0x7CC09AC2452D6555d5e0C213Ab9E2d44eFbFc956"),
L1ERC721BridgeProxy: common.HexToAddress("0x57C1C6b596ce90C0e010c358DD4Aa052404bB70F"), L1ERC721BridgeProxy: common.HexToAddress("0x57C1C6b596ce90C0e010c358DD4Aa052404bB70F"),
DisputeGameFactoryProxy: common.HexToAddress("0x1111111111111111111111111111111111111111"),
}, },
L1StartingHeight: 8942381, L1StartingHeight: 8942381,
L1ConfirmationDepth: 10, L1ConfirmationDepth: 10,
...@@ -181,6 +189,7 @@ var Presets = map[int]Preset{ ...@@ -181,6 +189,7 @@ var Presets = map[int]Preset{
L1CrossDomainMessengerProxy: common.HexToAddress("0x97BAf688E5d0465E149d1d5B497Ca99392a6760e"), L1CrossDomainMessengerProxy: common.HexToAddress("0x97BAf688E5d0465E149d1d5B497Ca99392a6760e"),
L1StandardBridgeProxy: common.HexToAddress("0xD0204B9527C1bA7bD765Fa5CCD9355d38338272b"), L1StandardBridgeProxy: common.HexToAddress("0xD0204B9527C1bA7bD765Fa5CCD9355d38338272b"),
L1ERC721BridgeProxy: common.HexToAddress("0xaFF0F8aaB6Cc9108D34b3B8423C76d2AF434d115"), L1ERC721BridgeProxy: common.HexToAddress("0xaFF0F8aaB6Cc9108D34b3B8423C76d2AF434d115"),
DisputeGameFactoryProxy: common.HexToAddress("0x1111111111111111111111111111111111111111"),
}, },
L1StartingHeight: 17672702, L1StartingHeight: 17672702,
L1ConfirmationDepth: 10, L1ConfirmationDepth: 10,
...@@ -199,6 +208,7 @@ var Presets = map[int]Preset{ ...@@ -199,6 +208,7 @@ var Presets = map[int]Preset{
L1CrossDomainMessengerProxy: common.HexToAddress("0x97f3558Ce48FE71B8CeFA5497708A49531D5A8E1"), L1CrossDomainMessengerProxy: common.HexToAddress("0x97f3558Ce48FE71B8CeFA5497708A49531D5A8E1"),
L1StandardBridgeProxy: common.HexToAddress("0xFaE6abCAF30D23e233AC7faF747F2fC3a5a6Bfa3"), L1StandardBridgeProxy: common.HexToAddress("0xFaE6abCAF30D23e233AC7faF747F2fC3a5a6Bfa3"),
L1ERC721BridgeProxy: common.HexToAddress("0xBA8397B6f255618D5985d0fB427D8c0496F3a5FA"), L1ERC721BridgeProxy: common.HexToAddress("0xBA8397B6f255618D5985d0fB427D8c0496F3a5FA"),
DisputeGameFactoryProxy: common.HexToAddress("0x1111111111111111111111111111111111111111"),
}, },
L1StartingHeight: 17672702, L1StartingHeight: 17672702,
L1ConfirmationDepth: 10, L1ConfirmationDepth: 10,
......
...@@ -169,7 +169,7 @@ func TestE2EBridgeL2CrossDomainMessenger(t *testing.T) { ...@@ -169,7 +169,7 @@ func TestE2EBridgeL2CrossDomainMessenger(t *testing.T) {
// (2) Process RelayedMessage on withdrawal finalization // (2) Process RelayedMessage on withdrawal finalization
require.Nil(t, sentMessage.RelayedMessageEventGUID) require.Nil(t, sentMessage.RelayedMessageEventGUID)
_, finalizedReceipt := op_e2e.ProveAndFinalizeWithdrawal(t, *testSuite.OpCfg, testSuite.OpSys, "sequencer", testSuite.OpCfg.Secrets.Alice, sentMsgReceipt) _, finalizedReceipt, _, _ := op_e2e.ProveAndFinalizeWithdrawal(t, *testSuite.OpCfg, testSuite.OpSys, "sequencer", testSuite.OpCfg.Secrets.Alice, sentMsgReceipt)
// wait for processor catchup // wait for processor catchup
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) { require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
......
...@@ -163,7 +163,7 @@ func TestE2EBridgeTransactionsL2ToL1MessagePasserWithdrawal(t *testing.T) { ...@@ -163,7 +163,7 @@ func TestE2EBridgeTransactionsL2ToL1MessagePasserWithdrawal(t *testing.T) {
// Test Withdrawal Finalized // Test Withdrawal Finalized
require.Nil(t, withdraw.FinalizedL1EventGUID) require.Nil(t, withdraw.FinalizedL1EventGUID)
finalizeReceipt := op_e2e.FinalizeWithdrawal(t, *testSuite.OpCfg, testSuite.L1Client, testSuite.OpCfg.Secrets.Alice, proveReceipt, withdrawParams) finalizeReceipt, _, _ := op_e2e.FinalizeWithdrawal(t, *testSuite.OpCfg, testSuite.L1Client, testSuite.OpCfg.Secrets.Alice, proveReceipt, withdrawParams)
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) { require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
l1Header := testSuite.Indexer.BridgeProcessor.LastFinalizedL1Header l1Header := testSuite.Indexer.BridgeProcessor.LastFinalizedL1Header
return l1Header != nil && l1Header.Number.Uint64() >= finalizeReceipt.BlockNumber.Uint64(), nil return l1Header != nil && l1Header.Number.Uint64() >= finalizeReceipt.BlockNumber.Uint64(), nil
...@@ -207,7 +207,7 @@ func TestE2EBridgeTransactionsL2ToL1MessagePasserFailedWithdrawal(t *testing.T) ...@@ -207,7 +207,7 @@ func TestE2EBridgeTransactionsL2ToL1MessagePasserFailedWithdrawal(t *testing.T)
require.NoError(t, err) require.NoError(t, err)
// Prove&Finalize withdrawal // Prove&Finalize withdrawal
_, finalizeReceipt := op_e2e.ProveAndFinalizeWithdrawal(t, *testSuite.OpCfg, testSuite.OpSys, "sequencer", testSuite.OpCfg.Secrets.Alice, withdrawReceipt) _, finalizeReceipt, _, _ := op_e2e.ProveAndFinalizeWithdrawal(t, *testSuite.OpCfg, testSuite.OpSys, "sequencer", testSuite.OpCfg.Secrets.Alice, withdrawReceipt)
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) { require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
l1Header := testSuite.Indexer.BridgeProcessor.LastFinalizedL1Header l1Header := testSuite.Indexer.BridgeProcessor.LastFinalizedL1Header
return l1Header != nil && l1Header.Number.Uint64() >= finalizeReceipt.BlockNumber.Uint64(), nil return l1Header != nil && l1Header.Number.Uint64() >= finalizeReceipt.BlockNumber.Uint64(), nil
......
...@@ -300,7 +300,7 @@ func TestE2EBridgeTransfersStandardBridgeETHWithdrawal(t *testing.T) { ...@@ -300,7 +300,7 @@ func TestE2EBridgeTransfersStandardBridgeETHWithdrawal(t *testing.T) {
require.Empty(t, aliceWithdrawals.Withdrawals[0].FinalizedL1TransactionHash) require.Empty(t, aliceWithdrawals.Withdrawals[0].FinalizedL1TransactionHash)
// wait for processor catchup // wait for processor catchup
proveReceipt, finalizeReceipt := op_e2e.ProveAndFinalizeWithdrawal(t, *testSuite.OpCfg, testSuite.OpSys, "sequencer", testSuite.OpCfg.Secrets.Alice, withdrawReceipt) proveReceipt, finalizeReceipt, _, _ := op_e2e.ProveAndFinalizeWithdrawal(t, *testSuite.OpCfg, testSuite.OpSys, "sequencer", testSuite.OpCfg.Secrets.Alice, withdrawReceipt)
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) { require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
l1Header := testSuite.Indexer.BridgeProcessor.LastFinalizedL1Header l1Header := testSuite.Indexer.BridgeProcessor.LastFinalizedL1Header
return l1Header != nil && l1Header.Number.Uint64() >= finalizeReceipt.BlockNumber.Uint64(), nil return l1Header != nil && l1Header.Number.Uint64() >= finalizeReceipt.BlockNumber.Uint64(), nil
...@@ -388,7 +388,7 @@ func TestE2EBridgeTransfersL2ToL1MessagePasserETHReceive(t *testing.T) { ...@@ -388,7 +388,7 @@ func TestE2EBridgeTransfersL2ToL1MessagePasserETHReceive(t *testing.T) {
require.Empty(t, aliceWithdrawals.Withdrawals[0].FinalizedL1TransactionHash) require.Empty(t, aliceWithdrawals.Withdrawals[0].FinalizedL1TransactionHash)
// wait for processor catchup // wait for processor catchup
proveReceipt, finalizeReceipt := op_e2e.ProveAndFinalizeWithdrawal(t, *testSuite.OpCfg, testSuite.OpSys, "sequencer", testSuite.OpCfg.Secrets.Alice, l2ToL1WithdrawReceipt) proveReceipt, finalizeReceipt, _, _ := op_e2e.ProveAndFinalizeWithdrawal(t, *testSuite.OpCfg, testSuite.OpSys, "sequencer", testSuite.OpCfg.Secrets.Alice, l2ToL1WithdrawReceipt)
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) { require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
l1Header := testSuite.Indexer.BridgeProcessor.LastFinalizedL1Header l1Header := testSuite.Indexer.BridgeProcessor.LastFinalizedL1Header
return l1Header != nil && l1Header.Number.Uint64() >= finalizeReceipt.BlockNumber.Uint64(), nil return l1Header != nil && l1Header.Number.Uint64() >= finalizeReceipt.BlockNumber.Uint64(), nil
...@@ -601,7 +601,7 @@ func TestClientBridgeFunctions(t *testing.T) { ...@@ -601,7 +601,7 @@ func TestClientBridgeFunctions(t *testing.T) {
s.proven = new(big.Int).Add(s.proven, actor.amt) s.proven = new(big.Int).Add(s.proven, actor.amt)
finalReceipt := op_e2e.FinalizeWithdrawal(t, *testSuite.OpCfg, testSuite.L1Client, actor.priv, proveReceipt, params) finalReceipt, _, _ := op_e2e.FinalizeWithdrawal(t, *testSuite.OpCfg, testSuite.L1Client, actor.priv, proveReceipt, params)
require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) { require.NoError(t, wait.For(context.Background(), 500*time.Millisecond, func() (bool, error) {
l1Header := testSuite.Indexer.BridgeProcessor.LastFinalizedL1Header l1Header := testSuite.Indexer.BridgeProcessor.LastFinalizedL1Header
seen := l1Header != nil && l1Header.Number.Uint64() >= finalReceipt.BlockNumber.Uint64() seen := l1Header != nil && l1Header.Number.Uint64() >= finalReceipt.BlockNumber.Uint64()
......
...@@ -8,7 +8,10 @@ import ( ...@@ -8,7 +8,10 @@ import (
"github.com/ethereum-optimism/optimism/indexer/database" "github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/bindingspreview"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
"github.com/ethereum-optimism/optimism/op-node/withdrawals"
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
...@@ -24,9 +27,29 @@ func TestE2EETL(t *testing.T) { ...@@ -24,9 +27,29 @@ func TestE2EETL(t *testing.T) {
l2OutputOracle, err := bindings.NewL2OutputOracle(testSuite.OpCfg.L1Deployments.L2OutputOracleProxy, testSuite.L1Client) l2OutputOracle, err := bindings.NewL2OutputOracle(testSuite.OpCfg.L1Deployments.L2OutputOracleProxy, testSuite.L1Client)
require.NoError(t, err) require.NoError(t, err)
disputeGameFactory, err := bindings.NewDisputeGameFactoryCaller(testSuite.OpCfg.L1Deployments.DisputeGameFactoryProxy, testSuite.L1Client)
require.NoError(t, err)
optimismPortal, err := bindingspreview.NewOptimismPortal2Caller(testSuite.OpCfg.L1Deployments.OptimismPortalProxy, testSuite.L1Client)
require.NoError(t, err)
// wait for at least 10 L2 blocks posted on L1 // wait for at least 10 L2 blocks posted on L1
require.NoError(t, wait.For(context.Background(), time.Second, func() (bool, error) { require.NoError(t, wait.For(context.Background(), time.Second, func() (bool, error) {
l2Height, err := l2OutputOracle.LatestBlockNumber(&bind.CallOpts{Context: context.Background()}) var l2Height *big.Int
var err error
if e2eutils.UseFPAC() {
gameCount, err := disputeGameFactory.GameCount(&bind.CallOpts{Context: context.Background()})
require.NoError(t, err)
if gameCount.Cmp(big.NewInt(0)) == 0 {
return false, nil
}
latestGame, err := withdrawals.FindLatestGame(context.Background(), disputeGameFactory, optimismPortal)
require.NoError(t, err)
l2Height = new(big.Int).SetBytes(latestGame.ExtraData[0:32])
} else {
l2Height, err = l2OutputOracle.LatestBlockNumber(&bind.CallOpts{Context: context.Background()})
}
return l2Height != nil && l2Height.Uint64() >= 9, err return l2Height != nil && l2Height.Uint64() >= 9, err
})) }))
......
...@@ -107,6 +107,7 @@ func createE2ETestSuite(t *testing.T) E2ETestSuite { ...@@ -107,6 +107,7 @@ func createE2ETestSuite(t *testing.T) E2ETestSuite {
L1CrossDomainMessengerProxy: opCfg.L1Deployments.L1CrossDomainMessengerProxy, L1CrossDomainMessengerProxy: opCfg.L1Deployments.L1CrossDomainMessengerProxy,
L1StandardBridgeProxy: opCfg.L1Deployments.L1StandardBridgeProxy, L1StandardBridgeProxy: opCfg.L1Deployments.L1StandardBridgeProxy,
L1ERC721BridgeProxy: opCfg.L1Deployments.L1ERC721BridgeProxy, L1ERC721BridgeProxy: opCfg.L1Deployments.L1ERC721BridgeProxy,
DisputeGameFactoryProxy: opCfg.L1Deployments.DisputeGameFactoryProxy,
}, },
}, },
HTTPServer: config.ServerConfig{Host: "127.0.0.1", Port: 0}, HTTPServer: config.ServerConfig{Host: "127.0.0.1", Port: 0},
......
SHELL := /usr/bin/env bash SHELL := /usr/bin/env bash
pkg := bindings pkg := bindings
pkg-preview := bindingspreview
monorepo-base := $(shell dirname $(realpath .)) monorepo-base := $(shell dirname $(realpath .))
contracts-dir := $(monorepo-base)/packages/contracts-bedrock contracts-dir := $(monorepo-base)/packages/contracts-bedrock
contracts-list := ./artifacts.json contracts-list := ./artifacts.json
contracts-list-preview := ./artifacts-preview.json
log-level := info log-level := info
ETHERSCAN_APIKEY_ETH ?= ETHERSCAN_APIKEY_ETH ?=
ETHERSCAN_APIKEY_OP ?= ETHERSCAN_APIKEY_OP ?=
...@@ -21,9 +23,9 @@ compile: ...@@ -21,9 +23,9 @@ compile:
forge clean && \ forge clean && \
pnpm build pnpm build
bindings: bindgen-local bindings: bindgen-local bindgen-preview
bindings-build: bindgen-generate-local bindings-build: bindgen-generate-local bindgen-generate-preview
bindgen: compile bindgen-generate-all bindgen: compile bindgen-generate-all
...@@ -53,6 +55,18 @@ bindgen-generate-local: ...@@ -53,6 +55,18 @@ bindgen-generate-local:
local \ local \
--forge-artifacts $(contracts-dir)/forge-artifacts --forge-artifacts $(contracts-dir)/forge-artifacts
bindgen-preview: compile bindgen-generate-preview
bindgen-generate-preview:
go run ./cmd \
generate \
--metadata-out ./$(pkg-preview) \
--bindings-package $(pkg-preview) \
--contracts-list $(contracts-list-preview) \
--log.level $(log-level) \
local \
--forge-artifacts $(contracts-dir)/forge-artifacts
bindgen-remote: bindgen-remote:
go run ./cmd/ \ go run ./cmd/ \
generate \ generate \
......
{
"local": [
"OptimismPortal2"
],
"remote": []
}
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
package bindingspreview
import (
"fmt"
"strings"
"github.com/ethereum-optimism/superchain-registry/superchain"
"github.com/ethereum-optimism/optimism/op-bindings/solc"
"github.com/ethereum/go-ethereum/common"
)
// layouts respresents the set of storage layouts. It is populated in an init function.
var layouts = make(map[string]*solc.StorageLayout)
// deployedBytecodes represents the set of deployed bytecodes. It is populated
// in an init function.
var deployedBytecodes = make(map[string]string)
var initBytecodes = make(map[string]string)
var deploymentSalts = make(map[string]string)
var deployers = make(map[string]string)
// immutableReferences represents the set of immutable references. It is populated
// in an init function.
var immutableReferences = make(map[string]bool)
// Create2DeployerCodeHash represents the codehash of the Create2Deployer contract.
var Create2DeployerCodeHash = common.HexToHash("0xb0550b5b431e30d38000efb7107aaa0ade03d48a7198a140edda9d27134468b2")
func init() {
code, err := superchain.LoadContractBytecode(superchain.Hash(Create2DeployerCodeHash))
if err != nil {
panic(err)
}
deployedBytecodes["Create2Deployer"] = common.Bytes2Hex(code)
}
// GetStorageLayout returns the storage layout of a contract by name.
func GetStorageLayout(name string) (*solc.StorageLayout, error) {
layout := layouts[name]
if layout == nil {
return nil, fmt.Errorf("%s: storage layout not found", name)
}
return layout, nil
}
// GetDeployedBytecode returns the deployed bytecode of a contract by name.
func GetDeployedBytecode(name string) ([]byte, error) {
bc := deployedBytecodes[name]
if bc == "" {
return nil, fmt.Errorf("%s: deployed bytecode not found", name)
}
if !isHex(bc) {
return nil, fmt.Errorf("%s: invalid deployed bytecode", name)
}
return common.FromHex(bc), nil
}
// HasImmutableReferences returns the immutable references of a contract by name.
func HasImmutableReferences(name string) (bool, error) {
has, ok := immutableReferences[name]
if !ok {
return false, fmt.Errorf("%s: immutable reference not found", name)
}
return has, nil
}
func GetInitBytecode(name string) ([]byte, error) {
bc := initBytecodes[name]
if bc == "" {
return nil, fmt.Errorf("%s: init bytecode not found", name)
}
if !isHex(bc) {
return nil, fmt.Errorf("%s: invalid init bytecode", name)
}
return common.FromHex(bc), nil
}
func GetDeployerAddress(name string) ([]byte, error) {
addr := deployers[name]
if addr == "" {
return nil, fmt.Errorf("%s: deployer address not found", name)
}
if !common.IsHexAddress(addr) {
return nil, fmt.Errorf("%s: invalid deployer address", name)
}
return common.FromHex(addr), nil
}
func GetDeploymentSalt(name string) ([]byte, error) {
salt := deploymentSalts[name]
if salt == "" {
return nil, fmt.Errorf("%s: deployment salt not found", name)
}
if !isHex(salt) {
return nil, fmt.Errorf("%s: invalid deployment salt", name)
}
return common.FromHex(salt), nil
}
// isHexCharacter returns bool of c being a valid hexadecimal.
func isHexCharacter(c byte) bool {
return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F')
}
// isHex validates whether each byte is valid hexadecimal string.
func isHex(str string) bool {
if len(str)%2 != 0 {
return false
}
str = strings.TrimPrefix(str, "0x")
for _, c := range []byte(str) {
if !isHexCharacter(c) {
return false
}
}
return true
}
...@@ -18,9 +18,11 @@ import ( ...@@ -18,9 +18,11 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-proposer/metrics" "github.com/ethereum-optimism/optimism/op-proposer/metrics"
"github.com/ethereum-optimism/optimism/op-proposer/proposer" "github.com/ethereum-optimism/optimism/op-proposer/proposer"
"github.com/ethereum-optimism/optimism/op-service/dial" "github.com/ethereum-optimism/optimism/op-service/dial"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/sources" "github.com/ethereum-optimism/optimism/op-service/sources"
"github.com/ethereum-optimism/optimism/op-service/txmgr" "github.com/ethereum-optimism/optimism/op-service/txmgr"
) )
...@@ -35,14 +37,16 @@ type ProposerCfg struct { ...@@ -35,14 +37,16 @@ type ProposerCfg struct {
} }
type L2Proposer struct { type L2Proposer struct {
log log.Logger log log.Logger
l1 *ethclient.Client l1 *ethclient.Client
driver *proposer.L2OutputSubmitter driver *proposer.L2OutputSubmitter
contract *bindings.L2OutputOracleCaller l2OutputOracle *bindings.L2OutputOracleCaller
address common.Address l2OutputOracleAddr *common.Address
privKey *ecdsa.PrivateKey disputeGameFactory *bindings.DisputeGameFactoryCaller
contractAddr common.Address disputeGameFactoryAddr *common.Address
lastTx common.Hash address common.Address
privKey *ecdsa.PrivateKey
lastTx common.Hash
} }
type fakeTxMgr struct { type fakeTxMgr struct {
...@@ -85,28 +89,34 @@ func NewL2Proposer(t Testing, log log.Logger, cfg *ProposerCfg, l1 *ethclient.Cl ...@@ -85,28 +89,34 @@ func NewL2Proposer(t Testing, log log.Logger, cfg *ProposerCfg, l1 *ethclient.Cl
RollupProvider: rollupProvider, RollupProvider: rollupProvider,
} }
if cfg.OutputOracleAddr == nil {
panic("L2OutputOracle address must be set in op-e2e test harness. The DisputeGameFactory is not yet supported as a proposal destination.")
}
dr, err := proposer.NewL2OutputSubmitter(driverSetup) dr, err := proposer.NewL2OutputSubmitter(driverSetup)
require.NoError(t, err) require.NoError(t, err)
contract, err := bindings.NewL2OutputOracleCaller(*cfg.OutputOracleAddr, l1)
require.NoError(t, err)
address := crypto.PubkeyToAddress(cfg.ProposerKey.PublicKey) address := crypto.PubkeyToAddress(cfg.ProposerKey.PublicKey)
proposer, err := contract.PROPOSER(&bind.CallOpts{})
require.NoError(t, err) var l2OutputOracle *bindings.L2OutputOracleCaller
require.Equal(t, proposer, address, "PROPOSER must be the proposer's address") var disputeGameFactory *bindings.DisputeGameFactoryCaller
if e2eutils.UseFPAC() {
disputeGameFactory, err = bindings.NewDisputeGameFactoryCaller(*cfg.DisputeGameFactoryAddr, l1)
require.NoError(t, err)
} else {
l2OutputOracle, err := bindings.NewL2OutputOracleCaller(*cfg.OutputOracleAddr, l1)
require.NoError(t, err)
proposer, err := l2OutputOracle.PROPOSER(&bind.CallOpts{})
require.NoError(t, err)
require.Equal(t, proposer, address, "PROPOSER must be the proposer's address")
}
return &L2Proposer{ return &L2Proposer{
log: log, log: log,
l1: l1, l1: l1,
driver: dr, driver: dr,
contract: contract, l2OutputOracle: l2OutputOracle,
address: address, l2OutputOracleAddr: cfg.OutputOracleAddr,
privKey: cfg.ProposerKey, disputeGameFactory: disputeGameFactory,
contractAddr: *cfg.OutputOracleAddr, disputeGameFactoryAddr: cfg.DisputeGameFactoryAddr,
address: address,
privKey: cfg.ProposerKey,
} }
} }
...@@ -122,9 +132,16 @@ func (p *L2Proposer) sendTx(t Testing, data []byte) { ...@@ -122,9 +132,16 @@ func (p *L2Proposer) sendTx(t Testing, data []byte) {
nonce, err := p.l1.NonceAt(t.Ctx(), p.address, nil) nonce, err := p.l1.NonceAt(t.Ctx(), p.address, nil)
require.NoError(t, err) require.NoError(t, err)
var addr common.Address
if e2eutils.UseFPAC() {
addr = *p.disputeGameFactoryAddr
} else {
addr = *p.l2OutputOracleAddr
}
gasLimit, err := estimateGasPending(t.Ctx(), p.l1, ethereum.CallMsg{ gasLimit, err := estimateGasPending(t.Ctx(), p.l1, ethereum.CallMsg{
From: p.address, From: p.address,
To: &p.contractAddr, To: &addr,
GasFeeCap: gasFeeCap, GasFeeCap: gasFeeCap,
GasTipCap: gasTipCap, GasTipCap: gasTipCap,
Data: data, Data: data,
...@@ -133,7 +150,7 @@ func (p *L2Proposer) sendTx(t Testing, data []byte) { ...@@ -133,7 +150,7 @@ func (p *L2Proposer) sendTx(t Testing, data []byte) {
rawTx := &types.DynamicFeeTx{ rawTx := &types.DynamicFeeTx{
Nonce: nonce, Nonce: nonce,
To: &p.contractAddr, To: &addr,
Data: data, Data: data,
GasFeeCap: gasFeeCap, GasFeeCap: gasFeeCap,
GasTipCap: gasTipCap, GasTipCap: gasTipCap,
...@@ -145,7 +162,6 @@ func (p *L2Proposer) sendTx(t Testing, data []byte) { ...@@ -145,7 +162,6 @@ func (p *L2Proposer) sendTx(t Testing, data []byte) {
require.NoError(t, err, "need to sign tx") require.NoError(t, err, "need to sign tx")
err = p.l1.SendTransaction(t.Ctx(), tx) err = p.l1.SendTransaction(t.Ctx(), tx)
log.Info("Proposer sent tx", "hash", tx.Hash(), "to", p.contractAddr)
require.NoError(t, err, "need to send tx") require.NoError(t, err, "need to send tx")
p.lastTx = tx.Hash() p.lastTx = tx.Hash()
...@@ -184,21 +200,56 @@ func toCallArg(msg ethereum.CallMsg) interface{} { ...@@ -184,21 +200,56 @@ func toCallArg(msg ethereum.CallMsg) interface{} {
return arg return arg
} }
func (p *L2Proposer) fetchNextOutput(t Testing) (*eth.OutputResponse, bool, error) {
if e2eutils.UseFPAC() {
blockNumber, err := p.driver.FetchCurrentBlockNumber(t.Ctx())
if err != nil {
return nil, false, err
}
output, _, err := p.driver.FetchOutput(t.Ctx(), blockNumber)
if err != nil {
return nil, false, err
}
encodedBlockNumber := make([]byte, 32)
copy(encodedBlockNumber[32-len(blockNumber.Bytes()):], blockNumber.Bytes())
game, err := p.disputeGameFactory.Games(&bind.CallOpts{}, p.driver.Cfg.DisputeGameType, output.OutputRoot, encodedBlockNumber)
if err != nil {
return nil, false, err
}
if game.Timestamp != 0 {
return nil, false, nil
}
return output, true, nil
} else {
return p.driver.FetchNextOutputInfo(t.Ctx())
}
}
func (p *L2Proposer) CanPropose(t Testing) bool { func (p *L2Proposer) CanPropose(t Testing) bool {
_, shouldPropose, err := p.driver.FetchNextOutputInfo(t.Ctx()) _, shouldPropose, err := p.fetchNextOutput(t)
require.NoError(t, err) require.NoError(t, err)
return shouldPropose return shouldPropose
} }
func (p *L2Proposer) ActMakeProposalTx(t Testing) { func (p *L2Proposer) ActMakeProposalTx(t Testing) {
output, shouldPropose, err := p.driver.FetchNextOutputInfo(t.Ctx()) output, shouldPropose, err := p.fetchNextOutput(t)
require.NoError(t, err)
if !shouldPropose { if !shouldPropose {
return return
} }
require.NoError(t, err)
txData, err := p.driver.ProposeL2OutputTxData(output) var txData []byte
require.NoError(t, err) if e2eutils.UseFPAC() {
txData, _, err = p.driver.ProposeL2OutputDGFTxData(output)
require.NoError(t, err)
} else {
txData, err = p.driver.ProposeL2OutputTxData(output)
require.NoError(t, err)
}
// Note: Use L1 instead of the output submitter's transaction manager because // Note: Use L1 instead of the output submitter's transaction manager because
// this is non-blocking while the txmgr is blocking & deadlocks the tests // this is non-blocking while the txmgr is blocking & deadlocks the tests
......
package actions package actions
import ( import (
"math/big"
"testing" "testing"
"time"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/bindingspreview"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum-optimism/optimism/op-service/testlog"
...@@ -51,11 +55,22 @@ func RunProposerTest(gt *testing.T, deltaTimeOffset *hexutil.Uint64) { ...@@ -51,11 +55,22 @@ func RunProposerTest(gt *testing.T, deltaTimeOffset *hexutil.Uint64) {
batcher := NewL2Batcher(log, sd.RollupCfg, DefaultBatcherCfg(dp), batcher := NewL2Batcher(log, sd.RollupCfg, DefaultBatcherCfg(dp),
rollupSeqCl, miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg)) rollupSeqCl, miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg))
proposer := NewL2Proposer(t, log, &ProposerCfg{ var proposer *L2Proposer
OutputOracleAddr: &sd.DeploymentsL1.L2OutputOracleProxy, if e2eutils.UseFPAC() {
ProposerKey: dp.Secrets.Proposer, proposer = NewL2Proposer(t, log, &ProposerCfg{
AllowNonFinalized: false, DisputeGameFactoryAddr: &sd.DeploymentsL1.DisputeGameFactoryProxy,
}, miner.EthClient(), sequencer.RollupClient()) ProposalInterval: 6 * time.Second,
DisputeGameType: 0,
ProposerKey: dp.Secrets.Proposer,
AllowNonFinalized: true,
}, miner.EthClient(), rollupSeqCl)
} else {
proposer = NewL2Proposer(t, log, &ProposerCfg{
OutputOracleAddr: &sd.DeploymentsL1.L2OutputOracleProxy,
ProposerKey: dp.Secrets.Proposer,
AllowNonFinalized: false,
}, miner.EthClient(), rollupSeqCl)
}
// L1 block // L1 block
miner.ActEmptyBlock(t) miner.ActEmptyBlock(t)
...@@ -94,17 +109,41 @@ func RunProposerTest(gt *testing.T, deltaTimeOffset *hexutil.Uint64) { ...@@ -94,17 +109,41 @@ func RunProposerTest(gt *testing.T, deltaTimeOffset *hexutil.Uint64) {
} }
// check that L1 stored the expected output root // check that L1 stored the expected output root
outputOracleContract, err := bindings.NewL2OutputOracle(sd.DeploymentsL1.L2OutputOracleProxy, miner.EthClient()) if e2eutils.UseFPAC() {
require.NoError(t, err) optimismPortal2Contract, err := bindingspreview.NewOptimismPortal2(sd.DeploymentsL1.OptimismPortalProxy, miner.EthClient())
blockNumber, err := outputOracleContract.LatestBlockNumber(&bind.CallOpts{}) require.NoError(t, err)
require.NoError(t, err) respectedGameType, err := optimismPortal2Contract.RespectedGameType(&bind.CallOpts{})
require.Greater(t, int64(blockNumber.Uint64()), int64(0), "latest block number must be greater than 0") require.NoError(t, err)
block, err := seqEngine.EthClient().BlockByNumber(t.Ctx(), blockNumber) disputeGameFactoryContract, err := bindings.NewDisputeGameFactory(sd.DeploymentsL1.DisputeGameFactoryProxy, miner.EthClient())
require.NoError(t, err) require.NoError(t, err)
outputOnL1, err := outputOracleContract.GetL2OutputAfter(&bind.CallOpts{}, blockNumber) gameCount, err := disputeGameFactoryContract.GameCount(&bind.CallOpts{})
require.NoError(t, err) require.NoError(t, err)
require.Less(t, block.Time(), outputOnL1.Timestamp.Uint64(), "output is registered with L1 timestamp of proposal tx, past L2 block") require.Greater(t, gameCount.Uint64(), uint64(0), "game count must be greater than 0")
outputComputed, err := sequencer.RollupClient().OutputAtBlock(t.Ctx(), blockNumber.Uint64()) latestGames, err := disputeGameFactoryContract.FindLatestGames(&bind.CallOpts{}, respectedGameType, new(big.Int).Sub(gameCount, common.Big1), common.Big1)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, eth.Bytes32(outputOnL1.OutputRoot), outputComputed.OutputRoot, "output roots must match") require.Greater(t, len(latestGames), 0, "latest games must be greater than 0")
latestGame := latestGames[0]
gameBlockNumber := new(big.Int)
gameBlockNumber.SetBytes(latestGame.ExtraData[0:32])
block, err := seqEngine.EthClient().BlockByNumber(t.Ctx(), gameBlockNumber)
require.NoError(t, err)
require.Less(t, block.Time(), latestGame.Timestamp, "output is registered with L1 timestamp of proposal tx, past L2 block")
outputComputed, err := sequencer.RollupClient().OutputAtBlock(t.Ctx(), gameBlockNumber.Uint64())
require.NoError(t, err)
require.Equal(t, eth.Bytes32(latestGame.RootClaim), outputComputed.OutputRoot, "output roots must match")
} else {
outputOracleContract, err := bindings.NewL2OutputOracle(sd.DeploymentsL1.L2OutputOracleProxy, miner.EthClient())
require.NoError(t, err)
blockNumber, err := outputOracleContract.LatestBlockNumber(&bind.CallOpts{})
require.NoError(t, err)
require.Greater(t, int64(blockNumber.Uint64()), int64(0), "latest block number must be greater than 0")
block, err := seqEngine.EthClient().BlockByNumber(t.Ctx(), blockNumber)
require.NoError(t, err)
outputOnL1, err := outputOracleContract.GetL2OutputAfter(&bind.CallOpts{}, blockNumber)
require.NoError(t, err)
require.Less(t, block.Time(), outputOnL1.Timestamp.Uint64(), "output is registered with L1 timestamp of proposal tx, past L2 block")
outputComputed, err := sequencer.RollupClient().OutputAtBlock(t.Ctx(), blockNumber.Uint64())
require.NoError(t, err)
require.Equal(t, eth.Bytes32(outputOnL1.OutputRoot), outputComputed.OutputRoot, "output roots must match")
}
} }
This diff is collapsed.
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"math/rand" "math/rand"
"testing" "testing"
"time"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
...@@ -131,11 +132,23 @@ func runCrossLayerUserTest(gt *testing.T, test hardforkScheduledTest) { ...@@ -131,11 +132,23 @@ func runCrossLayerUserTest(gt *testing.T, test hardforkScheduledTest) {
miner, seqEngine, seq := setupSequencerTest(t, sd, log) miner, seqEngine, seq := setupSequencerTest(t, sd, log)
batcher := NewL2Batcher(log, sd.RollupCfg, DefaultBatcherCfg(dp), batcher := NewL2Batcher(log, sd.RollupCfg, DefaultBatcherCfg(dp),
seq.RollupClient(), miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg)) seq.RollupClient(), miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg))
proposer := NewL2Proposer(t, log, &ProposerCfg{
OutputOracleAddr: &sd.DeploymentsL1.L2OutputOracleProxy, var proposer *L2Proposer
ProposerKey: dp.Secrets.Proposer, if e2eutils.UseFPAC() {
AllowNonFinalized: true, proposer = NewL2Proposer(t, log, &ProposerCfg{
}, miner.EthClient(), seq.RollupClient()) DisputeGameFactoryAddr: &sd.DeploymentsL1.DisputeGameFactoryProxy,
ProposalInterval: 6 * time.Second,
DisputeGameType: 0,
ProposerKey: dp.Secrets.Proposer,
AllowNonFinalized: true,
}, miner.EthClient(), seq.RollupClient())
} else {
proposer = NewL2Proposer(t, log, &ProposerCfg{
OutputOracleAddr: &sd.DeploymentsL1.L2OutputOracleProxy,
ProposerKey: dp.Secrets.Proposer,
AllowNonFinalized: true,
}, miner.EthClient(), seq.RollupClient())
}
// need to start derivation before we can make L2 blocks // need to start derivation before we can make L2 blocks
seq.ActL2PipelineFull(t) seq.ActL2PipelineFull(t)
...@@ -266,6 +279,25 @@ func runCrossLayerUserTest(gt *testing.T, test hardforkScheduledTest) { ...@@ -266,6 +279,25 @@ func runCrossLayerUserTest(gt *testing.T, test hardforkScheduledTest) {
miner.ActL1StartBlock(13)(t) miner.ActL1StartBlock(13)(t)
miner.ActL1EndBlock(t) miner.ActL1EndBlock(t)
// If using FPAC we need to resolve the game
if e2eutils.UseFPAC() {
// Resolve the root claim
alice.ActResolveClaim(t)
miner.ActL1StartBlock(12)(t)
miner.ActL1IncludeTx(alice.Address())(t)
miner.ActL1EndBlock(t)
// Resolve the game
alice.L1.ActCheckReceiptStatusOfLastTx(true)(t)
alice.ActResolve(t)
miner.ActL1StartBlock(12)(t)
miner.ActL1IncludeTx(alice.Address())(t)
miner.ActL1EndBlock(t)
// Create an empty block to pass the air-gap window
alice.L1.ActCheckReceiptStatusOfLastTx(true)(t)
miner.ActL1StartBlock(13)(t)
miner.ActL1EndBlock(t)
}
// make the L1 finalize withdrawal tx // make the L1 finalize withdrawal tx
alice.ActCompleteWithdrawal(t) alice.ActCompleteWithdrawal(t)
// include completed withdrawal in new L1 block // include completed withdrawal in new L1 block
......
...@@ -204,3 +204,7 @@ func ApplyDeployConfigForks(deployConfig *genesis.DeployConfig) { ...@@ -204,3 +204,7 @@ func ApplyDeployConfigForks(deployConfig *genesis.DeployConfig) {
deployConfig.L2GenesisCanyonTimeOffset = new(hexutil.Uint64) deployConfig.L2GenesisCanyonTimeOffset = new(hexutil.Uint64)
deployConfig.L2GenesisRegolithTimeOffset = new(hexutil.Uint64) deployConfig.L2GenesisRegolithTimeOffset = new(hexutil.Uint64)
} }
func UseFPAC() bool {
return os.Getenv("OP_E2E_USE_FPAC") == "true"
}
...@@ -7,9 +7,13 @@ import ( ...@@ -7,9 +7,13 @@ import (
"time" "time"
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/bindingspreview"
"github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain"
"github.com/ethereum-optimism/optimism/op-node/withdrawals"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
) )
// ForOutputRootPublished waits until there is an output published for an L2 block number larger than the supplied l2BlockNumber // ForOutputRootPublished waits until there is an output published for an L2 block number larger than the supplied l2BlockNumber
...@@ -69,3 +73,56 @@ func ForFinalizationPeriod(ctx context.Context, client *ethclient.Client, l1Prov ...@@ -69,3 +73,56 @@ func ForFinalizationPeriod(ctx context.Context, client *ethclient.Client, l1Prov
return header.Time > targetTimestamp.Uint64(), nil return header.Time > targetTimestamp.Uint64(), nil
}) })
} }
// ForGamePublished waits until a game is published on L1 for the given l2BlockNumber.
func ForGamePublished(ctx context.Context, client *ethclient.Client, optimismPortalAddr common.Address, disputeGameFactoryAddr common.Address, l2BlockNumber *big.Int) (uint64, error) {
l2BlockNumber = new(big.Int).Set(l2BlockNumber) // Don't clobber caller owned l2BlockNumber
optimismPortal2Contract, err := bindingspreview.NewOptimismPortal2Caller(optimismPortalAddr, client)
if err != nil {
return 0, err
}
disputeGameFactoryContract, err := bindings.NewDisputeGameFactoryCaller(disputeGameFactoryAddr, client)
if err != nil {
return 0, err
}
getL2BlockFromLatestGame := func() (*big.Int, error) {
latestGame, err := withdrawals.FindLatestGame(ctx, disputeGameFactoryContract, optimismPortal2Contract)
if err != nil {
return big.NewInt(-1), nil
}
gameBlockNumber := new(big.Int).SetBytes(latestGame.ExtraData[0:32])
return gameBlockNumber, nil
}
outputBlockNum, err := AndGet(ctx, time.Second, getL2BlockFromLatestGame, func(latest *big.Int) bool {
return latest.Cmp(l2BlockNumber) >= 0
})
if err != nil {
return 0, err
}
return outputBlockNum.Uint64(), nil
}
// ForWithdrawalCheck waits until the withdrawal check in the portal succeeds.
func ForWithdrawalCheck(ctx context.Context, client *ethclient.Client, withdrawal crossdomain.Withdrawal, optimismPortalAddr common.Address) error {
opts := &bind.CallOpts{Context: ctx}
portal, err := bindingspreview.NewOptimismPortal2Caller(optimismPortalAddr, client)
if err != nil {
return fmt.Errorf("create portal caller: %w", err)
}
return For(ctx, time.Second, func() (bool, error) {
log.Warn("checking withdrawal!")
wdHash, err := withdrawal.Hash()
if err != nil {
return false, fmt.Errorf("hash withdrawal: %w", err)
}
err = portal.CheckWithdrawal(opts, wdHash)
log.Warn("checking withdrawal", "hash", wdHash, "err", err)
return err == nil, nil
})
}
...@@ -39,6 +39,18 @@ func UsesCannon(t e2eutils.TestingBase, opts *testopts) { ...@@ -39,6 +39,18 @@ func UsesCannon(t e2eutils.TestingBase, opts *testopts) {
} }
} }
func SkipOnFPAC(t e2eutils.TestingBase, opts *testopts) {
if e2eutils.UseFPAC() {
t.Skip("Skipping test for FPAC")
}
}
func SkipOnNotFPAC(t e2eutils.TestingBase, opts *testopts) {
if !e2eutils.UseFPAC() {
t.Skip("Skipping test for non-FPAC")
}
}
// UseExecutor allows manually splitting tests between circleci executors // UseExecutor allows manually splitting tests between circleci executors
// //
// Tests default to run on the first executor but can be moved to the second with: // Tests default to run on the first executor but can be moved to the second with:
......
...@@ -747,17 +747,35 @@ func (cfg SystemConfig) Start(t *testing.T, _opts ...SystemConfigOption) (*Syste ...@@ -747,17 +747,35 @@ func (cfg SystemConfig) Start(t *testing.T, _opts ...SystemConfigOption) (*Syste
} }
// L2Output Submitter // L2Output Submitter
proposerCLIConfig := &l2os.CLIConfig{ var proposerCLIConfig *l2os.CLIConfig
L1EthRpc: sys.EthInstances["l1"].WSEndpoint(), if e2eutils.UseFPAC() {
RollupRpc: sys.RollupNodes["sequencer"].HTTPEndpoint(), proposerCLIConfig = &l2os.CLIConfig{
L2OOAddress: config.L1Deployments.L2OutputOracleProxy.Hex(), L1EthRpc: sys.EthInstances["l1"].WSEndpoint(),
PollInterval: 50 * time.Millisecond, RollupRpc: sys.RollupNodes["sequencer"].HTTPEndpoint(),
TxMgrConfig: newTxMgrConfig(sys.EthInstances["l1"].WSEndpoint(), cfg.Secrets.Proposer), DGFAddress: config.L1Deployments.DisputeGameFactoryProxy.Hex(),
AllowNonFinalized: cfg.NonFinalizedProposals, ProposalInterval: 6 * time.Second,
LogConfig: oplog.CLIConfig{ DisputeGameType: 0,
Level: log.LevelInfo, PollInterval: 50 * time.Millisecond,
Format: oplog.FormatText, TxMgrConfig: newTxMgrConfig(sys.EthInstances["l1"].WSEndpoint(), cfg.Secrets.Proposer),
}, AllowNonFinalized: cfg.NonFinalizedProposals,
LogConfig: oplog.CLIConfig{
Level: log.LvlInfo,
Format: oplog.FormatText,
},
}
} else {
proposerCLIConfig = &l2os.CLIConfig{
L1EthRpc: sys.EthInstances["l1"].WSEndpoint(),
RollupRpc: sys.RollupNodes["sequencer"].HTTPEndpoint(),
L2OOAddress: config.L1Deployments.L2OutputOracleProxy.Hex(),
PollInterval: 50 * time.Millisecond,
TxMgrConfig: newTxMgrConfig(sys.EthInstances["l1"].WSEndpoint(), cfg.Secrets.Proposer),
AllowNonFinalized: cfg.NonFinalizedProposals,
LogConfig: oplog.CLIConfig{
Level: log.LvlInfo,
Format: oplog.FormatText,
},
}
} }
proposer, err := l2os.ProposerServiceFromCLIConfig(context.Background(), "0.0.1", proposerCLIConfig, sys.Cfg.Loggers["proposer"]) proposer, err := l2os.ProposerServiceFromCLIConfig(context.Background(), "0.0.1", proposerCLIConfig, sys.Cfg.Loggers["proposer"])
if err != nil { if err != nil {
......
...@@ -10,6 +10,7 @@ import ( ...@@ -10,6 +10,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/geth" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/geth"
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
...@@ -90,7 +91,7 @@ func TestMain(m *testing.M) { ...@@ -90,7 +91,7 @@ func TestMain(m *testing.M) {
} }
func TestL2OutputSubmitter(t *testing.T) { func TestL2OutputSubmitter(t *testing.T) {
InitParallel(t) InitParallel(t, SkipOnFPAC)
cfg := DefaultSystemConfig(t) cfg := DefaultSystemConfig(t)
cfg.NonFinalizedProposals = true // speed up the time till we see output proposals cfg.NonFinalizedProposals = true // speed up the time till we see output proposals
...@@ -158,6 +159,66 @@ func TestL2OutputSubmitter(t *testing.T) { ...@@ -158,6 +159,66 @@ func TestL2OutputSubmitter(t *testing.T) {
} }
} }
func TestL2OutputSubmitterFPAC(t *testing.T) {
InitParallel(t, SkipOnNotFPAC)
cfg := DefaultSystemConfig(t)
cfg.NonFinalizedProposals = true // speed up the time till we see output proposals
sys, err := cfg.Start(t)
require.Nil(t, err, "Error starting up system")
defer sys.Close()
l1Client := sys.Clients["l1"]
rollupRPCClient, err := rpc.DialContext(context.Background(), sys.RollupNodes["sequencer"].HTTPEndpoint())
require.Nil(t, err)
rollupClient := sources.NewRollupClient(client.NewBaseRPCClient(rollupRPCClient))
disputeGameFactory, err := bindings.NewDisputeGameFactoryCaller(cfg.L1Deployments.DisputeGameFactoryProxy, l1Client)
require.Nil(t, err)
initialGameCount, err := disputeGameFactory.GameCount(&bind.CallOpts{})
require.Nil(t, err)
l2Verif := sys.Clients["verifier"]
_, err = geth.WaitForBlock(big.NewInt(6), l2Verif, 10*time.Duration(cfg.DeployConfig.L2BlockTime)*time.Second)
require.Nil(t, err)
timeoutCh := time.After(15 * time.Second)
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
for {
latestGameCount, err := disputeGameFactory.GameCount(&bind.CallOpts{})
require.Nil(t, err)
if latestGameCount.Cmp(initialGameCount) > 0 {
committedL2Output, err := disputeGameFactory.GameAtIndex(&bind.CallOpts{}, new(big.Int).Sub(latestGameCount, common.Big1))
require.Nil(t, err)
proxy, err := bindings.NewFaultDisputeGameCaller(committedL2Output.Proxy, l1Client)
require.Nil(t, err)
committedOutputRoot, err := proxy.RootClaim(&bind.CallOpts{})
require.Nil(t, err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
extradata, err := proxy.ExtraData(&bind.CallOpts{})
require.Nil(t, err)
gameBlockNumber := new(big.Int).SetBytes(extradata[0:32])
l2Output, err := rollupClient.OutputAtBlock(ctx, gameBlockNumber.Uint64())
require.Nil(t, err)
require.Equal(t, l2Output.OutputRoot[:], committedOutputRoot[:])
break
}
select {
case <-timeoutCh:
t.Fatalf("State root oracle not updated")
case <-ticker.C:
}
}
}
func TestSystemE2EDencunAtGenesis(t *testing.T) { func TestSystemE2EDencunAtGenesis(t *testing.T) {
InitParallel(t) InitParallel(t)
...@@ -1075,7 +1136,7 @@ func TestWithdrawals(t *testing.T) { ...@@ -1075,7 +1136,7 @@ func TestWithdrawals(t *testing.T) {
startBalanceBeforeFinalize, err := l1Client.BalanceAt(ctx, fromAddr, nil) startBalanceBeforeFinalize, err := l1Client.BalanceAt(ctx, fromAddr, nil)
require.Nil(t, err) require.Nil(t, err)
proveReceipt, finalizeReceipt := ProveAndFinalizeWithdrawal(t, cfg, sys, "verifier", ethPrivKey, receipt) proveReceipt, finalizeReceipt, resolveClaimReceipt, resolveReceipt := ProveAndFinalizeWithdrawal(t, cfg, sys, "verifier", ethPrivKey, receipt)
// Verify balance after withdrawal // Verify balance after withdrawal
ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second) ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second)
...@@ -1090,6 +1151,12 @@ func TestWithdrawals(t *testing.T) { ...@@ -1090,6 +1151,12 @@ func TestWithdrawals(t *testing.T) {
proveFee := new(big.Int).Mul(new(big.Int).SetUint64(proveReceipt.GasUsed), proveReceipt.EffectiveGasPrice) proveFee := new(big.Int).Mul(new(big.Int).SetUint64(proveReceipt.GasUsed), proveReceipt.EffectiveGasPrice)
finalizeFee := new(big.Int).Mul(new(big.Int).SetUint64(finalizeReceipt.GasUsed), finalizeReceipt.EffectiveGasPrice) finalizeFee := new(big.Int).Mul(new(big.Int).SetUint64(finalizeReceipt.GasUsed), finalizeReceipt.EffectiveGasPrice)
fees = new(big.Int).Add(proveFee, finalizeFee) fees = new(big.Int).Add(proveFee, finalizeFee)
if e2eutils.UseFPAC() {
resolveClaimFee := new(big.Int).Mul(new(big.Int).SetUint64(resolveClaimReceipt.GasUsed), resolveClaimReceipt.EffectiveGasPrice)
resolveFee := new(big.Int).Mul(new(big.Int).SetUint64(resolveReceipt.GasUsed), resolveReceipt.EffectiveGasPrice)
fees = new(big.Int).Add(fees, resolveClaimFee)
fees = new(big.Int).Add(fees, resolveFee)
}
withdrawAmount = withdrawAmount.Sub(withdrawAmount, fees) withdrawAmount = withdrawAmount.Sub(withdrawAmount, fees)
require.Equal(t, withdrawAmount, diff) require.Equal(t, withdrawAmount, diff)
} }
......
...@@ -11,7 +11,9 @@ import ( ...@@ -11,7 +11,9 @@ import (
"time" "time"
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/bindingspreview"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys" "github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/geth" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/geth"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
...@@ -446,6 +448,15 @@ func TestMixedWithdrawalValidity(t *testing.T) { ...@@ -446,6 +448,15 @@ func TestMixedWithdrawalValidity(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, cfg.DeployConfig.FinalizationPeriodSeconds, finalizationPeriod.Uint64()) require.Equal(t, cfg.DeployConfig.FinalizationPeriodSeconds, finalizationPeriod.Uint64())
disputeGameFactory, err := bindings.NewDisputeGameFactoryCaller(cfg.L1Deployments.DisputeGameFactoryProxy, l1Client)
require.NoError(t, err)
optimismPortal, err := bindings.NewOptimismPortalCaller(cfg.L1Deployments.OptimismPortalProxy, l1Client)
require.NoError(t, err)
optimismPortal2, err := bindingspreview.NewOptimismPortal2Caller(cfg.L1Deployments.OptimismPortalProxy, l1Client)
require.NoError(t, err)
// Create a struct used to track our transactors and their transactions sent. // Create a struct used to track our transactors and their transactions sent.
type TestAccountState struct { type TestAccountState struct {
Account *TestAccount Account *TestAccount
...@@ -535,10 +546,15 @@ func TestMixedWithdrawalValidity(t *testing.T) { ...@@ -535,10 +546,15 @@ func TestMixedWithdrawalValidity(t *testing.T) {
transactor.ExpectedL2Nonce = transactor.ExpectedL2Nonce + 1 transactor.ExpectedL2Nonce = transactor.ExpectedL2Nonce + 1
// Wait for the finalization period, then we can finalize this withdrawal. // Wait for the finalization period, then we can finalize this withdrawal.
ctx, withdaralCancel := context.WithTimeout(context.Background(), 40*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second) ctx, withdrawalCancel := context.WithTimeout(context.Background(), 60*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second)
require.NotEqual(t, cfg.L1Deployments.L2OutputOracleProxy, common.Address{}) require.NotEqual(t, cfg.L1Deployments.L2OutputOracleProxy, common.Address{})
blockNumber, err := wait.ForOutputRootPublished(ctx, l1Client, cfg.L1Deployments.L2OutputOracleProxy, receipt.BlockNumber) var blockNumber uint64
withdaralCancel() if e2eutils.UseFPAC() {
blockNumber, err = wait.ForGamePublished(ctx, l1Client, cfg.L1Deployments.OptimismPortalProxy, cfg.L1Deployments.DisputeGameFactoryProxy, receipt.BlockNumber)
} else {
blockNumber, err = wait.ForOutputRootPublished(ctx, l1Client, cfg.L1Deployments.L2OutputOracleProxy, receipt.BlockNumber)
}
withdrawalCancel()
require.Nil(t, err) require.Nil(t, err)
ctx, txCancel = context.WithTimeout(context.Background(), txTimeoutDuration) ctx, txCancel = context.WithTimeout(context.Background(), txTimeoutDuration)
...@@ -550,20 +566,22 @@ func TestMixedWithdrawalValidity(t *testing.T) { ...@@ -550,20 +566,22 @@ func TestMixedWithdrawalValidity(t *testing.T) {
require.Nil(t, err) require.Nil(t, err)
proofCl := gethclient.New(rpcClient) proofCl := gethclient.New(rpcClient)
receiptCl := ethclient.NewClient(rpcClient) receiptCl := ethclient.NewClient(rpcClient)
blockCl := ethclient.NewClient(rpcClient)
// Now create the withdrawal // Now create the withdrawal
params, err := withdrawals.ProveWithdrawalParameters(context.Background(), proofCl, receiptCl, tx.Hash(), header, l2OutputOracle) params, err := withdrawals.ProveWithdrawalParameters(context.Background(), proofCl, receiptCl, blockCl, tx.Hash(), header, l2OutputOracle, disputeGameFactory, optimismPortal, optimismPortal2)
require.Nil(t, err) require.Nil(t, err)
// Obtain our withdrawal parameters // Obtain our withdrawal parameters
withdrawalTransaction := &bindings.TypesWithdrawalTransaction{ withdrawal := crossdomain.Withdrawal{
Nonce: params.Nonce, Nonce: params.Nonce,
Sender: params.Sender, Sender: &params.Sender,
Target: params.Target, Target: &params.Target,
Value: params.Value, Value: params.Value,
GasLimit: params.GasLimit, GasLimit: params.GasLimit,
Data: params.Data, Data: params.Data,
} }
withdrawalTransaction := withdrawal.WithdrawalTransaction()
l2OutputIndexParam := params.L2OutputIndex l2OutputIndexParam := params.L2OutputIndex
outputRootProofParam := params.OutputRootProof outputRootProofParam := params.OutputRootProof
withdrawalProofParam := params.WithdrawalProof withdrawalProofParam := params.WithdrawalProof
...@@ -626,7 +644,7 @@ func TestMixedWithdrawalValidity(t *testing.T) { ...@@ -626,7 +644,7 @@ func TestMixedWithdrawalValidity(t *testing.T) {
// Prove withdrawal. This checks the proof so we only finalize if this succeeds // Prove withdrawal. This checks the proof so we only finalize if this succeeds
tx, err = depositContract.ProveWithdrawalTransaction( tx, err = depositContract.ProveWithdrawalTransaction(
transactor.Account.L1Opts, transactor.Account.L1Opts,
*withdrawalTransaction, withdrawalTransaction,
l2OutputIndexParam, l2OutputIndexParam,
outputRootProofParam, outputRootProofParam,
withdrawalProofParam, withdrawalProofParam,
...@@ -661,15 +679,20 @@ func TestMixedWithdrawalValidity(t *testing.T) { ...@@ -661,15 +679,20 @@ func TestMixedWithdrawalValidity(t *testing.T) {
require.Nil(t, err, "prove withdrawal") require.Nil(t, err, "prove withdrawal")
// Wait for finalization and then create the Finalized Withdrawal Transaction // Wait for finalization and then create the Finalized Withdrawal Transaction
ctx, withdrawalCancel := context.WithTimeout(context.Background(), 45*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second) ctx, withdrawalCancel := context.WithTimeout(context.Background(), 60*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second)
defer withdrawalCancel() defer withdrawalCancel()
err = wait.ForFinalizationPeriod(ctx, l1Client, header.Number, cfg.L1Deployments.L2OutputOracleProxy) if e2eutils.UseFPAC() {
require.Nil(t, err) err = wait.ForWithdrawalCheck(ctx, l1Client, withdrawal, cfg.L1Deployments.OptimismPortalProxy)
require.Nil(t, err)
} else {
err = wait.ForFinalizationPeriod(ctx, l1Client, header.Number, cfg.L1Deployments.L2OutputOracleProxy)
require.Nil(t, err)
}
// Finalize withdrawal // Finalize withdrawal
_, err = depositContract.FinalizeWithdrawalTransaction( _, err = depositContract.FinalizeWithdrawalTransaction(
transactor.Account.L1Opts, transactor.Account.L1Opts,
*withdrawalTransaction, withdrawalTransaction,
) )
require.NoError(t, err) require.NoError(t, err)
} }
......
...@@ -8,8 +8,11 @@ import ( ...@@ -8,8 +8,11 @@ import (
"time" "time"
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/bindingspreview"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys" "github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/crossdomain"
"github.com/ethereum-optimism/optimism/op-e2e/config" "github.com/ethereum-optimism/optimism/op-e2e/config"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/geth" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/geth"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait"
"github.com/ethereum-optimism/optimism/op-node/withdrawals" "github.com/ethereum-optimism/optimism/op-node/withdrawals"
...@@ -82,10 +85,10 @@ func defaultWithdrawalTxOpts() *WithdrawalTxOpts { ...@@ -82,10 +85,10 @@ func defaultWithdrawalTxOpts() *WithdrawalTxOpts {
} }
} }
func ProveAndFinalizeWithdrawal(t *testing.T, cfg SystemConfig, clients ClientProvider, l2NodeName string, ethPrivKey *ecdsa.PrivateKey, l2WithdrawalReceipt *types.Receipt) (*types.Receipt, *types.Receipt) { func ProveAndFinalizeWithdrawal(t *testing.T, cfg SystemConfig, clients ClientProvider, l2NodeName string, ethPrivKey *ecdsa.PrivateKey, l2WithdrawalReceipt *types.Receipt) (*types.Receipt, *types.Receipt, *types.Receipt, *types.Receipt) {
params, proveReceipt := ProveWithdrawal(t, cfg, clients, l2NodeName, ethPrivKey, l2WithdrawalReceipt) params, proveReceipt := ProveWithdrawal(t, cfg, clients, l2NodeName, ethPrivKey, l2WithdrawalReceipt)
finalizeReceipt := FinalizeWithdrawal(t, cfg, clients.NodeClient("l1"), ethPrivKey, proveReceipt, params) finalizeReceipt, resolveClaimReceipt, resolveReceipt := FinalizeWithdrawal(t, cfg, clients.NodeClient("l1"), ethPrivKey, proveReceipt, params)
return proveReceipt, finalizeReceipt return proveReceipt, finalizeReceipt, resolveClaimReceipt, resolveReceipt
} }
func ProveWithdrawal(t *testing.T, cfg SystemConfig, clients ClientProvider, l2NodeName string, ethPrivKey *ecdsa.PrivateKey, l2WithdrawalReceipt *types.Receipt) (withdrawals.ProvenWithdrawalParameters, *types.Receipt) { func ProveWithdrawal(t *testing.T, cfg SystemConfig, clients ClientProvider, l2NodeName string, ethPrivKey *ecdsa.PrivateKey, l2WithdrawalReceipt *types.Receipt) (withdrawals.ProvenWithdrawalParameters, *types.Receipt) {
...@@ -94,10 +97,18 @@ func ProveWithdrawal(t *testing.T, cfg SystemConfig, clients ClientProvider, l2N ...@@ -94,10 +97,18 @@ func ProveWithdrawal(t *testing.T, cfg SystemConfig, clients ClientProvider, l2N
defer cancel() defer cancel()
l1Client := clients.NodeClient("l1") l1Client := clients.NodeClient("l1")
blockNumber, err := wait.ForOutputRootPublished(ctx, l1Client, config.L1Deployments.L2OutputOracleProxy, l2WithdrawalReceipt.BlockNumber) var blockNumber uint64
require.Nil(t, err) var err error
if e2eutils.UseFPAC() {
blockNumber, err = wait.ForGamePublished(ctx, l1Client, config.L1Deployments.OptimismPortalProxy, config.L1Deployments.DisputeGameFactoryProxy, l2WithdrawalReceipt.BlockNumber)
require.Nil(t, err)
} else {
blockNumber, err = wait.ForOutputRootPublished(ctx, l1Client, config.L1Deployments.L2OutputOracleProxy, l2WithdrawalReceipt.BlockNumber)
require.Nil(t, err)
}
receiptCl := clients.NodeClient(l2NodeName) receiptCl := clients.NodeClient(l2NodeName)
blockCl := clients.NodeClient(l2NodeName)
proofCl := gethclient.New(receiptCl.Client()) proofCl := gethclient.New(receiptCl.Client())
ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second) ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second)
...@@ -106,11 +117,19 @@ func ProveWithdrawal(t *testing.T, cfg SystemConfig, clients ClientProvider, l2N ...@@ -106,11 +117,19 @@ func ProveWithdrawal(t *testing.T, cfg SystemConfig, clients ClientProvider, l2N
header, err := receiptCl.HeaderByNumber(ctx, new(big.Int).SetUint64(blockNumber)) header, err := receiptCl.HeaderByNumber(ctx, new(big.Int).SetUint64(blockNumber))
require.Nil(t, err) require.Nil(t, err)
// Now create withdrawal
oracle, err := bindings.NewL2OutputOracleCaller(config.L1Deployments.L2OutputOracleProxy, l1Client) oracle, err := bindings.NewL2OutputOracleCaller(config.L1Deployments.L2OutputOracleProxy, l1Client)
require.Nil(t, err) require.Nil(t, err)
params, err := withdrawals.ProveWithdrawalParameters(context.Background(), proofCl, receiptCl, l2WithdrawalReceipt.TxHash, header, oracle) factory, err := bindings.NewDisputeGameFactoryCaller(config.L1Deployments.DisputeGameFactoryProxy, l1Client)
require.Nil(t, err)
portal1, err := bindings.NewOptimismPortalCaller(config.L1Deployments.OptimismPortalProxy, l1Client)
require.Nil(t, err)
portal2, err := bindingspreview.NewOptimismPortal2Caller(config.L1Deployments.OptimismPortalProxy, l1Client)
require.Nil(t, err)
params, err := withdrawals.ProveWithdrawalParameters(context.Background(), proofCl, receiptCl, blockCl, l2WithdrawalReceipt.TxHash, header, oracle, factory, portal1, portal2)
require.Nil(t, err) require.Nil(t, err)
portal, err := bindings.NewOptimismPortal(config.L1Deployments.OptimismPortalProxy, l1Client) portal, err := bindings.NewOptimismPortal(config.L1Deployments.OptimismPortalProxy, l1Client)
...@@ -143,35 +162,76 @@ func ProveWithdrawal(t *testing.T, cfg SystemConfig, clients ClientProvider, l2N ...@@ -143,35 +162,76 @@ func ProveWithdrawal(t *testing.T, cfg SystemConfig, clients ClientProvider, l2N
return params, proveReceipt return params, proveReceipt
} }
func FinalizeWithdrawal(t *testing.T, cfg SystemConfig, l1Client *ethclient.Client, privKey *ecdsa.PrivateKey, withdrawalProofReceipt *types.Receipt, params withdrawals.ProvenWithdrawalParameters) *types.Receipt { func FinalizeWithdrawal(t *testing.T, cfg SystemConfig, l1Client *ethclient.Client, privKey *ecdsa.PrivateKey, withdrawalProofReceipt *types.Receipt, params withdrawals.ProvenWithdrawalParameters) (*types.Receipt, *types.Receipt, *types.Receipt) {
// Wait for finalization and then create the Finalized Withdrawal Transaction // Wait for finalization and then create the Finalized Withdrawal Transaction
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second)
defer cancel() defer cancel()
err := wait.ForFinalizationPeriod(ctx, l1Client, withdrawalProofReceipt.BlockNumber, config.L1Deployments.L2OutputOracleProxy) wd := crossdomain.Withdrawal{
require.Nil(t, err) Nonce: params.Nonce,
Sender: &params.Sender,
Target: &params.Target,
Value: params.Value,
GasLimit: params.GasLimit,
Data: params.Data,
}
opts, err := bind.NewKeyedTransactorWithChainID(privKey, cfg.L1ChainIDBig()) opts, err := bind.NewKeyedTransactorWithChainID(privKey, cfg.L1ChainIDBig())
require.Nil(t, err) require.Nil(t, err)
var resolveClaimReceipt *types.Receipt
var resolveReceipt *types.Receipt
if e2eutils.UseFPAC() {
portal2, err := bindingspreview.NewOptimismPortal2(config.L1Deployments.OptimismPortalProxy, l1Client)
require.Nil(t, err)
wdHash, err := wd.Hash()
require.Nil(t, err)
game, err := portal2.ProvenWithdrawals(&bind.CallOpts{}, wdHash)
require.Nil(t, err)
require.NotNil(t, game, "withdrawal should be proven")
proxy, err := bindings.NewFaultDisputeGame(game.DisputeGameProxy, l1Client)
require.Nil(t, err)
expiry, err := proxy.GameDuration(&bind.CallOpts{})
require.Nil(t, err)
time.Sleep(time.Duration(expiry) * time.Second)
resolveClaimTx, err := proxy.ResolveClaim(opts, common.Big0)
require.Nil(t, err)
resolveClaimReceipt, err = wait.ForReceiptOK(ctx, l1Client, resolveClaimTx.Hash())
require.Nil(t, err, "resolve claim")
require.Equal(t, types.ReceiptStatusSuccessful, resolveClaimReceipt.Status)
resolveTx, err := proxy.Resolve(opts)
require.Nil(t, err)
resolveReceipt, err = wait.ForReceiptOK(ctx, l1Client, resolveTx.Hash())
require.Nil(t, err, "resolve")
require.Equal(t, types.ReceiptStatusSuccessful, resolveReceipt.Status)
}
if e2eutils.UseFPAC() {
err := wait.ForWithdrawalCheck(ctx, l1Client, wd, config.L1Deployments.OptimismPortalProxy)
require.Nil(t, err)
} else {
err := wait.ForFinalizationPeriod(ctx, l1Client, withdrawalProofReceipt.BlockNumber, config.L1Deployments.L2OutputOracleProxy)
require.Nil(t, err)
}
portal, err := bindings.NewOptimismPortal(config.L1Deployments.OptimismPortalProxy, l1Client) portal, err := bindings.NewOptimismPortal(config.L1Deployments.OptimismPortalProxy, l1Client)
require.Nil(t, err) require.Nil(t, err)
// Finalize withdrawal // Finalize withdrawal
tx, err := portal.FinalizeWithdrawalTransaction( tx, err := portal.FinalizeWithdrawalTransaction(opts, wd.WithdrawalTransaction())
opts,
bindings.TypesWithdrawalTransaction{
Nonce: params.Nonce,
Sender: params.Sender,
Target: params.Target,
Value: params.Value,
GasLimit: params.GasLimit,
Data: params.Data,
},
)
require.Nil(t, err) require.Nil(t, err)
// Ensure that our withdrawal was finalized successfully // Ensure that our withdrawal was finalized successfully
finalizeReceipt, err := geth.WaitForTransaction(tx.Hash(), l1Client, 3*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second) finalizeReceipt, err := wait.ForReceiptOK(ctx, l1Client, tx.Hash())
require.Nil(t, err, "finalize withdrawal") require.Nil(t, err, "finalize withdrawal")
require.Equal(t, types.ReceiptStatusSuccessful, finalizeReceipt.Status) require.Equal(t, types.ReceiptStatusSuccessful, finalizeReceipt.Status)
return finalizeReceipt return finalizeReceipt, resolveClaimReceipt, resolveReceipt
} }
...@@ -15,7 +15,9 @@ import ( ...@@ -15,7 +15,9 @@ import (
"github.com/ethereum/go-ethereum/ethclient/gethclient" "github.com/ethereum/go-ethereum/ethclient/gethclient"
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/bindingspreview"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys" "github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
) )
var MessagePassedTopic = crypto.Keccak256Hash([]byte("MessagePassed(uint256,address,address,uint256,uint256,bytes,bytes32)")) var MessagePassedTopic = crypto.Keccak256Hash([]byte("MessagePassed(uint256,address,address,uint256,uint256,bytes,bytes32)"))
...@@ -28,6 +30,10 @@ type ReceiptClient interface { ...@@ -28,6 +30,10 @@ type ReceiptClient interface {
TransactionReceipt(context.Context, common.Hash) (*types.Receipt, error) TransactionReceipt(context.Context, common.Hash) (*types.Receipt, error)
} }
type BlockClient interface {
BlockByNumber(context.Context, *big.Int) (*types.Block, error)
}
// ProvenWithdrawalParameters is the set of parameters to pass to the ProveWithdrawalTransaction // ProvenWithdrawalParameters is the set of parameters to pass to the ProveWithdrawalTransaction
// and FinalizeWithdrawalTransaction functions // and FinalizeWithdrawalTransaction functions
type ProvenWithdrawalParameters struct { type ProvenWithdrawalParameters struct {
...@@ -45,7 +51,7 @@ type ProvenWithdrawalParameters struct { ...@@ -45,7 +51,7 @@ type ProvenWithdrawalParameters struct {
// ProveWithdrawalParameters queries L1 & L2 to generate all withdrawal parameters and proof necessary to prove a withdrawal on L1. // ProveWithdrawalParameters queries L1 & L2 to generate all withdrawal parameters and proof necessary to prove a withdrawal on L1.
// The header provided is very important. It should be a block (timestamp) for which there is a submitted output in the L2 Output Oracle // The header provided is very important. It should be a block (timestamp) for which there is a submitted output in the L2 Output Oracle
// contract. If not, the withdrawal will fail as it the storage proof cannot be verified if there is no submitted state root. // contract. If not, the withdrawal will fail as it the storage proof cannot be verified if there is no submitted state root.
func ProveWithdrawalParameters(ctx context.Context, proofCl ProofClient, l2ReceiptCl ReceiptClient, txHash common.Hash, header *types.Header, l2OutputOracleContract *bindings.L2OutputOracleCaller) (ProvenWithdrawalParameters, error) { func ProveWithdrawalParameters(ctx context.Context, proofCl ProofClient, l2ReceiptCl ReceiptClient, l2BlockCl BlockClient, txHash common.Hash, header *types.Header, l2OutputOracleContract *bindings.L2OutputOracleCaller, disputeGameFactoryContract *bindings.DisputeGameFactoryCaller, optimismPortalContract *bindings.OptimismPortalCaller, optimismPortal2Contract *bindingspreview.OptimismPortal2Caller) (ProvenWithdrawalParameters, error) {
// Transaction receipt // Transaction receipt
receipt, err := l2ReceiptCl.TransactionReceipt(ctx, txHash) receipt, err := l2ReceiptCl.TransactionReceipt(ctx, txHash)
if err != nil { if err != nil {
...@@ -65,18 +71,33 @@ func ProveWithdrawalParameters(ctx context.Context, proofCl ProofClient, l2Recei ...@@ -65,18 +71,33 @@ func ProveWithdrawalParameters(ctx context.Context, proofCl ProofClient, l2Recei
return ProvenWithdrawalParameters{}, err return ProvenWithdrawalParameters{}, err
} }
slot := StorageSlotOfWithdrawalHash(withdrawalHash) slot := StorageSlotOfWithdrawalHash(withdrawalHash)
p, err := proofCl.GetProof(ctx, predeploys.L2ToL1MessagePasserAddr, []string{slot.String()}, header.Number)
if err != nil { var l2OutputIndex *big.Int
return ProvenWithdrawalParameters{}, err var l2BlockNumber *big.Int
if e2eutils.UseFPAC() {
latestGame, err := FindLatestGame(ctx, disputeGameFactoryContract, optimismPortal2Contract)
if err != nil {
return ProvenWithdrawalParameters{}, fmt.Errorf("failed to find latest game: %w", err)
}
l2BlockNumber = new(big.Int).SetBytes(latestGame.ExtraData[0:32])
l2OutputIndex = latestGame.Index
} else {
l2OutputIndex, err = l2OutputOracleContract.GetL2OutputIndexAfter(&bind.CallOpts{}, header.Number)
if err != nil {
return ProvenWithdrawalParameters{}, fmt.Errorf("failed to get l2OutputIndex: %w", err)
}
l2BlockNumber = header.Number
} }
// Fetch the L2OutputIndex from the L2 Output Oracle caller (on L1) // Fetch the block from the L2 node
l2OutputIndex, err := l2OutputOracleContract.GetL2OutputIndexAfter(&bind.CallOpts{}, header.Number) l2Block, err := l2BlockCl.BlockByNumber(ctx, l2BlockNumber)
if err != nil { if err != nil {
return ProvenWithdrawalParameters{}, fmt.Errorf("failed to get l2OutputIndex: %w", err) return ProvenWithdrawalParameters{}, fmt.Errorf("failed to get l2Block: %w", err)
} }
// TODO: Could skip this step, but it's nice to double check it
err = VerifyProof(header.Root, p) p, err := proofCl.GetProof(ctx, predeploys.L2ToL1MessagePasserAddr, []string{slot.String()}, l2Block.Number())
if err != nil { if err != nil {
return ProvenWithdrawalParameters{}, err return ProvenWithdrawalParameters{}, err
} }
...@@ -84,6 +105,11 @@ func ProveWithdrawalParameters(ctx context.Context, proofCl ProofClient, l2Recei ...@@ -84,6 +105,11 @@ func ProveWithdrawalParameters(ctx context.Context, proofCl ProofClient, l2Recei
return ProvenWithdrawalParameters{}, errors.New("invalid amount of storage proofs") return ProvenWithdrawalParameters{}, errors.New("invalid amount of storage proofs")
} }
err = VerifyProof(l2Block.Root(), p)
if err != nil {
return ProvenWithdrawalParameters{}, err
}
// Encode it as expected by the contract // Encode it as expected by the contract
trieNodes := make([][]byte, len(p.StorageProof[0].Proof)) trieNodes := make([][]byte, len(p.StorageProof[0].Proof))
for i, s := range p.StorageProof[0].Proof { for i, s := range p.StorageProof[0].Proof {
...@@ -100,14 +126,42 @@ func ProveWithdrawalParameters(ctx context.Context, proofCl ProofClient, l2Recei ...@@ -100,14 +126,42 @@ func ProveWithdrawalParameters(ctx context.Context, proofCl ProofClient, l2Recei
Data: ev.Data, Data: ev.Data,
OutputRootProof: bindings.TypesOutputRootProof{ OutputRootProof: bindings.TypesOutputRootProof{
Version: [32]byte{}, // Empty for version 1 Version: [32]byte{}, // Empty for version 1
StateRoot: header.Root, StateRoot: l2Block.Root(),
MessagePasserStorageRoot: p.StorageHash, MessagePasserStorageRoot: p.StorageHash,
LatestBlockhash: header.Hash(), LatestBlockhash: l2Block.Hash(),
}, },
WithdrawalProof: trieNodes, WithdrawalProof: trieNodes,
}, nil }, nil
} }
// FindLatestGame finds the latest game in the DisputeGameFactory contract.
func FindLatestGame(ctx context.Context, disputeGameFactoryContract *bindings.DisputeGameFactoryCaller, optimismPortal2Contract *bindingspreview.OptimismPortal2Caller) (*bindings.IDisputeGameFactoryGameSearchResult, error) {
respectedGameType, err := optimismPortal2Contract.RespectedGameType(&bind.CallOpts{})
if err != nil {
return nil, fmt.Errorf("failed to get respected game type: %w", err)
}
gameCount, err := disputeGameFactoryContract.GameCount(&bind.CallOpts{})
if err != nil {
return nil, fmt.Errorf("failed to get game count: %w", err)
}
if gameCount.Cmp(common.Big0) == 0 {
return nil, errors.New("no games")
}
searchStart := new(big.Int).Sub(gameCount, common.Big1)
latestGames, err := disputeGameFactoryContract.FindLatestGames(&bind.CallOpts{}, respectedGameType, searchStart, common.Big1)
if err != nil {
return nil, fmt.Errorf("failed to get latest games: %w", err)
}
if len(latestGames) == 0 {
return nil, errors.New("no latest games")
}
latestGame := latestGames[0]
return &latestGame, nil
}
// Standard ABI types copied from golang ABI tests // Standard ABI types copied from golang ABI tests
var ( var (
Uint256Type, _ = abi.NewType("uint256", "", nil) Uint256Type, _ = abi.NewType("uint256", "", nil)
......
...@@ -238,7 +238,7 @@ func (l *L2OutputSubmitter) FetchNextOutputInfo(ctx context.Context) (*eth.Outpu ...@@ -238,7 +238,7 @@ func (l *L2OutputSubmitter) FetchNextOutputInfo(ctx context.Context) (*eth.Outpu
return nil, false, nil return nil, false, nil
} }
return l.fetchOutput(ctx, nextCheckpointBlock) return l.FetchOutput(ctx, nextCheckpointBlock)
} }
// FetchCurrentBlockNumber gets the current block number from the [L2OutputSubmitter]'s [RollupClient]. If the `AllowNonFinalized` configuration // FetchCurrentBlockNumber gets the current block number from the [L2OutputSubmitter]'s [RollupClient]. If the `AllowNonFinalized` configuration
...@@ -267,7 +267,7 @@ func (l *L2OutputSubmitter) FetchCurrentBlockNumber(ctx context.Context) (*big.I ...@@ -267,7 +267,7 @@ func (l *L2OutputSubmitter) FetchCurrentBlockNumber(ctx context.Context) (*big.I
return currentBlockNumber, nil return currentBlockNumber, nil
} }
func (l *L2OutputSubmitter) fetchOutput(ctx context.Context, block *big.Int) (*eth.OutputResponse, bool, error) { func (l *L2OutputSubmitter) FetchOutput(ctx context.Context, block *big.Int) (*eth.OutputResponse, bool, error) {
ctx, cancel := context.WithTimeout(ctx, l.Cfg.NetworkTimeout) ctx, cancel := context.WithTimeout(ctx, l.Cfg.NetworkTimeout)
defer cancel() defer cancel()
...@@ -451,7 +451,7 @@ func (l *L2OutputSubmitter) loopDGF(ctx context.Context) { ...@@ -451,7 +451,7 @@ func (l *L2OutputSubmitter) loopDGF(ctx context.Context) {
break break
} }
output, shouldPropose, err := l.fetchOutput(ctx, blockNumber) output, shouldPropose, err := l.FetchOutput(ctx, blockNumber)
if err != nil || !shouldPropose { if err != nil || !shouldPropose {
break break
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment