diff --git a/.github/workflows/espresso-e2e-tests.yaml b/.github/workflows/espresso-e2e-tests.yaml new file mode 100644 index 00000000000..c616e0490ff --- /dev/null +++ b/.github/workflows/espresso-e2e-tests.yaml @@ -0,0 +1,80 @@ +name: Espresso E2E tests + +# In-memory Espresso e2e suite (espresso/environment/...): mock Espresso client +# + op-e2e in-process L1/L2, no docker devnet, so it runs on free runners. +on: + pull_request: + paths: + - "espresso/**" + - "op-batcher/**" + - "op-e2e/**" + - "op-node/**" + - "packages/contracts-bedrock/**" + - ".github/workflows/espresso-e2e-tests.yaml" + workflow_dispatch: + +# Cancel superseded runs on the same ref. +concurrency: + group: espresso-e2e-${{ github.ref }} + cancel-in-progress: true + +jobs: + espresso-e2e: + runs-on: ubuntu-latest + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + include: + # In-memory groups (contracts only). + - group: core + needs_prestate: false + tests: "TestE2eDevnetWithEspressoSimpleTransactions|TestE2eDevnetWithEspressoAndAltDaSimpleTransactions|TestE2eDevnetWithoutEspressoSimpleTransaction|TestE2eDevnetWithoutAuthenticatingBatches|TestStatelessBatcher" + - group: integrity + needs_prestate: false + tests: "TestSequencerFeedConsistency|TestSequencerFeedConsistencyWithAttackOnEspresso|TestDeterministicDerivationExecutionStateWithInvalidTransaction|TestValidEspressoTransactionCreation|TestConfirmationIntegrityWithReorgs" + - group: fallback + needs_prestate: false + tests: "TestBatcherSwitching|TestFallbackMechanismIntegrationTestChannelNotClosed|TestEspressoEnforcementHardfork|TestE2eDevnetWithEspressoDegradedLiveness" + - group: forced-reorg + needs_prestate: false + tests: "TestForcedTransaction|TestBatcherWaitForFinality|TestE2eDevnetWithL1Reorg|TestPipelineEnhancement|TestEnforceMajorityRule" + # Needs cannon prestate + op-challenger; disabled until the prestate + # build works on free runners. + # - group: dispute + # needs_prestate: true + # tests: "TestOutputAlphabetGameWithEspresso_ChallengerWins" + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Set up mise (forge, go, just, ...) + uses: jdx/mise-action@v2 + + - name: Restore forge-artifacts cache + id: forge-cache + uses: actions/cache@v4 + with: + path: packages/contracts-bedrock/forge-artifacts + key: forge-artifacts-${{ hashFiles('packages/contracts-bedrock/foundry.toml', 'packages/contracts-bedrock/src/**/*.sol', 'packages/contracts-bedrock/interfaces/**/*.sol', 'packages/contracts-bedrock/scripts/**/*.sol', '.gitmodules') }} + + - name: Compile contracts + if: steps.forge-cache.outputs.cache-hit != 'true' + run: mise exec -- just build-contracts + + # Only the dispute group needs prestates. + - name: Build cannon prestates + if: matrix.needs_prestate + run: mise exec -- just cannon-prestates + + - name: Install gotestsum + run: go install gotest.tools/gotestsum@latest + + - name: Run ${{ matrix.group }} tests + run: | + gotestsum --format github-actions -- \ + -timeout 45m -p 1 -count 1 \ + -run '${{ matrix.tests }}' \ + ./espresso/environment/... diff --git a/espresso/bindings/espresso_tee_verifier.go b/espresso/bindings/espresso_tee_verifier.go new file mode 100644 index 00000000000..19b0c685b9c --- /dev/null +++ b/espresso/bindings/espresso_tee_verifier.go @@ -0,0 +1,1797 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package bindings + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// EspressoTEEVerifierMetaData contains all meta data concerning the EspressoTEEVerifier contract. +var EspressoTEEVerifierMetaData = &bind.MetaData{ + ABI: "[{\"type\":\"constructor\",\"inputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"acceptOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"addGuardian\",\"inputs\":[{\"name\":\"guardian\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"deleteEnclaveHashes\",\"inputs\":[{\"name\":\"enclaveHashes\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"},{\"name\":\"teeType\",\"type\":\"uint8\",\"internalType\":\"enumIEspressoTEEVerifier.TeeType\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"eip712Domain\",\"inputs\":[],\"outputs\":[{\"name\":\"fields\",\"type\":\"bytes1\",\"internalType\":\"bytes1\"},{\"name\":\"name\",\"type\":\"string\",\"internalType\":\"string\"},{\"name\":\"version\",\"type\":\"string\",\"internalType\":\"string\"},{\"name\":\"chainId\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"verifyingContract\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"salt\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"extensions\",\"type\":\"uint256[]\",\"internalType\":\"uint256[]\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"espressoNitroTEEVerifier\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"contractIEspressoNitroTEEVerifier\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getGuardians\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address[]\",\"internalType\":\"address[]\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"guardianCount\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"initialize\",\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_espressoNitroTEEVerifier\",\"type\":\"address\",\"internalType\":\"contractIEspressoNitroTEEVerifier\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"isGuardian\",\"inputs\":[{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"isSignerValid\",\"inputs\":[{\"name\":\"signer\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"teeType\",\"type\":\"uint8\",\"internalType\":\"enumIEspressoTEEVerifier.TeeType\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"owner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"pendingOwner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"registerService\",\"inputs\":[{\"name\":\"verificationData\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"teeType\",\"type\":\"uint8\",\"internalType\":\"enumIEspressoTEEVerifier.TeeType\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"registeredEnclaveHashes\",\"inputs\":[{\"name\":\"enclaveHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"teeType\",\"type\":\"uint8\",\"internalType\":\"enumIEspressoTEEVerifier.TeeType\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"removeGuardian\",\"inputs\":[{\"name\":\"guardian\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"renounceOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setEnclaveHash\",\"inputs\":[{\"name\":\"enclaveHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"valid\",\"type\":\"bool\",\"internalType\":\"bool\"},{\"name\":\"teeType\",\"type\":\"uint8\",\"internalType\":\"enumIEspressoTEEVerifier.TeeType\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setEspressoNitroTEEVerifier\",\"inputs\":[{\"name\":\"_espressoNitroTEEVerifier\",\"type\":\"address\",\"internalType\":\"contractIEspressoNitroTEEVerifier\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setNitroEnclaveVerifier\",\"inputs\":[{\"name\":\"nitroVerifier\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"transferOwnership\",\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"verify\",\"inputs\":[{\"name\":\"signature\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"userDataHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"teeType\",\"type\":\"uint8\",\"internalType\":\"enumIEspressoTEEVerifier.TeeType\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"event\",\"name\":\"EIP712DomainChanged\",\"inputs\":[],\"anonymous\":false},{\"type\":\"event\",\"name\":\"EspressoNitroTEEVerifierSet\",\"inputs\":[{\"name\":\"oldVerifier\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newVerifier\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"GuardianAdded\",\"inputs\":[{\"name\":\"guardian\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"GuardianRemoved\",\"inputs\":[{\"name\":\"guardian\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Initialized\",\"inputs\":[{\"name\":\"version\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferStarted\",\"inputs\":[{\"name\":\"previousOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferred\",\"inputs\":[{\"name\":\"previousOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"error\",\"name\":\"ECDSAInvalidSignature\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"ECDSAInvalidSignatureLength\",\"inputs\":[{\"name\":\"length\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]},{\"type\":\"error\",\"name\":\"ECDSAInvalidSignatureS\",\"inputs\":[{\"name\":\"s\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"type\":\"error\",\"name\":\"InvalidGuardianAddress\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidInitialization\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidSignature\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidVerifierAddress\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"NotGuardian\",\"inputs\":[{\"name\":\"caller\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"NotGuardianOrOwner\",\"inputs\":[{\"name\":\"caller\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"NotInitializing\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"OwnableInvalidOwner\",\"inputs\":[{\"name\":\"owner\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"OwnableUnauthorizedAccount\",\"inputs\":[{\"name\":\"account\",\"type\":\"address\",\"internalType\":\"address\"}]},{\"type\":\"error\",\"name\":\"OwnerCantBeGuardian\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"UnsupportedTeeType\",\"inputs\":[{\"name\":\"teeType\",\"type\":\"uint8\",\"internalType\":\"enumIEspressoTEEVerifier.TeeType\"}]}]", + Bin: "0x6080604052348015600e575f5ffd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b612528806100d65f395ff3fe608060405234801561000f575f5ffd5b506004361061016e575f3560e01c806379ba5097116100d2578063a81d9c5c11610088578063dac79fc811610063578063dac79fc81461031e578063e30c397814610331578063f2fde38b14610339575f5ffd5b8063a81d9c5c146102bb578063cd8f6997146102ce578063d80a4c28146102e1575f5ffd5b80638da5cb5b116100b85780638da5cb5b14610268578063a526d83b14610295578063a628a19e146102a8575f5ffd5b806379ba50971461024557806384b0196e1461024d575f5ffd5b8063485cc955116101275780636d8f5aa91161010d5780636d8f5aa914610217578063714041561461022a578063715018a61461023d575f5ffd5b8063485cc955146101ee57806354387ad714610201575f5ffd5b80630f1f0f86116101575780630f1f0f86146101b3578063330282f5146101c85780633cbe6803146101db575f5ffd5b80630665f04b146101725780630c68ba2114610190575b5f5ffd5b61017a61034c565b6040516101879190611c67565b60405180910390f35b6101a361019e366004611ce0565b61037c565b6040519015158152602001610187565b6101c66101c1366004611d1b565b6103af565b005b6101c66101d6366004611ce0565b61050f565b6101a36101e9366004611d56565b6105fd565b6101c66101fc366004611d80565b6106c1565b6102096108ff565b604051908152602001610187565b6101a3610225366004611db7565b610929565b6101c6610238366004611ce0565b6109ab565b6101c6610a2b565b6101c6610a3e565b610255610ab6565b6040516101879796959493929190611e2d565b610270610bb0565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610187565b6101c66102a3366004611ce0565b610bf1565b6101c66102b6366004611ce0565b610d65565b6101a36102c9366004611f68565b610e0f565b6101c66102dc366004612027565b610f73565b7f89639f446056f5d7661bbd94e8ab0617a80058ed7b072845818d4b93332e48005473ffffffffffffffffffffffffffffffffffffffff16610270565b6101c661032c366004612122565b611023565b6102706110dd565b6101c6610347366004611ce0565b611105565b60606103777f0f4ac8aae5a4fa6a3612928fcd8255b475ff86b500ae30bb272e61542cfc6f00611177565b905090565b5f6103a9827f0f4ac8aae5a4fa6a3612928fcd8255b475ff86b500ae30bb272e61542cfc6f005b9061118a565b92915050565b6103d9337f0f4ac8aae5a4fa6a3612928fcd8255b475ff86b500ae30bb272e61542cfc6f006103a3565b15801561041957506103e9610bb0565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614155b15610457576040517fd53780c40000000000000000000000000000000000000000000000000000000081523360048201526024015b60405180910390fd5b610460816111b8565b5f7f89639f446056f5d7661bbd94e8ab0617a80058ed7b072845818d4b93332e480080546040517f93b5552e00000000000000000000000000000000000000000000000000000000815260048101879052851515602482015291925073ffffffffffffffffffffffffffffffffffffffff16906393b5552e906044015f604051808303815f87803b1580156104f3575f5ffd5b505af1158015610505573d5f5f3e3d5ffd5b5050505050505050565b610517611201565b73ffffffffffffffffffffffffffffffffffffffff8116610564576040517f10c40e8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f89639f446056f5d7661bbd94e8ab0617a80058ed7b072845818d4b93332e480080547fffffffffffffffffffffffff0000000000000000000000000000000000000000811673ffffffffffffffffffffffffffffffffffffffff848116918217845560405192169184919083907facbe6384d204f2883b25fa4d03777cb88af5b2afaaffbe06609d3e17dc68d096905f90a350505050565b5f610607826111b8565b7f89639f446056f5d7661bbd94e8ab0617a80058ed7b072845818d4b93332e480080546040517f966989ee0000000000000000000000000000000000000000000000000000000081526004810186905273ffffffffffffffffffffffffffffffffffffffff9091169063966989ee906024015b602060405180830381865afa158015610695573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106b991906121a2565b949350505050565b5f6106ca611259565b805490915060ff68010000000000000000820416159067ffffffffffffffff165f811580156106f65750825b90505f8267ffffffffffffffff1660011480156107125750303b155b905081158015610720575080155b15610757576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000016600117855583156107b85784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16680100000000000000001785555b7f89639f446056f5d7661bbd94e8ab0617a80058ed7b072845818d4b93332e480080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff881617815561082088611281565b6108946040518060400160405280601381526020017f457370726573736f5445455665726966696572000000000000000000000000008152506040518060400160405280600181526020017f3100000000000000000000000000000000000000000000000000000000000000815250611292565b5083156108f65784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050565b5f6103777f0f4ac8aae5a4fa6a3612928fcd8255b475ff86b500ae30bb272e61542cfc6f006112a4565b5f610933826111b8565b7f89639f446056f5d7661bbd94e8ab0617a80058ed7b072845818d4b93332e480080546040517f4460790300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff86811660048301529091169063446079039060240161067a565b6109b3611201565b7f0f4ac8aae5a4fa6a3612928fcd8255b475ff86b500ae30bb272e61542cfc6f006109de81836112ad565b6109e6575050565b60405173ffffffffffffffffffffffffffffffffffffffff8316907fb8107d0c6b40be480ce3172ee66ba6d64b71f6b1685a851340036e6e2e3e3c52905f90a2505b50565b610a33611201565b610a3c5f6112ce565b565b3380610a486110dd565b73ffffffffffffffffffffffffffffffffffffffff1614610aad576040517f118cdaa700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8216600482015260240161044e565b610a28816112ce565b5f60608082808083817fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1008054909150158015610af457506001810154155b610b5a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4549503731323a20556e696e697469616c697a65640000000000000000000000604482015260640161044e565b610b6261131e565b610b6a6113f1565b604080515f808252602082019092527f0f000000000000000000000000000000000000000000000000000000000000009c939b5091995046985030975095509350915050565b5f807f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c1993005b5473ffffffffffffffffffffffffffffffffffffffff1692915050565b610bf9611201565b73ffffffffffffffffffffffffffffffffffffffff8116610c46576040517f1b08105400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610c4e610bb0565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161480610cb95750610c8a6110dd565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b15610cf0576040517f3af3c41c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0f4ac8aae5a4fa6a3612928fcd8255b475ff86b500ae30bb272e61542cfc6f00610d1b8183611442565b15610d615760405173ffffffffffffffffffffffffffffffffffffffff8316907f038596bb31e2e7d3d9f184d4c98b310103f6d7f5830e5eec32bffe6f1728f969905f90a25b5050565b610d6d611201565b7f89639f446056f5d7661bbd94e8ab0617a80058ed7b072845818d4b93332e4800546040517fa628a19e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff83811660048301529091169063a628a19e906024015f604051808303815f87803b158015610df6575f5ffd5b505af1158015610e08573d5f5f3e3d5ffd5b5050505050565b5f610e19826111b8565b604080517ff2069c4294e295535d771d7fbbd52e3db330ca380d24e42af36427513a27649b602080830191909152818301869052825180830384018152606090920190925280519101207f89639f446056f5d7661bbd94e8ab0617a80058ed7b072845818d4b93332e4800905f610e8f82611463565b90505f610e9c82896114aa565b84546040517f4460790300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8084166004830152929350911690634460790390602401602060405180830381865afa158015610f0b573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f2f91906121a2565b610f65576040517f8baa579f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b506001979650505050505050565b610f7b611201565b610f84816111b8565b7f89639f446056f5d7661bbd94e8ab0617a80058ed7b072845818d4b93332e480080546040517f6b8c01a600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690636b8c01a690610ffa9086906004016121bd565b5f604051808303815f87803b158015611011575f5ffd5b505af11580156108f6573d5f5f3e3d5ffd5b61102c816111b8565b7f89639f446056f5d7661bbd94e8ab0617a80058ed7b072845818d4b93332e480080546040517f0b1c4cde00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911690630b1c4cde906110a890899089908990899060040161223b565b5f604051808303815f87803b1580156110bf575f5ffd5b505af11580156110d1573d5f5f3e3d5ffd5b50505050505050505050565b5f807f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c00610bd4565b61110d611201565b611137817f0f4ac8aae5a4fa6a3612928fcd8255b475ff86b500ae30bb272e61542cfc6f006103a3565b1561116e576040517f3af3c41c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610a28816114d2565b60605f61118383611589565b9392505050565b73ffffffffffffffffffffffffffffffffffffffff81165f9081526001830160205260408120541515611183565b5f8180156111c8576111c861226c565b14610a2857806040517fdf84035d00000000000000000000000000000000000000000000000000000000815260040161044e9190612299565b3361120a610bb0565b73ffffffffffffffffffffffffffffffffffffffff1614610a3c576040517f118cdaa700000000000000000000000000000000000000000000000000000000815233600482015260240161044e565b5f807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a006103a9565b6112896115e2565b610a2881611620565b61129a6115e2565b610d618282611631565b5f6103a9825490565b5f6111838373ffffffffffffffffffffffffffffffffffffffff84166116a3565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0080547fffffffffffffffffffffffff0000000000000000000000000000000000000000168155610d6182611786565b7fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d10280546060917fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1009161136f906122d8565b80601f016020809104026020016040519081016040528092919081815260200182805461139b906122d8565b80156113e65780601f106113bd576101008083540402835291602001916113e6565b820191905f5260205f20905b8154815290600101906020018083116113c957829003601f168201915b505050505091505090565b7fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d10380546060917fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1009161136f906122d8565b5f6111838373ffffffffffffffffffffffffffffffffffffffff841661181b565b5f6103a961146f611867565b836040517f19010000000000000000000000000000000000000000000000000000000000008152600281019290925260228201526042902090565b5f5f5f5f6114b88686611870565b9250925092506114c882826118b9565b5090949350505050565b6114da611201565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081178255611543610bb0565b73ffffffffffffffffffffffffffffffffffffffff167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a35050565b6060815f018054806020026020016040519081016040528092919081815260200182805480156115d657602002820191905f5260205f20905b8154815260200190600101908083116115c2575b50505050509050919050565b6115ea6119bc565b610a3c576040517fd7e6bcf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6116286115e2565b610a28816119da565b6116396115e2565b7fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1007fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d1026116858482612372565b50600381016116948382612372565b505f8082556001909101555050565b5f818152600183016020526040812054801561177d575f6116c5600183612489565b85549091505f906116d890600190612489565b9050808214611737575f865f0182815481106116f6576116f66124c1565b905f5260205f200154905080875f018481548110611716576117166124c1565b5f918252602080832090910192909255918252600188019052604090208390555b8554869080611748576117486124ee565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f9055600193505050506103a9565b5f9150506103a9565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080547fffffffffffffffffffffffff0000000000000000000000000000000000000000811673ffffffffffffffffffffffffffffffffffffffff848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b5f81815260018301602052604081205461186057508154600181810184555f8481526020808220909301849055845484825282860190935260409020919091556103a9565b505f6103a9565b5f610377611a31565b5f5f5f83516041036118a7576020840151604085015160608601515f1a61189988828585611aa4565b9550955095505050506118b2565b505081515f91506002905b9250925092565b5f8260038111156118cc576118cc61226c565b036118d5575050565b60018260038111156118e9576118e961226c565b03611920576040517ff645eedf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60028260038111156119345761193461226c565b0361196e576040517ffce698f70000000000000000000000000000000000000000000000000000000081526004810182905260240161044e565b60038260038111156119825761198261226c565b03610d61576040517fd78bce0c0000000000000000000000000000000000000000000000000000000081526004810182905260240161044e565b5f6119c5611259565b5468010000000000000000900460ff16919050565b6119e26115e2565b73ffffffffffffffffffffffffffffffffffffffff8116610aad576040517f1e4fbdf70000000000000000000000000000000000000000000000000000000081525f600482015260240161044e565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f611a5b611b97565b611a63611c12565b60408051602081019490945283019190915260608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b5f80807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841115611add57505f91506003905082611b8d565b604080515f808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015611b2e573d5f5f3e3d5ffd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff8116611b8457505f925060019150829050611b8d565b92505f91508190505b9450945094915050565b5f7fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d10081611bc261131e565b805190915015611bda57805160209091012092915050565b81548015611be9579392505050565b7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470935050505090565b5f7fa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d10081611c3d6113f1565b805190915015611c5557805160209091012092915050565b60018201548015611be9579392505050565b602080825282518282018190525f918401906040840190835b81811015611cb457835173ffffffffffffffffffffffffffffffffffffffff16835260209384019390920191600101611c80565b509095945050505050565b73ffffffffffffffffffffffffffffffffffffffff81168114610a28575f5ffd5b5f60208284031215611cf0575f5ffd5b813561118381611cbf565b8015158114610a28575f5ffd5b803560018110611d16575f5ffd5b919050565b5f5f5f60608486031215611d2d575f5ffd5b833592506020840135611d3f81611cfb565b9150611d4d60408501611d08565b90509250925092565b5f5f60408385031215611d67575f5ffd5b82359150611d7760208401611d08565b90509250929050565b5f5f60408385031215611d91575f5ffd5b8235611d9c81611cbf565b91506020830135611dac81611cbf565b809150509250929050565b5f5f60408385031215611dc8575f5ffd5b8235611dd381611cbf565b9150611d7760208401611d08565b5f81518084528060208401602086015e5f6020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b7fff000000000000000000000000000000000000000000000000000000000000008816815260e060208201525f611e6760e0830189611de1565b8281036040840152611e798189611de1565b6060840188905273ffffffffffffffffffffffffffffffffffffffff8716608085015260a0840186905283810360c0850152845180825260208087019350909101905f5b81811015611edb578351835260209384019390920191600101611ebd565b50909b9a5050505050505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715611f6057611f60611eec565b604052919050565b5f5f5f60608486031215611f7a575f5ffd5b833567ffffffffffffffff811115611f90575f5ffd5b8401601f81018613611fa0575f5ffd5b803567ffffffffffffffff811115611fba57611fba611eec565b611feb60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601611f19565b818152876020838501011115611fff575f5ffd5b816020840160208301375f602092820183015294508501359250611d4d905060408501611d08565b5f5f60408385031215612038575f5ffd5b823567ffffffffffffffff81111561204e575f5ffd5b8301601f8101851361205e575f5ffd5b803567ffffffffffffffff81111561207857612078611eec565b8060051b61208860208201611f19565b918252602081840181019290810190888411156120a3575f5ffd5b6020850194505b838510156120c9578435808352602095860195909350909101906120aa565b8096505050505050611d7760208401611d08565b5f5f83601f8401126120ed575f5ffd5b50813567ffffffffffffffff811115612104575f5ffd5b60208301915083602082850101111561211b575f5ffd5b9250929050565b5f5f5f5f5f60608688031215612136575f5ffd5b853567ffffffffffffffff81111561214c575f5ffd5b612158888289016120dd565b909650945050602086013567ffffffffffffffff811115612177575f5ffd5b612183888289016120dd565b9094509250612196905060408701611d08565b90509295509295909350565b5f602082840312156121b2575f5ffd5b815161118381611cfb565b602080825282518282018190525f918401906040840190835b81811015611cb45783518352602093840193909201916001016121d6565b81835281816020850137505f602082840101525f60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b604081525f61224e6040830186886121f4565b82810360208401526122618185876121f4565b979650505050505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b60208101600183106122d2577f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b91905290565b600181811c908216806122ec57607f821691505b602082108103612323577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b50919050565b601f82111561236d57805f5260205f20601f840160051c8101602085101561234e5750805b601f840160051c820191505b81811015610e08575f815560010161235a565b505050565b815167ffffffffffffffff81111561238c5761238c611eec565b6123a08161239a84546122d8565b84612329565b6020601f8211600181146123f1575f83156123bb5750848201515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600385901b1c1916600184901b178455610e08565b5f848152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08516915b8281101561243e578785015182556020948501946001909201910161241e565b508482101561247a57868401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b60f8161c191681555b50505050600190811b01905550565b818103818111156103a9577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603160045260245ffdfea164736f6c634300081c000a", +} + +// EspressoTEEVerifierABI is the input ABI used to generate the binding from. +// Deprecated: Use EspressoTEEVerifierMetaData.ABI instead. +var EspressoTEEVerifierABI = EspressoTEEVerifierMetaData.ABI + +// EspressoTEEVerifierBin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use EspressoTEEVerifierMetaData.Bin instead. +var EspressoTEEVerifierBin = EspressoTEEVerifierMetaData.Bin + +// DeployEspressoTEEVerifier deploys a new Ethereum contract, binding an instance of EspressoTEEVerifier to it. +func DeployEspressoTEEVerifier(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *EspressoTEEVerifier, error) { + parsed, err := EspressoTEEVerifierMetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(EspressoTEEVerifierBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &EspressoTEEVerifier{EspressoTEEVerifierCaller: EspressoTEEVerifierCaller{contract: contract}, EspressoTEEVerifierTransactor: EspressoTEEVerifierTransactor{contract: contract}, EspressoTEEVerifierFilterer: EspressoTEEVerifierFilterer{contract: contract}}, nil +} + +// EspressoTEEVerifier is an auto generated Go binding around an Ethereum contract. +type EspressoTEEVerifier struct { + EspressoTEEVerifierCaller // Read-only binding to the contract + EspressoTEEVerifierTransactor // Write-only binding to the contract + EspressoTEEVerifierFilterer // Log filterer for contract events +} + +// EspressoTEEVerifierCaller is an auto generated read-only Go binding around an Ethereum contract. +type EspressoTEEVerifierCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// EspressoTEEVerifierTransactor is an auto generated write-only Go binding around an Ethereum contract. +type EspressoTEEVerifierTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// EspressoTEEVerifierFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type EspressoTEEVerifierFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// EspressoTEEVerifierSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type EspressoTEEVerifierSession struct { + Contract *EspressoTEEVerifier // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// EspressoTEEVerifierCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type EspressoTEEVerifierCallerSession struct { + Contract *EspressoTEEVerifierCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// EspressoTEEVerifierTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type EspressoTEEVerifierTransactorSession struct { + Contract *EspressoTEEVerifierTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// EspressoTEEVerifierRaw is an auto generated low-level Go binding around an Ethereum contract. +type EspressoTEEVerifierRaw struct { + Contract *EspressoTEEVerifier // Generic contract binding to access the raw methods on +} + +// EspressoTEEVerifierCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type EspressoTEEVerifierCallerRaw struct { + Contract *EspressoTEEVerifierCaller // Generic read-only contract binding to access the raw methods on +} + +// EspressoTEEVerifierTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type EspressoTEEVerifierTransactorRaw struct { + Contract *EspressoTEEVerifierTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewEspressoTEEVerifier creates a new instance of EspressoTEEVerifier, bound to a specific deployed contract. +func NewEspressoTEEVerifier(address common.Address, backend bind.ContractBackend) (*EspressoTEEVerifier, error) { + contract, err := bindEspressoTEEVerifier(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &EspressoTEEVerifier{EspressoTEEVerifierCaller: EspressoTEEVerifierCaller{contract: contract}, EspressoTEEVerifierTransactor: EspressoTEEVerifierTransactor{contract: contract}, EspressoTEEVerifierFilterer: EspressoTEEVerifierFilterer{contract: contract}}, nil +} + +// NewEspressoTEEVerifierCaller creates a new read-only instance of EspressoTEEVerifier, bound to a specific deployed contract. +func NewEspressoTEEVerifierCaller(address common.Address, caller bind.ContractCaller) (*EspressoTEEVerifierCaller, error) { + contract, err := bindEspressoTEEVerifier(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &EspressoTEEVerifierCaller{contract: contract}, nil +} + +// NewEspressoTEEVerifierTransactor creates a new write-only instance of EspressoTEEVerifier, bound to a specific deployed contract. +func NewEspressoTEEVerifierTransactor(address common.Address, transactor bind.ContractTransactor) (*EspressoTEEVerifierTransactor, error) { + contract, err := bindEspressoTEEVerifier(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &EspressoTEEVerifierTransactor{contract: contract}, nil +} + +// NewEspressoTEEVerifierFilterer creates a new log filterer instance of EspressoTEEVerifier, bound to a specific deployed contract. +func NewEspressoTEEVerifierFilterer(address common.Address, filterer bind.ContractFilterer) (*EspressoTEEVerifierFilterer, error) { + contract, err := bindEspressoTEEVerifier(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &EspressoTEEVerifierFilterer{contract: contract}, nil +} + +// bindEspressoTEEVerifier binds a generic wrapper to an already deployed contract. +func bindEspressoTEEVerifier(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := EspressoTEEVerifierMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_EspressoTEEVerifier *EspressoTEEVerifierRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _EspressoTEEVerifier.Contract.EspressoTEEVerifierCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_EspressoTEEVerifier *EspressoTEEVerifierRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _EspressoTEEVerifier.Contract.EspressoTEEVerifierTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_EspressoTEEVerifier *EspressoTEEVerifierRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _EspressoTEEVerifier.Contract.EspressoTEEVerifierTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_EspressoTEEVerifier *EspressoTEEVerifierCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _EspressoTEEVerifier.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_EspressoTEEVerifier *EspressoTEEVerifierTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _EspressoTEEVerifier.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_EspressoTEEVerifier *EspressoTEEVerifierTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _EspressoTEEVerifier.Contract.contract.Transact(opts, method, params...) +} + +// Eip712Domain is a free data retrieval call binding the contract method 0x84b0196e. +// +// Solidity: function eip712Domain() view returns(bytes1 fields, string name, string version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] extensions) +func (_EspressoTEEVerifier *EspressoTEEVerifierCaller) Eip712Domain(opts *bind.CallOpts) (struct { + Fields [1]byte + Name string + Version string + ChainId *big.Int + VerifyingContract common.Address + Salt [32]byte + Extensions []*big.Int +}, error) { + var out []interface{} + err := _EspressoTEEVerifier.contract.Call(opts, &out, "eip712Domain") + + outstruct := new(struct { + Fields [1]byte + Name string + Version string + ChainId *big.Int + VerifyingContract common.Address + Salt [32]byte + Extensions []*big.Int + }) + if err != nil { + return *outstruct, err + } + + outstruct.Fields = *abi.ConvertType(out[0], new([1]byte)).(*[1]byte) + outstruct.Name = *abi.ConvertType(out[1], new(string)).(*string) + outstruct.Version = *abi.ConvertType(out[2], new(string)).(*string) + outstruct.ChainId = *abi.ConvertType(out[3], new(*big.Int)).(**big.Int) + outstruct.VerifyingContract = *abi.ConvertType(out[4], new(common.Address)).(*common.Address) + outstruct.Salt = *abi.ConvertType(out[5], new([32]byte)).(*[32]byte) + outstruct.Extensions = *abi.ConvertType(out[6], new([]*big.Int)).(*[]*big.Int) + + return *outstruct, err + +} + +// Eip712Domain is a free data retrieval call binding the contract method 0x84b0196e. +// +// Solidity: function eip712Domain() view returns(bytes1 fields, string name, string version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] extensions) +func (_EspressoTEEVerifier *EspressoTEEVerifierSession) Eip712Domain() (struct { + Fields [1]byte + Name string + Version string + ChainId *big.Int + VerifyingContract common.Address + Salt [32]byte + Extensions []*big.Int +}, error) { + return _EspressoTEEVerifier.Contract.Eip712Domain(&_EspressoTEEVerifier.CallOpts) +} + +// Eip712Domain is a free data retrieval call binding the contract method 0x84b0196e. +// +// Solidity: function eip712Domain() view returns(bytes1 fields, string name, string version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] extensions) +func (_EspressoTEEVerifier *EspressoTEEVerifierCallerSession) Eip712Domain() (struct { + Fields [1]byte + Name string + Version string + ChainId *big.Int + VerifyingContract common.Address + Salt [32]byte + Extensions []*big.Int +}, error) { + return _EspressoTEEVerifier.Contract.Eip712Domain(&_EspressoTEEVerifier.CallOpts) +} + +// EspressoNitroTEEVerifier is a free data retrieval call binding the contract method 0xd80a4c28. +// +// Solidity: function espressoNitroTEEVerifier() view returns(address) +func (_EspressoTEEVerifier *EspressoTEEVerifierCaller) EspressoNitroTEEVerifier(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _EspressoTEEVerifier.contract.Call(opts, &out, "espressoNitroTEEVerifier") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// EspressoNitroTEEVerifier is a free data retrieval call binding the contract method 0xd80a4c28. +// +// Solidity: function espressoNitroTEEVerifier() view returns(address) +func (_EspressoTEEVerifier *EspressoTEEVerifierSession) EspressoNitroTEEVerifier() (common.Address, error) { + return _EspressoTEEVerifier.Contract.EspressoNitroTEEVerifier(&_EspressoTEEVerifier.CallOpts) +} + +// EspressoNitroTEEVerifier is a free data retrieval call binding the contract method 0xd80a4c28. +// +// Solidity: function espressoNitroTEEVerifier() view returns(address) +func (_EspressoTEEVerifier *EspressoTEEVerifierCallerSession) EspressoNitroTEEVerifier() (common.Address, error) { + return _EspressoTEEVerifier.Contract.EspressoNitroTEEVerifier(&_EspressoTEEVerifier.CallOpts) +} + +// GetGuardians is a free data retrieval call binding the contract method 0x0665f04b. +// +// Solidity: function getGuardians() view returns(address[]) +func (_EspressoTEEVerifier *EspressoTEEVerifierCaller) GetGuardians(opts *bind.CallOpts) ([]common.Address, error) { + var out []interface{} + err := _EspressoTEEVerifier.contract.Call(opts, &out, "getGuardians") + + if err != nil { + return *new([]common.Address), err + } + + out0 := *abi.ConvertType(out[0], new([]common.Address)).(*[]common.Address) + + return out0, err + +} + +// GetGuardians is a free data retrieval call binding the contract method 0x0665f04b. +// +// Solidity: function getGuardians() view returns(address[]) +func (_EspressoTEEVerifier *EspressoTEEVerifierSession) GetGuardians() ([]common.Address, error) { + return _EspressoTEEVerifier.Contract.GetGuardians(&_EspressoTEEVerifier.CallOpts) +} + +// GetGuardians is a free data retrieval call binding the contract method 0x0665f04b. +// +// Solidity: function getGuardians() view returns(address[]) +func (_EspressoTEEVerifier *EspressoTEEVerifierCallerSession) GetGuardians() ([]common.Address, error) { + return _EspressoTEEVerifier.Contract.GetGuardians(&_EspressoTEEVerifier.CallOpts) +} + +// GuardianCount is a free data retrieval call binding the contract method 0x54387ad7. +// +// Solidity: function guardianCount() view returns(uint256) +func (_EspressoTEEVerifier *EspressoTEEVerifierCaller) GuardianCount(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _EspressoTEEVerifier.contract.Call(opts, &out, "guardianCount") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GuardianCount is a free data retrieval call binding the contract method 0x54387ad7. +// +// Solidity: function guardianCount() view returns(uint256) +func (_EspressoTEEVerifier *EspressoTEEVerifierSession) GuardianCount() (*big.Int, error) { + return _EspressoTEEVerifier.Contract.GuardianCount(&_EspressoTEEVerifier.CallOpts) +} + +// GuardianCount is a free data retrieval call binding the contract method 0x54387ad7. +// +// Solidity: function guardianCount() view returns(uint256) +func (_EspressoTEEVerifier *EspressoTEEVerifierCallerSession) GuardianCount() (*big.Int, error) { + return _EspressoTEEVerifier.Contract.GuardianCount(&_EspressoTEEVerifier.CallOpts) +} + +// IsGuardian is a free data retrieval call binding the contract method 0x0c68ba21. +// +// Solidity: function isGuardian(address account) view returns(bool) +func (_EspressoTEEVerifier *EspressoTEEVerifierCaller) IsGuardian(opts *bind.CallOpts, account common.Address) (bool, error) { + var out []interface{} + err := _EspressoTEEVerifier.contract.Call(opts, &out, "isGuardian", account) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// IsGuardian is a free data retrieval call binding the contract method 0x0c68ba21. +// +// Solidity: function isGuardian(address account) view returns(bool) +func (_EspressoTEEVerifier *EspressoTEEVerifierSession) IsGuardian(account common.Address) (bool, error) { + return _EspressoTEEVerifier.Contract.IsGuardian(&_EspressoTEEVerifier.CallOpts, account) +} + +// IsGuardian is a free data retrieval call binding the contract method 0x0c68ba21. +// +// Solidity: function isGuardian(address account) view returns(bool) +func (_EspressoTEEVerifier *EspressoTEEVerifierCallerSession) IsGuardian(account common.Address) (bool, error) { + return _EspressoTEEVerifier.Contract.IsGuardian(&_EspressoTEEVerifier.CallOpts, account) +} + +// IsSignerValid is a free data retrieval call binding the contract method 0x6d8f5aa9. +// +// Solidity: function isSignerValid(address signer, uint8 teeType) view returns(bool) +func (_EspressoTEEVerifier *EspressoTEEVerifierCaller) IsSignerValid(opts *bind.CallOpts, signer common.Address, teeType uint8) (bool, error) { + var out []interface{} + err := _EspressoTEEVerifier.contract.Call(opts, &out, "isSignerValid", signer, teeType) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// IsSignerValid is a free data retrieval call binding the contract method 0x6d8f5aa9. +// +// Solidity: function isSignerValid(address signer, uint8 teeType) view returns(bool) +func (_EspressoTEEVerifier *EspressoTEEVerifierSession) IsSignerValid(signer common.Address, teeType uint8) (bool, error) { + return _EspressoTEEVerifier.Contract.IsSignerValid(&_EspressoTEEVerifier.CallOpts, signer, teeType) +} + +// IsSignerValid is a free data retrieval call binding the contract method 0x6d8f5aa9. +// +// Solidity: function isSignerValid(address signer, uint8 teeType) view returns(bool) +func (_EspressoTEEVerifier *EspressoTEEVerifierCallerSession) IsSignerValid(signer common.Address, teeType uint8) (bool, error) { + return _EspressoTEEVerifier.Contract.IsSignerValid(&_EspressoTEEVerifier.CallOpts, signer, teeType) +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_EspressoTEEVerifier *EspressoTEEVerifierCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _EspressoTEEVerifier.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_EspressoTEEVerifier *EspressoTEEVerifierSession) Owner() (common.Address, error) { + return _EspressoTEEVerifier.Contract.Owner(&_EspressoTEEVerifier.CallOpts) +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_EspressoTEEVerifier *EspressoTEEVerifierCallerSession) Owner() (common.Address, error) { + return _EspressoTEEVerifier.Contract.Owner(&_EspressoTEEVerifier.CallOpts) +} + +// PendingOwner is a free data retrieval call binding the contract method 0xe30c3978. +// +// Solidity: function pendingOwner() view returns(address) +func (_EspressoTEEVerifier *EspressoTEEVerifierCaller) PendingOwner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _EspressoTEEVerifier.contract.Call(opts, &out, "pendingOwner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// PendingOwner is a free data retrieval call binding the contract method 0xe30c3978. +// +// Solidity: function pendingOwner() view returns(address) +func (_EspressoTEEVerifier *EspressoTEEVerifierSession) PendingOwner() (common.Address, error) { + return _EspressoTEEVerifier.Contract.PendingOwner(&_EspressoTEEVerifier.CallOpts) +} + +// PendingOwner is a free data retrieval call binding the contract method 0xe30c3978. +// +// Solidity: function pendingOwner() view returns(address) +func (_EspressoTEEVerifier *EspressoTEEVerifierCallerSession) PendingOwner() (common.Address, error) { + return _EspressoTEEVerifier.Contract.PendingOwner(&_EspressoTEEVerifier.CallOpts) +} + +// RegisteredEnclaveHashes is a free data retrieval call binding the contract method 0x3cbe6803. +// +// Solidity: function registeredEnclaveHashes(bytes32 enclaveHash, uint8 teeType) view returns(bool) +func (_EspressoTEEVerifier *EspressoTEEVerifierCaller) RegisteredEnclaveHashes(opts *bind.CallOpts, enclaveHash [32]byte, teeType uint8) (bool, error) { + var out []interface{} + err := _EspressoTEEVerifier.contract.Call(opts, &out, "registeredEnclaveHashes", enclaveHash, teeType) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// RegisteredEnclaveHashes is a free data retrieval call binding the contract method 0x3cbe6803. +// +// Solidity: function registeredEnclaveHashes(bytes32 enclaveHash, uint8 teeType) view returns(bool) +func (_EspressoTEEVerifier *EspressoTEEVerifierSession) RegisteredEnclaveHashes(enclaveHash [32]byte, teeType uint8) (bool, error) { + return _EspressoTEEVerifier.Contract.RegisteredEnclaveHashes(&_EspressoTEEVerifier.CallOpts, enclaveHash, teeType) +} + +// RegisteredEnclaveHashes is a free data retrieval call binding the contract method 0x3cbe6803. +// +// Solidity: function registeredEnclaveHashes(bytes32 enclaveHash, uint8 teeType) view returns(bool) +func (_EspressoTEEVerifier *EspressoTEEVerifierCallerSession) RegisteredEnclaveHashes(enclaveHash [32]byte, teeType uint8) (bool, error) { + return _EspressoTEEVerifier.Contract.RegisteredEnclaveHashes(&_EspressoTEEVerifier.CallOpts, enclaveHash, teeType) +} + +// Verify is a free data retrieval call binding the contract method 0xa81d9c5c. +// +// Solidity: function verify(bytes signature, bytes32 userDataHash, uint8 teeType) view returns(bool) +func (_EspressoTEEVerifier *EspressoTEEVerifierCaller) Verify(opts *bind.CallOpts, signature []byte, userDataHash [32]byte, teeType uint8) (bool, error) { + var out []interface{} + err := _EspressoTEEVerifier.contract.Call(opts, &out, "verify", signature, userDataHash, teeType) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// Verify is a free data retrieval call binding the contract method 0xa81d9c5c. +// +// Solidity: function verify(bytes signature, bytes32 userDataHash, uint8 teeType) view returns(bool) +func (_EspressoTEEVerifier *EspressoTEEVerifierSession) Verify(signature []byte, userDataHash [32]byte, teeType uint8) (bool, error) { + return _EspressoTEEVerifier.Contract.Verify(&_EspressoTEEVerifier.CallOpts, signature, userDataHash, teeType) +} + +// Verify is a free data retrieval call binding the contract method 0xa81d9c5c. +// +// Solidity: function verify(bytes signature, bytes32 userDataHash, uint8 teeType) view returns(bool) +func (_EspressoTEEVerifier *EspressoTEEVerifierCallerSession) Verify(signature []byte, userDataHash [32]byte, teeType uint8) (bool, error) { + return _EspressoTEEVerifier.Contract.Verify(&_EspressoTEEVerifier.CallOpts, signature, userDataHash, teeType) +} + +// AcceptOwnership is a paid mutator transaction binding the contract method 0x79ba5097. +// +// Solidity: function acceptOwnership() returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierTransactor) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _EspressoTEEVerifier.contract.Transact(opts, "acceptOwnership") +} + +// AcceptOwnership is a paid mutator transaction binding the contract method 0x79ba5097. +// +// Solidity: function acceptOwnership() returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierSession) AcceptOwnership() (*types.Transaction, error) { + return _EspressoTEEVerifier.Contract.AcceptOwnership(&_EspressoTEEVerifier.TransactOpts) +} + +// AcceptOwnership is a paid mutator transaction binding the contract method 0x79ba5097. +// +// Solidity: function acceptOwnership() returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierTransactorSession) AcceptOwnership() (*types.Transaction, error) { + return _EspressoTEEVerifier.Contract.AcceptOwnership(&_EspressoTEEVerifier.TransactOpts) +} + +// AddGuardian is a paid mutator transaction binding the contract method 0xa526d83b. +// +// Solidity: function addGuardian(address guardian) returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierTransactor) AddGuardian(opts *bind.TransactOpts, guardian common.Address) (*types.Transaction, error) { + return _EspressoTEEVerifier.contract.Transact(opts, "addGuardian", guardian) +} + +// AddGuardian is a paid mutator transaction binding the contract method 0xa526d83b. +// +// Solidity: function addGuardian(address guardian) returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierSession) AddGuardian(guardian common.Address) (*types.Transaction, error) { + return _EspressoTEEVerifier.Contract.AddGuardian(&_EspressoTEEVerifier.TransactOpts, guardian) +} + +// AddGuardian is a paid mutator transaction binding the contract method 0xa526d83b. +// +// Solidity: function addGuardian(address guardian) returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierTransactorSession) AddGuardian(guardian common.Address) (*types.Transaction, error) { + return _EspressoTEEVerifier.Contract.AddGuardian(&_EspressoTEEVerifier.TransactOpts, guardian) +} + +// DeleteEnclaveHashes is a paid mutator transaction binding the contract method 0xcd8f6997. +// +// Solidity: function deleteEnclaveHashes(bytes32[] enclaveHashes, uint8 teeType) returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierTransactor) DeleteEnclaveHashes(opts *bind.TransactOpts, enclaveHashes [][32]byte, teeType uint8) (*types.Transaction, error) { + return _EspressoTEEVerifier.contract.Transact(opts, "deleteEnclaveHashes", enclaveHashes, teeType) +} + +// DeleteEnclaveHashes is a paid mutator transaction binding the contract method 0xcd8f6997. +// +// Solidity: function deleteEnclaveHashes(bytes32[] enclaveHashes, uint8 teeType) returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierSession) DeleteEnclaveHashes(enclaveHashes [][32]byte, teeType uint8) (*types.Transaction, error) { + return _EspressoTEEVerifier.Contract.DeleteEnclaveHashes(&_EspressoTEEVerifier.TransactOpts, enclaveHashes, teeType) +} + +// DeleteEnclaveHashes is a paid mutator transaction binding the contract method 0xcd8f6997. +// +// Solidity: function deleteEnclaveHashes(bytes32[] enclaveHashes, uint8 teeType) returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierTransactorSession) DeleteEnclaveHashes(enclaveHashes [][32]byte, teeType uint8) (*types.Transaction, error) { + return _EspressoTEEVerifier.Contract.DeleteEnclaveHashes(&_EspressoTEEVerifier.TransactOpts, enclaveHashes, teeType) +} + +// Initialize is a paid mutator transaction binding the contract method 0x485cc955. +// +// Solidity: function initialize(address _owner, address _espressoNitroTEEVerifier) returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierTransactor) Initialize(opts *bind.TransactOpts, _owner common.Address, _espressoNitroTEEVerifier common.Address) (*types.Transaction, error) { + return _EspressoTEEVerifier.contract.Transact(opts, "initialize", _owner, _espressoNitroTEEVerifier) +} + +// Initialize is a paid mutator transaction binding the contract method 0x485cc955. +// +// Solidity: function initialize(address _owner, address _espressoNitroTEEVerifier) returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierSession) Initialize(_owner common.Address, _espressoNitroTEEVerifier common.Address) (*types.Transaction, error) { + return _EspressoTEEVerifier.Contract.Initialize(&_EspressoTEEVerifier.TransactOpts, _owner, _espressoNitroTEEVerifier) +} + +// Initialize is a paid mutator transaction binding the contract method 0x485cc955. +// +// Solidity: function initialize(address _owner, address _espressoNitroTEEVerifier) returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierTransactorSession) Initialize(_owner common.Address, _espressoNitroTEEVerifier common.Address) (*types.Transaction, error) { + return _EspressoTEEVerifier.Contract.Initialize(&_EspressoTEEVerifier.TransactOpts, _owner, _espressoNitroTEEVerifier) +} + +// RegisterService is a paid mutator transaction binding the contract method 0xdac79fc8. +// +// Solidity: function registerService(bytes verificationData, bytes data, uint8 teeType) returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierTransactor) RegisterService(opts *bind.TransactOpts, verificationData []byte, data []byte, teeType uint8) (*types.Transaction, error) { + return _EspressoTEEVerifier.contract.Transact(opts, "registerService", verificationData, data, teeType) +} + +// RegisterService is a paid mutator transaction binding the contract method 0xdac79fc8. +// +// Solidity: function registerService(bytes verificationData, bytes data, uint8 teeType) returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierSession) RegisterService(verificationData []byte, data []byte, teeType uint8) (*types.Transaction, error) { + return _EspressoTEEVerifier.Contract.RegisterService(&_EspressoTEEVerifier.TransactOpts, verificationData, data, teeType) +} + +// RegisterService is a paid mutator transaction binding the contract method 0xdac79fc8. +// +// Solidity: function registerService(bytes verificationData, bytes data, uint8 teeType) returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierTransactorSession) RegisterService(verificationData []byte, data []byte, teeType uint8) (*types.Transaction, error) { + return _EspressoTEEVerifier.Contract.RegisterService(&_EspressoTEEVerifier.TransactOpts, verificationData, data, teeType) +} + +// RemoveGuardian is a paid mutator transaction binding the contract method 0x71404156. +// +// Solidity: function removeGuardian(address guardian) returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierTransactor) RemoveGuardian(opts *bind.TransactOpts, guardian common.Address) (*types.Transaction, error) { + return _EspressoTEEVerifier.contract.Transact(opts, "removeGuardian", guardian) +} + +// RemoveGuardian is a paid mutator transaction binding the contract method 0x71404156. +// +// Solidity: function removeGuardian(address guardian) returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierSession) RemoveGuardian(guardian common.Address) (*types.Transaction, error) { + return _EspressoTEEVerifier.Contract.RemoveGuardian(&_EspressoTEEVerifier.TransactOpts, guardian) +} + +// RemoveGuardian is a paid mutator transaction binding the contract method 0x71404156. +// +// Solidity: function removeGuardian(address guardian) returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierTransactorSession) RemoveGuardian(guardian common.Address) (*types.Transaction, error) { + return _EspressoTEEVerifier.Contract.RemoveGuardian(&_EspressoTEEVerifier.TransactOpts, guardian) +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierTransactor) RenounceOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _EspressoTEEVerifier.contract.Transact(opts, "renounceOwnership") +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierSession) RenounceOwnership() (*types.Transaction, error) { + return _EspressoTEEVerifier.Contract.RenounceOwnership(&_EspressoTEEVerifier.TransactOpts) +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierTransactorSession) RenounceOwnership() (*types.Transaction, error) { + return _EspressoTEEVerifier.Contract.RenounceOwnership(&_EspressoTEEVerifier.TransactOpts) +} + +// SetEnclaveHash is a paid mutator transaction binding the contract method 0x0f1f0f86. +// +// Solidity: function setEnclaveHash(bytes32 enclaveHash, bool valid, uint8 teeType) returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierTransactor) SetEnclaveHash(opts *bind.TransactOpts, enclaveHash [32]byte, valid bool, teeType uint8) (*types.Transaction, error) { + return _EspressoTEEVerifier.contract.Transact(opts, "setEnclaveHash", enclaveHash, valid, teeType) +} + +// SetEnclaveHash is a paid mutator transaction binding the contract method 0x0f1f0f86. +// +// Solidity: function setEnclaveHash(bytes32 enclaveHash, bool valid, uint8 teeType) returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierSession) SetEnclaveHash(enclaveHash [32]byte, valid bool, teeType uint8) (*types.Transaction, error) { + return _EspressoTEEVerifier.Contract.SetEnclaveHash(&_EspressoTEEVerifier.TransactOpts, enclaveHash, valid, teeType) +} + +// SetEnclaveHash is a paid mutator transaction binding the contract method 0x0f1f0f86. +// +// Solidity: function setEnclaveHash(bytes32 enclaveHash, bool valid, uint8 teeType) returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierTransactorSession) SetEnclaveHash(enclaveHash [32]byte, valid bool, teeType uint8) (*types.Transaction, error) { + return _EspressoTEEVerifier.Contract.SetEnclaveHash(&_EspressoTEEVerifier.TransactOpts, enclaveHash, valid, teeType) +} + +// SetEspressoNitroTEEVerifier is a paid mutator transaction binding the contract method 0x330282f5. +// +// Solidity: function setEspressoNitroTEEVerifier(address _espressoNitroTEEVerifier) returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierTransactor) SetEspressoNitroTEEVerifier(opts *bind.TransactOpts, _espressoNitroTEEVerifier common.Address) (*types.Transaction, error) { + return _EspressoTEEVerifier.contract.Transact(opts, "setEspressoNitroTEEVerifier", _espressoNitroTEEVerifier) +} + +// SetEspressoNitroTEEVerifier is a paid mutator transaction binding the contract method 0x330282f5. +// +// Solidity: function setEspressoNitroTEEVerifier(address _espressoNitroTEEVerifier) returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierSession) SetEspressoNitroTEEVerifier(_espressoNitroTEEVerifier common.Address) (*types.Transaction, error) { + return _EspressoTEEVerifier.Contract.SetEspressoNitroTEEVerifier(&_EspressoTEEVerifier.TransactOpts, _espressoNitroTEEVerifier) +} + +// SetEspressoNitroTEEVerifier is a paid mutator transaction binding the contract method 0x330282f5. +// +// Solidity: function setEspressoNitroTEEVerifier(address _espressoNitroTEEVerifier) returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierTransactorSession) SetEspressoNitroTEEVerifier(_espressoNitroTEEVerifier common.Address) (*types.Transaction, error) { + return _EspressoTEEVerifier.Contract.SetEspressoNitroTEEVerifier(&_EspressoTEEVerifier.TransactOpts, _espressoNitroTEEVerifier) +} + +// SetNitroEnclaveVerifier is a paid mutator transaction binding the contract method 0xa628a19e. +// +// Solidity: function setNitroEnclaveVerifier(address nitroVerifier) returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierTransactor) SetNitroEnclaveVerifier(opts *bind.TransactOpts, nitroVerifier common.Address) (*types.Transaction, error) { + return _EspressoTEEVerifier.contract.Transact(opts, "setNitroEnclaveVerifier", nitroVerifier) +} + +// SetNitroEnclaveVerifier is a paid mutator transaction binding the contract method 0xa628a19e. +// +// Solidity: function setNitroEnclaveVerifier(address nitroVerifier) returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierSession) SetNitroEnclaveVerifier(nitroVerifier common.Address) (*types.Transaction, error) { + return _EspressoTEEVerifier.Contract.SetNitroEnclaveVerifier(&_EspressoTEEVerifier.TransactOpts, nitroVerifier) +} + +// SetNitroEnclaveVerifier is a paid mutator transaction binding the contract method 0xa628a19e. +// +// Solidity: function setNitroEnclaveVerifier(address nitroVerifier) returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierTransactorSession) SetNitroEnclaveVerifier(nitroVerifier common.Address) (*types.Transaction, error) { + return _EspressoTEEVerifier.Contract.SetNitroEnclaveVerifier(&_EspressoTEEVerifier.TransactOpts, nitroVerifier) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierTransactor) TransferOwnership(opts *bind.TransactOpts, newOwner common.Address) (*types.Transaction, error) { + return _EspressoTEEVerifier.contract.Transact(opts, "transferOwnership", newOwner) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) { + return _EspressoTEEVerifier.Contract.TransferOwnership(&_EspressoTEEVerifier.TransactOpts, newOwner) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_EspressoTEEVerifier *EspressoTEEVerifierTransactorSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) { + return _EspressoTEEVerifier.Contract.TransferOwnership(&_EspressoTEEVerifier.TransactOpts, newOwner) +} + +// EspressoTEEVerifierEIP712DomainChangedIterator is returned from FilterEIP712DomainChanged and is used to iterate over the raw logs and unpacked data for EIP712DomainChanged events raised by the EspressoTEEVerifier contract. +type EspressoTEEVerifierEIP712DomainChangedIterator struct { + Event *EspressoTEEVerifierEIP712DomainChanged // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *EspressoTEEVerifierEIP712DomainChangedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(EspressoTEEVerifierEIP712DomainChanged) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(EspressoTEEVerifierEIP712DomainChanged) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *EspressoTEEVerifierEIP712DomainChangedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *EspressoTEEVerifierEIP712DomainChangedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// EspressoTEEVerifierEIP712DomainChanged represents a EIP712DomainChanged event raised by the EspressoTEEVerifier contract. +type EspressoTEEVerifierEIP712DomainChanged struct { + Raw types.Log // Blockchain specific contextual infos +} + +// FilterEIP712DomainChanged is a free log retrieval operation binding the contract event 0x0a6387c9ea3628b88a633bb4f3b151770f70085117a15f9bf3787cda53f13d31. +// +// Solidity: event EIP712DomainChanged() +func (_EspressoTEEVerifier *EspressoTEEVerifierFilterer) FilterEIP712DomainChanged(opts *bind.FilterOpts) (*EspressoTEEVerifierEIP712DomainChangedIterator, error) { + + logs, sub, err := _EspressoTEEVerifier.contract.FilterLogs(opts, "EIP712DomainChanged") + if err != nil { + return nil, err + } + return &EspressoTEEVerifierEIP712DomainChangedIterator{contract: _EspressoTEEVerifier.contract, event: "EIP712DomainChanged", logs: logs, sub: sub}, nil +} + +// WatchEIP712DomainChanged is a free log subscription operation binding the contract event 0x0a6387c9ea3628b88a633bb4f3b151770f70085117a15f9bf3787cda53f13d31. +// +// Solidity: event EIP712DomainChanged() +func (_EspressoTEEVerifier *EspressoTEEVerifierFilterer) WatchEIP712DomainChanged(opts *bind.WatchOpts, sink chan<- *EspressoTEEVerifierEIP712DomainChanged) (event.Subscription, error) { + + logs, sub, err := _EspressoTEEVerifier.contract.WatchLogs(opts, "EIP712DomainChanged") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(EspressoTEEVerifierEIP712DomainChanged) + if err := _EspressoTEEVerifier.contract.UnpackLog(event, "EIP712DomainChanged", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseEIP712DomainChanged is a log parse operation binding the contract event 0x0a6387c9ea3628b88a633bb4f3b151770f70085117a15f9bf3787cda53f13d31. +// +// Solidity: event EIP712DomainChanged() +func (_EspressoTEEVerifier *EspressoTEEVerifierFilterer) ParseEIP712DomainChanged(log types.Log) (*EspressoTEEVerifierEIP712DomainChanged, error) { + event := new(EspressoTEEVerifierEIP712DomainChanged) + if err := _EspressoTEEVerifier.contract.UnpackLog(event, "EIP712DomainChanged", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// EspressoTEEVerifierEspressoNitroTEEVerifierSetIterator is returned from FilterEspressoNitroTEEVerifierSet and is used to iterate over the raw logs and unpacked data for EspressoNitroTEEVerifierSet events raised by the EspressoTEEVerifier contract. +type EspressoTEEVerifierEspressoNitroTEEVerifierSetIterator struct { + Event *EspressoTEEVerifierEspressoNitroTEEVerifierSet // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *EspressoTEEVerifierEspressoNitroTEEVerifierSetIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(EspressoTEEVerifierEspressoNitroTEEVerifierSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(EspressoTEEVerifierEspressoNitroTEEVerifierSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *EspressoTEEVerifierEspressoNitroTEEVerifierSetIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *EspressoTEEVerifierEspressoNitroTEEVerifierSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// EspressoTEEVerifierEspressoNitroTEEVerifierSet represents a EspressoNitroTEEVerifierSet event raised by the EspressoTEEVerifier contract. +type EspressoTEEVerifierEspressoNitroTEEVerifierSet struct { + OldVerifier common.Address + NewVerifier common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterEspressoNitroTEEVerifierSet is a free log retrieval operation binding the contract event 0xacbe6384d204f2883b25fa4d03777cb88af5b2afaaffbe06609d3e17dc68d096. +// +// Solidity: event EspressoNitroTEEVerifierSet(address indexed oldVerifier, address indexed newVerifier) +func (_EspressoTEEVerifier *EspressoTEEVerifierFilterer) FilterEspressoNitroTEEVerifierSet(opts *bind.FilterOpts, oldVerifier []common.Address, newVerifier []common.Address) (*EspressoTEEVerifierEspressoNitroTEEVerifierSetIterator, error) { + + var oldVerifierRule []interface{} + for _, oldVerifierItem := range oldVerifier { + oldVerifierRule = append(oldVerifierRule, oldVerifierItem) + } + var newVerifierRule []interface{} + for _, newVerifierItem := range newVerifier { + newVerifierRule = append(newVerifierRule, newVerifierItem) + } + + logs, sub, err := _EspressoTEEVerifier.contract.FilterLogs(opts, "EspressoNitroTEEVerifierSet", oldVerifierRule, newVerifierRule) + if err != nil { + return nil, err + } + return &EspressoTEEVerifierEspressoNitroTEEVerifierSetIterator{contract: _EspressoTEEVerifier.contract, event: "EspressoNitroTEEVerifierSet", logs: logs, sub: sub}, nil +} + +// WatchEspressoNitroTEEVerifierSet is a free log subscription operation binding the contract event 0xacbe6384d204f2883b25fa4d03777cb88af5b2afaaffbe06609d3e17dc68d096. +// +// Solidity: event EspressoNitroTEEVerifierSet(address indexed oldVerifier, address indexed newVerifier) +func (_EspressoTEEVerifier *EspressoTEEVerifierFilterer) WatchEspressoNitroTEEVerifierSet(opts *bind.WatchOpts, sink chan<- *EspressoTEEVerifierEspressoNitroTEEVerifierSet, oldVerifier []common.Address, newVerifier []common.Address) (event.Subscription, error) { + + var oldVerifierRule []interface{} + for _, oldVerifierItem := range oldVerifier { + oldVerifierRule = append(oldVerifierRule, oldVerifierItem) + } + var newVerifierRule []interface{} + for _, newVerifierItem := range newVerifier { + newVerifierRule = append(newVerifierRule, newVerifierItem) + } + + logs, sub, err := _EspressoTEEVerifier.contract.WatchLogs(opts, "EspressoNitroTEEVerifierSet", oldVerifierRule, newVerifierRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(EspressoTEEVerifierEspressoNitroTEEVerifierSet) + if err := _EspressoTEEVerifier.contract.UnpackLog(event, "EspressoNitroTEEVerifierSet", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseEspressoNitroTEEVerifierSet is a log parse operation binding the contract event 0xacbe6384d204f2883b25fa4d03777cb88af5b2afaaffbe06609d3e17dc68d096. +// +// Solidity: event EspressoNitroTEEVerifierSet(address indexed oldVerifier, address indexed newVerifier) +func (_EspressoTEEVerifier *EspressoTEEVerifierFilterer) ParseEspressoNitroTEEVerifierSet(log types.Log) (*EspressoTEEVerifierEspressoNitroTEEVerifierSet, error) { + event := new(EspressoTEEVerifierEspressoNitroTEEVerifierSet) + if err := _EspressoTEEVerifier.contract.UnpackLog(event, "EspressoNitroTEEVerifierSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// EspressoTEEVerifierGuardianAddedIterator is returned from FilterGuardianAdded and is used to iterate over the raw logs and unpacked data for GuardianAdded events raised by the EspressoTEEVerifier contract. +type EspressoTEEVerifierGuardianAddedIterator struct { + Event *EspressoTEEVerifierGuardianAdded // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *EspressoTEEVerifierGuardianAddedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(EspressoTEEVerifierGuardianAdded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(EspressoTEEVerifierGuardianAdded) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *EspressoTEEVerifierGuardianAddedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *EspressoTEEVerifierGuardianAddedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// EspressoTEEVerifierGuardianAdded represents a GuardianAdded event raised by the EspressoTEEVerifier contract. +type EspressoTEEVerifierGuardianAdded struct { + Guardian common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterGuardianAdded is a free log retrieval operation binding the contract event 0x038596bb31e2e7d3d9f184d4c98b310103f6d7f5830e5eec32bffe6f1728f969. +// +// Solidity: event GuardianAdded(address indexed guardian) +func (_EspressoTEEVerifier *EspressoTEEVerifierFilterer) FilterGuardianAdded(opts *bind.FilterOpts, guardian []common.Address) (*EspressoTEEVerifierGuardianAddedIterator, error) { + + var guardianRule []interface{} + for _, guardianItem := range guardian { + guardianRule = append(guardianRule, guardianItem) + } + + logs, sub, err := _EspressoTEEVerifier.contract.FilterLogs(opts, "GuardianAdded", guardianRule) + if err != nil { + return nil, err + } + return &EspressoTEEVerifierGuardianAddedIterator{contract: _EspressoTEEVerifier.contract, event: "GuardianAdded", logs: logs, sub: sub}, nil +} + +// WatchGuardianAdded is a free log subscription operation binding the contract event 0x038596bb31e2e7d3d9f184d4c98b310103f6d7f5830e5eec32bffe6f1728f969. +// +// Solidity: event GuardianAdded(address indexed guardian) +func (_EspressoTEEVerifier *EspressoTEEVerifierFilterer) WatchGuardianAdded(opts *bind.WatchOpts, sink chan<- *EspressoTEEVerifierGuardianAdded, guardian []common.Address) (event.Subscription, error) { + + var guardianRule []interface{} + for _, guardianItem := range guardian { + guardianRule = append(guardianRule, guardianItem) + } + + logs, sub, err := _EspressoTEEVerifier.contract.WatchLogs(opts, "GuardianAdded", guardianRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(EspressoTEEVerifierGuardianAdded) + if err := _EspressoTEEVerifier.contract.UnpackLog(event, "GuardianAdded", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseGuardianAdded is a log parse operation binding the contract event 0x038596bb31e2e7d3d9f184d4c98b310103f6d7f5830e5eec32bffe6f1728f969. +// +// Solidity: event GuardianAdded(address indexed guardian) +func (_EspressoTEEVerifier *EspressoTEEVerifierFilterer) ParseGuardianAdded(log types.Log) (*EspressoTEEVerifierGuardianAdded, error) { + event := new(EspressoTEEVerifierGuardianAdded) + if err := _EspressoTEEVerifier.contract.UnpackLog(event, "GuardianAdded", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// EspressoTEEVerifierGuardianRemovedIterator is returned from FilterGuardianRemoved and is used to iterate over the raw logs and unpacked data for GuardianRemoved events raised by the EspressoTEEVerifier contract. +type EspressoTEEVerifierGuardianRemovedIterator struct { + Event *EspressoTEEVerifierGuardianRemoved // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *EspressoTEEVerifierGuardianRemovedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(EspressoTEEVerifierGuardianRemoved) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(EspressoTEEVerifierGuardianRemoved) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *EspressoTEEVerifierGuardianRemovedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *EspressoTEEVerifierGuardianRemovedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// EspressoTEEVerifierGuardianRemoved represents a GuardianRemoved event raised by the EspressoTEEVerifier contract. +type EspressoTEEVerifierGuardianRemoved struct { + Guardian common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterGuardianRemoved is a free log retrieval operation binding the contract event 0xb8107d0c6b40be480ce3172ee66ba6d64b71f6b1685a851340036e6e2e3e3c52. +// +// Solidity: event GuardianRemoved(address indexed guardian) +func (_EspressoTEEVerifier *EspressoTEEVerifierFilterer) FilterGuardianRemoved(opts *bind.FilterOpts, guardian []common.Address) (*EspressoTEEVerifierGuardianRemovedIterator, error) { + + var guardianRule []interface{} + for _, guardianItem := range guardian { + guardianRule = append(guardianRule, guardianItem) + } + + logs, sub, err := _EspressoTEEVerifier.contract.FilterLogs(opts, "GuardianRemoved", guardianRule) + if err != nil { + return nil, err + } + return &EspressoTEEVerifierGuardianRemovedIterator{contract: _EspressoTEEVerifier.contract, event: "GuardianRemoved", logs: logs, sub: sub}, nil +} + +// WatchGuardianRemoved is a free log subscription operation binding the contract event 0xb8107d0c6b40be480ce3172ee66ba6d64b71f6b1685a851340036e6e2e3e3c52. +// +// Solidity: event GuardianRemoved(address indexed guardian) +func (_EspressoTEEVerifier *EspressoTEEVerifierFilterer) WatchGuardianRemoved(opts *bind.WatchOpts, sink chan<- *EspressoTEEVerifierGuardianRemoved, guardian []common.Address) (event.Subscription, error) { + + var guardianRule []interface{} + for _, guardianItem := range guardian { + guardianRule = append(guardianRule, guardianItem) + } + + logs, sub, err := _EspressoTEEVerifier.contract.WatchLogs(opts, "GuardianRemoved", guardianRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(EspressoTEEVerifierGuardianRemoved) + if err := _EspressoTEEVerifier.contract.UnpackLog(event, "GuardianRemoved", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseGuardianRemoved is a log parse operation binding the contract event 0xb8107d0c6b40be480ce3172ee66ba6d64b71f6b1685a851340036e6e2e3e3c52. +// +// Solidity: event GuardianRemoved(address indexed guardian) +func (_EspressoTEEVerifier *EspressoTEEVerifierFilterer) ParseGuardianRemoved(log types.Log) (*EspressoTEEVerifierGuardianRemoved, error) { + event := new(EspressoTEEVerifierGuardianRemoved) + if err := _EspressoTEEVerifier.contract.UnpackLog(event, "GuardianRemoved", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// EspressoTEEVerifierInitializedIterator is returned from FilterInitialized and is used to iterate over the raw logs and unpacked data for Initialized events raised by the EspressoTEEVerifier contract. +type EspressoTEEVerifierInitializedIterator struct { + Event *EspressoTEEVerifierInitialized // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *EspressoTEEVerifierInitializedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(EspressoTEEVerifierInitialized) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(EspressoTEEVerifierInitialized) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *EspressoTEEVerifierInitializedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *EspressoTEEVerifierInitializedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// EspressoTEEVerifierInitialized represents a Initialized event raised by the EspressoTEEVerifier contract. +type EspressoTEEVerifierInitialized struct { + Version uint64 + Raw types.Log // Blockchain specific contextual infos +} + +// FilterInitialized is a free log retrieval operation binding the contract event 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2. +// +// Solidity: event Initialized(uint64 version) +func (_EspressoTEEVerifier *EspressoTEEVerifierFilterer) FilterInitialized(opts *bind.FilterOpts) (*EspressoTEEVerifierInitializedIterator, error) { + + logs, sub, err := _EspressoTEEVerifier.contract.FilterLogs(opts, "Initialized") + if err != nil { + return nil, err + } + return &EspressoTEEVerifierInitializedIterator{contract: _EspressoTEEVerifier.contract, event: "Initialized", logs: logs, sub: sub}, nil +} + +// WatchInitialized is a free log subscription operation binding the contract event 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2. +// +// Solidity: event Initialized(uint64 version) +func (_EspressoTEEVerifier *EspressoTEEVerifierFilterer) WatchInitialized(opts *bind.WatchOpts, sink chan<- *EspressoTEEVerifierInitialized) (event.Subscription, error) { + + logs, sub, err := _EspressoTEEVerifier.contract.WatchLogs(opts, "Initialized") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(EspressoTEEVerifierInitialized) + if err := _EspressoTEEVerifier.contract.UnpackLog(event, "Initialized", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseInitialized is a log parse operation binding the contract event 0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2. +// +// Solidity: event Initialized(uint64 version) +func (_EspressoTEEVerifier *EspressoTEEVerifierFilterer) ParseInitialized(log types.Log) (*EspressoTEEVerifierInitialized, error) { + event := new(EspressoTEEVerifierInitialized) + if err := _EspressoTEEVerifier.contract.UnpackLog(event, "Initialized", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// EspressoTEEVerifierOwnershipTransferStartedIterator is returned from FilterOwnershipTransferStarted and is used to iterate over the raw logs and unpacked data for OwnershipTransferStarted events raised by the EspressoTEEVerifier contract. +type EspressoTEEVerifierOwnershipTransferStartedIterator struct { + Event *EspressoTEEVerifierOwnershipTransferStarted // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *EspressoTEEVerifierOwnershipTransferStartedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(EspressoTEEVerifierOwnershipTransferStarted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(EspressoTEEVerifierOwnershipTransferStarted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *EspressoTEEVerifierOwnershipTransferStartedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *EspressoTEEVerifierOwnershipTransferStartedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// EspressoTEEVerifierOwnershipTransferStarted represents a OwnershipTransferStarted event raised by the EspressoTEEVerifier contract. +type EspressoTEEVerifierOwnershipTransferStarted struct { + PreviousOwner common.Address + NewOwner common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterOwnershipTransferStarted is a free log retrieval operation binding the contract event 0x38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e22700. +// +// Solidity: event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner) +func (_EspressoTEEVerifier *EspressoTEEVerifierFilterer) FilterOwnershipTransferStarted(opts *bind.FilterOpts, previousOwner []common.Address, newOwner []common.Address) (*EspressoTEEVerifierOwnershipTransferStartedIterator, error) { + + var previousOwnerRule []interface{} + for _, previousOwnerItem := range previousOwner { + previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + } + var newOwnerRule []interface{} + for _, newOwnerItem := range newOwner { + newOwnerRule = append(newOwnerRule, newOwnerItem) + } + + logs, sub, err := _EspressoTEEVerifier.contract.FilterLogs(opts, "OwnershipTransferStarted", previousOwnerRule, newOwnerRule) + if err != nil { + return nil, err + } + return &EspressoTEEVerifierOwnershipTransferStartedIterator{contract: _EspressoTEEVerifier.contract, event: "OwnershipTransferStarted", logs: logs, sub: sub}, nil +} + +// WatchOwnershipTransferStarted is a free log subscription operation binding the contract event 0x38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e22700. +// +// Solidity: event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner) +func (_EspressoTEEVerifier *EspressoTEEVerifierFilterer) WatchOwnershipTransferStarted(opts *bind.WatchOpts, sink chan<- *EspressoTEEVerifierOwnershipTransferStarted, previousOwner []common.Address, newOwner []common.Address) (event.Subscription, error) { + + var previousOwnerRule []interface{} + for _, previousOwnerItem := range previousOwner { + previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + } + var newOwnerRule []interface{} + for _, newOwnerItem := range newOwner { + newOwnerRule = append(newOwnerRule, newOwnerItem) + } + + logs, sub, err := _EspressoTEEVerifier.contract.WatchLogs(opts, "OwnershipTransferStarted", previousOwnerRule, newOwnerRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(EspressoTEEVerifierOwnershipTransferStarted) + if err := _EspressoTEEVerifier.contract.UnpackLog(event, "OwnershipTransferStarted", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseOwnershipTransferStarted is a log parse operation binding the contract event 0x38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e22700. +// +// Solidity: event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner) +func (_EspressoTEEVerifier *EspressoTEEVerifierFilterer) ParseOwnershipTransferStarted(log types.Log) (*EspressoTEEVerifierOwnershipTransferStarted, error) { + event := new(EspressoTEEVerifierOwnershipTransferStarted) + if err := _EspressoTEEVerifier.contract.UnpackLog(event, "OwnershipTransferStarted", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// EspressoTEEVerifierOwnershipTransferredIterator is returned from FilterOwnershipTransferred and is used to iterate over the raw logs and unpacked data for OwnershipTransferred events raised by the EspressoTEEVerifier contract. +type EspressoTEEVerifierOwnershipTransferredIterator struct { + Event *EspressoTEEVerifierOwnershipTransferred // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *EspressoTEEVerifierOwnershipTransferredIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(EspressoTEEVerifierOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(EspressoTEEVerifierOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *EspressoTEEVerifierOwnershipTransferredIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *EspressoTEEVerifierOwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// EspressoTEEVerifierOwnershipTransferred represents a OwnershipTransferred event raised by the EspressoTEEVerifier contract. +type EspressoTEEVerifierOwnershipTransferred struct { + PreviousOwner common.Address + NewOwner common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterOwnershipTransferred is a free log retrieval operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_EspressoTEEVerifier *EspressoTEEVerifierFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, previousOwner []common.Address, newOwner []common.Address) (*EspressoTEEVerifierOwnershipTransferredIterator, error) { + + var previousOwnerRule []interface{} + for _, previousOwnerItem := range previousOwner { + previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + } + var newOwnerRule []interface{} + for _, newOwnerItem := range newOwner { + newOwnerRule = append(newOwnerRule, newOwnerItem) + } + + logs, sub, err := _EspressoTEEVerifier.contract.FilterLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule) + if err != nil { + return nil, err + } + return &EspressoTEEVerifierOwnershipTransferredIterator{contract: _EspressoTEEVerifier.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +// WatchOwnershipTransferred is a free log subscription operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_EspressoTEEVerifier *EspressoTEEVerifierFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *EspressoTEEVerifierOwnershipTransferred, previousOwner []common.Address, newOwner []common.Address) (event.Subscription, error) { + + var previousOwnerRule []interface{} + for _, previousOwnerItem := range previousOwner { + previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + } + var newOwnerRule []interface{} + for _, newOwnerItem := range newOwner { + newOwnerRule = append(newOwnerRule, newOwnerItem) + } + + logs, sub, err := _EspressoTEEVerifier.contract.WatchLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(EspressoTEEVerifierOwnershipTransferred) + if err := _EspressoTEEVerifier.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseOwnershipTransferred is a log parse operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_EspressoTEEVerifier *EspressoTEEVerifierFilterer) ParseOwnershipTransferred(log types.Log) (*EspressoTEEVerifierOwnershipTransferred, error) { + event := new(EspressoTEEVerifierOwnershipTransferred) + if err := _EspressoTEEVerifier.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/espresso/environment/10_soft_confirmation_integrity_test.go b/espresso/environment/10_soft_confirmation_integrity_test.go new file mode 100644 index 00000000000..4fb3f0960d7 --- /dev/null +++ b/espresso/environment/10_soft_confirmation_integrity_test.go @@ -0,0 +1,551 @@ +// This file is dedicated to a series of tests related to soft confirmation +// integrity. This file contains tests related to, and ensuring that the +// integrity of the soft confirmations being provided by the underlying +// Rollup when compared against the confirmations being provided by +// Espresso/HotShot. The derivation from the L2 / L1 should not be compromised +// or result in different results than the derivation provided by the +// sequencer. +// +// Assumption: The rollup sequencer is correct, online, and honest. It +// produces a valid sequence of rollup blocks every few seconds or faster, +// and it never reorgs. +// +// The underlying documented definition of the soft confirmation integrity +// comes from this definition: +// The integration must not weaken soft confirmations provided by a rollup's +// sequencer. That is, while Espresso confirmations are valid even under a +// weakened security assumption where the sequencer may be malicious, if we +// consider the case with a stronger assumption where the sequencer is correct, +// online, and honest, the rollup should finalize what the sequencer produces. + +package environment_test + +import ( + "context" + crypto_rand "crypto/rand" + "encoding/hex" + "math/big" + "testing" + "time" + + espressoClient "github.com/EspressoSystems/espresso-network/sdks/go/client" + espressoCommon "github.com/EspressoSystems/espresso-network/sdks/go/types/common" + "github.com/EspressoSystems/espresso-streamers/op/derivation" + env "github.com/ethereum-optimism/optimism/espresso/environment" + "github.com/ethereum-optimism/optimism/op-e2e/system/e2esys" + "github.com/ethereum-optimism/optimism/op-node/rollup" + "github.com/ethereum-optimism/optimism/op-service/bigs" + op_crypto "github.com/ethereum-optimism/optimism/op-service/crypto" + op_signer "github.com/ethereum-optimism/optimism/op-service/signer" + ethereum "github.com/ethereum/go-ethereum" + geth_common "github.com/ethereum/go-ethereum/common" + geth_types "github.com/ethereum/go-ethereum/core/types" + geth_crypto "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/log" +) + +// messageWithTimestamp is a struct that contains an entry of type T +// and a timestamp. It is used to store messages with their corresponding +// timestamps. +type messageWithTimestamp[T any] struct { + entry T + timestamp time.Time +} + +// recordTimestamp is a helper function that takes an entry of type T and +// returns a messageWithTimestamp[T] struct that contains the entry and +// the current timestamp. +func recordTimestamp[T any](entry T) messageWithTimestamp[T] { + return messageWithTimestamp[T]{ + entry: entry, + timestamp: time.Now(), + } +} + +// produceTimestampedStream is a helper function that is designed to be run +// in a goroutine. It consumes values coming from the input channel and +// outputs them to the output channel with a timestamp. +func produceTimestampedStream[T any]( + ctx context.Context, + input <-chan T, + output chan<- messageWithTimestamp[T], +) { + for { + select { + case <-ctx.Done(): + return + case header, ok := <-input: + if !ok { + // Channel is closed, + // we should exit + return + } + + select { + case <-ctx.Done(): + case output <- recordTimestamp(header): + } + } + } +} + +// timestampedHeaderStream is a struct that contains a subscription +// to the Ethereum client and a channel to receive timestamped headers. +type timestampedHeaderStream struct { + sub ethereum.Subscription + ch chan messageWithTimestamp[*geth_types.Header] +} + +// setupHeaderStreamSubscription sets up a subscription to the new head +// event on the given Ethereum client. It creates a channel to receive +// headers and a channel to receive timestamped headers. It starts a +// goroutine to produce timestamped headers from the received headers. +// It returns a timestampedHeaderStream struct containing the subscription +// and the channel for timestamped headers. +func setupHeaderStreamSubscription(ctx context.Context, t *testing.T, cli *ethclient.Client) (timestampedHeaderStream, error) { + headerCh := make(chan *geth_types.Header) + timestampedHeaderCh := make(chan messageWithTimestamp[*geth_types.Header]) + sub, err := cli.SubscribeNewHead(ctx, headerCh) + if err != nil { + return timestampedHeaderStream{sub: sub}, err + } + go produceTimestampedStream(ctx, headerCh, timestampedHeaderCh) + + return timestampedHeaderStream{sub: sub, ch: timestampedHeaderCh}, nil + +} + +// setupHeaderStreamSubscriptions sets up subscriptions to the new head +// event on the given Ethereum clients (sequencer and verifier). +func setupHeaderStreamSubscriptions(ctx context.Context, t *testing.T, l2Seq, l2Verif *ethclient.Client) ( + seqStream timestampedHeaderStream, + verifStream timestampedHeaderStream, +) { + + seqStream, err := setupHeaderStreamSubscription(ctx, t, l2Seq) + if have, want := err, error(nil); have != want { + t.Fatalf("Failed to subscribe to sequencer new head:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + + verifStream, err = setupHeaderStreamSubscription(ctx, t, l2Verif) + if have, want := err, error(nil); have != want { + t.Fatalf("Failed to subscribe to verifier new head:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + + return seqStream, verifStream +} + +// nextStreamEntries is a helper function that retrieves the next entries +// from the sequencer and verifier streams. +func nextStreamEntries[T any](ctx context.Context, seqCh, verifCh <-chan messageWithTimestamp[T]) ( + seqHeader, verifHeader messageWithTimestamp[T], +) { + select { + case <-ctx.Done(): + return + + case seqHeader = <-seqCh: + } + + select { + case <-ctx.Done(): + return + case verifHeader = <-verifCh: + } + + return seqHeader, verifHeader +} + +// advanceStreamToHeight is a helper function that advances the +// timestampedHeaderStream to the specified height. It consumes headers +// from the stream until the block number of the header is greater than +// or equal to the specified height. +func advanceStreamToHeight( + ctx context.Context, + stream timestampedHeaderStream, + start messageWithTimestamp[*geth_types.Header], + height *big.Int, +) { + for i := start.entry.Number; i.Cmp(height) < 0; { + select { + case <-ctx.Done(): + return + case streamHeader, ok := <-stream.ch: + if !ok { + return + } + + i = streamHeader.entry.Number + } + } +} + +// EnsureStreamsAreSynced is a helper function that ensures that the +// sequencer and verifier streams are all at the same block height. +// It does this by advancing each stream to the largest block number +// among the two streams. +// +// Advancing the streams to the same block height is necessary as it ensures +// that we are comparing the same block across both streams. +// +// Advancing in this way does skip over existing blocks, so there is a +// potential for missing blocks in this way. +func ensureStreamsAreSynced( + ctx context.Context, + seqStream, verifStream timestampedHeaderStream, +) { + seqHeader, verifHeader := nextStreamEntries(ctx, seqStream.ch, verifStream.ch) + + // Determine the largest block from the two streams + var largestNumber = seqHeader.entry.Number + if verifHeader.entry.Number.Cmp(largestNumber) > 0 { + largestNumber = verifHeader.entry.Number + } + + // Now advance all of these streams so that the last entry consumed + // all point to the same block number. + + // Advance the Sequencer Stream + advanceStreamToHeight(ctx, seqStream, seqHeader, largestNumber) + advanceStreamToHeight(ctx, verifStream, verifHeader, largestNumber) +} + +// verifyStreamSequenceForNextN is a helper function that verifies +// the sequence of blocks being produced by the sequencer and verifier +// streams all match for the next N blocks. +// +// It does this by waiting for the next entry from each stream and +// comparing their header values. +// +// The sequence being consumed should be ordered, and the same across both +// streams. +// +// The streams are assumed to be synced before this function is called. +// This means that they should be at the same block height before this +// verification is called, otherwise we may fail due to being on different +// block heights. +func verifyStreamSequenceForNextN( + ctx context.Context, + t *testing.T, + seqStream, verifStream timestampedHeaderStream, + count int, +) { + for i := 0; i < count; i++ { + // The easiest way to verify this is to just wait for each of these + // streams entries in turn, then compare their header hashes. + + seqHeader, verifHeader := nextStreamEntries(ctx, seqStream.ch, verifStream.ch) + + // Alright, we should have both next headers now. + // Let's compare them to make sure they are the same. + select { + case <-ctx.Done(): + t.Errorf("test was canceled by context while waiting to verify sequence entry %d", i) + return + default: + } + + if have, want := seqHeader.entry.Hash(), verifHeader.entry.Hash(); have.Cmp(want) != 0 { + t.Fatalf("Sequencer and Verifier headers do not match:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + return + } + } +} + +// SUBMIT_RANDOM_DATA_INTERVAL is the interval / frequency at which we +// will attempt to submit random data to the Espresso using the +// sequencer's namespace. +const SUBMIT_RANDOM_DATA_INTERVAL = 500 * time.Millisecond + +// submitRandomDataToSequencerNamespace is a function that submits +// random data to the sequencer namespace at a specified interval. +func submitRandomDataToSequencerNamespace(ctx context.Context, espCli espressoClient.EspressoClient, namespace uint64) { + // We only want to submit garbage data to the sequencer so quickly + ticker := time.NewTicker(SUBMIT_RANDOM_DATA_INTERVAL) + buffer := make([]byte, 1024*3) + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + } + + // Fill buffer with random data + n, _ := crypto_rand.Read(buffer) + + // Submit garbage data to the sequencer namespace + _, err := espCli.SubmitTransaction(ctx, espressoCommon.Transaction{ + Namespace: namespace, + Payload: espressoCommon.Bytes(buffer[:n]), + }) + if err != nil { + log.Error("Failed to submit random data to sequencer namespace", "namespace", namespace, "error", err) + } + } +} + +// createMaliciousEspressoBatch creates a malicious Espresso batch by +// constructing a block with a deposit transaction. It uses the latest +// block from the sequencer to create a new block with a deposit +// transaction. The block is then converted to an Espresso batch using +// the derivation.BlockToEspressoBatch function. +func createMaliciousEspressoBatch(ctx context.Context, cli *ethclient.Client, rollupCfg *rollup.Config) (*derivation.EspressoBatch, error) { + // / Determine what the latest block in the sequencer is, so we can + // hope to create a valid transaction, to get something out of it. + latestBlock, err := cli.BlockByNumber(ctx, nil) + if err != nil { + return nil, err + } + + latestHeader := latestBlock.Header() + header := &geth_types.Header{ + ParentHash: latestBlock.Hash(), + UncleHash: latestHeader.UncleHash, + Coinbase: latestHeader.Coinbase, + Root: latestHeader.Root, + Bloom: latestHeader.Bloom, + Difficulty: latestHeader.Difficulty, + Number: new(big.Int).Add(latestBlock.Number(), big.NewInt(1)), + GasLimit: latestHeader.GasLimit, + GasUsed: latestHeader.GasUsed, + Time: latestHeader.Time + 1, + Extra: latestHeader.Extra, + MixDigest: latestHeader.MixDigest, + Nonce: latestHeader.Nonce, + } + body := geth_types.Body{ + Transactions: []*geth_types.Transaction{ + geth_types.NewTx( + &geth_types.DepositTx{ + Value: big.NewInt(1000), + }, + ), + }, + } + block := geth_types.NewBlockWithHeader(header).WithBody(body) + + return derivation.BlockToEspressoBatch(rollupCfg, block) +} + +// SUBMIT_VALID_DATA_WITH_WRONG_SIGNATURE_INTERVAlL is the interval / frequency +// at which we will attempt to submit valid data with the wrong signature to the +// Espresso using the sequencer's namespace. +const SUBMIT_VALID_DATA_WITH_WRONG_SIGNATURE_INTERVAlL = 500 * time.Millisecond + +// Attack Espresso Integrity by Submitting Valid Data with the wrong +// Signature to the Sequencer's namespace. +func submitValidDataWithWrongSignature(ctx context.Context, rollupCfg *rollup.Config, l2Seq *ethclient.Client, espCli espressoClient.EspressoClient, namespace uint64) { + // We only want to submit garbage data to the sequencer so quickly + ticker := time.NewTicker(SUBMIT_VALID_DATA_WITH_WRONG_SIGNATURE_INTERVAlL) + + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + } + privateKey, err := geth_crypto.GenerateKey() + if err != nil { + continue + } + privateKeyString := hex.EncodeToString(geth_crypto.FromECDSA(privateKey)) + factory, _, err := op_crypto.ChainSignerFactoryFromConfig(nil, privateKeyString, "", "", op_signer.CLIConfig{}) + if err != nil { + continue + } + randomChainSigner := factory(big.NewInt(int64(namespace)), geth_common.Address{}) + + batch, err := createMaliciousEspressoBatch(ctx, l2Seq, rollupCfg) + + if err != nil { + // Skip + continue + } + + txn, err := batch.ToEspressoTransaction(ctx, namespace, randomChainSigner) + if err != nil { + // Skip + continue + } + + // Submit garbage data to the sequencer namespace + _, _ = espCli.SubmitTransaction(ctx, *txn) + } +} + +// fakeChainSigner is a fake implementation of the ChainSigner interface. +// It will create fake signatures for the transaction. +type fakeChainSigner struct{} + +var _ op_crypto.ChainSigner = (*fakeChainSigner)(nil) + +// Sign implements crypto.ChainSigner. +func (f *fakeChainSigner) Sign(ctx context.Context, hash []byte) ([]byte, error) { + sig := make([]byte, geth_crypto.SignatureLength) + _, err := crypto_rand.Read(sig) + if err != nil { + return nil, err + } + return sig, nil +} + +// SignTransaction implements crypto.ChainSigner. +func (f *fakeChainSigner) SignTransaction( + ctx context.Context, + addr geth_common.Address, + tx *geth_types.Transaction, +) (*geth_types.Transaction, error) { + // This is a fake implementation, and we're not expecting this method to be + // called in this test, so this should be safe. + panic("unimplemented") +} + +// SUBMIT_VALID_DATA_WITH_RANDOM_SIGNATURE_INTERVAL is the interval / frequency +// at which we will attempt to submit valid data with a random signature to the +// Espresso using the sequencer's namespace. +const SUBMIT_VALID_DATA_WITH_RANDOM_SIGNATURE_INTERVAL = 100 * time.Millisecond + +// Attack Espresso Integrity by Submitting A properly formatted +// transaction, with a random signature value to the Sequencer's +// namespace +func submitValidDataWithRandomSignature( + ctx context.Context, + rollupCfg *rollup.Config, + l2Seq *ethclient.Client, + espCli espressoClient.EspressoClient, + namespace uint64, +) { + // We only want to submit garbage data to the sequencer so quickly + ticker := time.NewTicker(SUBMIT_VALID_DATA_WITH_RANDOM_SIGNATURE_INTERVAL) + signer := new(fakeChainSigner) + + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + } + + batch, err := createMaliciousEspressoBatch(ctx, l2Seq, rollupCfg) + + if err != nil { + // Skip + continue + } + + txn, err := batch.ToEspressoTransaction(ctx, namespace, signer) + if err != nil { + // Skip + continue + } + + // Submit garbage data to the sequencer namespace + _, _ = espCli.SubmitTransaction(ctx, *txn) + } +} + +// TestSequencerFeedConsistency is a test that ensures that the sequence of +// blocks being produced by the feeds from the Sequencer and another L2 +// Verifier are consistent with each other. +// +// The criteria / goal of this test are outlined by the following requirement: +// +// Run the rollup and subscribe to the sequencer feed and a feed which derives +// the finalized block sequence from L1. All of these should yield the same +// blocks in the same order (but at different times). +func TestSequencerFeedConsistency(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) + defer cancel() + + launcher := new(env.EspressoDevNodeLauncherDocker) + system, espressoDevNode, err := launcher.StartE2eDevnet(ctx, t, env.WithL1FinalizedDistance(0)) + + // Signal the testnet to shut down + if have, want := err, error(nil); have != want { + t.Fatalf("failed to start dev environment with espresso dev node:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + + defer env.Stop(t, system) + defer env.Stop(t, espressoDevNode) + + l2Seq := system.NodeClient(e2esys.RoleSeq) + l2Verif := system.NodeClient(e2esys.RoleVerif) + + seqStream, verifStream := setupHeaderStreamSubscriptions(ctx, t, l2Seq, l2Verif) + defer seqStream.sub.Unsubscribe() + defer verifStream.sub.Unsubscribe() + + // We need to sync these streams up. We created them at different points + // in their life times. so we need to wait for them all to be at the same + // block height before we start comparing them. + // + // It is most likely going to be the case that the sequencer is ahead of + // the verifier. We will play it safe, and just make no assumptions by + // grabbing the largest block + ensureStreamsAreSynced(ctx, seqStream, verifStream) + + // Let's verify that these streams are producing the same blocks + // in the same order. We will do this by waiting for a few blocks to + verifyStreamSequenceForNextN(ctx, t, seqStream, verifStream, 100) +} + +// TestSequencerFeedConsistencyWithAttackOnEspresso is a test that expands +// upon the previous test by introducing attacks against Espresso with the +// specific goal of arriving at a state where the Espresso feed is producing +// different blocks than the sequencer, for a variety of different potential +// reasons. +// +// These attacks are designed to cover some different use cases, and may +// reflect attempts of third parties to attack or manipulate the data being +// consumed for individual gain, or disruption. +// +// The criteria / goal of this test are outlined by the following requirement: +// Consider rollup-specific adversarial behavior which could break sequencer +// confirmations, such as an adversary sending non-sequencer blocks directly +// to Espresso. Such attacks should not cause Espresso to finalize something +// different than the sequencer feed. +func TestSequencerFeedConsistencyWithAttackOnEspresso(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) + defer cancel() + + launcher := new(env.EspressoDevNodeLauncherDocker) + system, espressoDevNode, err := launcher.StartE2eDevnet(ctx, t, env.WithL1FinalizedDistance(0)) + + // Signal the testnet to shut down + if have, want := err, error(nil); have != want { + t.Fatalf("failed to start dev environment with espresso dev node:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + + defer env.Stop(t, system) + defer env.Stop(t, espressoDevNode) + + l2Seq := system.NodeClient(e2esys.RoleSeq) + espCli := espressoDevNode.Client() + namespace := bigs.Uint64Strict(system.RollupConfig.L2ChainID) + + // Attack Espresso Integrity by Submitting Garbage Data to the Same + // namespace as the Sequencer's namespace. + go submitRandomDataToSequencerNamespace(ctx, espCli, namespace) + + // Attack Espresso Integrity by Submitting Valid Data with the wrong + // Signature to the Sequencer's namespace. + go submitValidDataWithWrongSignature(ctx, system.RollupConfig, l2Seq, espCli, namespace) + + // Attack Espresso Integrity by Submitting A properly formatted + // transaction, with a random signature value to the Sequencer's + // namespace + go submitValidDataWithRandomSignature(ctx, system.RollupConfig, l2Seq, espCli, namespace) + + l2Verif := system.NodeClient(e2esys.RoleVerif) + + seqStream, verifStream := setupHeaderStreamSubscriptions(ctx, t, l2Seq, l2Verif) + defer seqStream.sub.Unsubscribe() + defer verifStream.sub.Unsubscribe() + + // Sync the Streams to the same block height + ensureStreamsAreSynced(ctx, seqStream, verifStream) + + // Verify the sequence of blocks being produced. + verifyStreamSequenceForNextN(ctx, t, seqStream, verifStream, 100) +} diff --git a/espresso/environment/11_forced_transaction_test.go b/espresso/environment/11_forced_transaction_test.go new file mode 100644 index 00000000000..75447e9aaad --- /dev/null +++ b/espresso/environment/11_forced_transaction_test.go @@ -0,0 +1,145 @@ +package environment_test + +import ( + "context" + "math/big" + "testing" + "time" + + env "github.com/ethereum-optimism/optimism/espresso/environment" + "github.com/ethereum-optimism/optimism/op-core/predeploys" + "github.com/ethereum-optimism/optimism/op-e2e/bindings" + "github.com/ethereum-optimism/optimism/op-e2e/config" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" + "github.com/ethereum-optimism/optimism/op-e2e/system/e2esys" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/stretchr/testify/require" +) + +// Time to wait for a transaction to be enforced after submission. +const WAIT_FORCED_TXN_TIME = 25 * time.Second + +// Window small enough to guarantee that transactions are enforced before WAIT_FORCED_TXN_TIME. +const SMALL_SEQUENCER_WINDOW = 2 // Minimum possible value + +// Window large enough to guarantee that transactions are not enforced before WAIT_FORCED_TXN_TIME. +const LARGER_SEQUENCER_WINDOW = 1000 + +// Get the appropriate sequencer window size for testing. +func sequencer_window_size(withSmallWindow bool) uint64 { + if withSmallWindow { + return SMALL_SEQUENCER_WINDOW + } + return LARGER_SEQUENCER_WINDOW +} + +// ForcedTransaction attempts to verify that the forced transaction mechanism works for the +// withdrawal transaction with and without Espresso dev node. +// +// This function is designed to evaluate Test 11 as outlined within the Espresso Celo Integration +// plan. It has stated task definition as follows: +// +// Arrange: +// Set the sequencer window size small or large. +// Start the devnet with the sequencer window setting, with or without the Espresso dev node. +// Stop the sequencer. +// Act: +// Send a withdrawal and wait until the small window is passed. +// Assert: +// The balance reflects (or does not reflect) the withdrawal transaction, if the sequencer +// window is set small (or large, respectively), regardless of whether launching with the +// Espresso dev node. +func ForcedTransaction(t *testing.T, withSmallSequencerWindow bool, withEspresso bool) { + // Set up the test timeout condition. + // Extended timeout to accommodate slower processing in test environments + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) + defer cancel() + + // Launch the devnet with the given sequencer window size. + var system *e2esys.System + var err error + if withEspresso { + launcher := new(env.EspressoDevNodeLauncherDocker) + systemWithEspresso, espressoDevNode, err := launcher.StartE2eDevnet(ctx, t, env.WithSequencerWindowSize(sequencer_window_size(withSmallSequencerWindow))) + system = systemWithEspresso + require.NoError(t, err, "Failed to launch with the Espresso dev node") + defer env.Stop(t, system) + defer env.Stop(t, espressoDevNode) + } else { + sysConfig := e2esys.DefaultSystemConfig(t, e2esys.WithAllocType(config.DefaultAllocType)) + sysConfig.DeployConfig.SequencerWindowSize = sequencer_window_size(withSmallSequencerWindow) + system, err = sysConfig.Start(t) + require.NoError(t, err, "failed to launch without Espresso dev node") + defer env.Stop(t, system) + } + + // Set up Alice's address and record the initial balance. + l2Verif := system.NodeClient(e2esys.RoleVerif) + address := system.Cfg.Secrets.Addresses().Alice + l1Client := system.NodeClient(e2esys.RoleL1) + initialBalance, err := l2Verif.BalanceAt(ctx, address, nil) + require.NoError(t, err, "Failed to get initial balance") + + // Simulate sequencer downtime. + err = system.RollupNodes[e2esys.RoleSeq].Stop(ctx) + require.NoError(t, err, "Failed to stop sequencer") + + // Initiate a withdrawal from Alice to the L1 following + // https://docs.unichain.org/docs/technical-information/submitting-transactions-from-l1#initiating-a-withdrawal-from-l1. + portal, err := bindings.NewOptimismPortal(system.Cfg.L1Deployments.OptimismPortalProxy, l1Client) + require.NoError(t, err, "Failed to create Optimism portal") + opts, err := bind.NewKeyedTransactorWithChainID(system.Cfg.Secrets.Alice, system.Cfg.L1ChainIDBig()) + require.NoError(t, err, "Failed to create withdrawal transaction options") + withdrawalAmount := new(big.Int).SetUint64(1000) + tx, err := portal.DepositTransaction( + opts, + predeploys.L2ToL1MessagePasserAddr, + withdrawalAmount, + uint64(300_000), + false, + nil, + ) + require.NoError(t, err, "Failed to create transaction") + _, err = bind.WaitMined(ctx, l1Client, tx) + require.NoError(t, err, "Transaction not minted") + + // Wait and attempt to get the new balance after the withdrawal. + time.Sleep(WAIT_FORCED_TXN_TIME) + newBalance, err := wait.ForBalanceChange(ctx, l2Verif, address, initialBalance) + + if withSmallSequencerWindow { + // Verify that Alice's balance decreases as expected. Compare as big.Ints: + // wei balances routinely exceed uint64, so converting would overflow. + require.NoError(t, err, "Failed to get new balance") + maxExpected := new(big.Int).Sub(initialBalance, withdrawalAmount) + require.LessOrEqualf(t, newBalance.Cmp(maxExpected), 0, "Balance not decreased: newBalance=%s, max=%s", newBalance, maxExpected) + + } else { + // Verify that Alice's balance is inaccessible. + require.Error(t, err, "Not expected to get new balance") + } +} + +// TestForcedTransactionWithoutEspressoSmallWindow verifies that the withdrawal transaction is +// enforced after the sequencer window is passed when launching without the Espresso dev node. +func TestForcedTransactionWithoutEspressoSmallWindow(t *testing.T) { + ForcedTransaction(t, true, false) +} + +// TestForcedTransactionWithoutEspressoLargeWindow verifies that the withdrawal transaction is not +// enforced before the sequencer window is passed when launching without the Espresso dev node. +func TestForcedTransactionWithoutEspressoLargeWindow(t *testing.T) { + ForcedTransaction(t, false, false) +} + +// TestForcedTransactionWithEspressoSmallWindow verifies that the withdrawal transaction is +// enforced after the sequencer window is passed when launching with the Espresso dev node. +func TestForcedTransactionWithEspressoSmallWindow(t *testing.T) { + ForcedTransaction(t, true, true) +} + +// TestForcedTransactionWithEspressoLargeWindow verifies that the withdrawal transaction is not +// enforced before the sequencer window is passed when launching with the Espresso dev node. +func TestForcedTransactionWithEspressoLargeWindow(t *testing.T) { + ForcedTransaction(t, false, false) +} diff --git a/espresso/environment/12_enforce_majority_rule_test.go b/espresso/environment/12_enforce_majority_rule_test.go new file mode 100644 index 00000000000..70910afd03d --- /dev/null +++ b/espresso/environment/12_enforce_majority_rule_test.go @@ -0,0 +1,97 @@ +package environment_test + +import ( + "context" + "math/big" + "net/http" + "net/http/httptest" + "strings" + "testing" + "time" + + "github.com/coder/websocket" + env "github.com/ethereum-optimism/optimism/espresso/environment" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/geth" + "github.com/ethereum-optimism/optimism/op-e2e/system/e2esys" + "github.com/stretchr/testify/require" +) + +const ERROR_EXPECTED = true +const NO_ERROR_EXPECTED = false + +// runWithMultiClient spins up the sequencer, L2 verifier and batcher in Espresso mode. +// Moreover, a dummy Espresso Query Service (EQS) is run on port DUMMY_SERVER_PORT. +// The batcher is initialized with M good Espresso urls and N bad ones (using the dummy EQS url) +// @param numGoodUrls M as mentioned in the above description +// @param numBadUrls N as mentioned in the above description +// @param expectedError if set to true, we expect a timeout error as the L2 cannot make progress. Otherwise, we expect no error at all. +func runWithMultiClient(t *testing.T, numGoodUrls int, numBadUrls int, expectedError bool) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if strings.Contains(r.URL.Path, "stream") { + conn, err := websocket.Accept(w, r, &websocket.AcceptOptions{}) + require.NoError(t, err) + + defer conn.Close(websocket.StatusGoingAway, "Bye") + + err = conn.Write(ctx, websocket.MessageText, []byte("Hello")) + require.NoError(t, err) + + } else { + http.Error(w, "Hello", http.StatusOK) + } + })) + + badServerUrl := server.URL + + launcher := new(env.EspressoDevNodeLauncherDocker) + + system, devNode, err := launcher.StartE2eDevnet(ctx, t, env.SetEspressoUrls(numGoodUrls, numBadUrls, badServerUrl)) + if have, want := err, error(nil); have != want { + t.Fatalf("failed to start dev environment with espresso dev node:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + + defer env.Stop(t, system) + defer env.Stop(t, devNode) + + l2Verif := system.NodeClient(e2esys.RoleVerif) + + // Wait for batcher to start advancing L2 head + blockNumber := int64(2) + + // Check the l2Verif node can/cannot make progress + _, err = geth.WaitForBlockToBeSafe(big.NewInt(blockNumber), l2Verif, 60*time.Second) + if expectedError { + require.Error(t, err, "The L2 should not be progressing") + } else { + require.NoError(t, err, "The L2 should be progressing") + } + +} + +// TestEnforceMajorityRule allows to check that the batcher uses the multiclient for fetching information from Espresso and that this multiclient enforces the majority rule. +// This test is designed to evaluate Test 12 as outlined within the Espresso Celo Integration plan. +// Its concrete description is as follows: +// Arrange: +// +// Running Sequencer, Batcher in Espresso mode and OP node. +// Set up the batcher with a list of M "good" urls and N "bad" urls +// +// Act: +// +// Just wait for the batcher to submits batches and the L2 to make progress. +// +// Assert: +// +// If M>N, the chain should make progress, otherwise it should not. +func TestEnforceMajorityRule(t *testing.T) { + t.Skip("Skipping test: MajorityRule has been deprecated and replaced by SingleNode.") + + // To create a valid multiple nodes client, we need to provide at least 2 URLs. + runWithMultiClient(t, 2, 0, NO_ERROR_EXPECTED) + runWithMultiClient(t, 2, 1, NO_ERROR_EXPECTED) + runWithMultiClient(t, 0, 2, ERROR_EXPECTED) + runWithMultiClient(t, 1, 1, ERROR_EXPECTED) +} diff --git a/espresso/environment/13_dispute_game_test.go b/espresso/environment/13_dispute_game_test.go new file mode 100644 index 00000000000..ae3e2cecfb4 --- /dev/null +++ b/espresso/environment/13_dispute_game_test.go @@ -0,0 +1,113 @@ +package environment_test + +import ( + "context" + "testing" + + env "github.com/ethereum-optimism/optimism/espresso/environment" + "github.com/stretchr/testify/require" + + "github.com/ethereum-optimism/optimism/op-challenger/game/types" + op_e2e "github.com/ethereum-optimism/optimism/op-e2e" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/challenger" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/disputegame" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" + "github.com/ethereum/go-ethereum/common" +) + +// TestOutputAlphabetGameWithEspresso_ChallengerWins verifies that fraud proof challenges work correctly +// with Espresso integration enabled. It ensures a challenger can successfully detect and win +// against a malicious proposer, validating that the dispute resolution process remains intact +// when using Espresso for transaction finalization. +// +// This test mirrors the logic from TestOutputAlphabetGame_ChallengerWins in the non-Espresso +// implementation (op-e2e/faultproofs/output_alphabet_test.go), but runs with the Espresso-mode +// batcher enabled. +// +// Test structure: +// - Setup: Initialize Sequencer and Batcher in Espresso mode +// - Action: Deploy fault dispute system and trigger challenger response +// - Assert: Verify challenger successfully wins the dispute game +func TestOutputAlphabetGameWithEspresso_ChallengerWins(t *testing.T) { + op_e2e.InitParallel(t) + ctx := context.Background() + + // Start a Espresso Dev Node + launcher := new(env.EspressoDevNodeLauncherDocker) + + // Start a Fault Dispute System with Espresso Dev Node + sys, espressoDevNode, err := launcher.StartE2eDevnet( + ctx, + t, + env.WithFaultDisputeSystem(), + env.WithL1FinalizedDistance(0), + env.WithSequencerUseFinalized(true), + ) + + // Signal the testnet to shut down + if have, want := err, error(nil); have != want { + t.Fatalf("failed to start dev environment with espresso dev node:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + + l1Client := sys.NodeClient("l1") + + // Close the system and stop the Espresso Dev Node + defer sys.Close() + defer func() { + err = espressoDevNode.Stop() + if err != nil { + t.Fatalf("failed to stop espresso dev node: %v", err) + } + }() + + // All the following testing code is pasted from `TestOutputAlphabetGame_ChallengerWins` in `op-e2e/faultproofs/output_alphabet_test.go` + disputeGameFactory := disputegame.NewFactoryHelper(t, ctx, sys) + game := disputeGameFactory.StartOutputAlphabetGame(ctx, "sequencer", 3, common.Hash{0xff}) + correctTrace := game.CreateHonestActor(ctx, "sequencer") + game.LogGameData(ctx) + + opts := challenger.WithPrivKey(sys.Cfg.Secrets.Alice) + game.StartChallenger(ctx, "sequencer", "Challenger", opts) + game.LogGameData(ctx) + + // Challenger should post an output root to counter claims down to the leaf level of the top game + claim := game.RootClaim(ctx) + for claim.IsOutputRoot(ctx) && !claim.IsOutputRootLeaf(ctx) { + if claim.AgreesWithOutputRoot() { + // If the latest claim agrees with the output root, expect the honest challenger to counter it + claim = claim.WaitForCounterClaim(ctx) + game.LogGameData(ctx) + claim.RequireCorrectOutputRoot(ctx) + } else { + // Otherwise we should counter + claim = claim.Attack(ctx, common.Hash{0xaa}) + game.LogGameData(ctx) + } + } + + // Wait for the challenger to post the first claim in the cannon trace + claim = claim.WaitForCounterClaim(ctx) + game.LogGameData(ctx) + + // Attack the root of the alphabet trace subgame + claim = correctTrace.AttackClaim(ctx, claim) + for !claim.IsMaxDepth(ctx) { + if claim.AgreesWithOutputRoot() { + // If the latest claim supports the output root, wait for the honest challenger to respond + claim = claim.WaitForCounterClaim(ctx) + game.LogGameData(ctx) + } else { + // Otherwise we need to counter the honest claim + claim = correctTrace.AttackClaim(ctx, claim) + game.LogGameData(ctx) + } + } + // Challenger should be able to call step and counter the leaf claim. + claim.WaitForCountered(ctx) + game.LogGameData(ctx) + + sys.TimeTravelClock.AdvanceTime(game.MaxClockDuration(ctx)) + require.NoError(t, wait.ForNextBlock(ctx, l1Client)) + game.WaitForGameStatus(ctx, types.GameStatusChallengerWon) + game.LogGameData(ctx) +} diff --git a/espresso/environment/14_batcher_fallback_test.go b/espresso/environment/14_batcher_fallback_test.go new file mode 100644 index 00000000000..c597481046a --- /dev/null +++ b/espresso/environment/14_batcher_fallback_test.go @@ -0,0 +1,590 @@ +package environment_test + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "sync" + "testing" + "time" + + "github.com/ethereum-optimism/optimism/espresso/bindings" + env "github.com/ethereum-optimism/optimism/espresso/environment" + "github.com/ethereum-optimism/optimism/op-batcher/batcher" + "github.com/ethereum-optimism/optimism/op-batcher/compressor" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" + "github.com/ethereum-optimism/optimism/op-e2e/system/e2esys" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive/params" + "github.com/ethereum-optimism/optimism/op-service/bigs" + "github.com/ethereum-optimism/optimism/op-service/sources" + "github.com/ethereum-optimism/optimism/op-service/txmgr" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/require" +) + +// waitForRollupToMovePastL1Block waits until the targeted rollup cli moves +// past the reference l1BlockNumber. This indicates that the rollupCli is +// still receiveing information that indicates that the L1 has progressed +// past the desired height. +// +// For convenience, this also returns the Local Safe L2 Height of the last +// call to the Sync Status on the Rollup Client. If this wait passes, it +// will be the LocalSafeL2 height of the SyncStatus that exceeded the +// referenced l1BlockNumber. +func waitForRollupToMovePastL1Block(ctx context.Context, rollupCli *sources.RollupClient, l1BlockNumber uint64) (uint64, error) { + timeoutCtx, cancel := context.WithTimeout(ctx, 2*time.Minute) + var localSafeL2Height uint64 + defer cancel() + err := wait.For(timeoutCtx, 100*time.Millisecond, func() (bool, error) { + status, err := rollupCli.SyncStatus(ctx) + if err != nil { + return false, err + } + + localSafeL2Height = status.LocalSafeL2.Number + return status.CurrentL1.Number > l1BlockNumber, nil + }) + + return localSafeL2Height, err +} + +// TestBatcherSwitching is a test case that is meant to verify the correct +// expected behavior when switching between an Espresso batcher and a +// fallback batcher, ensuring seamless transitions in both directions. +// +// In this scenario the test starts with the batcher running in Espresso +// mode and verifies transactions work correctly. It then stops the Espresso batcher, +// sends switch action to the Batch Authenticator contract and switches to the +// fallback batcher, verifies transactions continue to go through. Next, it switches +// back to the Espresso batcher by restarting it with proper caffeination heights +// (both Espresso and L2 heights set to ensure correct sync points) and verifies +// that transactions continue to go through. +func TestBatcherSwitching(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + launcher := new(env.EspressoDevNodeLauncherDocker) + + // We will need this config to start a new instance of "TEE" batcher + // with parameters tweaked. + batcherConfig := &batcher.CLIConfig{} + // L1FinalizedDistance(0) to avoid long delays after batcher switch. + // The batcher-config options run before GetBatcherConfig so the snapshot it + // takes into batcherConfig reflects them. Small frames + a long channel + // duration force multi-frame channels split across L1 blocks. + system, espressoDevNode, err := launcher.StartE2eDevnet(ctx, t, + env.WithL1FinalizedDistance(0), + env.WithSequencerUseFinalized(true), + env.WithBatcherTargetNumFrames(10), + env.WithBatcherMaxL1TxSize(250), + env.WithBatcherMaxChannelDuration(1000), + // Unbounded pending L1 txs so the Espresso auth+batch pairs (routed + // through the ordered txmgr queue) publish concurrently instead of + // one-per-L1-block; otherwise L1 data availability lags far behind the + // sequencer and the verifier cannot derive recent blocks within the + // test's confirmation windows. + env.WithBatcherMaxPendingTransactions(0), + env.GetBatcherConfig(batcherConfig)) + require.NoError(t, err) + + l1Client := system.NodeClient(e2esys.RoleL1) + espClient := espressoDevNode.Client() + + deployerTransactor, err := bind.NewKeyedTransactorWithChainID(system.Config().Secrets.Deployer, system.Cfg.L1ChainIDBig()) + require.NoError(t, err) + + batchAuthenticator, err := bindings.NewBatchAuthenticator(system.RollupConfig.BatchAuthenticatorAddress, l1Client) + require.NoError(t, err) + + defer env.Stop(t, system) + defer env.Stop(t, espressoDevNode) + + // Send Transaction on L1, and wait for verification on the L2 Verifier + env.RunSimpleL1TransferAndVerifier(ctx, t, system) + + // Verify everything works + env.RunSimpleL2Burn(ctx, t, system) + + // Stop the "TEE" batcher + err = system.BatchSubmitter.TestDriver().StopBatchSubmitting(ctx) + require.NoError(t, err) + + // Switch active batcher to the fallback (non-Espresso) path + tx, err := batchAuthenticator.SetActiveIsEspresso(deployerTransactor, false) + require.NoError(t, err) + _, err = wait.ForReceiptOK(ctx, l1Client, tx.Hash()) + require.NoError(t, err) + + // Start the fallback batcher + err = system.FallbackBatchSubmitter.TestDriver().StartBatchSubmitting() + require.NoError(t, err) + + // Everything should still work + env.RunSimpleL2Burn(ctx, t, system) + + // Stop the fallback batcher + err = system.FallbackBatchSubmitter.TestDriver().StopBatchSubmitting(ctx) + require.NoError(t, err) + + // Switch batcher back to the "TEE" (Espresso) batcher + tx, err = batchAuthenticator.SetActiveIsEspresso(deployerTransactor, true) + require.NoError(t, err) + switchReceipt, err := wait.ForReceiptOK(ctx, l1Client, tx.Hash()) + require.NoError(t, err) + + // Give things time to settle + l2Height, err := waitForRollupToMovePastL1Block(ctx, system.RollupClient(e2esys.RoleVerif), bigs.Uint64Strict(switchReceipt.BlockNumber)) + require.NoError(t, err) + + espHeight, err := espClient.FetchLatestBlockHeight(ctx) + require.NoError(t, err) + + // Start a new "TEE" batcher + // Use moderate channel settings so the new batcher submits batches promptly without posting + // every L1 block. + batcherConfig.MaxChannelDuration = 10 + batcherConfig.TargetNumFrames = 1 + batcherConfig.MaxL1TxSize = 120_000 + // Caffeinate at espHeight-1 (last already-sealed block) so the streamer reads from + // espHeight inclusive and picks up the batches this batcher re-submits there. + batcherConfig.Espresso.CaffeinationHeightEspresso = espHeight - 1 + batcherConfig.Espresso.CaffeinationHeightL2 = l2Height + batcherCtx, cancelBatcher := context.WithCancelCause(ctx) + defer cancelBatcher(nil) + newBatcher, err := batcher.BatcherServiceFromCLIConfig(batcherCtx, cancelBatcher, "0.0.1", batcherConfig, system.BatchSubmitter.Log, + batcher.WithEspressoClientOverride(system.EspressoClient)) + require.NoError(t, err) + err = newBatcher.Start(batcherCtx) + require.NoError(t, err) + + // Everything should still work (use longer timeout after batcher switch) + env.RunSimpleL2BurnWithTimeout(ctx, t, system, 5*time.Minute) +} + +// TxManagerIntercept is a txmgr.TxManager that wraps another txmgr.TxManager +// and intercepts calls to Send and SendAsync. +// +// The purpose of this intercept is to simulate a failure in the tx submission +// process such that when activated a single frame of a multi-frame channel is +// sent to L1, and the remaining frames fail to be sent, triggering the fallback +// batcher to take over. +type TxManagerIntercept struct { + txmgr.TxManager + sync.Mutex + + // shouldFail indicates whether to simulate a failure on Send/SendAsync. + shouldFail bool + + // triggerAfterOne indicates whether to start failing after a single + // successful Send/SendAsync. + triggerAfterOne bool + + // failureCount tracks the number of failures that have occurred. + failureCount int + + successfulFrames map[derive.ChannelID][]derive.Frame + unsuccessfulFrames map[derive.ChannelID][]derive.Frame +} + +func NewTxManagerIntercept(base txmgr.TxManager) *TxManagerIntercept { + return &TxManagerIntercept{ + TxManager: base, + successfulFrames: make(map[derive.ChannelID][]derive.Frame), + unsuccessfulFrames: make(map[derive.ChannelID][]derive.Frame), + } +} + +// ErrSimulatedTxSubmissionFailure is the sentinel error returned when a +// simulated tx submission failure is triggered. +// +// We utilize this as a placeholder error to indicate that the tx submission +// failure was intentional for testing purposes. +var ErrSimulatedTxSubmissionFailure = errors.New("simulated tx submission failure") + +// decodeFrameInformation takes a txmgr.TxCandidate and attempts to decode +// frames contained within either the Blob fields, or the TxData field. +func decodeFrameInformation(candidate txmgr.TxCandidate) ([]derive.Frame, error) { + if len(candidate.TxData) > 0 { + // We have a CallData tx, so we can decode the frame information from + // the tx data. + return decodeFrameInformationFromTxData(candidate) + } + + if len(candidate.Blobs) > 0 { + // We have a Blob tx, so we can decode the frame information from + // the blobs. + return decodeFrameInformationFromBlobs(candidate) + } + + return nil, fmt.Errorf("tx candidate has neither tx data nor blobs to decode frame information from") +} + +// decodeFrameInformationFromData takes a byte slice and decodes each frame +// until it can no longer decode any frames. It returns a slice of all +// decoded frames, and any error encountered. +func decodeFrameInformationFromData(data []byte) ([]derive.Frame, error) { + if data[0] != params.DerivationVersion0 { + // Not a supported derivation version + return nil, fmt.Errorf("unsupported derivation version: %d", data[0]) + } + + var frames []derive.Frame + reader := bytes.NewBuffer(data[1:]) + for { + var frame derive.Frame + err := frame.UnmarshalBinary(reader) + if errors.Is(err, io.EOF) { + // We've consumed all of the frames. + break + } + + // If this is any other error, it indicates that there was an + // error decoding the frame. + if err != nil { + return frames, fmt.Errorf("error decoding frame: %w", err) + } + + frames = append(frames, frame) + } + + return frames, nil +} + +// decodeFrameInformationFromTxData takes a txmgr.TxCandidate and will assume +// that the frame data is encoded within the TxData. This data will be taken +// and decoded into frames and returned. +func decodeFrameInformationFromTxData(candidate txmgr.TxCandidate) ([]derive.Frame, error) { + data := candidate.TxData + + return decodeFrameInformationFromData(data) +} + +// decodeFrameInformationFromBlobs() takes a txmgr.TxCandidate and will assume +// that the frame data is encoded within the Blobs. The blobs will be +// converted back to txData, and the data will be decoded into frames. +func decodeFrameInformationFromBlobs(candidate txmgr.TxCandidate) ([]derive.Frame, error) { + var frames []derive.Frame + for _, blob := range candidate.Blobs { + data, err := blob.ToData() + if err != nil { + return frames, fmt.Errorf("error converting blob to data: %w", err) + } + + newFrames, err := decodeFrameInformationFromData(data) + if err != nil { + return frames, err + } + frames = append(frames, newFrames...) + } + + return frames, nil +} + +func (t *TxManagerIntercept) markFramesAsSuccessful(frames []derive.Frame) { + t.Lock() + defer t.Unlock() + for _, frame := range frames { + t.successfulFrames[frame.ID] = append(t.successfulFrames[frame.ID], frame) + } +} + +func (t *TxManagerIntercept) markFramesAsUnsuccessful(frames []derive.Frame) { + t.Lock() + defer t.Unlock() + for _, frame := range frames { + t.unsuccessfulFrames[frame.ID] = append(t.unsuccessfulFrames[frame.ID], frame) + } +} + +// Send implements txmgr.TxManager. +// +// Send is overridden to simulate a failure when shouldFail is true, and to +// allow for one final transaction to be sent before failures begin when +// triggerAfterOne is true. +func (t *TxManagerIntercept) Send(ctx context.Context, candidate txmgr.TxCandidate) (*types.Receipt, error) { + frames, err := decodeFrameInformation(candidate) + if err != nil { + // Not a batch frame transaction (e.g. authenticateBatch call) — pass through + return t.TxManager.Send(ctx, candidate) + } + + if t.shouldFail { + t.failureCount++ + t.markFramesAsUnsuccessful(frames) + time.Sleep(50 * time.Millisecond) // Simulate some delay + return nil, ErrSimulatedTxSubmissionFailure + } + + if t.triggerAfterOne { + t.shouldFail = true + } + + t.markFramesAsSuccessful(frames) + + return t.TxManager.Send(ctx, candidate) +} + +// SendAsync implements txmgr.TxManager. +// +// SendAsync is overridden to simulate a failure when shouldFail is true, and +// to allow for one final transaction to be sent before failures begin when +// triggerAfterOne is true. +func (t *TxManagerIntercept) SendAsync(ctx context.Context, candidate txmgr.TxCandidate, ch chan txmgr.SendResponse) { + frames, err := decodeFrameInformation(candidate) + if err != nil { + // Not a batch frame transaction (e.g. authenticateBatch call) — pass through + t.TxManager.SendAsync(ctx, candidate, ch) + return + } + + if t.shouldFail { + t.failureCount++ + t.markFramesAsUnsuccessful(frames) + time.Sleep(50 * time.Millisecond) // Simulate some delay + ch <- txmgr.SendResponse{Err: ErrSimulatedTxSubmissionFailure} + return + } + + if t.triggerAfterOne { + t.shouldFail = true + } + + t.markFramesAsSuccessful(frames) + t.TxManager.SendAsync(ctx, candidate, ch) +} + +type partialFrameData struct { + channelID derive.ChannelID + successfulFrames []derive.Frame + unsuccessfulFrames []derive.Frame +} + +func (t *TxManagerIntercept) partialFrameData() []partialFrameData { + var partials []partialFrameData + + for channelID, unsuccessfulFrames := range t.unsuccessfulFrames { + successfulFrames, ok := t.successfulFrames[channelID] + if !ok { + continue + } + + partials = append(partials, partialFrameData{ + channelID: channelID, + successfulFrames: successfulFrames, + unsuccessfulFrames: unsuccessfulFrames, + }) + } + + return partials +} + +// Compile time assertion to ensure TxManagerIntercept implements +// txmgr.TxManager. +var _ txmgr.TxManager = (*TxManagerIntercept)(nil) + +// retryWaitNTimes retries the given function up to n times until it +// succeeds. +func retryWaitNTimes(fn func() error, n int) error { + var lastErr error + for range n { + lastErr = fn() + if lastErr == nil { + break + } + } + + return lastErr +} + +// TestFallbackMechanismIntegrationTestChannelNotClosed is a test case that is +// meant to verify the correct expected behavior in the event that the Espresso +// Batcher encounters an error mid L1 Batch submission that prevents the full +// channel from being submitted to the L1. +// +// In this scenario this issue is expected to send a single frame of a +// multi-frame channel to the contract. At this point the batch should be +// switched to the fallback and the fallback batcher should continue +// submitting the remaining frames of the channel without any issues. +func TestFallbackMechanismIntegrationTestChannelNotClosed(t *testing.T) { + ctx, cancel := context.WithTimeoutCause(context.Background(), time.Minute*10, fmt.Errorf("test did not complete within expected time allotment: %w", context.DeadlineExceeded)) + defer cancel() + + launcher := new(env.EspressoDevNodeLauncherDocker) + + // In order to force a multi-frame channel with the e2e system setup, + // we need to multimately modify the channel config that will be utilized + // by the batcher. + // + // This may seem a bit convoluted, but we have to contend with a few + // different settings in order to ensure that the behavior we are + // targeting is achieved. + // + // All of the options given below are utilized with the specific purpose + // of triggering multi-frame channels. + // + // NOTE: Some of the configuration options pull double-duty. They are + // utilized in both the creation of the frames, and the sending of the + // frames. In both scenarios, they may behave differently. I will make + // an effor to note them where they occur. + + system, espressoDevNode, err := launcher.StartE2eDevnet( + ctx, + t, + // Make Sure that the Batcher does not start Running + env.WithBatcherStoppedInitially(), + + // Explicitly disable using any sort of compression. This is + // necessary as we will be specifying that we will be targeting + // a specific frame size, and we don't want compression to indirectly + // interfere with this process. + env.WithBatcherCompressor(compressor.NoneKind), + + // This sets the Target Number of Frames that each channel is aiming + // to achieve. In this case we specify 3 so that we can ensure that + // our channel will always be a multi frame channel. + // + // Coupling this with the max channel duration helps us to ensure that + // each channel will always aim for the same number of channels. + // + // NOTE: This has different behavior when constructing the frames on + // the Batcher preparation than it does on the L1 submission. + // Specifically concerning the Da Type DaTypeCalldata. When utilizing + // call data, the L1 Submission will **ALWAYS** utilize 1 frame instead + // of this passed value. Yet channel construction will utilize this + // provided value as appropriate. + env.WithBatcherTargetNumFrames(3), + + // We set the MaxL1TxSize to some value that will hold our L2 + // Transaction size comfortably. + env.WithBatcherMaxL1TxSize(1200), + + // We set the MaxChannelDuration to 0 specifically to disable premature + // channel closing before we have enough frames. The default behavior + // is to create a new Channel with the specified window of L1 Blocks. + // The idea is that you can prevent eager channel production when + // you are not producing blocks with transactions. + // + // Setting this to 0 explicitly disables the feature, and as a result + // it will only send the data when the previous conditions are met. + env.WithBatcherMaxChannelDuration(0), + + // Unbounded pending L1 txs so the Espresso auth+batch pairs (routed + // through the ordered txmgr queue) publish concurrently instead of + // one-per-L1-block; otherwise L1 data availability lags far behind the + // sequencer and the verifier cannot derive recent blocks within the + // test's confirmation windows. + env.WithBatcherMaxPendingTransactions(0), + ) + + require.NoError(t, err) + defer env.Stop(t, system) + defer env.Stop(t, espressoDevNode) + + // We create an intercept around the existing tx manager so we have + // control over when our failures start to occur. + + interceptTxManager := NewTxManagerIntercept( + system.BatchSubmitter.TxManager, + ) + + // Replace the existing TxManager with our intercept + system.BatchSubmitter.TestDriver().Txmgr = interceptTxManager + + // Start the Batcher again, so the publishingLoop picks up the TxManager + // when creating its queue. + err = system.BatchSubmitter.TestDriver().StartBatchSubmitting() + require.NoError(t, err) + + // Wait for the Next L2 Block to be verified by ensure everything is + // working and progressing without issue + err = wait.ForProcessingFullBatch(ctx, system.RollupClient(e2esys.RoleVerif)) + require.NoError(t, err) + + l2Seq := system.NodeClient(e2esys.RoleSeq) + l1Client := system.NodeClient(e2esys.RoleL1) + l2Verif := system.NodeClient(e2esys.RoleVerif) + + // Let's make sure that the system is progressing initially for both + // the Sequencer, the Verifier, and the L1Node + err = wait.ForBlock(ctx, l2Seq, 3) + require.NoError(t, err) + err = retryWaitNTimes(func() error { + return wait.ForNextBlock(ctx, l2Verif) + }, 3) + require.NoError(t, err) + + // Verify everything works + env.RunSimpleL2Burn(ctx, t, system) + + // We want to trigger the failure mode now. + interceptTxManager.triggerAfterOne = true + + // Now we need to submit a multi-frame channel to L1 to trigger the + // failure. We can do this by adjusting the batcher config to use a very + // small MaxL1TxSize such that even a small L2 transaction will result in + // multiple frames. + + // We want enough L2 Transactions to ensure we have multiple frames. + const n = 10 + + receipts, err := env.RunSimpleMultiTransactions(ctx, t, system, n) + require.NoError(t, err) + + // We want to wait until we know that the intercept tx manager has + // trigger the failure mode successfully, and that all n transactions + // have been attempted. + + // Wait until at least 2 L2 blocks have been mined (one for the + // a block with successful frames, and one for a block with failed frames). + err = wait.ForNextBlock(ctx, l2Seq) + require.NoError(t, err) + err = wait.ForNextBlock(ctx, l2Seq) + require.NoError(t, err) + + // Make sure that at least one failure has occurred, as this should + // indicate that the submission process should have failed a multiframe + // channel submission. + err = wait.For(ctx, 10*time.Second, func() (bool, error) { + return interceptTxManager.failureCount >= 1, nil + }) + require.NoError(t, err) + + // Stop the "TEE" batcher + err = system.BatchSubmitter.TestDriver().StopBatchSubmitting(ctx) + require.NoError(t, err) + + // Switch active batcher + options, err := bind.NewKeyedTransactorWithChainID(system.Config().Secrets.Deployer, system.Cfg.L1ChainIDBig()) + require.NoError(t, err) + + batchAuthenticator, err := bindings.NewBatchAuthenticator(system.RollupConfig.BatchAuthenticatorAddress, l1Client) + require.NoError(t, err) + + tx, err := batchAuthenticator.SetActiveIsEspresso(options, false) + require.NoError(t, err) + _, err = wait.ForReceiptOK(ctx, l1Client, tx.Hash()) + require.NoError(t, err) + + // Start the fallback batcher + err = system.FallbackBatchSubmitter.TestDriver().StartBatchSubmitting() + require.NoError(t, err) + + // There should be some failure recorded in the intercept tx manager that + // has a corresponding success in the intercept tx manager. + + partialFrameData := interceptTxManager.partialFrameData() + require.Greaterf(t, len(partialFrameData), 0, "expected to find at least one partially submitted frame") + + // Verify that our previous receipts were also recorded on L1 + for i, receipt := range receipts { + _, err := wait.ForReceiptOK(ctx, l2Verif, receipt.TxHash) + require.NoError(t, err, "failed to find receipt %d for tx %s on L2 Verifier", i, receipt.TxHash) + } + + // Everything should still work + env.RunSimpleL2Burn(ctx, t, system) +} diff --git a/espresso/environment/15_espresso_enforcement_hardfork_test.go b/espresso/environment/15_espresso_enforcement_hardfork_test.go new file mode 100644 index 00000000000..41c86582379 --- /dev/null +++ b/espresso/environment/15_espresso_enforcement_hardfork_test.go @@ -0,0 +1,192 @@ +package environment_test + +import ( + "context" + "testing" + "time" + + "github.com/ethereum-optimism/optimism/espresso/bindings" + env "github.com/ethereum-optimism/optimism/espresso/environment" + "github.com/ethereum-optimism/optimism/op-batcher/batcher" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" + "github.com/ethereum-optimism/optimism/op-e2e/system/e2esys" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/stretchr/testify/require" +) + +// TestEspressoEnforcementHardfork exercises the EspressoTime +// hardfork transition while the chain is running on the fallback batcher. +// +// Pre-fork the fallback batcher posts plain BatchInbox txs (no +// BatchAuthenticator interaction) and the verifier accepts them via upstream +// sender-based authorization. At EspressoTime the batcher's gate flips +// (`isFallbackAuthRequired`) and it starts calling `authenticateBatchInfo`; +// the verifier only *enforces* event-based authentication a grace period later +// (EspressoTime + derive.BatchAuthEnforcementDelaySecs), accepting either +// sender-auth or event-auth in between, so there is no window where the +// batcher omits authentication while the verifier requires it. +// +// `activeIsEspresso` is flipped to false before Phase 1 (modeling a chain +// that experienced a fallback-batcher event before the hardfork) and back to +// true before Phase 3 so the subsequently-started TEE batcher is the +// on-chain active batcher. +func TestEspressoEnforcementHardfork(t *testing.T) { + // 5 minutes covers Espresso devnet startup plus pre-fork test ops. + const enforcementOffset = 5 * time.Minute + + ctx, cancel := context.WithTimeout(context.Background(), 25*time.Minute) + defer cancel() + + launcher := new(env.EspressoDevNodeLauncherDocker) + + // Captured for the post-fork TEE batcher restart. + espressoBatcherConfig := &batcher.CLIConfig{} + + // The batcher-config options run before GetBatcherConfig so the snapshot it + // takes into espressoBatcherConfig reflects them. Small frames + a long + // channel duration force multi-frame channels split across L1 blocks, which + // makes a batch tx land in an L1 block at/after the fork boundary likely so + // the post-fork authentication gate is actually exercised. + system, espressoDevNode, err := launcher.StartE2eDevnet(ctx, t, + env.WithEspressoEnforcementOffset(enforcementOffset), + env.WithL1FinalizedDistance(0), + env.WithSequencerUseFinalized(true), + env.WithBatcherStoppedInitially(), + env.WithBatcherTargetNumFrames(10), + env.WithBatcherMaxL1TxSize(250), + env.WithBatcherMaxChannelDuration(1000), + // Unbounded pending L1 txs so the Espresso auth+batch pairs (routed + // through the ordered txmgr queue) publish concurrently instead of + // one-per-L1-block; otherwise L1 data availability lags far behind the + // sequencer and the verifier cannot derive recent blocks within the + // test's confirmation windows. + env.WithBatcherMaxPendingTransactions(0), + env.GetBatcherConfig(espressoBatcherConfig), + ) + require.NoError(t, err) + defer env.Stop(t, system) + defer env.Stop(t, espressoDevNode) + + require.NotNil(t, system.RollupConfig.EspressoTime, + "test requires EspressoTime to be set on the rollup config") + forkTime := *system.RollupConfig.EspressoTime + nowSec := uint64(time.Now().Unix()) + require.Greater(t, forkTime, nowSec, "fork timestamp must be in the future at test start") + t.Logf("EspressoTime = %d (now = %d, in %ds)", forkTime, nowSec, forkTime-nowSec) + + l1Client := system.NodeClient(e2esys.RoleL1) + verifClient := system.NodeClient(e2esys.RoleVerif) + verifRollup := system.RollupClient(e2esys.RoleVerif) + espClient := espressoDevNode.Client() + + deployerTransactor, err := bind.NewKeyedTransactorWithChainID( + system.Config().Secrets.Deployer, system.Cfg.L1ChainIDBig()) + require.NoError(t, err) + batchAuthenticator, err := bindings.NewBatchAuthenticator( + system.RollupConfig.BatchAuthenticatorAddress, l1Client) + require.NoError(t, err) + + activeIsEspresso, err := batchAuthenticator.ActiveIsEspresso(nil) + require.NoError(t, err) + require.True(t, activeIsEspresso, "BatchAuthenticator default should be activeIsEspresso=true") + + // Flip to fallback before Phase 1 so the fallback batcher's + // `isBatcherActive` check (consulted post-lead-time) sees itself as + // active and continues publishing across the boundary. + switchTx, err := batchAuthenticator.SetActiveIsEspresso(deployerTransactor, false) + require.NoError(t, err) + _, err = wait.ForReceiptOK(ctx, l1Client, switchTx.Hash()) + require.NoError(t, err) + activeIsEspresso, err = batchAuthenticator.ActiveIsEspresso(nil) + require.NoError(t, err) + require.False(t, activeIsEspresso, "first SetActiveIsEspresso(false) should set activeIsEspresso=false") + + // Phase 1 (pre-fork): fallback batcher publishes plain BatchInbox txs. + require.NoError(t, system.FallbackBatchSubmitter.TestDriver().StartBatchSubmitting()) + env.RunSimpleL1TransferAndVerifier(ctx, t, system) + env.RunSimpleL2Burn(ctx, t, system) + + status, err := verifRollup.SyncStatus(ctx) + require.NoError(t, err) + require.Less(t, status.SafeL2.Time, forkTime, "verifier should still be pre-fork after initial L2 burn") + + // Phase 2 (boundary): fallback batcher continues across forkTime, + // switching to authenticateBatchInfo at forkTime - leadTime. + preBoundaryL1Head, err := l1Client.BlockNumber(ctx) + require.NoError(t, err) + require.NoError(t, wait.ForBlockWithTimestamp(ctx, l1Client, forkTime)) + t.Logf("L1 reached fork timestamp; pre-boundary L1 head was %d", preBoundaryL1Head) + + // A stall here would indicate dropped batches (unauthenticated tx in a + // post-fork L1 block) — i.e. lead-time too short. + require.NoError(t, wait.ForBlockWithTimestamp(ctx, verifClient, forkTime+30)) + require.NoError(t, wait.ForNextBlock(ctx, l1Client)) + require.NoError(t, wait.ForNextBlock(ctx, l1Client)) + + status, err = verifRollup.SyncStatus(ctx) + require.NoError(t, err) + require.Greater(t, status.SafeL2.Time, forkTime, + "verifier safe head should have advanced past forkTime (lead-time too short?)") + + postBoundaryL1Head, err := l1Client.BlockNumber(ctx) + require.NoError(t, err) + + // Confirm the lead-time path actually executed. + authIter, err := batchAuthenticator.FilterBatchInfoAuthenticated(&bind.FilterOpts{ + Context: ctx, + Start: preBoundaryL1Head, + End: &postBoundaryL1Head, + }, nil) + require.NoError(t, err) + defer authIter.Close() + authEventCount := 0 + for authIter.Next() { + authEventCount++ + } + require.NoError(t, authIter.Error()) + require.Greater(t, authEventCount, 0, + "fallback batcher should have emitted >=1 BatchInfoAuthenticated event around the boundary") + t.Logf("fallback batcher emitted %d BatchInfoAuthenticated events around the boundary", authEventCount) + + // Phase 3: stop fallback, flip back to TEE, start TEE batcher. + require.NoError(t, system.FallbackBatchSubmitter.TestDriver().StopBatchSubmitting(ctx)) + + switchTx, err = batchAuthenticator.SetActiveIsEspresso(deployerTransactor, true) + require.NoError(t, err) + _, err = wait.ForReceiptOK(ctx, l1Client, switchTx.Hash()) + require.NoError(t, err) + activeIsEspresso, err = batchAuthenticator.ActiveIsEspresso(nil) + require.NoError(t, err) + require.True(t, activeIsEspresso, "second SetActiveIsEspresso(true) should set activeIsEspresso=true") + + // Stream from the live head, not from genesis. + l2Height, err := waitForRollupToMovePastL1Block(ctx, verifRollup, status.CurrentL1.Number) + require.NoError(t, err) + espHeight, err := espClient.FetchLatestBlockHeight(ctx) + require.NoError(t, err) + + espressoBatcherConfig.MaxChannelDuration = 10 + espressoBatcherConfig.TargetNumFrames = 1 + espressoBatcherConfig.MaxL1TxSize = 120_000 + // Caffeinate at espHeight-1 (last already-sealed block) so the streamer reads from + // espHeight inclusive and picks up the batches this batcher re-submits there. + espressoBatcherConfig.Espresso.CaffeinationHeightEspresso = espHeight - 1 + espressoBatcherConfig.Espresso.CaffeinationHeightL2 = l2Height + // Inherited Stopped=true from WithBatcherStoppedInitially. + espressoBatcherConfig.Stopped = false + + teeCtx, teeCancel := context.WithCancelCause(ctx) + defer teeCancel(nil) + teeBatcher, err := batcher.BatcherServiceFromCLIConfig( + teeCtx, teeCancel, "0.0.1", espressoBatcherConfig, system.BatchSubmitter.Log, + batcher.WithEspressoClientOverride(system.EspressoClient)) + require.NoError(t, err) + require.NoError(t, teeBatcher.Start(teeCtx)) + + // Phase 4 (post-fork): TEE batcher takes over, verifier keeps advancing. + env.RunSimpleL2BurnWithTimeout(ctx, t, system, 5*time.Minute) + + status, err = verifRollup.SyncStatus(ctx) + require.NoError(t, err) + require.Greater(t, status.SafeL2.Time, forkTime, "verifier should have advanced post-fork") +} diff --git a/espresso/environment/2_espresso_liveness_test.go b/espresso/environment/2_espresso_liveness_test.go new file mode 100644 index 00000000000..bb845afb4a8 --- /dev/null +++ b/espresso/environment/2_espresso_liveness_test.go @@ -0,0 +1,126 @@ +package environment_test + +import ( + "context" + "math/big" + "math/rand" + "testing" + + env "github.com/ethereum-optimism/optimism/espresso/environment" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" + "github.com/ethereum-optimism/optimism/op-e2e/system/e2esys" + "github.com/ethereum-optimism/optimism/op-e2e/system/helpers" + geth_types "github.com/ethereum/go-ethereum/core/types" +) + +// TestE2eDevnetWithEspressoDegradedLiveness is a test that checks that +// the rollup will continue to make progress even in the event of intermittent +// Espresso system failures. +// +// The Criteria for this test is as follows: +// +// Requirement: Resubmission to Espresso. +// Randomly turn the Espresso builder off and on. Check that the rollup +// continues to make progress, including progressing settlement on the +// base layer. +// +// We don't have any direct way of turning the Espresso builder off and on via +// the Dev node API at the moment. However, we do have the ability to turn +// the consensus layer on and off via turning hotshot on and off. +// +// This is **NOT** the same thing, nor would it result in the same behavior as +// turning the Builder off and on. For the following reasons: +// +// 1 HotShot being off means no new blocks are being produced +// 2 The Builder being off means that only empty blocks are being produced +// 3 Turning the Builder off potentially means losing pool information, +// requiring re-submission so that the builder can include the transaction +// in the next block. +// +// With these caveats in mind, we may be able to simulate the behavior of 2 +// at the very least, if we intercept the client submitting transactions to +// Espresso, and simulating the client being unable to submit transactions. +// Likewise, we might be able to simulate 3 by falsely reporting to the +// submitter that the transaction was submitted successfully, and withholding +// the submission itself. +func TestE2eDevnetWithEspressoDegradedLiveness(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + launcher := new(env.EspressoDevNodeLauncherDocker) + + // Start a Server to proxy requests to Espresso + _, server, option := env.SetupQueryServiceIntercept( + // This decider will randomly report successful submissions of + // transactions to Espresso, but will not actually submit them. + // This will approximately occur 10% of the time, given the + // criteria to roll a number 0-9 and only to occur if the rolled + // number is 0. + env.SetDecider(env.NewRandomRollFakeSubmitTransactionSuccess( + 10, + 0, + 1, + rand.New(rand.NewSource(0)), + )), + ) + + defer server.Close() + system, espressoDevNode, err := launcher.StartE2eDevnet(ctx, t, option) + + // Signal the testnet to shut down + if have, want := err, error(nil); have != want { + t.Fatalf("failed to start dev environment with espresso dev node:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + + defer system.Close() + defer func() { + err = espressoDevNode.Stop() + if err != nil { + t.Fatalf("failed to stop espresso dev node: %v", err) + } + }() + + addressAlice := system.Cfg.Secrets.Addresses().Alice + + l2Seq := system.NodeClient(e2esys.RoleSeq) + l2Verif := system.NodeClient(e2esys.RoleVerif) + + balanceAliceInitial, err := l2Verif.BalanceAt(ctx, addressAlice, nil) + if have, want := err, error(nil); have != want { + t.Fatalf("Failed to fetch Alice's balance:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + + const N = 10 + { + var receipts []*geth_types.Receipt + + for i := range N { + receipt := helpers.SendL2TxWithID(t, system.Cfg.L2ChainIDBig(), l2Seq, system.Cfg.Secrets.Bob, func(opts *helpers.TxOpts) { + opts.Nonce = uint64(i) + opts.ToAddr = &addressAlice + opts.Value = big.NewInt(1) + }) + + receipts = append(receipts, receipt) + } + + // Let's verify that all of our transactions came through successfully + for _, receipt := range receipts { + _, err := wait.ForReceiptOK(ctx, l2Verif, receipt.TxHash) + if have, want := err, error(nil); have != want { + t.Fatalf("Waiting for L2 tx on verification client:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + } + + // Alice's balance should have increased by N + balanceAliceFinal, err := l2Verif.BalanceAt(ctx, addressAlice, nil) + if have, want := err, error(nil); have != want { + t.Fatalf("Failed to fetch Alice's balance:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + + expectedBalance := new(big.Int).Add(balanceAliceInitial, big.NewInt(int64(N))) + if balanceAliceFinal.Cmp(expectedBalance) != 0 { + t.Fatalf("Alice's balance did not increase as expected:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", balanceAliceFinal, expectedBalance) + } + } +} diff --git a/espresso/environment/3_2_espresso_deterministic_state_test.go b/espresso/environment/3_2_espresso_deterministic_state_test.go new file mode 100644 index 00000000000..b6a8512e2f1 --- /dev/null +++ b/espresso/environment/3_2_espresso_deterministic_state_test.go @@ -0,0 +1,302 @@ +package environment_test + +import ( + "bytes" + "context" + "crypto/ecdsa" + "fmt" + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/rpc" + "github.com/stretchr/testify/require" + + espressoCommon "github.com/EspressoSystems/espresso-network/sdks/go/types" + env "github.com/ethereum-optimism/optimism/espresso/environment" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" + "github.com/ethereum-optimism/optimism/op-e2e/system/e2esys" + "github.com/ethereum-optimism/optimism/op-e2e/system/helpers" + "github.com/ethereum-optimism/optimism/op-service/bigs" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common/hexutil" + geth_types "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" +) + +// TestDeterministicDerivationExecutionStateWithInvalidTransaction is a test that +// attempts to make sure that the op-node continues to derive and finalize a +// valid chain even when malicious transactions are submitted. +// +// This test is designed to evaluate Test 3.2 as outlined within the +// Espresso Celo Integration plan. It has stated task definition as follows: +// +// Arrange: +// Running Sequencer, Batcher in Espresso mode, and OP node. +// Act: +// Send some transactions from Bob to Alice and some regular L2 transactions. +// While sending regular L2 transactions to the sequencer also send transactions to Espresso using an invalid batcher address, and transactions directly to L1 (e.g. transactions that were not previously posted to Espresso). +// Assert: +// The op-node continues to finalize valid blocks on L1, ignoring the malicious transactions. + +func TestDeterministicDerivationExecutionStateWithInvalidTransaction(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + launcher := new(env.EspressoDevNodeLauncherDocker) + + // Start the devnet with the sequencer using finalized blocks + system, espressoDevNode, err := launcher.StartE2eDevnet(ctx, t, env.WithL1FinalizedDistance(0), env.WithSequencerUseFinalized(true)) + // Signal the testnet to shut down + if have, want := err, error(nil); have != want { + t.Fatalf("failed to start dev environment with espresso dev node:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + + defer env.Stop(t, system) + defer env.Stop(t, espressoDevNode) + + // We want to setup our test + addressAlice := system.Cfg.Secrets.Addresses().Alice + espressoClient := espressoDevNode.Client() + l1Client := system.NodeClient(e2esys.RoleL1) + l2Verif := system.NodeClient(e2esys.RoleVerif) + l2Seq := system.NodeClient(e2esys.RoleSeq) + + // We want to send some transactions from Bob to Alice + { + privateKey := system.Cfg.Secrets.Bob + bobOptions, err := bind.NewKeyedTransactorWithChainID(privateKey, system.Cfg.L1ChainIDBig()) + if have, want := err, error(nil); have != want { + t.Fatalf("failed to create transaction options for bob:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + + mintAmount := new(big.Int).SetUint64(1) + bobOptions.Value = mintAmount + _ = helpers.SendDepositTx(t, system.Cfg, l1Client, l2Verif, bobOptions, func(l2Opts *helpers.DepositTxOpts) { + // Send from Bob to Alice + l2Opts.ToAddr = addressAlice + }) + } + + // Send some regular L2 transactions in each iteration and there are 10 rounds in total + // Since we wait for valid transactions sent before attackRoundEspresso and after attackRoundL1 to be included, + // we can be confident that the malicious transactions are included on L1/Espresso while the L2 chain is making progress. + // The reason is that the iterations of the test are executed sequentially. + numIterations := 10 + attackRoundEspresso := 5 // the round where we send transactions directly to Espresso outside without running the batcher code. + attackRoundL1 := 7 // the round where we send a transaction directly to the BatchInbox EOA. + // Compare states between nodes for multiple latest blocks + // We don't compare states for every individual block as any diff in block x will be reflected in block x + n + for i := 0; i < numIterations; i++ { + + // Send some regular L2 transactions in each iteration + tx := geth_types.MustSignNewTx(system.Cfg.Secrets.Bob, geth_types.LatestSignerForChainID(system.Cfg.L2ChainIDBig()), &geth_types.DynamicFeeTx{ + ChainID: system.Cfg.L2ChainIDBig(), + Nonce: uint64(i + 1), // +1 because of the deposit transaction above + To: &addressAlice, + Value: big.NewInt(1), + GasTipCap: big.NewInt(10), + GasFeeCap: big.NewInt(200), + Gas: 21_000, + }) + err := l2Seq.SendTransaction(ctx, tx) + if have, want := err, error(nil); have != want { + t.Fatalf("Sending L2 tx:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + // Wait for the receipt + _, err = wait.ForReceiptOK(ctx, l2Seq, tx.Hash()) + if have, want := err, error(nil); have != want { + t.Fatalf("Waiting for L2 tx:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + + // When it is the attack round, send some Espresso transactions using fakeBatcherPrivateKey directly to Espresso. + // The L2 batch embedded in the Espresso transaction is well formed but will be ignored as the transaction is not signed by the batcher and the batch information is not authenticated to the batch authentication contract either. + + switch i { + case attackRoundEspresso: + // Create a fake Espresso transaction + fakeBatcherPrivateKey, err := forgedBatcherPrivateKey() + if err != nil { + t.Fatalf("Failed to get fake batcher private key:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", err, nil) + } + fakeEspressoTransaction, err := createEspressoTransaction(TEST_ESPRESSO_TRANSACTION, system.Cfg.L2ChainIDBig(), fakeBatcherPrivateKey) + if err != nil { + t.Fatalf("Failed to create fake Espresso transaction:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", err, nil) + } + + // Send transaction directly to Espresso to bypass the batcher + txHash, err := espressoClient.SubmitTransaction(ctx, *fakeEspressoTransaction) + if err != nil { + t.Fatalf("Failed to submit transaction:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", err, nil) + } + + err = env.WaitForEspressoTx(ctx, txHash, espressoClient) + if err != nil { + t.Fatalf("Espresso transaction failed to be confirmed:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", err, nil) + } + + case attackRoundL1: + // create a transaction + tx := geth_types.MustSignNewTx(system.Cfg.Secrets.Bob, system.RollupConfig.L1Signer(), &geth_types.DynamicFeeTx{ + ChainID: system.Cfg.L1ChainIDBig(), + Nonce: 1, + To: &system.RollupConfig.BatchInboxAddress, + Value: big.NewInt(1), + GasTipCap: big.NewInt(1 * params.GWei), + GasFeeCap: big.NewInt(10 * params.GWei), + Gas: 5_000_000, + }) + // Send a transaction directly to L1 + err = l1Client.SendTransaction(ctx, tx) + if have, want := err, error(nil); have != want { + t.Fatalf("failed to send transaction directly to L1:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + + // BatchInbox is an EOA, so the tx succeeds on L1. The pipeline + // ignores it because there is no matching BatchInfoAuthenticated event. + _, err = wait.ForReceiptOK(ctx, l1Client, tx.Hash()) + if have, want := err, error(nil); have != want { + t.Fatalf("failed to get receipt for transaction:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + } + + // Get latest finalized block from op node, ensuring that the chain + // continues to finalize despite the malicious transactions submitted + // directly to Espresso and L1, which the derivation pipeline ignores. + // We use BlockByNumber to get the states as the engine state will be reflected in the block. + _, err = l2Verif.BlockByNumber(ctx, big.NewInt(rpc.FinalizedBlockNumber.Int64())) + if err != nil { + t.Fatalf("failed to get block from opBlock: %v", err) + } + } + +} + +// forgeBatcherPrivateKey is a helper function that forge a batcher private key +func forgedBatcherPrivateKey() (*ecdsa.PrivateKey, error) { + return crypto.GenerateKey() +} + +func realBatcherPrivateKey(system *e2esys.System) (*ecdsa.PrivateKey, error) { + return system.Cfg.Secrets.Batcher, nil +} + +const TEST_ESPRESSO_TRANSACTION = "0xf9039df9023da00d68b82fa254b7d23a8584bcaa67be241a269c86aac05a2a6fc805a672bb910ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347944200000000000000000000000000000000000011a0d6cc9c002bc6a8d1c8501c57301b6b2f037494e1e0f61e417411e17f4e80b5afa028881bc4fc4c5fa67f26462837f88937961b6667ae4af043218a0c1b72a5f53ca0d8056577b8ef8e580c0ebc96def906b3699ddc8d91e15abf9c7a7e7bb4f85c96b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080018401c9c380830272ca84681d98b780a0000000000000000000000000000000000000000000000000000000000000000088000000000000000001a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000f849a00d68b82fa254b7d23a8584bcaa67be241a269c86aac05a2a6fc805a672bb910e80a0d7d069186bed40982ca7e7747d61c78718d0dda165d74e62164c1bba165001f784681d98b7c0b8fb7ef8f8a07a2aa57f213dfe5e61ceaebcd45c61252157b4e3c1e82e1ec0dca455b1173ad894deaddeaddeaddeaddeaddeaddeaddeaddead00019442000000000000000000000000000000000000158080830f424080b8a4440a5e20000f424000000000000000000000000100000000681d98b60000000000000000000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001d7d069186bed40982ca7e7747d61c78718d0dda165d74e62164c1bba165001f70000000000000000000000003c44cdddb6a900fa2b585dd299e03d12fa4293bc940000000000000000000000000000000000000000" + +// createEspressoTransaction creates a Espresso transaction with a FAKE or REAL batcher private key +func createEspressoTransaction(transactionString string, chainID *big.Int, batcherKey *ecdsa.PrivateKey) (*espressoCommon.Transaction, error) { + // This is the genesis Espresso transaction that created by honest sequencer + bufData, err := hexutil.Decode(transactionString) + if err != nil { + log.Error("failed to decode Espresso transaction in the test", "error", err) + return nil, err + } + buf := bytes.NewBuffer(bufData) + + // Sign the encoded batch with FAKE or REAL batcher private key + batcherSignature, err := crypto.Sign(crypto.Keccak256(buf.Bytes()), batcherKey) + if err != nil { + return nil, fmt.Errorf("failed to create batcher signature: %w", err) + } + + // Combine signature and batch data + payload := append(batcherSignature, buf.Bytes()...) + + // Create and return Espresso Transaction + return &espressoCommon.Transaction{ + Namespace: bigs.Uint64Strict(chainID), + Payload: payload, + }, nil +} + +// TestValidEspressoTransactionCreation is a test that +// make sure we have correct way to create a Espresso transaction. +// This test is a unit test to serve the correctness of TestDeterministicDerivationExecutionStateWithInvalidTransaction. +func TestValidEspressoTransactionCreation(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + launcher := new(env.EspressoDevNodeLauncherDocker) + + // once this StartE2eDevnet returns, we have a running Espresso Dev Node + system, espressoDevNode, err := launcher.StartE2eDevnet(ctx, t) + // Signal the testnet to shut down + if have, want := err, error(nil); have != want { + t.Fatalf("failed to start dev environment with espresso dev node:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + + defer env.Stop(t, system) + defer env.Stop(t, espressoDevNode) + + // We want to setup our test + espressoClient := espressoDevNode.Client() + // create a real Espresso transaction and make sure it can go through + { + // Create a real Espresso transaction + realBatcherPrivateKey, err := realBatcherPrivateKey(system) + if err != nil { + t.Fatalf("Failed to get real batcher private key:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", err, nil) + } + realEspressoTransaction, err := createEspressoTransaction(TEST_ESPRESSO_TRANSACTION, system.Cfg.L2ChainIDBig(), realBatcherPrivateKey) + if err != nil { + t.Fatalf("Failed to create real Espresso transaction:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", err, nil) + } + + // Send transaction directly to Espresso + txHash, err := espressoClient.SubmitTransaction(ctx, *realEspressoTransaction) + if err != nil { + t.Fatalf("Failed to submit transaction:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", err, nil) + } + + // Parameters for transaction fetching loop, which waits for transactions + // to be sequenced on Espresso + transactionFetchTimeout := 4 * time.Second + transactionFetchInterval := 100 * time.Millisecond + + // Check Espresso will accept the transaction + timer := time.NewTimer(transactionFetchTimeout) + defer timer.Stop() + + ticker := time.NewTicker(transactionFetchInterval) + defer ticker.Stop() + + transactionFound := false + for !transactionFound { + select { + case <-ticker.C: + _, err := espressoClient.FetchTransactionByHash(ctx, txHash) + if err == nil { + // test pass + transactionFound = true + } + case <-timer.C: + t.Fatalf("Failed to fetch transaction by hash after multiple attempts") + case <-ctx.Done(): + t.Fatalf("Cancelling transaction publishing") + } + } + + // The batcher's streamer must be able to unmarshal the transaction the + // same way it would a batcher-produced one, recovering the batcher + // address from the prepended signature. + batchSubmitter := system.BatchSubmitter + // The l1Finalized anchor is attached after decoding and only feeds batcher + // authorization, which this test does not exercise, so 0 is fine here. + batch, err := batchSubmitter.EspressoStreamer().UnmarshalBatch(realEspressoTransaction.Payload, 0) + if have, want := err, error(nil); have != want { + t.Fatalf("Failed to unmarshal batch:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + + // The signer recovered from the signature must be the real batcher, since + // realEspressoTransaction was signed with the real batcher key. + realBatcherAddress := crypto.PubkeyToAddress(realBatcherPrivateKey.PublicKey) + require.Equal(t, realBatcherAddress, batch.Signer(), + "recovered signer should be the real batcher address") + + // The embedded L1-info deposit must be extractable from the batch. + require.NotNil(t, batch.L1InfoDeposit, "batch should carry an L1 info deposit") + } + +} diff --git a/espresso/environment/4_confirmation_integrity_with_reorgs_test.go b/espresso/environment/4_confirmation_integrity_with_reorgs_test.go new file mode 100644 index 00000000000..ddcc3cd17b1 --- /dev/null +++ b/espresso/environment/4_confirmation_integrity_with_reorgs_test.go @@ -0,0 +1,212 @@ +package environment_test + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "math/big" + "strconv" + "testing" + "time" + + env "github.com/ethereum-optimism/optimism/espresso/environment" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/geth" + "github.com/ethereum-optimism/optimism/op-e2e/system/e2esys" + "github.com/ethereum-optimism/optimism/op-e2e/system/helpers" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/log" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// Computes the hash of the content of a batch. Introduced for testing purposes only. +// @param b batch +// @return string containing the hash of a batch +func BatchHash(b *derive.SingularBatch) string { + + // Concatenate the transactions and other relevant metadata of the batch + str := "" + + for _, tx := range b.Transactions { + str = str + tx.String() + } + str = str + b.EpochHash.String() + str = str + strconv.Itoa(int(b.Timestamp)) + str = str + b.ParentHash.String() + + h := sha256.New() + h.Write([]byte(str)) + hash := h.Sum(nil) + + res := hex.EncodeToString(hash) + + return res +} + +// This function computes a list where several batches are sent to unfinalized L1 blocks. +// It works as follows: +// 1. Pick the current L1 block number. Store it so that later we can reorg back to this point +// 2. Start from the height of the first L2 block unsafe block. +// 3. Pick the blocks from this height and the subsequent. +// This first block is initially unsafe, but we wait for it to become safe inside the L1 finality window and likewise for the others. +// 4. Do this until the "finality" window closes i.e. before the number of new L1 blocks is bigger than L1FinalizedDistance. +// 5. While doing this also send transactions to the sequencer to avoid dealing with empty blocks. +// 6. Return the list of batch hashes, the L1 block number to reorg to and also the L2 block height determined in step 2. +// @param ctx,t,system standard parameters to execute a test +// @param l1Client L1 client used to fetch L1 block numbers +// @param l2Seq sequencer used to send transactions +// @param l2Verif OP node we monitor for unsafe blocks +// @return batches list of hashes of the batches +// @return l1HeightStart L1 block number collected in step 1. +// @return unsafeL2BlockNumber L2 block number collected in step 2. +func collectBatchesPublishedOnUnfinalizedL1Blocks(ctx context.Context, t *testing.T, system *e2esys.System, l1Client *ethclient.Client, l2Seq *ethclient.Client, l2Verif *ethclient.Client) ([]string, uint64, uint64) { + var batches []string + + l1Height, err := l1Client.BlockNumber(ctx) + require.NoError(t, err) + l1HeightStart := l1Height + log.Info("L1 height to reorg to", "height", l1HeightStart) + // Keep monitoring L2 blocks while L1 is producing unfinalized blocks + i := int64(0) + // Fetch height of the most recent block which is unsafe and which batch will be sent to L1 at a later stage + rollupClient := system.RollupClient(e2esys.RoleVerif) + + status, err := rollupClient.SyncStatus(ctx) + require.NoError(t, err) + unsafeL2BlockNumber := status.SafeL2.Number + 1 + + nonce := uint64(0) + addressAlice := system.Cfg.Secrets.Addresses().Alice + + for (l1Height - l1HeightStart) < system.Cfg.L1FinalizedDistance { + height := uint64(i) + unsafeL2BlockNumber + + //Send some transactions to fill the batches + receipt := helpers.SendL2TxWithID(t, system.Cfg.L2ChainIDBig(), l2Seq, system.Cfg.Secrets.Bob, func(opts *helpers.TxOpts) { + opts.Nonce = nonce + opts.ToAddr = &addressAlice + opts.Value = new(big.Int).SetUint64(1) + }) + nonce++ + log.Info("Receipt", "value", receipt) + + l2Head, err := geth.WaitForBlockToBeSafe(new(big.Int).SetUint64(height), l2Verif, 10*time.Second) + require.NoError(t, err) + + if err == nil { // Insert new batch in the list + + batch, l2HeadL1Info, err := derive.BlockToSingularBatch(system.RollupCfg(), l2Head) + require.NoError(t, err) + log.Info("l2HeadL1Info", "value", l2HeadL1Info) + + batchHash := BatchHash(batch) + + t.Log("New element inserted", "value", batchHash, "list length", len(batches)) + batches = append(batches, batchHash) + + i++ + } + + l1Height, err = l1Client.BlockNumber(ctx) + require.NoError(t, err) + } + + return batches, l1HeightStart, unsafeL2BlockNumber +} + +// This collect the first N L2 blocks from a specific height. Collected blocks are guaranteed to be safe +// @param ctx,t,system standard parameters to execute a test. +// @param l2Verif OP node to fetch the safe blocks. +// @apram n number of blocks to fetch. +// @param startIndex initial height of the L2 chain to start fetching blocks from. +func collectFirstNL2SafeBlocks(ctx context.Context, t *testing.T, system *e2esys.System, l2Verif *ethclient.Client, n int, startIndex uint64) []string { + var batches []string + + for i := 0; i < n; i++ { + height := startIndex + uint64(i) + _, err := geth.WaitForBlockToBeSafe(big.NewInt(int64(height)), l2Verif, 2*time.Minute) + require.NoError(t, err) + + l2Head, err := l2Verif.BlockByNumber(ctx, new(big.Int).SetUint64(height)) + require.NoError(t, err) + + batch, _, err := derive.BlockToSingularBatch(system.RollupCfg(), l2Head) + require.NoError(t, err) + batchHash := BatchHash(batch) + batches = append(batches, batchHash) + + } + return batches +} + +// Main logic of the test: +// 1. Collect some unsafe batches in list L. +// 2. Do the reorg. +// 3. Collect the batches into list L'. +// 4. Check that L=L'. +func run(ctx context.Context, t *testing.T, system *e2esys.System) { + l2Seq := system.NodeClient(e2esys.RoleSeq) + l2Verif := system.NodeClient(e2esys.RoleVerif) + l1Client := system.NodeClient(e2esys.RoleL1) + + var unsafeL2Height uint64 + var l1Height uint64 + + // Wait for batcher to start advancing L2 head + _, err := geth.WaitForBlockToBeSafe(big.NewInt(2), l2Seq, 2*time.Minute) + require.NoError(t, err, "L2 isn't progressing as expected") + + t.Log("L2 is progressing") + + // Fetch batches before reorg + batchesBefore, L1BlockHeightToReorgTo, startIndex := collectBatchesPublishedOnUnfinalizedL1Blocks(ctx, t, system, l1Client, l2Seq, l2Verif) + + l1Origin, err := l1Client.BlockByNumber(ctx, new(big.Int).SetUint64(L1BlockHeightToReorgTo)) + require.NoError(t, err) + + log.Info("+++ L2 blocks before reorg", "value", batchesBefore) + // Introduce a reorg at L1 + l1Height, err = l1Client.BlockNumber(ctx) + require.NoError(t, err) + t.Logf("Introducing reorg at L1Origin %d, L1Head %d, l2Head %d", l1Origin.Number(), l1Height, unsafeL2Height) + err = system.ForkL1(l1Origin.ParentHash()) + require.NoError(t, err) + + n := len(batchesBefore) + batchesAfter := collectFirstNL2SafeBlocks(ctx, t, system, l2Verif, n, startIndex) + + log.Info("+++ L2 blocks after reorg", "value", batchesAfter) + + assert.Equal(t, batchesAfter, batchesBefore) + +} + +// TestConfirmationIntegrityWithReorgs +// Post batches to both Espresso and the L1 then force the L1 to reorg back to an earlier state in which those batches have not been posted. +// Wait for some time and check that the batches are eventually posted to the L1 again, in the same order as they were originally sequenced, +// as if the reorg did not happen. +// More specifically the test is defined as follows +// +// Arrange: +// Running Sequencer, Batcher in Espresso mode, OP node. +// Act: +// Store the (unfinalized) head of the L1 in variable h. +// Wait for the first n batches to be posted on possibly unfinalized L1 blocks. +// Collect the batches of the corresponding batches and store them in list L. +// Reorg the L1 to height h. +// Wait for the L2 to reach safe height n again as the corresponding batches are submitted again to L1. +// Store these n batches in L' +// Assert: +// L == L' +func TestConfirmationIntegrityWithReorgs(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + launcher := new(env.EspressoDevNodeLauncherDocker) + + system, _, err := launcher.StartE2eDevnet(ctx, t) + require.NoError(t, err, "failed to start dev environment with espresso dev node") + + run(ctx, t, system) +} diff --git a/espresso/environment/6_batch_inbox_test.go b/espresso/environment/6_batch_inbox_test.go new file mode 100644 index 00000000000..bbef4211101 --- /dev/null +++ b/espresso/environment/6_batch_inbox_test.go @@ -0,0 +1,183 @@ +package environment_test + +import ( + "context" + "math/big" + "testing" + "time" + + env "github.com/ethereum-optimism/optimism/espresso/environment" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/geth" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/setuputils" + "github.com/ethereum-optimism/optimism/op-e2e/system/e2esys" + "github.com/ethereum-optimism/optimism/op-service/txmgr" + "github.com/ethereum-optimism/optimism/op-service/txmgr/metrics" + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" + "github.com/stretchr/testify/require" +) + +// TestE2eDevnetWithoutAuthenticatingBatches verifies that batches posted without +// a corresponding BatchInfoAuthenticated event are rejected by the derivation pipeline. +// +// The batcher's BatchAuthenticatorAddress is zeroed out so it skips the +// authenticateBatchInfo call. Batches land on L1 (BatchInbox is an EOA, so txs +// succeed), but the derivation pipeline finds no matching auth event and ignores them. +// +// Arrange: +// +// Start sequencer, batcher in Espresso mode and OP node. +// Zero out the batcher's BatchAuthenticatorAddress so it skips authentication. +// +// Assert: +// +// Assert that the batch transaction lands on L1 (BatchInbox is an EOA). +// Assert that the derivation pipeline doesn't progress (no auth event). +func TestE2eDevnetWithoutAuthenticatingBatches(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + launcher := new(env.EspressoDevNodeLauncherDocker) + + system, _, err := launcher.StartE2eDevnet(ctx, t, + env.WithBatcherStoppedInitially(), + ) + + if have, want := err, error(nil); have != want { + t.Fatalf("failed to start dev environment with espresso dev node:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + + batchDriver := system.BatchSubmitter.TestDriver() + // Set mock batcher authenticator address + batchDriver.RollupConfig.BatchAuthenticatorAddress = common.Address{} + + // Substitute batcher's transaction manager with one that always sends transactions, even + // if they won't succeed. Otherwise batcher wouldn't submit transactions that would revert to + // batch inbox. + // Use the Espresso batcher key (HD index 6) — the same key the primary batcher signs with. + // This ensures the tx comes from an address that is NOT the SystemConfig batcher, so the + // derivation pipeline's fallback authorization won't accept it either. + txMgrCliConfig := setuputils.NewTxMgrConfig(system.NodeEndpoint(e2esys.RoleL1), system.Cfg.Secrets.AccountAtIdx(6)) + txMgrConfig, err := txmgr.NewConfig(txMgrCliConfig, log.Root()) + require.NoError(t, err) + txMgrConfig.Backend = AlwaysSendingETHBackend{ + inner: txMgrConfig.Backend, + } + txMgr, err := txmgr.NewSimpleTxManagerFromConfig("always-sending", log.Root(), &metrics.NoopTxMetrics{}, txMgrConfig) + require.NoError(t, err) + batchDriver.Txmgr = txMgr + + // Start the batcher + err = batchDriver.StartBatchSubmitting() + require.NoError(t, err, "Couldn't start batcher") + l1Client := system.NodeClient(e2esys.RoleL1) + + // Wait for batcher to submit a transaction to BatchInbox + var batchInboxTxHash common.Hash + for { + l1Height, err := l1Client.BlockNumber(ctx) + require.NoError(t, err) + _, err = geth.FindBlock(l1Client, + 0, + int(l1Height), + time.Minute*2, + func(block *types.Block) (bool, error) { + for _, tx := range block.Transactions() { + if *tx.To() == system.RollupConfig.BatchInboxAddress { + batchInboxTxHash = tx.Hash() + return true, nil + } + } + return false, nil + }) + if err == nil { + break + } + } + + receipt, err := l1Client.TransactionReceipt(ctx, batchInboxTxHash) + require.NoError(t, err) + + // BatchInbox is an EOA, so the transaction lands successfully on L1. + // However, the derivation pipeline should reject it because there is no + // BatchInfoAuthenticated event (the batcher skipped authentication). + require.Equal(t, receipt.Status, types.ReceiptStatusSuccessful, "transaction to EOA BatchInbox should succeed") + + _, err = geth.WaitForBlockToBeSafe(new(big.Int).SetUint64(1), system.NodeClient(e2esys.RoleVerif), time.Minute) + require.Error(t, err) +} + +// A wrapper for testing that proxies all calls to ETHBackend unchanged, +// except EstimateGas and CallContract calls, which always "succeed" +// without making any actual RPC calls. +// +// Wrapping SimpleTxManager's backend with it ensures that SimpleTxManager will always send +// transactions, even if they would be reverted. The reason for this behaviour is +// that SimpleTxManager will check whether transaction will be executed successfully +// before submitting it, either by calling CallContract if transaction request had +// set the gas cap, or by checking EstimateGas return value if transaction request +// doesn't have the gas cap set. Mocking these two methods to always succeed thus +// makes SimpleTxManager submit even invalid transactions, which it wouldn't normally do. +type AlwaysSendingETHBackend struct { + inner txmgr.ETHBackend +} + +// BlockNumber implements txmgr.ETHBackend. +func (m AlwaysSendingETHBackend) BlockNumber(ctx context.Context) (uint64, error) { + return m.inner.BlockNumber(ctx) +} + +// CallContract implements txmgr.ETHBackend. +func (m AlwaysSendingETHBackend) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { + return []byte{}, nil +} + +// Close implements txmgr.ETHBackend. +func (m AlwaysSendingETHBackend) Close() { + m.inner.Close() +} + +// EstimateGas implements txmgr.ETHBackend. +func (m AlwaysSendingETHBackend) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64, error) { + return 1_000_000, nil +} + +// HeaderByNumber implements txmgr.ETHBackend. +func (m AlwaysSendingETHBackend) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) { + return m.inner.HeaderByNumber(ctx, number) +} + +// NonceAt implements txmgr.ETHBackend. +func (m AlwaysSendingETHBackend) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) { + return m.inner.NonceAt(ctx, account, blockNumber) +} + +// PendingNonceAt implements txmgr.ETHBackend. +func (m AlwaysSendingETHBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { + return m.inner.PendingNonceAt(ctx, account) +} + +// SendTransaction implements txmgr.ETHBackend. +func (m AlwaysSendingETHBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error { + return m.inner.SendTransaction(ctx, tx) +} + +// SuggestGasTipCap implements txmgr.ETHBackend. +func (m AlwaysSendingETHBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { + return m.inner.SuggestGasTipCap(ctx) +} + +// TransactionReceipt implements txmgr.ETHBackend. +func (m AlwaysSendingETHBackend) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) { + return m.inner.TransactionReceipt(ctx, txHash) +} + +// BlobBaseFee implements txmgr.ETHBackend. +func (m AlwaysSendingETHBackend) BlobBaseFee(ctx context.Context) (*big.Int, error) { + return m.inner.BlobBaseFee(ctx) +} + +// Ensure conformance to ETHBackend +var _ txmgr.ETHBackend = AlwaysSendingETHBackend{} diff --git a/espresso/environment/7_stateless_batcher_test.go b/espresso/environment/7_stateless_batcher_test.go new file mode 100644 index 00000000000..788ecc98da0 --- /dev/null +++ b/espresso/environment/7_stateless_batcher_test.go @@ -0,0 +1,163 @@ +package environment_test + +import ( + "context" + "math/big" + "math/rand/v2" + "testing" + "time" + + env "github.com/ethereum-optimism/optimism/espresso/environment" + "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/system/e2esys" + "github.com/ethereum-optimism/optimism/op-e2e/system/helpers" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestStatelessBatcher is a test that verifies a batcher can operate (especially restart) correctly and efficiently without persistent storage. +// +// This test is designed to evaluate Test 7 as outlined within the +// Espresso Celo Integration plan. It has stated task definition as follows: +// Run the rollup and randomly restart the batcher. Check the liveness of the rollup, and the consistency of Espresso confirmations and L1 confirmations. +// We don't need to clear persistent storage because the original Optimism code isn't and our integration work shouldn't use any. +// More specifically the test is defined as follows +// Arrange: +// Running Sequencer, Batcher in Espresso mode, OP node. +// Act: +// Loop over n iterations +// Randomly pick one iteration to stop the batcher and another to start the batcher +// For all the other iterations send one coin to Alice. +// Assert: +// Query the OP node to check that Alice balance has been increased by n-2 + +func TestStatelessBatcher(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + launcher := new(env.EspressoDevNodeLauncherDocker) + + system, espressoDevNode, err := launcher.StartE2eDevnet(ctx, t) + // Signal the testnet to shut down + if have, want := err, error(nil); have != want { + t.Fatalf("failed to start dev environment with espresso dev node:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + + defer env.Stop(t, system) + defer env.Stop(t, espressoDevNode) + + addressAlice := system.Cfg.Secrets.Addresses().Alice + rollupClient := system.RollupClient(e2esys.RoleVerif) + l2Seq := system.NodeClient(e2esys.RoleSeq) + l2Verif := system.NodeClient(e2esys.RoleVerif) + + // Fund Alice + env.RunSimpleL1TransferAndVerifier(ctx, t, system) + + balanceAliceInitial, err := l2Verif.BalanceAt(ctx, addressAlice, nil) + if have, want := err, error(nil); have != want { + t.Fatalf("Failed to fetch Alice's balance:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + + // Setup Bob for sending coins to Alice + privateKey := system.Cfg.Secrets.Bob + bobOptions, err := bind.NewKeyedTransactorWithChainID(privateKey, system.Cfg.L1ChainIDBig()) + if have, want := err, error(nil); have != want { + t.Fatalf("failed to create transaction options for bob:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + + amount := new(big.Int).SetUint64(1) + numTransfers := 0 + bobOptions.Value = amount + + driver := system.BatchSubmitter.TestDriver() + safeBlockInclusionDuration := time.Duration(6*system.Cfg.DeployConfig.L1BlockTime) * time.Second + + numIterations := 8 + + var txHashes []common.Hash + + // We select a range of iterations when the batcher is turned off. + restartIteration := 1 + rand.IntN(numIterations-1) + for i := 0; i < numIterations; i++ { + + // +1 because of the deposit transaction above + nonce := uint64(numTransfers + 1) + + t.Log("******************* Iteration: ", i) + //Let us stop the batcher + if i == restartIteration { + + t.Log("+++++++++++++++++ Iteration with batcher restart: ", i) + // Stop the batcher + err = driver.StopBatchSubmitting(ctx) + require.NoError(t, err) + + // wait for any old safe blocks being submitted / derived + time.Sleep(safeBlockInclusionDuration) + + // get the initial sync status + seqStatus, err := rollupClient.SyncStatus(context.Background()) + require.NoError(t, err) + + // ensure that the safe chain does not advance while the batcher is stopped + newSeqStatus, err := rollupClient.SyncStatus(ctx) + require.NoError(t, err) + require.Equal(t, newSeqStatus.SafeL2.Number, seqStatus.SafeL2.Number, "Safe chain advanced while batcher was stopped") + + // Send a transaction while the batcher is down. This transaction should still be processed correctly by the sequencer and at some point be + // inserted in a safe L2 block + receipt := helpers.SendL2TxWithID(t, system.Cfg.L2ChainIDBig(), l2Seq, system.Cfg.Secrets.Bob, func(opts *helpers.TxOpts) { + opts.Nonce = nonce + opts.ToAddr = &addressAlice + opts.Value = new(big.Int).SetUint64(1) + }) + + // Store the hash to check later if the transaction has been submitted successfully to the L2 + tx_hash := receipt.TxHash + + // Start again + err = driver.StartBatchSubmitting() + require.NoError(t, err) + time.Sleep(safeBlockInclusionDuration) + t.Log("Batcher restarting....") + + // Ensure that the safe chain does advance while the batcher is stopped + _, err = geth.WaitForBlockToBeSafe(new(big.Int).SetUint64(seqStatus.SafeL2.Number+1), l2Verif, 2*time.Minute) + require.NoError(t, err) + newSeqStatus, err = rollupClient.SyncStatus(ctx) + require.NoError(t, err) + require.Greater(t, newSeqStatus.SafeL2.Number, seqStatus.SafeL2.Number, "Safe chain does not make progress") + + // Ensure the transaction sent while the batcher was down did go through + _, err = wait.ForReceiptOK(ctx, l2Verif, tx_hash) + require.NoError(t, err) + + } else { + // The batcher is up, we can send coins + txHash := env.RunSimpleL2Transfer(ctx, t, system, nonce, *amount, l2Seq) + txHashes = append(txHashes, txHash) + } + + // There should be a transfer for each iteration + numTransfers++ + } + + var numDepositsBigInt big.Int + numDepositsBigInt.SetInt64(int64(numTransfers)) + + expectedAmount := new(big.Int).Mul(new(big.Int).Add(balanceAliceInitial, &numDepositsBigInt), amount) + + // Wait for the transfers to be processed + for _, hash := range txHashes { + _, err := wait.ForReceiptOK(ctx, l2Verif, hash) + require.NoError(t, err) + } + + l2BalanceNew, _ := l2Verif.BalanceAt(ctx, addressAlice, nil) + + assert.Equal(t, expectedAmount, l2BalanceNew) +} diff --git a/espresso/environment/8_reorg_test.go b/espresso/environment/8_reorg_test.go new file mode 100644 index 00000000000..5309ffc0cf6 --- /dev/null +++ b/espresso/environment/8_reorg_test.go @@ -0,0 +1,158 @@ +package environment_test + +import ( + "context" + "math/big" + "testing" + "time" + + env "github.com/ethereum-optimism/optimism/espresso/environment" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/geth" + "github.com/ethereum-optimism/optimism/op-e2e/system/e2esys" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" + "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/require" +) + +// TestBatcherWaitForFinality is a test that attempts to make sure that the batcher waits for the +// derived L1 block to be finalized before submitting a new block. +// +// This tests is designed to evaluate Test 8.1.1 as outlined within the Espresso Celo Integration +// plan. It has stated task definition as follows: +// +// Arrange: +// Run the sequencer and the batcher in Espresso mode. +// Act: +// Wait until a new block is finalized. +// Assert: +// The batcher doesn't submit a block without finalized L1 origin to the L1. +// After the L1 origin is finalized, the batcher submits the block. +func TestBatcherWaitForFinality(t *testing.T) { + // Basic test setup. + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + launcher := new(env.EspressoDevNodeLauncherDocker) + + // Set NonFinalizedProposals to true and SequencerUseFinalized to false, to make sure we are + // testing how the batcher handles the finality. + system, espressoDevNode, err := launcher.StartE2eDevnet(ctx, t, env.WithL1FinalizedDistance(4), env.WithNonFinalizedProposals(true), env.WithSequencerUseFinalized(false)) + if have, want := err, error(nil); have != want { + t.Fatalf("failed to start dev environment with espresso dev node:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + defer env.Stop(t, system) + defer env.Stop(t, espressoDevNode) + + rollupClient := system.RollupClient(e2esys.RoleVerif) + + initialStatus, err := rollupClient.SyncStatus(context.Background()) + require.NoError(t, err) + initialSafeL1Number := initialStatus.SafeL1.Number + + // Wait for new blocks to be finalized, which will enable the batcher to submit more blocks to + // to the L1. + tickerFinality := time.NewTicker(1 * time.Second) + defer tickerFinality.Stop() + + for { + select { + case <-ctx.Done(): + require.FailNow(t, "Timeout: Finalized L1 number not increased by 10") + case <-tickerFinality.C: + // Verify that the batcher waits for the L1 origin to be finalized before submitting a new + // block to the L1. + statusAfterWait, err := rollupClient.SyncStatus(context.Background()) + require.NoError(t, err) + require.LessOrEqual(t, statusAfterWait.SafeL2.L1Origin.Number, statusAfterWait.FinalizedL1.Number, "L1 origin not finalized before submission") + + // Exit the test if there are 10 new safe blocks on the L1. + if statusAfterWait.SafeL1.Number >= initialSafeL1Number+10 { + return + } + } + } +} + +func runL1Reorg(ctx context.Context, t *testing.T, system *e2esys.System) { + l2Seq := system.NodeClient(e2esys.RoleSeq) + l1Client := system.NodeClient(e2esys.RoleL1) + + // Wait for batcher to start advancing L2 head + _, err := geth.WaitForBlockToBeSafe(big.NewInt(2), l2Seq, 2*time.Minute) + if have, want := err, error(nil); have != want { + t.Fatalf("L2 isn't progressing:\nhave:\n\t%v\nwant:\n\t%v", have, want) + } + + t.Log("L2 is progressing") + + // Wait for L2 head to be based off non-genesis unfinalized block + l2HeadL1Info := &derive.L1BlockInfo{} + var l2Head *types.Block + var unsafeL2Height uint64 + var l1Height uint64 + for l2HeadL1Info.Number == 0 || (l1Height-l2HeadL1Info.Number) >= system.Cfg.L1FinalizedDistance { + unsafeL2Height, err = l2Seq.BlockNumber(ctx) + require.NoError(t, err) + + l2Head, err = l2Seq.BlockByNumber(ctx, new(big.Int).SetUint64(unsafeL2Height)) + require.NoError(t, err) + + _, l2HeadL1Info, err = derive.BlockToSingularBatch(system.RollupCfg(), l2Head) + require.NoError(t, err) + + l1Height, err = l1Client.BlockNumber(ctx) + require.NoError(t, err) + } + + l1Origin, err := l1Client.BlockByNumber(ctx, new(big.Int).SetUint64(l2HeadL1Info.Number)) + require.NoError(t, err) + + // Introduce a reorg at L1 + t.Logf("Introducing reorg at L1Origin %d, L1Head %d, l2Head %d", l1Origin.Number(), l1Height, unsafeL2Height) + err = system.ForkL1(l1Origin.ParentHash()) + require.NoError(t, err) + + // Wait for SafeL2 to advance despite the reorg + _, err = geth.WaitForBlockToBeSafe(new(big.Int).SetUint64(unsafeL2Height+1), l2Seq, 2*time.Minute) + require.NoError(t, err) + + // Check that safe chain doesn't contain the forked block + newL2Head, err := l2Seq.BlockByNumber(ctx, new(big.Int).SetUint64(unsafeL2Height)) + require.NoError(t, err) + require.NotEqual(t, newL2Head.Hash(), l2Head.Hash()) +} + +// TestE2eDevnetWithL1Reorg tests how the batcher handles an L1 reorg. +// Specifically, it focuses on cases where unsafe L2 chain contains blocks that +// reference unfinalized L1 blocks as their origin. +// +// This tests is designed to evaluate Test 8.1.2 as outlined within the Espresso Celo +// Integration plan. The test is defined as follows: +// Arrange: +// +// Running Sequencer, Batcher in Espresso mode & OP node. +// +// Act: +// +// Wait for sequencer to propose an unsafe L2 block with unfinalized L1 origin +// Simulate L1 reorg at that block's origin +// +// Assert: +// +// Assert that derivation pipeline still progresses +// Assert that the OP node reports a new block at the target L2 height +func TestE2eDevnetWithL1Reorg(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + launcher := new(env.EspressoDevNodeLauncherDocker) + + system, devNode, err := launcher.StartE2eDevnet(ctx, t, env.WithL1FinalizedDistance(16)) + if have, want := err, error(nil); have != want { + t.Fatalf("failed to start dev environment with espresso dev node:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + + defer env.Stop(t, system) + defer env.Stop(t, devNode) + + runL1Reorg(ctx, t, system) +} diff --git a/espresso/environment/9_pipeline_enhancement_test.go b/espresso/environment/9_pipeline_enhancement_test.go new file mode 100644 index 00000000000..522a6988136 --- /dev/null +++ b/espresso/environment/9_pipeline_enhancement_test.go @@ -0,0 +1,117 @@ +package environment_test + +import ( + "context" + "io" + "log/slog" + "math/big" + "testing" + + env "github.com/ethereum-optimism/optimism/espresso/environment" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" + "github.com/ethereum-optimism/optimism/op-e2e/system/e2esys" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" + "github.com/ethereum-optimism/optimism/op-service/client" + "github.com/ethereum-optimism/optimism/op-service/eth" + "github.com/ethereum-optimism/optimism/op-service/sources" + "github.com/ethereum-optimism/optimism/op-service/sources/mocks" + gethTypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" + "github.com/stretchr/testify/require" +) + +// TestPipelineEnhancement ensures the derivation pipeline does not include batches that lack +// a BatchInfoAuthenticated event from the BatchAuthenticator contract. +// +// When batch authentication is enabled (BatchAuthenticatorAddress is set), the pipeline uses +// event-based authentication: it scans L1 receipts in a lookback window for a +// BatchInfoAuthenticated event matching the batch hash. Transactions without a corresponding +// auth event are filtered out, regardless of sender or receipt status. +// +// Arrange: +// Running Sequencer, Batcher in Espresso mode (with BatchAuthenticator deployed) +// Act: +// Send a transaction from a non-batcher account to the batch inbox contract +// with a specific payload (0x42424242424242424242). +// Fetch N, the block number where the transaction was included. +// Assert: +// Instantiate a data source with block N via the DataSourceFactory. +// Verify that the pipeline returns no data, because the transaction has no +// corresponding BatchInfoAuthenticated event. + +func TestPipelineEnhancement(t *testing.T) { + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + launcher := new(env.EspressoDevNodeLauncherDocker) + + system, espressoDevNode, err := launcher.StartE2eDevnet(ctx, t) + require.NoError(t, err, "failed to start dev environment with espresso dev node") + + // Stop the batcher to ensure no valid batch is posted to L1. + driver := system.BatchSubmitter.TestDriver() + err = driver.StopBatchSubmitting(ctx) + require.NoError(t, err, "failed to stop batch submitter") + + l1Client := system.NodeClient(e2esys.RoleL1) + + defer env.Stop(t, system) + defer env.Stop(t, espressoDevNode) + + // Send a transaction from a non-batcher account to the BatchInbox EOA. + // This tx will not have a BatchInfoAuthenticated event, so the pipeline should reject it. + txData := []byte("42424242424242424242") + + tx := gethTypes.MustSignNewTx(system.Cfg.Secrets.Bob, system.RollupConfig.L1Signer(), &gethTypes.DynamicFeeTx{ + ChainID: system.Cfg.L1ChainIDBig(), + Nonce: 0, + GasTipCap: big.NewInt(1 * params.GWei), + GasFeeCap: big.NewInt(10 * params.GWei), + Gas: 5_000_000, + To: &system.RollupConfig.BatchInboxAddress, + Value: big.NewInt(0), + Data: txData, + }) + + l := log.NewLogger(slog.Default().Handler()) + err = l1Client.SendTransaction(ctx, tx) + require.NoError(t, err) + + // BatchInbox is an EOA, so the tx succeeds on L1. The pipeline rejects it because + // there is no matching BatchInfoAuthenticated event. + receipt, err := wait.ForReceiptMaybe(ctx, l1Client, tx.Hash(), gethTypes.ReceiptStatusSuccessful, true) + require.NoError(t, err, "Waiting for receipt on transaction", tx) + + l1ClientFetching, err := client.NewRPC(ctx, nil, system.NodeEndpoint(e2esys.RoleL1).RPC()) + require.NoError(t, err) + l1RefClient, err := sources.NewL1Client(l1ClientFetching, l, nil, sources.L1ClientDefaultConfig(system.RollupConfig, true, sources.RPCKindStandard)) + require.NoError(t, err) + + // Mock the L1 Beacon client as by default system.RollupConfig.EcotoneTime = 0 + p := mocks.NewBeaconClient(t) + f := mocks.NewBeaconClient(t) + c := sources.NewL1BeaconClient(p, sources.L1BeaconClientConfig{}, f) + + factory := derive.NewDataSourceFactory(l, system.RollupConfig, l1RefClient, c, nil) + + batcherAddress := crypto.PubkeyToAddress(*system.BatchSubmitter.Espresso.BatcherPublicKey) + l1Block, err := l1Client.BlockByNumber(ctx, receipt.BlockNumber) + require.NoError(t, err) + l1BlockRef := eth.L1BlockRef{ + Hash: l1Block.Hash(), + Number: l1Block.NumberU64(), + ParentHash: l1Block.ParentHash(), + Time: l1Block.Time(), + } + datas, err := factory.OpenData(ctx, l1BlockRef, batcherAddress) + require.NoError(t, err) + + data, err := datas.Next(ctx) + + // The pipeline returns no data because the tx has no matching BatchInfoAuthenticated event + require.Equal(t, data, eth.Data(nil)) + require.Equal(t, err, io.EOF) +} diff --git a/espresso/environment/allocs.json b/espresso/environment/allocs.json new file mode 100644 index 00000000000..7ec5ca45509 --- /dev/null +++ b/espresso/environment/allocs.json @@ -0,0 +1,200 @@ +{ + "0x0165878a594ca255338adfa4d48449f69242eb8f": { + "name": "ESPRESSO_SEQUENCER_FEE_CONTRACT_ADDRESS", + "state": { + "balance": "0x0", + "code": "0x6080604052600436106100aa575f3560e01c80638da5cb5b116100635780638da5cb5b1461019c5780638ed83271146101e2578063ad3cb1cc146101f6578063c4d66de814610233578063f2fde38b14610252578063f340fa0114610271576100c8565b80630d8e6e2c146100e157806327e235e3146101115780634f1ef2861461014a57806352d1902d1461015f578063645006ca14610173578063715018a614610188576100c8565b366100c85760405163bc8eca1b60e01b815260040160405180910390fd5b604051631535ac5f60e31b815260040160405180910390fd5b3480156100ec575f5ffd5b5060408051600181525f60208201819052918101919091526060015b60405180910390f35b34801561011c575f5ffd5b5061013c61012b366004610a40565b60026020525f908152604090205481565b604051908152602001610108565b61015d610158366004610a6d565b610284565b005b34801561016a575f5ffd5b5061013c6102a3565b34801561017e575f5ffd5b5061013c60015481565b348015610193575f5ffd5b5061015d6102be565b3480156101a7575f5ffd5b507f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546040516001600160a01b039091168152602001610108565b3480156101ed575f5ffd5b5061013c5f5481565b348015610201575f5ffd5b50610226604051806040016040528060058152602001640352e302e360dc1b81525081565b6040516101089190610b31565b34801561023e575f5ffd5b5061015d61024d366004610a40565b6102df565b34801561025d575f5ffd5b5061015d61026c366004610a40565b61040b565b61015d61027f366004610a40565b61044d565b61028c610526565b610295826105cc565b61029f8282610613565b5050565b5f6102ac6106d4565b505f516020610bb35f395f51905f5290565b6102c661071d565b6040516317d5c96560e11b815260040160405180910390fd5b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff165f811580156103245750825b90505f8267ffffffffffffffff1660011480156103405750303b155b90508115801561034e575080155b1561036c5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561039657845460ff60401b1916600160401b1785555b61039f86610778565b6103a7610789565b670de0b6b3a76400005f5566038d7ea4c68000600155831561040357845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050565b61041361071d565b6001600160a01b03811661044157604051631e4fbdf760e01b81525f60048201526024015b60405180910390fd5b61044a81610791565b50565b60015434101561047057604051636ba4a1c760e01b815260040160405180910390fd5b5f543411156104925760405163c56d46d360e01b815260040160405180910390fd5b6001600160a01b0381166104b957604051630702b3d960e41b815260040160405180910390fd5b6001600160a01b0381165f90815260026020526040812080543492906104e0908490610b66565b90915550506040513481526001600160a01b038216907fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c9060200160405180910390a250565b306001600160a01b037f0000000000000000000000000165878a594ca255338adfa4d48449f69242eb8f1614806105ac57507f0000000000000000000000000165878a594ca255338adfa4d48449f69242eb8f6001600160a01b03166105a05f516020610bb35f395f51905f52546001600160a01b031690565b6001600160a01b031614155b156105ca5760405163703e46dd60e11b815260040160405180910390fd5b565b6105d461071d565b6040516001600160a01b03821681527ff78721226efe9a1bb678189a16d1554928b9f2192e2cb93eeda83b79fa40007d9060200160405180910390a150565b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa92505050801561066d575060408051601f3d908101601f1916820190925261066a91810190610b85565b60015b61069557604051634c9c8ce360e01b81526001600160a01b0383166004820152602401610438565b5f516020610bb35f395f51905f5281146106c557604051632a87526960e21b815260048101829052602401610438565b6106cf8383610801565b505050565b306001600160a01b037f0000000000000000000000000165878a594ca255338adfa4d48449f69242eb8f16146105ca5760405163703e46dd60e11b815260040160405180910390fd5b3361074f7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b0316146105ca5760405163118cdaa760e01b8152336004820152602401610438565b610780610856565b61044a8161089f565b6105ca610856565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b61080a826108a7565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a280511561084e576106cf828261090a565b61029f61097e565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff166105ca57604051631afcd79f60e31b815260040160405180910390fd5b610413610856565b806001600160a01b03163b5f036108dc57604051634c9c8ce360e01b81526001600160a01b0382166004820152602401610438565b5f516020610bb35f395f51905f5280546001600160a01b0319166001600160a01b0392909216919091179055565b60605f5f846001600160a01b0316846040516109269190610b9c565b5f60405180830381855af49150503d805f811461095e576040519150601f19603f3d011682016040523d82523d5f602084013e610963565b606091505b509150915061097385838361099d565b925050505b92915050565b34156105ca5760405163b398979f60e01b815260040160405180910390fd5b6060826109b2576109ad826109fc565b6109f5565b81511580156109c957506001600160a01b0384163b155b156109f257604051639996b31560e01b81526001600160a01b0385166004820152602401610438565b50805b9392505050565b805115610a0c5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b80356001600160a01b0381168114610a3b575f5ffd5b919050565b5f60208284031215610a50575f5ffd5b6109f582610a25565b634e487b7160e01b5f52604160045260245ffd5b5f5f60408385031215610a7e575f5ffd5b610a8783610a25565b9150602083013567ffffffffffffffff811115610aa2575f5ffd5b8301601f81018513610ab2575f5ffd5b803567ffffffffffffffff811115610acc57610acc610a59565b604051601f8201601f19908116603f0116810167ffffffffffffffff81118282101715610afb57610afb610a59565b604052818152828201602001871015610b12575f5ffd5b816020840160208301375f602083830101528093505050509250929050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b8082018082111561097857634e487b7160e01b5f52601160045260245ffd5b5f60208284031215610b95575f5ffd5b5051919050565b5f82518060208501845e5f92019182525091905056fe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca164736f6c634300081c000a", + "nonce": 1, + "storage": { + "0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00": "0x000000000000000000000000000000000000000000000000ffffffffffffffff" + } + } + }, + "0x2279b7a0a67db372996a5fab50d91eaa73d2ebe6": { + "name": "ESPRESSO_SEQUENCER_ESP_TOKEN_ADDRESS", + "state": { + "balance": "0x0", + "code": "0x6080604052600436106100fa575f3560e01c806370a08231116100925780639ab8367e116100625780639ab8367e146102d0578063a9059cbb146102ef578063ad3cb1cc1461030e578063dd62ed3e1461033e578063f2fde38b1461035d575f5ffd5b806370a0823114610222578063715018a6146102625780638da5cb5b1461027657806395d89b41146102bc575f5ffd5b806323b872dd116100cd57806323b872dd146101bf578063313ce567146101de5780634f1ef286146101f957806352d1902d1461020e575f5ffd5b806306fdde03146100fe578063095ea7b3146101285780630d8e6e2c1461015757806318160ddd14610182575b5f5ffd5b348015610109575f5ffd5b5061011261037c565b60405161011f9190610f2c565b60405180910390f35b348015610133575f5ffd5b50610147610142366004610f7c565b61043c565b604051901515815260200161011f565b348015610162575f5ffd5b5060408051600181525f602082018190529181019190915260600161011f565b34801561018d575f5ffd5b507f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace02545b60405190815260200161011f565b3480156101ca575f5ffd5b506101476101d9366004610fa4565b610455565b3480156101e9575f5ffd5b506040516012815260200161011f565b61020c610207366004611069565b61047a565b005b348015610219575f5ffd5b506101b1610499565b34801561022d575f5ffd5b506101b161023c3660046110c7565b6001600160a01b03165f9081525f5160206114515f395f51905f52602052604090205490565b34801561026d575f5ffd5b5061020c6104b4565b348015610281575f5ffd5b507f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546040516001600160a01b03909116815260200161011f565b3480156102c7575f5ffd5b506101126104d5565b3480156102db575f5ffd5b5061020c6102ea3660046110fe565b610513565b3480156102fa575f5ffd5b50610147610309366004610f7c565b610659565b348015610319575f5ffd5b50610112604051806040016040528060058152602001640352e302e360dc1b81525081565b348015610349575f5ffd5b506101b161035836600461118c565b610666565b348015610368575f5ffd5b5061020c6103773660046110c7565b6106af565b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace0380546060915f5160206114515f395f51905f52916103ba906111bd565b80601f01602080910402602001604051908101604052809291908181526020018280546103e6906111bd565b80156104315780601f1061040857610100808354040283529160200191610431565b820191905f5260205f20905b81548152906001019060200180831161041457829003601f168201915b505050505091505090565b5f336104498185856106f1565b60019150505b92915050565b5f33610462858285610703565b61046d858585610766565b60019150505b9392505050565b6104826107c3565b61048b82610869565b6104958282610871565b5050565b5f6104a261092d565b505f5160206114715f395f51905f5290565b6104bc610976565b6040516317d5c96560e11b815260040160405180910390fd5b7f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace0480546060915f5160206114515f395f51905f52916103ba906111bd565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff165f811580156105585750825b90505f8267ffffffffffffffff1660011480156105745750303b155b905081158015610582575080155b156105a05760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156105ca57845460ff60401b1916600160401b1785555b6105d487876109d1565b6105dd8a6109e3565b6105e56109f4565b6105f16012600a6112ec565b6105fb90896112fa565b975061060789896109fc565b831561064d57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050565b5f33610449818585610766565b6001600160a01b039182165f9081527f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace016020908152604080832093909416825291909152205490565b6106b7610976565b6001600160a01b0381166106e557604051631e4fbdf760e01b81525f60048201526024015b60405180910390fd5b6106ee81610a30565b50565b6106fe8383836001610aa0565b505050565b5f61070e8484610666565b90505f198114610760578181101561075257604051637dc7a0d960e11b81526001600160a01b038416600482015260248101829052604481018390526064016106dc565b61076084848484035f610aa0565b50505050565b6001600160a01b03831661078f57604051634b637e8f60e11b81525f60048201526024016106dc565b6001600160a01b0382166107b85760405163ec442f0560e01b81525f60048201526024016106dc565b6106fe838383610b84565b306001600160a01b037f0000000000000000000000002279b7a0a67db372996a5fab50d91eaa73d2ebe616148061084957507f0000000000000000000000002279b7a0a67db372996a5fab50d91eaa73d2ebe66001600160a01b031661083d5f5160206114715f395f51905f52546001600160a01b031690565b6001600160a01b031614155b156108675760405163703e46dd60e11b815260040160405180910390fd5b565b6106ee610976565b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156108cb575060408051601f3d908101601f191682019092526108c891810190611311565b60015b6108f357604051634c9c8ce360e01b81526001600160a01b03831660048201526024016106dc565b5f5160206114715f395f51905f52811461092357604051632a87526960e21b8152600481018290526024016106dc565b6106fe8383610cbd565b306001600160a01b037f0000000000000000000000002279b7a0a67db372996a5fab50d91eaa73d2ebe616146108675760405163703e46dd60e11b815260040160405180910390fd5b336109a87f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b0316146108675760405163118cdaa760e01b81523360048201526024016106dc565b6109d9610d12565b6104958282610d5b565b6109eb610d12565b6106ee81610dab565b610867610d12565b6001600160a01b038216610a255760405163ec442f0560e01b81525f60048201526024016106dc565b6104955f8383610b84565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b5f5160206114515f395f51905f526001600160a01b038516610ad75760405163e602df0560e01b81525f60048201526024016106dc565b6001600160a01b038416610b0057604051634a1406b160e11b81525f60048201526024016106dc565b6001600160a01b038086165f90815260018301602090815260408083209388168352929052208390558115610b7d57836001600160a01b0316856001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92585604051610b7491815260200190565b60405180910390a35b5050505050565b5f5160206114515f395f51905f526001600160a01b038416610bbe5781816002015f828254610bb39190611328565b90915550610c2e9050565b6001600160a01b0384165f9081526020829052604090205482811015610c105760405163391434e360e21b81526001600160a01b038616600482015260248101829052604481018490526064016106dc565b6001600160a01b0385165f9081526020839052604090209083900390555b6001600160a01b038316610c4c576002810180548390039055610c6a565b6001600160a01b0383165f9081526020829052604090208054830190555b826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051610caf91815260200190565b60405180910390a350505050565b610cc682610db3565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a2805115610d0a576106fe8282610e16565b610495610e88565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661086757604051631afcd79f60e31b815260040160405180910390fd5b610d63610d12565b5f5160206114515f395f51905f527f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace03610d9c848261137f565b5060048101610760838261137f565b6106b7610d12565b806001600160a01b03163b5f03610de857604051634c9c8ce360e01b81526001600160a01b03821660048201526024016106dc565b5f5160206114715f395f51905f5280546001600160a01b0319166001600160a01b0392909216919091179055565b60605f5f846001600160a01b031684604051610e32919061143a565b5f60405180830381855af49150503d805f8114610e6a576040519150601f19603f3d011682016040523d82523d5f602084013e610e6f565b606091505b5091509150610e7f858383610ea7565b95945050505050565b34156108675760405163b398979f60e01b815260040160405180910390fd5b606082610ebc57610eb782610f03565b610473565b8151158015610ed357506001600160a01b0384163b155b15610efc57604051639996b31560e01b81526001600160a01b03851660048201526024016106dc565b5080610473565b805115610f135780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b80356001600160a01b0381168114610f77575f5ffd5b919050565b5f5f60408385031215610f8d575f5ffd5b610f9683610f61565b946020939093013593505050565b5f5f5f60608486031215610fb6575f5ffd5b610fbf84610f61565b9250610fcd60208501610f61565b929592945050506040919091013590565b634e487b7160e01b5f52604160045260245ffd5b5f5f67ffffffffffffffff84111561100c5761100c610fde565b50604051601f19601f85018116603f0116810181811067ffffffffffffffff8211171561103b5761103b610fde565b604052838152905080828401851015611052575f5ffd5b838360208301375f60208583010152509392505050565b5f5f6040838503121561107a575f5ffd5b61108383610f61565b9150602083013567ffffffffffffffff81111561109e575f5ffd5b8301601f810185136110ae575f5ffd5b6110bd85823560208401610ff2565b9150509250929050565b5f602082840312156110d7575f5ffd5b61047382610f61565b5f82601f8301126110ef575f5ffd5b61047383833560208501610ff2565b5f5f5f5f5f60a08688031215611112575f5ffd5b61111b86610f61565b945061112960208701610f61565b935060408601359250606086013567ffffffffffffffff81111561114b575f5ffd5b611157888289016110e0565b925050608086013567ffffffffffffffff811115611173575f5ffd5b61117f888289016110e0565b9150509295509295909350565b5f5f6040838503121561119d575f5ffd5b6111a683610f61565b91506111b460208401610f61565b90509250929050565b600181811c908216806111d157607f821691505b6020821081036111ef57634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52601160045260245ffd5b6001815b600184111561124457808504811115611228576112286111f5565b600184161561123657908102905b60019390931c92800261120d565b935093915050565b5f8261125a5750600161044f565b8161126657505f61044f565b816001811461127c5760028114611286576112a2565b600191505061044f565b60ff841115611297576112976111f5565b50506001821b61044f565b5060208310610133831016604e8410600b84101617156112c5575081810a61044f565b6112d15f198484611209565b805f19048211156112e4576112e46111f5565b029392505050565b5f61047360ff84168361124c565b808202811582820484141761044f5761044f6111f5565b5f60208284031215611321575f5ffd5b5051919050565b8082018082111561044f5761044f6111f5565b601f8211156106fe57805f5260205f20601f840160051c810160208510156113605750805b601f840160051c820191505b81811015610b7d575f815560010161136c565b815167ffffffffffffffff81111561139957611399610fde565b6113ad816113a784546111bd565b8461133b565b6020601f8211600181146113df575f83156113c85750848201515b5f19600385901b1c1916600184901b178455610b7d565b5f84815260208120601f198516915b8281101561140e57878501518255602094850194600190920191016113ee565b508482101561142b57868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b5f82518060208501845e5f92019182525091905056fe52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca164736f6c634300081c000a", + "nonce": 1, + "storage": { + "0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00": "0x000000000000000000000000000000000000000000000000ffffffffffffffff" + } + } + }, + "0x4e59b44847b379578588920ca78fbf26c0b4956c": { + "name": null, + "state": { + "balance": "0x0", + "code": "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3", + "nonce": 0, + "storage": {} + } + }, + "0x5fbdb2315678afecb367f032d93f642f64180aa3": { + "name": "ESPRESSO_SEQUENCER_PLONK_VERIFIER_ADDRESS", + "state": { + "balance": "0x0", + "code": "0x735fbdb2315678afecb367f032d93f642f64180aa33014608060405260043610610034575f3560e01c8063ce537a7714610038575b5f5ffd5b61004b610046366004611f0c565b61005f565b604051901515815260200160405180910390f35b5f610069826100d0565b610079835f5b602002015161020b565b61008483600161006f565b61008f83600261006f565b61009a83600361006f565b6100a583600461006f565b6100b083600561006f565b6100bb83600661006f565b6100c684848461023d565b90505b9392505050565b80516100db90610431565b6100e88160200151610431565b6100f58160400151610431565b6101028160600151610431565b61010f8160800151610431565b61011c8160a00151610431565b6101298160c00151610431565b6101368160e00151610431565b610144816101000151610431565b610152816101200151610431565b610160816101400151610431565b61016e816101600151610431565b61017c816101800151610431565b61018a816101a0015161020b565b610198816101c0015161020b565b6101a6816101e0015161020b565b6101b481610200015161020b565b6101c281610220015161020b565b6101d081610240015161020b565b6101de81610260015161020b565b6101ec81610280015161020b565b6101fa816102a0015161020b565b610208816102c0015161020b565b50565b5f5160206121525f395f51905f528110806102395760405163016c173360e21b815260040160405180910390fd5b5050565b5f8360200151600714610263576040516320fa9d8960e11b815260040160405180910390fd5b5f61026f8585856104b0565b90505f61027e865f0151610a10565b90505f610290828460a0015188610dee565b90506102ad60405180604001604052805f81526020015f81525090565b604080518082019091525f80825260208201526102e18761016001516102dc8961018001518860e00151610e4b565b610eae565b91505f5f6102f18b88878c610f15565b91509150610302816102dc8461114d565b925061031b836102dc8b61016001518a60a00151610e4b565b60a08801516040880151602001519194505f5160206121525f395f51905f52918290820990508160e08a01518209905061035e856102dc8d610180015184610e4b565b94505f60405180608001604052807f0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b081526020017f260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c181526020017f22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e5581526020017f04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4815250905061041f87826104128961114d565b61041a6111ea565b6112b7565b9e9d5050505050505050505050505050565b805160208201515f917f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4791159015161561046a57505050565b8251602084015182600384858586098509088382830914838210848410161693505050816104ab5760405163279e345360e21b815260040160405180910390fd5b505050565b6104f06040518061010001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b5f5f5160206121525f395f51905f529050604051602081015f815260fe60e01b8152865160c01b6004820152602087015160c01b600c82015261028087015160208201526102a08701516040820152600160608201527f2f8dd1f1a7583c42c4e12a44e110404c73ca6c94813f85835da4fb7bb1301d4a60808201527f1ee678a0470a75a6eaa8fe837060498ba828a3703b311d0f77f010424afeb02560a08201527f2042a587a90c187b0a087c03e29c968b950b1db26d5c82d666905a6895790c0a60c08201527f2e2b91456103698adf57b799969dea1c8f739da5d8d40dd3eb9222db7c81e88160e082015260e087015180516101008301526020810151610120830152506101008701518051610140830152602081015161016083015250610120870151805161018083015260208101516101a08301525061014087015180516101c083015260208101516101e083015250610160870151805161020083015260208101516102208301525061018087015180516102408301526020810151610260830152506101e0870151805161028083015260208101516102a08301525061020087015180516102c083015260208101516102e083015250610220870151805161030083015260208101516103208301525061024087015180516103408301526020810151610360830152506101a0870151805161038083015260208101516103a0830152506101c087015180516103c083015260208101516103e0830152506102608701518051610400830152602081015161042083015250604087015180516104408301526020810151610460830152506060870151805161048083015260208101516104a083015250608087015180516104c083015260208101516104e08301525060a0870151805161050083015260208101516105208301525060c08701518051610540830152602081015161056083015250855161058082015260208601516105a082015260408601516105c082015260608601516105e0820152608086015161060082015260a086015161062082015260c086015161064082015284518051610660830152602081015161068083015250602085015180516106a083015260208101516106c083015250604085015180516106e083015260208101516107008301525060608501518051610720830152602081015161074083015250608085015180516107608301526020810151610780830152505f82526107c08220825282825106606085015260208220825282825106608085015260a085015180518252602081015160208301525060608220808352838106855283818209848282099150806020870152508060408601525060c085015180518252602081015160208301525060e085015180516040830152602081015160608301525061010085015180516080830152602081015160a083015250610120850151805160c0830152602081015160e0830152506101408501518051610100830152602081015161012083015250610160822082528282510660a08501526101a085015181526101c085015160208201526101e085015160408201526102008501516060820152610220850151608082015261024085015160a082015261026085015160c082015261028085015160e08201526102a08501516101008201526102c0850151610120820152610160822082528282510660c08501526101608501518051825260208101516020830152506101808501518051604083015260208101516060830152505060a0812082810660e08501525050509392505050565b610a18611be9565b816201000003610b57576040518060600160405280601081526020017f30641e0e92bebef818268d663bcad6dbcfd6c0149170f6d7d350b1b1fa6c100181526020016040518060e00160405280600181526020017eeeb2cb5981ed45649abebde081dcff16c8601de4347e7dd1628ba2daac43b781526020017f2d1ba66f5941dc91017171fa69ec2bd0022a2a2d4115a009a93458fd4e26ecfb81526020017f086812a00ac43ea801669c640171203c41a496671bfbc065ac8db24d52cf31e581526020017f2d965651cdd9e4811f4e51b80ddca8a8b4a93ee17420aae6adaa01c2617c6e8581526020017f12597a56c2e438620b9041b98992ae0d4e705b780057bf7766a2767cece16e1d81526020017f02d94117cd17bcf1290fd67c01155dd40807857dff4a5a0b4dc67befa8aa34fd8152508152509050919050565b816210000003610c97576040518060600160405280601481526020017f30644b6c9c4a72169e4daa317d25f04512ae15c53b34e8f5acd8e155d0a6c10181526020016040518060e00160405280600181526020017f26125da10a0ed06327508aba06d1e303ac616632dbed349f53422da95333785781526020017f2260e724844bca5251829353968e4915305258418357473a5c1d597f613f6cbd81526020017f2087ea2cd664278608fb0ebdb820907f598502c81b6690c185e2bf15cb935f4281526020017f19ddbcaf3a8d46c15c0176fbb5b95e4dc57088ff13f4d1bd84c6bfa57dcdc0e081526020017f05a2c85cfc591789605cae818e37dd4161eef9aa666bec6fe4288d09e6d2341881526020017f11f70e5363258ff4f0d716a653e1dc41f1c64484d7f4b6e219d6377614a3905c8152508152509050919050565b81602003610dd5576040518060600160405280600581526020017f2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e75080000181526020016040518060e00160405280600181526020017f09c532c6306b93d29678200d47c0b2a99c18d51b838eeb1d3eed4c533bb512d081526020017f21082ca216cbbf4e1c6e4f4594dd508c996dfbe1174efb98b11509c6e306460b81526020017f1277ae6415f0ef18f2ba5fb162c39eb7311f386e2d26d64401f4a25da77c253b81526020017f2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e8081526020017f2fbd4dd2976be55d1a163aa9820fb88dfac5ddce77e1872e90632027327a5ebe81526020017f107aab49e65a67f9da9cd2abf78be38bd9dc1d5db39f81de36bcfa5b4b0390438152508152509050919050565b60405163e2ef09e560e01b815260040160405180910390fd5b610e0f60405180606001604052805f81526020015f81526020015f81525090565b610e198484611368565b808252610e2990859085906113b9565b60208201528051610e3f90859084908690611428565b60408201529392505050565b604080518082019091525f8082526020820152610e66611c0d565b835181526020808501519082015260408082018490525f908360608460075afa905080610ea65760405163033b714d60e31b815260040160405180910390fd5b505092915050565b604080518082019091525f8082526020820152610ec9611c2b565b835181526020808501518183015283516040808401919091529084015160608301525f908360808460065afa905080610ea65760405163302aedb560e11b815260040160405180910390fd5b604080518082019091525f8082526020820152604080518082019091525f80825260208201525f610f4887878787611576565b90505f5160206121525f395f51905f525f610f64888789611a40565b9050610f7081836120f9565b60c08901516101a088015191925090819084908190830984089250610f9c856102dc8a5f015184610e4b565b955083828209905083846101c08a0151830984089250610fc4866102dc8a6020015184610e4b565b955083828209905083846101e08a0151830984089250610fec866102dc8a6040015184610e4b565b955083828209905083846102008a0151830984089250611014866102dc8a6060015184610e4b565b955083828209905083846102208a015183098408925061103c866102dc8a6080015184610e4b565b955083828209905083846102408a0151830984089250611064866102dc8d6040015184610e4b565b955083828209905083846102608a015183098408925061108c866102dc8d6060015184610e4b565b955083828209905083846102808a01518309840892506110b4866102dc8d6080015184610e4b565b955083828209905083846102a08a01518309840892506110dc866102dc8d60a0015184610e4b565b95505f8a60e00151905084856102c08b0151830985089350611106876102dc8b60a0015184610e4b565b965061113c6111366040805180820182525f80825260209182015281518083019092526001825260029082015290565b85610e4b565b975050505050505094509492505050565b604080518082019091525f8082526020820152815160208301511590151615611174575090565b6040518060400160405280835f015181526020017f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4784602001516111b89190612132565b6111e2907f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476120f9565b905292915050565b61121160405180608001604052805f81526020015f81526020015f81526020015f81525090565b60405180608001604052807f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed81526020017f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c281526020017f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa81526020017f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b815250905090565b5f5f5f6040518751815260208801516020820152602087015160408201528651606082015260608701516080820152604087015160a0820152855160c0820152602086015160e0820152602085015161010082015284516101208201526060850151610140820152604085015161016082015260205f6101808360085afa9150505f5191508061135a5760405163c206334f60e01b815260040160405180910390fd5b50151590505b949350505050565b81515f905f5160206121525f395f51905f52908380156113a9578493505f5b8281101561139d57838586099450600101611387565b506001840393506113b0565b6001830393505b50505092915050565b5f826001036113ca575060016100c9565b815f036113d857505f6100c9565b60208401515f5160206121525f395f51905f52905f908281860990508580156114065760018703925061140d565b6001840392505b5061141782611b2b565b915082828209979650505050505050565b5f5f5160206121525f395f51905f528282036114a15760015f5b60078110156114965781860361147357868160078110611464576114646120e5565b60200201519350505050611360565b82806114815761148161211e565b60408901516020015183099150600101611442565b505f92505050611360565b6114a9611c49565b6040870151600160c0838101828152920190805b60078110156114ea5760208403935085868a85518903088309808552601f199093019291506001016114bd565b505050505f5f5f90506001838960408c01515f5b600781101561153e578882518a85518c88518a0909098981880896505088898d84518c0308860994506020938401939283019291909101906001016114fe565b50505050809250505f61155083611b2b565b905060208a015185818909965050848187099550848287099a9950505050505050505050565b604080518082019091525f80825260208201525f5f5f5f5f5f5160206121525f395f51905f52905060808901518160208a015160208c0151099550895194508160a08b015160608c0151099350816101a089015185089250818184089250818584099450817f2f8dd1f1a7583c42c4e12a44e110404c73ca6c94813f85835da4fb7bb1301d4a85099250816101c089015184089250818184089250818584099450817f1ee678a0470a75a6eaa8fe837060498ba828a3703b311d0f77f010424afeb02585099250816101e089015184089250818184089250818584099450817f2042a587a90c187b0a087c03e29c968b950b1db26d5c82d666905a6895790c0a850992508161020089015184089250818184089250818584099450817f2e2b91456103698adf57b799969dea1c8f739da5d8d40dd3eb9222db7c81e881850992508161022089015184089250818184089250508084830993508084860894506116e38760a0015186610e4b565b9550885160608a015160808b0151838284099750836102c08b015189099750836102408b015183099550836101a08b015187089550838187089550838689099750836102608b015183099550836101c08b015187089550838187089550838689099750836102808b015183099550836101e08b015187089550838187089550838689099750836102a08b015183099550836102008b0151870895508381870895505050508083860994506117aa866102dc8c60c0015188856117a591906120f9565b610e4b565b95506117c3866102dc8c60e001518a6101a00151610e4b565b95506117dd866102dc8c61010001518a6101c00151610e4b565b95506117f7866102dc8c61012001518a6101e00151610e4b565b9550611811866102dc8c61014001518a6102000151610e4b565b9550806101c08801516101a0890151099250611836866102dc8c610160015186610e4b565b9550806102008801516101e089015109925061185b866102dc8c610180015186610e4b565b95506101a0870151925080838409915080828309915080828409925061188a866102dc8c6101e0015186610e4b565b95506101c087015192508083840991508082830991508082840992506118b9866102dc8c610200015186610e4b565b95506101e087015192508083840991508082830991508082840992506118e8866102dc8c610220015186610e4b565b95506102008701519250808384099150808283099150808284099250611917866102dc8c610240015186610e4b565b9550611934866102dc8c6101a001516117a58b6102200151611bbd565b9550611945868b6101c00151610eae565b9550806101c08801516101a0890151099250806101e0880151840992508061020088015184099250806102208801518409925061198b866102dc8c610260015186610e4b565b9550611999885f0151611bbd565b94506119ad866102dc8960c0015188610e4b565b955080600189510860a08a01519093508190800991508082840992508083860994506119e1866102dc8960e0015188610e4b565b95508083860994506119fc866102dc89610100015188610e4b565b9550808386099450611a17866102dc89610120015188610e4b565b9550808386099450611a32866102dc89610140015188610e4b565b9a9950505050505050505050565b5f5f5f5160206121525f395f51905f5290505f836020015190505f846040015190505f60019050606088015160808901516101a08901516102408a01518788898387098a868608088609945050506101c08901516102608a01518788898387098a868608088609945050506101e08901516102808a01518788898387098a868608088609945050506102008901516102a08a01518788898387098a8686080886099450505061022089015191506102c0890151868782898587080985099350505050875160208901518586868309870385089650508485838309860387089998505050505050505050565b5f815f03611b4c5760405163d6dbbb0d60e01b815260040160405180910390fd5b5f5f5f5160206121525f395f51905f52905060405160208152602080820152602060408201528460608201526002820360808201528160a082015260205f60c08360055afa9250505f51925081611bb657604051630c9d3e9960e21b815260040160405180910390fd5b5050919050565b5f5f5160206121525f395f51905f52821560018114611be0578382039250611bb6565b505f9392505050565b60405180606001604052805f81526020015f8152602001611c08611c49565b905290565b60405180606001604052806003906020820280368337509192915050565b60405180608001604052806004906020820280368337509192915050565b6040518060e001604052806007906020820280368337509192915050565b634e487b7160e01b5f52604160045260245ffd5b6040516102e0810167ffffffffffffffff81118282101715611c9f57611c9f611c67565b60405290565b6040516102c0810167ffffffffffffffff81118282101715611c9f57611c9f611c67565b5f60408284031215611cd9575f5ffd5b6040805190810167ffffffffffffffff81118282101715611cfc57611cfc611c67565b604052823581526020928301359281019290925250919050565b5f82601f830112611d25575f5ffd5b60405160e0810167ffffffffffffffff81118282101715611d4857611d48611c67565b6040528060e0840185811115611d5c575f5ffd5b845b81811015611d76578035835260209283019201611d5e565b509195945050505050565b5f6104808284031215611d92575f5ffd5b611d9a611c7b565b9050611da68383611cc9565b8152611db58360408401611cc9565b6020820152611dc78360808401611cc9565b6040820152611dd98360c08401611cc9565b6060820152611dec836101008401611cc9565b6080820152611dff836101408401611cc9565b60a0820152611e12836101808401611cc9565b60c0820152611e25836101c08401611cc9565b60e0820152611e38836102008401611cc9565b610100820152611e4c836102408401611cc9565b610120820152611e60836102808401611cc9565b610140820152611e74836102c08401611cc9565b610160820152611e88836103008401611cc9565b6101808201526103408201356101a08201526103608201356101c08201526103808201356101e08201526103a08201356102008201526103c08201356102208201526103e08201356102408201526104008201356102608201526104208201356102808201526104408201356102a0820152610460909101356102c0820152919050565b5f5f5f838503610a60811215611f20575f5ffd5b610500811215611f2e575f5ffd5b50611f37611ca5565b8435815260208086013590820152611f528660408701611cc9565b6040820152611f648660808701611cc9565b6060820152611f768660c08701611cc9565b6080820152611f89866101008701611cc9565b60a0820152611f9c866101408701611cc9565b60c0820152611faf866101808701611cc9565b60e0820152611fc2866101c08701611cc9565b610100820152611fd6866102008701611cc9565b610120820152611fea866102408701611cc9565b610140820152611ffe866102808701611cc9565b610160820152612012866102c08701611cc9565b610180820152612026866103008701611cc9565b6101a082015261203a866103408701611cc9565b6101c082015261204e866103808701611cc9565b6101e0820152612062866103c08701611cc9565b610200820152612076866104008701611cc9565b61022082015261208a866104408701611cc9565b61024082015261209e866104808701611cc9565b6102608201526104c08501356102808201526104e08501356102a082015292506120cc856105008601611d16565b91506120dc856105e08601611d81565b90509250925092565b634e487b7160e01b5f52603260045260245ffd5b8181038181111561211857634e487b7160e01b5f52601160045260245ffd5b92915050565b634e487b7160e01b5f52601260045260245ffd5b5f8261214c57634e487b7160e01b5f52601260045260245ffd5b50069056fe30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a164736f6c634300081c000a", + "nonce": 1, + "storage": {} + } + }, + "0x610178da211fef7d417bc0e6fed39f05609ad788": { + "name": "ESPRESSO_SEQUENCER_STAKE_TABLE_ADDRESS", + "state": { + "balance": "0x0", + "code": "0x608060405260043610610161575f3560e01c80639b30a5e6116100cd578063b5700e6811610087578063c64814dd11610062578063c64814dd1461047c578063f2fde38b146104b2578063fa52c7d8146104d1578063fc0c546a14610514575f5ffd5b8063b5700e6814610413578063b5ecb34414610432578063be2030941461045d575f5ffd5b80639b30a5e6146102f35780639e9a8f3114610312578063a2d78dd514610327578063a3066aab14610379578063ad3cb1cc14610398578063b3e6ebd5146103d5575f5ffd5b80634f1ef2861161011e5780634f1ef2861461023557806352d1902d146102485780635544c2f11461025c5780636a911ccf1461027b578063715018a61461028f5780638da5cb5b146102a3575f5ffd5b8063026e402b146101655780630d8e6e2c1461018657806313b9057a146101b65780632140fecd146101d55780633e9df9b5146101f45780634d99dd1614610216575b5f5ffd5b348015610170575f5ffd5b5061018461017f366004612346565b610533565b005b348015610191575f5ffd5b5060408051600181525f60208201819052918101919091526060015b60405180910390f35b3480156101c1575f5ffd5b506101846101d036600461246c565b6106d6565b3480156101e0575f5ffd5b506101846101ef3660046124ca565b610869565b3480156101ff575f5ffd5b506102085f5481565b6040519081526020016101ad565b348015610221575f5ffd5b50610184610230366004612346565b61098a565b6101846102433660046124e3565b610b3c565b348015610253575f5ffd5b50610208610b5b565b348015610267575f5ffd5b50610184610276366004612588565b610b76565b348015610286575f5ffd5b50610184610c3f565b34801561029a575f5ffd5b50610184610ccd565b3480156102ae575f5ffd5b507f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b03165b6040516001600160a01b0390911681526020016101ad565b3480156102fe575f5ffd5b5061020861030d3660046125cc565b610cee565b34801561031d575f5ffd5b5061020860085481565b348015610332575f5ffd5b506103646103413660046125e6565b600760209081525f92835260408084209091529082529020805460019091015482565b604080519283526020830191909152016101ad565b348015610384575f5ffd5b506101846103933660046124ca565b610d48565b3480156103a3575f5ffd5b506103c8604051806040016040528060058152602001640352e302e360dc1b81525081565b6040516101ad9190612617565b3480156103e0575f5ffd5b506104036103ef36600461264c565b60046020525f908152604090205460ff1681565b60405190151581526020016101ad565b34801561041e575f5ffd5b506001546102db906001600160a01b031681565b34801561043d575f5ffd5b5061020861044c3660046124ca565b60056020525f908152604090205481565b348015610468575f5ffd5b50610184610477366004612663565b610e58565b348015610487575f5ffd5b506102086104963660046125e6565b600660209081525f928352604080842090915290825290205481565b3480156104bd575f5ffd5b506101846104cc3660046124ca565b610f84565b3480156104dc575f5ffd5b506105066104eb3660046124ca565b60036020525f90815260409020805460019091015460ff1682565b6040516101ad9291906126c1565b34801561051f575f5ffd5b506002546102db906001600160a01b031681565b61053c82610fc1565b335f82900361055e57604051631f2a200560e01b815260040160405180910390fd5b600254604051636eb1769f60e11b81526001600160a01b0383811660048301523060248301525f92169063dd62ed3e90604401602060405180830381865afa1580156105ac573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105d091906126f1565b9050828110156106025760405163054365bb60e31b815260048101829052602481018490526044015b60405180910390fd5b60025461061a906001600160a01b0316833086611042565b6001600160a01b0384165f908152600360205260408120805485929061064190849061271c565b90915550506001600160a01b038085165f9081526006602090815260408083209386168352929052908120805485929061067c90849061271c565b92505081905550836001600160a01b0316826001600160a01b03167fe5541a6b6103d4fa7e021ed54fad39c66f27a76bd13d374cf6240ae6bd0bb72b856040516106c891815260200190565b60405180910390a350505050565b336106e0816110e6565b6106e984611133565b6106f28561116e565b604080516001600160a01b03831660208201525f910160405160208183030381529060405290506107248185886111aa565b6127108361ffff16111561074b5760405163dc81db8560e01b815260040160405180910390fd5b600160045f61075989610cee565b81526020019081526020015f205f6101000a81548160ff02191690831515021790555060405180604001604052805f8152602001600160028111156107a0576107a06126ad565b90526001600160a01b0383165f908152600360209081526040909120825181559082015160018083018054909160ff19909116908360028111156107e6576107e66126ad565b02179055505060408051885181526020808a01518183015289830151828401526060808b0151908301528851608083015288015160a082015261ffff861660c082015290516001600160a01b03851692507ff6e8359c57520b469634736bfc3bb7ec5cbd1a0bd28b10a8275793bb730b797f9181900360e00190a2505050505050565b6001600160a01b0381165f9081526005602052604081205433918190036108a3576040516379298a5360e11b815260040160405180910390fd5b804210156108c457604051635a77435760e01b815260040160405180910390fd5b6001600160a01b038084165f9081526006602090815260408083209386168352929052908120549081900361090c57604051630686827b60e51b815260040160405180910390fd5b6001600160a01b038085165f90815260066020908152604080832087851684529091528120556002546109419116848361123f565b826001600160a01b03167f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b658260405161097c91815260200190565b60405180910390a250505050565b61099382610fc1565b335f8290036109b557604051631f2a200560e01b815260040160405180910390fd5b6001600160a01b038084165f90815260076020908152604080832093851683529290522054156109f85760405163d423a4f160e01b815260040160405180910390fd5b6001600160a01b038084165f9081526006602090815260408083209385168352929052205482811015610a4157604051639266535160e01b8152600481018290526024016105f9565b6001600160a01b038085165f90815260066020908152604080832093861683529290529081208054859290610a7790849061272f565b92505081905550604051806040016040528084815260200160085442610a9d919061271c565b90526001600160a01b038086165f81815260076020908152604080832094881683529381528382208551815594810151600190950194909455908152600390925281208054859290610af090849061272f565b92505081905550836001600160a01b0316826001600160a01b03167f4d10bd049775c77bd7f255195afba5088028ecb3c7c277d393ccff7934f2f92c856040516106c891815260200190565b610b446112ce565b610b4d82611374565b610b57828261137c565b5050565b5f610b6461143d565b505f516020612a1c5f395f51905f5290565b33610b8081610fc1565b610b8983611133565b610b928461116e565b604080516001600160a01b03831660208201525f91016040516020818303038152906040529050610bc48184876111aa565b600160045f610bd288610cee565b81526020019081526020015f205f6101000a81548160ff021916908315150217905550816001600160a01b03167f80d8a4a1663328a998d4555ba21d8bba6ef1576a8c5e9d27f9c545f1a3d52b1d8686604051610c30929190612742565b60405180910390a25050505050565b33610c4981610fc1565b6001600160a01b0381165f908152600360205260409020600101805460ff19166002179055600854610c7b904261271c565b6001600160a01b0382165f8181526005602090815260408083209490945560039052828120819055915190917ffb24305354c87762d557487ae4a564e8d03ecbb9a97dd8afff8e1f6fcaf0dd1691a250565b610cd5611486565b6040516317d5c96560e11b815260040160405180910390fd5b5f815f0151826020015183604001518460600151604051602001610d2b949392919093845260208401929092526040830152606082015260800190565b604051602081830303815290604052805190602001209050919050565b6001600160a01b0381165f9081526007602090815260408083203380855292528220549091819003610d8d57604051630686827b60e51b815260040160405180910390fd5b6001600160a01b038084165f90815260076020908152604080832093861683529290522060010154421015610dd557604051635a77435760e01b815260040160405180910390fd5b6001600160a01b038084165f9081526007602090815260408083208685168452909152812081815560010155600254610e109116838361123f565b816001600160a01b03167f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b6582604051610e4b91815260200190565b60405180910390a2505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff165f81158015610e9d5750825b90505f8267ffffffffffffffff166001148015610eb95750303b155b905081158015610ec7575080155b15610ee55760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610f0f57845460ff60401b1916600160401b1785555b610f18866114e1565b610f206114f2565b610f286114fa565b610f33898989611600565b8315610f7957845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050565b610f8c611486565b6001600160a01b038116610fb557604051631e4fbdf760e01b81525f60048201526024016105f9565b610fbe816116ab565b50565b6001600160a01b0381165f9081526003602052604081206001015460ff1690816002811115610ff257610ff26126ad565b036110105760405163508a793f60e01b815260040160405180910390fd5b6002816002811115611024576110246126ad565b03610b575760405163eab4a96360e01b815260040160405180910390fd5b5f6040516323b872dd60e01b81526001600160a01b03851660048201526001600160a01b038416602482015282604482015260205f6064835f8a5af191505080601f3d1160015f51141615161561109b5750833b153d17155b806110df5760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b60448201526064016105f9565b5050505050565b6001600160a01b0381165f9081526003602052604081206001015460ff166002811115611115576111156126ad565b14610fbe5760405163132e7efb60e31b815260040160405180910390fd5b604080518082019091525f8082526020820152611150828261171b565b15610b57576040516306cf438f60e01b815260040160405180910390fd5b60045f61117a83610cee565b815260208101919091526040015f205460ff1615610fbe5760405162da8a5760e11b815260040160405180910390fd5b6111b38261173e565b5f6040518060600160405280602481526020016129d86024913990505f84826040516020016111e3929190612793565b60405160208183030381529060405290505f6111fe826117a5565b905061121b818561120e88611892565b611216611909565b6119d6565b6112375760405162ced3e560e41b815260040160405180910390fd5b505050505050565b5f60405163a9059cbb60e01b81526001600160a01b038416600482015282602482015260205f6044835f895af191505080601f3d1160015f5114161516156112895750823b153d17155b806112c85760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b60448201526064016105f9565b50505050565b306001600160a01b037f000000000000000000000000610178da211fef7d417bc0e6fed39f05609ad78816148061135457507f000000000000000000000000610178da211fef7d417bc0e6fed39f05609ad7886001600160a01b03166113485f516020612a1c5f395f51905f52546001600160a01b031690565b6001600160a01b031614155b156113725760405163703e46dd60e11b815260040160405180910390fd5b565b610fbe611486565b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156113d6575060408051601f3d908101601f191682019092526113d3918101906126f1565b60015b6113fe57604051634c9c8ce360e01b81526001600160a01b03831660048201526024016105f9565b5f516020612a1c5f395f51905f52811461142e57604051632a87526960e21b8152600481018290526024016105f9565b6114388383611a85565b505050565b306001600160a01b037f000000000000000000000000610178da211fef7d417bc0e6fed39f05609ad78816146113725760405163703e46dd60e11b815260040160405180910390fd5b336114b87f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b0316146113725760405163118cdaa760e01b81523360048201526024016105f9565b6114e9611ada565b610fbe81611b23565b611372611ada565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff165f8115801561153f5750825b90505f8267ffffffffffffffff16600114801561155b5750303b155b905081158015611569575080155b156115875760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156115b157845460ff60401b1916600160401b1785555b435f5583156110df57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15050505050565b6001600160a01b0383166116275760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b03821661164e5760405163d92e233d60e01b815260040160405180910390fd5b600280546001600160a01b038086166001600160a01b03199283161790925560018054928516929091169190911790556202a300808210156116a35760405163b57e21df60e01b815260040160405180910390fd5b506008555050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b805182515f91148015611735575081602001518360200151145b90505b92915050565b805160208201515f915f5160206129fc5f395f51905f5291159015161561176457505050565b8251602084015182600384858586098509088382830914838210848410161693505050816114385760405163279e345360e21b815260040160405180910390fd5b604080518082019091525f80825260208201525f6117c283611b2b565b90505f5160206129fc5f395f51905f5260035f82848509905082806117e9576117e96127af565b848209905082806117fc576117fc6127af565b82820890505f5f61180c83611d34565b925090505b80611875578480611824576118246127af565b6001870895508480611838576118386127af565b8687099250848061184b5761184b6127af565b8684099250848061185e5761185e6127af565b848408925061186c83611d34565b92509050611811565b506040805180820190915294855260208501525091949350505050565b604080518082019091525f80825260208201528151602083015115901516156118b9575090565b6040518060400160405280835f015181526020015f5160206129fc5f395f51905f5284602001516118ea91906127c3565b611901905f5160206129fc5f395f51905f5261272f565b905292915050565b61193060405180608001604052805f81526020015f81526020015f81526020015f81525090565b60405180608001604052807f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed81526020017f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c281526020017f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa81526020017f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b815250905090565b5f5f5f6040518751815260208801516020820152602087015160408201528651606082015260608701516080820152604087015160a0820152855160c0820152602086015160e0820152602085015161010082015284516101208201526060850151610140820152604085015161016082015260205f6101808360085afa9150505f51915080611a795760405163c206334f60e01b815260040160405180910390fd5b50151595945050505050565b611a8e82611dfc565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a2805115611ad2576114388282611e5f565b610b57611ed1565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661137257604051631afcd79f60e31b815260040160405180910390fd5b610f8c611ada565b5f5f611b3683611ef0565b805190915060308114611b4b57611b4b6127e2565b5f8167ffffffffffffffff811115611b6557611b6561236e565b6040519080825280601f01601f191660200182016040528015611b8f576020820181803683370190505b5090505f5b82811015611bfe57836001611ba9838661272f565b611bb3919061272f565b81518110611bc357611bc36127f6565b602001015160f81c60f81b828281518110611be057611be06127f6565b60200101906001600160f81b03191690815f1a905350600101611b94565b5060408051601f80825261040082019092525f9082602082016103e0803683370190505090505f5b82811015611c8e578381611c3a858861272f565b611c44919061271c565b81518110611c5457611c546127f6565b602001015160f81c60f81b60f81c828281518110611c7457611c746127f6565b60ff90921660209283029190910190910152600101611c26565b505f611c998261223c565b90506101005f5160206129fc5f395f51905f525f611cb7868961272f565b90505f5b81811015611d24575f886001611cd1848661272f565b611cdb919061272f565b81518110611ceb57611ceb6127f6565b016020015160f81c90508380611d0357611d036127af565b85870995508380611d1657611d166127af565b818708955050600101611cbb565b50929a9950505050505050505050565b5f5f5f5f5f7f0c19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f5290505f5f5160206129fc5f395f51905f52905060405160208152602080820152602060408201528760608201528260808201528160a082015260205f60c08360055afa9450505f51925083611dc257604051630c9d3e9960e21b815260040160405180910390fd5b80600184901b1115611ddb57611dd8838261272f565b92505b8080611de957611de96127af565b8384099690961496919550909350505050565b806001600160a01b03163b5f03611e3157604051634c9c8ce360e01b81526001600160a01b03821660048201526024016105f9565b5f516020612a1c5f395f51905f5280546001600160a01b0319166001600160a01b0392909216919091179055565b60605f5f846001600160a01b031684604051611e7b919061280a565b5f60405180830381855af49150503d805f8114611eb3576040519150601f19603f3d011682016040523d82523d5f602084013e611eb8565b606091505b5091509150611ec88583836122a3565b95945050505050565b34156113725760405163b398979f60e01b815260040160405180910390fd5b604080516030808252606082810190935290602090600160f91b905f90846020820181803683370190505090508086604051602001611f30929190612793565b6040516020818303038152906040529050808460f81b604051602001611f57929190612815565b604051602081830303815290604052905080604051602001611f79919061283f565b60408051601f1981840301815290829052915061010160f01b90611fa39083908390602001612857565b60408051808303601f190181528282528051602091820120818401819052600160f81b848401526001600160f01b031985166041850152825160238186030181526043909401909252825190830120919350905f60ff881667ffffffffffffffff8111156120135761201361236e565b6040519080825280601f01601f19166020018201604052801561203d576020820181803683370190505b5090505f8260405160200161205491815260200190565b60408051601f1981840301815291905290505f5b81518110156120be57818181518110612083576120836127f6565b602001015160f81c60f81b8382815181106120a0576120a06127f6565b60200101906001600160f81b03191690815f1a905350600101612068565b505f846040516020016120d391815260200190565b60408051601f19818403018152602083019091525f80835291985091505b89811015612165575f83828151811061210c5761210c6127f6565b602001015160f81c60f81b838381518110612129576121296127f6565b602001015160f81c60f81b189050888160405160200161214a92919061287b565b60408051601f198184030181529190529850506001016120f1565b5086888760405160200161217b9392919061289f565b604051602081830303815290604052965086805190602001209350836040516020016121a991815260200190565b60408051601f1981840301815291905291505f5b6121ca8a60ff8d1661272f565b81101561222b578281815181106121e3576121e36127f6565b01602001516001600160f81b031916846121fd838d61271c565b8151811061220d5761220d6127f6565b60200101906001600160f81b03191690815f1a9053506001016121bd565b50919b9a5050505050505050505050565b5f80805b835181101561229c5783818151811061225b5761225b6127f6565b602002602001015160ff1681600861227391906128d2565b61227e9060026129cc565b61228891906128d2565b612292908361271c565b9150600101612240565b5092915050565b6060826122b8576122b382612302565b6122fb565b81511580156122cf57506001600160a01b0384163b155b156122f857604051639996b31560e01b81526001600160a01b03851660048201526024016105f9565b50805b9392505050565b8051156123125780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b80356001600160a01b0381168114612341575f5ffd5b919050565b5f5f60408385031215612357575f5ffd5b6123608361232b565b946020939093013593505050565b634e487b7160e01b5f52604160045260245ffd5b6040805190810167ffffffffffffffff811182821017156123a5576123a561236e565b60405290565b604051601f8201601f1916810167ffffffffffffffff811182821017156123d4576123d461236e565b604052919050565b5f608082840312156123ec575f5ffd5b6040516080810167ffffffffffffffff8111828210171561240f5761240f61236e565b6040908152833582526020808501359083015283810135908201526060928301359281019290925250919050565b5f6040828403121561244d575f5ffd5b612455612382565b823581526020928301359281019290925250919050565b5f5f5f5f6101208587031215612480575f5ffd5b61248a86866123dc565b9350612499866080870161243d565b92506124a88660c0870161243d565b915061010085013561ffff811681146124bf575f5ffd5b939692955090935050565b5f602082840312156124da575f5ffd5b6117358261232b565b5f5f604083850312156124f4575f5ffd5b6124fd8361232b565b9150602083013567ffffffffffffffff811115612518575f5ffd5b8301601f81018513612528575f5ffd5b803567ffffffffffffffff8111156125425761254261236e565b612555601f8201601f19166020016123ab565b818152866020838501011115612569575f5ffd5b816020840160208301375f602083830101528093505050509250929050565b5f5f5f610100848603121561259b575f5ffd5b6125a585856123dc565b92506125b4856080860161243d565b91506125c38560c0860161243d565b90509250925092565b5f608082840312156125dc575f5ffd5b61173583836123dc565b5f5f604083850312156125f7575f5ffd5b6126008361232b565b915061260e6020840161232b565b90509250929050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b5f6020828403121561265c575f5ffd5b5035919050565b5f5f5f5f60808587031215612676575f5ffd5b61267f8561232b565b935061268d6020860161232b565b9250604085013591506126a26060860161232b565b905092959194509250565b634e487b7160e01b5f52602160045260245ffd5b82815260408101600383106126e457634e487b7160e01b5f52602160045260245ffd5b8260208301529392505050565b5f60208284031215612701575f5ffd5b5051919050565b634e487b7160e01b5f52601160045260245ffd5b8082018082111561173857611738612708565b8181038181111561173857611738612708565b825181526020808401518183015260408085015190830152606080850151908301528251608083015282015160a082015260c081016122fb565b5f81518060208401855e5f93019283525090919050565b5f6127a76127a1838661277c565b8461277c565b949350505050565b634e487b7160e01b5f52601260045260245ffd5b5f826127dd57634e487b7160e01b5f52601260045260245ffd5b500690565b634e487b7160e01b5f52600160045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b5f611735828461277c565b5f612820828561277c565b5f81526001600160f81b03199390931660018401525050600201919050565b5f61284a828461277c565b5f81526001019392505050565b5f612862828561277c565b6001600160f01b03199390931683525050600201919050565b5f612886828561277c565b6001600160f81b03199390931683525050600101919050565b5f6128aa828661277c565b6001600160f81b031994909416845250506001600160f01b0319166001820152600301919050565b808202811582820484141761173857611738612708565b6001815b60018411156129245780850481111561290857612908612708565b600184161561291657908102905b60019390931c9280026128ed565b935093915050565b5f8261293a57506001611738565b8161294657505f611738565b816001811461295c576002811461296657612982565b6001915050611738565b60ff84111561297757612977612708565b50506001821b611738565b5060208310610133831016604e8410600b84101617156129a5575081810a611738565b6129b15f1984846128e9565b805f19048211156129c4576129c4612708565b029392505050565b5f611735838361292c56fe424c535f5349475f424e32353447315f584d443a4b454343414b5f4e4354485f4e554c5f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca164736f6c634300081c000a", + "nonce": 1, + "storage": { + "0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00": "0x000000000000000000000000000000000000000000000000ffffffffffffffff" + } + } + }, + "0x6f6c6d0e7a6bb0898333aadaeb4c87368041c9d6": { + "name": null, + "state": { + "balance": "0x8ac6f96f0ea1d2fe", + "code": "0x", + "nonce": 3, + "storage": {} + } + }, + "0x8a791620dd6260079bf849dc5567adc3f2fdc318": { + "name": "ESPRESSO_SEQUENCER_ESP_TOKEN_PROXY_ADDRESS", + "state": { + "balance": "0x0", + "code": "0x6080604052600a600c565b005b60186014601a565b6050565b565b5f604b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b365f5f375f5f365f845af43d5f5f3e8080156069573d5ff35b3d5ffdfea164736f6c634300081c000a", + "nonce": 1, + "storage": { + "0x03b401b7b39ad5148aeb9ef28bef316a982c01bdaadee32abc45fed3bc7f4746": "0x00000000000000000000000000000000000000000b9993a58a7a70d408c00000", + "0x2167eff7539e891c54e98b32937b10a4f6269ef4c2975ae5edbcdc76e7da9ebf": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x25a12f267ec5c0c6bc157bd9f2a5f8853928b268c69df0f4f481a5b93de807bc": "0x00000000000000000000000000000000000000000000002b5e3af16b18800000", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x0000000000000000000000002279b7a0a67db372996a5fab50d91eaa73d2ebe6", + "0x52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace02": "0x00000000000000000000000000000000000000000b999411f60dcc5fc6000000", + "0x52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace03": "0x457370726573736f000000000000000000000000000000000000000000000010", + "0x52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace04": "0x4553500000000000000000000000000000000000000000000000000000000006", + "0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300": "0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "0xa723b6812b36513a13b880a4cb14668a0e53064052b338092d0622774b736bae": "0x000000000000000000000000000000000000000000000030ca024f987b900000", + "0xeb3cf0b3fdb786328dc87f4ae2f6ff264030822d601983cfa90e9c396887ec91": "0x00000000000000000000000000000000000000000000001043561a8829300000", + "0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0xf2d351354d16d58033c2b6b59a768e7acfc5d94d06391b408a001f1980ef2bcb": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + } + }, + "0x9fe46736679d2d9a65f0992f2272de9f3c7fa6e0": { + "name": "ESPRESSO_SEQUENCER_LIGHT_CLIENT_PROXY_ADDRESS", + "state": { + "balance": "0x0", + "code": "0x6080604052600a600c565b005b60186014601a565b6050565b565b5f604b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b365f5f375f5f365f845af43d5f5f3e8080156069573d5ff35b3d5ffdfea164736f6c634300081c000a", + "nonce": 1, + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0e28d2d3671776a08300039b18dd065a1acdb6282aee49e329b4085ac511e1a0", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x02d4b4bf8826b33f87f986dde73bb311d77e297f55b133dad21288833be1b8b4", + "0x0000000000000000000000000000000000000000000000000000000000000003": "0x2dfcb5714318766addfd9e7cbdda0321b7e8bbf57e42fd4fc546d314f312b6db", + "0x0000000000000000000000000000000000000000000000000000000000000004": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000005": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000006": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000007": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000008": "0x0000000000000000000d2f000000000000000000000000000000000000000000", + "0x000000000000000000000000000000000000000000000000000000000000000a": "0x000000000000000000000000000000010000000000000001000000000000012c", + "0x000000000000000000000000000000000000000000000000000000000000000b": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x000000000000000000000000000000000000000000000000000000000000000c": "0x0e28d2d3671776a08300039b18dd065a1acdb6282aee49e329b4085ac511e1a0", + "0x000000000000000000000000000000000000000000000000000000000000000d": "0x02d4b4bf8826b33f87f986dde73bb311d77e297f55b133dad21288833be1b8b4", + "0x000000000000000000000000000000000000000000000000000000000000000e": "0x2dfcb5714318766addfd9e7cbdda0321b7e8bbf57e42fd4fc546d314f312b6db", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000dc64a140aa3e981100a9beca4e685f962f0cf6c9", + "0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300": "0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00": "0x0000000000000000000000000000000000000000000000000000000000000002" + } + } + }, + "0xa513e6e4b8f2a923d98304ec87f64353c4d5c853": { + "name": "ESPRESSO_SEQUENCER_FEE_CONTRACT_PROXY_ADDRESS", + "state": { + "balance": "0x0", + "code": "0x6080604052600a600c565b005b60186014601a565b6050565b565b5f604b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b365f5f375f5f365f845af43d5f5f3e8080156069573d5ff35b3d5ffdfea164736f6c634300081c000a", + "nonce": 1, + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000de0b6b3a7640000", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000000038d7ea4c68000", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x0000000000000000000000000165878a594ca255338adfa4d48449f69242eb8f", + "0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300": "0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00": "0x0000000000000000000000000000000000000000000000000000000000000001" + } + } + }, + "0xb7f8bc63bbcad18155201308c8f3540b07f84f5e": { + "name": "ESPRESSO_SEQUENCER_STAKE_TABLE_PROXY_ADDRESS", + "state": { + "balance": "0x0", + "code": "0x6080604052600a600c565b005b60186014601a565b6050565b565b5f604b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b365f5f375f5f365f845af43d5f5f3e8080156069573d5ff35b3d5ffdfea164736f6c634300081c000a", + "nonce": 1, + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000000000000000000000000000000000000000000c", + "0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000009fe46736679d2d9a65f0992f2272de9f3c7fa6e0", + "0x0000000000000000000000000000000000000000000000000000000000000002": "0x0000000000000000000000008a791620dd6260079bf849dc5567adc3f2fdc318", + "0x0000000000000000000000000000000000000000000000000000000000000008": "0x000000000000000000000000000000000000000000000000000000000002a300", + "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000610178da211fef7d417bc0e6fed39f05609ad788", + "0x65988aaab6fee60b915a7c6b43c7588db33087a016180dd1a794699707697e08": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x7f159dfb2339d762a397026e6cfea24f9ddfa67757f734cbde60a0a04c80d411": "0x00000000000000000000000000000000000000000000000ad78ebc5ac6200000", + "0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300": "0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "0xa4aaa97df7f6bcdc97da4ca9e4116885d4a807ec2b5ad4a9b130b094dc97a171": "0x00000000000000000000000000000000000000000000000ad78ebc5ac6200000", + "0xa4aaa97df7f6bcdc97da4ca9e4116885d4a807ec2b5ad4a9b130b094dc97a172": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0xb0f3cc9fe3f537bf629d5d8b7774df4118bac03cf980517e5bd1c420d6326395": "0x0000000000000000000000000000000000000000000000056bc75e2d63100000", + "0xc8108b74fd3c6b13a7516728f2f1252673903e850abc5c5f03033fce78ec2501": "0x0000000000000000000000000000000000000000000000056bc75e2d63100000", + "0xc8108b74fd3c6b13a7516728f2f1252673903e850abc5c5f03033fce78ec2502": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0xfbbe536cce17c94bdd99c5535667338ecd0323409ac4888e1f8a7e808f3c1d66": "0x0000000000000000000000000000000000000000000000000000000000000001" + } + } + }, + "0xcf7ed3acca5a467e9e704c703e8d87f634fb0fc9": { + "name": "ESPRESSO_SEQUENCER_PLONK_VERIFIER_V2_ADDRESS", + "state": { + "balance": "0x0", + "code": "0x73cf7ed3acca5a467e9e704c703e8d87f634fb0fc9301460806040526004361061009b575f3560e01c8063af196ba21161006e578063af196ba21461014e578063de24ac0f14610175578063e3512d561461019c578063f5144326146101c3578063fc8660c7146101ea575f5ffd5b80630c551f3f1461009f5780634b4734e3146100d95780635a14c0fe14610100578063834c452a14610127575b5f5ffd5b6100c67f1ee678a0470a75a6eaa8fe837060498ba828a3703b311d0f77f010424afeb02581565b6040519081526020015b60405180910390f35b6100c67f22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e5581565b6100c67f2042a587a90c187b0a087c03e29c968b950b1db26d5c82d666905a6895790c0a81565b6100c67f260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c181565b6100c67f0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b081565b6100c67f2e2b91456103698adf57b799969dea1c8f739da5d8d40dd3eb9222db7c81e88181565b6100c67f2f8dd1f1a7583c42c4e12a44e110404c73ca6c94813f85835da4fb7bb1301d4a81565b6100c67f04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe481565b6101fd6101f83660046122e2565b61020d565b60405190151581526020016100d0565b5f610217826102aa565b610227835f5b60200201516103e5565b61023283600161021d565b61023d83600261021d565b61024883600361021d565b61025383600461021d565b61025e83600561021d565b61026983600661021d565b61027483600761021d565b61027f83600861021d565b61028a83600961021d565b61029583600a61021d565b6102a0848484610417565b90505b9392505050565b80516102b59061060b565b6102c2816020015161060b565b6102cf816040015161060b565b6102dc816060015161060b565b6102e9816080015161060b565b6102f68160a0015161060b565b6103038160c0015161060b565b6103108160e0015161060b565b61031e81610100015161060b565b61032c81610120015161060b565b61033a81610140015161060b565b61034881610160015161060b565b61035681610180015161060b565b610364816101a001516103e5565b610372816101c001516103e5565b610380816101e001516103e5565b61038e8161020001516103e5565b61039c8161022001516103e5565b6103aa8161024001516103e5565b6103b88161026001516103e5565b6103c68161028001516103e5565b6103d4816102a001516103e5565b6103e2816102c001516103e5565b50565b5f5160206125285f395f51905f528110806104135760405163016c173360e21b815260040160405180910390fd5b5050565b5f8360200151600b1461043d576040516320fa9d8960e11b815260040160405180910390fd5b5f61044985858561068a565b90505f610458865f0151610c19565b90505f61046a828460a00151886111c0565b905061048760405180604001604052805f81526020015f81525090565b604080518082019091525f80825260208201526104bb8761016001516104b68961018001518860e0015161121d565b611280565b91505f5f6104cb8b88878c6112e7565b915091506104dc816104b68461151f565b92506104f5836104b68b61016001518a60a0015161121d565b60a08801516040880151602001519194505f5160206125285f395f51905f52918290820990508160e08a015182099050610538856104b68d61018001518461121d565b94505f60405180608001604052807f0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b081526020017f260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c181526020017f22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e5581526020017f04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe481525090506105f987826105ec8961151f565b6105f46115bc565b611689565b9e9d5050505050505050505050505050565b805160208201515f917f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4791159015161561064457505050565b8251602084015182600384858586098509088382830914838210848410161693505050816106855760405163279e345360e21b815260040160405180910390fd5b505050565b6106ca6040518061010001604052805f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81526020015f81525090565b5f5f5160206125285f395f51905f529050604051602081015f815260fe60e01b8152865160c01b6004820152602087015160c01b600c82015261028087015160208201526102a08701516040820152600160608201527f2f8dd1f1a7583c42c4e12a44e110404c73ca6c94813f85835da4fb7bb1301d4a60808201527f1ee678a0470a75a6eaa8fe837060498ba828a3703b311d0f77f010424afeb02560a08201527f2042a587a90c187b0a087c03e29c968b950b1db26d5c82d666905a6895790c0a60c08201527f2e2b91456103698adf57b799969dea1c8f739da5d8d40dd3eb9222db7c81e88160e082015260e087015180516101008301526020810151610120830152506101008701518051610140830152602081015161016083015250610120870151805161018083015260208101516101a08301525061014087015180516101c083015260208101516101e083015250610160870151805161020083015260208101516102208301525061018087015180516102408301526020810151610260830152506101e0870151805161028083015260208101516102a08301525061020087015180516102c083015260208101516102e083015250610220870151805161030083015260208101516103208301525061024087015180516103408301526020810151610360830152506101a0870151805161038083015260208101516103a0830152506101c087015180516103c083015260208101516103e0830152506102608701518051610400830152602081015161042083015250604087015180516104408301526020810151610460830152506060870151805161048083015260208101516104a083015250608087015180516104c083015260208101516104e08301525060a0870151805161050083015260208101516105208301525060c08701518051610540830152602081015161056083015250855161058082015260208601516105a082015260408601516105c082015260608601516105e0820152608086015161060082015260a086015161062082015260c086015161064082015260e08601516106608201526101008601516106808201526101208601516106a08201526101408601516106c0820152845180516106e08301526020810151610700830152506020850151805161072083015260208101516107408301525060408501518051610760830152602081015161078083015250606085015180516107a083015260208101516107c083015250608085015180516107e08301526020810151610800830152505f82526108408220825282825106606085015260208220825282825106608085015260a085015180518252602081015160208301525060608220808352838106855283818209848282099150806020870152508060408601525060c085015180518252602081015160208301525060e085015180516040830152602081015160608301525061010085015180516080830152602081015160a083015250610120850151805160c0830152602081015160e0830152506101408501518051610100830152602081015161012083015250610160822082528282510660a08501526101a085015181526101c085015160208201526101e085015160408201526102008501516060820152610220850151608082015261024085015160a082015261026085015160c082015261028085015160e08201526102a08501516101008201526102c0850151610120820152610160822082528282510660c08501526101608501518051825260208101516020830152506101808501518051604083015260208101516060830152505060a0812082810660e08501525050509392505050565b610c21611fbc565b816201000003610df8576040518060600160405280601081526020017f30641e0e92bebef818268d663bcad6dbcfd6c0149170f6d7d350b1b1fa6c10018152602001604051806101600160405280600181526020017eeeb2cb5981ed45649abebde081dcff16c8601de4347e7dd1628ba2daac43b781526020017f2d1ba66f5941dc91017171fa69ec2bd0022a2a2d4115a009a93458fd4e26ecfb81526020017f086812a00ac43ea801669c640171203c41a496671bfbc065ac8db24d52cf31e581526020017f2d965651cdd9e4811f4e51b80ddca8a8b4a93ee17420aae6adaa01c2617c6e8581526020017f12597a56c2e438620b9041b98992ae0d4e705b780057bf7766a2767cece16e1d81526020017f02d94117cd17bcf1290fd67c01155dd40807857dff4a5a0b4dc67befa8aa34fd81526020017f15ee2475bee517c4ee05e51fa1ee7312a8373a0b13db8c51baf04cb2e99bd2bd81526020017e6fab49b869ae62001deac878b2667bd31bf3e28e3a2d764aa49b8d9bbdd31081526020017f2e856bf6d037708ffa4c06d4d8820f45ccadce9c5a6d178cbd573f82e0f9701181526020017f1407eee35993f2b1ad5ec6d9b8950ca3af33135d06037f871c5e33bf566dd7b48152508152509050919050565b816210000003610fd1576040518060600160405280601481526020017f30644b6c9c4a72169e4daa317d25f04512ae15c53b34e8f5acd8e155d0a6c1018152602001604051806101600160405280600181526020017f26125da10a0ed06327508aba06d1e303ac616632dbed349f53422da95333785781526020017f2260e724844bca5251829353968e4915305258418357473a5c1d597f613f6cbd81526020017f2087ea2cd664278608fb0ebdb820907f598502c81b6690c185e2bf15cb935f4281526020017f19ddbcaf3a8d46c15c0176fbb5b95e4dc57088ff13f4d1bd84c6bfa57dcdc0e081526020017f05a2c85cfc591789605cae818e37dd4161eef9aa666bec6fe4288d09e6d2341881526020017f11f70e5363258ff4f0d716a653e1dc41f1c64484d7f4b6e219d6377614a3905c81526020017f29e84143f5870d4776a92df8da8c6c9303d59088f37ba85f40cf6fd14265b4bc81526020017f1bf82deba7d74902c3708cc6e70e61f30512eca95655210e276e5858ce8f58e581526020017f22b94b2e2b0043d04e662d5ec018ea1c8a99a23a62c9eb46f0318f6a194985f081526020017f29969d8d5363bef1101a68e446a14e1da7ba9294e142a146a980fddb4d4d41a58152508152509050919050565b816020036111a7576040518060600160405280600581526020017f2ee12bff4a2813286a8dc388cd754d9a3ef2490635eba50cb9c2e5e7508000018152602001604051806101600160405280600181526020017f09c532c6306b93d29678200d47c0b2a99c18d51b838eeb1d3eed4c533bb512d081526020017f21082ca216cbbf4e1c6e4f4594dd508c996dfbe1174efb98b11509c6e306460b81526020017f1277ae6415f0ef18f2ba5fb162c39eb7311f386e2d26d64401f4a25da77c253b81526020017f2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e8081526020017f2fbd4dd2976be55d1a163aa9820fb88dfac5ddce77e1872e90632027327a5ebe81526020017f107aab49e65a67f9da9cd2abf78be38bd9dc1d5db39f81de36bcfa5b4b03904381526020017ee14b6364a47e9c4284a9f80a5fc41cd212b0d4dbf8a5703770a40a9a34399081526020017f30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f70363681526020017f22399c34139bffada8de046aac50c9628e3517a3a452795364e777cd65bb9f4881526020017f2290ee31c482cf92b79b1944db1c0147635e9004db8c3b9d13644bef31ec3bd38152508152509050919050565b60405163e2ef09e560e01b815260040160405180910390fd5b6111e160405180606001604052805f81526020015f81526020015f81525090565b6111eb848461173a565b8082526111fb908590859061178b565b60208201528051611211908590849086906117fa565b60408201529392505050565b604080518082019091525f8082526020820152611238611fe0565b835181526020808501519082015260408082018490525f908360608460075afa9050806112785760405163033b714d60e31b815260040160405180910390fd5b505092915050565b604080518082019091525f808252602082015261129b611ffe565b835181526020808501518183015283516040808401919091529084015160608301525f908360808460065afa9050806112785760405163302aedb560e11b815260040160405180910390fd5b604080518082019091525f8082526020820152604080518082019091525f80825260208201525f61131a87878787611949565b90505f5160206125285f395f51905f525f611336888789611e13565b905061134281836124cf565b60c08901516101a08801519192509081908490819083098408925061136e856104b68a5f01518461121d565b955083828209905083846101c08a0151830984089250611396866104b68a602001518461121d565b955083828209905083846101e08a01518309840892506113be866104b68a604001518461121d565b955083828209905083846102008a01518309840892506113e6866104b68a606001518461121d565b955083828209905083846102208a015183098408925061140e866104b68a608001518461121d565b955083828209905083846102408a0151830984089250611436866104b68d604001518461121d565b955083828209905083846102608a015183098408925061145e866104b68d606001518461121d565b955083828209905083846102808a0151830984089250611486866104b68d608001518461121d565b955083828209905083846102a08a01518309840892506114ae866104b68d60a001518461121d565b95505f8a60e00151905084856102c08b01518309850893506114d8876104b68b60a001518461121d565b965061150e6115086040805180820182525f80825260209182015281518083019092526001825260029082015290565b8561121d565b975050505050505094509492505050565b604080518082019091525f8082526020820152815160208301511590151615611546575090565b6040518060400160405280835f015181526020017f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47846020015161158a9190612508565b6115b4907f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476124cf565b905292915050565b6115e360405180608001604052805f81526020015f81526020015f81526020015f81525090565b60405180608001604052807f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed81526020017f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c281526020017f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa81526020017f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b815250905090565b5f5f5f6040518751815260208801516020820152602087015160408201528651606082015260608701516080820152604087015160a0820152855160c0820152602086015160e0820152602085015161010082015284516101208201526060850151610140820152604085015161016082015260205f6101808360085afa9150505f5191508061172c5760405163c206334f60e01b815260040160405180910390fd5b50151590505b949350505050565b81515f905f5160206125285f395f51905f529083801561177b578493505f5b8281101561176f57838586099450600101611759565b50600184039350611782565b6001830393505b50505092915050565b5f8260010361179c575060016102a3565b815f036117aa57505f6102a3565b60208401515f5160206125285f395f51905f52905f908281860990508580156117d8576001870392506117df565b6001840392505b506117e982611efe565b915082828209979650505050505050565b5f5f5160206125285f395f51905f528282036118735760015f5b600b81101561186857818603611845578681600b8110611836576118366124bb565b60200201519350505050611732565b8280611853576118536124f4565b60408901516020015183099150600101611814565b505f92505050611732565b61187b61201c565b60408701516001610140838101828152920190805b600b8110156118bd5760208403935085868a85518903088309808552601f19909301929150600101611890565b505050505f5f5f90506001838960408c01515f5b600b811015611911578882518a85518c88518a0909098981880896505088898d84518c0308860994506020938401939283019291909101906001016118d1565b50505050809250505f61192383611efe565b905060208a015185818909965050848187099550848287099a9950505050505050505050565b604080518082019091525f80825260208201525f5f5f5f5f5f5160206125285f395f51905f52905060808901518160208a015160208c0151099550895194508160a08b015160608c0151099350816101a089015185089250818184089250818584099450817f2f8dd1f1a7583c42c4e12a44e110404c73ca6c94813f85835da4fb7bb1301d4a85099250816101c089015184089250818184089250818584099450817f1ee678a0470a75a6eaa8fe837060498ba828a3703b311d0f77f010424afeb02585099250816101e089015184089250818184089250818584099450817f2042a587a90c187b0a087c03e29c968b950b1db26d5c82d666905a6895790c0a850992508161020089015184089250818184089250818584099450817f2e2b91456103698adf57b799969dea1c8f739da5d8d40dd3eb9222db7c81e88185099250816102208901518408925081818408925050808483099350808486089450611ab68760a001518661121d565b9550885160608a015160808b0151838284099750836102c08b015189099750836102408b015183099550836101a08b015187089550838187089550838689099750836102608b015183099550836101c08b015187089550838187089550838689099750836102808b015183099550836101e08b015187089550838187089550838689099750836102a08b015183099550836102008b015187089550838187089550505050808386099450611b7d866104b68c60c001518885611b7891906124cf565b61121d565b9550611b96866104b68c60e001518a6101a0015161121d565b9550611bb0866104b68c61010001518a6101c0015161121d565b9550611bca866104b68c61012001518a6101e0015161121d565b9550611be4866104b68c61014001518a610200015161121d565b9550806101c08801516101a0890151099250611c09866104b68c61016001518661121d565b9550806102008801516101e0890151099250611c2e866104b68c61018001518661121d565b95506101a08701519250808384099150808283099150808284099250611c5d866104b68c6101e001518661121d565b95506101c08701519250808384099150808283099150808284099250611c8c866104b68c61020001518661121d565b95506101e08701519250808384099150808283099150808284099250611cbb866104b68c61022001518661121d565b95506102008701519250808384099150808283099150808284099250611cea866104b68c61024001518661121d565b9550611d07866104b68c6101a00151611b788b6102200151611f90565b9550611d18868b6101c00151611280565b9550806101c08801516101a0890151099250806101e08801518409925080610200880151840992508061022088015184099250611d5e866104b68c61026001518661121d565b9550611d6c885f0151611f90565b9450611d80866104b68960c001518861121d565b955080600189510860a08a0151909350819080099150808284099250808386099450611db4866104b68960e001518861121d565b9550808386099450611dcf866104b68961010001518861121d565b9550808386099450611dea866104b68961012001518861121d565b9550808386099450611e05866104b68961014001518861121d565b9a9950505050505050505050565b5f5f5f5160206125285f395f51905f5290505f836020015190505f846040015190505f60019050606088015160808901516101a08901516102408a01518788898387098a868608088609945050506101c08901516102608a01518788898387098a868608088609945050506101e08901516102808a01518788898387098a868608088609945050506102008901516102a08a01518788898387098a8686080886099450505061022089015191506102c0890151868782898587080985099350505050875160208901518586868309870385089650508485838309860387089998505050505050505050565b5f815f03611f1f5760405163d6dbbb0d60e01b815260040160405180910390fd5b5f5f5f5160206125285f395f51905f52905060405160208152602080820152602060408201528460608201526002820360808201528160a082015260205f60c08360055afa9250505f51925081611f8957604051630c9d3e9960e21b815260040160405180910390fd5b5050919050565b5f5f5160206125285f395f51905f52821560018114611fb3578382039250611f89565b505f9392505050565b60405180606001604052805f81526020015f8152602001611fdb61201c565b905290565b60405180606001604052806003906020820280368337509192915050565b60405180608001604052806004906020820280368337509192915050565b604051806101600160405280600b906020820280368337509192915050565b634e487b7160e01b5f52604160045260245ffd5b6040516102e0810167ffffffffffffffff811182821017156120735761207361203b565b60405290565b6040516102c0810167ffffffffffffffff811182821017156120735761207361203b565b5f604082840312156120ad575f5ffd5b6040805190810167ffffffffffffffff811182821017156120d0576120d061203b565b604052823581526020928301359281019290925250919050565b5f82601f8301126120f9575f5ffd5b604051610160810167ffffffffffffffff8111828210171561211d5761211d61203b565b60405280610160840185811115612132575f5ffd5b845b8181101561214c578035835260209283019201612134565b509195945050505050565b5f6104808284031215612168575f5ffd5b61217061204f565b905061217c838361209d565b815261218b836040840161209d565b602082015261219d836080840161209d565b60408201526121af8360c0840161209d565b60608201526121c283610100840161209d565b60808201526121d583610140840161209d565b60a08201526121e883610180840161209d565b60c08201526121fb836101c0840161209d565b60e082015261220e83610200840161209d565b61010082015261222283610240840161209d565b61012082015261223683610280840161209d565b61014082015261224a836102c0840161209d565b61016082015261225e83610300840161209d565b6101808201526103408201356101a08201526103608201356101c08201526103808201356101e08201526103a08201356102008201526103c08201356102208201526103e08201356102408201526104008201356102608201526104208201356102808201526104408201356102a0820152610460909101356102c0820152919050565b5f5f5f838503610ae08112156122f6575f5ffd5b610500811215612304575f5ffd5b5061230d612079565b8435815260208086013590820152612328866040870161209d565b604082015261233a866080870161209d565b606082015261234c8660c0870161209d565b608082015261235f86610100870161209d565b60a082015261237286610140870161209d565b60c082015261238586610180870161209d565b60e0820152612398866101c0870161209d565b6101008201526123ac86610200870161209d565b6101208201526123c086610240870161209d565b6101408201526123d486610280870161209d565b6101608201526123e8866102c0870161209d565b6101808201526123fc86610300870161209d565b6101a082015261241086610340870161209d565b6101c082015261242486610380870161209d565b6101e0820152612438866103c0870161209d565b61020082015261244c86610400870161209d565b61022082015261246086610440870161209d565b61024082015261247486610480870161209d565b6102608201526104c08501356102808201526104e08501356102a082015292506124a28561050086016120ea565b91506124b2856106608601612157565b90509250925092565b634e487b7160e01b5f52603260045260245ffd5b818103818111156124ee57634e487b7160e01b5f52601160045260245ffd5b92915050565b634e487b7160e01b5f52601260045260245ffd5b5f8261252257634e487b7160e01b5f52601260045260245ffd5b50069056fe30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001a164736f6c634300081c000a", + "nonce": 1, + "storage": {} + } + }, + "0xd208510a88ed64fe278dc04d331901fd8ad99434": { + "name": null, + "state": { + "balance": "0x8ac6ff51571280c7", + "code": "0x", + "nonce": 3, + "storage": {} + } + }, + "0xdc64a140aa3e981100a9beca4e685f962f0cf6c9": { + "name": null, + "state": { + "balance": "0x0", + "code": "0x608060405260043610610254575f3560e01c8063715018a61161013f578063b33bc491116100b3578063d24d933d11610078578063d24d933d14610835578063e030330114610864578063f068205414610883578063f2fde38b146108a2578063f5676160146108c1578063f9e50d19146108e0575f5ffd5b8063b33bc49114610790578063b3daf254146107af578063b5adea3c146107c3578063c23b9e9e146107e2578063c8e5e4981461081a575f5ffd5b80638da5cb5b116101045780638da5cb5b1461066557806390c14390146106a157806396c1ca61146106c05780639baa3cc9146106df5780639fdb54a7146106fe578063ad3cb1cc14610753575f5ffd5b8063715018a6146105c3578063757c37ad146105d757806376671808146105f6578063826e41fc1461060a5780638584d23f14610629575f5ffd5b8063300c89dd116101d6578063426d31941161019b578063426d319414610510578063433dba9f146105315780634f1ef2861461055057806352d1902d14610563578063623a13381461057757806369cc6a04146105af575f5ffd5b8063300c89dd1461043b578063313df7b11461045a578063378ec23b146104915780633c23b6db146104ad5780633ed55b7b146104ea575f5ffd5b8063167ac6181161021c578063167ac618146103645780632063d4f71461038357806325297427146103a25780632d52aad6146103d15780632f79889d146103fd575f5ffd5b8063013fa5fc1461025857806302b592f3146102795780630625e19b146102d65780630d8e6e2c1461031857806312173c2c14610343575b5f5ffd5b348015610263575f5ffd5b50610277610272366004612a09565b6108f4565b005b348015610284575f5ffd5b50610298610293366004612a22565b6109a7565b6040516102cd94939291906001600160401b039485168152928416602084015292166040820152606081019190915260800190565b60405180910390f35b3480156102e1575f5ffd5b50600b54600c54600d54600e546102f89392919084565b6040805194855260208501939093529183015260608201526080016102cd565b348015610323575f5ffd5b5060408051600281525f60208201819052918101919091526060016102cd565b34801561034e575f5ffd5b506103576109f0565b6040516102cd9190612a39565b34801561036f575f5ffd5b5061027761037e366004612c50565b61101f565b34801561038e575f5ffd5b5061027761039d366004612f34565b611096565b3480156103ad575f5ffd5b506103c16103bc366004612c50565b6110af565b60405190151581526020016102cd565b3480156103dc575f5ffd5b506102776103eb366004612a22565b600f805460ff19166001179055601055565b348015610408575f5ffd5b5060085461042390600160c01b90046001600160401b031681565b6040516001600160401b0390911681526020016102cd565b348015610446575f5ffd5b506103c1610455366004612c50565b611111565b348015610465575f5ffd5b50600854610479906001600160a01b031681565b6040516001600160a01b0390911681526020016102cd565b34801561049c575f5ffd5b50435b6040519081526020016102cd565b3480156104b8575f5ffd5b506102776104c7366004612c50565b600a805467ffffffffffffffff19166001600160401b0392909216919091179055565b3480156104f5575f5ffd5b50600a5461042390600160401b90046001600160401b031681565b34801561051b575f5ffd5b505f546001546002546003546102f89392919084565b34801561053c575f5ffd5b5061027761054b366004612f7b565b6111a6565b61027761055e366004612f94565b6111ba565b34801561056e575f5ffd5b5061049f6111d9565b348015610582575f5ffd5b5061027761059136600461307a565b8051600b556020810151600c556040810151600d5560600151600e55565b3480156105ba575f5ffd5b506102776111f4565b3480156105ce575f5ffd5b50610277611262565b3480156105e2575f5ffd5b506102776105f1366004613094565b611283565b348015610601575f5ffd5b506104236115b6565b348015610615575f5ffd5b506008546001600160a01b031615156103c1565b348015610634575f5ffd5b50610648610643366004612a22565b6115e0565b604080519283526001600160401b039091166020830152016102cd565b348015610670575f5ffd5b507f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b0316610479565b3480156106ac575f5ffd5b506104236106bb3660046130d8565b61170b565b3480156106cb575f5ffd5b506102776106da366004612f7b565b61177a565b3480156106ea575f5ffd5b506102776106f9366004613100565b611803565b348015610709575f5ffd5b5060065460075461072d916001600160401b0380821692600160401b909204169083565b604080516001600160401b039485168152939092166020840152908201526060016102cd565b34801561075e575f5ffd5b50610783604051806040016040528060058152602001640352e302e360dc1b81525081565b6040516102cd9190613155565b34801561079b575f5ffd5b506102776107aa3660046130d8565b611925565b3480156107ba575f5ffd5b50610423611a91565b3480156107ce575f5ffd5b506102776107dd36600461318a565b611ab2565b3480156107ed575f5ffd5b5060085461080590600160a01b900463ffffffff1681565b60405163ffffffff90911681526020016102cd565b348015610825575f5ffd5b50610277600f805460ff19169055565b348015610840575f5ffd5b5060045460055461072d916001600160401b0380821692600160401b909204169083565b34801561086f575f5ffd5b506103c161087e3660046131a4565b611af9565b34801561088e575f5ffd5b50600a54610423906001600160401b031681565b3480156108ad575f5ffd5b506102776108bc366004612a09565b611b2c565b3480156108cc575f5ffd5b506102776108db3660046131c4565b611b6b565b3480156108eb575f5ffd5b5060095461049f565b6108fc611c16565b6001600160a01b0381166109235760405163e6c4247b60e01b815260040160405180910390fd5b6008546001600160a01b03908116908216036109525760405163a863aec960e01b815260040160405180910390fd5b600880546001600160a01b0319166001600160a01b0383169081179091556040519081527f8017bb887fdf8fca4314a9d40f6e73b3b81002d67e5cfa85d88173af6aa46072906020015b60405180910390a150565b600981815481106109b6575f80fd5b5f918252602090912060029091020180546001909101546001600160401b038083169350600160401b8304811692600160801b9004169084565b6109f8612728565b620100008152600b60208201527f2faf5a113efd87d75818e63ff9a6170007f22c89bbc4a8bd0f2b48268757b0146040820151527f185aee05f8d3babfce67931f15db39e61f25f794a4134d7bee6e18c5ad1ec0576020604083015101527f0dccf5dcf667a37ca93b8d721091d8f3a8049b3d1e89a56d66e42751bbaf7b5e6060820151527f2cf10949fc5bfcecb3bc54dd4121e55807430f17f30498a7ea6a026070b191626020606083015101527f08d70e4e0184fe53bd566f0d7edc4cd7b0e339490973d0faec7dac2089f538e56080820151527ef665fe1fd110d37d1dea446e8400f06f06b9b58ab3df90fbae7c47ee5860416020608083015101527f087e14d71924ac0f2880adf0f106925e5a6fdd57d020bb3c8aa70fa9fc00ccf360a0820151527f01db7e3178b342f91d54fc972cee72569f429a393988ee43c289e2ed96077152602060a083015101527f196dd42d767201f7f196c42aaef485656046310f5083559592bd1313e16948b760c0820151527f17889680810aaabd1ff3ac4a6c5492100579e059170cd2b77e2b3da6d37cc246602060c083015101527f24935e7a77ac313fd3d60ff3f1a0a79ec32c7dc519b39da0acb2c49f367771cc60e0820151527f168e29425ef138cb6943c75352f33c190e5f1488eb54a9e11deb744da7fb6b2e602060e083015101527f1b58d558b5526453bd1028ca938c940bb89e723f7c35787c02f9f179ae9a0cea610100820151527f21afc121d91d9d1c17dafb9236bc9b872c5b43df064c0b1286012fb43a762324602061010083015101527f1047fc55794d1e597de155077611e3c789a0a2be02183821bba56cf61cc1b8ed610120820151527f174252324727c0d2ee5e50eb57a5231f67474ceed6932ad4ffe9bcf866aa3428602061012083015101527f28db289a4cfb73ba92961572f3185298ae366ed1a44971607bcbf801f120f561610140820151527f045cfe7ae2cd175508172e7d9c2e899bb1d216dfc31fe89fc6c917caaee877a2602061014083015101527f195f2eec8547727fc46ed01b79e8f666ded64ae54f57073874a5a2470380a785610160820151527f1527322e85da1aefbd839e65d11dc695aac16b0db6c62591d9813242d41cbe31602061016083015101527f10c8d7d7355f7e0f8c002f482cc3b98c90baa94261c59a17b424eecfe4e963b2610180820151527f2272e30178647167bbead3a2d7371988f2e198e65815029ded4c64bfc0850f1f602061018083015101527f15d56ea7ab2fa61265f551c2ae25389c8fe7bcb3bf6608082c36a201f225f77d6101a0820151527f0b58546887202e7273d3d0c55d65dd6132cac98ebf04efb1b52445c513c4a4df60206101a083015101527f050d6f43774e8dffaa868f2a7dc82f566c69d175d818d4517cc70ac5fcb2f1b16101c0820151527f2fff87bf605e998373bb64553f3a625dabcd12888692d678a8f44d136440c86360206101c083015101527f12d085608c602cfb5b8c03ec7bd13ac0ff9e64a9ac1e9aa746594a033e464bf26101e0820151527f18ac5a3536042eeb0b0c7c2f43f5e2ca3b2173daa4c2812ffca64787e8e956b260206101e083015101527f0f0f9891fc2b790e74dc253c8854df6392e010f4de6760b8423a3dd69bbe5dce610200820151527f16bed1d244a2fe3ab9a652c7feec5650161d8a75227dece7294f3c8fc542fd6c602061020083015101527f0fa36d00672fa6a1c44cd3c259212c1ada48c66bf7bb085f24471b15b17e6e51610220820151527f182088e56b64955232460891d2b279765325813aef1dae855e5f496c418afc41602061022083015101527f2baf5ae2dd832e1449facc611b6b80fd66d58c871d5827c5c8e2747064e29964610240820151527f29f543b543137e881804c989cd3b99934010002238e8ab3eec882e09d306681f602061024083015101527f2db0ddc7123b42f520e257466a0d92da8b564fe01ec665096c14119643012984610260820151527f1b7ab27a66966284d7fb29bce9d550eafba16c49fbc6267827cdfc8d0b16f94f602061026083015101527fb0838893ec1f237e8b07323b0744599f4e97b598b3b589bcc2bc37b8d5c418016102808201527fc18393c0fa30fe4e8b038e357ad851eae8de9107584effe7c7f1f651b2010e266102a082015290565b611027611c16565b600a80546fffffffffffffffff0000000000000000198116600160401b6001600160401b0385811682029283179485905561106d9491909104811692811691161761170b565b600a60106101000a8154816001600160401b0302191690836001600160401b0316021790555050565b604051634e405c8d60e01b815260040160405180910390fd5b5f6001600160401b03821615806110cf5750600a546001600160401b0316155b156110db57505f919050565b600a546001600160401b03166110f28360056132d0565b6110fc9190613303565b6001600160401b03161592915050565b919050565b5f6001600160401b03821615806111315750600a546001600160401b0316155b1561113d57505f919050565b600a54611153906001600160401b031683613303565b6001600160401b031615806111a05750600a5461117b906005906001600160401b0316613330565b600a546001600160401b0391821691611195911684613303565b6001600160401b0316115b92915050565b6111ae611c16565b6111b78161177a565b50565b6111c2611c71565b6111cb82611d15565b6111d58282611d56565b5050565b5f6111e2611e17565b505f5160206138315f395f51905f5290565b6111fc611c16565b6008546001600160a01b03161561124757600880546001600160a01b03191690556040517f9a5f57de856dd668c54dd95e5c55df93432171cbca49a8776d5620ea59c02450905f90a1565b60405163a863aec960e01b815260040160405180910390fd5b565b61126a611c16565b6040516317d5c96560e11b815260040160405180910390fd5b6008546001600160a01b0316151580156112a857506008546001600160a01b03163314155b156112c6576040516301474c8f60e71b815260040160405180910390fd5b60065483516001600160401b0391821691161115806112ff575060065460208401516001600160401b03600160401b9092048216911611155b1561131d5760405163051c46ef60e01b815260040160405180910390fd5b61132a8360400151611e60565b6113378260200151611e60565b6113448260400151611e60565b6113518260600151611e60565b5f61135a6115b6565b6020850151600a549192505f9161137a91906001600160401b031661170b565b600a549091506001600160401b03600160801b9091048116908216106113c5576113a78560200151611111565b156113c55760405163080ae8d960e01b815260040160405180910390fd5b600a546001600160401b03600160801b909104811690821611156114785760026113ef8383613330565b6001600160401b0316106114165760405163080ae8d960e01b815260040160405180910390fd5b6114218260016132d0565b6001600160401b0316816001600160401b031614801561145a575060065461145890600160401b90046001600160401b03166110af565b155b156114785760405163080ae8d960e01b815260040160405180910390fd5b611483858585611ea1565b84516006805460208801516001600160401b03908116600160401b026001600160801b0319909216938116939093171790556040860151600755600a54600160801b90048116908216108015906114e257506114e285602001516110af565b1561154c578351600b556020840151600c556040840151600d556060840151600e557f31eabd9099fdb25dacddd206abff87311e553441fc9d0fcdef201062d7e7071b6115308260016132d0565b6040516001600160401b03909116815260200160405180910390a15b611557434287612018565b84602001516001600160401b0316855f01516001600160401b03167fa04a773924505a418564363725f56832f5772e6b8d0dbd6efce724dfe803dae687604001516040516115a791815260200190565b60405180910390a35050505050565b600654600a545f916115db916001600160401b03600160401b9092048216911661170b565b905090565b600980545f918291906115f460018361334f565b8154811061160457611604613362565b5f918252602090912060029091020154600160801b90046001600160401b0316841061164357604051631856a49960e21b815260040160405180910390fd5b600854600160c01b90046001600160401b03165b8181101561170457846009828154811061167357611673613362565b5f918252602090912060029091020154600160801b90046001600160401b031611156116fc57600981815481106116ac576116ac613362565b905f5260205f20906002020160010154600982815481106116cf576116cf613362565b905f5260205f2090600202015f0160109054906101000a90046001600160401b0316935093505050915091565b600101611657565b5050915091565b5f816001600160401b03165f0361172357505f6111a0565b826001600160401b03165f0361173b575060016111a0565b6117458284613303565b6001600160401b03165f036117655761175e8284613376565b90506111a0565b61176f8284613376565b61175e9060016132d0565b611782611c16565b610e108163ffffffff1610806117a157506301e133808163ffffffff16115b806117bf575060085463ffffffff600160a01b909104811690821611155b156117dd576040516307a5077760e51b815260040160405180910390fd5b6008805463ffffffff909216600160a01b0263ffffffff60a01b19909216919091179055565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f811580156118475750825b90505f826001600160401b031660011480156118625750303b155b905081158015611870575080155b1561188e5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156118b857845460ff60401b1916600160401b1785555b6118c186612201565b6118c9612212565b6118d489898961221a565b831561191a57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050565b61192d611c16565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805460029190600160401b900460ff1680611976575080546001600160401b03808416911610155b156119945760405163f92ee8a960e01b815260040160405180910390fd5b805468ffffffffffffffffff19166001600160401b0380841691909117600160401b1782556005908516116119dc576040516350dd03f760e11b815260040160405180910390fd5b5f54600b55600154600c55600254600d55600354600e55600a80546001600160401b03858116600160401b026001600160801b031990921690871617179055611a25838561170b565b600a805467ffffffffffffffff60801b1916600160801b6001600160401b0393841602179055815460ff60401b1916825560405190831681527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a150505050565b600a545f906115db906001600160401b03600160401b82048116911661170b565b80516006805460208401516001600160401b03908116600160401b026001600160801b031990921693169290921791909117905560408101516007556111b7434283612018565b600f545f9060ff16611b1457611b0f8383612346565b611b25565b8160105484611b23919061334f565b115b9392505050565b611b34611c16565b6001600160a01b038116611b6257604051631e4fbdf760e01b81525f60048201526024015b60405180910390fd5b6111b78161249e565b611b7660095f61298d565b5f5b81518110156111d5576009828281518110611b9557611b95613362565b6020908102919091018101518254600181810185555f94855293839020825160029092020180549383015160408401516001600160401b03908116600160801b0267ffffffffffffffff60801b19928216600160401b026001600160801b031990971691909416179490941793909316178255606001519082015501611b78565b33611c487f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b0316146112605760405163118cdaa760e01b8152336004820152602401611b59565b306001600160a01b037f000000000000000000000000dc64a140aa3e981100a9beca4e685f962f0cf6c9161480611cf757507f000000000000000000000000dc64a140aa3e981100a9beca4e685f962f0cf6c96001600160a01b0316611ceb5f5160206138315f395f51905f52546001600160a01b031690565b6001600160a01b031614155b156112605760405163703e46dd60e11b815260040160405180910390fd5b611d1d611c16565b6040516001600160a01b03821681527ff78721226efe9a1bb678189a16d1554928b9f2192e2cb93eeda83b79fa40007d9060200161099c565b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015611db0575060408051601f3d908101601f19168201909252611dad918101906133a3565b60015b611dd857604051634c9c8ce360e01b81526001600160a01b0383166004820152602401611b59565b5f5160206138315f395f51905f528114611e0857604051632a87526960e21b815260048101829052602401611b59565b611e12838361250e565b505050565b306001600160a01b037f000000000000000000000000dc64a140aa3e981100a9beca4e685f962f0cf6c916146112605760405163703e46dd60e11b815260040160405180910390fd5b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018110806111d55760405163016c173360e21b815260040160405180910390fd5b5f611eaa6109f0565b9050611eb46129ab565b84516001600160401b0390811682526020808701805183169184019190915260408088015190840152600c546060840152600d546080840152600e5460a0840152600b5460c0840152600a549051600160401b9091048216911610801590611f245750611f2485602001516110af565b15611f5657602084015160e0820152604084015161010082015260608401516101208201528351610140820152611f7a565b600c5460e0820152600d54610100820152600e54610120820152600b546101408201525b60405163fc8660c760e01b815273cf7ed3acca5a467e9e704c703e8d87f634fb0fc99063fc8660c790611fb59085908590889060040161359c565b602060405180830381865af4158015611fd0573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ff491906137bc565b612011576040516309bde33960e01b815260040160405180910390fd5b5050505050565b6009541580159061208d575060085460098054600160a01b830463ffffffff1692600160c01b90046001600160401b031690811061205857612058613362565b5f91825260209091206002909102015461208290600160401b90046001600160401b031684613330565b6001600160401b0316115b1561212057600854600980549091600160c01b90046001600160401b03169081106120ba576120ba613362565b5f9182526020822060029091020180546001600160c01b03191681556001015560088054600160c01b90046001600160401b03169060186120fa836137db565b91906101000a8154816001600160401b0302191690836001600160401b03160217905550505b604080516080810182526001600160401b03948516815292841660208085019182528301518516848301908152929091015160608401908152600980546001810182555f91909152935160029094027f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af81018054935194518716600160801b0267ffffffffffffffff60801b19958816600160401b026001600160801b03199095169690971695909517929092179290921693909317909155517f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7b090910155565b612209612563565b6111b7816125ac565b611260612563565b82516001600160401b031615158061223e575060208301516001600160401b031615155b8061224b57506020820151155b8061225857506040820151155b8061226557506060820151155b8061226f57508151155b806122815750610e108163ffffffff16105b8061229557506301e133808163ffffffff16115b156122b3576040516350dd03f760e11b815260040160405180910390fd5b8251600480546020808701516001600160401b03908116600160401b026001600160801b0319938416919095169081178517909355604096870151600581905586515f5590860151600155958501516002556060909401516003556006805490941617179091556007919091556008805463ffffffff909216600160a01b0263ffffffff60a01b19909216919091179055565b6009545f9043841180612357575080155b806123a15750600854600980549091600160c01b90046001600160401b031690811061238557612385613362565b5f9182526020909120600290910201546001600160401b031684105b156123bf5760405163b0b4387760e01b815260040160405180910390fd5b5f80806123cd60018561334f565b90505b8161246957600854600160c01b90046001600160401b0316811061246957866009828154811061240257612402613362565b5f9182526020909120600290910201546001600160401b03161161245757600191506009818154811061243757612437613362565b5f9182526020909120600290910201546001600160401b03169250612469565b8061246181613805565b9150506123d0565b816124875760405163b0b4387760e01b815260040160405180910390fd5b85612492848961334f565b11979650505050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b612517826125b4565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a280511561255b57611e128282612617565b6111d5612689565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661126057604051631afcd79f60e31b815260040160405180910390fd5b611b34612563565b806001600160a01b03163b5f036125e957604051634c9c8ce360e01b81526001600160a01b0382166004820152602401611b59565b5f5160206138315f395f51905f5280546001600160a01b0319166001600160a01b0392909216919091179055565b60605f5f846001600160a01b031684604051612633919061381a565b5f60405180830381855af49150503d805f811461266b576040519150601f19603f3d011682016040523d82523d5f602084013e612670565b606091505b50915091506126808583836126a8565b95945050505050565b34156112605760405163b398979f60e01b815260040160405180910390fd5b6060826126b857611b0f826126ff565b81511580156126cf57506001600160a01b0384163b155b156126f857604051639996b31560e01b81526001600160a01b0385166004820152602401611b59565b5092915050565b80511561270f5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b604051806102c001604052805f81526020015f815260200161275b60405180604001604052805f81526020015f81525090565b815260200161277b60405180604001604052805f81526020015f81525090565b815260200161279b60405180604001604052805f81526020015f81525090565b81526020016127bb60405180604001604052805f81526020015f81525090565b81526020016127db60405180604001604052805f81526020015f81525090565b81526020016127fb60405180604001604052805f81526020015f81525090565b815260200161281b60405180604001604052805f81526020015f81525090565b815260200161283b60405180604001604052805f81526020015f81525090565b815260200161285b60405180604001604052805f81526020015f81525090565b815260200161287b60405180604001604052805f81526020015f81525090565b815260200161289b60405180604001604052805f81526020015f81525090565b81526020016128bb60405180604001604052805f81526020015f81525090565b81526020016128db60405180604001604052805f81526020015f81525090565b81526020016128fb60405180604001604052805f81526020015f81525090565b815260200161291b60405180604001604052805f81526020015f81525090565b815260200161293b60405180604001604052805f81526020015f81525090565b815260200161295b60405180604001604052805f81526020015f81525090565b815260200161297b60405180604001604052805f81526020015f81525090565b81526020015f81526020015f81525090565b5080545f8255600202905f5260205f20908101906111b791906129ca565b604051806101600160405280600b906020820280368337509192915050565b5b808211156129ef5780546001600160c01b03191681555f60018201556002016129cb565b5090565b80356001600160a01b038116811461110c575f5ffd5b5f60208284031215612a19575f5ffd5b611b25826129f3565b5f60208284031215612a32575f5ffd5b5035919050565b5f6105008201905082518252602083015160208301526040830151612a6b604084018280518252602090810151910152565b50606083015180516080840152602081015160a0840152506080830151805160c0840152602081015160e08401525060a0830151805161010084015260208101516101208401525060c0830151805161014084015260208101516101608401525060e0830151805161018084015260208101516101a08401525061010083015180516101c084015260208101516101e08401525061012083015180516102008401526020810151610220840152506101408301518051610240840152602081015161026084015250610160830151805161028084015260208101516102a08401525061018083015180516102c084015260208101516102e0840152506101a083015180516103008401526020810151610320840152506101c083015180516103408401526020810151610360840152506101e0830151805161038084015260208101516103a08401525061020083015180516103c084015260208101516103e08401525061022083015180516104008401526020810151610420840152506102408301518051610440840152602081015161046084015250610260830151805161048084015260208101516104a0840152506102808301516104c08301526102a0909201516104e09091015290565b80356001600160401b038116811461110c575f5ffd5b5f60208284031215612c60575f5ffd5b611b2582612c3a565b634e487b7160e01b5f52604160045260245ffd5b6040516102e081016001600160401b0381118282101715612ca057612ca0612c69565b60405290565b604051608081016001600160401b0381118282101715612ca057612ca0612c69565b604051601f8201601f191681016001600160401b0381118282101715612cf057612cf0612c69565b604052919050565b5f60608284031215612d08575f5ffd5b604051606081016001600160401b0381118282101715612d2a57612d2a612c69565b604052905080612d3983612c3a565b8152612d4760208401612c3a565b6020820152604092830135920191909152919050565b5f60408284031215612d6d575f5ffd5b604080519081016001600160401b0381118282101715612d8f57612d8f612c69565b604052823581526020928301359281019290925250919050565b5f6104808284031215612dba575f5ffd5b612dc2612c7d565b9050612dce8383612d5d565b8152612ddd8360408401612d5d565b6020820152612def8360808401612d5d565b6040820152612e018360c08401612d5d565b6060820152612e14836101008401612d5d565b6080820152612e27836101408401612d5d565b60a0820152612e3a836101808401612d5d565b60c0820152612e4d836101c08401612d5d565b60e0820152612e60836102008401612d5d565b610100820152612e74836102408401612d5d565b610120820152612e88836102808401612d5d565b610140820152612e9c836102c08401612d5d565b610160820152612eb0836103008401612d5d565b6101808201526103408201356101a08201526103608201356101c08201526103808201356101e08201526103a08201356102008201526103c08201356102208201526103e08201356102408201526104008201356102608201526104208201356102808201526104408201356102a0820152610460909101356102c0820152919050565b5f5f6104e08385031215612f46575f5ffd5b612f508484612cf8565b9150612f5f8460608501612da9565b90509250929050565b803563ffffffff8116811461110c575f5ffd5b5f60208284031215612f8b575f5ffd5b611b2582612f68565b5f5f60408385031215612fa5575f5ffd5b612fae836129f3565b915060208301356001600160401b03811115612fc8575f5ffd5b8301601f81018513612fd8575f5ffd5b80356001600160401b03811115612ff157612ff1612c69565b613004601f8201601f1916602001612cc8565b818152866020838501011115613018575f5ffd5b816020840160208301375f602083830101528093505050509250929050565b5f60808284031215613047575f5ffd5b61304f612ca6565b8235815260208084013590820152604080840135908201526060928301359281019290925250919050565b5f6080828403121561308a575f5ffd5b611b258383613037565b5f5f5f61056084860312156130a7575f5ffd5b6130b18585612cf8565b92506130c08560608601613037565b91506130cf8560e08601612da9565b90509250925092565b5f5f604083850312156130e9575f5ffd5b6130f283612c3a565b9150612f5f60208401612c3a565b5f5f5f5f6101208587031215613114575f5ffd5b61311e8686612cf8565b935061312d8660608701613037565b925061313b60e08601612f68565b915061314a61010086016129f3565b905092959194509250565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b5f6060828403121561319a575f5ffd5b611b258383612cf8565b5f5f604083850312156131b5575f5ffd5b50508035926020909101359150565b5f602082840312156131d4575f5ffd5b81356001600160401b038111156131e9575f5ffd5b8201601f810184136131f9575f5ffd5b80356001600160401b0381111561321257613212612c69565b61322160208260051b01612cc8565b8082825260208201915060208360071b850101925086831115613242575f5ffd5b6020840193505b828410156132b25760808488031215613260575f5ffd5b613268612ca6565b61327185612c3a565b815261327f60208601612c3a565b602082015261329060408601612c3a565b6040820152606085810135908201528252608090930192602090910190613249565b9695505050505050565b634e487b7160e01b5f52601160045260245ffd5b6001600160401b0381811683821601908111156111a0576111a06132bc565b634e487b7160e01b5f52601260045260245ffd5b5f6001600160401b0383168061331b5761331b6132ef565b806001600160401b0384160691505092915050565b6001600160401b0382811682821603908111156111a0576111a06132bc565b818103818111156111a0576111a06132bc565b634e487b7160e01b5f52603260045260245ffd5b5f6001600160401b0383168061338e5761338e6132ef565b806001600160401b0384160491505092915050565b5f602082840312156133b3575f5ffd5b5051919050565b805f5b600b8110156133dc5781518452602093840193909101906001016133bd565b50505050565b6133f782825180518252602090810151910152565b6020818101518051604085015290810151606084015250604081015180516080840152602081015160a0840152506060810151805160c0840152602081015160e0840152506080810151805161010084015260208101516101208401525060a0810151805161014084015260208101516101608401525060c0810151805161018084015260208101516101a08401525060e081015180516101c084015260208101516101e08401525061010081015180516102008401526020810151610220840152506101208101518051610240840152602081015161026084015250610140810151805161028084015260208101516102a08401525061016081015180516102c084015260208101516102e08401525061018081015180516103008401526020810151610320840152506101a08101516103408301526101c08101516103608301526101e08101516103808301526102008101516103a08301526102208101516103c08301526102408101516103e08301526102608101516104008301526102808101516104208301526102a08101516104408301526102c0015161046090910152565b5f610ae082019050845182526020850151602083015260408501516135ce604084018280518252602090810151910152565b50606085015180516080840152602081015160a0840152506080850151805160c0840152602081015160e08401525060a0850151805161010084015260208101516101208401525060c0850151805161014084015260208101516101608401525060e0850151805161018084015260208101516101a08401525061010085015180516101c084015260208101516101e08401525061012085015180516102008401526020810151610220840152506101408501518051610240840152602081015161026084015250610160850151805161028084015260208101516102a08401525061018085015180516102c084015260208101516102e0840152506101a085015180516103008401526020810151610320840152506101c085015180516103408401526020810151610360840152506101e0850151805161038084015260208101516103a08401525061020085015180516103c084015260208101516103e08401525061022085015180516104008401526020810151610420840152506102408501518051610440840152602081015161046084015250610260850151805161048084015260208101516104a0840152506102808501516104c08301526102a08501516104e08301526137a66105008301856133ba565b6137b46106608301846133e2565b949350505050565b5f602082840312156137cc575f5ffd5b81518015158114611b25575f5ffd5b5f6001600160401b0382166001600160401b0381036137fc576137fc6132bc565b60010192915050565b5f81613813576138136132bc565b505f190190565b5f82518060208501845e5f92019182525091905056fe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca164736f6c634300081c000a", + "nonce": 1, + "storage": { + "0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00": "0x000000000000000000000000000000000000000000000000ffffffffffffffff" + } + } + }, + "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512": { + "name": null, + "state": { + "balance": "0x0", + "code": "0x6080604052600436106101ba575f3560e01c8063826e41fc116100f2578063b5adea3c11610092578063e030330111610062578063e030330114610640578063f2fde38b1461065f578063f56761601461067e578063f9e50d191461069d575f5ffd5b8063b5adea3c14610567578063c23b9e9e146105be578063c8e5e498146105f6578063d24d933d14610611575f5ffd5b806396c1ca61116100cd57806396c1ca61146104975780639baa3cc9146104b65780639fdb54a7146104d5578063ad3cb1cc1461052a575f5ffd5b8063826e41fc146103f45780638584d23f1461041f5780638da5cb5b1461045b575f5ffd5b8063313df7b11161015d5780634f1ef286116101385780634f1ef286146103a557806352d1902d146103b857806369cc6a04146103cc578063715018a6146103e0575f5ffd5b8063313df7b114610311578063378ec23b14610348578063426d319414610364575f5ffd5b806312173c2c1161019857806312173c2c146102675780632063d4f7146102885780632d52aad6146102a75780632f79889d146102d3575f5ffd5b8063013fa5fc146101be57806302b592f3146101df5780630d8e6e2c1461023c575b5f5ffd5b3480156101c9575f5ffd5b506101dd6101d8366004612189565b6106b1565b005b3480156101ea575f5ffd5b506101fe6101f93660046121a2565b610764565b60405161023394939291906001600160401b039485168152928416602084015292166040820152606081019190915260800190565b60405180910390f35b348015610247575f5ffd5b5060408051600181525f6020820181905291810191909152606001610233565b348015610272575f5ffd5b5061027b6107ad565b60405161023391906121b9565b348015610293575f5ffd5b506101dd6102a2366004612510565b6107c2565b3480156102b2575f5ffd5b506101dd6102c13660046121a2565b600a805460ff19166001179055600b55565b3480156102de575f5ffd5b506008546102f990600160c01b90046001600160401b031681565b6040516001600160401b039091168152602001610233565b34801561031c575f5ffd5b50600854610330906001600160a01b031681565b6040516001600160a01b039091168152602001610233565b348015610353575f5ffd5b50435b604051908152602001610233565b34801561036f575f5ffd5b505f546001546002546003546103859392919084565b604080519485526020850193909352918301526060820152608001610233565b6101dd6103b33660046126c0565b61091c565b3480156103c3575f5ffd5b5061035661093b565b3480156103d7575f5ffd5b506101dd610956565b3480156103eb575f5ffd5b506101dd6109c4565b3480156103ff575f5ffd5b506008546001600160a01b031615155b6040519015158152602001610233565b34801561042a575f5ffd5b5061043e6104393660046121a2565b6109e5565b604080519283526001600160401b03909116602083015201610233565b348015610466575f5ffd5b507f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b0316610330565b3480156104a2575f5ffd5b506101dd6104b1366004612776565b610b10565b3480156104c1575f5ffd5b506101dd6104d036600461278f565b610b99565b3480156104e0575f5ffd5b50600654600754610504916001600160401b0380821692600160401b909204169083565b604080516001600160401b03948516815293909216602084015290820152606001610233565b348015610535575f5ffd5b5061055a604051806040016040528060058152602001640352e302e360dc1b81525081565b6040516102339190612817565b348015610572575f5ffd5b506101dd61058136600461284c565b80516006805460208401516001600160401b03908116600160401b026001600160801b031990921693169290921791909117905560400151600755565b3480156105c9575f5ffd5b506008546105e190600160a01b900463ffffffff1681565b60405163ffffffff9091168152602001610233565b348015610601575f5ffd5b506101dd600a805460ff19169055565b34801561061c575f5ffd5b50600454600554610504916001600160401b0380821692600160401b909204169083565b34801561064b575f5ffd5b5061040f61065a366004612866565b610cbb565b34801561066a575f5ffd5b506101dd610679366004612189565b610cf0565b348015610689575f5ffd5b506101dd610698366004612886565b610d32565b3480156106a8575f5ffd5b50600954610356565b6106b9610ddd565b6001600160a01b0381166106e05760405163e6c4247b60e01b815260040160405180910390fd5b6008546001600160a01b039081169082160361070f5760405163a863aec960e01b815260040160405180910390fd5b600880546001600160a01b0319166001600160a01b0383169081179091556040519081527f8017bb887fdf8fca4314a9d40f6e73b3b81002d67e5cfa85d88173af6aa46072906020015b60405180910390a150565b60098181548110610773575f80fd5b5f918252602090912060029091020180546001909101546001600160401b038083169350600160401b8304811692600160801b9004169084565b6107b5611ea4565b6107bd610e38565b905090565b6008546001600160a01b0316151580156107e757506008546001600160a01b03163314155b15610805576040516301474c8f60e71b815260040160405180910390fd5b60065482516001600160401b03918216911611158061083e575060065460208301516001600160401b03600160401b9092048216911611155b1561085c5760405163051c46ef60e01b815260040160405180910390fd5b6108698260400151611468565b61087382826114a9565b81516006805460208501516001600160401b03908116600160401b026001600160801b031990921693169290921791909117905560408201516007556108c06108b94390565b428461159d565b81602001516001600160401b0316825f01516001600160401b03167fa04a773924505a418564363725f56832f5772e6b8d0dbd6efce724dfe803dae6846040015160405161091091815260200190565b60405180910390a35050565b610924611786565b61092d8261182a565b610937828261186b565b5050565b5f61094461192c565b505f516020612e605f395f51905f5290565b61095e610ddd565b6008546001600160a01b0316156109a957600880546001600160a01b03191690556040517f9a5f57de856dd668c54dd95e5c55df93432171cbca49a8776d5620ea59c02450905f90a1565b60405163a863aec960e01b815260040160405180910390fd5b565b6109cc610ddd565b6040516317d5c96560e11b815260040160405180910390fd5b600980545f918291906109f9600183612992565b81548110610a0957610a096129a5565b5f918252602090912060029091020154600160801b90046001600160401b03168410610a4857604051631856a49960e21b815260040160405180910390fd5b600854600160c01b90046001600160401b03165b81811015610b09578460098281548110610a7857610a786129a5565b5f918252602090912060029091020154600160801b90046001600160401b03161115610b015760098181548110610ab157610ab16129a5565b905f5260205f2090600202016001015460098281548110610ad457610ad46129a5565b905f5260205f2090600202015f0160109054906101000a90046001600160401b0316935093505050915091565b600101610a5c565b5050915091565b610b18610ddd565b610e108163ffffffff161080610b3757506301e133808163ffffffff16115b80610b55575060085463ffffffff600160a01b909104811690821611155b15610b73576040516307a5077760e51b815260040160405180910390fd5b6008805463ffffffff909216600160a01b0263ffffffff60a01b19909216919091179055565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f81158015610bdd5750825b90505f826001600160401b03166001148015610bf85750303b155b905081158015610c06575080155b15610c245760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610c4e57845460ff60401b1916600160401b1785555b610c5786611975565b610c5f611986565b610c6a89898961198e565b8315610cb057845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050565b600a545f9060ff16610cd657610cd18383611aba565b610ce7565b81600b5484610ce59190612992565b115b90505b92915050565b610cf8610ddd565b6001600160a01b038116610d2657604051631e4fbdf760e01b81525f60048201526024015b60405180910390fd5b610d2f81611c12565b50565b610d3d60095f612109565b5f5b8151811015610937576009828281518110610d5c57610d5c6129a5565b6020908102919091018101518254600181810185555f94855293839020825160029092020180549383015160408401516001600160401b03908116600160801b0267ffffffffffffffff60801b19928216600160401b026001600160801b031990971691909416179490941793909316178255606001519082015501610d3f565b33610e0f7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b0316146109c25760405163118cdaa760e01b8152336004820152602401610d1d565b610e40611ea4565b620100008152600760208201527f1369aa78dc50135ad756d62c97a64a0edcd30066584168200d9d1facf82ca4f56040820151527f2cf23456d712b06f8e3aa5bf0acc3e46a3d094602a3a2b99d873bba05a4391476020604083015101527f08a35f379d2d2c490a51006697275e4db79b67b4a175c1477e262d29e25e42316060820151527f218828131bb7940ccc88c561b299755af4bf0b71ed930b129e8be0a1218139ea6020606083015101527f23a2172436c1145b36d5bc6d3b31fa1610c73a543ea443918aaa3ee175f9921b6080820151527f2502adf404d62877c310214ae9942e93c40b154d34c024bab48a3ca057e60a116020608083015101527f1bb88ada91ab7734882f7826b81275320081ac485f9cf8bfbc3ba54b6eb4dff360a0820151527f25c74a27e9a3b20114a3a91f31c20f01777e7ed913e0ef949f0285e2e7c2069b602060a083015101527f12b0ce76ac8b0dbd405ebc5dd0bae0f91aed50033c7ea36fc62aaba2b98333dc60c0820151527f185b42af49dd1cbe337a84f74b704172428e754a0bea024ab3eb2f996afb2c47602060c083015101527f21f53ad4538b45438bbf0521446070223920e3df6f9022a64cc16d7f94e85c0860e0820151527f2278ac3dedfdac7feb9725a022497175518eada52c8932fc40e6e75bea889fb8602060e083015101527f0876136f81c16298487bfb1be74d4a3487ec45645ab1d09dc2e5b865d62230df610100820151527f098c641c947ecd798dfd5e1b2fe428024cdf03061a53ff774ea8a9e3de9d3f2b602061010083015101527f15eaac2c6232d2268bf79dc47ed9666f992fb3d96ad23fb21690c21586c5472e610120820151527f0f10f1ffc54881287fda6f200bc85d8245b508d844a974098a41119867b325d0602061012083015101527f0895ceea40b085534e9739ca5442ba48b3a3592affde2b509df74521b47d8ab0610140820151527f2e12ec5800ac92fe2a8e7040bc5b435b9eb71e31380173fa7688bf81fcbba455602061014083015101527f2f5384eb5653e47576efe248e7903f463243414bfed5237dda750df3996bd918610160820151527f1c3cd6b11da8704cdc871ab4fa323d7ee57bd40ce165b49a56d5ef6489cd251a602061016083015101527f13579994957ce1554cc1e5b194fb63c9513707f627414f8442681ae736e36450610180820151527f26c9bdcd96d8e420b12974ade93ad9c312c4185213d2f6831a7c625a18890e95602061018083015101527f0cc70a1d542a9a1535ae5d9201696adc5c99c1bcebd9951dfa8afec79fa0b6446101a0820151527f10b043d9f1869181b96579d6616efc17a5df7b84c4d431d966c9094bf1e8815360206101a083015101527f198a65309d131a43b0ab1c47659d0336cfbf62b27f4727106b4fd971c73dd4036101c0820151527f23df99eac3c1947903b211b800efeb76f47d5e87b7414866543492e8c7798d1a60206101c083015101527f221cc5e47b81ce8dcfa72ef981916a8eddef12fcde59c56c62830c126ebef0de6101e0820151527f231f99340c35c9e09652a6df73c9cec5d88738cb71ff45716fdc9e9e45a4926e60206101e083015101527f2c9f1489fce0f263e03f3e97bf0a72273aafcca9325ff47786adb04a52a6d22c610200820151527f21f66e28f17e01e9fd593e16d022c4eca25bd5db96daec606d97b604cc414838602061020083015101527f2015745604a9571e226bd99043cfaf1f96267cc5de67f497563ff81100531d26610220820151527f206889ff4c58dd08ee1107191a2a5bc5dbae55c49d7d8397801799868d10f805602061022083015101527f21062ab8f8ecd8932b429a1eb8614b1e03db61bff6a5cd2d5d7ea193e90e9927610240820151527f217f9b27b934b88ffe555d682dfe6e8b6d503f86b14bbd96342bc48487a60b27602061024083015101527f1c9eda2d195cb731f903235ead6a4f7c66db49da713ecb27afee076f0eea7154610260820151527f2647c161c00b90258e1cefebb17481f8a5d91b5f9dca626e3e89a9215bcca16a602061026083015101527fb0838893ec1f237e8b07323b0744599f4e97b598b3b589bcc2bc37b8d5c418016102808201527fc18393c0fa30fe4e8b038e357ad851eae8de9107584effe7c7f1f651b2010e266102a082015290565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018110806109375760405163016c173360e21b815260040160405180910390fd5b5f6114b26107ad565b90506114bc612127565b83516001600160401b0390811682526020850151168160016020020152604084810151828201526001546060830152600254608083015260035460a08301525f5460c08301525163ce537a7760e01b8152735fbdb2315678afecb367f032d93f642f64180aa39063ce537a779061153b90859085908890600401612b95565b602060405180830381865af4158015611556573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061157a9190612db5565b611597576040516309bde33960e01b815260040160405180910390fd5b50505050565b60095415801590611612575060085460098054600160a01b830463ffffffff1692600160c01b90046001600160401b03169081106115dd576115dd6129a5565b5f91825260209091206002909102015461160790600160401b90046001600160401b031684612dd4565b6001600160401b0316115b156116a557600854600980549091600160c01b90046001600160401b031690811061163f5761163f6129a5565b5f9182526020822060029091020180546001600160c01b03191681556001015560088054600160c01b90046001600160401b031690601861167f83612df3565b91906101000a8154816001600160401b0302191690836001600160401b03160217905550505b604080516080810182526001600160401b03948516815292841660208085019182528301518516848301908152929091015160608401908152600980546001810182555f91909152935160029094027f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af81018054935194518716600160801b0267ffffffffffffffff60801b19958816600160401b026001600160801b03199095169690971695909517929092179290921693909317909155517f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7b090910155565b306001600160a01b037f000000000000000000000000e7f1725e7734ce288f8367e1bb143e90bb3f051216148061180c57507f000000000000000000000000e7f1725e7734ce288f8367e1bb143e90bb3f05126001600160a01b03166118005f516020612e605f395f51905f52546001600160a01b031690565b6001600160a01b031614155b156109c25760405163703e46dd60e11b815260040160405180910390fd5b611832610ddd565b6040516001600160a01b03821681527ff78721226efe9a1bb678189a16d1554928b9f2192e2cb93eeda83b79fa40007d90602001610759565b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa9250505080156118c5575060408051601f3d908101601f191682019092526118c291810190612e1d565b60015b6118ed57604051634c9c8ce360e01b81526001600160a01b0383166004820152602401610d1d565b5f516020612e605f395f51905f52811461191d57604051632a87526960e21b815260048101829052602401610d1d565b6119278383611c82565b505050565b306001600160a01b037f000000000000000000000000e7f1725e7734ce288f8367e1bb143e90bb3f051216146109c25760405163703e46dd60e11b815260040160405180910390fd5b61197d611cd7565b610d2f81611d20565b6109c2611cd7565b82516001600160401b03161515806119b2575060208301516001600160401b031615155b806119bf57506020820151155b806119cc57506040820151155b806119d957506060820151155b806119e357508151155b806119f55750610e108163ffffffff16105b80611a0957506301e133808163ffffffff16115b15611a27576040516350dd03f760e11b815260040160405180910390fd5b8251600480546020808701516001600160401b03908116600160401b026001600160801b0319938416919095169081178517909355604096870151600581905586515f5590860151600155958501516002556060909401516003556006805490941617179091556007919091556008805463ffffffff909216600160a01b0263ffffffff60a01b19909216919091179055565b6009545f9043841180611acb575080155b80611b155750600854600980549091600160c01b90046001600160401b0316908110611af957611af96129a5565b5f9182526020909120600290910201546001600160401b031684105b15611b335760405163b0b4387760e01b815260040160405180910390fd5b5f8080611b41600185612992565b90505b81611bdd57600854600160c01b90046001600160401b03168110611bdd578660098281548110611b7657611b766129a5565b5f9182526020909120600290910201546001600160401b031611611bcb576001915060098181548110611bab57611bab6129a5565b5f9182526020909120600290910201546001600160401b03169250611bdd565b80611bd581612e34565b915050611b44565b81611bfb5760405163b0b4387760e01b815260040160405180910390fd5b85611c068489612992565b11979650505050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0905f90a3505050565b611c8b82611d28565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a2805115611ccf576119278282611d8b565b610937611dfd565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff166109c257604051631afcd79f60e31b815260040160405180910390fd5b610cf8611cd7565b806001600160a01b03163b5f03611d5d57604051634c9c8ce360e01b81526001600160a01b0382166004820152602401610d1d565b5f516020612e605f395f51905f5280546001600160a01b0319166001600160a01b0392909216919091179055565b60605f5f846001600160a01b031684604051611da79190612e49565b5f60405180830381855af49150503d805f8114611ddf576040519150601f19603f3d011682016040523d82523d5f602084013e611de4565b606091505b5091509150611df4858383611e1c565b95945050505050565b34156109c25760405163b398979f60e01b815260040160405180910390fd5b606082611e3157611e2c82611e7b565b611e74565b8151158015611e4857506001600160a01b0384163b155b15611e7157604051639996b31560e01b81526001600160a01b0385166004820152602401610d1d565b50805b9392505050565b805115611e8b5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b604051806102c001604052805f81526020015f8152602001611ed760405180604001604052805f81526020015f81525090565b8152602001611ef760405180604001604052805f81526020015f81525090565b8152602001611f1760405180604001604052805f81526020015f81525090565b8152602001611f3760405180604001604052805f81526020015f81525090565b8152602001611f5760405180604001604052805f81526020015f81525090565b8152602001611f7760405180604001604052805f81526020015f81525090565b8152602001611f9760405180604001604052805f81526020015f81525090565b8152602001611fb760405180604001604052805f81526020015f81525090565b8152602001611fd760405180604001604052805f81526020015f81525090565b8152602001611ff760405180604001604052805f81526020015f81525090565b815260200161201760405180604001604052805f81526020015f81525090565b815260200161203760405180604001604052805f81526020015f81525090565b815260200161205760405180604001604052805f81526020015f81525090565b815260200161207760405180604001604052805f81526020015f81525090565b815260200161209760405180604001604052805f81526020015f81525090565b81526020016120b760405180604001604052805f81526020015f81525090565b81526020016120d760405180604001604052805f81526020015f81525090565b81526020016120f760405180604001604052805f81526020015f81525090565b81526020015f81526020015f81525090565b5080545f8255600202905f5260205f2090810190610d2f9190612145565b6040518060e001604052806007906020820280368337509192915050565b5b8082111561216a5780546001600160c01b03191681555f6001820155600201612146565b5090565b80356001600160a01b0381168114612184575f5ffd5b919050565b5f60208284031215612199575f5ffd5b610ce78261216e565b5f602082840312156121b2575f5ffd5b5035919050565b5f61050082019050825182526020830151602083015260408301516121eb604084018280518252602090810151910152565b50606083015180516080840152602081015160a0840152506080830151805160c0840152602081015160e08401525060a0830151805161010084015260208101516101208401525060c0830151805161014084015260208101516101608401525060e0830151805161018084015260208101516101a08401525061010083015180516101c084015260208101516101e08401525061012083015180516102008401526020810151610220840152506101408301518051610240840152602081015161026084015250610160830151805161028084015260208101516102a08401525061018083015180516102c084015260208101516102e0840152506101a083015180516103008401526020810151610320840152506101c083015180516103408401526020810151610360840152506101e0830151805161038084015260208101516103a08401525061020083015180516103c084015260208101516103e08401525061022083015180516104008401526020810151610420840152506102408301518051610440840152602081015161046084015250610260830151805161048084015260208101516104a0840152506102808301516104c08301526102a0909201516104e09091015290565b634e487b7160e01b5f52604160045260245ffd5b6040516102e081016001600160401b03811182821017156123f1576123f16123ba565b60405290565b604051608081016001600160401b03811182821017156123f1576123f16123ba565b604051601f8201601f191681016001600160401b0381118282101715612441576124416123ba565b604052919050565b80356001600160401b0381168114612184575f5ffd5b5f6060828403121561246f575f5ffd5b604051606081016001600160401b0381118282101715612491576124916123ba565b6040529050806124a083612449565b81526124ae60208401612449565b6020820152604092830135920191909152919050565b5f604082840312156124d4575f5ffd5b604080519081016001600160401b03811182821017156124f6576124f66123ba565b604052823581526020928301359281019290925250919050565b5f5f8284036104e0811215612523575f5ffd5b61252d858561245f565b9250610480605f1982011215612541575f5ffd5b5061254a6123ce565b61255785606086016124c4565b81526125668560a086016124c4565b60208201526125788560e086016124c4565b604082015261258b8561012086016124c4565b606082015261259e8561016086016124c4565b60808201526125b1856101a086016124c4565b60a08201526125c4856101e086016124c4565b60c08201526125d78561022086016124c4565b60e08201526125ea8561026086016124c4565b6101008201526125fe856102a086016124c4565b610120820152612612856102e086016124c4565b6101408201526126268561032086016124c4565b61016082015261263a8561036086016124c4565b6101808201526103a08401356101a08201526103c08401356101c08201526103e08401356101e08201526104008401356102008201526104208401356102208201526104408401356102408201526104608401356102608201526104808401356102808201526104a08401356102a08201526104c0909301356102c08401525092909150565b5f5f604083850312156126d1575f5ffd5b6126da8361216e565b915060208301356001600160401b038111156126f4575f5ffd5b8301601f81018513612704575f5ffd5b80356001600160401b0381111561271d5761271d6123ba565b612730601f8201601f1916602001612419565b818152866020838501011115612744575f5ffd5b816020840160208301375f602083830101528093505050509250929050565b803563ffffffff81168114612184575f5ffd5b5f60208284031215612786575f5ffd5b610ce782612763565b5f5f5f5f8486036101208112156127a4575f5ffd5b6127ae878761245f565b94506080605f19820112156127c1575f5ffd5b506127ca6123f7565b60608681013582526080870135602083015260a0870135604083015260c08701359082015292506127fd60e08601612763565b915061280c610100860161216e565b905092959194509250565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b5f6060828403121561285c575f5ffd5b610ce7838361245f565b5f5f60408385031215612877575f5ffd5b50508035926020909101359150565b5f60208284031215612896575f5ffd5b81356001600160401b038111156128ab575f5ffd5b8201601f810184136128bb575f5ffd5b80356001600160401b038111156128d4576128d46123ba565b6128e360208260051b01612419565b8082825260208201915060208360071b850101925086831115612904575f5ffd5b6020840193505b828410156129745760808488031215612922575f5ffd5b61292a6123f7565b61293385612449565b815261294160208601612449565b602082015261295260408601612449565b604082015260608581013590820152825260809093019260209091019061290b565b9695505050505050565b634e487b7160e01b5f52601160045260245ffd5b81810381811115610cea57610cea61297e565b634e487b7160e01b5f52603260045260245ffd5b805f5b60078110156115975781518452602093840193909101906001016129bc565b6129f082825180518252602090810151910152565b6020818101518051604085015290810151606084015250604081015180516080840152602081015160a0840152506060810151805160c0840152602081015160e0840152506080810151805161010084015260208101516101208401525060a0810151805161014084015260208101516101608401525060c0810151805161018084015260208101516101a08401525060e081015180516101c084015260208101516101e08401525061010081015180516102008401526020810151610220840152506101208101518051610240840152602081015161026084015250610140810151805161028084015260208101516102a08401525061016081015180516102c084015260208101516102e08401525061018081015180516103008401526020810151610320840152506101a08101516103408301526101c08101516103608301526101e08101516103808301526102008101516103a08301526102208101516103c08301526102408101516103e08301526102608101516104008301526102808101516104208301526102a08101516104408301526102c0015161046090910152565b5f610a608201905084518252602085015160208301526040850151612bc7604084018280518252602090810151910152565b50606085015180516080840152602081015160a0840152506080850151805160c0840152602081015160e08401525060a0850151805161010084015260208101516101208401525060c0850151805161014084015260208101516101608401525060e0850151805161018084015260208101516101a08401525061010085015180516101c084015260208101516101e08401525061012085015180516102008401526020810151610220840152506101408501518051610240840152602081015161026084015250610160850151805161028084015260208101516102a08401525061018085015180516102c084015260208101516102e0840152506101a085015180516103008401526020810151610320840152506101c085015180516103408401526020810151610360840152506101e0850151805161038084015260208101516103a08401525061020085015180516103c084015260208101516103e08401525061022085015180516104008401526020810151610420840152506102408501518051610440840152602081015161046084015250610260850151805161048084015260208101516104a0840152506102808501516104c08301526102a08501516104e0830152612d9f6105008301856129b9565b612dad6105e08301846129db565b949350505050565b5f60208284031215612dc5575f5ffd5b81518015158114611e74575f5ffd5b6001600160401b038281168282160390811115610cea57610cea61297e565b5f6001600160401b0382166001600160401b038103612e1457612e1461297e565b60010192915050565b5f60208284031215612e2d575f5ffd5b5051919050565b5f81612e4257612e4261297e565b505f190190565b5f82518060208501845e5f92019182525091905056fe360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbca164736f6c634300081c000a", + "nonce": 1, + "storage": { + "0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00": "0x000000000000000000000000000000000000000000000000ffffffffffffffff" + } + } + }, + "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266": { + "name": null, + "state": { + "balance": "0xd3c1061d4a156ec14b08", + "code": "0x", + "nonce": 16, + "storage": {} + } + } +} diff --git a/espresso/environment/doc.go b/espresso/environment/doc.go new file mode 100644 index 00000000000..6156ebb478a --- /dev/null +++ b/espresso/environment/doc.go @@ -0,0 +1,11 @@ +// Package environment package is a collection of files that assist with the +// creation, management, and easy configuration of the Espresso chain in +// conjunction with the Optimism E2E (end-to-end) local testing devnet. +// +// This package contains a lot of helper functions, and utilities that allow +// for the easy setup, with sensible defaults, and the configuration of an +// `espresso-dev-node`. This process is predominately automatic, and isolated, +// with piecemeal parts of the Espresso Chain being launched in a Docker +// Container, with support for ports that are automatically mapped to external +// ports without explicit configuration. +package environment diff --git a/espresso/environment/e2e_helpers.go b/espresso/environment/e2e_helpers.go new file mode 100644 index 00000000000..840df4482c1 --- /dev/null +++ b/espresso/environment/e2e_helpers.go @@ -0,0 +1,333 @@ +package environment + +import ( + "math/big" + "time" + + bss "github.com/ethereum-optimism/optimism/op-batcher/batcher" + "github.com/ethereum-optimism/optimism/op-batcher/flags" + "github.com/ethereum-optimism/optimism/op-e2e/system/e2esys" + "github.com/ethereum-optimism/optimism/op-e2e/system/helpers" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/ethclient" +) + +// L2TxWithAmount is a helper.TxOptsFn that sets the Amount of the transaction. +func L2TxWithAmount(amount *big.Int) helpers.TxOptsFn { + return func(opts *helpers.TxOpts) { + opts.Value = amount + } +} + +// L2TxWithNonce is a helper.TxOptsFn that sets the Nonce of the transaction. +func L2TxWithNonce(nonce uint64) helpers.TxOptsFn { + return func(opts *helpers.TxOpts) { + opts.Nonce = nonce + } +} + +// L2WithToAddress is a helper.TxOptsFn that sets the To address of the +// transaction. +func L2TxWithToAddress(toAddr *common.Address) helpers.TxOptsFn { + return func(opts *helpers.TxOpts) { + opts.ToAddr = toAddr + } +} + +// L2TxWithVerifyOnClients is a helper.TxOptsFn that sets the list of +// verification clients to verify the transaction on. +func L2TxWithVerifyOnClients(clients ...*ethclient.Client) helpers.TxOptsFn { + return func(opts *helpers.TxOpts) { + opts.VerifyOnClients(clients...) + } +} + +// L2TxWithOptions is a helper.TxOptsFn that sets multiple options for the +// transaction. By default the L2 transaction helper function is only able +// to accept a single helpers.TxOptsFn, so this function allows multiple +// to be passed as a single option, allowing for more granular configuration +// options. +func L2TxWithOptions(options ...helpers.TxOptsFn) helpers.TxOptsFn { + return func(opts *helpers.TxOpts) { + for _, option := range options { + option(opts) + } + } +} + +// WithSequencerUseFinalized is a E2eDevnetLauncherOption that configures the sequencer's +// `SequencerUseFinalized` option to the provided value. +func WithSequencerUseFinalized(useFinalized bool) E2eDevnetLauncherOption { + return func(c *E2eDevnetLauncherContext) E2eSystemOption { + return E2eSystemOption{ + SystemConfigOption: func(cfg *e2esys.SystemConfig) { + seqConfig := cfg.Nodes[e2esys.RoleSeq] + seqConfig.Driver.SequencerUseFinalized = useFinalized + }, + } + } +} + +// WithNonFinalizedProposals is a E2eDevnetLauncherOption that configures the system's +// `NonFinalizedProposals` option to the provided value. +func WithNonFinalizedProposals(useNonFinalized bool) E2eDevnetLauncherOption { + return func(c *E2eDevnetLauncherContext) E2eSystemOption { + return E2eSystemOption{ + SystemConfigOption: func(cfg *e2esys.SystemConfig) { + cfg.NonFinalizedProposals = useNonFinalized + }, + } + } +} + +// WithL1FinalizedDistance is a E2eDevnetLauncherOption that configures the system's +// `L1FinalizedDistance` option to the provided value. +func WithL1FinalizedDistance(distance uint64) E2eDevnetLauncherOption { + return func(c *E2eDevnetLauncherContext) E2eSystemOption { + return E2eSystemOption{ + SystemConfigOption: func(cfg *e2esys.SystemConfig) { + cfg.L1FinalizedDistance = distance + }, + } + } +} + +// WithSeqWindowSize is a E2eDevnetLauncherOption that configures the deployment's +// `SequencerWindowSize` option to the provided value. +func WithSequencerWindowSize(size uint64) E2eDevnetLauncherOption { + return func(c *E2eDevnetLauncherContext) E2eSystemOption { + return E2eSystemOption{ + SystemConfigOption: func(cfg *e2esys.SystemConfig) { + cfg.DeployConfig.SequencerWindowSize = size + }, + } + } +} + +// WithL1BlockTime is a E2eDevnetLauncherOption that configures the system's +// `L1BlockTime` option to the provided value. +// +// The passed block time should be on the order of seconds. Any sub-second +// resolution will be lost. The value **MUST** be at least 1 second or greater. +func WithL1BlockTime(blockTime time.Duration) E2eDevnetLauncherOption { + return func(c *E2eDevnetLauncherContext) E2eSystemOption { + return E2eSystemOption{ + SystemConfigOption: func(cfg *e2esys.SystemConfig) { + cfg.DeployConfig.L1BlockTime = uint64(blockTime / time.Second) + }, + } + } +} + +// WithL2BlockTime is a E2eDevnetLauncherOption that configures the system's +// `L2BlockTime` option to the provided value. +// +// The passed block time should be on the order of seconds. Any sub-second +// resolution will be lost. The value **MUST** be at least 1 second or greater. +func WithL2BlockTime(blockTime time.Duration) E2eDevnetLauncherOption { + return func(c *E2eDevnetLauncherContext) E2eSystemOption { + return E2eSystemOption{ + SystemConfigOption: func(cfg *e2esys.SystemConfig) { + cfg.DeployConfig.L2BlockTime = uint64(blockTime / time.Second) + }, + } + } +} + +// WithEspressoEnforcementOffset is an E2eDevnetLauncherOption that activates the +// EspressoEnforcement hardfork at the given offset (in seconds; sub-second +// resolution is dropped) after L1 genesis. By default the Espresso devnet +// activates the hardfork at genesis (offset = 0); use this helper to test +// pre-fork behavior of the chain. +func WithEspressoEnforcementOffset(offset time.Duration) E2eDevnetLauncherOption { + return func(c *E2eDevnetLauncherContext) E2eSystemOption { + return E2eSystemOption{ + SystemConfigOption: func(cfg *e2esys.SystemConfig) { + seconds := hexutil.Uint64(offset / time.Second) + cfg.DeployConfig.L2GenesisEspressoTimeOffset = &seconds + }, + } + } +} + +// WithBatcherTargetNumFrames is a E2eDevnetLauncherOption that configures the +// batcher's `TargetNumFrames` option to the provided value. +// +// This governs how many frames the batcher will attempt to utilize when +// submitting a channel to the L1. +func WithBatcherTargetNumFrames(size int) E2eDevnetLauncherOption { + return func(c *E2eDevnetLauncherContext) E2eSystemOption { + return E2eSystemOption{ + StartOptions: []e2esys.StartOption{ + { + Key: "maxL1NumFrames", + Role: e2esys.RoleSeq, + BatcherMod: func(batchConfig *bss.CLIConfig, sys *e2esys.System) { + batchConfig.TargetNumFrames = size + }, + }, + }, + } + } +} + +// WithBatcherMaxPendingTransactions is a E2eDevnetLauncherOption that +// configures the batcher's `MaxPendingTransactions` option to the provided +// value. +// +// This governs how many pending L1 transactions the batcher will allow +// before pausing new submissions. +func WithBatcherMaxPendingTransactions(pendingTransactions uint64) E2eDevnetLauncherOption { + return func(c *E2eDevnetLauncherContext) E2eSystemOption { + return E2eSystemOption{ + StartOptions: []e2esys.StartOption{ + { + Key: "maxPendingTransactions", + Role: e2esys.RoleSeq, + BatcherMod: func(batchConfig *bss.CLIConfig, sys *e2esys.System) { + batchConfig.MaxPendingTransactions = pendingTransactions + }, + }, + }, + } + } +} + +// WithBatcherMaxL1TxSize is a E2eDevnetLauncherOption that configures the +// batcher's `MaxL1TxSize` option to the provided value. +// +// This governs the maximum L1 transaction size that the batcher will attempt +// to submit when submitting a channel to L1. +func WithBatcherMaxL1TxSize(maxL1TxSize uint64) E2eDevnetLauncherOption { + return func(c *E2eDevnetLauncherContext) E2eSystemOption { + return E2eSystemOption{ + StartOptions: []e2esys.StartOption{ + { + Key: "maxL1TxSize", + Role: e2esys.RoleSeq, + BatcherMod: func(batchConfig *bss.CLIConfig, sys *e2esys.System) { + batchConfig.MaxL1TxSize = maxL1TxSize + + if batchConfig.DataAvailabilityType == flags.BlobsType { + // If we're setting the max data size for blobs, + // we need to also inform the batcher to use that + // setting when calculating blob sizes. + // + // Otherwise it will use the max blob size constant. + batchConfig.TestUseMaxTxSizeForBlobs = true + } + }, + }, + }, + } + } +} + +// WithBatcherMaxBlocksPerSpanBatch is a E2eDevnetLauncherOption that +// configures the batcher's `MaxBlocksPerSpanBatch` option to the provided +// value. +// +// This governs how many blocks the batcher will include in a single span +// when creating batches to submit to L1. +func WithBatcherMaxBlocksPerSpanBatch(maxBlocksPerSpanBatch int) E2eDevnetLauncherOption { + return func(c *E2eDevnetLauncherContext) E2eSystemOption { + return E2eSystemOption{ + StartOptions: []e2esys.StartOption{ + { + Key: "maxBlocksPerSpanBatch", + Role: e2esys.RoleSeq, + BatcherMod: func(batchConfig *bss.CLIConfig, sys *e2esys.System) { + batchConfig.MaxBlocksPerSpanBatch = maxBlocksPerSpanBatch + }, + }, + }, + } + } +} + +// WithBatcherDataAvailabilityType is a E2eDevnetLauncherOption that configures +// the batcher's `DataAvailabilityType` option to the provided value. +// +// This governs which data availability method the batcher will use when +// submitting frames to L1. +func WithBatcherDataAvailabilityType(daAvailabilityType flags.DataAvailabilityType) E2eDevnetLauncherOption { + { + return func(c *E2eDevnetLauncherContext) E2eSystemOption { + return E2eSystemOption{ + StartOptions: []e2esys.StartOption{ + { + Key: "dataAvailabilityType", + Role: e2esys.RoleSeq, + BatcherMod: func(batchConfig *bss.CLIConfig, sys *e2esys.System) { + batchConfig.DataAvailabilityType = daAvailabilityType + }, + }, + }, + } + } + } +} + +// WithBatcherMaxChannelDuration is a configuration option that modifies the +// MaxChannelDuration for the Batcher Config. This value will then be +// utilized by the Channels created by the batcher. +func WithBatcherMaxChannelDuration(maxChannelDuration uint64) E2eDevnetLauncherOption { + { + return func(c *E2eDevnetLauncherContext) E2eSystemOption { + return E2eSystemOption{ + StartOptions: []e2esys.StartOption{ + { + Key: "maxChannelDuration", + Role: e2esys.RoleSeq, + BatcherMod: func(batchConfig *bss.CLIConfig, sys *e2esys.System) { + batchConfig.MaxChannelDuration = maxChannelDuration + }, + }, + }, + } + } + } +} + +// WithBatcherMaxFrameSize is a configuration option that modifies the +// MaxChannelDuration for the Batcher Config. This value will then be +// utilized by the channels created by the batcher. +func WithBatcherMaxFrameSize(maxFrameSize uint64) E2eDevnetLauncherOption { + { + return func(c *E2eDevnetLauncherContext) E2eSystemOption { + return E2eSystemOption{ + StartOptions: []e2esys.StartOption{ + { + Key: "maxFrameSize", + Role: e2esys.RoleSeq, + BatcherMod: func(batchConfig *bss.CLIConfig, sys *e2esys.System) { + batchConfig.MaxChannelDuration = maxFrameSize + }, + }, + }, + } + } + } +} + +// WithBatcherCompressor is a configuration option that modifies the Compressor +// setting of the Batcher Config. This value will be utilized to determine +// compression options for the channels created by the batcher. +func WithBatcherCompressor(compressor string) E2eDevnetLauncherOption { + { + return func(c *E2eDevnetLauncherContext) E2eSystemOption { + return E2eSystemOption{ + StartOptions: []e2esys.StartOption{ + { + Key: "compressor", + Role: e2esys.RoleSeq, + BatcherMod: func(batchConfig *bss.CLIConfig, sys *e2esys.System) { + batchConfig.Compressor = compressor + }, + }, + }, + } + } + } +} diff --git a/espresso/environment/espresso_dev_net_launcher.go b/espresso/environment/espresso_dev_net_launcher.go new file mode 100644 index 00000000000..811220a5af1 --- /dev/null +++ b/espresso/environment/espresso_dev_net_launcher.go @@ -0,0 +1,105 @@ +package environment + +import ( + "context" + "testing" + + espressoClient "github.com/EspressoSystems/espresso-network/sdks/go/client" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/geth" + "github.com/ethereum-optimism/optimism/op-e2e/system/e2esys" +) + +// EspressoE2eDevnetLauncher is an interface for launching an E2E devnet with Espresso, and +// configuring it to run in a desired manner. +type EspressoE2eDevnetLauncher interface { + // StartE2eDevnet will launch the devnet with the provided options. The returned system will be + // a fully configured e2e system with the configured options. + StartE2eDevnet(ctx context.Context, t *testing.T, options ...E2eDevnetLauncherOption) (*e2esys.System, EspressoDevNode, error) +} + +// E2eDevnetLauncherContext is a struct that contains the context and any errors that may have +// occurred during the launch of the E2E devnet. It also contains the current system instance. +type E2eDevnetLauncherContext struct { + // The launching Context + Ctx context.Context + + // The testing.T for the current test, used to fail with a clear error message on + // launch failures (e.g. Docker container failing to start). + T *testing.T + + // Any Current Error + Error error + + // The Current System configuration + SystemCfg *e2esys.SystemConfig + + // The Current System instance + System *e2esys.System + + // EspressoDevNode represents the Espresso Dev Node that is being launched. + EspressoDevNode +} + +// E2eDevnetLauncherOption is a function that takes a E2eDevnetLauncherContext +// and returns an E2eSystemOption. +type E2eDevnetLauncherOption func( + ctx *E2eDevnetLauncherContext, +) E2eSystemOption + +// SysConfigBuilder is a function that is used to construct the Initial System +// Config Options +type SysConfigBuilder func(*testing.T, ...e2esys.SystemConfigOpt) e2esys.SystemConfig + +// E2eSystemOption is a struct that contains the options for the +// e2e system that is being launched. It contains the GethOptions and +// any relevant StartOptions that may be needed for the system. +type E2eSystemOption struct { + // SystemConfigOption is a function that modifies the SystemConfig. + // This occurs specifically after initialization, but before startup. + // + // This is separate from the SystemConfigOpt, which only happens + // at intiial creation time. + SystemConfigOption func(*e2esys.SystemConfig) + + // SystemConfigOpt is a Configuration Options for the creation of + // the intiial SystemConfig. + // + // This is necessary, as the initialization has some additional triggered + // side-effects that will not occur if not encountered otherwise. + SystemConfigOpt e2esys.SystemConfigOpt + + // The GethOptions to pass to the Geth Node. + GethOptions map[string][]geth.GethOption + + // Any relevant StartOptions to pass to the e2e system. + StartOptions []e2esys.StartOption + + // SysConfigBuilder allows for the overidding of the initially constructed + // System Configuration Behavior. + // + // This is only necessary if some other systems are launched as a + // consequence, suche as those with the Dispute Game setup. + SysConfigBuilder +} + +// EspressoDevNode is an interface that wraps the Espresso Dev Node +// to expose certain functionality, and information that may be needed +// to effectively interact with the Espresso Dev Node. +type EspressoDevNode interface { + // SequencerPort returns the port that the sequencer is running on. + SequencerPort() string + + // BuilderPort returns the port that the builder is running on. + BuilderPort() string + + // EspressoUrls returns the URLs of the Espresso node + EspressoUrls() []string + + // Client returns the Espresso query-service client backing this dev node. + // For the in-memory mock dev node this is the shared mock client that the + // batchers also use, so tests submit/read against the same Espresso chain. + Client() espressoClient.EspressoClient + + // Shut Down the Espresso Dev Node + Stop() error +} diff --git a/espresso/environment/espresso_dev_node_logs.go b/espresso/environment/espresso_dev_node_logs.go new file mode 100644 index 00000000000..4a0ea4946fd --- /dev/null +++ b/espresso/environment/espresso_dev_node_logs.go @@ -0,0 +1,382 @@ +package environment + +import ( + "bytes" + "fmt" + "io" + "net/url" + "strings" + "time" + "unicode" + "unicode/utf8" + + "github.com/ethereum/go-ethereum/common" +) + +// LineReader is an interface that abstracts out the ability to read a whole +// line from a source. +// +// The definition is extracted from bufio.Reader, but is here for convenience +// of reference and implementation +type LineReader interface { + ReadLine() (line []byte, isPrefix bool, err error) +} + +// ansiEscapeCodeLineReader is a LineReader that removes ANSI escape codes +// from the line it reads. This is useful for cleaning up log lines that +// contain ANSI escape codes for coloring or formatting. The reader wraps +// another LineReader and processes the lines it reads to remove the escape +// codes before returning them. +type ansiEscapeCodeLineReader struct { + r LineReader +} + +// NewAnsiEscapeCodeLineReader creates a new LineReader from the LineReader +// passed in. It removes any ANSI escape code for color formatting from +// line entries encountered. +func NewAnsiEscapeCodeLineReader(r LineReader) LineReader { + return &ansiEscapeCodeLineReader{ + r: r, + } +} + +// ReadLine implements LineReader. It reads a line from the underlying +// LineReader and removes any ANSI escape codes from the line. +// +// This avoids extra allocation by replacing contents from the line returned +// by the underlying LineReader with the contents of the line without +// escape codes. +func (a *ansiEscapeCodeLineReader) ReadLine() (line []byte, isPrefix bool, err error) { + line, isPrefix, err = a.r.ReadLine() + if err != nil { + return line, isPrefix, err + } + + // Go through the Escape sequence codes, and remove them from the + // line + + i := 0 + o := 0 + for l := len(line); i < l; i, o = i+1, o+1 { + line[o] = line[i] + + if line[i] != 0x1b { + // this is not the escape character + continue + } + + if (i+1 < l) && line[i+1] != '[' { + // We'll ignore this case for now + continue + } + + // We want to ignore this escape sequence + o-- + + // We have already read the ESC rune and '[' + i++ + i++ + + for i < l && line[i] != 'm' { + i++ + } + } + + // truncate the line to the new length + return line[:o], isPrefix, err +} + +// EspressoDevNodeLogEntry represents a simple log entry from the +// Espresso Dev Node. +// +// It contains the timestamp, the logging level, the file location, and the +// rest of the message for quick reference. +// +// The Format of the log lines is anticipated to be of the following form: +// : +type EspressoDevNodeLogEntry struct { + Time time.Time + Level string + Location string + Message string +} + +// EspressoDevNodeLogEntryReader is an interface that abstracts out the +// ability to read a log entry from a source. +type EspressoDevNodeLogEntryReader interface { + ReadLogLine() (EspressoDevNodeLogEntry, error) +} + +// espressoDevNodeLogEntryReader is a struct that will implement the +// EspressoDevNodeLogEntryReader interface. +type espressoDevNodeLogEntryReader struct { + r LineReader +} + +// NewEspressoDevNodeLogReader creates a new EspressoDevNodeLogEntryReader +// from the LineReader passed in. +func NewEspressoDevNodeLogReader(r LineReader) EspressoDevNodeLogEntryReader { + return &espressoDevNodeLogEntryReader{ + r: r, + } +} + +func readOffsetOfCondition(line []byte, offset int, cond func(r rune) bool) int { + i, l := offset, len(line) + + for i < l { + r, size := utf8.DecodeRune(line[i:]) + if cond(r) { + // We have our entry + return i + } + + i += size + } + + return i + +} + +// isNotSpace is a helper function that will return true if the rune +// is not a space character. This is used to skip over whitespace in the +// log line. +func isNotSpace(r rune) bool { + return !unicode.IsSpace(r) +} + +// isColon is a helper function that will return true if the rune +// is a colon character. +func isColon(r rune) bool { + return r == ':' +} + +func skipWhitespace(line []byte, offset int) int { + // Skip the whitespace in between + whiteSpaceBytesEnd := readOffsetOfCondition(line, offset, isNotSpace) + return whiteSpaceBytesEnd +} + +// ReadLogLine implements EspressoDevNodeLogEntryReader. +// +// It will read lines from the LineReader until it encounters a line that +// matches the expected format. Once the expected format is encountered, and +// the values are able to be parsed into an `EspressoDevNodeLogEntry`, it will +// return the entry. +// +// If there is an error in the underlying LineReader, it will return that +// error instead of returning an entry. +func (e *espressoDevNodeLogEntryReader) ReadLogLine() (EspressoDevNodeLogEntry, error) { + for { + line, _, err := e.r.ReadLine() + if err != nil { + return EspressoDevNodeLogEntry{}, err + } + + // Trim the spaces from the line + line = bytes.TrimSpace(line) + + offset := 0 + var tsField, infoField, locField, messageField []byte + { + // Read the first field + tsFieldStartOffset := offset + tsFieldEndOffset := readOffsetOfCondition(line, offset, unicode.IsSpace) + tsField = line[tsFieldStartOffset:tsFieldEndOffset] + + // Ignore the white space in between + offset = skipWhitespace(line, tsFieldEndOffset) + } + + // Read the second field + { + levelFieldStartOffset := offset + levelFieldEndOffset := readOffsetOfCondition(line, offset, unicode.IsSpace) + infoField = line[levelFieldStartOffset:levelFieldEndOffset] + + // Ignore the white space in between + offset = skipWhitespace(line, levelFieldEndOffset) + } + + { + // Read the third field + locFieldStartOffset := offset + locFieldEndOffset := readOffsetOfCondition(line, offset, unicode.IsSpace) + locField = line[locFieldStartOffset:locFieldEndOffset] + + // Ignore the white space in between + offset = skipWhitespace(line, locFieldEndOffset) + } + + { + // Message field + messageField = line[offset:] + } + + ts, err := time.Parse(time.RFC3339Nano, string(tsField)) + if err != nil { + // This isn't a log entry we're expecting or wanting, skip + continue + } + + lvl := string(infoField) + loc := string(bytes.TrimRightFunc(locField, isColon)) + msg := string(messageField) + + entry := EspressoDevNodeLogEntry{ + Time: ts, + Level: lvl, + Location: loc, + Message: msg, + } + + return entry, nil + } +} + +// There are two types of log entries we are interested in. Deployed contract +// lines, and Server Listening Lines. + +// EspressoDeployedContractLogEntry represents a log entry for a +// deployed contract. +// It contains the original log entry, and for convenience it also contains +// the Name of the contract and the address of the contract for easy access. +type EspressoDeployedContractLogEntry struct { + Entry EspressoDevNodeLogEntry + Name string + Address common.Address +} + +// handleDeployedLogEntry is a helper function that will take a log entry +// and parse it into an `EspressoDeployedContractLogEntry`. +// +// It is expected to be of the form: +// : deployed at
+func handleDeployedLogEntry(entry EspressoDevNodeLogEntry, fields []string) (EspressoDeployedContractLogEntry, error) { + // deployed at
+ if len(fields) != 4 { + return EspressoDeployedContractLogEntry{}, fmt.Errorf("invalid deployed entry: %s", entry.Message) + } + + name := fields[1] + address := common.HexToAddress(fields[3]) + + return EspressoDeployedContractLogEntry{ + Entry: entry, + Name: name, + Address: address, + }, nil +} + +// EspressoStartListeningLogEntry represents a log entry for a +// server listening entry. +// It contains the original log entry, and for convenience it also contains +// the URL of the server for easy access. +type EspressoStartListeningLogEntry struct { + Entry EspressoDevNodeLogEntry + Url url.URL +} + +// handleServerListeningLogEntry is a helper function that will take a log entry +// and parse it into an `EspressoStartListeningLogEntry`. +// +// It is expected to be of the form: +// : Server listening on +func handleServerListeningLogEntry(entry EspressoDevNodeLogEntry, fields []string) (EspressoStartListeningLogEntry, error) { + // Server listening on + if len(fields) != 4 { + return EspressoStartListeningLogEntry{}, fmt.Errorf("invalid server listening entry: %s", entry.Message) + } + + rawUrl := fields[3] + u, err := url.Parse(rawUrl) + if err != nil { + return EspressoStartListeningLogEntry{}, fmt.Errorf("invalid url: %s", rawUrl) + } + + return EspressoStartListeningLogEntry{ + Entry: entry, + Url: *u, + }, nil +} + +// EspressoDeployedContractLogEntryReader is an interface that abstracts out +// the ability to read a log entry from a source. +type EspressoDeployedContractLogEntryReader interface { + ReadDeployedContractLogEntry() (EspressoDeployedContractLogEntry, error) +} + +// espressoDeployedContractDevNodeLogEntryReader is a struct that will +// implement the EspressoDeployedContractLogEntryReader interface. +type espressoDeployedContractDevNodeLogEntryReader struct { + r EspressoDevNodeLogEntryReader + numStartListeningEntries int +} + +// NewEspressoDeployedContractLogEntryReader creates a new +// EspressoDeployedContractLogEntryReader from the +// EspressoDevNodeLogEntryReader passed in. +func NewEspressoDeployedContractLogEntryReader(r EspressoDevNodeLogEntryReader) EspressoDeployedContractLogEntryReader { + return &espressoDeployedContractDevNodeLogEntryReader{ + r: r, + } +} + +// MAX_NUM_STARTING_ENTIRES represents the maximum number of starting entries +// we expect to encounter before we consider ourselves "started" +const MAX_NUM_STARTING_ENTRIES = 4 + +// ReadDeployedContractLogEntry implements EspressoDeployedContractLogEntryReader. +// +// It will read entries from the EspressoDevNodeLogEntryReader until it +// encounters an entry that matches the expected deployed contract entry. +// Once the expected format is encountered, and the values are able to +// be parsed into an `EspressoDeployedContractLogEntry`, it will return the +// entry. +// +// If an error is encountered from the underlying EspressoDevNodeLogEntryReader, +// it will return that error instead of returning an entry. +// +// If more than 4 server listening entries are encountered, it will return +// an EOF error. This is an anticipated condition for the Espresso Dev Node +// being started. +func (e *espressoDeployedContractDevNodeLogEntryReader) ReadDeployedContractLogEntry() (EspressoDeployedContractLogEntry, error) { + for { + if e.numStartListeningEntries >= MAX_NUM_STARTING_ENTRIES { + return EspressoDeployedContractLogEntry{}, io.EOF + } + + entry, err := e.r.ReadLogLine() + if err != nil { + return EspressoDeployedContractLogEntry{}, err + } + + fields := strings.Fields(entry.Message) + // = deployed at
+ // = Server listening on + + if len(fields) < 4 { + // This isn't a log entry we're considering at the moment, skip it + continue + } + + switch { + default: + // This isn't a log entry we're considering at the moment, skip it + continue + + case strings.HasPrefix(strings.ToLower(entry.Message), "deployed"): + // We have a deployed contract entry + return handleDeployedLogEntry(entry, fields) + + case strings.HasPrefix(strings.ToLower(entry.Message), "server listening on"): + // We have a server listening entry + _, err := handleServerListeningLogEntry(entry, fields) + if err == nil { + e.numStartListeningEntries++ + } + // Skip this entry + continue + } + } +} diff --git a/espresso/environment/espresso_dev_node_test.go b/espresso/environment/espresso_dev_node_test.go new file mode 100644 index 00000000000..a374e1ed800 --- /dev/null +++ b/espresso/environment/espresso_dev_node_test.go @@ -0,0 +1,80 @@ +package environment_test + +import ( + "context" + "testing" + + env "github.com/ethereum-optimism/optimism/espresso/environment" + "github.com/ethereum-optimism/optimism/op-e2e/config" + "github.com/ethereum-optimism/optimism/op-e2e/system/e2esys" +) + +// TestE2eDevnetWithEspressoSimpleTransactions launches the e2e Dev Net with the Espresso Dev Node +// and runs a couple of simple transactions to it. +func TestE2eDevnetWithEspressoSimpleTransactions(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + launcher := new(env.EspressoDevNodeLauncherDocker) + + system, espressoDevNode, err := launcher.StartE2eDevnet(ctx, t) + if have, want := err, error(nil); have != want { + t.Fatalf("failed to start dev environment with espresso dev node:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + + // Signal the testnet to shut down on exit + defer env.Stop(t, espressoDevNode) + defer env.Stop(t, system) + // Send Transaction on L1, and wait for verification on the L2 Verifier + env.RunSimpleL1TransferAndVerifier(ctx, t, system) + + // Submit a Transaction on the L2 Sequencer node, to a Burn Address + env.RunSimpleL2Burn(ctx, t, system) +} + +// TestE2eDevnetWithEspressoAndAltDaSimpleTransactions launches the e2e Dev Net with the Espresso +// Dev Node in AltDA mode and runs a couple of simple transactions to it. +func TestE2eDevnetWithEspressoAndAltDaSimpleTransactions(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + launcher := new(env.EspressoDevNodeLauncherDocker) + + // WithAltDa enables UseAltDA, which wires the e2e system to the in-process + // altda.FakeDAServer; no external EigenDA proxy is contacted. + system, espressoDevNode, err := launcher.StartE2eDevnet(ctx, t, env.WithAltDa()) + if have, want := err, error(nil); have != want { + t.Fatalf("failed to start dev environment with espresso dev node:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + + // Signal the testnet to shut down on exit + defer env.Stop(t, espressoDevNode) + defer env.Stop(t, system) + // Send Transaction on L1, and wait for verification on the L2 Verifier + env.RunSimpleL1TransferAndVerifier(ctx, t, system) + + // Submit a Transaction on the L2 Sequencer node, to a Burn Address + env.RunSimpleL2Burn(ctx, t, system) +} + +// TestE2eDevnetWithoutEspressoSimpleTransactions launches the e2e Dev Net +// without the Espresso Dev Node and runs a couple of simple transactions to it. +func TestE2eDevnetWithoutEspressoSimpleTransaction(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + sysConfig := e2esys.DefaultSystemConfig(t, e2esys.WithAllocType(config.DefaultAllocType)) + + system, err := sysConfig.Start(t) + if have, want := err, error(nil); have != want { + t.Fatalf("failed to start e2e dev environment:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + // Shut down the test net on exit + defer env.Stop(t, system) + + // Send Transaction on L1, and wait for verification on the L2 Verifier + env.RunSimpleL1TransferAndVerifier(ctx, t, system) + + // Submit a Transaction on the L2 Sequencer node, to a Burn Address + env.RunSimpleL2Burn(ctx, t, system) +} diff --git a/espresso/environment/espresso_eth_helpers.go b/espresso/environment/espresso_eth_helpers.go new file mode 100644 index 00000000000..588bb551819 --- /dev/null +++ b/espresso/environment/espresso_eth_helpers.go @@ -0,0 +1,54 @@ +package environment + +import ( + "context" + "crypto/ecdsa" + "errors" + "math/big" + "time" + + opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto" + "github.com/ethereum/go-ethereum/common" + gethTypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" +) + +// ErrBalanceDidNotIncrease is a sentinel error that indicates that the balance +// did not increase before the request was cancelled. +var ErrBalanceDidNotIncrease = errors.New("balance did not increase") + +// WaitForIncreasedBalance waits for the balance of the given account to +// increase from the given initial balance. It will return nil if the balance +// increases, or an error if the context is cancelled before the balance +// increases. +func WaitForIncreasedBalance(ctx context.Context, client *ethclient.Client, account common.Address, initialBalance *big.Int) error { + for { + // Check context to see if we should stop + select { + case <-ctx.Done(): + return ErrBalanceDidNotIncrease + + default: + } + + nextBalance, err := client.BalanceAt(ctx, account, nil) + if err != nil { + return err + } + + if nextBalance.Cmp(initialBalance) > 0 { + // Our balance has increased + return nil + } + + // Sleep for a bit + time.Sleep(time.Millisecond * 100) + } +} + +func SignTransaction(txData gethTypes.TxData, privateKey *ecdsa.PrivateKey, chainID *big.Int) (*gethTypes.Transaction, error) { + tx := gethTypes.NewTx(txData) + signer := opcrypto.PrivateKeySignerFn(privateKey, chainID) + return signer(crypto.PubkeyToAddress(privateKey.PublicKey), tx) +} diff --git a/espresso/environment/mock_dev_node.go b/espresso/environment/mock_dev_node.go new file mode 100644 index 00000000000..3d732b4adbe --- /dev/null +++ b/espresso/environment/mock_dev_node.go @@ -0,0 +1,32 @@ +package environment + +import ( + espressoClient "github.com/EspressoSystems/espresso-network/sdks/go/client" + "github.com/ethereum-optimism/optimism/espresso" +) + +// mockEspressoDevNode is an EspressoDevNode backed by the in-memory mock Espresso +// client (espresso.MockEspressoClient) instead of a dockerized espresso-dev-node. +// The mock is owned by the e2esys.System (System.EspressoClient) and stopped when +// the system is closed, so Stop here is a no-op. +type mockEspressoDevNode struct { + client *espresso.MockEspressoClient +} + +var _ EspressoDevNode = (*mockEspressoDevNode)(nil) + +func (m *mockEspressoDevNode) Client() espressoClient.EspressoClient { + return m.client +} + +// SequencerPort is unused by the in-memory mock; there is no listening port. +func (m *mockEspressoDevNode) SequencerPort() string { return "" } + +// BuilderPort is unused by the in-memory mock; there is no listening port. +func (m *mockEspressoDevNode) BuilderPort() string { return "" } + +// EspressoUrls is unused by the in-memory mock; there are no URLs. +func (m *mockEspressoDevNode) EspressoUrls() []string { return nil } + +// Stop is a no-op; the mock client's lifecycle is owned by the e2esys.System. +func (m *mockEspressoDevNode) Stop() error { return nil } diff --git a/espresso/environment/optitmism_espresso_test_helpers.go b/espresso/environment/optitmism_espresso_test_helpers.go new file mode 100644 index 00000000000..e45d09ebf72 --- /dev/null +++ b/espresso/environment/optitmism_espresso_test_helpers.go @@ -0,0 +1,597 @@ +package environment + +import ( + "bytes" + "context" + "crypto/ecdsa" + _ "embed" + "encoding/json" + "errors" + "fmt" + "io" + "log/slog" + "math/big" + "net/http" + "os" + "testing" + "time" + + espressoClient "github.com/EspressoSystems/espresso-network/sdks/go/client" + espressoCommon "github.com/EspressoSystems/espresso-network/sdks/go/types" + "github.com/ethereum-optimism/optimism/op-batcher/batcher" + "github.com/ethereum-optimism/optimism/op-batcher/flags" + "github.com/ethereum-optimism/optimism/op-e2e/config" + "github.com/ethereum-optimism/optimism/op-e2e/faultproofs" + "github.com/ethereum-optimism/optimism/op-e2e/system/e2esys" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" +) + +type EspressoAllocAccount struct { + State types.Account `json:"state"` + Name string `json:"name"` +} + +//go:embed allocs.json +var ESPRESSO_ALLOCS_RAW string +var ESPRESSO_ALLOCS map[common.Address]EspressoAllocAccount + +func init() { + // Unmarshal allocs to set up the dockerConfig environment variables + ESPRESSO_ALLOCS = make(map[common.Address]EspressoAllocAccount) + + if err := json.Unmarshal([]byte(ESPRESSO_ALLOCS_RAW), &ESPRESSO_ALLOCS); err != nil { + panic(fmt.Sprintf("failed to unmarshal ESPRESSO_ALLOCS: %v", err)) + } +} + +// mockDummyLightClientAddr is a non-zero placeholder light-client address used when +// running against the in-memory mock Espresso client, which does not model a light +// client. No contract is deployed there; the streamer tolerates the resulting error. +var mockDummyLightClientAddr = common.HexToAddress("0x9fe46736679d2d9a65f0992f2272de9f3c7fa6e0") + +// mockLightClientAddr returns the configured light-client address if +// ESPRESSO_SEQUENCER_LIGHT_CLIENT_PROXY_ADDRESS is set, otherwise a fixed dummy. +func mockLightClientAddr() common.Address { + if v, ok := os.LookupEnv("ESPRESSO_SEQUENCER_LIGHT_CLIENT_PROXY_ADDRESS"); ok && common.IsHexAddress(v) { + return common.HexToAddress(v) + } + return mockDummyLightClientAddr +} + +// This is the mnemonic that we use to create the private key for deploying +// contacts on the L1 +const ESPRESSO_MNEMONIC = "giant issue aisle success illegal bike spike question tent bar rely arctic volcano long crawl hungry vocal artwork sniff fantasy very lucky have athlete" + +// This is the Mnemonic Index that we use to create the private key for deploying +// contracts on the L1 +const ESPRESSO_MNEMONIC_INDEX = "0" + +const ESPRESSO_TESTING_BATCHER_KEY = "0xfad9c8855b740a0b7ed4c221dbad0f33a83a49cad6b3fe8d5817ac83d38b6a19" + +// This is address that corresponds to the menmonic we pass to the espresso-dev-node +var ESPRESSO_CONTRACT_ACCOUNT = common.HexToAddress("0x8943545177806ed17b9f23f0a21ee5948ecaa776") + +// ErrEspressoBlockHeightDidNotIncrease is a sentinel error that occurs when +// the Espresso Block Height does not increase within the alloted context +// allowance. +var ErrEspressoBlockHeightDidNotIncrease = errors.New("espresso block height did not increase") + +// ErrFailedToParseNumber is a sentinel error that occurs when we are unable +// to parse a number from a string +var ErrFailedToParseNumber = errors.New("failed to parse number from string") + +// WaitForEspressoBlockHeightToBePositive waits for the Espresso Block Height to +// increase beyond 0. +func WaitForEspressoBlockHeightToBePositive(ctx context.Context, url string) error { + for { + select { + case <-ctx.Done(): + // We've timed out + return ErrEspressoBlockHeightDidNotIncrease + default: + } + + time.Sleep(time.Millisecond * 10) + + request, err := http.NewRequest("GET", url, nil) + if err != nil { + return err + } + + response, err := http.DefaultClient.Do(request) + if err != nil { + // Service may not yet be available? + continue + } + + if response.StatusCode != http.StatusOK { + // Service may not yet be available? + continue + } + + // Alright, presumably, we have a block height + + buf := new(bytes.Buffer) + if _, err := io.Copy(buf, response.Body); err != nil { + return err + } + if err := response.Body.Close(); err != nil { + return err + } + + blockHeight, ok := new(big.Int).SetString(buf.String(), 10) + if !ok { + return ErrFailedToParseNumber + } + + if blockHeight.Cmp(big.NewInt(0)) > 0 { + // We have a positive block height! That means we're + // committing blocks, and we're progressing. We + // **SHOULD** be good to continue" + return nil + } + } +} + +// EspressoDevNodeLauncherDocker is an implementation of EspressoDevNodeLauncher +// that uses Docker to launch the Espresso Dev Node +type EspressoDevNodeLauncherDocker struct{} + +var _ EspressoE2eDevnetLauncher = (*EspressoDevNodeLauncherDocker)(nil) + +// FailedToDetermineL1RPCURL represents a class of errors that occur when we +// are unable to correctly form our L1 RPC URL +type FailedToDetermineL1RPCURL struct { + Cause error +} + +// Error implements error +func (f FailedToDetermineL1RPCURL) Error() string { + return fmt.Sprintf("failed to determine the L1 RPC URL: %v", f.Cause) +} + +// FailedToLoadEspressoAccount represents a class of errors that occur when we +// are unable to load the espresso account +type FailedToLoadEspressoAccount struct { + Cause error +} + +// Error implements error +func (f FailedToLoadEspressoAccount) Error() string { + return fmt.Sprintf("failed to load the espresso account: %v", f.Cause) +} + +// EspressoNodeFailedToBecomeReady represents a class of errors that indicate +// that the espresso-dev-node failed to become ready. +type EspressoNodeFailedToBecomeReady struct { + Cause error +} + +// Error implements error +func (e EspressoNodeFailedToBecomeReady) Error() string { + return fmt.Sprintf("espresso node failed to become ready: %v", e.Cause) +} + +// ErrUnableToDetermineEspressoDevNodeSequencerHost is a sentinel error that +// indicates that we were unable to determine what the Sequencer API host +// is meant to be. +var ErrUnableToDetermineEspressoDevNodeSequencerHost = errors.New("unable to determine the host for the espresso-dev-node sequencer api") + +// defaultSystemConfigBuilder is the default SystemConfigBuilder utilized by +// the GetE2eDevnetSysConfig method. +func defaultSystemConfigBuilder(t *testing.T, options ...e2esys.SystemConfigOpt) e2esys.SystemConfig { + return e2esys.DefaultSystemConfig(t, options...) +} + +// GetE2eDevnetSysConfig returns a configuration for an E2E devnet. +func (l *EspressoDevNodeLauncherDocker) GetE2eDevnetSysConfig(ctx context.Context, t *testing.T, options ...E2eSystemOption) e2esys.SystemConfig { + systemConfigsOpts := []e2esys.SystemConfigOpt{ + e2esys.WithAllocType(config.AllocTypeEspressoWithoutEnclave), + } + + sysConfigBuilder := defaultSystemConfigBuilder + for _, opt := range options { + if sysConfigOption := opt.SystemConfigOpt; sysConfigOption != nil { + systemConfigsOpts = append(systemConfigsOpts, sysConfigOption) + } + + if builder := opt.SysConfigBuilder; builder != nil { + sysConfigBuilder = builder + } + } + + sysConfig := sysConfigBuilder(t, systemConfigsOpts...) + + // Set a short L1 block time and finalized distance to make tests faster and reach finality sooner + sysConfig.DeployConfig.L1BlockTime = 2 + + // Activate the Espresso hardfork at genesis. + espressoOffset := hexutil.Uint64(0) + sysConfig.DeployConfig.L2GenesisEspressoTimeOffset = &espressoOffset + + // Ensure that we fund the dev accounts + sysConfig.DeployConfig.FundDevAccounts = true + + millionEthers := new(big.Int).Mul(new(big.Int).SetUint64(1_000_000), new(big.Int).SetUint64(params.Ether)) + + sysConfig.L1Allocs[ESPRESSO_CONTRACT_ACCOUNT] = types.Account{ + Nonce: 100000, // Set the nonce to avoid collisions with predeployed contracts + Balance: millionEthers, // Pre-fund Espresso deployer acount with 1M Ether + } + + // Set up the L1Allocs in the system config + for address, account := range ESPRESSO_ALLOCS { + sysConfig.L1Allocs[address] = account.State + } + + for _, opt := range options { + if sysConfigOption := opt.SystemConfigOption; sysConfigOption != nil { + sysConfigOption(&sysConfig) + } + } + + return sysConfig +} + +// faultDisputeSystemConfigBuilder id a SystemConfigBuilder that configures +// the system for use with the Fault Dispute System. +func faultDisputeSystemConfigBuilder(t *testing.T, options ...e2esys.SystemConfigOpt) e2esys.SystemConfig { + return faultproofs.GetFaultDisputeSystemConfigForEspresso(t, options) +} + +// WithFaultDisputeSystem will modify the default SysConfigBuilder utilized +// to be one that configures the FaultDisputeSsytem for Espresso. +func WithFaultDisputeSystem() E2eDevnetLauncherOption { + return func(launcherCtx *E2eDevnetLauncherContext) E2eSystemOption { + return E2eSystemOption{ + SysConfigBuilder: faultDisputeSystemConfigBuilder, + } + } +} + +// WithAltDa is an E2eDevnetLauncherOption that adjusts the SystemConfig +// to be configured for use as a Alt Da. +func WithAltDa() E2eDevnetLauncherOption { + return func(_ *E2eDevnetLauncherContext) E2eSystemOption { + return E2eSystemOption{ + SystemConfigOption: func(sysConfig *e2esys.SystemConfig) { + sysConfig.DeployConfig.UseAltDA = true + sysConfig.DeployConfig.DACommitmentType = "KeccakCommitment" + sysConfig.DeployConfig.DAChallengeWindow = 16 + sysConfig.DeployConfig.DAResolveWindow = 16 + sysConfig.DeployConfig.DABondSize = 1000000 + sysConfig.DeployConfig.DAResolverRefundPercentage = 0 + sysConfig.BatcherMaxPendingTransactions = 0 + sysConfig.BatcherBatchType = 0 + sysConfig.DataAvailabilityType = flags.CalldataType + }, + } + } +} + +// GetE2eDevnetStartOptions returns the start options for the E2E devnet. +func (l *EspressoDevNodeLauncherDocker) GetE2eDevnetStartOptions(originalCtx context.Context, t *testing.T, launchContext *E2eDevnetLauncherContext, options ...E2eDevnetLauncherOption) []e2esys.StartOption { + initialOptions := []E2eDevnetLauncherOption{ + launchEspressoDevNode(), + } + + allOptions := append(initialOptions, options...) + + startOptions := []e2esys.StartOption{} + + for _, opt := range allOptions { + options := opt(launchContext) + + if gethOption := options.GethOptions; gethOption != nil { + for k, v := range gethOption { + launchContext.SystemCfg.GethOptions[k] = append(launchContext.SystemCfg.GethOptions[k], v...) + } + } + + if startOption := options.StartOptions; startOption != nil { + startOptions = append(startOptions, startOption...) + } + } + + return startOptions +} + +func expandLauncherOptionsToSystemOptions(launchContext *E2eDevnetLauncherContext, options []E2eDevnetLauncherOption) []E2eSystemOption { + e2eSystemOption := make([]E2eSystemOption, 0, len(options)) + for _, opt := range options { + e2eSystemOption = append(e2eSystemOption, opt(launchContext)) + } + + return e2eSystemOption +} + +func (l *EspressoDevNodeLauncherDocker) StartE2eDevnet(ctx context.Context, t *testing.T, options ...E2eDevnetLauncherOption) (*e2esys.System, EspressoDevNode, error) { + launchContext := E2eDevnetLauncherContext{ + Ctx: ctx, + T: t, + SystemCfg: nil, + } + + e2eSystemOption := expandLauncherOptionsToSystemOptions(&launchContext, options) + + sysConfig := l.GetE2eDevnetSysConfig(ctx, t, e2eSystemOption...) + originalCtx := ctx + launchContext.SystemCfg = &sysConfig + + startOptions := l.GetE2eDevnetStartOptions(originalCtx, t, &launchContext, options...) + + // We want to run the espresso-dev-node. But we need it to be able to + // access the L1 node. + + system, err := sysConfig.Start( + t, + + startOptions..., + ) + if err != nil { + if system != nil { + // We don't want the system running in a partial / incomplete + // state. So we'll tell it to stop here, just in case. + system.Close() + } + + return system, nil, err + } + + // Auto System Cleanup tied to the passed in context. + { + // We want to ensure that the lifecycle of the system node is tied to + // the context we were given. So if the context is canceled, or + // otherwise closed, it will automatically clean up the system. + go (func(ctx context.Context) { + <-ctx.Done() + + // The system is guaranteed to not be null here. + system.Close() + })(originalCtx) + } + + // Back the EspressoDevNode with the in-memory mock client owned by the system. + if system.EspressoClient != nil { + launchContext.EspressoDevNode = &mockEspressoDevNode{client: system.EspressoClient} + } + + return system, launchContext.EspressoDevNode, launchContext.Error +} + +func SetBatcherKey(privateKey ecdsa.PrivateKey) E2eDevnetLauncherOption { + return func(ct *E2eDevnetLauncherContext) E2eSystemOption { + return E2eSystemOption{ + StartOptions: []e2esys.StartOption{ + { + Role: "set-batcher-key", + BatcherMod: func(c *batcher.CLIConfig, sys *e2esys.System) { + c.Espresso.TestingBatcherPrivateKey = &privateKey + }, + }, + }, + } + } +} + +// GetBatcherConfig snapshots the system batcher's fully-resolved CLIConfig into +// *c so the caller can later start an additional batcher with identical wiring. +// It does not modify the config; any launcher options that tweak the batcher +// config must be passed before this one so the snapshot reflects them. +func GetBatcherConfig(c *batcher.CLIConfig) E2eDevnetLauncherOption { + return func(ct *E2eDevnetLauncherContext) E2eSystemOption { + return E2eSystemOption{ + StartOptions: []e2esys.StartOption{ + { + Role: "get-batcher-config", + BatcherMod: func(cfg *batcher.CLIConfig, sys *e2esys.System) { + *c = *cfg + }, + }, + }, + } + } +} + +// SetEspressoUrls allows to set the list of urls for the Espresso client in such a way that N of them are "good" and M of them are "bad". +// Good urls are the urls defined by this test framework repeated M times. The bad url is provided to the function +// This function is introduced for testing purposes. It allows to check the enforcement of the majority rule (Test 12) +func SetEspressoUrls(numGood int, numBad int, badServerUrl string) E2eDevnetLauncherOption { + return func(ct *E2eDevnetLauncherContext) E2eSystemOption { + return E2eSystemOption{ + StartOptions: []e2esys.StartOption{ + { + BatcherMod: func(c *batcher.CLIConfig, sys *e2esys.System) { + goodUrl := c.Espresso.QueryServiceURLs[0] + var urls []string + + for i := 0; i < numGood; i++ { + urls = append(urls, goodUrl) + } + + for i := 0; i < numBad; i++ { + urls = append(urls, badServerUrl) + } + c.Espresso.QueryServiceURLs = urls + }, + }, + }, + } + } +} + +// SystemConfigOptionDisableBatcher is a SystemConfigOption that disables +// the Batcher. +// +// | NOTE: This doesn't actually stop the Batcher from being created entirely. +// +// Instead, it prevents the Batcher from "Starting". The Batcher still +// exists in the local context, it just won't be running initially. But +// it can still be started programatically via its API. This is most +// easily done by calling `StartBatchSubmitting` on the `TestDriver` of +// the system. +func SystemConfigOptionDisableBatcher(cfg *e2esys.SystemConfig) { + cfg.DisableBatcher = true +} + +// Config is a convenience function that allows for the initial modification +// of the SystemConfig only. +func Config(fn func(*e2esys.SystemConfig)) E2eDevnetLauncherOption { + return func(ct *E2eDevnetLauncherContext) E2eSystemOption { + return E2eSystemOption{ + SystemConfigOption: fn, + } + } +} + +// WithBatcherStoppedInitially is an E2eDevNetLauncherOption that ensures that +// the locally created Batcher is not running initially. +// +// The Batcher can still be started locally with a call to the TestDriver's +// method: `StartBatchSubmitting`. +func WithBatcherStoppedInitially() E2eDevnetLauncherOption { + return Config(SystemConfigOptionDisableBatcher) +} + +// launchEspressoDevNodeStartOption is E2eDevnetLauncherOption that launches the +// Espresso Dev Node within a Docker container. It also ensures that the +// Espresso Dev Node is actively producing blocks before returning. +func launchEspressoDevNodeStartOption(ct *E2eDevnetLauncherContext) e2esys.StartOption { + return e2esys.StartOption{ + Role: "launch-espresso-dev-node", + BatcherMod: func(c *batcher.CLIConfig, sys *e2esys.System) { + // Fail early if there was a prior setup failure. + if ct.Error != nil { + ct.T.Fatalf("devnet setup failed before espresso dev node could start: %v", ct.Error) + return + } + + // The in-memory mock Espresso client is created and injected into the + // batchers by e2esys.SystemConfig.Start (sys.EspressoClient). Here we only + // finish configuring the Espresso CLI config; the QueryServiceURLs placeholder + // is set by e2esys and is never dialed because the mock client overrides it. + c.Espresso.Enabled = true + c.LogConfig.Level = slog.LevelDebug + // The light client is not modeled by the in-memory mock. The batcher still + // requires a non-zero address to construct its LightclientCaller, but the + // streamer tolerates the resulting "no contract" error and continues without + // pinning the HotShot height. Use the configured address if present, else a + // fixed dummy so tests don't require ESPRESSO_SEQUENCER_LIGHT_CLIENT_PROXY_ADDRESS. + c.Espresso.LightClientAddr = mockLightClientAddr() + c.Espresso.AllowEmptyAttestationService() + }, + } +} + +// launchEspressoDevNode is an E2eDevnetLauncherOption that configures the system to +// use the in-memory mock Espresso client in place of a dockerized espresso-dev-node. +func launchEspressoDevNode() E2eDevnetLauncherOption { + return func(ct *E2eDevnetLauncherContext) E2eSystemOption { + return E2eSystemOption{ + StartOptions: []e2esys.StartOption{ + launchEspressoDevNodeStartOption(ct), + }, + } + } +} + +// StopConfig represents the configuration options for the Stop function. +// The configuration options help to define how the Stop function should +// to failure types. +type StopConfig struct { + IgnoreErrors bool + Ctx context.Context +} + +// StopOption is a functional option that allows for the modification of the +// Stop Config +type StopOption func(*StopConfig) + +// IgnoreStopErrors is a functional option that ignores errors encountered +// by the stop function, so that they do not cause test failure +func IgnoreStopErrors(c *StopConfig) { + c.IgnoreErrors = true +} + +// Stop is a convenience method to handle the graceful shutdown, and the errors +// thereof of any node that should be stopped on test exit. +// There are different type signatures for the shutdown methods, and this +// aims to handle each of them as gracefully as possible while still ensuring +// that any returned errors are handled accordingly. +func Stop(t *testing.T, toStop any, options ...StopOption) { + config := StopConfig{ + Ctx: context.Background(), + } + + for _, opt := range options { + opt(&config) + } + + ctx := config.Ctx + if cast, castOk := toStop.(interface{ Stop() error }); castOk { + if have, want := cast.Stop(), error(nil); have != want && !config.IgnoreErrors { + t.Fatalf("failed to stop node:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + + return + + } + + if cast, castOk := toStop.(interface{ Stop(context.Context) error }); castOk { + if have, want := cast.Stop(ctx), error(nil); have != want && !config.IgnoreErrors { + t.Fatalf("failed to stop node:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + + return + } + + if cast, castOk := toStop.(interface{ Close() }); castOk { + cast.Close() + return + } + + if cast, castOk := toStop.(interface{ Close(context.Context) }); castOk { + cast.Close(ctx) + return + } + + if cast, castOk := toStop.(interface{ Close(context.Context) error }); castOk { + if have, want := cast.Close(ctx), error(nil); have != want && !config.IgnoreErrors { + t.Fatalf("failed to stop node:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", have, want) + } + + return + } + + t.Fatalf("unable to determine how to stop the given node") +} + +// Waits for an Espresso transaction to be confirmed using its hash. +func WaitForEspressoTx(ctx context.Context, txHash *espressoCommon.TaggedBase64, espressoClient espressoClient.EspressoClient) error { + const transactionFetchTimeout = 4 * time.Second + const transactionFetchInterval = 100 * time.Millisecond + + timer := time.NewTimer(transactionFetchTimeout) + defer timer.Stop() + + ticker := time.NewTicker(transactionFetchInterval) + defer ticker.Stop() + + var err error + for { + select { + case <-ticker.C: + _, err := espressoClient.FetchTransactionByHash(ctx, txHash) + if err == nil { + return nil + } + case <-timer.C: + return fmt.Errorf("failed to fetch transaction by hash: %w", err) + case <-ctx.Done(): + return nil + } + } +} diff --git a/espresso/environment/query_service_intercept.go b/espresso/environment/query_service_intercept.go new file mode 100644 index 00000000000..6bc406a82f9 --- /dev/null +++ b/espresso/environment/query_service_intercept.go @@ -0,0 +1,499 @@ +package environment + +import ( + "bytes" + "context" + "encoding/json" + "io" + "net/http" + "net/http/httptest" + "net/url" + "regexp" + "time" + + espressoTaggedBase64 "github.com/EspressoSystems/espresso-network/sdks/go/tagged-base64" + espressoCommon "github.com/EspressoSystems/espresso-network/sdks/go/types" + "github.com/coder/websocket" + "github.com/ethereum-optimism/optimism/op-batcher/batcher" + "github.com/ethereum-optimism/optimism/op-e2e/system/e2esys" +) + +// InterceptHandleDecision is an enum that represents a decision on how to +// handle the http handler request for the specific request. +// +// It is meant to represent the specific behavior that the user would like to +// happen to a given request, without needing to worry about the implementation +// for how to make that behavior happen. +type InterceptHandleDecision int + +const ( + // DecisionProxy means that the request should be proxied unmodified to the + // target service (the Espresso Dev Node). + DecisionProxy InterceptHandleDecision = iota + + // DecisionReportSubmitSuccessWhileDropped means that the request should + // be handled by simulating a successful transaction submission, but + // without actually submitting the transaction to the Espresso Dev Node. + DecisionReportSubmitSuccessWhileDropped + + // DecisionReportServerUnreachable means that the request should be + // handled by returning an error indicating that the Espresso Dev Node + // was unreachable to the client. + DecisionReportServerUnreachable +) + +// builderHandler is a method that will build the appropriate HTTP handler based +// on the provided decision. +func (d InterceptHandleDecision) buildHandler(client *http.Client, baseURL url.URL) http.Handler { + switch d { + case DecisionProxy: + return &proxyRequest{ + client: client, + baseURL: baseURL, + } + + case DecisionReportSubmitSuccessWhileDropped: + return fakeSubmitTransactionSuccess{} + + case DecisionReportServerUnreachable: + return reportServerUnreachable{} + + default: + return nil + } +} + +// InterceptHandlerDecider is an interface that defines a method for +// deciding how it should handle a given HTTP request. +// +// The idea is to make it simple for the user to implement their own logic for +// how to determine how to handle a request without needing to worry about the +// implementation details of the proxying, or the specific handling cases of +// his / her desired behaviors. +type InterceptHandlerDecider interface { + DecideHowToHandleRequest(w http.ResponseWriter, r *http.Request) InterceptHandleDecision +} + +// defaultInterceptHandlerDecider is a simple implementation of the +// InterceptHandlerDecider interface that always returns a proxy decision. +type defaultInterceptHandlerDecider struct{} + +// DecideHowToHandleRequest implements InterceptHandlerDecider +func (defaultInterceptHandlerDecider) DecideHowToHandleRequest(w http.ResponseWriter, r *http.Request) InterceptHandleDecision { + return DecisionProxy +} + +// proxyRequest is a simple HTTP handler that proxies requests to the given +// baseURL, utilizing the given http.Client. +type proxyRequest struct { + client *http.Client + baseURL url.URL +} + +// ServeHTTP implements http.Handler +func (p *proxyRequest) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // Check if this is a websocket stream request + if isWebSocketStreamRequest(r) { + p.proxyWebSocket(w, r) + return + } + + // Handle regular HTTP requests + defer r.Body.Close() + buf := new(bytes.Buffer) + if _, err := io.Copy(buf, r.Body); err != nil && err != io.EOF { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + req, err := http.NewRequest(r.Method, p.baseURL.JoinPath(r.URL.Path).String(), buf) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Copy over the headers + for k, v := range r.Header { + req.Header.Set(k, v[0]) + } + + res, err := p.client.Do(req) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + defer res.Body.Close() + + buf.Reset() + if _, err := io.Copy(buf, res.Body); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.WriteHeader(res.StatusCode) + for k, v := range res.Header { + w.Header().Set(k, v[0]) + } + + // Write the proxy response contents + if _, err := io.Copy(w, buf); err != nil { + // If we encounter an error here, it will be difficult to actually + // handle it at this point, as we've already sent the response headers. + // + // The best we can do at this point, is log the error. + _ = err + return + } +} + +// proxyWebSocket handles websocket upgrade and proxying +func (p *proxyRequest) proxyWebSocket(w http.ResponseWriter, r *http.Request) { + // Accept the websocket connection from the client + clientConn, err := websocket.Accept(w, r, &websocket.AcceptOptions{ + InsecureSkipVerify: true, + }) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + defer clientConn.Close(websocket.StatusInternalError, "proxy error") + + // Create websocket URL for the backend + backendURL := p.baseURL + if backendURL.Scheme == "https" { + backendURL.Scheme = "wss" + } else { + backendURL.Scheme = "ws" + } + backendURL.Path = r.URL.Path + backendURL.RawQuery = r.URL.RawQuery + + // Connect to the backend websocket + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + //nolint:bodyclose // Not applicable to coder/websocket. From the websocket.Dial docs: "You never need to close resp.Body yourself." + backendConn, _, err := websocket.Dial(ctx, backendURL.String(), &websocket.DialOptions{}) + if err != nil { + clientConn.Close(websocket.StatusInternalError, "backend connection failed") + return + } + defer backendConn.Close(websocket.StatusNormalClosure, "") + + // Proxy messages bidirectionally + ctx, cancel = context.WithCancel(context.Background()) + defer cancel() + + // Client to backend + go func() { + defer cancel() + for { + msgType, data, err := clientConn.Read(ctx) + if err != nil { + return + } + err = backendConn.Write(ctx, msgType, data) + if err != nil { + return + } + } + }() + + // Backend to client + go func() { + defer cancel() + for { + msgType, data, err := backendConn.Read(ctx) + if err != nil { + return + } + err = clientConn.Write(ctx, msgType, data) + if err != nil { + return + } + } + }() + + // Wait until context is cancelled (one of the goroutines finished) + <-ctx.Done() + + // Close connections gracefully + clientConn.Close(websocket.StatusNormalClosure, "") + backendConn.Close(websocket.StatusNormalClosure, "") +} + +// fakeSubmitTransactionSuccess is a simple HTTP handler that simulates a +// successful transaction submission by returning a fake commit hash. +type fakeSubmitTransactionSuccess struct{} + +// generateCommitForSubmitTransaction generates a commit hash for the +// transaction in the request body. This is a fake implementation that +// simulates a successful transaction submission by returning a commit hash +// that won't collide with the real transaction commit hashes. +func generateCommitForSubmitTransaction(r *http.Request) (*espressoTaggedBase64.TaggedBase64, error) { + defer r.Body.Close() + + var txn espressoCommon.Transaction + if err := json.NewDecoder(r.Body).Decode(&txn); err != nil { + // Unable to decode, this is a problem? + var emptyHash [32]byte + return espressoTaggedBase64.New("FAKE", emptyHash[:]) + } + + commit := txn.Commit() + return espressoTaggedBase64.New("FAKE", commit[:]) +} + +// ServeHTTP implements http.Handler +func (fakeSubmitTransactionSuccess) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // We could do a lot of effort to validate the request, and return a + // hash that is actually representative of the transaction that was + // just submitted. In some cases we may actually want this sort of + // validated behavior, but it's very simple to just return any hash + // instead. + + // We should probably validate the request contents and format here, but + // we will just assume the settings. + defer r.Body.Close() + hash, err := generateCommitForSubmitTransaction(r) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + contents, err := json.Marshal(hash) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.WriteHeader(http.StatusOK) + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write(contents) +} + +// reportServerUnreachable is a simple HTTP handler that simulates a load +// balancer, or some other intermediary, returning an error indicating that the +// target handling service is unreachable. +type reportServerUnreachable struct{} + +// ServeHTTP implements http.Handler +func (reportServerUnreachable) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // Don't forget to close the request body, though we won't actually read + // anything from it. + defer r.Body.Close() + + http.Error(w, "service unreachable", http.StatusServiceUnavailable) +} + +// EspressoDevNodeIntercept is a struct that is a Proxy to the Espresso Dev Node. +// It is used to intercept request to the Espresso Dev Node and make decisions +// about handling the requests. This is useful for simulating failures or +// bad behaviors for Espresso. +type EspressoDevNodeIntercept struct { + u url.URL + client *http.Client + decider InterceptHandlerDecider +} + +type Rng interface { + Intn(n int) int +} + +// randomRollFakeSubmitTransactionSuccess is a InterceptHandlerDecider that aids +// in the simulation of various transaction submission failures by randomly +// deciding whether to return a successful submission response, to return that +// the service is unavailable or to proxy the request to the Espresso Dev Node. +type randomRollFakeSubmitTransactionSuccess struct { + // n the upper end of the range to roll against. + n int + + // The fakeSuccessThreshold, under which will trigger a faked submission + // success. + fakeSuccessThreshold int + + // fakeServiceUnavailableThreshold is the threshold under which the + // decision will return a simulated service unavailable error. + fakeServiceUnavailableThreshold int + + // the Rng to use to determine the random roll + r Rng +} + +// NewRandomRollFakeSubmitTransactionSuccess creates a new +// InterceptHandlerDecider that will proxy all requests to the espresso dev +// node except for the submit transaction requests. +// +// When a submit transaction request is received it will use the provided Rng +// roll a number between 0 and n-1. +// Depending on the value rolled it will determine the resulting behavior as +// follows: +// - If the number rolled is less than or equal to the given +// fakeSuccessThreshold, it will return a simulated successful transaction +// submission response, while dropping the request ensuring it does not +// actually reach the Espresso Dev Node. +// - If the number rolled is less than or equal to the given +// fakeServiceUnavailableThreshold, it will return a simulated service +// unavailable error response. +// - Otherwise, it will proxy the request to the Espresso Dev Node. +// +// NOTE: We only roll once per request, so the thresholds are not cumulative, +// This means if they overlap, then one of the behaviors will never be +// triggered. However, you can utilize this to your advantage by setting +// the thresholds so that they overlap directly, ensuring you only test +// one of the behaviors. +// +// NOTE: Setting the `fakeSuccessThreshold` value less than `0` will ensure +// that the fake success threshold case is never triggered. +// +// NOTE: Setting the `fakeServiceUnavailableThreshold` value less than or +// equal to `fakeSuccessThreshold` will ensure that the service unavailable +// threshold case is never triggered. +// +// The thresholds are evaluated in order of `fakeSuccessThreshold`, then +// `fakeServiceUnavailableThreshold`, then the default proxy behavior. +// So if you want to ensure that all cases are tested you should specify your +// values with the following constraints: +// - `fakeSuccessThreshold` >= 0 +// - `fakeServiceUnavailableThreshold` > `fakeSuccessThreshold` +// - `fakeServiceUnavailableThreshold` < `n` +func NewRandomRollFakeSubmitTransactionSuccess( + rollUpperRange, + fakeSuccessThreshold, + fakeServiceUnavailableThreshold int, + r Rng, +) InterceptHandlerDecider { + return &randomRollFakeSubmitTransactionSuccess{ + n: rollUpperRange, + fakeSuccessThreshold: fakeSuccessThreshold, + fakeServiceUnavailableThreshold: fakeServiceUnavailableThreshold, + r: r, + } +} + +// requestMatchesPath checks if the HTTP request matches the specified method +func requestMatchesPath(r *http.Request, method string, pathMatcher func(string) bool) bool { + return r.Method == method && r.URL != nil && pathMatcher(r.URL.Path) +} + +// stringEquals is a helper function that returns a function that checks if +// a given path string equals the specified string. +func stringEquals(s string) func(string) bool { + return func(path string) bool { + return path == s + } +} + +// isSubmitTransactionRequest represents the different variations of the submit +// transaction endpoint that we can utilize or support. +func isSubmitTransactionRequest(r *http.Request) bool { + return requestMatchesPath(r, http.MethodPost, stringEquals("/submit/submit")) || + requestMatchesPath(r, http.MethodPost, stringEquals("/v0/submit/submit")) +} + +// isWebSocketStreamRequest checks if the request is a websocket request +// matching the pattern "vN/stream/*" where N is an integer. +func isWebSocketStreamRequest(r *http.Request) bool { + matched, _ := regexp.MatchString(`/stream/`, r.URL.Path) + return matched +} + +// DecideHowToHandleRequest implements InterceptHandlerDecider +func (d *randomRollFakeSubmitTransactionSuccess) DecideHowToHandleRequest(w http.ResponseWriter, r *http.Request) InterceptHandleDecision { + if isSubmitTransactionRequest(r) { + // We want to randomly simulate a failure in the transaction + // submission. We compare our random roll against our thresholds in + // order to return the appropriate decision for how to handle the + // request. + roll := d.r.Intn(d.n) + if roll <= d.fakeSuccessThreshold { + return DecisionReportSubmitSuccessWhileDropped + } else if roll <= d.fakeServiceUnavailableThreshold { + return DecisionReportServerUnreachable + } + } + return DecisionProxy +} + +// ServerHTTP implements http.Handler +func (e *EspressoDevNodeIntercept) ServeHTTP(w http.ResponseWriter, r *http.Request) { + decision := e.decider.DecideHowToHandleRequest(w, r) + handler := decision.buildHandler(e.client, e.u) + handler.ServeHTTP(w, r) +} + +// createEspressoProxyOption will return a Batch CLIConfig option that will +// replace the Espresso URL with the URL of the proxy server. +func createEspressoProxyOption(ctx *E2eDevnetLauncherContext, proxy *EspressoDevNodeIntercept, server *httptest.Server) func(*batcher.CLIConfig, *e2esys.System) { + return func(cfg *batcher.CLIConfig, sys *e2esys.System) { + if ctx.Error != nil { + return + } + + if len(cfg.Espresso.QueryServiceURLs) == 0 { + // This should be being called after the Espresso + // Dev Node is Already Live. + // Without an Espresso URL, we cannot proceed. + return + } + + u, err := url.Parse(cfg.Espresso.QueryServiceURLs[0]) + if err != nil || u == nil { + // We encountered an error + ctx.Error = err + return + } + + // Set the proxy + proxy.u = *u + // Replace the Espresso URL with the proxy URL + // We need to provide at least 2 URLs to create a valid multiple nodes client + cfg.Espresso.QueryServiceURLs = []string{server.URL, server.URL} + } +} + +// EspressoDevNodeInterceptOption is a function that modifies the +// EspressoDevNodeIntercept configuration. +type EspressoDevNodeInterceptOption func(*EspressoDevNodeIntercept) + +// SetDecider sets the InterceptHandlerDecider for the EspressoDevNodeIntercept. +func SetDecider(decider InterceptHandlerDecider) EspressoDevNodeInterceptOption { + return func(e *EspressoDevNodeIntercept) { + e.decider = decider + } +} + +// SetHTTPClient sets the HTTP client for the EspressoDevNodeIntercept. +func SetHTTPClient(client *http.Client) EspressoDevNodeInterceptOption { + return func(e *EspressoDevNodeIntercept) { + e.client = client + } +} + +// SetupQueryServiceIntercept sets up an intercept traffic headed for the +// Query Service for the Espresso Dev Node +func SetupQueryServiceIntercept(options ...EspressoDevNodeInterceptOption) (*EspressoDevNodeIntercept, *httptest.Server, E2eDevnetLauncherOption) { + // Start a Server to proxy requests to Espresso + proxy := &EspressoDevNodeIntercept{ + client: http.DefaultClient, + decider: defaultInterceptHandlerDecider{}, + } + + for _, opt := range options { + opt(proxy) + } + + // Start up a local http server to handle the requests + server := httptest.NewServer(proxy) + + return proxy, server, func(ctx *E2eDevnetLauncherContext) E2eSystemOption { + return E2eSystemOption{ + StartOptions: []e2esys.StartOption{ + { + Key: "espresso-proxy", + BatcherMod: createEspressoProxyOption(ctx, proxy, server), + }, + }, + } + } +} diff --git a/espresso/environment/tx_helpers.go b/espresso/environment/tx_helpers.go new file mode 100644 index 00000000000..5e3a1947bf4 --- /dev/null +++ b/espresso/environment/tx_helpers.go @@ -0,0 +1,235 @@ +package environment + +import ( + "context" + "crypto/ecdsa" + "fmt" + "math/big" + "testing" + "time" + + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/require" + + "github.com/ethereum-optimism/optimism/op-e2e/system/e2esys" + "github.com/ethereum-optimism/optimism/op-e2e/system/helpers" + "github.com/ethereum/go-ethereum/ethclient" +) + +// runSimpleL2Transfer runs a simple L2 burn transaction and verifies it on the +// L2 Verifier. +func RunSimpleL2Transfer( + ctx context.Context, + t *testing.T, + system *e2esys.System, + nonce uint64, + amount big.Int, + l2Seq *ethclient.Client, +) common.Hash { + _, cancel := context.WithTimeout(ctx, 2*time.Minute) + defer cancel() + + privateKey := system.Cfg.Secrets.Bob + + t.Log("Sending tx", "nonce", nonce) + + destAddress := system.Cfg.Secrets.Addresses().Alice + + receipt := helpers.SendL2TxWithID(t, system.Cfg.L2ChainIDBig(), l2Seq, privateKey, func(opts *helpers.TxOpts) { + opts.Nonce = nonce + opts.ToAddr = &destAddress + opts.Value = &amount + }) + + t.Log("Receipt", receipt) + + txHash := receipt.TxHash + + return txHash +} + +// runSimpleL1TransferAndVerifier runs a simple L1 transfer and verifies it on +// the L2 Verifier. +func RunSimpleL1TransferAndVerifier(ctx context.Context, t *testing.T, system *e2esys.System) { + privateKey := system.Cfg.Secrets.Bob + + l1Client := system.NodeClient(e2esys.RoleL1) + l2Verif := system.NodeClient(e2esys.RoleVerif) + + fromAddress := system.Cfg.Secrets.Addresses().Bob + + // Send Transaction on L1, and wait for verification on the L2 Verifier + ctx, cancel := context.WithTimeout(ctx, 2*time.Minute) + defer cancel() + + // Get the Starting Balance of the Address + startBalance, err := l2Verif.BalanceAt(ctx, fromAddress, nil) + if have, want := err, error(nil); have != want { + t.Errorf("attempt to get starting balance for %s failed:\nhave:\n\t\"%v\"\nwant:\n\t\"%v\"\n", fromAddress, have, want) + } + + // Create a new Keyed Transaction + options, err := bind.NewKeyedTransactorWithChainID(privateKey, system.Cfg.L1ChainIDBig()) + require.NoError(t, err, "failed to create keyed transaction with chain ID %d", system.Cfg.L1ChainIDBig()) + + // Send a Deposit Transaction + mintAmount := big.NewInt(1_000_000_000_000) + options.Value = mintAmount + _ = helpers.SendDepositTx(t, system.Cfg, l1Client, l2Verif, options, nil) + + endBalance, err := wait.ForBalanceChange(ctx, l2Verif, fromAddress, startBalance) + require.NoError(t, err, "waiting for balance change failed") + + diff := new(big.Int).Sub(endBalance, startBalance) + require.Equal(t, diff, mintAmount, "balance change does not match mint amount") + + cancel() +} + +// RunSimpleL2Burn runs a simple L2 burn transaction and verifies it on the +// L2 Verifier with a 2-minute timeout. +func RunSimpleL2Burn(ctx context.Context, t *testing.T, system *e2esys.System) { + RunSimpleL2BurnWithTimeout(ctx, t, system, 2*time.Minute) +} + +// RunSimpleL2BurnWithTimeout runs a simple L2 burn and verifies on the verifier, +// using the given timeout for the overall operation. Use a longer timeout (e.g. 5*time.Minute) +// when the verifier may be slow to derive, e.g. after a batcher switch. +func RunSimpleL2BurnWithTimeout(ctx context.Context, t *testing.T, system *e2esys.System, timeout time.Duration) { + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + l2Seq := system.NodeClient(e2esys.RoleSeq) + l2Verif := system.NodeClient(e2esys.RoleVerif) + + senderKey := system.Cfg.Secrets.Bob + senderAddress := system.Cfg.Secrets.Addresses().Bob + amountToBurn := big.NewInt(1234) + burnAddress := common.Address{0xff, 0xff} + + nonce, err := l2Seq.NonceAt(ctx, senderAddress, nil) + require.NoError(t, err, "failed to get nonce for account %s", senderAddress) + + initialBurnAddressBalance, err := l2Seq.BalanceAt(ctx, burnAddress, nil) + require.NoError(t, err, "failed to get initial balance for burn address %s", burnAddress) + + // Use the ctx-scoped sender (not helpers.SendL2TxWithID, which hardcodes its + // own 30s timeout) so the caller's timeout governs the verifier-receipt wait; + // after a batcher switch the verifier can take longer than 30s to derive. + sendL2TxAndVerify( + ctx, + t, + system.Cfg.L2ChainIDBig(), + l2Seq, + senderKey, + L2TxWithOptions( + L2TxWithAmount(amountToBurn), + L2TxWithNonce(nonce), + L2TxWithToAddress(&burnAddress), + L2TxWithVerifyOnClients(l2Verif), + ), + ) + + // Check the balance of hte burn address using the L2 Verifier + burnAddressBalance, err := wait.ForBalanceChange(ctx, l2Verif, burnAddress, initialBurnAddressBalance) + require.NoError(t, err, "burn address balance didn't change") + + // Make sure that these match + require.Equal(t, new(big.Int).Sub(burnAddressBalance, initialBurnAddressBalance), amountToBurn, "burn address balance doesn't match the amount burned") + + cancel() +} + +// RunSimpleMultiTransactions sends numTransactions simple L2 transactions +// from Bob's account and returns the receipts. +// +// This is all attempted in parallel, as it will spawn a separate goroutine +// for each transaction submission. Each transaction will be provided its +// own nonce, based on the currently understood value of the nonce for +// Bob. +// +// This will return once all receipts have been returned. +func RunSimpleMultiTransactions(ctx context.Context, t *testing.T, system *e2esys.System, numTransactions int) ([]*types.Receipt, error) { + ctx, cancel := context.WithTimeoutCause(ctx, 2*time.Minute, fmt.Errorf("failed to submit all transactions within time frame: %w", context.DeadlineExceeded)) + defer cancel() + + senderKey := system.Cfg.Secrets.Bob + senderAddress := system.Cfg.Secrets.Addresses().Bob + l2Seq := system.NodeClient(e2esys.RoleSeq) + nonce, err := l2Seq.NonceAt(ctx, senderAddress, nil) + if err != nil { + require.NoError(t, err, "failed to get nonce for account %s", senderAddress) + } + + ch := make(chan *types.Receipt, numTransactions) + defer close(ch) + for i := range numTransactions { + go (func(ch chan *types.Receipt, i int, nonce uint64) { + receipt := helpers.SendL2Tx(t, system.Cfg, l2Seq, senderKey, func(opts *helpers.TxOpts) { + opts.Nonce = nonce + uint64(i) + // We need to explicitly increase the gas beyond some threshold + // for an unknown reason. We'll set it high enough so that + // it hopefully won't cause a problem + opts.Gas = 100_000 + }) + ch <- receipt + })(ch, i, nonce) + } + + var receipts []*types.Receipt + for range numTransactions { + select { + case <-ctx.Done(): + return receipts, ctx.Err() + case receipt := <-ch: + receipts = append(receipts, receipt) + } + } + return receipts, nil +} + +// sendL2TxAndVerify is an Espresso-local variant of helpers.SendL2TxWithID that +// honours the supplied ctx for the receipt waits instead of imposing its own +// fixed 30s deadline. The Espresso batcher-switch tests need a longer window +// because the verifier can lag well past 30s while it re-derives after a switch. +// Behaviour is otherwise identical: it signs and sends the tx, waits for an OK +// receipt on l2Client, asserts the expected status, and verifies the same +// receipt on every TxOpts.VerifyClients client. +func sendL2TxAndVerify(ctx context.Context, t *testing.T, chainID *big.Int, l2Client *ethclient.Client, privKey *ecdsa.PrivateKey, applyTxOpts helpers.TxOptsFn) *types.Receipt { + opts := &helpers.TxOpts{ + Value: common.Big0, + GasTipCap: big.NewInt(10), + GasFeeCap: big.NewInt(200), + Gas: 21_000, + ExpectedStatus: types.ReceiptStatusSuccessful, + } + applyTxOpts(opts) + + tx := types.MustSignNewTx(privKey, types.LatestSignerForChainID(chainID), &types.DynamicFeeTx{ + ChainID: chainID, + Nonce: opts.Nonce, + To: opts.ToAddr, + Value: opts.Value, + GasTipCap: opts.GasTipCap, + GasFeeCap: opts.GasFeeCap, + Gas: opts.Gas, + Data: opts.Data, + }) + + require.NoError(t, l2Client.SendTransaction(ctx, tx), "Sending L2 tx") + + receipt, err := wait.ForReceiptOK(ctx, l2Client, tx.Hash()) + require.NoError(t, err, "Waiting for L2 tx") + require.Equal(t, opts.ExpectedStatus, receipt.Status, "TX should have expected status") + + for i, client := range opts.VerifyClients { + t.Logf("Waiting for tx %v on verification client %d", tx.Hash(), i) + receiptVerif, err := wait.ForReceiptOK(ctx, client, tx.Hash()) + require.NoErrorf(t, err, "Waiting for L2 tx on verification client %d", i) + require.Equalf(t, receipt, receiptVerif, "Receipts should be the same on sequencer and verification client %d", i) + } + return receipt +} diff --git a/espresso/mock_client.go b/espresso/mock_client.go new file mode 100644 index 00000000000..3df507061e9 --- /dev/null +++ b/espresso/mock_client.go @@ -0,0 +1,264 @@ +package espresso + +import ( + "context" + "encoding/json" + "fmt" + "sync" + "time" + + espressoClient "github.com/EspressoSystems/espresso-network/sdks/go/client" + espressoTaggedBase64 "github.com/EspressoSystems/espresso-network/sdks/go/tagged-base64" + espressoTypes "github.com/EspressoSystems/espresso-network/sdks/go/types" + espressoTypesV0_3 "github.com/EspressoSystems/espresso-network/sdks/go/types/v0/v0_3" +) + +// MockEspressoClient is an in-memory implementation of the Espresso SDK's +// client.EspressoClient interface used in the e2e tests in place of a real +// dockerized espresso-dev-node. +// +// It models a HotShot chain as an append-only sequence of blocks. Submitted +// transactions are appended to the pending block; a background ticker seals the +// pending block (whether or not it contains transactions) so the block height +// advances continuously, matching how the batcher's verification logic measures +// elapsed blocks. The read path (FetchNamespaceTransactionsInRange / +// FetchTransactionByHash) round-trips the submitted payloads back to the +// streamer, which performs no cryptographic verification of HotShot data. +// +// Only the methods the batcher and streamer use are functional; the remaining +// QueryService methods are stubs. +type MockEspressoClient struct { + mu sync.Mutex + // sealed blocks, index = block height. Each block is the list of txs it contains. + blocks [][]espressoTypes.Transaction + // transactions still accumulating into the next (unsealed) block. + pending []espressoTypes.Transaction + // txByHash maps a submitted transaction's hash string to its stored copy. + txByHash map[string]storedTx + + blockTime time.Duration + stop chan struct{} + stopped bool +} + +type storedTx struct { + tx espressoTypes.Transaction + hash *espressoTypes.TaggedBase64 + blockHeight uint64 + index uint64 +} + +var _ espressoClient.EspressoClient = (*MockEspressoClient)(nil) + +// NewMockEspressoClient creates a mock Espresso client and starts its block +// production ticker. Call Close to stop it. blockTime controls how often an +// (empty or non-empty) block is sealed. +func NewMockEspressoClient(blockTime time.Duration) *MockEspressoClient { + if blockTime <= 0 { + blockTime = 250 * time.Millisecond + } + c := &MockEspressoClient{ + txByHash: make(map[string]storedTx), + blockTime: blockTime, + stop: make(chan struct{}), + } + // Seal an initial genesis block so the height starts at 1, mirroring a live + // node that always has at least the genesis block available. + c.blocks = append(c.blocks, nil) + go c.produceBlocks() + return c +} + +// Close stops the block-production ticker. +func (c *MockEspressoClient) Close() { + c.mu.Lock() + defer c.mu.Unlock() + if c.stopped { + return + } + c.stopped = true + close(c.stop) +} + +func (c *MockEspressoClient) produceBlocks() { + ticker := time.NewTicker(c.blockTime) + defer ticker.Stop() + for { + select { + case <-c.stop: + return + case <-ticker.C: + c.sealBlock() + } + } +} + +// sealBlock moves the pending transactions into a new sealed block, advancing +// the height. Empty blocks are sealed too so the height keeps advancing. +func (c *MockEspressoClient) sealBlock() { + c.mu.Lock() + defer c.mu.Unlock() + height := uint64(len(c.blocks)) + block := c.pending + c.pending = nil + c.blocks = append(c.blocks, block) + for i := range block { + // index of this tx within the just-sealed block + st := storedTx{ + tx: block[i], + blockHeight: height, + index: uint64(i), + } + hashStr, err := transactionHashString(block[i]) + if err != nil { + continue + } + if existing, ok := c.txByHash[hashStr]; ok { + existing.blockHeight = height + existing.index = uint64(i) + c.txByHash[hashStr] = existing + st.hash = existing.hash + } + } +} + +// transactionHashString derives a stable, opaque hash string for a transaction. +// The exact value is not consensus-meaningful for the mock; it only needs to be +// stable for a given transaction so submit and FetchTransactionByHash agree. +func transactionHashString(tx espressoTypes.Transaction) (string, error) { + commit := tx.Commit() + tb, err := espressoTaggedBase64.New("TX", commit[:]) + if err != nil { + return "", err + } + return tb.String(), nil +} + +func transactionHash(tx espressoTypes.Transaction) (*espressoTypes.TaggedBase64, error) { + commit := tx.Commit() + return espressoTaggedBase64.New("TX", commit[:]) +} + +// SubmitTransaction appends the transaction to the pending block and returns its hash. +func (c *MockEspressoClient) SubmitTransaction(ctx context.Context, tx espressoTypes.Transaction) (*espressoTypes.TaggedBase64, error) { + hash, err := transactionHash(tx) + if err != nil { + return nil, fmt.Errorf("%w: %w", espressoClient.ErrPermanent, err) + } + c.mu.Lock() + defer c.mu.Unlock() + c.pending = append(c.pending, tx) + c.txByHash[hash.String()] = storedTx{tx: tx, hash: hash} + return hash, nil +} + +// FetchLatestBlockHeight returns the current sealed block height. +func (c *MockEspressoClient) FetchLatestBlockHeight(ctx context.Context) (uint64, error) { + c.mu.Lock() + defer c.mu.Unlock() + return uint64(len(c.blocks)), nil +} + +// FetchNamespaceTransactionsInRange returns the transactions in blocks [from, until) +// matching the requested namespace, one NamespaceTransactionsRangeData per block. +func (c *MockEspressoClient) FetchNamespaceTransactionsInRange(ctx context.Context, from uint64, until uint64, namespace uint64) ([]espressoTypes.NamespaceTransactionsRangeData, error) { + c.mu.Lock() + defer c.mu.Unlock() + if until <= from { + return nil, nil + } + height := uint64(len(c.blocks)) + if until > height { + until = height + } + res := make([]espressoTypes.NamespaceTransactionsRangeData, 0, until-from) + for h := from; h < until; h++ { + var nsTxs []espressoTypes.Transaction + for _, tx := range c.blocks[h] { + if tx.Namespace == namespace { + nsTxs = append(nsTxs, tx) + } + } + res = append(res, espressoTypes.NamespaceTransactionsRangeData{ + Transactions: nsTxs, + }) + } + return res, nil +} + +// FetchTransactionByHash returns the stored transaction for the given hash, erroring +// if it has not been submitted. +func (c *MockEspressoClient) FetchTransactionByHash(ctx context.Context, hash *espressoTypes.TaggedBase64) (espressoTypes.TransactionQueryData, error) { + if hash == nil { + return espressoTypes.TransactionQueryData{}, fmt.Errorf("%w: hash is nil", espressoClient.ErrPermanent) + } + c.mu.Lock() + defer c.mu.Unlock() + st, ok := c.txByHash[hash.String()] + if !ok { + return espressoTypes.TransactionQueryData{}, fmt.Errorf("%w: transaction not found", espressoClient.ErrEphemeral) + } + return espressoTypes.TransactionQueryData{ + Transaction: st.tx, + Hash: st.hash, + Index: st.index, + BlockHeight: st.blockHeight, + }, nil +} + +// ---- Stubbed QueryService methods (not used by the batcher or streamer) ---- + +func (c *MockEspressoClient) FetchHeaderByHeight(ctx context.Context, height uint64) (espressoTypes.HeaderImpl, error) { + return espressoTypes.HeaderImpl{}, fmt.Errorf("%w: FetchHeaderByHeight not supported by mock", espressoClient.ErrPermanent) +} + +func (c *MockEspressoClient) FetchRawHeaderByHeight(ctx context.Context, height uint64) (json.RawMessage, error) { + return nil, fmt.Errorf("%w: FetchRawHeaderByHeight not supported by mock", espressoClient.ErrPermanent) +} + +// FetchHeadersByRange returns one header per sealed block in [from, until). +func (c *MockEspressoClient) FetchHeadersByRange(ctx context.Context, from uint64, until uint64) ([]espressoTypes.HeaderImpl, error) { + c.mu.Lock() + defer c.mu.Unlock() + if until <= from { + return nil, nil + } + height := uint64(len(c.blocks)) + if until > height { + until = height + } + res := make([]espressoTypes.HeaderImpl, 0, until-from) + for h := from; h < until; h++ { + res = append(res, espressoTypes.HeaderImpl{ + Header: &espressoTypesV0_3.Header{ + Height: h, + L1Finalized: &espressoTypes.L1BlockInfo{Number: 0}, + }, + }) + } + return res, nil +} + +func (c *MockEspressoClient) FetchTransactionsInBlock(ctx context.Context, blockHeight uint64, namespace uint64) (espressoClient.TransactionsInBlock, error) { + return espressoClient.TransactionsInBlock{}, fmt.Errorf("%w: FetchTransactionsInBlock not supported by mock", espressoClient.ErrPermanent) +} + +func (c *MockEspressoClient) FetchVidCommonByHeight(ctx context.Context, blockHeight uint64) (espressoTypes.VidCommon, error) { + return espressoTypes.VidCommon{}, fmt.Errorf("%w: FetchVidCommonByHeight not supported by mock", espressoClient.ErrPermanent) +} + +func (c *MockEspressoClient) FetchExplorerTransactionByHash(ctx context.Context, hash *espressoTypes.TaggedBase64) (espressoTypes.ExplorerTransactionQueryData, error) { + return espressoTypes.ExplorerTransactionQueryData{}, fmt.Errorf("%w: FetchExplorerTransactionByHash not supported by mock", espressoClient.ErrPermanent) +} + +func (c *MockEspressoClient) StreamPayloads(ctx context.Context, height uint64) (espressoClient.Stream[espressoTypes.PayloadQueryData], error) { + return nil, fmt.Errorf("%w: StreamPayloads not supported by mock", espressoClient.ErrPermanent) +} + +func (c *MockEspressoClient) StreamTransactions(ctx context.Context, height uint64) (espressoClient.Stream[espressoTypes.TransactionQueryData], error) { + return nil, fmt.Errorf("%w: StreamTransactions not supported by mock", espressoClient.ErrPermanent) +} + +func (c *MockEspressoClient) StreamTransactionsInNamespace(ctx context.Context, height uint64, namespace uint64) (espressoClient.Stream[espressoTypes.TransactionQueryData], error) { + return nil, fmt.Errorf("%w: StreamTransactionsInNamespace not supported by mock", espressoClient.ErrPermanent) +} diff --git a/op-batcher/batcher/espresso_driver.go b/op-batcher/batcher/espresso_driver.go index f964c6cd258..64988093141 100644 --- a/op-batcher/batcher/espresso_driver.go +++ b/op-batcher/batcher/espresso_driver.go @@ -27,7 +27,7 @@ import ( // fallback batcher's ChainSigner/SequencerAddress, which are always populated // by applyEspressoDriverSetup. type EspressoDriverSetup struct { - Client *espressoClient.MultipleNodesClient + Client espressoClient.EspressoClient LightClient *espressoLightClient.LightclientCaller ChainSigner opcrypto.ChainSigner SequencerAddress common.Address diff --git a/op-batcher/batcher/espresso_service.go b/op-batcher/batcher/espresso_service.go index baf99c89e1e..110d4565ae2 100644 --- a/op-batcher/batcher/espresso_service.go +++ b/op-batcher/batcher/espresso_service.go @@ -104,8 +104,7 @@ func (bs *BatcherService) initKeyPair() error { // initEspresso configures the Espresso TEE-batcher integration on the // BatcherService. When --espresso.enabled is false this is a no-op (the -// fallback batcher gets its own FallbackAuthLeadTime knob from -// BatcherConfig). When enabled, it wires up the Espresso query-service +// fallback batcher needs no Espresso runtime state). When enabled, it wires up the Espresso query-service // client, light client, ephemeral key pair, and Nitro Enclave attestation // (if running in TEE). func (bs *BatcherService) initEspresso(cfg *CLIConfig) error { diff --git a/op-batcher/batcher/service.go b/op-batcher/batcher/service.go index 01d39f7d3a4..f319ce61947 100644 --- a/op-batcher/batcher/service.go +++ b/op-batcher/batcher/service.go @@ -94,7 +94,7 @@ type BatcherService struct { // Espresso runtime state. Defined in espresso_service.go to keep the // upstream Optimism field block compact. EspressoClient and // EspressoLightClient are nil when --espresso.enabled=false. - EspressoClient *espressoClient.MultipleNodesClient + EspressoClient espressoClient.EspressoClient EspressoLightClient *espressoLightClient.LightclientCaller opcrypto.ChainSigner Attestation []byte @@ -102,6 +102,16 @@ type BatcherService struct { type DriverSetupOption func(setup *DriverSetup) +// WithEspressoClientOverride returns a DriverSetupOption that replaces the Espresso +// query-service client built from --espresso.query-service-urls with the provided +// client. It is intended for tests that inject an in-memory Espresso fake in place of +// a real espresso-dev-node; production code never sets it. +func WithEspressoClientOverride(client espressoClient.EspressoClient) DriverSetupOption { + return func(setup *DriverSetup) { + setup.Espresso.Client = client + } +} + // BatcherServiceFromCLIConfig creates a new BatcherService from a CLIConfig. // The service components are fully started, except for the driver, // which will not be submitting batches (if it was configured to) until the Start part of the lifecycle. diff --git a/op-chain-ops/genesis/config.go b/op-chain-ops/genesis/config.go index abb37483c5f..4958d34899d 100644 --- a/op-chain-ops/genesis/config.go +++ b/op-chain-ops/genesis/config.go @@ -406,6 +406,12 @@ type UpgradeScheduleDeployConfig struct { // Set it to 0 to activate at genesis. Nil to disable the PectraBlobSchedule fix. L2GenesisPectraBlobScheduleTimeOffset *hexutil.Uint64 `json:"l2GenesisPectraBlobScheduleTimeOffset,omitempty"` + // L2GenesisEspressoTimeOffset is the number of seconds after genesis block that the Espresso + // upgrade activates. Set it to 0 to activate at genesis. Nil to disable Espresso (in which case + // the chain runs as upstream Optimism). Espresso is not a core OP Stack fork, so it is excluded + // from ForkTimeOffset/SetForkTimeOffset and the fork-iteration helpers. + L2GenesisEspressoTimeOffset *hexutil.Uint64 `json:"l2GenesisEspressoTimeOffset,omitempty"` + // When Cancun activates. Relative to L1 genesis. L1CancunTimeOffset *hexutil.Uint64 `json:"l1CancunTimeOffset,omitempty"` // When Prague activates. Relative to L1 genesis. @@ -572,6 +578,10 @@ func (d *UpgradeScheduleDeployConfig) InteropTime(genesisTime uint64) *uint64 { return offsetToUpgradeTime(d.L2GenesisInteropTimeOffset, genesisTime) } +func (d *UpgradeScheduleDeployConfig) EspressoTime(genesisTime uint64) *uint64 { + return offsetToUpgradeTime(d.L2GenesisEspressoTimeOffset, genesisTime) +} + func (d *UpgradeScheduleDeployConfig) AllocMode(genesisTime uint64) L2AllocsMode { forks := d.forks() for i := len(forks) - 1; i >= 0; i-- { @@ -939,6 +949,11 @@ type L1DependenciesConfig struct { // DAChallengeProxy represents the L1 address of the DataAvailabilityChallenge contract. DAChallengeProxy common.Address `json:"daChallengeProxy"` + + // BatchAuthenticatorAddress represents the L1 address of the Espresso BatchAuthenticator + // contract whose BatchInfoAuthenticated events gate post-Espresso derivation. Only set for + // Espresso-enabled chains. + BatchAuthenticatorAddress common.Address `json:"batchAuthenticatorAddress,omitempty,omitzero"` } // DependencyContext is the contextual configuration needed to verify the L1 dependencies, @@ -1150,6 +1165,9 @@ func (d *DeployConfig) RollupConfig(l1StartBlock *eth.BlockRef, l2GenesisBlockHa AltDAConfig: altDA, ChainOpConfig: chainOpConfig, Cel2Time: func() *uint64 { v := uint64(0); return &v }(), + + EspressoTime: d.EspressoTime(l1StartTime), + BatchAuthenticatorAddress: d.BatchAuthenticatorAddress, }, nil } diff --git a/op-deployer/pkg/deployer/apply.go b/op-deployer/pkg/deployer/apply.go index 7a81788180b..08216a4406d 100644 --- a/op-deployer/pkg/deployer/apply.go +++ b/op-deployer/pkg/deployer/apply.go @@ -490,6 +490,11 @@ func ApplyPipeline( func() error { return pipeline.DeployAltDA(pEnv, intent, st, chainID) }, + }, pipelineStage{ + fmt.Sprintf("deploy-espresso-%s", chainID.Hex()), + func() error { + return pipeline.DeployEspresso(pEnv, intent, st, chainID) + }, }, pipelineStage{ fmt.Sprintf("deploy-additional-dispute-games-%s", chainID.Hex()), func() error { diff --git a/op-deployer/pkg/deployer/opcm/espresso.go b/op-deployer/pkg/deployer/opcm/espresso.go new file mode 100644 index 00000000000..a2ec12c8b48 --- /dev/null +++ b/op-deployer/pkg/deployer/opcm/espresso.go @@ -0,0 +1,74 @@ +package opcm + +import ( + "fmt" + + "github.com/ethereum-optimism/optimism/op-chain-ops/script" + "github.com/ethereum/go-ethereum/common" +) + +// DeployEspressoInput mirrors the DeployEspressoInput contract in +// scripts/deploy/DeployEspresso.s.sol. The field names (and thus the generated +// setter selectors) must match the contract's getters. +type DeployEspressoInput struct { + // NitroEnclaveVerifier is the underlying AWS Nitro enclave verifier (from Automata). + // Set to the zero address to deploy mock verifiers (dev/test only). + NitroEnclaveVerifier common.Address + // EspressoBatcher is the TEE batcher EOA authorized to call the BatchAuthenticator. + EspressoBatcher common.Address + // SystemConfig is the chain's SystemConfig proxy address. + SystemConfig common.Address + // EspressoOwner is the application-level (OZ Ownable) owner gating operational setters. + EspressoOwner common.Address + // SharedProxyAdmin is the existing OP Stack ProxyAdmin the Espresso proxies are handed to. + SharedProxyAdmin common.Address +} + +// DeployEspressoOutput mirrors the DeployEspressoOutput contract. +type DeployEspressoOutput struct { + BatchAuthenticatorAddress common.Address + TeeVerifierProxy common.Address + NitroTEEVerifier common.Address +} + +// DeployEspressoScript is the typed handle to the DeployEspresso script's run +// function, which takes (input, output, deployerAddress). +type DeployEspressoScript struct { + Run func(input, output, deployerAddress common.Address) error +} + +func DeployEspresso( + host *script.Host, + input DeployEspressoInput, + deployerAddress common.Address, +) (DeployEspressoOutput, error) { + var output DeployEspressoOutput + inputAddr := host.NewScriptAddress() + outputAddr := host.NewScriptAddress() + + cleanupInput, err := script.WithPrecompileAtAddress(host, inputAddr, &input) + if err != nil { + return output, fmt.Errorf("failed to insert DeployEspressoInput precompile: %w", err) + } + defer cleanupInput() + + cleanupOutput, err := script.WithPrecompileAtAddress(host, outputAddr, &output, + script.WithFieldSetter[*DeployEspressoOutput]) + if err != nil { + return output, fmt.Errorf("failed to insert DeployEspressoOutput precompile: %w", err) + } + defer cleanupOutput() + + implContract := "DeployEspresso" + deployScript, cleanupDeploy, err := script.WithScript[DeployEspressoScript](host, "DeployEspresso.s.sol", implContract) + if err != nil { + return output, fmt.Errorf("failed to load %s script: %w", implContract, err) + } + defer cleanupDeploy() + + if err := deployScript.Run(inputAddr, outputAddr, deployerAddress); err != nil { + return output, fmt.Errorf("failed to run %s script: %w", implContract, err) + } + + return output, nil +} diff --git a/op-deployer/pkg/deployer/pipeline/espresso.go b/op-deployer/pkg/deployer/pipeline/espresso.go new file mode 100644 index 00000000000..ec10f1147af --- /dev/null +++ b/op-deployer/pkg/deployer/pipeline/espresso.go @@ -0,0 +1,81 @@ +package pipeline + +import ( + "fmt" + "os" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" + "github.com/ethereum/go-ethereum/common" +) + +// DeployEspresso deploys the Espresso BatchAuthenticator + TEE verifier contracts for a chain +// whose intent has EspressoEnabled. It is a no-op for chains without Espresso enabled. +// +// It targets the redesigned scripts/deploy/DeployEspresso.s.sol (PR #455), whose inputs are +// espressoOwner + sharedProxyAdmin (the Espresso proxies are handed to the existing shared +// OP Stack ProxyAdmin rather than dedicated ones). +func DeployEspresso(env *Env, intent *state.Intent, st *state.State, chainID common.Hash) error { + lgr := env.Logger.New("stage", "deploy-espresso") + + chainIntent, err := intent.Chain(chainID) + if err != nil { + return fmt.Errorf("failed to get chain intent: %w", err) + } + + chainState, err := st.Chain(chainID) + if err != nil { + return fmt.Errorf("failed to get chain state: %w", err) + } + + if !chainIntent.EspressoEnabled { + lgr.Info("espresso not enabled, skipping BatchAuthenticator deployment") + return nil + } + + lgr.Info("deploying espresso contracts") + + // Read the underlying AWS NitroEnclaveVerifier address (from Automata). + // If not set, the zero address triggers mock verifier deployment — dev/test only. + var nitroEnclaveVerifierAddress common.Address + if envVar := os.Getenv("NITRO_ENCLAVE_VERIFIER_ADDRESS"); envVar != "" { + nitroEnclaveVerifierAddress = common.HexToAddress(envVar) + lgr.Info("using nitro enclave verifier from NITRO_ENCLAVE_VERIFIER_ADDRESS", "address", nitroEnclaveVerifierAddress.Hex()) + } else { + lgr.Info("NITRO_ENCLAVE_VERIFIER_ADDRESS not set — deploying mock TEE verifiers") + } + + // The application-level owner gating operational setters. Defaults to the deployer. + espressoOwner := env.Deployer + if envVar := os.Getenv("BATCH_AUTHENTICATOR_OWNER_ADDRESS"); envVar != "" { + espressoOwner = common.HexToAddress(envVar) + lgr.Info("using espresso owner from BATCH_AUTHENTICATOR_OWNER_ADDRESS", "address", espressoOwner.Hex()) + } else { + lgr.Info("using deployer as espresso owner", "address", espressoOwner.Hex()) + } + + // The Espresso proxies are handed to the existing shared OP Stack ProxyAdmin. + sharedProxyAdmin := chainState.OpChainProxyAdminImpl + if sharedProxyAdmin == (common.Address{}) { + return fmt.Errorf("shared OP Stack ProxyAdmin not deployed for chain %s", chainID.Hex()) + } + + eo, err := opcm.DeployEspresso(env.L1ScriptHost, opcm.DeployEspressoInput{ + NitroEnclaveVerifier: nitroEnclaveVerifierAddress, + EspressoBatcher: chainIntent.EspressoBatcher, + SystemConfig: chainState.SystemConfigProxy, + EspressoOwner: espressoOwner, + SharedProxyAdmin: sharedProxyAdmin, + }, espressoOwner) + if err != nil { + return fmt.Errorf("failed to deploy espresso contracts: %w", err) + } + + chainState.BatchAuthenticatorAddress = eo.BatchAuthenticatorAddress + lgr.Info("espresso contracts deployed", + "batchAuthenticator", eo.BatchAuthenticatorAddress, + "teeVerifier", eo.TeeVerifierProxy, + "nitroTEEVerifier", eo.NitroTEEVerifier, + ) + return nil +} diff --git a/op-deployer/pkg/deployer/state/chain_intent.go b/op-deployer/pkg/deployer/state/chain_intent.go index 406744493cc..09258f1a678 100644 --- a/op-deployer/pkg/deployer/state/chain_intent.go +++ b/op-deployer/pkg/deployer/state/chain_intent.go @@ -94,6 +94,13 @@ type ChainIntent struct { DAFootprintGasScalar uint16 `json:"daFootprintGasScalar,omitempty" toml:"daFootprintGasScalar,omitempty"` CustomGasToken CustomGasToken `json:"customGasToken" toml:"customGasToken"` + // EspressoEnabled enables deployment of the Espresso BatchAuthenticator + TEE verifier + // contracts for this chain. + EspressoEnabled bool `json:"espressoEnabled,omitzero" toml:"espressoEnabled,omitzero"` + // EspressoBatcher is the address of the Espresso (TEE) batcher EOA authorized to call + // the BatchAuthenticator. Only used when EspressoEnabled is true. + EspressoBatcher common.Address `json:"espressoBatcher,omitzero" toml:"espressoBatcher,omitzero"` + // Optional. For development purposes only. Only enabled if the operation mode targets a genesis-file output. L2DevGenesisParams *L2DevGenesisParams `json:"l2DevGenesisParams,omitempty" toml:"l2DevGenesisParams,omitempty"` } diff --git a/op-deployer/pkg/deployer/state/deploy_config.go b/op-deployer/pkg/deployer/state/deploy_config.go index 3bde06784f6..e8dd21fa6ff 100644 --- a/op-deployer/pkg/deployer/state/deploy_config.go +++ b/op-deployer/pkg/deployer/state/deploy_config.go @@ -24,6 +24,15 @@ var l2GenesisBlockBaseFeePerGas = hexutil.Big(*(big.NewInt(1000000000))) func CombineDeployConfig(intent *Intent, chainIntent *ChainIntent, state *State, chainState *ChainState) (genesis.DeployConfig, error) { upgradeSchedule := standard.DefaultHardforkSchedule() + // Activate the Espresso upgrade at genesis for Espresso-enabled chains, unless an offset is + // already configured. This makes the in-memory e2e devnet run with Espresso (event-based) + // derivation from block 0. Production chains that want to stage Espresso onto an existing + // chain set L2GenesisEspressoTimeOffset explicitly upstream of this. + if chainIntent.EspressoEnabled && upgradeSchedule.L2GenesisEspressoTimeOffset == nil { + espressoOffset := hexutil.Uint64(0) + upgradeSchedule.L2GenesisEspressoTimeOffset = &espressoOffset + } + cfg := genesis.DeployConfig{ L1DependenciesConfig: genesis.L1DependenciesConfig{ L1StandardBridgeProxy: chainState.L1StandardBridgeProxy, @@ -31,6 +40,7 @@ func CombineDeployConfig(intent *Intent, chainIntent *ChainIntent, state *State, L1ERC721BridgeProxy: chainState.L1Erc721BridgeProxy, SystemConfigProxy: chainState.SystemConfigProxy, OptimismPortalProxy: chainState.OptimismPortalProxy, + BatchAuthenticatorAddress: chainState.BatchAuthenticatorAddress, }, L2InitializationConfig: genesis.L2InitializationConfig{ DevDeployConfig: genesis.DevDeployConfig{ diff --git a/op-deployer/pkg/deployer/state/state.go b/op-deployer/pkg/deployer/state/state.go index 40f31c5f3f1..ece566c2ca8 100644 --- a/op-deployer/pkg/deployer/state/state.go +++ b/op-deployer/pkg/deployer/state/state.go @@ -98,6 +98,10 @@ type ChainState struct { addresses.OpChainContracts + // BatchAuthenticatorAddress is the deployed Espresso BatchAuthenticator proxy address. + // Only set when the chain's intent has EspressoEnabled. + BatchAuthenticatorAddress common.Address `json:"batchAuthenticatorAddress,omitzero,omitempty"` + AdditionalDisputeGames []AdditionalDisputeGameState `json:"additionalDisputeGames"` Allocs *GzipData[foundry.ForgeAllocs] `json:"allocs"` diff --git a/op-e2e/config/init.go b/op-e2e/config/init.go index 468b89c0187..fba7a42bb4b 100644 --- a/op-e2e/config/init.go +++ b/op-e2e/config/init.go @@ -3,6 +3,7 @@ package config import ( "context" "encoding/json" + "errors" "fmt" "log/slog" "maps" @@ -48,6 +49,10 @@ const ( LegacyLevelTrace ) +// ESPRESSO_TESTING_BATCHER_EPHEMERAL_KEY is the pre-approved ephemeral batcher key used by the +// Espresso e2e devnet. It is a well-known dev key — never use it in production. +const ESPRESSO_TESTING_BATCHER_EPHEMERAL_KEY = "404520dcd0335deccd7d4a01f29136dfd651b89ec3969d53a06c3cc5aae5f515" + type AllocType string const ( @@ -57,6 +62,9 @@ const ( AllocTypeMTCannonNext AllocType = "mt-cannon-next" AllocTypeFastGame AllocType = "fast-game" + AllocTypeEspressoWithoutEnclave AllocType = "espresso-no-enclave" + AllocTypeEspressoWithEnclave AllocType = "espresso-enclave" + DefaultAllocType = AllocTypeMTCannon ) @@ -69,14 +77,18 @@ func (a AllocType) Check() error { func (a AllocType) UsesProofs() bool { switch a { - case AllocTypeMTCannon, AllocTypeMTCannonNext, AllocTypeAltDA, AllocTypeAltDAGeneric: + case AllocTypeMTCannon, AllocTypeMTCannonNext, AllocTypeAltDA, AllocTypeAltDAGeneric, AllocTypeEspressoWithEnclave, AllocTypeEspressoWithoutEnclave: return true default: return false } } -var allocTypes = []AllocType{AllocTypeAltDA, AllocTypeAltDAGeneric, AllocTypeMTCannon, AllocTypeMTCannonNext, AllocTypeFastGame} +func (a AllocType) IsEspresso() bool { + return a == AllocTypeEspressoWithEnclave || a == AllocTypeEspressoWithoutEnclave +} + +var allocTypes = []AllocType{AllocTypeAltDA, AllocTypeAltDAGeneric, AllocTypeMTCannon, AllocTypeMTCannonNext, AllocTypeFastGame, AllocTypeEspressoWithEnclave, AllocTypeEspressoWithoutEnclave} var ( // All of the following variables are set in the init function @@ -101,6 +113,10 @@ var ( // EthNodeVerbosity is the (legacy geth) level of verbosity to output EthNodeVerbosity int = 3 + // espresso: tracks Espresso alloc types that failed to initialize (e.g. missing mock TEE + // contracts in --skip test builds). Tests can call IsAllocTypeAvailable() to skip gracefully. + unavailableEspressoAllocTypes = make(map[AllocType]bool) + // mtx is a lock to protect the above variables mtx sync.RWMutex ) @@ -158,6 +174,15 @@ func DeployConfig(allocType AllocType) *genesis.DeployConfig { return dc } +// IsAllocTypeAvailable reports whether allocType was successfully initialized. +// Espresso alloc types are unavailable in CI builds compiled with --skip test, where mock TEE +// contracts are not present. +func IsAllocTypeAvailable(allocType AllocType) bool { + mtx.RLock() + defer mtx.RUnlock() + return !unavailableEspressoAllocTypes[allocType] +} + func init() { // Used by the rust team, to skip legacy op-e2e init. Not used by devstack acceptance tests. if os.Getenv("DISABLE_OP_E2E_LEGACY") == "true" { @@ -204,22 +229,35 @@ func init() { oplog.SetGlobalLogHandler(errHandler) for _, allocType := range allocTypes { - initAllocType(root, allocType) + if err := initAllocType(root, allocType); err != nil { + if allocType.IsEspresso() { + // espresso: Espresso alloc types require mock TEE contracts that are compiled only + // when forge builds include test contracts (i.e. without --skip test). + fmt.Fprintf(os.Stderr, "WARNING: Espresso alloc type %q initialization failed "+ + "(mock TEE contracts may not be available; rebuild with `just compile-contracts` "+ + "to enable Espresso op-e2e tests): %v\n", allocType, err) + mtx.Lock() + unavailableEspressoAllocTypes[allocType] = true + mtx.Unlock() + } else { + panic(fmt.Errorf("failed to init alloc type %q: %w", allocType, err)) + } + } } // Use regular level going forward. oplog.SetGlobalLogHandler(handler) } -func initAllocType(root string, allocType AllocType) { +func initAllocType(root string, allocType AllocType) error { artifactsPath := path.Join(root, "packages", "contracts-bedrock", "forge-artifacts") if err := ensureDir(artifactsPath); err != nil { - panic(fmt.Errorf("invalid artifacts path: %w", err)) + return fmt.Errorf("invalid artifacts path: %w (run `just compile-contracts` or `cd packages/contracts-bedrock && just build` to build contracts first)", err) } loc, err := artifacts.NewFileLocator(artifactsPath) if err != nil { - panic(fmt.Errorf("failed to create artifacts locator: %w", err)) + return fmt.Errorf("failed to create artifacts locator: %w", err) } lgr := log.New() @@ -237,6 +275,8 @@ func initAllocType(root string, allocType AllocType) { l2Alloc := make(map[genesis.L2AllocsMode]*foundry.ForgeAllocs) var wg sync.WaitGroup + var initErr error + var errMu sync.Mutex pk := secrets.DefaultSecrets.Deployer deployerAddr := crypto.PubkeyToAddress(pk.PublicKey) @@ -246,6 +286,13 @@ func initAllocType(root string, allocType AllocType) { wg.Add(1) go func(mode genesis.L2AllocsMode) { defer wg.Done() + defer func() { + if r := recover(); r != nil { + errMu.Lock() + initErr = errors.Join(initErr, fmt.Errorf("panic initializing alloc type %q mode %q: %v", allocType, mode, r)) + errMu.Unlock() + } + }() intent := defaultIntent(root, loc, deployerAddr, allocType) if allocType == AllocTypeAltDA { @@ -279,6 +326,16 @@ func initAllocType(root string, allocType AllocType) { } } + // Configure Espresso allocation types. + // The Espresso batcher uses a separate key (HD index 6) from the standard + // OP stack batcher (Roles.Batcher, HD index 2). The fallback batcher + // uses the SystemConfig batcher address (Roles.Batcher). + if allocType.IsEspresso() { + intent.Chains[0].EspressoEnabled = true + espressoBatcherKey := secrets.DefaultSecrets.AccountAtIdx(6) + intent.Chains[0].EspressoBatcher = crypto.PubkeyToAddress(espressoBatcherKey.PublicKey) + } + baseUpgradeSchedule := map[string]any{ "l2GenesisRegolithTimeOffset": nil, "l2GenesisCanyonTimeOffset": nil, @@ -363,7 +420,13 @@ func initAllocType(root string, allocType AllocType) { }(mode) } wg.Wait() + if initErr != nil { + return initErr + } + mtx.Lock() l2AllocsByType[allocType] = l2Alloc + mtx.Unlock() + return nil } func defaultIntent(root string, loc *artifacts.Locator, deployer common.Address, allocType AllocType) *state.Intent { diff --git a/op-e2e/e2eutils/geth/instance.go b/op-e2e/e2eutils/geth/instance.go index cf731ea1945..211790184e0 100644 --- a/op-e2e/e2eutils/geth/instance.go +++ b/op-e2e/e2eutils/geth/instance.go @@ -1,6 +1,9 @@ package geth import ( + "errors" + + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/node" @@ -41,3 +44,15 @@ func (gi *GethInstance) AuthRPC() endpoint.RPC { func (gi *GethInstance) Close() error { return gi.Node.Close() } + +// Fork reorganizes the L1 chain so that the block with parentHash becomes the new canonical head, +// dropping any descendants. Used by e2e tests to simulate L1 reorgs. +func (gi *GethInstance) Fork(parentHash common.Hash) error { + gi.Backend.TxPool().Clear() + parent := gi.Backend.BlockChain().GetBlockByHash(parentHash) + if parent == nil { + return errors.New("parent not found") + } + _, err := gi.Backend.BlockChain().SetCanonical(parent) + return err +} diff --git a/op-e2e/faultproofs/util.go b/op-e2e/faultproofs/util.go index fbc5da4b615..d03a5c8dc5c 100644 --- a/op-e2e/faultproofs/util.go +++ b/op-e2e/faultproofs/util.go @@ -117,6 +117,31 @@ func StartFaultDisputeSystem(t *testing.T, opts ...faultDisputeConfigOpts) (*e2e return sys, sys.NodeClient("l1") } +// GetFaultDisputeSystemConfigForEspresso returns a Fault Dispute System configuration, merging an +// additional set of SystemConfigOpts (e.g. the Espresso alloc type) with the fault-dispute options. +// Unlike StartFaultDisputeSystem it does not delete the verifier node and returns the config without +// starting it, so Espresso e2e tests can layer further options before launch. +func GetFaultDisputeSystemConfigForEspresso(t *testing.T, originalOpts []e2esys.SystemConfigOpt, opts ...faultDisputeConfigOpts) e2esys.SystemConfig { + fdc := new(faultDisputeConfig) + for _, opt := range opts { + opt(fdc) + } + + cfg := e2esys.DefaultSystemConfig(t, append(originalOpts, fdc.sysOpts...)...) + + cfg.Nodes["sequencer"].SafeDBPath = t.TempDir() + cfg.DeployConfig.SequencerWindowSize = 30 + cfg.DeployConfig.FinalizationPeriodSeconds = 2 + cfg.SupportL1TimeTravel = true + // Disable proposer creating fast games automatically - required games are manually created + cfg.DisableProposer = true + for _, opt := range fdc.cfgModifiers { + opt(&cfg) + } + + return cfg +} + func SendKZGPointEvaluationTx(t *testing.T, sys *e2esys.System, l2Node string, privateKey *ecdsa.PrivateKey) *types.Receipt { return helpers.SendL2Tx(t, sys.Cfg, sys.NodeClient(l2Node), privateKey, func(opts *helpers.TxOpts) { precompile := common.BytesToAddress([]byte{0x0a}) diff --git a/op-e2e/system/e2esys/setup.go b/op-e2e/system/e2esys/setup.go index 7526fb601cb..e4b92c33527 100644 --- a/op-e2e/system/e2esys/setup.go +++ b/op-e2e/system/e2esys/setup.go @@ -19,6 +19,7 @@ import ( "testing" "time" + "github.com/ethereum-optimism/optimism/espresso" gameTypes "github.com/ethereum-optimism/optimism/op-challenger/game/types" "github.com/stretchr/testify/require" @@ -36,6 +37,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/log" @@ -124,6 +126,12 @@ func DefaultSystemConfig(t testing.TB, opts ...SystemConfigOpt) SystemConfig { opt(sco) } + // espresso: Espresso alloc types require mock TEE contracts compiled only in full builds + // (without --skip test). Skip gracefully instead of panicking. + if !config.IsAllocTypeAvailable(sco.AllocType) { + t.Skipf("alloc type %q not available (mock TEE contracts not built); run `just compile-contracts` to enable", sco.AllocType) + } + secrets := secrets.DefaultSecrets deployConfig := config.DeployConfig(sco.AllocType) require.Nil(t, deployConfig.L2GenesisKarstTimeOffset, "karst not supported yet") @@ -147,6 +155,7 @@ func DefaultSystemConfig(t testing.TB, opts ...SystemConfigOpt) SystemConfig { return SystemConfig{ Secrets: secrets, Premine: premine, + L1Allocs: make(map[common.Address]types.Account), DeployConfig: deployConfig, L1Deployments: l1Deployments, L1InfoPredeployAddress: predeploys.L1BlockAddr, @@ -312,6 +321,7 @@ type SystemConfig struct { L1FinalizedDistance uint64 Premine map[common.Address]*big.Int + L1Allocs map[common.Address]types.Account Nodes map[string]*config2.Config // Per node config. Don't use populate rollup.Config Loggers map[string]log.Logger GethOptions map[string][]geth.GethOption @@ -380,12 +390,19 @@ type System struct { L2GenesisCfg *core.Genesis // Connections to running nodes - EthInstances map[string]services.EthInstance - RollupNodes map[string]services.RollupNode - L2OutputSubmitter *l2os.ProposerService - BatchSubmitter *bss.BatcherService - Mocknet mocknet.Mocknet - FakeAltDAServer *altda.FakeDAServer + EthInstances map[string]services.EthInstance + RollupNodes map[string]services.RollupNode + L2OutputSubmitter *l2os.ProposerService + BatchSubmitter *bss.BatcherService + FallbackBatchSubmitter *bss.BatcherService + Mocknet mocknet.Mocknet + FakeAltDAServer *altda.FakeDAServer + + // EspressoClient is the shared in-memory mock Espresso client used by Espresso + // e2e systems in place of a real espresso-dev-node. It is nil for non-Espresso + // alloc types. All batchers (primary, fallback, and any started by tests) and the + // tests themselves share this single instance so they observe the same Espresso chain. + EspressoClient *espresso.MockEspressoClient L1BeaconAPIAddr endpoint.RestHTTP @@ -406,6 +423,8 @@ type System struct { // clients caches lazily created L1/L2 ethclient.Client // instances so they can be reused and closed clients map[string]*ethclient.Client + + L1 *geth.GethInstance } func (sys *System) PrestateVariant() shared.PrestateVariant { @@ -512,6 +531,11 @@ func (sys *System) L1Slot(l1Timestamp uint64) uint64 { sys.Cfg.DeployConfig.L1BlockTime } +// ForkL1 reorganizes the L1 chain so the block with parentHash becomes the canonical head. +func (sys *System) ForkL1(parentHash common.Hash) error { + return sys.L1.Fork(parentHash) +} + func (sys *System) Close() { sys.t.Log("CLOSING") if !sys.closed.CompareAndSwap(false, true) { @@ -532,6 +556,11 @@ func (sys *System) Close() { combinedErr = errors.Join(combinedErr, fmt.Errorf("stop BatchSubmitter: %w", err)) } } + if sys.FallbackBatchSubmitter != nil { + if err := sys.FallbackBatchSubmitter.Kill(); err != nil && !errors.Is(err, bss.ErrAlreadyStopped) { + combinedErr = errors.Join(combinedErr, fmt.Errorf("stop FallbackBatchSubmitter: %w", err)) + } + } for name, node := range sys.RollupNodes { if err := node.Stop(postCtx); err != nil && !errors.Is(err, rollupNode.ErrAlreadyClosed) && !errors.Is(err, postCtx.Err()) { @@ -554,6 +583,14 @@ func (sys *System) Close() { combinedErr = errors.Join(combinedErr, fmt.Errorf("stop Mocknet: %w", err)) } } + if sys.FakeAltDAServer != nil { + if err := sys.FakeAltDAServer.Stop(); err != nil { + combinedErr = errors.Join(combinedErr, fmt.Errorf("stop FakeAltDAServer: %w", err)) + } + } + if sys.EspressoClient != nil { + sys.EspressoClient.Close() + } require.NoError(sys.t, combinedErr, "Failed to stop system") } @@ -565,7 +602,7 @@ type StartOption struct { Action SystemConfigHook // Batcher CLIConfig modifications to apply before starting the batcher. - BatcherMod func(*bss.CLIConfig) + BatcherMod func(*bss.CLIConfig, *System) } type startOptions struct { @@ -588,7 +625,7 @@ func parseStartOptions(_opts []StartOption) (startOptions, error) { func WithBatcherCompressionAlgo(ca derive.CompressionAlgo) StartOption { return StartOption{ - BatcherMod: func(cfg *bss.CLIConfig) { + BatcherMod: func(cfg *bss.CLIConfig, _ *System) { cfg.CompressionAlgo = ca }, } @@ -596,7 +633,7 @@ func WithBatcherCompressionAlgo(ca derive.CompressionAlgo) StartOption { func WithBatcherThrottling(interval time.Duration, threshold, txSize, blockSize uint64) StartOption { return StartOption{ - BatcherMod: func(cfg *bss.CLIConfig) { + BatcherMod: func(cfg *bss.CLIConfig, _ *System) { cfg.ThrottleConfig.LowerThreshold = threshold cfg.ThrottleConfig.ControllerType = batcherCfg.StepControllerType cfg.ThrottleConfig.TxSizeLowerLimit = txSize @@ -665,6 +702,13 @@ func (cfg SystemConfig) Start(t *testing.T, startOpts ...StartOption) (*System, } } + for addr, account := range cfg.L1Allocs { + if _, ok := l1Genesis.Alloc[addr]; ok { + t.Logf("Additional L1 alloc conflicts with existing account: %v", addr) + } + l1Genesis.Alloc[addr] = account + } + l1Block := l1Genesis.ToBlock() allocsMode := cfg.DeployConfig.AllocMode(l1Block.Time()) @@ -715,29 +759,31 @@ func (cfg SystemConfig) Start(t *testing.T, startOpts ...StartOption) (*System, L2Time: uint64(cfg.DeployConfig.L1GenesisBlockTimestamp), SystemConfig: e2eutils.SystemConfigFromDeployConfig(cfg.DeployConfig), }, - BlockTime: cfg.DeployConfig.L2BlockTime, - MaxSequencerDrift: cfg.DeployConfig.MaxSequencerDrift, - SeqWindowSize: cfg.DeployConfig.SequencerWindowSize, - ChannelTimeoutBedrock: cfg.DeployConfig.ChannelTimeoutBedrock, - L1ChainID: cfg.L1ChainIDBig(), - L2ChainID: cfg.L2ChainIDBig(), - BatchInboxAddress: cfg.DeployConfig.BatchInboxAddress, - DepositContractAddress: cfg.DeployConfig.OptimismPortalProxy, - L1SystemConfigAddress: cfg.DeployConfig.SystemConfigProxy, - RegolithTime: cfg.DeployConfig.RegolithTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), - CanyonTime: cfg.DeployConfig.CanyonTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), - DeltaTime: cfg.DeployConfig.DeltaTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), - EcotoneTime: cfg.DeployConfig.EcotoneTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), - FjordTime: cfg.DeployConfig.FjordTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), - GraniteTime: cfg.DeployConfig.GraniteTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), - HoloceneTime: cfg.DeployConfig.HoloceneTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), - PectraBlobScheduleTime: cfg.DeployConfig.PectraBlobScheduleTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), - IsthmusTime: cfg.DeployConfig.IsthmusTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), - JovianTime: cfg.DeployConfig.JovianTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), - KarstTime: cfg.DeployConfig.KarstTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), - InteropTime: cfg.DeployConfig.InteropTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), - Cel2Time: func() *uint64 { v := uint64(0); return &v }(), - AltDAConfig: rollupAltDAConfig, + BlockTime: cfg.DeployConfig.L2BlockTime, + MaxSequencerDrift: cfg.DeployConfig.MaxSequencerDrift, + SeqWindowSize: cfg.DeployConfig.SequencerWindowSize, + ChannelTimeoutBedrock: cfg.DeployConfig.ChannelTimeoutBedrock, + L1ChainID: cfg.L1ChainIDBig(), + L2ChainID: cfg.L2ChainIDBig(), + BatchInboxAddress: cfg.DeployConfig.BatchInboxAddress, + BatchAuthenticatorAddress: cfg.DeployConfig.BatchAuthenticatorAddress, + DepositContractAddress: cfg.DeployConfig.OptimismPortalProxy, + L1SystemConfigAddress: cfg.DeployConfig.SystemConfigProxy, + RegolithTime: cfg.DeployConfig.RegolithTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), + CanyonTime: cfg.DeployConfig.CanyonTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), + DeltaTime: cfg.DeployConfig.DeltaTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), + EcotoneTime: cfg.DeployConfig.EcotoneTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), + FjordTime: cfg.DeployConfig.FjordTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), + GraniteTime: cfg.DeployConfig.GraniteTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), + HoloceneTime: cfg.DeployConfig.HoloceneTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), + PectraBlobScheduleTime: cfg.DeployConfig.PectraBlobScheduleTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), + IsthmusTime: cfg.DeployConfig.IsthmusTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), + JovianTime: cfg.DeployConfig.JovianTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), + KarstTime: cfg.DeployConfig.KarstTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), + InteropTime: cfg.DeployConfig.InteropTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), + EspressoTime: cfg.DeployConfig.EspressoTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), + Cel2Time: func() *uint64 { v := uint64(0); return &v }(), + AltDAConfig: rollupAltDAConfig, ChainOpConfig: ¶ms.OptimismConfig{ EIP1559Elasticity: cfg.DeployConfig.EIP1559Elasticity, EIP1559Denominator: cfg.DeployConfig.EIP1559Denominator, @@ -770,6 +816,7 @@ func (cfg SystemConfig) Start(t *testing.T, startOpts ...StartOption) (*System, return nil, err } sys.EthInstances[RoleL1] = l1Geth + sys.L1 = l1Geth err = l1Geth.Node.Start() if err != nil { return nil, err @@ -1001,6 +1048,40 @@ func (cfg SystemConfig) Start(t *testing.T, startOpts ...StartOption) (*System, batcherTargetNumFrames = 1 } + testingBatcherPk, err := crypto.HexToECDSA(config.ESPRESSO_TESTING_BATCHER_EPHEMERAL_KEY) + if err != nil { + return nil, fmt.Errorf("failed to parse pre-approved batcher private key: %w", err) + } + espressoCfg := espresso.CLIConfig{ + Enabled: cfg.AllocType.IsEspresso(), + PollInterval: 250 * time.Millisecond, + L1URL: sys.EthInstances[RoleL1].UserRPC().RPC(), + RollupL1URL: sys.EthInstances[RoleL1].UserRPC().RPC(), + TestingBatcherPrivateKey: testingBatcherPk, + VerifyReceiptMaxBlocks: espresso.DefaultVerifyReceiptMaxBlocks, + VerifyReceiptSafetyTimeout: espresso.DefaultVerifyReceiptSafetyTimeout, + VerifyReceiptRetryDelay: espresso.DefaultVerifyReceiptRetryDelay, + } + + // Espresso e2e systems use an in-memory mock Espresso client (shared across all + // batchers and the tests) in place of a real espresso-dev-node. The mock is + // injected into the batcher via WithEspressoClientOverride; the placeholder query + // service URLs only satisfy the SDK client's ">=2 URLs" validation and are never + // dialed. + var espressoOverrideOpts []bss.DriverSetupOption + if cfg.AllocType.IsEspresso() { + sys.EspressoClient = espresso.NewMockEspressoClient(250 * time.Millisecond) + espressoCfg.QueryServiceURLs = []string{"http://espresso-mock.invalid", "http://espresso-mock.invalid"} + espressoOverrideOpts = append(espressoOverrideOpts, bss.WithEspressoClientOverride(sys.EspressoClient)) + } + + // When Espresso is enabled, the primary batcher is the Espresso batcher which uses + // a dedicated key (HD index 6) distinct from the SystemConfig batcher (HD index 2). + batcherKey := cfg.Secrets.Batcher + if cfg.AllocType.IsEspresso() { + batcherKey = cfg.Secrets.AccountAtIdx(6) + } + batcherCLIConfig := &bss.CLIConfig{ L1EthRpc: sys.EthInstances[RoleL1].UserRPC().RPC(), L2EthRpc: []string{sys.EthInstances[RoleSeq].UserRPC().RPC()}, @@ -1013,7 +1094,7 @@ func (cfg SystemConfig) Start(t *testing.T, startOpts ...StartOption) (*System, ApproxComprRatio: 0.4, SubSafetyMargin: 4, PollInterval: 50 * time.Millisecond, - TxMgrConfig: setuputils.NewTxMgrConfig(sys.EthInstances[RoleL1].UserRPC(), cfg.Secrets.Batcher), + TxMgrConfig: setuputils.NewTxMgrConfig(sys.EthInstances[RoleL1].UserRPC(), batcherKey), LogConfig: oplog.CLIConfig{ Level: log.LevelInfo, Format: oplog.FormatText, @@ -1024,12 +1105,14 @@ func (cfg SystemConfig) Start(t *testing.T, startOpts ...StartOption) (*System, DataAvailabilityType: sys.Cfg.DataAvailabilityType, CompressionAlgo: derive.Zlib, AltDA: altDACLIConfig, + + Espresso: espressoCfg, } // Apply batcher cli modifications for _, opt := range startOpts { if opt.BatcherMod != nil { - opt.BatcherMod(batcherCLIConfig) + opt.BatcherMod(batcherCLIConfig, sys) } } @@ -1039,7 +1122,7 @@ func (cfg SystemConfig) Start(t *testing.T, startOpts ...StartOption) (*System, t.Fatalf("closeAppFn called, batcher hit a critical error: %v", cause) batcherCancel() } - batcher, err := bss.BatcherServiceFromCLIConfig(batcherContext, closeAppFn, "0.0.1", batcherCLIConfig, sys.Cfg.Loggers["batcher"]) + batcher, err := bss.BatcherServiceFromCLIConfig(batcherContext, closeAppFn, "0.0.1", batcherCLIConfig, sys.Cfg.Loggers["batcher"], espressoOverrideOpts...) if err != nil { return nil, fmt.Errorf("failed to setup batch submitter: %w", err) } @@ -1050,6 +1133,28 @@ func (cfg SystemConfig) Start(t *testing.T, startOpts ...StartOption) (*System, if err := batcher.Start(context.Background()); err != nil { return nil, errors.Join(fmt.Errorf("failed to start batch submitter: %w", err), batcher.Stop(context.Background())) } + + if cfg.AllocType.IsEspresso() { + fallbackBatcherCliConfigVal := *batcherCLIConfig + fallbackBatcherCliConfig := &fallbackBatcherCliConfigVal + fallbackBatcherCliConfig.Stopped = true + fallbackBatcherCliConfig.Espresso.Enabled = false + fallbackBatcherCliConfig.TxMgrConfig = setuputils.NewTxMgrConfig(sys.EthInstances[RoleL1].UserRPC(), cfg.Secrets.Batcher) + fallbackBatcherCtx, fallbackBatcherCancel := context.WithCancel(context.Background()) + fallbackCloseAppFn := func(cause error) { + t.Fatalf("fallback closeAppFn called: %v", cause) + fallbackBatcherCancel() + } + fallbackBatcher, err := bss.BatcherServiceFromCLIConfig(fallbackBatcherCtx, fallbackCloseAppFn, "0.0.1", fallbackBatcherCliConfig, sys.Cfg.Loggers["batcher"], espressoOverrideOpts...) + if err != nil { + return nil, fmt.Errorf("failed to setup fallback batch submitter: %w", err) + } + sys.FallbackBatchSubmitter = fallbackBatcher + if err := sys.FallbackBatchSubmitter.Start(context.Background()); err != nil { + return nil, fmt.Errorf("failed to start fallback batch submitter: %w", err) + } + } + return sys, nil }