Commit 1bfe79f2 authored by clabby's avatar clabby Committed by GitHub

feat(ctb): Two Step Withdrawals V2 (#3836)

* Start contract changes for two step withdrawals v2

* Fix maurelian's nits

* Refactor Kelvin's SDK changes; SDK/integration test time

* Merge w/ `develop`

* Add tests for changed output proposal *after* proving the withdrawal hash

Whoops

* Gas snapshot / comments

* Regenerate bindings; Fix E2E Withdrawal test; Add extra indexed params to `WithdrawalProven`

* Start fixing indexer integration tests

* Fix conflicts; Start updating mark's new `op-e2e` withdrawal action tests

* Remove proposal timestamp >= withdrawal timestamp check

* Fix mark's `op-e2e` test + add docs to `proveMessage` in SDK

* Update changeset

* Lint contracts

* Merge with `develop`

* Re-order mapping declarations so that `finalizedWithdrawals` retains its old storage slot

* Merge with `develop`

* Start updating devnet tests

* Fix devnet tests

* Update ERC20 binding

* Clean up SDK

* Merge with `develop`

* Remove `integration-tests-bedrock` package

* Add check for equality between locally computed withdrawal hash vs. on-chain withdrawal hash

* Add Kelvin's check + complimentary test

Update bindings

* Fix finalization period in `TestCrossLayerUser`
parent f7410440
---
'@eth-optimism/indexer': minor
'@eth-optimism/contracts-bedrock': minor
'@eth-optimism/integration-tests-bedrock': minor
'@eth-optimism/sdk': minor
---
Adds an implementation of the Two Step Withdrawals V2 proposal
...@@ -201,11 +201,12 @@ func TestBedrockIndexer(t *testing.T) { ...@@ -201,11 +201,12 @@ func TestBedrockIndexer(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
proofCl := gethclient.New(rpcClient) proofCl := gethclient.New(rpcClient)
receiptCl := ethclient.NewClient(rpcClient) receiptCl := ethclient.NewClient(rpcClient)
wParams, err := withdrawals.FinalizeWithdrawalParameters(context.Background(), proofCl, receiptCl, wdTx.Hash(), finHeader) wParams, err := withdrawals.ProveWithdrawalParameters(context.Background(), proofCl, receiptCl, wdTx.Hash(), finHeader)
require.NoError(t, err) require.NoError(t, err)
l1Opts.Value = big.NewInt(0) l1Opts.Value = big.NewInt(0)
finTx, err := portal.FinalizeWithdrawalTransaction( // Prove our withdrawal
proveTx, err := portal.ProveWithdrawalTransaction(
l1Opts, l1Opts,
bindings.TypesWithdrawalTransaction{ bindings.TypesWithdrawalTransaction{
Nonce: wParams.Nonce, Nonce: wParams.Nonce,
...@@ -221,6 +222,32 @@ func TestBedrockIndexer(t *testing.T) { ...@@ -221,6 +222,32 @@ func TestBedrockIndexer(t *testing.T) {
) )
require.NoError(t, err) require.NoError(t, err)
_, err = e2eutils.WaitReceiptOK(e2eutils.TimeoutCtx(t, time.Minute), l1Client, proveTx.Hash())
require.NoError(t, err)
// Wait for the finalization period to elapse
_, err = withdrawals.WaitForFinalizationPeriod(
e2eutils.TimeoutCtx(t, time.Minute),
l1Client,
predeploys.DevOptimismPortalAddr,
wParams.BlockNumber,
)
require.NoError(t, err)
// Send our finalize withdrawal transaction
finTx, err := portal.FinalizeWithdrawalTransaction(
l1Opts,
bindings.TypesWithdrawalTransaction{
Nonce: wParams.Nonce,
Sender: wParams.Sender,
Target: wParams.Target,
Value: wParams.Value,
GasLimit: wParams.GasLimit,
Data: wParams.Data,
},
)
require.NoError(t, err)
finReceipt, err := e2eutils.WaitReceiptOK(e2eutils.TimeoutCtx(t, time.Minute), l1Client, finTx.Hash()) finReceipt, err := e2eutils.WaitReceiptOK(e2eutils.TimeoutCtx(t, time.Minute), l1Client, finTx.Hash())
require.NoError(t, err) require.NoError(t, err)
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -9,11 +9,11 @@ import ( ...@@ -9,11 +9,11 @@ import (
"github.com/ethereum-optimism/optimism/op-bindings/solc" "github.com/ethereum-optimism/optimism/op-bindings/solc"
) )
const OptimismPortalStorageLayoutJSON = "{\"storage\":[{\"astId\":1000,\"contract\":\"contracts/L1/OptimismPortal.sol:OptimismPortal\",\"label\":\"_initialized\",\"offset\":0,\"slot\":\"0\",\"type\":\"t_uint8\"},{\"astId\":1001,\"contract\":\"contracts/L1/OptimismPortal.sol:OptimismPortal\",\"label\":\"_initializing\",\"offset\":1,\"slot\":\"0\",\"type\":\"t_bool\"},{\"astId\":1002,\"contract\":\"contracts/L1/OptimismPortal.sol:OptimismPortal\",\"label\":\"params\",\"offset\":0,\"slot\":\"1\",\"type\":\"t_struct(ResourceParams)1007_storage\"},{\"astId\":1003,\"contract\":\"contracts/L1/OptimismPortal.sol:OptimismPortal\",\"label\":\"__gap\",\"offset\":0,\"slot\":\"2\",\"type\":\"t_array(t_uint256)1006_storage\"},{\"astId\":1004,\"contract\":\"contracts/L1/OptimismPortal.sol:OptimismPortal\",\"label\":\"l2Sender\",\"offset\":0,\"slot\":\"50\",\"type\":\"t_address\"},{\"astId\":1005,\"contract\":\"contracts/L1/OptimismPortal.sol:OptimismPortal\",\"label\":\"finalizedWithdrawals\",\"offset\":0,\"slot\":\"51\",\"type\":\"t_mapping(t_bytes32,t_bool)\"}],\"types\":{\"t_address\":{\"encoding\":\"inplace\",\"label\":\"address\",\"numberOfBytes\":\"20\"},\"t_array(t_uint256)1006_storage\":{\"encoding\":\"inplace\",\"label\":\"uint256[48]\",\"numberOfBytes\":\"1536\"},\"t_bool\":{\"encoding\":\"inplace\",\"label\":\"bool\",\"numberOfBytes\":\"1\"},\"t_bytes32\":{\"encoding\":\"inplace\",\"label\":\"bytes32\",\"numberOfBytes\":\"32\"},\"t_mapping(t_bytes32,t_bool)\":{\"encoding\":\"mapping\",\"label\":\"mapping(bytes32 =\u003e bool)\",\"numberOfBytes\":\"32\",\"key\":\"t_bytes32\",\"value\":\"t_bool\"},\"t_struct(ResourceParams)1007_storage\":{\"encoding\":\"inplace\",\"label\":\"struct ResourceMetering.ResourceParams\",\"numberOfBytes\":\"32\"},\"t_uint128\":{\"encoding\":\"inplace\",\"label\":\"uint128\",\"numberOfBytes\":\"16\"},\"t_uint256\":{\"encoding\":\"inplace\",\"label\":\"uint256\",\"numberOfBytes\":\"32\"},\"t_uint64\":{\"encoding\":\"inplace\",\"label\":\"uint64\",\"numberOfBytes\":\"8\"},\"t_uint8\":{\"encoding\":\"inplace\",\"label\":\"uint8\",\"numberOfBytes\":\"1\"}}}" const OptimismPortalStorageLayoutJSON = "{\"storage\":[{\"astId\":1000,\"contract\":\"contracts/L1/OptimismPortal.sol:OptimismPortal\",\"label\":\"_initialized\",\"offset\":0,\"slot\":\"0\",\"type\":\"t_uint8\"},{\"astId\":1001,\"contract\":\"contracts/L1/OptimismPortal.sol:OptimismPortal\",\"label\":\"_initializing\",\"offset\":1,\"slot\":\"0\",\"type\":\"t_bool\"},{\"astId\":1002,\"contract\":\"contracts/L1/OptimismPortal.sol:OptimismPortal\",\"label\":\"params\",\"offset\":0,\"slot\":\"1\",\"type\":\"t_struct(ResourceParams)1009_storage\"},{\"astId\":1003,\"contract\":\"contracts/L1/OptimismPortal.sol:OptimismPortal\",\"label\":\"__gap\",\"offset\":0,\"slot\":\"2\",\"type\":\"t_array(t_uint256)1007_storage\"},{\"astId\":1004,\"contract\":\"contracts/L1/OptimismPortal.sol:OptimismPortal\",\"label\":\"l2Sender\",\"offset\":0,\"slot\":\"50\",\"type\":\"t_address\"},{\"astId\":1005,\"contract\":\"contracts/L1/OptimismPortal.sol:OptimismPortal\",\"label\":\"finalizedWithdrawals\",\"offset\":0,\"slot\":\"51\",\"type\":\"t_mapping(t_bytes32,t_bool)\"},{\"astId\":1006,\"contract\":\"contracts/L1/OptimismPortal.sol:OptimismPortal\",\"label\":\"provenWithdrawals\",\"offset\":0,\"slot\":\"52\",\"type\":\"t_mapping(t_bytes32,t_struct(ProvenWithdrawal)1008_storage)\"}],\"types\":{\"t_address\":{\"encoding\":\"inplace\",\"label\":\"address\",\"numberOfBytes\":\"20\"},\"t_array(t_uint256)1007_storage\":{\"encoding\":\"inplace\",\"label\":\"uint256[48]\",\"numberOfBytes\":\"1536\"},\"t_bool\":{\"encoding\":\"inplace\",\"label\":\"bool\",\"numberOfBytes\":\"1\"},\"t_bytes32\":{\"encoding\":\"inplace\",\"label\":\"bytes32\",\"numberOfBytes\":\"32\"},\"t_mapping(t_bytes32,t_bool)\":{\"encoding\":\"mapping\",\"label\":\"mapping(bytes32 =\u003e bool)\",\"numberOfBytes\":\"32\",\"key\":\"t_bytes32\",\"value\":\"t_bool\"},\"t_mapping(t_bytes32,t_struct(ProvenWithdrawal)1008_storage)\":{\"encoding\":\"mapping\",\"label\":\"mapping(bytes32 =\u003e struct OptimismPortal.ProvenWithdrawal)\",\"numberOfBytes\":\"32\",\"key\":\"t_bytes32\",\"value\":\"t_struct(ProvenWithdrawal)1008_storage\"},\"t_struct(ProvenWithdrawal)1008_storage\":{\"encoding\":\"inplace\",\"label\":\"struct OptimismPortal.ProvenWithdrawal\",\"numberOfBytes\":\"64\"},\"t_struct(ResourceParams)1009_storage\":{\"encoding\":\"inplace\",\"label\":\"struct ResourceMetering.ResourceParams\",\"numberOfBytes\":\"32\"},\"t_uint128\":{\"encoding\":\"inplace\",\"label\":\"uint128\",\"numberOfBytes\":\"16\"},\"t_uint256\":{\"encoding\":\"inplace\",\"label\":\"uint256\",\"numberOfBytes\":\"32\"},\"t_uint64\":{\"encoding\":\"inplace\",\"label\":\"uint64\",\"numberOfBytes\":\"8\"},\"t_uint8\":{\"encoding\":\"inplace\",\"label\":\"uint8\",\"numberOfBytes\":\"1\"}}}"
var OptimismPortalStorageLayout = new(solc.StorageLayout) var OptimismPortalStorageLayout = new(solc.StorageLayout)
var OptimismPortalDeployedBin = "0x6080604052600436106101115760003560e01c80638b4c40b0116100a5578063ca3e99ba11610074578063cff0ab9611610059578063cff0ab9614610332578063e9e05c42146103d3578063f4daa291146103e657600080fd5b8063ca3e99ba14610308578063cd7c97891461031d57600080fd5b80638b4c40b0146101365780639bf62d821461027b578063a14238e7146102a8578063c4fc4798146102e857600080fd5b8063683d95c8116100e1578063683d95c81461021b5780636bb0291e1461023b5780638129fc1c14610250578063867ead131461026557600080fd5b80621c2ff61461013d57806313620abd1461019b57806354fd4d50146101d457806364b79208146101f657600080fd5b36610138576101363334620186a060006040518060200160405280600081525061041a565b005b600080fd5b34801561014957600080fd5b506101717f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156101a757600080fd5b506101b3633b9aca0081565b6040516fffffffffffffffffffffffffffffffff9091168152602001610192565b3480156101e057600080fd5b506101e96108e7565b6040516101929190613b25565b34801561020257600080fd5b5061020d627a120081565b604051908152602001610192565b34801561022757600080fd5b50610136610236366004613cfa565b61098a565b34801561024757600080fd5b5061020d600481565b34801561025c57600080fd5b50610136610fe2565b34801561027157600080fd5b5061020d61271081565b34801561028757600080fd5b506032546101719073ffffffffffffffffffffffffffffffffffffffff1681565b3480156102b457600080fd5b506102d86102c3366004613dee565b60336020526000908152604090205460ff1681565b6040519015158152602001610192565b3480156102f457600080fd5b506102d8610303366004613dee565b6111a0565b34801561031457600080fd5b5061020d611265565b34801561032957600080fd5b5061020d600881565b34801561033e57600080fd5b5060015461039a906fffffffffffffffffffffffffffffffff81169067ffffffffffffffff7001000000000000000000000000000000008204811691780100000000000000000000000000000000000000000000000090041683565b604080516fffffffffffffffffffffffffffffffff909416845267ffffffffffffffff9283166020850152911690820152606001610192565b6101366103e1366004613e07565b61041a565b3480156103f257600080fd5b5061020d7f000000000000000000000000000000000000000000000000000000000000000081565b8260005a905083156104d15773ffffffffffffffffffffffffffffffffffffffff8716156104d157604080517f08c379a00000000000000000000000000000000000000000000000000000000081526020600482015260248101919091527f4f7074696d69736d506f7274616c3a206d7573742073656e6420746f2061646460448201527f72657373283029207768656e206372656174696e67206120636f6e747261637460648201526084015b60405180910390fd5b333281146104f2575033731111000000000000000000000000000000001111015b6000348888888860405160200161050d959493929190613e94565b604051602081830303815290604052905060008973ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fb3813568d9991fc951961fcb4c784893574240a28925604d09fc577c55bb7c328460405161057d9190613b25565b60405180910390a450506001546000906105bd907801000000000000000000000000000000000000000000000000900467ffffffffffffffff1643613f28565b905080156107465760006105d56004627a1200613f6e565b6001546106009190700100000000000000000000000000000000900467ffffffffffffffff16613fd6565b9050600060086106146004627a1200613f6e565b6001546106349085906fffffffffffffffffffffffffffffffff1661404a565b61063e9190613f6e565b6106489190613f6e565b6001549091506000906106949061067e906106769085906fffffffffffffffffffffffffffffffff16614106565b612710611292565b6fffffffffffffffffffffffffffffffff6112ad565b905060018411156107075761070461067e670de0b6b3a76400006106f06106bc600883613f6e565b6106ce90670de0b6b3a7640000613fd6565b6106d960018a613f28565b6106eb90670de0b6b3a764000061417a565b6112bc565b6106fa908561404a565b6106769190613f6e565b90505b6fffffffffffffffffffffffffffffffff16780100000000000000000000000000000000000000000000000067ffffffffffffffff4316021760015550505b60018054849190601090610779908490700100000000000000000000000000000000900467ffffffffffffffff166141b7565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550627a1200600160000160109054906101000a900467ffffffffffffffff1667ffffffffffffffff161315610855576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603e60248201527f5265736f757263654d65746572696e673a2063616e6e6f7420627579206d6f7260448201527f6520676173207468616e20617661696c61626c6520676173206c696d6974000060648201526084016104c8565b600154600090610881906fffffffffffffffffffffffffffffffff1667ffffffffffffffff86166141e3565b6fffffffffffffffffffffffffffffffff16905060006108a548633b9aca006112ed565b6108af908361421b565b905060005a6108be9086613f28565b9050808211156108da576108da6108d58284613f28565b6112fd565b5050505050505050505050565b60606109127f000000000000000000000000000000000000000000000000000000000000000061132b565b61093b7f000000000000000000000000000000000000000000000000000000000000000061132b565b6109647f000000000000000000000000000000000000000000000000000000000000000061132b565b6040516020016109769392919061422f565b604051602081830303815290604052905090565b60325473ffffffffffffffffffffffffffffffffffffffff1661dead14610a33576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603f60248201527f4f7074696d69736d506f7274616c3a2063616e206f6e6c79207472696767657260448201527f206f6e65207769746864726177616c20706572207472616e73616374696f6e0060648201526084016104c8565b3073ffffffffffffffffffffffffffffffffffffffff16856040015173ffffffffffffffffffffffffffffffffffffffff1603610af2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603f60248201527f4f7074696d69736d506f7274616c3a20796f752063616e6e6f742073656e642060448201527f6d6573736167657320746f2074686520706f7274616c20636f6e74726163740060648201526084016104c8565b6040517fa25ae557000000000000000000000000000000000000000000000000000000008152600481018590526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae557906024016040805180830381865afa158015610b7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ba391906142a5565b9050610bae81611468565b610c3a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602d60248201527f4f7074696d69736d506f7274616c3a2070726f706f73616c206973206e6f742060448201527f7965742066696e616c697a65640000000000000000000000000000000000000060648201526084016104c8565b610c51610c4c368690038601866142f4565b6114a2565b815114610ce0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f7074696d69736d506f7274616c3a20696e76616c6964206f7574707574207260448201527f6f6f742070726f6f66000000000000000000000000000000000000000000000060648201526084016104c8565b6000610ceb876114fe565b9050610d05816040870135610d00868861435a565b61152e565b610d91576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4f7074696d69736d506f7274616c3a20696e76616c696420776974686472617760448201527f616c20696e636c7573696f6e2070726f6f66000000000000000000000000000060648201526084016104c8565b60008181526033602052604090205460ff1615610e30576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173206160448201527f6c7265616479206265656e2066696e616c697a6564000000000000000000000060648201526084016104c8565b600081815260336020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556080870151610e7990614e20906143de565b5a1015610f08576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603760248201527f4f7074696d69736d506f7274616c3a20696e73756666696369656e742067617360448201527f20746f2066696e616c697a65207769746864726177616c00000000000000000060648201526084016104c8565b8660200151603260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000610f6b886040015189608001518a606001518b60a001516115f5565b603280547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead17905560405190915082907fdb5c7652857aa163daadd670e116628fb42e869d8ac4251ef8971d9e5727df1b90610fd090841515815260200190565b60405180910390a25050505050505050565b600054610100900460ff16158080156110025750600054600160ff909116105b8061101c5750303b15801561101c575060005460ff166001145b6110a8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016104c8565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055801561110657600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b603280547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead17905561113a61160f565b801561119d57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50565b6040517fa25ae55700000000000000000000000000000000000000000000000000000000815260048101829052600090819073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063a25ae557906024016040805180830381865afa15801561122f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061125391906142a5565b905061125e81611468565b9392505050565b6112736004627a1200613f6e565b81565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b6000818312156112a257816112a4565b825b90505b92915050565b60008183126112a257816112a4565b60006112a4670de0b6b3a7640000836112d4866116f2565b6112de919061404a565b6112e89190613f6e565b611936565b6000818310156112a257816112a4565b6000805a90505b825a6113109083613f28565b10156113265761131f826143f6565b9150611304565b505050565b60608160000361136e57505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b81156113985780611382816143f6565b91506113919050600a8361421b565b9150611372565b60008167ffffffffffffffff8111156113b3576113b3613b38565b6040519080825280601f01601f1916602001820160405280156113dd576020820181803683370190505b5090505b8415611460576113f2600183613f28565b91506113ff600a8661442e565b61140a9060306143de565b60f81b81838151811061141f5761141f614442565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350611459600a8661421b565b94506113e1565b949350505050565b60007f0000000000000000000000000000000000000000000000000000000000000000826020015161149a91906143de565b421192915050565b600081600001518260200151836040015184606001516040516020016114e1949392919093845260208401929092526040830152606082015260800190565b604051602081830303815290604052805190602001209050919050565b80516020808301516040808501516060860151608087015160a088015193516000976114e1979096959101614471565b604080516020810185905260009181018290528190606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152828252805160209182012090830181905292506115ec9101604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152828201909152600182527f01000000000000000000000000000000000000000000000000000000000000006020830152908587611b75565b95945050505050565b600080600080845160208601878a8af19695505050505050565b600054610100900460ff166116a6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016104c8565b60408051606081018252633b9aca00808252600060208301524367ffffffffffffffff169190920181905278010000000000000000000000000000000000000000000000000217600155565b600080821361175d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e444546494e4544000000000000000000000000000000000000000000000060448201526064016104c8565b6000606061176a84611b99565b03609f8181039490941b90931c6c465772b2bbbb5f824b15207a3081018102606090811d6d0388eaa27412d5aca026815d636e018202811d6d0df99ac502031bf953eff472fdcc018202811d6d13cdffb29d51d99322bdff5f2211018202811d6d0a0f742023def783a307a986912e018202811d6d01920d8043ca89b5239253284e42018202811d6c0b7a86d7375468fac667a0a527016c29508e458543d8aa4df2abee7883018302821d6d0139601a2efabe717e604cbb4894018302821d6d02247f7a7b6594320649aa03aba1018302821d7fffffffffffffffffffffffffffffffffffffff73c0c716a594e00d54e3c4cbc9018302821d7ffffffffffffffffffffffffffffffffffffffdc7b88c420e53a9890533129f6f01830290911d7fffffffffffffffffffffffffffffffffffffff465fda27eb4d63ded474e5f832019091027ffffffffffffffff5f6af8f7b3396644f18e157960000000000000000000000000105711340daa0d5f769dba1915cef59f0815a5506027d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b393909302929092017d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b88642840160ae1d92915050565b60007ffffffffffffffffffffffffffffffffffffffffffffffffdb731c958f34d94c1821361196757506000919050565b680755bf798b4a1bf1e582126119d9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f4558505f4f564552464c4f57000000000000000000000000000000000000000060448201526064016104c8565b6503782dace9d9604e83901b059150600060606bb17217f7d1cf79abc9e3b39884821b056b80000000000000000000000001901d6bb17217f7d1cf79abc9e3b39881029093037fffffffffffffffffffffffffffffffffffffffdbf3ccf1604d263450f02a550481018102606090811d6d0277594991cfc85f6e2461837cd9018202811d7fffffffffffffffffffffffffffffffffffffe5adedaa1cb095af9e4da10e363c018202811d6db1bbb201f443cf962f1a1d3db4a5018202811d7ffffffffffffffffffffffffffffffffffffd38dc772608b0ae56cce01296c0eb018202811d6e05180bb14799ab47a8a8cb2a527d57016d02d16720577bd19bf614176fe9ea6c10fe68e7fd37d0007b713f765084018402831d9081019084017ffffffffffffffffffffffffffffffffffffffe2c69812cf03b0763fd454a8f7e010290911d6e0587f503bb6ea29d25fcb7401964500190910279d835ebba824c98fb31b83b2ca45c000000000000000000000000010574029d9dc38563c32e5c2f6dc192ee70ef65f9978af30260c3939093039290921c92915050565b600080611b8186611c6f565b9050611b8f81868686611ca1565b9695505050505050565b6000808211611c04576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e444546494e4544000000000000000000000000000000000000000000000060448201526064016104c8565b5060016fffffffffffffffffffffffffffffffff821160071b82811c67ffffffffffffffff1060061b1782811c63ffffffff1060051b1782811c61ffff1060041b1782811c60ff10600390811b90911783811c600f1060021b1783811c909110821b1791821c111790565b60608180519060200120604051602001611c8b91815260200190565b6040516020818303038152906040529050919050565b6000806000611cb1878686611cde565b91509150818015611cd357508051602080830191909120875191880191909120145b979650505050505050565b600060606000611ced85611dfc565b90506000806000611cff848a89611eeb565b81519295509093509150158080611d135750815b611d9f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f4d65726b6c65547269653a2070726f76696465642070726f6f6620697320696e60448201527f76616c696400000000000000000000000000000000000000000000000000000060648201526084016104c8565b600081611dbb5760405180602001604052806000815250611de7565b611de786611dca600188613f28565b81518110611dda57611dda614442565b60200260200101516125ef565b805115159c909b509950505050505050505050565b805160609060008167ffffffffffffffff811115611e1c57611e1c613b38565b604051908082528060200260200182016040528015611e6157816020015b6040805180820190915260608082526020820152815260200190600190039081611e3a5790505b50905060005b82811015611ee3576040518060400160405280868381518110611e8c57611e8c614442565b60200260200101518152602001611ebb878481518110611eae57611eae614442565b6020026020010151612626565b815250828281518110611ed057611ed0614442565b6020908102919091010152600101611e67565b509392505050565b60006060818080611efb87612639565b9050600086604051602001611f1291815260200190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152828201909152606080835260208301529150600090819060005b8c51811015612571578c8181518110611f7257611f72614442565b602002602001015191508284611f8891906143de565b9350611f956001886143de565b9650836000036120505781518051602091820120604051611fe592611fbf92910190815260200190565b604051602081830303815290604052868051602091820120825192909101919091201490565b61204b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4d65726b6c65547269653a20696e76616c696420726f6f74206861736800000060448201526064016104c8565b6121a7565b815151602011612106578151805160209182012060405161207a92611fbf92910190815260200190565b61204b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d65726b6c65547269653a20696e76616c6964206c6172676520696e7465726e60448201527f616c20686173680000000000000000000000000000000000000000000000000060648201526084016104c8565b8151855160208088019190912082519190920120146121a7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4d65726b6c65547269653a20696e76616c696420696e7465726e616c206e6f6460448201527f652068617368000000000000000000000000000000000000000000000000000060648201526084016104c8565b6121b3601060016143de565b8260200151510361222557855184146125715760008685815181106121da576121da614442565b602001015160f81c60f81b60f81c9050600083602001518260ff168151811061220557612205614442565b60200260200101519050612218816127d4565b965060019450505061255f565b6002826020015151036124d757600061223d836127f9565b905060008160008151811061225457612254614442565b016020015160f81c9050600061226b6002836144c8565b6122769060026144ea565b90506000612287848360ff1661281d565b905060006122958b8a61281d565b905060006122a38383612853565b9050825182511015612337576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f4d65726b6c65547269653a20696e76616c6964206b6579206c656e677468206660448201527f6f72206c656166206f7220657874656e73696f6e206e6f64650000000000000060648201526084016104c8565b60ff85166002148061234c575060ff85166003145b156123b6578083511480156123615750808251145b1561237357612370818b6143de565b99505b6040518060400160405280600181526020017f80000000000000000000000000000000000000000000000000000000000000008152509a50505050505050612571565b60ff851615806123c9575060ff85166001145b1561244f5782518114612419576040518060400160405280600181526020017f80000000000000000000000000000000000000000000000000000000000000008152509a50505050505050612571565b612440886020015160018151811061243357612433614442565b60200260200101516127d4565b9a50975061255f945050505050565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4d65726b6c65547269653a2072656365697665642061206e6f6465207769746860448201527f20616e20756e6b6e6f776e20707265666978000000000000000000000000000060648201526084016104c8565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f4d65726b6c65547269653a20726563656976656420616e20756e70617273656160448201527f626c65206e6f646500000000000000000000000000000000000000000000000060648201526084016104c8565b80612569816143f6565b915050611f57565b508561257d868561281d565b60408051808201909152600181527f80000000000000000000000000000000000000000000000000000000000000006020918201528651908701207f56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4211498509850985050505050505093509350939050565b602081015180516060916112a79161260990600190613f28565b8151811061261957612619614442565b6020026020010151612902565b60606112a761263483612a62565b612b4b565b8051606090600061264b82600261417a565b67ffffffffffffffff81111561266357612663613b38565b6040519080825280601f01601f19166020018201604052801561268d576020820181803683370190505b5090506000805b838110156127ca578581815181106126ae576126ae614442565b6020910101517fff000000000000000000000000000000000000000000000000000000000000008116925060041c7f0ff0000000000000000000000000000000000000000000000000000000000000168361270a83600261417a565b8151811061271a5761271a614442565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f0f0000000000000000000000000000000000000000000000000000000000000082168361277883600261417a565b6127839060016143de565b8151811061279357612793614442565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600101612694565b5090949350505050565b606060208260000151106127f0576127eb82612902565b6112a7565b6112a782612daf565b60606112a7612818836020015160008151811061261957612619614442565b612639565b60608251821061283c57506040805160208101909152600081526112a7565b6112a4838384865161284e9190613f28565b612dc5565b6000806000835185511061286857835161286b565b84515b90505b80821080156128f2575083828151811061288a5761288a614442565b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168583815181106128c9576128c9614442565b01602001517fff0000000000000000000000000000000000000000000000000000000000000016145b15611ee35781600101915061286e565b6060600080600061291285612f9d565b91945092509050600081600181111561292d5761292d61450d565b146129ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f524c505265616465723a206465636f646564206974656d207479706520666f7260448201527f206279746573206973206e6f7420612064617461206974656d0000000000000060648201526084016104c8565b6129c482846143de565b855114612a53576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603460248201527f524c505265616465723a2062797465732076616c756520636f6e7461696e732060448201527f616e20696e76616c69642072656d61696e64657200000000000000000000000060648201526084016104c8565b6115ec85602001518484613a0a565b60408051808201909152600080825260208201526000825111612b2d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620616e20524c50206974656d60448201527f206d7573742062652067726561746572207468616e207a65726f20746f20626560648201527f206465636f6461626c6500000000000000000000000000000000000000000000608482015260a4016104c8565b50604080518082019091528151815260209182019181019190915290565b60606000806000612b5b85612f9d565b919450925090506001816001811115612b7657612b7661450d565b14612c03576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f524c505265616465723a206465636f646564206974656d207479706520666f7260448201527f206c697374206973206e6f742061206c697374206974656d000000000000000060648201526084016104c8565b8451612c0f83856143de565b14612c9c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f524c505265616465723a206c697374206974656d2068617320616e20696e766160448201527f6c696420646174612072656d61696e646572000000000000000000000000000060648201526084016104c8565b6040805160208082526104208201909252600091816020015b6040805180820190915260008082526020820152815260200190600190039081612cb55790505090506000845b8751811015612da357600080612d286040518060400160405280858d60000151612d0c9190613f28565b8152602001858d60200151612d2191906143de565b9052612f9d565b509150915060405180604001604052808383612d4491906143de565b8152602001848c60200151612d5991906143de565b815250858581518110612d6e57612d6e614442565b6020908102919091010152612d846001856143de565b9350612d9081836143de565b612d9a90846143de565b92505050612ce2565b50815295945050505050565b60606112a7826020015160008460000151613a0a565b60608182601f011015612e34576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f7700000000000000000000000000000000000060448201526064016104c8565b828284011015612ea0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f7700000000000000000000000000000000000060448201526064016104c8565b81830184511015612f0d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f736c6963655f6f75744f66426f756e647300000000000000000000000000000060448201526064016104c8565b606082158015612f2c5760405191506000825260208201604052612f94565b6040519150601f8416801560200281840101858101878315602002848b0101015b81831015612f65578051835260209283019201612f4d565b5050858452601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016604052505b50949350505050565b60008060008084600001511161305b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620616e20524c50206974656d60448201527f206d7573742062652067726561746572207468616e207a65726f20746f20626560648201527f206465636f6461626c6500000000000000000000000000000000000000000000608482015260a4016104c8565b6020840151805160001a607f8111613080576000600160009450945094505050613a03565b60b7811161328e576000613095608083613f28565b905080876000015111613150576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604e60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20737472696e67206c656e6774682060648201527f2873686f727420737472696e6729000000000000000000000000000000000000608482015260a4016104c8565b6001838101517fff000000000000000000000000000000000000000000000000000000000000001690821415806131c957507f80000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000821610155b61327b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604d60248201527f524c505265616465723a20696e76616c6964207072656669782c2073696e676c60448201527f652062797465203c203078383020617265206e6f74207072656669786564202860648201527f73686f727420737472696e672900000000000000000000000000000000000000608482015260a4016104c8565b5060019550935060009250613a03915050565b60bf81116135dc5760006132a360b783613f28565b90508087600001511161335e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152605160248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206265203e207468616e206c656e677468206f6620737472696e67206c656e60648201527f67746820286c6f6e6720737472696e6729000000000000000000000000000000608482015260a4016104c8565b60018301517fff0000000000000000000000000000000000000000000000000000000000000016600081900361343c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206e6f74206861766520616e79206c656164696e67207a65726f7320286c6f60648201527f6e6720737472696e672900000000000000000000000000000000000000000000608482015260a4016104c8565b600184015160088302610100031c60378111613500576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604860248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20353520627974657320286c6f6e6760648201527f20737472696e6729000000000000000000000000000000000000000000000000608482015260a4016104c8565b61350a81846143de565b8951116135bf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604c60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20746f74616c206c656e677468202860648201527f6c6f6e6720737472696e67290000000000000000000000000000000000000000608482015260a4016104c8565b6135ca8360016143de565b9750955060009450613a039350505050565b60f781116136bd5760006135f160c083613f28565b9050808760000151116136ac576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e206c697374206c656e67746820287360648201527f686f7274206c6973742900000000000000000000000000000000000000000000608482015260a4016104c8565b600195509350849250613a03915050565b60006136ca60f783613f28565b905080876000015111613785576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604d60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206265203e207468616e206c656e677468206f66206c697374206c656e677460648201527f6820286c6f6e67206c6973742900000000000000000000000000000000000000608482015260a4016104c8565b60018301517fff00000000000000000000000000000000000000000000000000000000000000166000819003613863576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604860248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206e6f74206861766520616e79206c656164696e67207a65726f7320286c6f60648201527f6e67206c69737429000000000000000000000000000000000000000000000000608482015260a4016104c8565b600184015160088302610100031c60378111613927576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604660248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20353520627974657320286c6f6e6760648201527f206c697374290000000000000000000000000000000000000000000000000000608482015260a4016104c8565b61393181846143de565b8951116139e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20746f74616c206c656e677468202860648201527f6c6f6e67206c6973742900000000000000000000000000000000000000000000608482015260a4016104c8565b6139f18360016143de565b9750955060019450613a039350505050565b9193909250565b606060008267ffffffffffffffff811115613a2757613a27613b38565b6040519080825280601f01601f191660200182016040528015613a51576020820181803683370190505b50905082600003613a6357905061125e565b6000613a6f85876143de565b90506020820160005b85811015613a90578281015182820152602001613a78565b85811115613a9f576000868301525b50919695505050505050565b60005b83811015613ac6578181015183820152602001613aae565b83811115613ad5576000848401525b50505050565b60008151808452613af3816020860160208601613aab565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6020815260006112a46020830184613adb565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405160c0810167ffffffffffffffff81118282101715613b8a57613b8a613b38565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613bd757613bd7613b38565b604052919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114613c0357600080fd5b919050565b600082601f830112613c1957600080fd5b813567ffffffffffffffff811115613c3357613c33613b38565b613c6460207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601613b90565b818152846020838601011115613c7957600080fd5b816020850160208301376000918101602001919091529392505050565b600060808284031215613ca857600080fd5b50919050565b60008083601f840112613cc057600080fd5b50813567ffffffffffffffff811115613cd857600080fd5b6020830191508360208260051b8501011115613cf357600080fd5b9250929050565b600080600080600060e08688031215613d1257600080fd5b853567ffffffffffffffff80821115613d2a57600080fd5b9087019060c0828a031215613d3e57600080fd5b613d46613b67565b82358152613d5660208401613bdf565b6020820152613d6760408401613bdf565b6040820152606083013560608201526080830135608082015260a083013582811115613d9257600080fd5b613d9e8b828601613c08565b60a083015250965060208801359550613dba8960408a01613c96565b945060c0880135915080821115613dd057600080fd5b50613ddd88828901613cae565b969995985093965092949392505050565b600060208284031215613e0057600080fd5b5035919050565b600080600080600060a08688031215613e1f57600080fd5b613e2886613bdf565b945060208601359350604086013567ffffffffffffffff8082168214613e4d57600080fd5b9093506060870135908115158214613e6457600080fd5b90925060808701359080821115613e7a57600080fd5b50613e8788828901613c08565b9150509295509295909350565b8581528460208201527fffffffffffffffff0000000000000000000000000000000000000000000000008460c01b16604082015282151560f81b604882015260008251613ee8816049850160208701613aab565b919091016049019695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082821015613f3a57613f3a613ef9565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082613f7d57613f7d613f3f565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f800000000000000000000000000000000000000000000000000000000000000083141615613fd157613fd1613ef9565b500590565b6000808312837f80000000000000000000000000000000000000000000000000000000000000000183128115161561401057614010613ef9565b837f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01831381161561404457614044613ef9565b50500390565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60008413600084138583048511828216161561408b5761408b613ef9565b7f800000000000000000000000000000000000000000000000000000000000000060008712868205881281841616156140c6576140c6613ef9565b600087129250878205871284841616156140e2576140e2613ef9565b878505871281841616156140f8576140f8613ef9565b505050929093029392505050565b6000808212827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0384138115161561414057614140613ef9565b827f800000000000000000000000000000000000000000000000000000000000000003841281161561417457614174613ef9565b50500190565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156141b2576141b2613ef9565b500290565b600067ffffffffffffffff8083168185168083038211156141da576141da613ef9565b01949350505050565b60006fffffffffffffffffffffffffffffffff8083168185168183048111821515161561421257614212613ef9565b02949350505050565b60008261422a5761422a613f3f565b500490565b60008451614241818460208901613aab565b80830190507f2e00000000000000000000000000000000000000000000000000000000000000808252855161427d816001850160208a01613aab565b60019201918201528351614298816002840160208801613aab565b0160020195945050505050565b6000604082840312156142b757600080fd5b6040516040810181811067ffffffffffffffff821117156142da576142da613b38565b604052825181526020928301519281019290925250919050565b60006080828403121561430657600080fd5b6040516080810181811067ffffffffffffffff8211171561432957614329613b38565b8060405250823581526020830135602082015260408301356040820152606083013560608201528091505092915050565b600067ffffffffffffffff8084111561437557614375613b38565b8360051b6020614386818301613b90565b86815291850191818101903684111561439e57600080fd5b865b848110156143d2578035868111156143b85760008081fd5b6143c436828b01613c08565b8452509183019183016143a0565b50979650505050505050565b600082198211156143f1576143f1613ef9565b500190565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361442757614427613ef9565b5060010190565b60008261443d5761443d613f3f565b500690565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b868152600073ffffffffffffffffffffffffffffffffffffffff808816602084015280871660408401525084606083015283608083015260c060a08301526144bc60c0830184613adb565b98975050505050505050565b600060ff8316806144db576144db613f3f565b8060ff84160691505092915050565b600060ff821660ff84168082101561450457614504613ef9565b90039392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fdfea164736f6c634300080f000a" var OptimismPortalDeployedBin = "0x6080604052600436106101475760003560e01c80638c3152e9116100c0578063cd7c978911610074578063e965084c11610059578063e965084c14610429578063e9e05c42146104b5578063f4daa291146104c857600080fd5b8063cd7c978914610373578063cff0ab961461038857600080fd5b8063a14238e7116100a5578063a14238e7146102fe578063c4fc47981461033e578063ca3e99ba1461035e57600080fd5b80638c3152e9146102b15780639bf62d82146102d157600080fd5b806364b79208116101175780638129fc1c116100fc5780638129fc1c14610286578063867ead131461029b5780638b4c40b01461016c57600080fd5b806364b792081461024c5780636bb0291e1461027157600080fd5b80621c2ff61461017357806313620abd146101d15780634870496f1461020a57806354fd4d501461022a57600080fd5b3661016e5761016c3334620186a06000604051806020016040528060008152506104fc565b005b600080fd5b34801561017f57600080fd5b506101a77f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156101dd57600080fd5b506101e9633b9aca0081565b6040516fffffffffffffffffffffffffffffffff90911681526020016101c8565b34801561021657600080fd5b5061016c6102253660046142e8565b6109c9565b34801561023657600080fd5b5061023f610d52565b6040516101c8919061443e565b34801561025857600080fd5b50610263627a120081565b6040519081526020016101c8565b34801561027d57600080fd5b50610263600481565b34801561029257600080fd5b5061016c610df5565b3480156102a757600080fd5b5061026361271081565b3480156102bd57600080fd5b5061016c6102cc366004614451565b610fb3565b3480156102dd57600080fd5b506032546101a79073ffffffffffffffffffffffffffffffffffffffff1681565b34801561030a57600080fd5b5061032e610319366004614486565b60336020526000908152604090205460ff1681565b60405190151581526020016101c8565b34801561034a57600080fd5b5061032e610359366004614486565b61180c565b34801561036a57600080fd5b506102636118d0565b34801561037f57600080fd5b50610263600881565b34801561039457600080fd5b506001546103f0906fffffffffffffffffffffffffffffffff81169067ffffffffffffffff7001000000000000000000000000000000008204811691780100000000000000000000000000000000000000000000000090041683565b604080516fffffffffffffffffffffffffffffffff909416845267ffffffffffffffff92831660208501529116908201526060016101c8565b34801561043557600080fd5b50610487610444366004614486565b603460205260009081526040902080546001909101546fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041683565b604080519384526fffffffffffffffffffffffffffffffff92831660208501529116908201526060016101c8565b61016c6104c336600461449f565b6104fc565b3480156104d457600080fd5b506102637f000000000000000000000000000000000000000000000000000000000000000081565b8260005a905083156105b35773ffffffffffffffffffffffffffffffffffffffff8716156105b357604080517f08c379a00000000000000000000000000000000000000000000000000000000081526020600482015260248101919091527f4f7074696d69736d506f7274616c3a206d7573742073656e6420746f2061646460448201527f72657373283029207768656e206372656174696e67206120636f6e747261637460648201526084015b60405180910390fd5b333281146105d4575033731111000000000000000000000000000000001111015b600034888888886040516020016105ef95949392919061452c565b604051602081830303815290604052905060008973ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fb3813568d9991fc951961fcb4c784893574240a28925604d09fc577c55bb7c328460405161065f919061443e565b60405180910390a4505060015460009061069f907801000000000000000000000000000000000000000000000000900467ffffffffffffffff16436145c0565b905080156108285760006106b76004627a1200614606565b6001546106e29190700100000000000000000000000000000000900467ffffffffffffffff1661466e565b9050600060086106f66004627a1200614606565b6001546107169085906fffffffffffffffffffffffffffffffff166146e2565b6107209190614606565b61072a9190614606565b60015490915060009061077690610760906107589085906fffffffffffffffffffffffffffffffff1661479e565b6127106118fd565b6fffffffffffffffffffffffffffffffff611916565b905060018411156107e9576107e6610760670de0b6b3a76400006107d261079e600883614606565b6107b090670de0b6b3a764000061466e565b6107bb60018a6145c0565b6107cd90670de0b6b3a7640000614812565b611925565b6107dc90856146e2565b6107589190614606565b90505b6fffffffffffffffffffffffffffffffff16780100000000000000000000000000000000000000000000000067ffffffffffffffff4316021760015550505b6001805484919060109061085b908490700100000000000000000000000000000000900467ffffffffffffffff1661484f565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550627a1200600160000160109054906101000a900467ffffffffffffffff1667ffffffffffffffff161315610937576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603e60248201527f5265736f757263654d65746572696e673a2063616e6e6f7420627579206d6f7260448201527f6520676173207468616e20617661696c61626c6520676173206c696d6974000060648201526084016105aa565b600154600090610963906fffffffffffffffffffffffffffffffff1667ffffffffffffffff861661487b565b6fffffffffffffffffffffffffffffffff169050600061098748633b9aca00611956565b61099190836148b3565b905060005a6109a090866145c0565b9050808211156109bc576109bc6109b782846145c0565b611966565b5050505050505050505050565b3073ffffffffffffffffffffffffffffffffffffffff16856040015173ffffffffffffffffffffffffffffffffffffffff1603610a88576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603f60248201527f4f7074696d69736d506f7274616c3a20796f752063616e6e6f742073656e642060448201527f6d6573736167657320746f2074686520706f7274616c20636f6e74726163740060648201526084016105aa565b6040517fa25ae557000000000000000000000000000000000000000000000000000000008152600481018590526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae557906024016040805180830381865afa158015610b15573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b3991906148c7565b519050610b53610b4e36869003860186614916565b611994565b8114610be1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f7074696d69736d506f7274616c3a20696e76616c6964206f7574707574207260448201527f6f6f742070726f6f66000000000000000000000000000000000000000000000060648201526084016105aa565b6000610bec876119f0565b9050610c06816040870135610c01868861497c565b611a20565b610c92576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4f7074696d69736d506f7274616c3a20696e76616c696420776974686472617760448201527f616c20696e636c7573696f6e2070726f6f66000000000000000000000000000060648201526084016105aa565b604080516060810182528381526fffffffffffffffffffffffffffffffff42811660208084019182528a8316848601908152600087815260348352868120955186559251905184167001000000000000000000000000000000000293169290921760019093019290925589830151908a0151925173ffffffffffffffffffffffffffffffffffffffff918216939091169184917f67a6208cfcc0801d50f6cbe764733f4fddf66ac0b04442061a8a8c0cb6b63f629190a450505050505050565b6060610d7d7f0000000000000000000000000000000000000000000000000000000000000000611ae7565b610da67f0000000000000000000000000000000000000000000000000000000000000000611ae7565b610dcf7f0000000000000000000000000000000000000000000000000000000000000000611ae7565b604051602001610de193929190614a00565b604051602081830303815290604052905090565b600054610100900460ff1615808015610e155750600054600160ff909116105b80610e2f5750303b158015610e2f575060005460ff166001145b610ebb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084016105aa565b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790558015610f1957600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b603280547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead179055610f4d611c24565b8015610fb057600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50565b60325473ffffffffffffffffffffffffffffffffffffffff1661dead1461105c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603f60248201527f4f7074696d69736d506f7274616c3a2063616e206f6e6c79207472696767657260448201527f206f6e65207769746864726177616c20706572207472616e73616374696f6e0060648201526084016105aa565b6000611067826119f0565b60008181526034602090815260408083208151606081018352815481526001909101546fffffffffffffffffffffffffffffffff80821694830185905270010000000000000000000000000000000090910416918101919091529293509003611152576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173206e60448201527f6f74206265656e2070726f76656e00000000000000000000000000000000000060648201526084016105aa565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166320e9fcd46040518163ffffffff1660e01b8152600401602060405180830381865afa1580156111bd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111e19190614a76565b81602001516fffffffffffffffffffffffffffffffff1610156112ac576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604b60248201527f4f7074696d69736d506f7274616c3a207769746864726177616c2074696d657360448201527f74616d70206c657373207468616e204c32204f7261636c65207374617274696e60648201527f672074696d657374616d70000000000000000000000000000000000000000000608482015260a4016105aa565b6112cb81602001516fffffffffffffffffffffffffffffffff16611d07565b61137d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604560248201527f4f7074696d69736d506f7274616c3a2070726f76656e2077697468647261776160448201527f6c2066696e616c697a6174696f6e20706572696f6420686173206e6f7420656c60648201527f6170736564000000000000000000000000000000000000000000000000000000608482015260a4016105aa565b60408181015190517fa25ae5570000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff90911660048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063a25ae557906024016040805180830381865afa158015611421573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061144591906148c7565b82518151919250146114ff576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604960248201527f4f7074696d69736d506f7274616c3a206f757470757420726f6f742070726f7660448201527f656e206973206e6f74207468652073616d652061732063757272656e74206f7560648201527f7470757420726f6f740000000000000000000000000000000000000000000000608482015260a4016105aa565b61150c8160200151611d07565b6115be576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604360248201527f4f7074696d69736d506f7274616c3a206f75747075742070726f706f73616c2060448201527f66696e616c697a6174696f6e20706572696f6420686173206e6f7420656c617060648201527f7365640000000000000000000000000000000000000000000000000000000000608482015260a4016105aa565b60008381526033602052604090205460ff161561165d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603560248201527f4f7074696d69736d506f7274616c3a207769746864726177616c20686173206160448201527f6c7265616479206265656e2066696e616c697a6564000000000000000000000060648201526084016105aa565b600083815260336020526040902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905560808401516116a690614e2090614a8f565b5a1015611735576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603760248201527f4f7074696d69736d506f7274616c3a20696e73756666696369656e742067617360448201527f20746f2066696e616c697a65207769746864726177616c00000000000000000060648201526084016105aa565b8360200151603260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060006117988560400151866080015187606001518860a00151611d3b565b603280547fffffffffffffffffffffffff00000000000000000000000000000000000000001661dead17905560405190915084907fdb5c7652857aa163daadd670e116628fb42e869d8ac4251ef8971d9e5727df1b906117fd90841515815260200190565b60405180910390a25050505050565b6040517fa25ae557000000000000000000000000000000000000000000000000000000008152600481018290526000906118ca9073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063a25ae557906024016040805180830381865afa15801561189d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118c191906148c7565b60200151611d07565b92915050565b6118de6004627a1200614606565b81565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b60008183121561190d578161190f565b825b9392505050565b600081831261190d578161190f565b600061190f670de0b6b3a76400008361193d86611d55565b61194791906146e2565b6119519190614606565b611f99565b60008183101561190d578161190f565b6000805a90505b825a61197990836145c0565b101561198f5761198882614aa7565b915061196d565b505050565b600081600001518260200151836040015184606001516040516020016119d3949392919093845260208401929092526040830152606082015260800190565b604051602081830303815290604052805190602001209050919050565b80516020808301516040808501516060860151608087015160a088015193516000976119d3979096959101614adf565b604080516020810185905260009181018290528190606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201209083018190529250611ade9101604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152828201909152600182527f010000000000000000000000000000000000000000000000000000000000000060208301529085876121d8565b95945050505050565b606081600003611b2a57505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b8115611b545780611b3e81614aa7565b9150611b4d9050600a836148b3565b9150611b2e565b60008167ffffffffffffffff811115611b6f57611b6f61410e565b6040519080825280601f01601f191660200182016040528015611b99576020820181803683370190505b5090505b8415611c1c57611bae6001836145c0565b9150611bbb600a86614b36565b611bc6906030614a8f565b60f81b818381518110611bdb57611bdb614b4a565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350611c15600a866148b3565b9450611b9d565b949350505050565b600054610100900460ff16611cbb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e6700000000000000000000000000000000000000000060648201526084016105aa565b60408051606081018252633b9aca00808252600060208301524367ffffffffffffffff169190920181905278010000000000000000000000000000000000000000000000000217600155565b6000611d337f000000000000000000000000000000000000000000000000000000000000000083614a8f565b421192915050565b600080600080845160208601878a8af19695505050505050565b6000808213611dc0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e444546494e4544000000000000000000000000000000000000000000000060448201526064016105aa565b60006060611dcd846121fc565b03609f8181039490941b90931c6c465772b2bbbb5f824b15207a3081018102606090811d6d0388eaa27412d5aca026815d636e018202811d6d0df99ac502031bf953eff472fdcc018202811d6d13cdffb29d51d99322bdff5f2211018202811d6d0a0f742023def783a307a986912e018202811d6d01920d8043ca89b5239253284e42018202811d6c0b7a86d7375468fac667a0a527016c29508e458543d8aa4df2abee7883018302821d6d0139601a2efabe717e604cbb4894018302821d6d02247f7a7b6594320649aa03aba1018302821d7fffffffffffffffffffffffffffffffffffffff73c0c716a594e00d54e3c4cbc9018302821d7ffffffffffffffffffffffffffffffffffffffdc7b88c420e53a9890533129f6f01830290911d7fffffffffffffffffffffffffffffffffffffff465fda27eb4d63ded474e5f832019091027ffffffffffffffff5f6af8f7b3396644f18e157960000000000000000000000000105711340daa0d5f769dba1915cef59f0815a5506027d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b393909302929092017d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b88642840160ae1d92915050565b60007ffffffffffffffffffffffffffffffffffffffffffffffffdb731c958f34d94c18213611fca57506000919050565b680755bf798b4a1bf1e5821261203c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f4558505f4f564552464c4f57000000000000000000000000000000000000000060448201526064016105aa565b6503782dace9d9604e83901b059150600060606bb17217f7d1cf79abc9e3b39884821b056b80000000000000000000000001901d6bb17217f7d1cf79abc9e3b39881029093037fffffffffffffffffffffffffffffffffffffffdbf3ccf1604d263450f02a550481018102606090811d6d0277594991cfc85f6e2461837cd9018202811d7fffffffffffffffffffffffffffffffffffffe5adedaa1cb095af9e4da10e363c018202811d6db1bbb201f443cf962f1a1d3db4a5018202811d7ffffffffffffffffffffffffffffffffffffd38dc772608b0ae56cce01296c0eb018202811d6e05180bb14799ab47a8a8cb2a527d57016d02d16720577bd19bf614176fe9ea6c10fe68e7fd37d0007b713f765084018402831d9081019084017ffffffffffffffffffffffffffffffffffffffe2c69812cf03b0763fd454a8f7e010290911d6e0587f503bb6ea29d25fcb7401964500190910279d835ebba824c98fb31b83b2ca45c000000000000000000000000010574029d9dc38563c32e5c2f6dc192ee70ef65f9978af30260c3939093039290921c92915050565b6000806121e4866122d2565b90506121f281868686612304565b9695505050505050565b6000808211612267576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f554e444546494e4544000000000000000000000000000000000000000000000060448201526064016105aa565b5060016fffffffffffffffffffffffffffffffff821160071b82811c67ffffffffffffffff1060061b1782811c63ffffffff1060051b1782811c61ffff1060041b1782811c60ff10600390811b90911783811c600f1060021b1783811c909110821b1791821c111790565b606081805190602001206040516020016122ee91815260200190565b6040516020818303038152906040529050919050565b6000806000612314878686612341565b9150915081801561233657508051602080830191909120875191880191909120145b979650505050505050565b6000606060006123508561245f565b90506000806000612362848a8961254e565b815192955090935091501580806123765750815b612402576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f4d65726b6c65547269653a2070726f76696465642070726f6f6620697320696e60448201527f76616c696400000000000000000000000000000000000000000000000000000060648201526084016105aa565b60008161241e576040518060200160405280600081525061244a565b61244a8661242d6001886145c0565b8151811061243d5761243d614b4a565b6020026020010151612c52565b805115159c909b509950505050505050505050565b805160609060008167ffffffffffffffff81111561247f5761247f61410e565b6040519080825280602002602001820160405280156124c457816020015b604080518082019091526060808252602082015281526020019060019003908161249d5790505b50905060005b828110156125465760405180604001604052808683815181106124ef576124ef614b4a565b6020026020010151815260200161251e87848151811061251157612511614b4a565b6020026020010151612c89565b81525082828151811061253357612533614b4a565b60209081029190910101526001016124ca565b509392505050565b6000606081808061255e87612c9c565b905060008660405160200161257591815260200190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152828201909152606080835260208301529150600090819060005b8c51811015612bd4578c81815181106125d5576125d5614b4a565b6020026020010151915082846125eb9190614a8f565b93506125f8600188614a8f565b9650836000036126b357815180516020918201206040516126489261262292910190815260200190565b604051602081830303815290604052868051602091820120825192909101919091201490565b6126ae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f4d65726b6c65547269653a20696e76616c696420726f6f74206861736800000060448201526064016105aa565b61280a565b81515160201161276957815180516020918201206040516126dd9261262292910190815260200190565b6126ae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d65726b6c65547269653a20696e76616c6964206c6172676520696e7465726e60448201527f616c20686173680000000000000000000000000000000000000000000000000060648201526084016105aa565b81518551602080880191909120825191909201201461280a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4d65726b6c65547269653a20696e76616c696420696e7465726e616c206e6f6460448201527f652068617368000000000000000000000000000000000000000000000000000060648201526084016105aa565b61281660106001614a8f565b826020015151036128885785518414612bd457600086858151811061283d5761283d614b4a565b602001015160f81c60f81b60f81c9050600083602001518260ff168151811061286857612868614b4a565b6020026020010151905061287b81612e37565b9650600194505050612bc2565b600282602001515103612b3a5760006128a083612e5c565b90506000816000815181106128b7576128b7614b4a565b016020015160f81c905060006128ce600283614b79565b6128d9906002614b9b565b905060006128ea848360ff16612e80565b905060006128f88b8a612e80565b905060006129068383612eb6565b905082518251101561299a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f4d65726b6c65547269653a20696e76616c6964206b6579206c656e677468206660448201527f6f72206c656166206f7220657874656e73696f6e206e6f64650000000000000060648201526084016105aa565b60ff8516600214806129af575060ff85166003145b15612a19578083511480156129c45750808251145b156129d6576129d3818b614a8f565b99505b6040518060400160405280600181526020017f80000000000000000000000000000000000000000000000000000000000000008152509a50505050505050612bd4565b60ff85161580612a2c575060ff85166001145b15612ab25782518114612a7c576040518060400160405280600181526020017f80000000000000000000000000000000000000000000000000000000000000008152509a50505050505050612bd4565b612aa38860200151600181518110612a9657612a96614b4a565b6020026020010151612e37565b9a509750612bc2945050505050565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f4d65726b6c65547269653a2072656365697665642061206e6f6465207769746860448201527f20616e20756e6b6e6f776e20707265666978000000000000000000000000000060648201526084016105aa565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602860248201527f4d65726b6c65547269653a20726563656976656420616e20756e70617273656160448201527f626c65206e6f646500000000000000000000000000000000000000000000000060648201526084016105aa565b80612bcc81614aa7565b9150506125ba565b5085612be08685612e80565b60408051808201909152600181527f80000000000000000000000000000000000000000000000000000000000000006020918201528651908701207f56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4211498509850985050505050505093509350939050565b602081015180516060916118ca91612c6c906001906145c0565b81518110612c7c57612c7c614b4a565b6020026020010151612f65565b60606118ca612c97836130c5565b6131ae565b80516060906000612cae826002614812565b67ffffffffffffffff811115612cc657612cc661410e565b6040519080825280601f01601f191660200182016040528015612cf0576020820181803683370190505b5090506000805b83811015612e2d57858181518110612d1157612d11614b4a565b6020910101517fff000000000000000000000000000000000000000000000000000000000000008116925060041c7f0ff00000000000000000000000000000000000000000000000000000000000001683612d6d836002614812565b81518110612d7d57612d7d614b4a565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f0f00000000000000000000000000000000000000000000000000000000000000821683612ddb836002614812565b612de6906001614a8f565b81518110612df657612df6614b4a565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600101612cf7565b5090949350505050565b60606020826000015110612e5357612e4e82612f65565b6118ca565b6118ca82613412565b60606118ca612e7b8360200151600081518110612c7c57612c7c614b4a565b612c9c565b606082518210612e9f57506040805160208101909152600081526118ca565b61190f8383848651612eb191906145c0565b613428565b60008060008351855110612ecb578351612ece565b84515b90505b8082108015612f555750838281518110612eed57612eed614b4a565b602001015160f81c60f81b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916858381518110612f2c57612f2c614b4a565b01602001517fff0000000000000000000000000000000000000000000000000000000000000016145b1561254657816001019150612ed1565b60606000806000612f7585613600565b919450925090506000816001811115612f9057612f90614bbe565b1461301d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603960248201527f524c505265616465723a206465636f646564206974656d207479706520666f7260448201527f206279746573206973206e6f7420612064617461206974656d0000000000000060648201526084016105aa565b6130278284614a8f565b8551146130b6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603460248201527f524c505265616465723a2062797465732076616c756520636f6e7461696e732060448201527f616e20696e76616c69642072656d61696e64657200000000000000000000000060648201526084016105aa565b611ade8560200151848461406d565b60408051808201909152600080825260208201526000825111613190576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620616e20524c50206974656d60448201527f206d7573742062652067726561746572207468616e207a65726f20746f20626560648201527f206465636f6461626c6500000000000000000000000000000000000000000000608482015260a4016105aa565b50604080518082019091528151815260209182019181019190915290565b606060008060006131be85613600565b9194509250905060018160018111156131d9576131d9614bbe565b14613266576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603860248201527f524c505265616465723a206465636f646564206974656d207479706520666f7260448201527f206c697374206973206e6f742061206c697374206974656d000000000000000060648201526084016105aa565b84516132728385614a8f565b146132ff576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603260248201527f524c505265616465723a206c697374206974656d2068617320616e20696e766160448201527f6c696420646174612072656d61696e646572000000000000000000000000000060648201526084016105aa565b6040805160208082526104208201909252600091816020015b60408051808201909152600080825260208201528152602001906001900390816133185790505090506000845b87518110156134065760008061338b6040518060400160405280858d6000015161336f91906145c0565b8152602001858d602001516133849190614a8f565b9052613600565b5091509150604051806040016040528083836133a79190614a8f565b8152602001848c602001516133bc9190614a8f565b8152508585815181106133d1576133d1614b4a565b60209081029190910101526133e7600185614a8f565b93506133f38183614a8f565b6133fd9084614a8f565b92505050613345565b50815295945050505050565b60606118ca82602001516000846000015161406d565b60608182601f011015613497576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f7700000000000000000000000000000000000060448201526064016105aa565b828284011015613503576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f7700000000000000000000000000000000000060448201526064016105aa565b81830184511015613570576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f736c6963655f6f75744f66426f756e647300000000000000000000000000000060448201526064016105aa565b60608215801561358f57604051915060008252602082016040526135f7565b6040519150601f8416801560200281840101858101878315602002848b0101015b818310156135c85780518352602092830192016135b0565b5050858452601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016604052505b50949350505050565b6000806000808460000151116136be576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620616e20524c50206974656d60448201527f206d7573742062652067726561746572207468616e207a65726f20746f20626560648201527f206465636f6461626c6500000000000000000000000000000000000000000000608482015260a4016105aa565b6020840151805160001a607f81116136e3576000600160009450945094505050614066565b60b781116138f15760006136f86080836145c0565b9050808760000151116137b3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604e60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20737472696e67206c656e6774682060648201527f2873686f727420737472696e6729000000000000000000000000000000000000608482015260a4016105aa565b6001838101517fff0000000000000000000000000000000000000000000000000000000000000016908214158061382c57507f80000000000000000000000000000000000000000000000000000000000000007fff00000000000000000000000000000000000000000000000000000000000000821610155b6138de576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604d60248201527f524c505265616465723a20696e76616c6964207072656669782c2073696e676c60448201527f652062797465203c203078383020617265206e6f74207072656669786564202860648201527f73686f727420737472696e672900000000000000000000000000000000000000608482015260a4016105aa565b5060019550935060009250614066915050565b60bf8111613c3f57600061390660b7836145c0565b9050808760000151116139c1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152605160248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206265203e207468616e206c656e677468206f6620737472696e67206c656e60648201527f67746820286c6f6e6720737472696e6729000000000000000000000000000000608482015260a4016105aa565b60018301517fff00000000000000000000000000000000000000000000000000000000000000166000819003613a9f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206e6f74206861766520616e79206c656164696e67207a65726f7320286c6f60648201527f6e6720737472696e672900000000000000000000000000000000000000000000608482015260a4016105aa565b600184015160088302610100031c60378111613b63576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604860248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20353520627974657320286c6f6e6760648201527f20737472696e6729000000000000000000000000000000000000000000000000608482015260a4016105aa565b613b6d8184614a8f565b895111613c22576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604c60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20746f74616c206c656e677468202860648201527f6c6f6e6720737472696e67290000000000000000000000000000000000000000608482015260a4016105aa565b613c2d836001614a8f565b97509550600094506140669350505050565b60f78111613d20576000613c5460c0836145c0565b905080876000015111613d0f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e206c697374206c656e67746820287360648201527f686f7274206c6973742900000000000000000000000000000000000000000000608482015260a4016105aa565b600195509350849250614066915050565b6000613d2d60f7836145c0565b905080876000015111613de8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604d60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206265203e207468616e206c656e677468206f66206c697374206c656e677460648201527f6820286c6f6e67206c6973742900000000000000000000000000000000000000608482015260a4016105aa565b60018301517fff00000000000000000000000000000000000000000000000000000000000000166000819003613ec6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604860248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f74206e6f74206861766520616e79206c656164696e67207a65726f7320286c6f60648201527f6e67206c69737429000000000000000000000000000000000000000000000000608482015260a4016105aa565b600184015160088302610100031c60378111613f8a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604660248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20353520627974657320286c6f6e6760648201527f206c697374290000000000000000000000000000000000000000000000000000608482015260a4016105aa565b613f948184614a8f565b895111614049576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604a60248201527f524c505265616465723a206c656e677468206f6620636f6e74656e74206d757360448201527f742062652067726561746572207468616e20746f74616c206c656e677468202860648201527f6c6f6e67206c6973742900000000000000000000000000000000000000000000608482015260a4016105aa565b614054836001614a8f565b97509550600194506140669350505050565b9193909250565b606060008267ffffffffffffffff81111561408a5761408a61410e565b6040519080825280601f01601f1916602001820160405280156140b4576020820181803683370190505b509050826000036140c657905061190f565b60006140d28587614a8f565b90506020820160005b858110156140f35782810151828201526020016140db565b85811115614102576000868301525b50919695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156141845761418461410e565b604052919050565b803573ffffffffffffffffffffffffffffffffffffffff811681146141b057600080fd5b919050565b600082601f8301126141c657600080fd5b813567ffffffffffffffff8111156141e0576141e061410e565b61421160207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160161413d565b81815284602083860101111561422657600080fd5b816020850160208301376000918101602001919091529392505050565b600060c0828403121561425557600080fd5b60405160c0810167ffffffffffffffff82821081831117156142795761427961410e565b81604052829350843583526142906020860161418c565b60208401526142a16040860161418c565b6040840152606085013560608401526080850135608084015260a08501359150808211156142ce57600080fd5b506142db858286016141b5565b60a0830152505092915050565b600080600080600085870360e081121561430157600080fd5b863567ffffffffffffffff8082111561431957600080fd5b6143258a838b01614243565b97506020890135965060807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc08401121561435e57600080fd5b60408901955060c089013592508083111561437857600080fd5b828901925089601f84011261438c57600080fd5b823591508082111561439d57600080fd5b508860208260051b84010111156143b357600080fd5b959894975092955050506020019190565b60005b838110156143df5781810151838201526020016143c7565b838111156143ee576000848401525b50505050565b6000815180845261440c8160208601602086016143c4565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061190f60208301846143f4565b60006020828403121561446357600080fd5b813567ffffffffffffffff81111561447a57600080fd5b611c1c84828501614243565b60006020828403121561449857600080fd5b5035919050565b600080600080600060a086880312156144b757600080fd5b6144c08661418c565b945060208601359350604086013567ffffffffffffffff80821682146144e557600080fd5b90935060608701359081151582146144fc57600080fd5b9092506080870135908082111561451257600080fd5b5061451f888289016141b5565b9150509295509295909350565b8581528460208201527fffffffffffffffff0000000000000000000000000000000000000000000000008460c01b16604082015282151560f81b6048820152600082516145808160498501602087016143c4565b919091016049019695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000828210156145d2576145d2614591565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082614615576146156145d7565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83147f80000000000000000000000000000000000000000000000000000000000000008314161561466957614669614591565b500590565b6000808312837f8000000000000000000000000000000000000000000000000000000000000000018312811516156146a8576146a8614591565b837f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0183138116156146dc576146dc614591565b50500390565b60007f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60008413600084138583048511828216161561472357614723614591565b7f8000000000000000000000000000000000000000000000000000000000000000600087128682058812818416161561475e5761475e614591565b6000871292508782058712848416161561477a5761477a614591565b8785058712818416161561479057614790614591565b505050929093029392505050565b6000808212827f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038413811516156147d8576147d8614591565b827f800000000000000000000000000000000000000000000000000000000000000003841281161561480c5761480c614591565b50500190565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561484a5761484a614591565b500290565b600067ffffffffffffffff80831681851680830382111561487257614872614591565b01949350505050565b60006fffffffffffffffffffffffffffffffff808316818516818304811182151516156148aa576148aa614591565b02949350505050565b6000826148c2576148c26145d7565b500490565b6000604082840312156148d957600080fd5b6040516040810181811067ffffffffffffffff821117156148fc576148fc61410e565b604052825181526020928301519281019290925250919050565b60006080828403121561492857600080fd5b6040516080810181811067ffffffffffffffff8211171561494b5761494b61410e565b8060405250823581526020830135602082015260408301356040820152606083013560608201528091505092915050565b600067ffffffffffffffff808411156149975761499761410e565b8360051b60206149a881830161413d565b8681529185019181810190368411156149c057600080fd5b865b848110156149f4578035868111156149da5760008081fd5b6149e636828b016141b5565b8452509183019183016149c2565b50979650505050505050565b60008451614a128184602089016143c4565b80830190507f2e000000000000000000000000000000000000000000000000000000000000008082528551614a4e816001850160208a016143c4565b60019201918201528351614a698160028401602088016143c4565b0160020195945050505050565b600060208284031215614a8857600080fd5b5051919050565b60008219821115614aa257614aa2614591565b500190565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203614ad857614ad8614591565b5060010190565b868152600073ffffffffffffffffffffffffffffffffffffffff808816602084015280871660408401525084606083015283608083015260c060a0830152614b2a60c08301846143f4565b98975050505050505050565b600082614b4557614b456145d7565b500690565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600060ff831680614b8c57614b8c6145d7565b8060ff84160691505092915050565b600060ff821660ff841680821015614bb557614bb5614591565b90039392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fdfea164736f6c634300080f000a"
func init() { func init() {
if err := json.Unmarshal([]byte(OptimismPortalStorageLayoutJSON), OptimismPortalStorageLayout); err != nil { if err := json.Unmarshal([]byte(OptimismPortalStorageLayoutJSON), OptimismPortalStorageLayout); err != nil {
......
...@@ -379,14 +379,68 @@ func (s *CrossLayerUser) Address() common.Address { ...@@ -379,14 +379,68 @@ func (s *CrossLayerUser) Address() common.Address {
return s.L1.address return s.L1.address
} }
// ActCompleteWithdrawal creates a L1 withdrawal completion tx for latest withdrawal. // ActCompleteWithdrawal creates a L1 proveWithdrawal tx for latest withdrawal.
// The tx hash is remembered as the last L1 tx, to check as L1 actor.
func (s *CrossLayerUser) ActProveWithdrawal(t Testing) {
s.L1.lastTxHash = s.ProveWithdrawal(t, s.lastL2WithdrawalTxHash)
}
// ProveWithdrawal creates a L1 proveWithdrawal tx for the given L2 withdrawal tx, returning the tx hash.
func (s *CrossLayerUser) ProveWithdrawal(t Testing, l2TxHash common.Hash) common.Hash {
// Figure out when our withdrawal was included
receipt := s.L2.CheckReceipt(t, true, l2TxHash)
l2WithdrawalBlock, err := s.L2.env.EthCl.BlockByNumber(t.Ctx(), receipt.BlockNumber)
require.NoError(t, err)
// Figure out what the Output oracle on L1 has seen so far
l2OutputBlockNr, err := s.L1.env.Bindings.L2OutputOracle.LatestBlockNumber(&bind.CallOpts{})
require.NoError(t, err)
l2OutputBlock, err := s.L2.env.EthCl.BlockByNumber(t.Ctx(), l2OutputBlockNr)
require.NoError(t, err)
// Check if the L2 output is even old enough to include the withdrawal
if l2OutputBlock.NumberU64() < l2WithdrawalBlock.NumberU64() {
t.InvalidAction("the latest L2 output is %d and is not past L2 block %d that includes the withdrawal yet, no withdrawal can be proved yet", l2OutputBlock.NumberU64(), l2WithdrawalBlock.NumberU64())
return common.Hash{}
}
// We generate a proof for the latest L2 output, which shouldn't require archive-node data if it's recent enough.
header, err := s.L2.env.EthCl.HeaderByNumber(t.Ctx(), l2OutputBlockNr)
require.NoError(t, err)
params, err := withdrawals.ProveWithdrawalParameters(t.Ctx(), s.L2.env.Bindings.ProofClient, s.L2.env.EthCl, s.lastL2WithdrawalTxHash, header)
require.NoError(t, err)
// Create the prove tx
tx, err := s.L1.env.Bindings.OptimismPortal.ProveWithdrawalTransaction(
&s.L1.txOpts,
bindings.TypesWithdrawalTransaction{
Nonce: params.Nonce,
Sender: params.Sender,
Target: params.Target,
Value: params.Value,
GasLimit: params.GasLimit,
Data: params.Data,
},
params.BlockNumber,
params.OutputRootProof,
params.WithdrawalProof,
)
require.NoError(t, err)
// Send the actual tx (since tx opts don't send by default)
err = s.L1.env.EthCl.SendTransaction(t.Ctx(), tx)
require.NoError(t, err, "must send prove tx")
return tx.Hash()
}
// ActCompleteWithdrawal creates a L1 withdrawal finalization tx for latest withdrawal.
// The tx hash is remembered as the last L1 tx, to check as L1 actor. // The tx hash is remembered as the last L1 tx, to check as L1 actor.
// The withdrawal functions like CompleteWithdrawal // The withdrawal functions like CompleteWithdrawal
func (s *CrossLayerUser) ActCompleteWithdrawal(t Testing) { func (s *CrossLayerUser) ActCompleteWithdrawal(t Testing) {
s.L1.lastTxHash = s.CompleteWithdrawal(t, s.lastL2WithdrawalTxHash) s.L1.lastTxHash = s.CompleteWithdrawal(t, s.lastL2WithdrawalTxHash)
} }
// CompleteWithdrawal creates a L1 withdrawal completion tx for the given L2 withdrawal tx, returning the tx hash. // CompleteWithdrawal creates a L1 withdrawal finalization tx for the given L2 withdrawal tx, returning the tx hash.
// It's an invalid action to attempt to complete a withdrawal that has not passed the L1 finalization period yet // It's an invalid action to attempt to complete a withdrawal that has not passed the L1 finalization period yet
func (s *CrossLayerUser) CompleteWithdrawal(t Testing, l2TxHash common.Hash) common.Hash { func (s *CrossLayerUser) CompleteWithdrawal(t Testing, l2TxHash common.Hash) common.Hash {
finalizationPeriod, err := s.L1.env.Bindings.OptimismPortal.FINALIZATIONPERIODSECONDS(&bind.CallOpts{}) finalizationPeriod, err := s.L1.env.Bindings.OptimismPortal.FINALIZATIONPERIODSECONDS(&bind.CallOpts{})
...@@ -420,9 +474,11 @@ func (s *CrossLayerUser) CompleteWithdrawal(t Testing, l2TxHash common.Hash) com ...@@ -420,9 +474,11 @@ func (s *CrossLayerUser) CompleteWithdrawal(t Testing, l2TxHash common.Hash) com
} }
// We generate a proof for the latest L2 output, which shouldn't require archive-node data if it's recent enough. // We generate a proof for the latest L2 output, which shouldn't require archive-node data if it's recent enough.
// Note that for the `FinalizeWithdrawalTransaction` function, this proof isn't needed. We simply use some of the
// params for the `WithdrawalTransaction` type generated in the bindings.
header, err := s.L2.env.EthCl.HeaderByNumber(t.Ctx(), l2OutputBlockNr) header, err := s.L2.env.EthCl.HeaderByNumber(t.Ctx(), l2OutputBlockNr)
require.NoError(t, err) require.NoError(t, err)
params, err := withdrawals.FinalizeWithdrawalParameters(t.Ctx(), s.L2.env.Bindings.ProofClient, s.L2.env.EthCl, s.lastL2WithdrawalTxHash, header) params, err := withdrawals.ProveWithdrawalParameters(t.Ctx(), s.L2.env.Bindings.ProofClient, s.L2.env.EthCl, s.lastL2WithdrawalTxHash, header)
require.NoError(t, err) require.NoError(t, err)
// Create the withdrawal tx // Create the withdrawal tx
...@@ -436,14 +492,11 @@ func (s *CrossLayerUser) CompleteWithdrawal(t Testing, l2TxHash common.Hash) com ...@@ -436,14 +492,11 @@ func (s *CrossLayerUser) CompleteWithdrawal(t Testing, l2TxHash common.Hash) com
GasLimit: params.GasLimit, GasLimit: params.GasLimit,
Data: params.Data, Data: params.Data,
}, },
params.BlockNumber,
params.OutputRootProof,
params.WithdrawalProof,
) )
require.NoError(t, err) require.NoError(t, err)
// Send the actual tx (since tx opts don't send by default) // Send the actual tx (since tx opts don't send by default)
err = s.L1.env.EthCl.SendTransaction(t.Ctx(), tx) err = s.L1.env.EthCl.SendTransaction(t.Ctx(), tx)
require.NoError(t, err, "must send tx") require.NoError(t, err, "must send finalize tx")
return tx.Hash() return tx.Hash()
} }
...@@ -17,6 +17,8 @@ import ( ...@@ -17,6 +17,8 @@ import (
// - transact on L2 // - transact on L2
// - deposit on L1 // - deposit on L1
// - withdraw from L2 // - withdraw from L2
// - prove tx on L1
// - wait 1 week + 1 second
// - finalize withdrawal on L1 // - finalize withdrawal on L1
func TestCrossLayerUser(gt *testing.T) { func TestCrossLayerUser(gt *testing.T) {
t := NewDefaultTesting(gt) t := NewDefaultTesting(gt)
...@@ -133,7 +135,22 @@ func TestCrossLayerUser(gt *testing.T) { ...@@ -133,7 +135,22 @@ func TestCrossLayerUser(gt *testing.T) {
require.Equal(t, types.ReceiptStatusSuccessful, receipt.Status, "proposal failed") require.Equal(t, types.ReceiptStatusSuccessful, receipt.Status, "proposal failed")
} }
// make the L1 side of the withdrawal tx // prove our withdrawal on L1
alice.ActProveWithdrawal(t)
// include proved withdrawal in new L1 block
miner.ActL1StartBlock(12)(t)
miner.ActL1IncludeTx(alice.Address())(t)
miner.ActL1EndBlock(t)
// check withdrawal succeeded
alice.L1.ActCheckReceiptStatusOfLastTx(true)(t)
// A bit hacky- Mines an empty block with the time delta
// of the finalization period (12s) + 1 in order for the
// withdrawal to be finalized successfully.
miner.ActL1StartBlock(13)(t)
miner.ActL1EndBlock(t)
// make the L1 finalize withdrawal tx
alice.ActCompleteWithdrawal(t) alice.ActCompleteWithdrawal(t)
// include completed withdrawal in new L1 block // include completed withdrawal in new L1 block
miner.ActL1StartBlock(12)(t) miner.ActL1StartBlock(12)(t)
......
...@@ -85,6 +85,7 @@ func MakeDeployParams(t require.TestingT, tp *TestParams) *DeployParams { ...@@ -85,6 +85,7 @@ func MakeDeployParams(t require.TestingT, tp *TestParams) *DeployParams {
L1GenesisBlockGasUsed: 0, L1GenesisBlockGasUsed: 0,
L1GenesisBlockParentHash: common.Hash{}, L1GenesisBlockParentHash: common.Hash{},
L1GenesisBlockBaseFeePerGas: uint64ToBig(1000_000_000), // 1 gwei L1GenesisBlockBaseFeePerGas: uint64ToBig(1000_000_000), // 1 gwei
FinalizationPeriodSeconds: 12,
L2GenesisBlockNonce: 0, L2GenesisBlockNonce: 0,
L2GenesisBlockExtraData: []byte{}, L2GenesisBlockExtraData: []byte{},
......
...@@ -819,7 +819,7 @@ func TestWithdrawals(t *testing.T) { ...@@ -819,7 +819,7 @@ func TestWithdrawals(t *testing.T) {
startBalance, err = l1Client.BalanceAt(ctx, fromAddr, nil) startBalance, err = l1Client.BalanceAt(ctx, fromAddr, nil)
require.Nil(t, err) require.Nil(t, err)
// Wait for finalization and then create the Finalized Withdrawal Transaction // Get l2BlockNumber for proof generation
ctx, cancel = context.WithTimeout(context.Background(), 20*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second) ctx, cancel = context.WithTimeout(context.Background(), 20*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second)
defer cancel() defer cancel()
blockNumber, err := withdrawals.WaitForFinalizationPeriod(ctx, l1Client, predeploys.DevOptimismPortalAddr, receipt.BlockNumber) blockNumber, err := withdrawals.WaitForFinalizationPeriod(ctx, l1Client, predeploys.DevOptimismPortalAddr, receipt.BlockNumber)
...@@ -836,14 +836,16 @@ func TestWithdrawals(t *testing.T) { ...@@ -836,14 +836,16 @@ func TestWithdrawals(t *testing.T) {
receiptCl := ethclient.NewClient(rpcClient) receiptCl := ethclient.NewClient(rpcClient)
// Now create withdrawal // Now create withdrawal
params, err := withdrawals.FinalizeWithdrawalParameters(context.Background(), proofCl, receiptCl, tx.Hash(), header) params, err := withdrawals.ProveWithdrawalParameters(context.Background(), proofCl, receiptCl, tx.Hash(), header)
require.Nil(t, err) require.Nil(t, err)
portal, err := bindings.NewOptimismPortal(predeploys.DevOptimismPortalAddr, l1Client) portal, err := bindings.NewOptimismPortal(predeploys.DevOptimismPortalAddr, l1Client)
require.Nil(t, err) require.Nil(t, err)
opts.Value = nil opts.Value = nil
tx, err = portal.FinalizeWithdrawalTransaction(
// Prove withdrawal
tx, err = portal.ProveWithdrawalTransaction(
opts, opts,
bindings.TypesWithdrawalTransaction{ bindings.TypesWithdrawalTransaction{
Nonce: params.Nonce, Nonce: params.Nonce,
...@@ -857,17 +859,42 @@ func TestWithdrawals(t *testing.T) { ...@@ -857,17 +859,42 @@ func TestWithdrawals(t *testing.T) {
params.OutputRootProof, params.OutputRootProof,
params.WithdrawalProof, params.WithdrawalProof,
) )
require.Nil(t, err)
// Ensure that our withdrawal was proved successfully
proveReceipt, err := waitForTransaction(tx.Hash(), l1Client, 3*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second)
require.Nil(t, err, "prove withdrawal")
require.Equal(t, types.ReceiptStatusSuccessful, proveReceipt.Status)
// Wait for finalization and then create the Finalized Withdrawal Transaction
ctx, cancel = context.WithTimeout(context.Background(), 20*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second)
defer cancel()
_, err = withdrawals.WaitForFinalizationPeriod(ctx, l1Client, predeploys.DevOptimismPortalAddr, params.BlockNumber)
require.Nil(t, err)
// Finalize withdrawal
tx, err = portal.FinalizeWithdrawalTransaction(
opts,
bindings.TypesWithdrawalTransaction{
Nonce: params.Nonce,
Sender: params.Sender,
Target: params.Target,
Value: params.Value,
GasLimit: params.GasLimit,
Data: params.Data,
},
)
require.Nil(t, err) require.Nil(t, err)
receipt, err = waitForTransaction(tx.Hash(), l1Client, 3*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second) // Ensure that our withdrawal was finalized successfully
finalizeReceipt, err := waitForTransaction(tx.Hash(), l1Client, 3*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second)
require.Nil(t, err, "finalize withdrawal") require.Nil(t, err, "finalize withdrawal")
require.Equal(t, types.ReceiptStatusSuccessful, receipt.Status) require.Equal(t, types.ReceiptStatusSuccessful, finalizeReceipt.Status)
// Verify balance after withdrawal // Verify balance after withdrawal
ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second) ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second)
defer cancel() defer cancel()
header, err = l1Client.HeaderByNumber(ctx, receipt.BlockNumber) header, err = l1Client.HeaderByNumber(ctx, finalizeReceipt.BlockNumber)
require.Nil(t, err) require.Nil(t, err)
ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second) ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second)
...@@ -877,8 +904,9 @@ func TestWithdrawals(t *testing.T) { ...@@ -877,8 +904,9 @@ func TestWithdrawals(t *testing.T) {
// Ensure that withdrawal - gas fees are added to the L1 balance // Ensure that withdrawal - gas fees are added to the L1 balance
// Fun fact, the fee is greater than the withdrawal amount // Fun fact, the fee is greater than the withdrawal amount
// NOTE: The gas fees include *both* the ProveWithdrawalTransaction and FinalizeWithdrawalTransaction transactions.
diff = new(big.Int).Sub(endBalance, startBalance) diff = new(big.Int).Sub(endBalance, startBalance)
fees = calcGasFees(receipt.GasUsed, tx.GasTipCap(), tx.GasFeeCap(), header.BaseFee) fees = calcGasFees(proveReceipt.GasUsed+finalizeReceipt.GasUsed, tx.GasTipCap(), tx.GasFeeCap(), header.BaseFee)
withdrawAmount = withdrawAmount.Sub(withdrawAmount, fees) withdrawAmount = withdrawAmount.Sub(withdrawAmount, fees)
require.Equal(t, withdrawAmount, diff) require.Equal(t, withdrawAmount, diff)
} }
......
...@@ -129,8 +129,9 @@ type ReceiptClient interface { ...@@ -129,8 +129,9 @@ type ReceiptClient interface {
TransactionReceipt(context.Context, common.Hash) (*types.Receipt, error) TransactionReceipt(context.Context, common.Hash) (*types.Receipt, error)
} }
// FinalizedWithdrawalParameters is the set of parameters to pass to the FinalizedWithdrawal function // ProvenWithdrawalParameters is the set of parameters to pass to the ProveWithdrawalTransaction
type FinalizedWithdrawalParameters struct { // and FinalizeWithdrawalTransaction functions
type ProvenWithdrawalParameters struct {
Nonce *big.Int Nonce *big.Int
Sender common.Address Sender common.Address
Target common.Address Target common.Address
...@@ -142,40 +143,40 @@ type FinalizedWithdrawalParameters struct { ...@@ -142,40 +143,40 @@ type FinalizedWithdrawalParameters struct {
WithdrawalProof [][]byte // List of trie nodes to prove L2 storage WithdrawalProof [][]byte // List of trie nodes to prove L2 storage
} }
// FinalizeWithdrawalParameters queries L2 to generate all withdrawal parameters and proof necessary to finalize an withdrawal on L1. // ProveWithdrawalParameters queries L2 to generate all withdrawal parameters and proof necessary to prove a withdrawal on L1.
// The header provided is very important. It should be a block (timestamp) for which there is a submitted output in the L2 Output Oracle // The header provided is very important. It should be a block (timestamp) for which there is a submitted output in the L2 Output Oracle
// contract. If not, the withdrawal will fail as it the storage proof cannot be verified if there is no submitted state root. // contract. If not, the withdrawal will fail as it the storage proof cannot be verified if there is no submitted state root.
func FinalizeWithdrawalParameters(ctx context.Context, proofCl ProofClient, l2ReceiptCl ReceiptClient, txHash common.Hash, header *types.Header) (FinalizedWithdrawalParameters, error) { func ProveWithdrawalParameters(ctx context.Context, proofCl ProofClient, l2ReceiptCl ReceiptClient, txHash common.Hash, header *types.Header) (ProvenWithdrawalParameters, error) {
// Transaction receipt // Transaction receipt
receipt, err := l2ReceiptCl.TransactionReceipt(ctx, txHash) receipt, err := l2ReceiptCl.TransactionReceipt(ctx, txHash)
if err != nil { if err != nil {
return FinalizedWithdrawalParameters{}, err return ProvenWithdrawalParameters{}, err
} }
// Parse the receipt // Parse the receipt
ev, err := ParseMessagePassed(receipt) ev, err := ParseMessagePassed(receipt)
if err != nil { if err != nil {
return FinalizedWithdrawalParameters{}, err return ProvenWithdrawalParameters{}, err
} }
// Generate then verify the withdrawal proof // Generate then verify the withdrawal proof
withdrawalHash, err := WithdrawalHash(ev) withdrawalHash, err := WithdrawalHash(ev)
if !bytes.Equal(withdrawalHash[:], ev.WithdrawalHash[:]) { if !bytes.Equal(withdrawalHash[:], ev.WithdrawalHash[:]) {
return FinalizedWithdrawalParameters{}, errors.New("Computed withdrawal hash incorrectly") return ProvenWithdrawalParameters{}, errors.New("Computed withdrawal hash incorrectly")
} }
if err != nil { if err != nil {
return FinalizedWithdrawalParameters{}, err return ProvenWithdrawalParameters{}, err
} }
slot := StorageSlotOfWithdrawalHash(withdrawalHash) slot := StorageSlotOfWithdrawalHash(withdrawalHash)
p, err := proofCl.GetProof(ctx, predeploys.L2ToL1MessagePasserAddr, []string{slot.String()}, header.Number) p, err := proofCl.GetProof(ctx, predeploys.L2ToL1MessagePasserAddr, []string{slot.String()}, header.Number)
if err != nil { if err != nil {
return FinalizedWithdrawalParameters{}, err return ProvenWithdrawalParameters{}, err
} }
// TODO: Could skip this step, but it's nice to double check it // TODO: Could skip this step, but it's nice to double check it
err = VerifyProof(header.Root, p) err = VerifyProof(header.Root, p)
if err != nil { if err != nil {
return FinalizedWithdrawalParameters{}, err return ProvenWithdrawalParameters{}, err
} }
if len(p.StorageProof) != 1 { if len(p.StorageProof) != 1 {
return FinalizedWithdrawalParameters{}, errors.New("invalid amount of storage proofs") return ProvenWithdrawalParameters{}, errors.New("invalid amount of storage proofs")
} }
// Encode it as expected by the contract // Encode it as expected by the contract
...@@ -184,7 +185,7 @@ func FinalizeWithdrawalParameters(ctx context.Context, proofCl ProofClient, l2Re ...@@ -184,7 +185,7 @@ func FinalizeWithdrawalParameters(ctx context.Context, proofCl ProofClient, l2Re
trieNodes[i] = common.FromHex(s) trieNodes[i] = common.FromHex(s)
} }
return FinalizedWithdrawalParameters{ return ProvenWithdrawalParameters{
Nonce: ev.Nonce, Nonce: ev.Nonce,
Sender: ev.Sender, Sender: ev.Sender,
Target: ev.Target, Target: ev.Target,
......
AddressAliasHelper_Test:test_fuzz_roundtrip(address) (runs: 256, μ: 466, ~: 466) AddressAliasHelper_Test:test_fuzz_roundtrip(address) (runs: 256, μ: 466, ~: 466)
GasBenchMark_L1CrossDomainMessenger:test_L1MessengerSendMessage_benchmark_0() (gas: 261401) GasBenchMark_L1CrossDomainMessenger:test_L1MessengerSendMessage_benchmark_0() (gas: 261393)
GasBenchMark_L1CrossDomainMessenger:test_L1MessengerSendMessage_benchmark_1() (gas: 75561) GasBenchMark_L1CrossDomainMessenger:test_L1MessengerSendMessage_benchmark_1() (gas: 75533)
GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_0() (gas: 348162) GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_0() (gas: 348154)
GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_1() (gas: 112340) GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_1() (gas: 112312)
GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_0() (gas: 348184) GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_0() (gas: 348176)
GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_1() (gas: 112315) GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_1() (gas: 112287)
GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (gas: 40569) GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (gas: 40569)
GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 68693) GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 68693)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 75010) GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 75002)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 35823) GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 35795)
CrossDomainMessenger_Test:testFuzz_baseGas(uint32) (runs: 256, μ: 20263, ~: 20263) CrossDomainMessenger_Test:testFuzz_baseGas(uint32) (runs: 256, μ: 20263, ~: 20263)
CrossDomainMessenger_Test:test_baseGas() (gas: 20100) CrossDomainMessenger_Test:test_baseGas() (gas: 20100)
CrossDomainOwnableThroughPortal_Test:test_depositTransaction_crossDomainOwner() (gas: 61896) CrossDomainOwnableThroughPortal_Test:test_depositTransaction_crossDomainOwner() (gas: 61888)
CrossDomainOwnable_Test:test_onlyOwner() (gas: 34945) CrossDomainOwnable_Test:test_onlyOwner() (gas: 34945)
CrossDomainOwnable_Test:test_revertOnlyOwner() (gas: 10619) CrossDomainOwnable_Test:test_revertOnlyOwner() (gas: 10619)
CrossDomainOwnable2_Test:test_onlyOwner() (gas: 77878) CrossDomainOwnable2_Test:test_onlyOwner() (gas: 77878)
...@@ -20,9 +20,9 @@ CrossDomainOwnable2_Test:test_revertNotSetOnlyOwner2() (gas: 14598) ...@@ -20,9 +20,9 @@ CrossDomainOwnable2_Test:test_revertNotSetOnlyOwner2() (gas: 14598)
CrossDomainOwnable2_Test:test_revertOnlyOwner() (gas: 61800) CrossDomainOwnable2_Test:test_revertOnlyOwner() (gas: 61800)
DeployerWhitelist_Test:test_owner() (gas: 7591) DeployerWhitelist_Test:test_owner() (gas: 7591)
DeployerWhitelist_Test:test_storageSlots() (gas: 33427) DeployerWhitelist_Test:test_storageSlots() (gas: 33427)
Encoding_Test:test_decodeVersionedNonce_differential(uint240,uint16) (runs: 256, μ: 13329, ~: 13345) Encoding_Test:test_decodeVersionedNonce_differential(uint240,uint16) (runs: 256, μ: 13351, ~: 13367)
Encoding_Test:test_encodeCrossDomainMessage_differential(uint240,uint8,address,address,uint256,uint256,bytes) (runs: 256, μ: 87650, ~: 91649) Encoding_Test:test_encodeCrossDomainMessage_differential(uint240,uint8,address,address,uint256,uint256,bytes) (runs: 256, μ: 87476, ~: 91604)
Encoding_Test:test_encodeDepositTransaction_differential(address,address,uint256,uint256,uint64,bool,bytes,uint256) (runs: 256, μ: 106526, ~: 101730) Encoding_Test:test_encodeDepositTransaction_differential(address,address,uint256,uint256,uint64,bool,bytes,uint256) (runs: 256, μ: 106528, ~: 101730)
Encoding_Test:test_nonceVersioning(uint240,uint16) (runs: 256, μ: 652, ~: 652) Encoding_Test:test_nonceVersioning(uint240,uint16) (runs: 256, μ: 652, ~: 652)
FeeVault_Test:test_constructor() (gas: 10748) FeeVault_Test:test_constructor() (gas: 10748)
FeeVault_Test:test_minWithdrawalAmount() (gas: 10711) FeeVault_Test:test_minWithdrawalAmount() (gas: 10711)
...@@ -35,9 +35,9 @@ GasPriceOracle_Test:test_setGasPriceReverts() (gas: 5911) ...@@ -35,9 +35,9 @@ GasPriceOracle_Test:test_setGasPriceReverts() (gas: 5911)
GasPriceOracle_Test:test_setL1BaseFeeReverts() (gas: 5954) GasPriceOracle_Test:test_setL1BaseFeeReverts() (gas: 5954)
Hashing_Test:test_hashCrossDomainMessage_differential(uint256,address,address,uint256,uint256,bytes) (runs: 256, μ: 23905, ~: 23788) Hashing_Test:test_hashCrossDomainMessage_differential(uint256,address,address,uint256,uint256,bytes) (runs: 256, μ: 23905, ~: 23788)
Hashing_Test:test_hashDepositSource() (gas: 628) Hashing_Test:test_hashDepositSource() (gas: 628)
Hashing_Test:test_hashDepositTransaction_differential(address,address,uint256,uint256,uint64,bytes,uint256) (runs: 256, μ: 66710, ~: 66432) Hashing_Test:test_hashDepositTransaction_differential(address,address,uint256,uint256,uint64,bytes,uint256) (runs: 256, μ: 66644, ~: 66366)
Hashing_Test:test_hashOutputRootProof_differential(bytes32,bytes32,bytes32,bytes32) (runs: 256, μ: 72062, ~: 93483) Hashing_Test:test_hashOutputRootProof_differential(bytes32,bytes32,bytes32,bytes32) (runs: 256, μ: 72084, ~: 93505)
Hashing_Test:test_hashWithdrawal_differential(uint256,address,address,uint256,uint256,bytes) (runs: 256, μ: 23654, ~: 23478) Hashing_Test:test_hashWithdrawal_differential(uint256,address,address,uint256,uint256,bytes) (runs: 256, μ: 23676, ~: 23500)
L1BlockTest:test_basefee() (gas: 7553) L1BlockTest:test_basefee() (gas: 7553)
L1BlockTest:test_hash() (gas: 7597) L1BlockTest:test_hash() (gas: 7597)
L1BlockTest:test_number() (gas: 7630) L1BlockTest:test_number() (gas: 7630)
...@@ -52,23 +52,23 @@ L1CrossDomainMessenger_Test:testCannot_L1MessengerPause() (gas: 24560) ...@@ -52,23 +52,23 @@ L1CrossDomainMessenger_Test:testCannot_L1MessengerPause() (gas: 24560)
L1CrossDomainMessenger_Test:testCannot_L1MessengerUnpause() (gas: 24518) L1CrossDomainMessenger_Test:testCannot_L1MessengerUnpause() (gas: 24518)
L1CrossDomainMessenger_Test:test_L1MessengerMessageVersion() (gas: 24738) L1CrossDomainMessenger_Test:test_L1MessengerMessageVersion() (gas: 24738)
L1CrossDomainMessenger_Test:test_L1MessengerPause() (gas: 52972) L1CrossDomainMessenger_Test:test_L1MessengerPause() (gas: 52972)
L1CrossDomainMessenger_Test:test_L1MessengerRelayMessageFirstStuckSecondSucceeds() (gas: 197363) L1CrossDomainMessenger_Test:test_L1MessengerRelayMessageFirstStuckSecondSucceeds() (gas: 197386)
L1CrossDomainMessenger_Test:test_L1MessengerRelayMessageRevertsOnReentrancy() (gas: 190971) L1CrossDomainMessenger_Test:test_L1MessengerRelayMessageRevertsOnReentrancy() (gas: 190994)
L1CrossDomainMessenger_Test:test_L1MessengerRelayMessageSucceeds() (gas: 73729) L1CrossDomainMessenger_Test:test_L1MessengerRelayMessageSucceeds() (gas: 73752)
L1CrossDomainMessenger_Test:test_L1MessengerRelayMessageToSystemContract() (gas: 65858) L1CrossDomainMessenger_Test:test_L1MessengerRelayMessageToSystemContract() (gas: 65881)
L1CrossDomainMessenger_Test:test_L1MessengerRelayMessageV0Fails() (gas: 33280) L1CrossDomainMessenger_Test:test_L1MessengerRelayMessageV0Fails() (gas: 33280)
L1CrossDomainMessenger_Test:test_L1MessengerRelayShouldRevertIfPaused() (gas: 60549) L1CrossDomainMessenger_Test:test_L1MessengerRelayShouldRevertIfPaused() (gas: 60549)
L1CrossDomainMessenger_Test:test_L1MessengerReplayMessageWithValue() (gas: 38215) L1CrossDomainMessenger_Test:test_L1MessengerReplayMessageWithValue() (gas: 38215)
L1CrossDomainMessenger_Test:test_L1MessengerSendMessage() (gas: 299555) L1CrossDomainMessenger_Test:test_L1MessengerSendMessage() (gas: 299547)
L1CrossDomainMessenger_Test:test_L1MessengerTwiceSendMessage() (gas: 1490502) L1CrossDomainMessenger_Test:test_L1MessengerTwiceSendMessage() (gas: 1490486)
L1CrossDomainMessenger_Test:test_L1MessengerUnpause() (gas: 45233) L1CrossDomainMessenger_Test:test_L1MessengerUnpause() (gas: 45233)
L1CrossDomainMessenger_Test:test_L1MessengerXDomainSenderReverts() (gas: 24283) L1CrossDomainMessenger_Test:test_L1MessengerXDomainSenderReverts() (gas: 24283)
L1CrossDomainMessenger_Test:test_L1MessengerxDomainMessageSenderResets() (gas: 84115) L1CrossDomainMessenger_Test:test_L1MessengerxDomainMessageSenderResets() (gas: 84138)
L1StandardBridge_DepositERC20To_Test:test_depositERC20To_success() (gas: 575637) L1StandardBridge_DepositERC20To_Test:test_depositERC20To_success() (gas: 575801)
L1StandardBridge_DepositERC20_Test:test_depositERC20_succeeds() (gas: 573505) L1StandardBridge_DepositERC20_Test:test_depositERC20_succeeds() (gas: 573669)
L1StandardBridge_DepositERC20_TestFail:test_depositERC20_revert_notEoa() (gas: 22321) L1StandardBridge_DepositERC20_TestFail:test_depositERC20_revert_notEoa() (gas: 22321)
L1StandardBridge_DepositETHTo_Test:test_depositETHTo() (gas: 324769) L1StandardBridge_DepositETHTo_Test:test_depositETHTo() (gas: 324761)
L1StandardBridge_DepositETH_Test:test_depositETH_success() (gas: 367616) L1StandardBridge_DepositETH_Test:test_depositETH_success() (gas: 367608)
L1StandardBridge_DepositETH_TestFail:test_DepositETH_revert_notEoa() (gas: 40781) L1StandardBridge_DepositETH_TestFail:test_DepositETH_revert_notEoa() (gas: 40781)
L1StandardBridge_FinalizeBridgeETH_TestFail:test_finalizeBridgeETH_revert_incorrectValue() (gas: 34229) L1StandardBridge_FinalizeBridgeETH_TestFail:test_finalizeBridgeETH_revert_incorrectValue() (gas: 34229)
L1StandardBridge_FinalizeBridgeETH_TestFail:test_finalizeBridgeETH_revert_sendToMessenger() (gas: 34312) L1StandardBridge_FinalizeBridgeETH_TestFail:test_finalizeBridgeETH_revert_sendToMessenger() (gas: 34312)
...@@ -79,7 +79,7 @@ L1StandardBridge_FinalizeERC20Withdrawal_TestFail:test_finalizeERC20Withdrawal_r ...@@ -79,7 +79,7 @@ L1StandardBridge_FinalizeERC20Withdrawal_TestFail:test_finalizeERC20Withdrawal_r
L1StandardBridge_FinalizeETHWithdrawal_Test:test_finalizeETHWithdrawal() (gas: 58708) L1StandardBridge_FinalizeETHWithdrawal_Test:test_finalizeETHWithdrawal() (gas: 58708)
L1StandardBridge_Getter_Test:test_getters_success() (gas: 31568) L1StandardBridge_Getter_Test:test_getters_success() (gas: 31568)
L1StandardBridge_Initialize_Test:test_initialize_success() (gas: 22072) L1StandardBridge_Initialize_Test:test_initialize_success() (gas: 22072)
L1StandardBridge_Receive_Test:test_receive_success() (gas: 514315) L1StandardBridge_Receive_Test:test_receive_success() (gas: 514479)
L2CrossDomainMessenger_Test:testCannot_L2MessengerPause() (gas: 10860) L2CrossDomainMessenger_Test:testCannot_L2MessengerPause() (gas: 10860)
L2CrossDomainMessenger_Test:test_L1MessengerRelayMessageRevertsOnReentrancy() (gas: 167833) L2CrossDomainMessenger_Test:test_L1MessengerRelayMessageRevertsOnReentrancy() (gas: 167833)
L2CrossDomainMessenger_Test:test_L2MessengerMessageVersion() (gas: 8411) L2CrossDomainMessenger_Test:test_L2MessengerMessageVersion() (gas: 8411)
...@@ -154,33 +154,39 @@ OptimismMintableTokenFactory_Test:test_bridge() (gas: 7672) ...@@ -154,33 +154,39 @@ OptimismMintableTokenFactory_Test:test_bridge() (gas: 7672)
OptimismMintableTokenFactory_Test:test_createStandardL2Token() (gas: 1109858) OptimismMintableTokenFactory_Test:test_createStandardL2Token() (gas: 1109858)
OptimismMintableTokenFactory_Test:test_createStandardL2TokenSameTwice() (gas: 2205813) OptimismMintableTokenFactory_Test:test_createStandardL2TokenSameTwice() (gas: 2205813)
OptimismMintableTokenFactory_Test:test_createStandardL2TokenShouldRevertIfRemoteIsZero() (gas: 9443) OptimismMintableTokenFactory_Test:test_createStandardL2TokenShouldRevertIfRemoteIsZero() (gas: 9443)
OptimismPortalUpgradeable_Test:test_cannotInitImpl() (gas: 10835) OptimismPortalUpgradeable_Test:test_initialize_cannotInitImpl_reverts() (gas: 10791)
OptimismPortalUpgradeable_Test:test_cannotInitProxy() (gas: 15811) OptimismPortalUpgradeable_Test:test_initialize_cannotInitProxy_reverts() (gas: 15834)
OptimismPortalUpgradeable_Test:test_initValuesOnProxy() (gas: 15989) OptimismPortalUpgradeable_Test:test_params_initValuesOnProxy_success() (gas: 16010)
OptimismPortalUpgradeable_Test:test_upgrading() (gas: 180632) OptimismPortalUpgradeable_Test:test_upgradeToAndCall_upgrading_success() (gas: 180630)
OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_differential(address,address,uint256,uint256,bytes) (runs: 256, μ: 234653, ~: 235021) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_differential_success(address,address,uint256,uint256,bytes) (runs: 256, μ: 220459, ~: 220292)
OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_revertsOnInsufficientGas() (gas: 135146) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_ifOutputRootChanges_reverts() (gas: 192857)
OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_revertsOnInvalidOutputRootProof() (gas: 83661) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_ifOutputTimestampIsNotFinalized_reverts() (gas: 195122)
OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_revertsOnRecentWithdrawal() (gas: 52898) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_ifWithdrawalNotProven_reverts() (gas: 39628)
OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_revertsOnReentrancy() (gas: 180409) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_ifWithdrawalProofNotOldEnough_reverts() (gas: 190555)
OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_revertsOnReplay() (gas: 232157) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_onInsufficientGas_reverts() (gas: 193103)
OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_revertsOnSelfCall() (gas: 52937) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_onRecentWithdrawal_reverts() (gas: 173032)
OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_revertsOninvalidWithdrawalProof() (gas: 132466) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_onReentrancy_reverts() (gas: 233436)
OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_succeeds() (gas: 164519) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_onReplay_reverts() (gas: 232836)
OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_targetFails() (gas: 267168) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_provenWithdrawalHash_success() (gas: 224781)
OptimismPortal_Test:test_OptimismPortalConstructor() (gas: 17319) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_targetFails_fails() (gas: 327453)
OptimismPortal_Test:test_OptimismPortalContractCreationReverts() (gas: 14245) OptimismPortal_FinalizeWithdrawal_Test:test_finalizeWithdrawalTransaction_timestampLessThanL2OracleStart_reverts() (gas: 191332)
OptimismPortal_Test:test_OptimismPortalReceiveEth() (gas: 127534) OptimismPortal_FinalizeWithdrawal_Test:test_proveWithdrawalTransaction_onInvalidOutputRootProof_reverts() (gas: 81377)
OptimismPortal_Test:test_depositTransaction_NoValueContract() (gas: 76714) OptimismPortal_FinalizeWithdrawal_Test:test_proveWithdrawalTransaction_onSelfCall_reverts() (gas: 50821)
OptimismPortal_Test:test_depositTransaction_NoValueEOA() (gas: 77015) OptimismPortal_FinalizeWithdrawal_Test:test_proveWithdrawalTransaction_oninvalidWithdrawalProof_reverts() (gas: 130170)
OptimismPortal_Test:test_depositTransaction_createWithZeroValueForContract() (gas: 76696) OptimismPortal_FinalizeWithdrawal_Test:test_proveWithdrawalTransaction_validWithdrawalProof_success() (gas: 176914)
OptimismPortal_Test:test_depositTransaction_createWithZeroValueForEOA() (gas: 77040) OptimismPortal_Test:test_OptimismPortalConstructor() (gas: 17342)
OptimismPortal_Test:test_depositTransaction_withEthValueAndContractContractCreation() (gas: 83740) OptimismPortal_Test:test_OptimismPortalReceiveEth_success() (gas: 127528)
OptimismPortal_Test:test_depositTransaction_withEthValueAndEOAContractCreation() (gas: 75927) OptimismPortal_Test:test_depositTransaction_NoValueContract_success() (gas: 76728)
OptimismPortal_Test:test_depositTransaction_withEthValueFromContract() (gas: 83379) OptimismPortal_Test:test_depositTransaction_NoValueEOA_success() (gas: 77006)
OptimismPortal_Test:test_depositTransaction_withEthValueFromEOA() (gas: 84039) OptimismPortal_Test:test_depositTransaction_contractCreation_reverts() (gas: 14224)
OptimismPortal_Test:test_isBlockFinalized() (gas: 114183) OptimismPortal_Test:test_depositTransaction_createWithZeroValueForContract_success() (gas: 76688)
OptimismPortal_Test:test_simple_isBlockFinalized() (gas: 24262) OptimismPortal_Test:test_depositTransaction_createWithZeroValueForEOA_success() (gas: 77076)
OptimismPortal_Test:test_depositTransaction_withEthValueAndContractContractCreation_success() (gas: 83690)
OptimismPortal_Test:test_depositTransaction_withEthValueAndEOAContractCreation_success() (gas: 75897)
OptimismPortal_Test:test_depositTransaction_withEthValueFromContract_success() (gas: 83414)
OptimismPortal_Test:test_depositTransaction_withEthValueFromEOA_success() (gas: 84009)
OptimismPortal_Test:test_isBlockFinalized_success() (gas: 113932)
OptimismPortal_Test:test_simple_isBlockFinalized_success() (gas: 24222)
Proxy_Test:test_clashingFunctionSignatures() (gas: 101427) Proxy_Test:test_clashingFunctionSignatures() (gas: 101427)
Proxy_Test:test_implementationKey() (gas: 20942) Proxy_Test:test_implementationKey() (gas: 20942)
Proxy_Test:test_implementationProxyCallIfNotAdmin() (gas: 30021) Proxy_Test:test_implementationProxyCallIfNotAdmin() (gas: 30021)
......
...@@ -88,21 +88,23 @@ ...@@ -88,21 +88,23 @@
➡ contracts/L1/OptimismPortal.sol:OptimismPortal ➡ contracts/L1/OptimismPortal.sol:OptimismPortal
======================= =======================
+----------------------+----------------------------------------+------+--------+-------+------------------------------------------------+ +----------------------+------------------------------------------------------------+------+--------+-------+------------------------------------------------+
| Name | Type | Slot | Offset | Bytes | Contract | | Name | Type | Slot | Offset | Bytes | Contract |
+========================================================================================================================================+ +============================================================================================================================================================+
| _initialized | uint8 | 0 | 0 | 1 | contracts/L1/OptimismPortal.sol:OptimismPortal | | _initialized | uint8 | 0 | 0 | 1 | contracts/L1/OptimismPortal.sol:OptimismPortal |
|----------------------+----------------------------------------+------+--------+-------+------------------------------------------------| |----------------------+------------------------------------------------------------+------+--------+-------+------------------------------------------------|
| _initializing | bool | 0 | 1 | 1 | contracts/L1/OptimismPortal.sol:OptimismPortal | | _initializing | bool | 0 | 1 | 1 | contracts/L1/OptimismPortal.sol:OptimismPortal |
|----------------------+----------------------------------------+------+--------+-------+------------------------------------------------| |----------------------+------------------------------------------------------------+------+--------+-------+------------------------------------------------|
| params | struct ResourceMetering.ResourceParams | 1 | 0 | 32 | contracts/L1/OptimismPortal.sol:OptimismPortal | | params | struct ResourceMetering.ResourceParams | 1 | 0 | 32 | contracts/L1/OptimismPortal.sol:OptimismPortal |
|----------------------+----------------------------------------+------+--------+-------+------------------------------------------------| |----------------------+------------------------------------------------------------+------+--------+-------+------------------------------------------------|
| __gap | uint256[48] | 2 | 0 | 1536 | contracts/L1/OptimismPortal.sol:OptimismPortal | | __gap | uint256[48] | 2 | 0 | 1536 | contracts/L1/OptimismPortal.sol:OptimismPortal |
|----------------------+----------------------------------------+------+--------+-------+------------------------------------------------| |----------------------+------------------------------------------------------------+------+--------+-------+------------------------------------------------|
| l2Sender | address | 50 | 0 | 20 | contracts/L1/OptimismPortal.sol:OptimismPortal | | l2Sender | address | 50 | 0 | 20 | contracts/L1/OptimismPortal.sol:OptimismPortal |
|----------------------+----------------------------------------+------+--------+-------+------------------------------------------------| |----------------------+------------------------------------------------------------+------+--------+-------+------------------------------------------------|
| finalizedWithdrawals | mapping(bytes32 => bool) | 51 | 0 | 32 | contracts/L1/OptimismPortal.sol:OptimismPortal | | finalizedWithdrawals | mapping(bytes32 => bool) | 51 | 0 | 32 | contracts/L1/OptimismPortal.sol:OptimismPortal |
+----------------------+----------------------------------------+------+--------+-------+------------------------------------------------+ |----------------------+------------------------------------------------------------+------+--------+-------+------------------------------------------------|
| provenWithdrawals | mapping(bytes32 => struct OptimismPortal.ProvenWithdrawal) | 52 | 0 | 32 | contracts/L1/OptimismPortal.sol:OptimismPortal |
+----------------------+------------------------------------------------------------+------+--------+-------+------------------------------------------------+
======================= =======================
➡ contracts/L1/SystemConfig.sol:SystemConfig ➡ contracts/L1/SystemConfig.sol:SystemConfig
......
...@@ -19,6 +19,15 @@ import { Semver } from "../universal/Semver.sol"; ...@@ -19,6 +19,15 @@ import { Semver } from "../universal/Semver.sol";
* Users are encouraged to use the L1CrossDomainMessenger for a higher-level interface. * Users are encouraged to use the L1CrossDomainMessenger for a higher-level interface.
*/ */
contract OptimismPortal is Initializable, ResourceMetering, Semver { contract OptimismPortal is Initializable, ResourceMetering, Semver {
/**
* @notice Represents a proven withdrawal
*/
struct ProvenWithdrawal {
bytes32 outputRoot;
uint128 timestamp;
uint128 l2BlockNumber;
}
/** /**
* @notice Version of the deposit event. * @notice Version of the deposit event.
*/ */
...@@ -61,6 +70,11 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver { ...@@ -61,6 +70,11 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
*/ */
mapping(bytes32 => bool) public finalizedWithdrawals; mapping(bytes32 => bool) public finalizedWithdrawals;
/**
* @notice A mapping of withdrawal hashes to `ProvenWithdrawal` data.
*/
mapping(bytes32 => ProvenWithdrawal) public provenWithdrawals;
/** /**
* @notice Emitted when a transaction is deposited from L1 to L2. The parameters of this event * @notice Emitted when a transaction is deposited from L1 to L2. The parameters of this event
* are read by the rollup node and used to derive deposit transactions on L2. * are read by the rollup node and used to derive deposit transactions on L2.
...@@ -77,6 +91,17 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver { ...@@ -77,6 +91,17 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
bytes opaqueData bytes opaqueData
); );
/**
* @notice Emitted when a withdrawal transaction is proven.
*
* @param withdrawalHash Hash of the withdrawal transaction.
*/
event WithdrawalProven(
bytes32 indexed withdrawalHash,
address indexed from,
address indexed to
);
/** /**
* @notice Emitted when a withdrawal transaction is finalized. * @notice Emitted when a withdrawal transaction is finalized.
* *
...@@ -125,47 +150,38 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver { ...@@ -125,47 +150,38 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
} }
/** /**
* @notice Finalizes a withdrawal transaction. * @notice Proves a withdrawal transaction.
* *
* @param _tx Withdrawal transaction to finalize. * @param _tx Withdrawal transaction to finalize.
* @param _l2BlockNumber L2 block number of the outputRoot. * @param _l2BlockNumber L2 block number of the outputRoot.
* @param _outputRootProof Inclusion proof of the L2ToL1MessagePasser contract's storage root. * @param _outputRootProof Inclusion proof of the L2ToL1MessagePasser contract's storage root.
* @param _withdrawalProof Inclusion proof of the withdrawal in L2ToL1MessagePasser contract. * @param _withdrawalProof Inclusion proof of the withdrawal in L2ToL1MessagePasser contract.
*/ */
function finalizeWithdrawalTransaction( function proveWithdrawalTransaction(
Types.WithdrawalTransaction memory _tx, Types.WithdrawalTransaction memory _tx,
uint256 _l2BlockNumber, uint256 _l2BlockNumber,
Types.OutputRootProof calldata _outputRootProof, Types.OutputRootProof calldata _outputRootProof,
bytes[] calldata _withdrawalProof bytes[] calldata _withdrawalProof
) external { ) external {
// Prevent nested withdrawals within withdrawals.
require(
l2Sender == DEFAULT_L2_SENDER,
"OptimismPortal: can only trigger one withdrawal per transaction"
);
// Prevent users from creating a deposit transaction where this address is the message // Prevent users from creating a deposit transaction where this address is the message
// sender on L2. // sender on L2.
// In the context of the proxy delegate calling to this implementation, // In the context of the proxy delegate calling to this implementation,
// address(this) will return the address of the proxy. // address(this) will return the address of the proxy.
//
// Because this is checked here, we do not need to check again in
// `finalizeWithdrawalTransaction`
require( require(
_tx.target != address(this), _tx.target != address(this),
"OptimismPortal: you cannot send messages to the portal contract" "OptimismPortal: you cannot send messages to the portal contract"
); );
// Get the output root. This will fail if there is no // Get the output root and load onto the stack to prevent multiple mloads. This will
// output root for the given block number. // fail if there is no output root for the given block number.
Types.OutputProposal memory proposal = L2_ORACLE.getL2Output(_l2BlockNumber); bytes32 outputRoot = L2_ORACLE.getL2Output(_l2BlockNumber).outputRoot;
// Ensure that enough time has passed since the proposal was submitted before allowing a
// withdrawal. Under the assumption that the fault proof mechanism is operating correctly,
// we can infer that any withdrawal that has passed the finalization period must be valid
// and can therefore be operated on.
require(_isOutputFinalized(proposal), "OptimismPortal: proposal is not yet finalized");
// Verify that the output root can be generated with the elements in the proof. // Verify that the output root can be generated with the elements in the proof.
require( require(
proposal.outputRoot == Hashing.hashOutputRootProof(_outputRootProof), outputRoot == Hashing.hashOutputRootProof(_outputRootProof),
"OptimismPortal: invalid output root proof" "OptimismPortal: invalid output root proof"
); );
...@@ -185,6 +201,73 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver { ...@@ -185,6 +201,73 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
"OptimismPortal: invalid withdrawal inclusion proof" "OptimismPortal: invalid withdrawal inclusion proof"
); );
// Designate the withdrawalHash as proven by storing the `outputRoot`, `timestamp`,
// and `l2BlockNumber` in the `provenWithdrawals` mapping. A certain withdrawal
// can be proved multiple times and thus overwrite a previously stored `ProvenWithdrawal`,
// but this is safe due to the replay check in `finalizeWithdrawalTransaction`.
provenWithdrawals[withdrawalHash] = ProvenWithdrawal({
outputRoot: outputRoot,
timestamp: uint128(block.timestamp),
l2BlockNumber: uint128(_l2BlockNumber)
});
// Emit a `WithdrawalProven` event.
emit WithdrawalProven(withdrawalHash, _tx.sender, _tx.target);
}
/**
* @notice Finalizes a withdrawal transaction.
*
* @param _tx Withdrawal transaction to finalize.
*/
function finalizeWithdrawalTransaction(Types.WithdrawalTransaction memory _tx) external {
// Prevent nested withdrawals within withdrawals.
require(
l2Sender == DEFAULT_L2_SENDER,
"OptimismPortal: can only trigger one withdrawal per transaction"
);
// All withdrawals have a unique hash, we'll use this as the identifier for the withdrawal
// and to prevent replay attacks.
bytes32 withdrawalHash = Hashing.hashWithdrawal(_tx);
// Grab the proven withdrawal from the `provenWithdrawals` map.
ProvenWithdrawal memory provenWithdrawal = provenWithdrawals[withdrawalHash];
// Ensure that the withdrawal has been proven
require(provenWithdrawal.timestamp != 0, "OptimismPortal: withdrawal has not been proven");
// Ensure that the proven withdrawal's timestamp is greater than the
// L2 Oracle's starting timestamp.
require(
provenWithdrawal.timestamp >= L2_ORACLE.STARTING_TIMESTAMP(),
"OptimismPortal: withdrawal timestamp less than L2 Oracle starting timestamp"
);
// Ensure that the withdrawal's finalization period has elapsed.
require(
_isFinalizationPeriodElapsed(provenWithdrawal.timestamp),
"OptimismPortal: proven withdrawal finalization period has not elapsed"
);
// Grab the OutputProposal from the L2 Oracle
Types.OutputProposal memory proposal = L2_ORACLE.getL2Output(
provenWithdrawal.l2BlockNumber
);
// Check that the output proposal hasn't been updated.
require(
proposal.outputRoot == provenWithdrawal.outputRoot,
"OptimismPortal: output root proven is not the same as current output root"
);
// Perform second checks on the withdrawal's finalization period, this time with
// the `OutputProposal`'s timestamp fetched from the L2 Oracle.
require(
_isFinalizationPeriodElapsed(proposal.timestamp),
"OptimismPortal: output proposal finalization period has not elapsed"
);
// Check that this withdrawal has not already been finalized, this is replay protection. // Check that this withdrawal has not already been finalized, this is replay protection.
require( require(
finalizedWithdrawals[withdrawalHash] == false, finalizedWithdrawals[withdrawalHash] == false,
...@@ -225,8 +308,7 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver { ...@@ -225,8 +308,7 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
* @param _l2BlockNumber The number of the L2 block. * @param _l2BlockNumber The number of the L2 block.
*/ */
function isBlockFinalized(uint256 _l2BlockNumber) external view returns (bool) { function isBlockFinalized(uint256 _l2BlockNumber) external view returns (bool) {
Types.OutputProposal memory proposal = L2_ORACLE.getL2Output(_l2BlockNumber); return _isFinalizationPeriodElapsed(L2_ORACLE.getL2Output(_l2BlockNumber).timestamp);
return _isOutputFinalized(proposal);
} }
/** /**
...@@ -277,16 +359,13 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver { ...@@ -277,16 +359,13 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
} }
/** /**
* @notice Determine if an L2 Output is finalized. * @notice Determine if the finalization period has elapsed with respect to the
* passed timestamp.
* *
* @param _proposal The output proposal to check. * @param _timestamp The timestamp to check.
*/ */
function _isOutputFinalized(Types.OutputProposal memory _proposal) function _isFinalizationPeriodElapsed(uint256 _timestamp) internal view returns (bool) {
internal return block.timestamp > _timestamp + FINALIZATION_PERIOD_SECONDS;
view
returns (bool)
{
return block.timestamp > _proposal.timestamp + FINALIZATION_PERIOD_SECONDS;
} }
/** /**
......
...@@ -453,7 +453,7 @@ contract ERC721Bridge_Initializer is Messenger_Initializer { ...@@ -453,7 +453,7 @@ contract ERC721Bridge_Initializer is Messenger_Initializer {
} }
contract FFIInterface is Test { contract FFIInterface is Test {
function getFinalizeWithdrawalTransactionInputs(Types.WithdrawalTransaction memory _tx) function getProveWithdrawalTransactionInputs(Types.WithdrawalTransaction memory _tx)
external external
returns ( returns (
bytes32, bytes32,
...@@ -466,7 +466,7 @@ contract FFIInterface is Test { ...@@ -466,7 +466,7 @@ contract FFIInterface is Test {
string[] memory cmds = new string[](9); string[] memory cmds = new string[](9);
cmds[0] = "node"; cmds[0] = "node";
cmds[1] = "dist/scripts/differential-testing.js"; cmds[1] = "dist/scripts/differential-testing.js";
cmds[2] = "getFinalizeWithdrawalTransactionInputs"; cmds[2] = "getProveWithdrawalTransactionInputs";
cmds[3] = vm.toString(_tx.nonce); cmds[3] = vm.toString(_tx.nonce);
cmds[4] = vm.toString(_tx.sender); cmds[4] = vm.toString(_tx.sender);
cmds[5] = vm.toString(_tx.target); cmds[5] = vm.toString(_tx.target);
......
...@@ -16,7 +16,7 @@ contract OptimismPortal_Test is Portal_Initializer { ...@@ -16,7 +16,7 @@ contract OptimismPortal_Test is Portal_Initializer {
assertEq(op.l2Sender(), 0x000000000000000000000000000000000000dEaD); assertEq(op.l2Sender(), 0x000000000000000000000000000000000000dEaD);
} }
function test_OptimismPortalReceiveEth() external { function test_OptimismPortalReceiveEth_success() external {
vm.expectEmit(true, true, false, true); vm.expectEmit(true, true, false, true);
emitTransactionDeposited(alice, alice, 100, 100, 100_000, false, hex""); emitTransactionDeposited(alice, alice, 100, 100, 100_000, false, hex"");
...@@ -30,14 +30,14 @@ contract OptimismPortal_Test is Portal_Initializer { ...@@ -30,14 +30,14 @@ contract OptimismPortal_Test is Portal_Initializer {
} }
// Test: depositTransaction fails when contract creation has a non-zero destination address // Test: depositTransaction fails when contract creation has a non-zero destination address
function test_OptimismPortalContractCreationReverts() external { function test_depositTransaction_contractCreation_reverts() external {
// contract creation must have a target of address(0) // contract creation must have a target of address(0)
vm.expectRevert("OptimismPortal: must send to address(0) when creating a contract"); vm.expectRevert("OptimismPortal: must send to address(0) when creating a contract");
op.depositTransaction(address(1), 1, 0, true, hex""); op.depositTransaction(address(1), 1, 0, true, hex"");
} }
// Test: depositTransaction should emit the correct log when an EOA deposits a tx with 0 value // Test: depositTransaction should emit the correct log when an EOA deposits a tx with 0 value
function test_depositTransaction_NoValueEOA() external { function test_depositTransaction_NoValueEOA_success() external {
// EOA emulation // EOA emulation
vm.prank(address(this), address(this)); vm.prank(address(this), address(this));
vm.expectEmit(true, true, false, true); vm.expectEmit(true, true, false, true);
...@@ -61,7 +61,7 @@ contract OptimismPortal_Test is Portal_Initializer { ...@@ -61,7 +61,7 @@ contract OptimismPortal_Test is Portal_Initializer {
} }
// Test: depositTransaction should emit the correct log when a contract deposits a tx with 0 value // Test: depositTransaction should emit the correct log when a contract deposits a tx with 0 value
function test_depositTransaction_NoValueContract() external { function test_depositTransaction_NoValueContract_success() external {
vm.expectEmit(true, true, false, true); vm.expectEmit(true, true, false, true);
emitTransactionDeposited( emitTransactionDeposited(
AddressAliasHelper.applyL1ToL2Alias(address(this)), AddressAliasHelper.applyL1ToL2Alias(address(this)),
...@@ -83,7 +83,7 @@ contract OptimismPortal_Test is Portal_Initializer { ...@@ -83,7 +83,7 @@ contract OptimismPortal_Test is Portal_Initializer {
} }
// Test: depositTransaction should emit the correct log when an EOA deposits a contract creation with 0 value // Test: depositTransaction should emit the correct log when an EOA deposits a contract creation with 0 value
function test_depositTransaction_createWithZeroValueForEOA() external { function test_depositTransaction_createWithZeroValueForEOA_success() external {
// EOA emulation // EOA emulation
vm.prank(address(this), address(this)); vm.prank(address(this), address(this));
...@@ -102,7 +102,7 @@ contract OptimismPortal_Test is Portal_Initializer { ...@@ -102,7 +102,7 @@ contract OptimismPortal_Test is Portal_Initializer {
} }
// Test: depositTransaction should emit the correct log when a contract deposits a contract creation with 0 value // Test: depositTransaction should emit the correct log when a contract deposits a contract creation with 0 value
function test_depositTransaction_createWithZeroValueForContract() external { function test_depositTransaction_createWithZeroValueForContract_success() external {
vm.expectEmit(true, true, false, true); vm.expectEmit(true, true, false, true);
emitTransactionDeposited( emitTransactionDeposited(
AddressAliasHelper.applyL1ToL2Alias(address(this)), AddressAliasHelper.applyL1ToL2Alias(address(this)),
...@@ -118,7 +118,7 @@ contract OptimismPortal_Test is Portal_Initializer { ...@@ -118,7 +118,7 @@ contract OptimismPortal_Test is Portal_Initializer {
} }
// Test: depositTransaction should increase its eth balance when an EOA deposits a transaction with ETH // Test: depositTransaction should increase its eth balance when an EOA deposits a transaction with ETH
function test_depositTransaction_withEthValueFromEOA() external { function test_depositTransaction_withEthValueFromEOA_success() external {
// EOA emulation // EOA emulation
vm.prank(address(this), address(this)); vm.prank(address(this), address(this));
...@@ -144,7 +144,7 @@ contract OptimismPortal_Test is Portal_Initializer { ...@@ -144,7 +144,7 @@ contract OptimismPortal_Test is Portal_Initializer {
} }
// Test: depositTransaction should increase its eth balance when a contract deposits a transaction with ETH // Test: depositTransaction should increase its eth balance when a contract deposits a transaction with ETH
function test_depositTransaction_withEthValueFromContract() external { function test_depositTransaction_withEthValueFromContract_success() external {
vm.expectEmit(true, true, false, true); vm.expectEmit(true, true, false, true);
emitTransactionDeposited( emitTransactionDeposited(
AddressAliasHelper.applyL1ToL2Alias(address(this)), AddressAliasHelper.applyL1ToL2Alias(address(this)),
...@@ -166,7 +166,7 @@ contract OptimismPortal_Test is Portal_Initializer { ...@@ -166,7 +166,7 @@ contract OptimismPortal_Test is Portal_Initializer {
} }
// Test: depositTransaction should increase its eth balance when an EOA deposits a contract creation with ETH // Test: depositTransaction should increase its eth balance when an EOA deposits a contract creation with ETH
function test_depositTransaction_withEthValueAndEOAContractCreation() external { function test_depositTransaction_withEthValueAndEOAContractCreation_success() external {
// EOA emulation // EOA emulation
vm.prank(address(this), address(this)); vm.prank(address(this), address(this));
...@@ -192,7 +192,7 @@ contract OptimismPortal_Test is Portal_Initializer { ...@@ -192,7 +192,7 @@ contract OptimismPortal_Test is Portal_Initializer {
} }
// Test: depositTransaction should increase its eth balance when a contract deposits a contract creation with ETH // Test: depositTransaction should increase its eth balance when a contract deposits a contract creation with ETH
function test_depositTransaction_withEthValueAndContractContractCreation() external { function test_depositTransaction_withEthValueAndContractContractCreation_success() external {
vm.expectEmit(true, true, false, true); vm.expectEmit(true, true, false, true);
emitTransactionDeposited( emitTransactionDeposited(
AddressAliasHelper.applyL1ToL2Alias(address(this)), AddressAliasHelper.applyL1ToL2Alias(address(this)),
...@@ -214,7 +214,7 @@ contract OptimismPortal_Test is Portal_Initializer { ...@@ -214,7 +214,7 @@ contract OptimismPortal_Test is Portal_Initializer {
assertEq(address(op).balance, NON_ZERO_VALUE); assertEq(address(op).balance, NON_ZERO_VALUE);
} }
function test_simple_isBlockFinalized() external { function test_simple_isBlockFinalized_success() external {
vm.mockCall( vm.mockCall(
address(op.L2_ORACLE()), address(op.L2_ORACLE()),
abi.encodeWithSelector(L2OutputOracle.getL2Output.selector), abi.encodeWithSelector(L2OutputOracle.getL2Output.selector),
...@@ -229,7 +229,7 @@ contract OptimismPortal_Test is Portal_Initializer { ...@@ -229,7 +229,7 @@ contract OptimismPortal_Test is Portal_Initializer {
assertEq(op.isBlockFinalized(startingBlockNumber), true); assertEq(op.isBlockFinalized(startingBlockNumber), true);
} }
function test_isBlockFinalized() external { function test_isBlockFinalized_success() external {
uint256 checkpoint = oracle.nextBlockNumber(); uint256 checkpoint = oracle.nextBlockNumber();
vm.roll(checkpoint); vm.roll(checkpoint);
vm.warp(oracle.computeL2Timestamp(checkpoint) + 1); vm.warp(oracle.computeL2Timestamp(checkpoint) + 1);
...@@ -272,7 +272,12 @@ contract OptimismPortal_FinalizeWithdrawal_Test is Portal_Initializer { ...@@ -272,7 +272,12 @@ contract OptimismPortal_FinalizeWithdrawal_Test is Portal_Initializer {
bytes[] _withdrawalProof; bytes[] _withdrawalProof;
Types.OutputRootProof internal _outputRootProof; Types.OutputRootProof internal _outputRootProof;
event WithdrawalFinalized(bytes32 indexed, bool success); event WithdrawalFinalized(bytes32 indexed withdrawalHash, bool success);
event WithdrawalProven(
bytes32 indexed withdrawalHash,
address indexed from,
address indexed to
);
// Use a constructor to set the storage vars above, so as to minimize the number of ffi calls. // Use a constructor to set the storage vars above, so as to minimize the number of ffi calls.
constructor() { constructor() {
...@@ -287,7 +292,7 @@ contract OptimismPortal_FinalizeWithdrawal_Test is Portal_Initializer { ...@@ -287,7 +292,7 @@ contract OptimismPortal_FinalizeWithdrawal_Test is Portal_Initializer {
}); });
// Get withdrawal proof data we can use for testing. // Get withdrawal proof data we can use for testing.
(_stateRoot, _storageRoot, _outputRoot, _withdrawalHash, _withdrawalProof) = ffi (_stateRoot, _storageRoot, _outputRoot, _withdrawalHash, _withdrawalProof) = ffi
.getFinalizeWithdrawalTransactionInputs(_defaultTx); .getProveWithdrawalTransactionInputs(_defaultTx);
// Setup a dummy output root proof for reuse. // Setup a dummy output root proof for reuse.
_outputRootProof = Types.OutputRootProof({ _outputRootProof = Types.OutputRootProof({
...@@ -316,102 +321,297 @@ contract OptimismPortal_FinalizeWithdrawal_Test is Portal_Initializer { ...@@ -316,102 +321,297 @@ contract OptimismPortal_FinalizeWithdrawal_Test is Portal_Initializer {
vm.deal(address(op), 0xFFFFFFFF); vm.deal(address(op), 0xFFFFFFFF);
} }
// Utility function used in the subsequent test. This is necessary to assert that the
// reentrant call will revert.
function callPortalAndExpectRevert() external payable {
vm.expectRevert("OptimismPortal: can only trigger one withdrawal per transaction");
// Arguments here don't matter, as the require check is the first thing that happens.
// We assume that this has already been proven.
op.finalizeWithdrawalTransaction(_defaultTx);
// Assert that the withdrawal was not finalized.
assertFalse(op.finalizedWithdrawals(Hashing.hashWithdrawal(_defaultTx)));
}
// Test: proveWithdrawalTransaction cannot prove a withdrawal with itself (the OptimismPortal) as the target.
function test_proveWithdrawalTransaction_onSelfCall_reverts() external {
_defaultTx.target = address(op);
vm.expectRevert("OptimismPortal: you cannot send messages to the portal contract");
op.proveWithdrawalTransaction(
_defaultTx,
_proposedBlockNumber,
_outputRootProof,
_withdrawalProof
);
}
// Test: proveWithdrawalTransaction reverts if the outputRootProof does not match the output root
function test_proveWithdrawalTransaction_onInvalidOutputRootProof_reverts() external {
// Modify the version to invalidate the withdrawal proof.
_outputRootProof.version = bytes32(uint256(1));
vm.expectRevert("OptimismPortal: invalid output root proof");
op.proveWithdrawalTransaction(
_defaultTx,
_proposedBlockNumber,
_outputRootProof,
_withdrawalProof
);
}
// Test: proveWithdrawalTransaction reverts if the proof is invalid due to non-existence of
// the withdrawal.
function test_proveWithdrawalTransaction_oninvalidWithdrawalProof_reverts() external {
// modify the default test values to invalidate the proof.
_defaultTx.data = hex"abcd";
vm.expectRevert("OptimismPortal: invalid withdrawal inclusion proof");
op.proveWithdrawalTransaction(
_defaultTx,
_proposedBlockNumber,
_outputRootProof,
_withdrawalProof
);
}
// Test: proveWithdrawalTransaction succeeds and emits the WithdrawalProven event.
function test_proveWithdrawalTransaction_validWithdrawalProof_success() external {
vm.expectEmit(true, true, true, true);
emit WithdrawalProven(_withdrawalHash, alice, bob);
op.proveWithdrawalTransaction(
_defaultTx,
_proposedBlockNumber,
_outputRootProof,
_withdrawalProof
);
}
// Test: finalizeWithdrawalTransaction succeeds and emits the WithdrawalFinalized event. // Test: finalizeWithdrawalTransaction succeeds and emits the WithdrawalFinalized event.
function test_finalizeWithdrawalTransaction_succeeds() external { function test_finalizeWithdrawalTransaction_provenWithdrawalHash_success() external {
uint256 bobBalanceBefore = address(bob).balance; uint256 bobBalanceBefore = address(bob).balance;
vm.expectEmit(true, true, true, true); vm.expectEmit(true, true, true, true);
emit WithdrawalFinalized(_withdrawalHash, true); emit WithdrawalProven(_withdrawalHash, alice, bob);
op.finalizeWithdrawalTransaction( op.proveWithdrawalTransaction(
_defaultTx, _defaultTx,
_proposedBlockNumber, _proposedBlockNumber,
_outputRootProof, _outputRootProof,
_withdrawalProof _withdrawalProof
); );
vm.warp(block.timestamp + op.FINALIZATION_PERIOD_SECONDS() + 1);
vm.expectEmit(true, true, false, true);
emit WithdrawalFinalized(_withdrawalHash, true);
op.finalizeWithdrawalTransaction(_defaultTx);
assert(address(bob).balance == bobBalanceBefore + 100); assert(address(bob).balance == bobBalanceBefore + 100);
} }
// Test: finalizeWithdrawalTransaction fails because the target reverts, // Test: finalizeWithdrawalTransaction reverts if the withdrawal has not been proven.
// and emits the WithdrawalFinalized event with success=false. function test_finalizeWithdrawalTransaction_ifWithdrawalNotProven_reverts() external {
function test_finalizeWithdrawalTransaction_targetFails() external { uint256 bobBalanceBefore = address(bob).balance;
vm.expectRevert("OptimismPortal: withdrawal has not been proven");
op.finalizeWithdrawalTransaction(_defaultTx);
assert(address(bob).balance == bobBalanceBefore);
}
// Test: finalizeWithdrawalTransaction reverts if withdrawal not proven long enough ago.
function test_finalizeWithdrawalTransaction_ifWithdrawalProofNotOldEnough_reverts() external {
uint256 bobBalanceBefore = address(bob).balance; uint256 bobBalanceBefore = address(bob).balance;
vm.etch(bob, hex"fe"); // Contract with just the invalid opcode.
vm.expectEmit(true, true, true, true); vm.expectEmit(true, true, true, true);
emit WithdrawalFinalized(_withdrawalHash, false); emit WithdrawalProven(_withdrawalHash, alice, bob);
op.finalizeWithdrawalTransaction( op.proveWithdrawalTransaction(
_defaultTx, _defaultTx,
_proposedBlockNumber, _proposedBlockNumber,
_outputRootProof, _outputRootProof,
_withdrawalProof _withdrawalProof
); );
// Mock a call where the resulting output root is anything but the original output root. In
// this case we just use bytes32(uint256(1)).
vm.mockCall(
address(op.L2_ORACLE()),
abi.encodeWithSelector(L2OutputOracle.getL2Output.selector),
abi.encode(bytes32(uint256(1)), _proposedBlockNumber)
);
vm.expectRevert("OptimismPortal: proven withdrawal finalization period has not elapsed");
op.finalizeWithdrawalTransaction(_defaultTx);
assert(address(bob).balance == bobBalanceBefore); assert(address(bob).balance == bobBalanceBefore);
} }
// Test: finalizeWithdrawalTransaction cannot finalize a withdrawal with itself (the OptimismPortal) as the target. // Test: finalizeWithdrawalTransaction reverts if the provenWithdrawal's timestamp is less
function test_finalizeWithdrawalTransaction_revertsOnSelfCall() external { // than the L2 output oracle's starting timestamp
_defaultTx.target = address(op); function test_finalizeWithdrawalTransaction_timestampLessThanL2OracleStart_reverts() external {
vm.expectRevert("OptimismPortal: you cannot send messages to the portal contract"); uint256 bobBalanceBefore = address(bob).balance;
op.finalizeWithdrawalTransaction(
// Prove our withdrawal
vm.expectEmit(true, true, true, true);
emit WithdrawalProven(_withdrawalHash, alice, bob);
op.proveWithdrawalTransaction(
_defaultTx, _defaultTx,
_proposedBlockNumber, _proposedBlockNumber,
_outputRootProof, _outputRootProof,
_withdrawalProof _withdrawalProof
); );
// Warp to after the finalization period
vm.warp(block.timestamp + op.FINALIZATION_PERIOD_SECONDS() + 1);
// Mock a STARTING_TIMESTAMP change on the L2 Oracle
vm.mockCall(
address(op.L2_ORACLE()),
abi.encodeWithSignature("STARTING_TIMESTAMP()"),
abi.encode(block.timestamp + 1)
);
// Attempt to finalize the withdrawal
vm.expectRevert(
"OptimismPortal: withdrawal timestamp less than L2 Oracle starting timestamp"
);
op.finalizeWithdrawalTransaction(_defaultTx);
// Ensure that bob's balance has remained the same
assertEq(bobBalanceBefore, address(bob).balance);
} }
// Test: finalizeWithdrawalTransaction reverts if the outputRootProof does not match the output root // Test: finalizeWithdrawalTransaction reverts if the output root proven is not the same as the
function test_finalizeWithdrawalTransaction_revertsOnInvalidOutputRootProof() external { // output root at the time of finalization.
// Modify the version to invalidate the withdrawal proof. function test_finalizeWithdrawalTransaction_ifOutputRootChanges_reverts() external {
_outputRootProof.version = bytes32(uint256(1)); uint256 bobBalanceBefore = address(bob).balance;
vm.expectRevert("OptimismPortal: invalid output root proof");
op.finalizeWithdrawalTransaction( // Prove our withdrawal
vm.expectEmit(true, true, true, true);
emit WithdrawalProven(_withdrawalHash, alice, bob);
op.proveWithdrawalTransaction(
_defaultTx, _defaultTx,
_proposedBlockNumber, _proposedBlockNumber,
_outputRootProof, _outputRootProof,
_withdrawalProof _withdrawalProof
); );
// Warp to after the finalization period
vm.warp(block.timestamp + op.FINALIZATION_PERIOD_SECONDS() + 1);
// Mock an outputRoot change on the output proposal before attempting
// to finalize the withdrawal.
vm.mockCall(
address(op.L2_ORACLE()),
abi.encodeWithSelector(L2OutputOracle.getL2Output.selector),
abi.encode(Types.OutputProposal(bytes32(uint256(0)), _proposedBlockNumber))
);
// Attempt to finalize the withdrawal
vm.expectRevert(
"OptimismPortal: output root proven is not the same as current output root"
);
op.finalizeWithdrawalTransaction(_defaultTx);
// Ensure that bob's balance has remained the same
assertEq(bobBalanceBefore, address(bob).balance);
}
// Test: finalizeWithdrawalTransaction reverts if the output proposal's timestamp has
// not passed the finalization period.
function test_finalizeWithdrawalTransaction_ifOutputTimestampIsNotFinalized_reverts() external {
uint256 bobBalanceBefore = address(bob).balance;
// Prove our withdrawal
vm.expectEmit(true, true, true, true);
emit WithdrawalProven(_withdrawalHash, alice, bob);
op.proveWithdrawalTransaction(
_defaultTx,
_proposedBlockNumber,
_outputRootProof,
_withdrawalProof
);
// Warp to after the finalization period
vm.warp(block.timestamp + op.FINALIZATION_PERIOD_SECONDS() + 1);
// Mock a timestamp change on the output proposal that has not passed the
// finalization period.
vm.mockCall(
address(op.L2_ORACLE()),
abi.encodeWithSelector(L2OutputOracle.getL2Output.selector),
abi.encode(Types.OutputProposal(_outputRoot, block.timestamp + 1))
);
// Attempt to finalize the withdrawal
vm.expectRevert("OptimismPortal: output proposal finalization period has not elapsed");
op.finalizeWithdrawalTransaction(_defaultTx);
// Ensure that bob's balance has remained the same
assertEq(bobBalanceBefore, address(bob).balance);
}
// Test: finalizeWithdrawalTransaction fails because the target reverts,
// and emits the WithdrawalFinalized event with success=false.
function test_finalizeWithdrawalTransaction_targetFails_fails() external {
uint256 bobBalanceBefore = address(bob).balance;
vm.etch(bob, hex"fe"); // Contract with just the invalid opcode.
vm.expectEmit(true, true, true, true);
emit WithdrawalProven(_withdrawalHash, alice, bob);
op.proveWithdrawalTransaction(
_defaultTx,
_proposedBlockNumber,
_outputRootProof,
_withdrawalProof
);
vm.warp(block.timestamp + op.FINALIZATION_PERIOD_SECONDS() + 1);
vm.expectEmit(true, true, true, true);
emit WithdrawalFinalized(_withdrawalHash, false);
op.finalizeWithdrawalTransaction(_defaultTx);
assert(address(bob).balance == bobBalanceBefore);
} }
// Test: finalizeWithdrawalTransaction reverts if the finalization period has not yet passed. // Test: finalizeWithdrawalTransaction reverts if the finalization period has not yet passed.
function test_finalizeWithdrawalTransaction_revertsOnRecentWithdrawal() external { function test_finalizeWithdrawalTransaction_onRecentWithdrawal_reverts() external {
// Setup the Oracle to return an output with a recent timestamp // Setup the Oracle to return an output with a recent timestamp
uint256 recentTimestamp = block.timestamp - 1000; uint256 recentTimestamp = block.timestamp - 1000;
vm.mockCall( vm.mockCall(
address(op.L2_ORACLE()), address(op.L2_ORACLE()),
abi.encodeWithSelector(L2OutputOracle.getL2Output.selector), abi.encodeWithSelector(L2OutputOracle.getL2Output.selector),
abi.encode(bytes32(uint256(1)), recentTimestamp) abi.encode(_outputRoot, recentTimestamp)
); );
vm.expectRevert("OptimismPortal: proposal is not yet finalized"); op.proveWithdrawalTransaction(
op.finalizeWithdrawalTransaction(
_defaultTx, _defaultTx,
_proposedBlockNumber, _proposedBlockNumber,
_outputRootProof, _outputRootProof,
_withdrawalProof _withdrawalProof
); );
vm.expectRevert("OptimismPortal: proven withdrawal finalization period has not elapsed");
op.finalizeWithdrawalTransaction(_defaultTx);
} }
// Test: finalizeWithdrawalTransaction reverts if the withdrawal has already been finalized. // Test: finalizeWithdrawalTransaction reverts if the withdrawal has already been finalized.
function test_finalizeWithdrawalTransaction_revertsOnReplay() external { function test_finalizeWithdrawalTransaction_onReplay_reverts() external {
vm.expectEmit(true, true, true, true); vm.expectEmit(true, true, true, true);
emit WithdrawalFinalized(_withdrawalHash, true); emit WithdrawalProven(_withdrawalHash, alice, bob);
op.finalizeWithdrawalTransaction( op.proveWithdrawalTransaction(
_defaultTx, _defaultTx,
_proposedBlockNumber, _proposedBlockNumber,
_outputRootProof, _outputRootProof,
_withdrawalProof _withdrawalProof
); );
vm.warp(block.timestamp + op.FINALIZATION_PERIOD_SECONDS() + 1);
vm.expectEmit(true, true, true, true);
emit WithdrawalFinalized(_withdrawalHash, true);
op.finalizeWithdrawalTransaction(_defaultTx);
vm.expectRevert("OptimismPortal: withdrawal has already been finalized"); vm.expectRevert("OptimismPortal: withdrawal has already been finalized");
op.finalizeWithdrawalTransaction( op.finalizeWithdrawalTransaction(_defaultTx);
_defaultTx,
_proposedBlockNumber,
_outputRootProof,
_withdrawalProof
);
} }
// Test: finalizeWithdrawalTransaction reverts if insufficient gas is supplied. // Test: finalizeWithdrawalTransaction reverts if insufficient gas is supplied.
function test_finalizeWithdrawalTransaction_revertsOnInsufficientGas() external { function test_finalizeWithdrawalTransaction_onInsufficientGas_reverts() external {
// This number was identified through trial and error. // This number was identified through trial and error.
uint256 gasLimit = 150_000; uint256 gasLimit = 150_000;
Types.WithdrawalTransaction memory insufficientGasTx = Types.WithdrawalTransaction({ Types.WithdrawalTransaction memory insufficientGasTx = Types.WithdrawalTransaction({
...@@ -422,57 +622,40 @@ contract OptimismPortal_FinalizeWithdrawal_Test is Portal_Initializer { ...@@ -422,57 +622,40 @@ contract OptimismPortal_FinalizeWithdrawal_Test is Portal_Initializer {
gasLimit: gasLimit, gasLimit: gasLimit,
data: hex"" data: hex""
}); });
// Get updated proof inputs.
(bytes32 stateRoot, bytes32 storageRoot, , , bytes[] memory withdrawalProof) = ffi (bytes32 stateRoot, bytes32 storageRoot, , , bytes[] memory withdrawalProof) = ffi
.getFinalizeWithdrawalTransactionInputs(insufficientGasTx); .getProveWithdrawalTransactionInputs(insufficientGasTx);
Types.OutputRootProof memory outputRootProof = Types.OutputRootProof({ Types.OutputRootProof memory outputRootProof = Types.OutputRootProof({
version: bytes32(0), version: bytes32(0),
stateRoot: stateRoot, stateRoot: stateRoot,
messagePasserStorageRoot: storageRoot, messagePasserStorageRoot: storageRoot,
latestBlockhash: bytes32(0) latestBlockhash: bytes32(0)
}); });
vm.mockCall( vm.mockCall(
address(op.L2_ORACLE()), address(op.L2_ORACLE()),
abi.encodeWithSelector(L2OutputOracle.getL2Output.selector), abi.encodeWithSelector(L2OutputOracle.getL2Output.selector),
abi.encode(Hashing.hashOutputRootProof(outputRootProof), _proposedBlockNumber) abi.encode(Hashing.hashOutputRootProof(outputRootProof), _proposedBlockNumber)
); );
vm.expectRevert("OptimismPortal: insufficient gas to finalize withdrawal");
op.finalizeWithdrawalTransaction{ gas: gasLimit }( op.proveWithdrawalTransaction(
insufficientGasTx, insufficientGasTx,
_proposedBlockNumber, _proposedBlockNumber,
outputRootProof, outputRootProof,
withdrawalProof withdrawalProof
); );
}
// Test: finalizeWithdrawalTransaction reverts if the proof is invalid due to non-existence of
// the withdrawal.
function test_finalizeWithdrawalTransaction_revertsOninvalidWithdrawalProof() external {
// modify the default test values to invalidate the proof.
_defaultTx.data = hex"abcd";
vm.expectRevert("OptimismPortal: invalid withdrawal inclusion proof");
op.finalizeWithdrawalTransaction(
_defaultTx,
_proposedBlockNumber,
_outputRootProof,
_withdrawalProof
);
}
// Utility function used in the subsequent test. This is necessary to assert that the vm.warp(block.timestamp + op.FINALIZATION_PERIOD_SECONDS() + 1);
// reentrant call will revert. vm.expectRevert("OptimismPortal: insufficient gas to finalize withdrawal");
function callPortalAndExpectRevert() external payable { op.finalizeWithdrawalTransaction{ gas: gasLimit }(insufficientGasTx);
vm.expectRevert("OptimismPortal: can only trigger one withdrawal per transaction");
// Arguments here don't matter, as the require check is the first thing that happens.
bytes[] memory proof = new bytes[](1);
op.finalizeWithdrawalTransaction(_defaultTx, 0, _outputRootProof, proof);
// Assert that the withdrawal was not finalized.
assertFalse(op.finalizedWithdrawals(Hashing.hashWithdrawal(_defaultTx)));
} }
// Test: finalizeWithdrawalTransaction reverts if a sub-call attempts to finalize another // Test: finalizeWithdrawalTransaction reverts if a sub-call attempts to finalize another
// withdrawal. // withdrawal.
function test_finalizeWithdrawalTransaction_revertsOnReentrancy() external { function test_finalizeWithdrawalTransaction_onReentrancy_reverts() external {
uint256 bobBalanceBefore = address(bob).balance; uint256 bobBalanceBefore = address(bob).balance;
// Copy and modify the default test values to attempt a reentrant call by first calling to // Copy and modify the default test values to attempt a reentrant call by first calling to
// this contract's callPortalAndExpectRevert() function above. // this contract's callPortalAndExpectRevert() function above.
Types.WithdrawalTransaction memory _testTx = _defaultTx; Types.WithdrawalTransaction memory _testTx = _defaultTx;
...@@ -486,7 +669,7 @@ contract OptimismPortal_FinalizeWithdrawal_Test is Portal_Initializer { ...@@ -486,7 +669,7 @@ contract OptimismPortal_FinalizeWithdrawal_Test is Portal_Initializer {
bytes32 outputRoot, bytes32 outputRoot,
bytes32 withdrawalHash, bytes32 withdrawalHash,
bytes[] memory withdrawalProof bytes[] memory withdrawalProof
) = ffi.getFinalizeWithdrawalTransactionInputs(_testTx); ) = ffi.getProveWithdrawalTransactionInputs(_testTx);
Types.OutputRootProof memory outputRootProof = Types.OutputRootProof({ Types.OutputRootProof memory outputRootProof = Types.OutputRootProof({
version: bytes32(0), version: bytes32(0),
stateRoot: stateRoot, stateRoot: stateRoot,
...@@ -502,35 +685,36 @@ contract OptimismPortal_FinalizeWithdrawal_Test is Portal_Initializer { ...@@ -502,35 +685,36 @@ contract OptimismPortal_FinalizeWithdrawal_Test is Portal_Initializer {
abi.encode(Types.OutputProposal(outputRoot, finalizedTimestamp)) abi.encode(Types.OutputProposal(outputRoot, finalizedTimestamp))
); );
// Assert that this contract is called with the expected data (i.e. the function signature of
// callPortalAndExpectRevert).
vm.expectCall(address(this), _testTx.data);
vm.expectEmit(true, true, true, true); vm.expectEmit(true, true, true, true);
// Assert that the withdrawal should be finalized, and that the sub-call passes (because the emit WithdrawalProven(withdrawalHash, alice, address(this));
// assertions in callPortalAndExpectRevert pass). op.proveWithdrawalTransaction(
emit WithdrawalFinalized(withdrawalHash, true);
op.finalizeWithdrawalTransaction(
_testTx, _testTx,
_proposedBlockNumber, _proposedBlockNumber,
outputRootProof, outputRootProof,
withdrawalProof withdrawalProof
); );
vm.warp(block.timestamp + op.FINALIZATION_PERIOD_SECONDS() + 1);
vm.expectCall(address(this), _testTx.data);
vm.expectEmit(true, true, true, true);
emit WithdrawalFinalized(withdrawalHash, true);
op.finalizeWithdrawalTransaction(_testTx);
// Ensure that bob's balance was not changed by the reentrant call. // Ensure that bob's balance was not changed by the reentrant call.
assert(address(bob).balance == bobBalanceBefore); assert(address(bob).balance == bobBalanceBefore);
} }
function test_finalizeWithdrawalTransaction_differential( function test_finalizeWithdrawalTransaction_differential_success(
address _sender, address _sender,
address _target, address _target,
uint256 _value, uint256 _value,
uint256 _gasLimit, uint256 _gasLimit,
bytes memory _data bytes memory _data
) external { ) external {
// Cannot call the optimism portal vm.assume(_target != address(op)); // Cannot call the optimism portal.
vm.assume(_target != address(op)); vm.assume(_value < 200_000_000 ether); // Total ETH supply is currently ~120M ETH.
// Total ETH supply is currently about 120M ETH.
vm.assume(_value < 200_000_000 ether);
vm.assume(_gasLimit < 50_000_000); vm.assume(_gasLimit < 50_000_000);
uint256 _nonce = messagePasser.nonce(); uint256 _nonce = messagePasser.nonce();
Types.WithdrawalTransaction memory _tx = Types.WithdrawalTransaction({ Types.WithdrawalTransaction memory _tx = Types.WithdrawalTransaction({
nonce: _nonce, nonce: _nonce,
...@@ -546,7 +730,7 @@ contract OptimismPortal_FinalizeWithdrawal_Test is Portal_Initializer { ...@@ -546,7 +730,7 @@ contract OptimismPortal_FinalizeWithdrawal_Test is Portal_Initializer {
bytes32 outputRoot, bytes32 outputRoot,
bytes32 withdrawalHash, bytes32 withdrawalHash,
bytes[] memory withdrawalProof bytes[] memory withdrawalProof
) = ffi.getFinalizeWithdrawalTransactionInputs(_tx); ) = ffi.getProveWithdrawalTransactionInputs(_tx);
Types.OutputRootProof memory proof = Types.OutputRootProof({ Types.OutputRootProof memory proof = Types.OutputRootProof({
version: bytes32(uint256(0)), version: bytes32(uint256(0)),
...@@ -571,11 +755,12 @@ contract OptimismPortal_FinalizeWithdrawal_Test is Portal_Initializer { ...@@ -571,11 +755,12 @@ contract OptimismPortal_FinalizeWithdrawal_Test is Portal_Initializer {
vm.deal(_tx.sender, _tx.value); vm.deal(_tx.sender, _tx.value);
vm.prank(_tx.sender); vm.prank(_tx.sender);
messagePasser.initiateWithdrawal{ value: _tx.value }(_tx.target, _tx.gasLimit, _tx.data); messagePasser.initiateWithdrawal{ value: _tx.value }(_tx.target, _tx.gasLimit, _tx.data);
// Ensure that the sentMessages is correct // Ensure that the sentMessages is correct
assertEq(messagePasser.sentMessages(withdrawalHash), true); assertEq(messagePasser.sentMessages(withdrawalHash), true);
vm.warp(op.FINALIZATION_PERIOD_SECONDS() + 1); vm.warp(block.timestamp + op.FINALIZATION_PERIOD_SECONDS() + 1);
op.finalizeWithdrawalTransaction( op.proveWithdrawalTransaction(
_tx, _tx,
100, // l2BlockNumber 100, // l2BlockNumber
proof, proof,
...@@ -594,7 +779,7 @@ contract OptimismPortalUpgradeable_Test is Portal_Initializer { ...@@ -594,7 +779,7 @@ contract OptimismPortalUpgradeable_Test is Portal_Initializer {
proxy = Proxy(payable(address(op))); proxy = Proxy(payable(address(op)));
} }
function test_initValuesOnProxy() external { function test_params_initValuesOnProxy_success() external {
(uint128 prevBaseFee, uint64 prevBoughtGas, uint64 prevBlockNum) = OptimismPortal( (uint128 prevBaseFee, uint64 prevBoughtGas, uint64 prevBlockNum) = OptimismPortal(
payable(address(proxy)) payable(address(proxy))
).params(); ).params();
...@@ -603,17 +788,17 @@ contract OptimismPortalUpgradeable_Test is Portal_Initializer { ...@@ -603,17 +788,17 @@ contract OptimismPortalUpgradeable_Test is Portal_Initializer {
assertEq(prevBlockNum, initialBlockNum); assertEq(prevBlockNum, initialBlockNum);
} }
function test_cannotInitProxy() external { function test_initialize_cannotInitProxy_reverts() external {
vm.expectRevert("Initializable: contract is already initialized"); vm.expectRevert("Initializable: contract is already initialized");
OptimismPortal(payable(proxy)).initialize(); OptimismPortal(payable(proxy)).initialize();
} }
function test_cannotInitImpl() external { function test_initialize_cannotInitImpl_reverts() external {
vm.expectRevert("Initializable: contract is already initialized"); vm.expectRevert("Initializable: contract is already initialized");
OptimismPortal(opImpl).initialize(); OptimismPortal(opImpl).initialize();
} }
function test_upgrading() external { function test_upgradeToAndCall_upgrading_success() external {
// Check an unused slot before upgrading. // Check an unused slot before upgrading.
bytes32 slot21Before = vm.load(address(op), bytes32(uint256(21))); bytes32 slot21Before = vm.load(address(op), bytes32(uint256(21)));
assertEq(bytes32(0), slot21Before); assertEq(bytes32(0), slot21Before);
......
...@@ -165,7 +165,7 @@ const command = args[0] ...@@ -165,7 +165,7 @@ const command = args[0]
process.stdout.write(output) process.stdout.write(output)
break break
} }
case 'getFinalizeWithdrawalTransactionInputs': { case 'getProveWithdrawalTransactionInputs': {
const nonce = BigNumber.from(args[1]) const nonce = BigNumber.from(args[1])
const sender = args[2] const sender = args[2]
const target = args[3] const target = args[3]
......
...@@ -52,6 +52,8 @@ import { ...@@ -52,6 +52,8 @@ import {
StateRoot, StateRoot,
StateRootBatch, StateRootBatch,
IBridgeAdapter, IBridgeAdapter,
ProvenWithdrawal,
WithdrawalEntry,
} from './interfaces' } from './interfaces'
import { import {
toSignerOrProvider, toSignerOrProvider,
...@@ -550,7 +552,30 @@ export class CrossChainMessenger { ...@@ -550,7 +552,30 @@ export class CrossChainMessenger {
return MessageStatus.STATE_ROOT_NOT_PUBLISHED return MessageStatus.STATE_ROOT_NOT_PUBLISHED
} }
timestamp = output.l1Timestamp // Fetch the receipt for the resolved CrossChainMessage
const _receipt = await this.l2Provider.getTransactionReceipt(
resolved.transactionHash
)
// Get the withdrawal hash for the receipt
const [_, withdrawalHash] = this.getWithdrawalFromReceipt(
_receipt,
resolved
)
// Attempt to fetch the proven withdrawal
const provenWithdrawal = await this.getProvenWithdrawal(
withdrawalHash
)
// If the withdrawal hash has not been proven on L1,
// return `READY_TO_PROVE`
if (provenWithdrawal.timestamp.eq(BigNumber.from(0))) {
return MessageStatus.READY_TO_PROVE
}
// Set the timestamp to the provenWithdrawal's timestamp
timestamp = provenWithdrawal.timestamp.toNumber()
} else { } else {
const stateRoot = await this.getMessageStateRoot(resolved) const stateRoot = await this.getMessageStateRoot(resolved)
if (stateRoot === null) { if (stateRoot === null) {
...@@ -892,6 +917,24 @@ export class CrossChainMessenger { ...@@ -892,6 +917,24 @@ export class CrossChainMessenger {
return challengePeriod.toNumber() return challengePeriod.toNumber()
} }
/**
* Queries the OptimismPortal contract's `provenWithdrawals` mapping
* for a ProvenWithdrawal that matches the passed withdrawalHash
*
* Note: This function is bedrock-specific.
*
* @returns A ProvenWithdrawal object
*/
public async getProvenWithdrawal(
withdrawalHash: string
): Promise<ProvenWithdrawal> {
if (!this.bedrock) {
throw new Error('message proving only applies after the bedrock upgrade')
}
return this.contracts.l1.OptimismPortal.provenWithdrawals(withdrawalHash)
}
/** /**
* Returns the Bedrock output root that corresponds to the given message. * Returns the Bedrock output root that corresponds to the given message.
* *
...@@ -1212,41 +1255,9 @@ export class CrossChainMessenger { ...@@ -1212,41 +1255,9 @@ export class CrossChainMessenger {
resolved.transactionHash resolved.transactionHash
) )
interface WithdrawalEntry { const [withdrawal, withdrawalHash] = this.getWithdrawalFromReceipt(
MessagePassed: any receipt,
} resolved
// Handle multiple withdrawals in the same tx
const logs: Partial<{ number: WithdrawalEntry }> = {}
for (const [i, log] of Object.entries(receipt.logs)) {
if (log.address === this.contracts.l2.BedrockMessagePasser.address) {
const decoded =
this.contracts.l2.L2ToL1MessagePasser.interface.parseLog(log)
// Find the withdrawal initiated events
if (decoded.name === 'MessagePassed') {
logs[log.logIndex] = {
MessagePassed: decoded.args,
}
}
}
}
// TODO(tynes): be able to handle transactions that do multiple withdrawals
// in a single transaction. Right now just go for the first one.
const withdrawal = Object.values(logs)[0]
if (!withdrawal) {
throw new Error(
`Cannot find withdrawal logs for ${resolved.transactionHash}`
)
}
const withdrawalHash = hashWithdrawal(
withdrawal.MessagePassed.nonce,
withdrawal.MessagePassed.sender,
withdrawal.MessagePassed.target,
withdrawal.MessagePassed.value,
withdrawal.MessagePassed.gasLimit,
withdrawal.MessagePassed.data
) )
// Sanity check // Sanity check
...@@ -1311,6 +1322,58 @@ export class CrossChainMessenger { ...@@ -1311,6 +1322,58 @@ export class CrossChainMessenger {
] ]
} }
/**
* Helper function that gets a withdrawal and a withdrawal hash from the logs
* of a L2 to L2 CrossChainMessage and its transaction receipt.
*
* TODO: Process multiple withdrawals in a single transaction.
*/
public getWithdrawalFromReceipt(
receipt: TransactionReceipt,
message: CrossChainMessage
): [WithdrawalEntry, string] {
// Handle multiple withdrawals in the same tx
const logs: Partial<{ number: WithdrawalEntry }> = {}
for (const [_, log] of Object.entries(receipt.logs)) {
if (log.address === this.contracts.l2.BedrockMessagePasser.address) {
const decoded =
this.contracts.l2.L2ToL1MessagePasser.interface.parseLog(log)
// Find the withdrawal initiated events
if (decoded.name === 'MessagePassed') {
logs[log.logIndex] = {
MessagePassed: decoded.args,
}
}
}
}
// TODO(tynes): be able to handle transactions that do multiple withdrawals
// in a single transaction. Right now just go for the first one.
const withdrawal = Object.values(logs)[0]
if (!withdrawal) {
throw new Error(
`Cannot find withdrawal logs for ${message.transactionHash}`
)
}
const withdrawalHash = hashWithdrawal(
withdrawal.MessagePassed.nonce,
withdrawal.MessagePassed.sender,
withdrawal.MessagePassed.target,
withdrawal.MessagePassed.value,
withdrawal.MessagePassed.gasLimit,
withdrawal.MessagePassed.data
)
if (withdrawalHash !== withdrawal.MessagePassed.withdrawalHash) {
throw new Error(
'Locally computed withdrawal hash is not equal to the withdrawal hash computed on-chain!'
)
}
return [withdrawal, withdrawalHash]
}
/** /**
* Sends a given cross chain message. Where the message is sent depends on the direction attached * Sends a given cross chain message. Where the message is sent depends on the direction attached
* to the message itself. * to the message itself.
...@@ -1366,6 +1429,28 @@ export class CrossChainMessenger { ...@@ -1366,6 +1429,28 @@ export class CrossChainMessenger {
) )
} }
/**
* Proves a cross chain message that was sent from L2 to L1. Only applicable for L2 to L1
* messages.
*
* @param message Message to finalize.
* @param opts Additional options.
* @param opts.signer Optional signer to use to send the transaction.
* @param opts.overrides Optional transaction overrides.
* @returns Transaction response for the finalization transaction.
*/
public async proveMessage(
message: MessageLike,
opts?: {
signer?: Signer
overrides?: Overrides
}
): Promise<TransactionResponse> {
return (opts?.signer || this.l1Signer).sendTransaction(
await this.populateTransaction.proveMessage(message, opts)
)
}
/** /**
* Finalizes a cross chain message that was sent from L2 to L1. Only applicable for L2 to L1 * Finalizes a cross chain message that was sent from L2 to L1. Only applicable for L2 to L1
* messages. Will throw an error if the message has not completed its challenge period yet. * messages. Will throw an error if the message has not completed its challenge period yet.
...@@ -1639,6 +1724,57 @@ export class CrossChainMessenger { ...@@ -1639,6 +1724,57 @@ export class CrossChainMessenger {
} }
}, },
/**
* Generates a message proving transaction that can be signed and executed. Only
* applicable for L2 to L1 messages.
*
* @param message Message to generate the proving transaction for.
* @param opts Additional options.
* @param opts.overrides Optional transaction overrides.
* @returns Transaction that can be signed and executed to prove the message.
*/
proveMessage: async (
message: MessageLike,
opts?: {
overrides?: PayableOverrides
}
): Promise<TransactionRequest> => {
const resolved = await this.toCrossChainMessage(message)
if (resolved.direction === MessageDirection.L1_TO_L2) {
throw new Error('cannot finalize L1 to L2 message')
}
if (!this.bedrock) {
throw new Error(
'message proving only applies after the bedrock upgrade'
)
}
const [proof, output, withdrawalTx] = await this.getBedrockMessageProof(
message
)
return this.contracts.l1.OptimismPortal.populateTransaction.proveWithdrawalTransaction(
[
withdrawalTx.messageNonce,
withdrawalTx.sender,
withdrawalTx.target,
withdrawalTx.value,
withdrawalTx.minGasLimit,
withdrawalTx.message,
],
output.l2BlockNumber,
[
proof.outputRootProof.version,
proof.outputRootProof.stateRoot,
proof.outputRootProof.messagePasserStorageRoot,
proof.outputRootProof.latestBlockhash,
],
proof.withdrawalProof,
opts?.overrides || {}
)
},
/** /**
* Generates a message finalization transaction that can be signed and executed. Only * Generates a message finalization transaction that can be signed and executed. Only
* applicable for L2 to L1 messages. Will throw an error if the message has not completed * applicable for L2 to L1 messages. Will throw an error if the message has not completed
...@@ -1661,9 +1797,7 @@ export class CrossChainMessenger { ...@@ -1661,9 +1797,7 @@ export class CrossChainMessenger {
} }
if (this.bedrock) { if (this.bedrock) {
const [proof, output, withdrawalTx] = await this.getBedrockMessageProof( const [, , withdrawalTx] = await this.getBedrockMessageProof(message)
message
)
return this.contracts.l1.OptimismPortal.populateTransaction.finalizeWithdrawalTransaction( return this.contracts.l1.OptimismPortal.populateTransaction.finalizeWithdrawalTransaction(
[ [
...@@ -1674,14 +1808,6 @@ export class CrossChainMessenger { ...@@ -1674,14 +1808,6 @@ export class CrossChainMessenger {
withdrawalTx.minGasLimit, withdrawalTx.minGasLimit,
withdrawalTx.message, withdrawalTx.message,
], ],
output.l2BlockNumber,
[
proof.outputRootProof.version,
proof.outputRootProof.stateRoot,
proof.outputRootProof.messagePasserStorageRoot,
proof.outputRootProof.latestBlockhash,
],
proof.withdrawalProof,
opts?.overrides || {} opts?.overrides || {}
) )
} else { } else {
...@@ -1882,6 +2008,25 @@ export class CrossChainMessenger { ...@@ -1882,6 +2008,25 @@ export class CrossChainMessenger {
) )
}, },
/**
* Estimates gas required to prove a cross chain message. Only applies to L2 to L1 messages.
*
* @param message Message to generate the proving transaction for.
* @param opts Additional options.
* @param opts.overrides Optional transaction overrides.
* @returns Gas estimate for the transaction.
*/
proveMessage: async (
message: MessageLike,
opts?: {
overrides?: CallOverrides
}
): Promise<BigNumber> => {
return this.l1Provider.estimateGas(
await this.populateTransaction.proveMessage(message, opts)
)
},
/** /**
* Estimates gas required to finalize a cross chain message. Only applies to L2 to L1 messages. * Estimates gas required to finalize a cross chain message. Only applies to L2 to L1 messages.
* *
......
...@@ -143,7 +143,12 @@ export enum MessageStatus { ...@@ -143,7 +143,12 @@ export enum MessageStatus {
STATE_ROOT_NOT_PUBLISHED, STATE_ROOT_NOT_PUBLISHED,
/** /**
* Message is an L2 to L1 message and awaiting the challenge period. * Message is ready to be proved on L1 to initiate the challenge period.
*/
READY_TO_PROVE,
/**
* Message is a proved L2 to L1 message and is undergoing the challenge period.
*/ */
IN_CHALLENGE_PERIOD, IN_CHALLENGE_PERIOD,
...@@ -215,6 +220,14 @@ export interface TokenBridgeMessage { ...@@ -215,6 +220,14 @@ export interface TokenBridgeMessage {
transactionHash: string transactionHash: string
} }
/**
* Represents a withdrawal entry within the logs of a L2 to L1
* CrossChainMessage
*/
export interface WithdrawalEntry {
MessagePassed: any
}
/** /**
* Enum describing the status of a CrossDomainMessage message receipt. * Enum describing the status of a CrossDomainMessage message receipt.
*/ */
...@@ -231,6 +244,15 @@ export interface MessageReceipt { ...@@ -231,6 +244,15 @@ export interface MessageReceipt {
transactionReceipt: TransactionReceipt transactionReceipt: TransactionReceipt
} }
/**
* ProvenWithdrawal in OptimismPortal
*/
export interface ProvenWithdrawal {
outputRoot: string
timestamp: BigNumber
l2BlockNumber: BigNumber
}
/** /**
* Header for a state root batch. * Header for a state root batch.
*/ */
......
...@@ -304,17 +304,29 @@ task('deposit-erc20', 'Deposits WETH9 onto L2.') ...@@ -304,17 +304,29 @@ task('deposit-erc20', 'Deposits WETH9 onto L2.')
const now = Math.floor(Date.now() / 1000) const now = Math.floor(Date.now() / 1000)
console.log('Waiting for message to be able to be proved')
await messenger.waitForMessageStatus(withdraw, MessageStatus.READY_TO_PROVE)
console.log('Proving withdrawal...')
const prove = await messenger.proveMessage(withdraw)
const proveReceipt = await prove.wait()
console.log(proveReceipt)
if (proveReceipt.status !== 1) {
throw new Error('Prove withdrawal transaction reverted')
}
console.log('Waiting for message to be able to be relayed') console.log('Waiting for message to be able to be relayed')
await messenger.waitForMessageStatus( await messenger.waitForMessageStatus(
withdraw, withdraw,
MessageStatus.READY_FOR_RELAY MessageStatus.READY_FOR_RELAY
) )
console.log('Finalizing withdrawal...')
const finalize = await messenger.finalizeMessage(withdraw) const finalize = await messenger.finalizeMessage(withdraw)
const receipt = await finalize.wait() const finalizeReceipt = await finalize.wait()
console.log(`Took ${Math.floor(Date.now() / 1000) - now} seconds`) console.log(`Took ${Math.floor(Date.now() / 1000) - now} seconds`)
for (const log of receipt.logs) { for (const log of finalizeReceipt.logs) {
switch (log.address) { switch (log.address) {
case OptimismPortal.address: { case OptimismPortal.address: {
const parsed = OptimismPortal.interface.parseLog(log) const parsed = OptimismPortal.interface.parseLog(log)
......
...@@ -229,8 +229,30 @@ task('deposit-eth', 'Deposits WETH9 onto L2.') ...@@ -229,8 +229,30 @@ task('deposit-eth', 'Deposits WETH9 onto L2.')
`Withdrawal on L2 complete: ${ethWithdrawReceipt.transactionHash}` `Withdrawal on L2 complete: ${ethWithdrawReceipt.transactionHash}`
) )
console.log('Waiting to be able to withdraw') console.log('Waiting to be able to prove withdrawal')
const interval = setInterval(async () => { const proveInterval = setInterval(async () => {
const currentStatus = await messenger.getMessageStatus(ethWithdrawReceipt)
console.log(`Message status: ${MessageStatus[currentStatus]}`)
}, 3000)
try {
await messenger.waitForMessageStatus(
ethWithdrawReceipt,
MessageStatus.READY_TO_PROVE
)
} finally {
clearInterval(proveInterval)
}
console.log('Proving eth withdrawal...')
const ethProve = await messenger.proveMessage(ethWithdrawReceipt)
const ethProveReceipt = await ethProve.wait()
if (ethProveReceipt.status !== 1) {
throw new Error('Prove withdrawal transaction reverted')
}
console.log('Waiting to be able to finalize withdrawal')
const finalizeInterval = setInterval(async () => {
const currentStatus = await messenger.getMessageStatus(ethWithdrawReceipt) const currentStatus = await messenger.getMessageStatus(ethWithdrawReceipt)
console.log(`Message status: ${MessageStatus[currentStatus]}`) console.log(`Message status: ${MessageStatus[currentStatus]}`)
}, 3000) }, 3000)
...@@ -241,9 +263,10 @@ task('deposit-eth', 'Deposits WETH9 onto L2.') ...@@ -241,9 +263,10 @@ task('deposit-eth', 'Deposits WETH9 onto L2.')
MessageStatus.READY_FOR_RELAY MessageStatus.READY_FOR_RELAY
) )
} finally { } finally {
clearInterval(interval) clearInterval(finalizeInterval)
} }
console.log('Finalizing eth withdrawal...')
const ethFinalize = await messenger.finalizeMessage(ethWithdrawReceipt) const ethFinalize = await messenger.finalizeMessage(ethWithdrawReceipt)
const ethFinalizeReceipt = await ethFinalize.wait() const ethFinalizeReceipt = await ethFinalize.wait()
if (ethFinalizeReceipt.status !== 1) { if (ethFinalizeReceipt.status !== 1) {
......
...@@ -87,7 +87,25 @@ task('finalize-withdrawal', 'Finalize a withdrawal') ...@@ -87,7 +87,25 @@ task('finalize-withdrawal', 'Finalize a withdrawal')
const status = await messenger.getMessageStatus(txHash) const status = await messenger.getMessageStatus(txHash)
console.log(`Status: ${MessageStatus[status]}`) console.log(`Status: ${MessageStatus[status]}`)
if (status === MessageStatus.READY_FOR_RELAY) { if (status === MessageStatus.READY_TO_PROVE) {
const proveTx = await messenger.proveMessage(txHash)
const proveReceipt = await proveTx.wait()
console.log('Prove receipt', proveReceipt)
const finalizeInterval = setInterval(async () => {
const currentStatus = await messenger.getMessageStatus(txHash)
console.log(`Message status: ${MessageStatus[currentStatus]}`)
}, 3000)
try {
await messenger.waitForMessageStatus(
txHash,
MessageStatus.READY_FOR_RELAY
)
} finally {
clearInterval(finalizeInterval)
}
const tx = await messenger.finalizeMessage(txHash) const tx = await messenger.finalizeMessage(txHash)
const receipt = await tx.wait() const receipt = await tx.wait()
console.log(receipt) console.log(receipt)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment