From 0ee82593c2fc2704a2c8f0130fd1887bc67f764b Mon Sep 17 00:00:00 2001 From: Roger <50648015+RogerLamTd@users.noreply.github.com> Date: Sat, 11 Nov 2023 21:33:57 +0300 Subject: [PATCH] feat(prover): add `--prover.blockSlippage` flag (#449) --- bindings/.githead | 2 +- bindings/encoding/input.go | 16 +++- bindings/encoding/input_test.go | 1 + bindings/encoding/struct.go | 11 +-- bindings/gen_taiko_l2.go | 1 - bindings/gen_taiko_token.go | 44 ++++++++- cmd/flags/prover.go | 8 ++ pkg/rpc/utils.go | 59 ++++++++---- .../prover_selector/eth_fee_eoa_selector.go | 21 +++-- prover/config.go | 2 + prover/proof_submitter/interface.go | 9 +- prover/proof_submitter/proof_contester.go | 40 +++++---- .../proof_submitter/proof_contester_test.go | 13 ++- prover/proof_submitter/proof_submitter.go | 1 - prover/prover.go | 89 +++++++++++++++++-- prover/prover_test.go | 30 ++++--- prover/server/api.go | 9 ++ prover/server/server.go | 3 + 18 files changed, 282 insertions(+), 77 deletions(-) diff --git a/bindings/.githead b/bindings/.githead index 4fa498f8c..500ceca5c 100644 --- a/bindings/.githead +++ b/bindings/.githead @@ -1 +1 @@ -7090d5f5c7e4f290a87b42e83d73accdbfe176c7 +205eb2c49f2364ffe382408577040767a69c5a03 diff --git a/bindings/encoding/input.go b/bindings/encoding/input.go index 37ee24a1b..0bf343da8 100644 --- a/bindings/encoding/input.go +++ b/bindings/encoding/input.go @@ -152,6 +152,10 @@ var ( Name: "expiry", Type: "uint64", }, + { + Name: "maxBlockId", + Type: "uint64", + }, { Name: "signature", Type: "bytes", @@ -188,6 +192,7 @@ var ( {Name: "blobHash", Type: bytes32Type}, {Name: "assignment.feeToken", Type: addressType}, {Name: "assignment.expiry", Type: uint64Type}, + {Name: "assignment.maxBlockId", Type: uint64Type}, {Name: "assignment.tierFees", Type: tierFeesType}, } blockMetadataComponentsType, _ = abi.NewType("tuple", "TaikoData.BlockMetadata", blockMetadataComponents) @@ -233,9 +238,18 @@ func EncodeProverAssignmentPayload( txListHash common.Hash, feeToken common.Address, expiry uint64, + maxBlockID uint64, tierFees []TierFee, ) ([]byte, error) { - b, err := proverAssignmentPayloadArgs.Pack("PROVER_ASSIGNMENT", taikoAddress, txListHash, feeToken, expiry, tierFees) + b, err := proverAssignmentPayloadArgs.Pack( + "PROVER_ASSIGNMENT", + taikoAddress, + txListHash, + feeToken, + expiry, + maxBlockID, + tierFees, + ) if err != nil { return nil, fmt.Errorf("failed to abi.encode prover assignment hash payload, %w", err) } diff --git a/bindings/encoding/input_test.go b/bindings/encoding/input_test.go index 6f3a785ff..fbcd85677 100644 --- a/bindings/encoding/input_test.go +++ b/bindings/encoding/input_test.go @@ -21,6 +21,7 @@ func TestEncodeProverAssignmentPayload(t *testing.T) { common.BytesToHash(randomBytes(32)), common.BytesToAddress(randomBytes(20)), 120, + 1024, []TierFee{{Tier: 0, Fee: common.Big1}}, ) diff --git a/bindings/encoding/struct.go b/bindings/encoding/struct.go index 1eef675e7..3922a1876 100644 --- a/bindings/encoding/struct.go +++ b/bindings/encoding/struct.go @@ -56,11 +56,12 @@ type TierFee struct { // ProverAssignment should be same with TaikoData.ProverAssignment. type ProverAssignment struct { - Prover common.Address - FeeToken common.Address - TierFees []TierFee - Expiry uint64 - Signature []byte + Prover common.Address + FeeToken common.Address + TierFees []TierFee + Expiry uint64 + MaxBlockId uint64 + Signature []byte } // FromGethHeader converts a GETH *types.Header to *BlockHeader. diff --git a/bindings/gen_taiko_l2.go b/bindings/gen_taiko_l2.go index 344a19266..40a3e9e80 100644 --- a/bindings/gen_taiko_l2.go +++ b/bindings/gen_taiko_l2.go @@ -29,7 +29,6 @@ var ( _ = abi.ConvertType ) - // TaikoL2Config is an auto generated low-level Go binding around an user-defined struct. type TaikoL2Config struct { GasTargetPerL1Block uint32 diff --git a/bindings/gen_taiko_token.go b/bindings/gen_taiko_token.go index 2f7d14dfb..e93e81bb2 100644 --- a/bindings/gen_taiko_token.go +++ b/bindings/gen_taiko_token.go @@ -37,7 +37,7 @@ type ERC20VotesUpgradeableCheckpoint struct { // TaikoTokenMetaData contains all meta data concerning the TaikoToken contract. var TaikoTokenMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"name\":\"INVALID_PAUSE_STATUS\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"REENTRANT_CALL\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RESOLVER_DENIED\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RESOLVER_INVALID_MANAGER\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RESOLVER_UNEXPECTED_CHAINID\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"chainId\",\"type\":\"uint64\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"RESOLVER_ZERO_ADDR\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TKO_INVALID_ADDR\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TKO_INVALID_PREMINT_PARAMS\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"fromDelegate\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"toDelegate\",\"type\":\"address\"}],\"name\":\"DelegateChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegate\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"previousBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newBalance\",\"type\":\"uint256\"}],\"name\":\"DelegateVotesChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferStarted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"Snapshot\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"DOMAIN_SEPARATOR\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"addressManager\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"snapshotId\",\"type\":\"uint256\"}],\"name\":\"balanceOfAt\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"pos\",\"type\":\"uint32\"}],\"name\":\"checkpoints\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fromBlock\",\"type\":\"uint32\"},{\"internalType\":\"uint224\",\"name\":\"votes\",\"type\":\"uint224\"}],\"internalType\":\"structERC20VotesUpgradeable.Checkpoint\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"subtractedValue\",\"type\":\"uint256\"}],\"name\":\"decreaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"delegatee\",\"type\":\"address\"}],\"name\":\"delegate\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"delegatee\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"expiry\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"delegateBySig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"delegates\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"name\":\"getPastTotalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"name\":\"getPastVotes\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"getVotes\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"addedValue\",\"type\":\"uint256\"}],\"name\":\"increaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addressManager\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"_name\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"_symbol\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"_recipient\",\"type\":\"address\"}],\"name\":\"init\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"nonces\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"numCheckpoints\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pendingOwner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"deadline\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"permit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"chainId\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"name\",\"type\":\"bytes32\"},{\"internalType\":\"bool\",\"name\":\"allowZeroAddress\",\"type\":\"bool\"}],\"name\":\"resolve\",\"outputs\":[{\"internalType\":\"addresspayable\",\"name\":\"addr\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"name\",\"type\":\"bytes32\"},{\"internalType\":\"bool\",\"name\":\"allowZeroAddress\",\"type\":\"bool\"}],\"name\":\"resolve\",\"outputs\":[{\"internalType\":\"addresspayable\",\"name\":\"addr\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"snapshot\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"snapshotId\",\"type\":\"uint256\"}],\"name\":\"totalSupplyAt\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + ABI: "[{\"inputs\":[],\"name\":\"INVALID_PAUSE_STATUS\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"REENTRANT_CALL\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RESOLVER_DENIED\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RESOLVER_INVALID_MANAGER\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RESOLVER_UNEXPECTED_CHAINID\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"chainId\",\"type\":\"uint64\"},{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"name\":\"RESOLVER_ZERO_ADDR\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TKO_INVALID_ADDR\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"TKO_INVALID_PREMINT_PARAMS\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegator\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"fromDelegate\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"toDelegate\",\"type\":\"address\"}],\"name\":\"DelegateChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"delegate\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"previousBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newBalance\",\"type\":\"uint256\"}],\"name\":\"DelegateVotesChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferStarted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Paused\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"}],\"name\":\"Snapshot\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"Unpaused\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"DOMAIN_SEPARATOR\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"addressManager\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"snapshotId\",\"type\":\"uint256\"}],\"name\":\"balanceOfAt\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"pos\",\"type\":\"uint32\"}],\"name\":\"checkpoints\",\"outputs\":[{\"components\":[{\"internalType\":\"uint32\",\"name\":\"fromBlock\",\"type\":\"uint32\"},{\"internalType\":\"uint224\",\"name\":\"votes\",\"type\":\"uint224\"}],\"internalType\":\"structERC20VotesUpgradeable.Checkpoint\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"subtractedValue\",\"type\":\"uint256\"}],\"name\":\"decreaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"delegatee\",\"type\":\"address\"}],\"name\":\"delegate\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"delegatee\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"expiry\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"delegateBySig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"delegates\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"name\":\"getPastTotalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"blockNumber\",\"type\":\"uint256\"}],\"name\":\"getPastVotes\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"getVotes\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"addedValue\",\"type\":\"uint256\"}],\"name\":\"increaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addressManager\",\"type\":\"address\"},{\"internalType\":\"string\",\"name\":\"_name\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"_symbol\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"_recipient\",\"type\":\"address\"}],\"name\":\"init\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"nonces\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"numCheckpoints\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"paused\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pendingOwner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"deadline\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"permit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"chainId\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"name\",\"type\":\"bytes32\"},{\"internalType\":\"bool\",\"name\":\"allowZeroAddress\",\"type\":\"bool\"}],\"name\":\"resolve\",\"outputs\":[{\"internalType\":\"addresspayable\",\"name\":\"addr\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"name\",\"type\":\"bytes32\"},{\"internalType\":\"bool\",\"name\":\"allowZeroAddress\",\"type\":\"bool\"}],\"name\":\"resolve\",\"outputs\":[{\"internalType\":\"addresspayable\",\"name\":\"addr\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"snapshot\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"snapshotId\",\"type\":\"uint256\"}],\"name\":\"totalSupplyAt\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unpause\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", } // TaikoTokenABI is the input ABI used to generate the binding from. @@ -910,6 +910,27 @@ func (_TaikoToken *TaikoTokenTransactorSession) Approve(spender common.Address, return _TaikoToken.Contract.Approve(&_TaikoToken.TransactOpts, spender, amount) } +// Burn is a paid mutator transaction binding the contract method 0x9dc29fac. +// +// Solidity: function burn(address from, uint256 amount) returns() +func (_TaikoToken *TaikoTokenTransactor) Burn(opts *bind.TransactOpts, from common.Address, amount *big.Int) (*types.Transaction, error) { + return _TaikoToken.contract.Transact(opts, "burn", from, amount) +} + +// Burn is a paid mutator transaction binding the contract method 0x9dc29fac. +// +// Solidity: function burn(address from, uint256 amount) returns() +func (_TaikoToken *TaikoTokenSession) Burn(from common.Address, amount *big.Int) (*types.Transaction, error) { + return _TaikoToken.Contract.Burn(&_TaikoToken.TransactOpts, from, amount) +} + +// Burn is a paid mutator transaction binding the contract method 0x9dc29fac. +// +// Solidity: function burn(address from, uint256 amount) returns() +func (_TaikoToken *TaikoTokenTransactorSession) Burn(from common.Address, amount *big.Int) (*types.Transaction, error) { + return _TaikoToken.Contract.Burn(&_TaikoToken.TransactOpts, from, amount) +} + // DecreaseAllowance is a paid mutator transaction binding the contract method 0xa457c2d7. // // Solidity: function decreaseAllowance(address spender, uint256 subtractedValue) returns(bool) @@ -1015,6 +1036,27 @@ func (_TaikoToken *TaikoTokenTransactorSession) Init(_addressManager common.Addr return _TaikoToken.Contract.Init(&_TaikoToken.TransactOpts, _addressManager, _name, _symbol, _recipient) } +// Mint is a paid mutator transaction binding the contract method 0x40c10f19. +// +// Solidity: function mint(address to, uint256 amount) returns() +func (_TaikoToken *TaikoTokenTransactor) Mint(opts *bind.TransactOpts, to common.Address, amount *big.Int) (*types.Transaction, error) { + return _TaikoToken.contract.Transact(opts, "mint", to, amount) +} + +// Mint is a paid mutator transaction binding the contract method 0x40c10f19. +// +// Solidity: function mint(address to, uint256 amount) returns() +func (_TaikoToken *TaikoTokenSession) Mint(to common.Address, amount *big.Int) (*types.Transaction, error) { + return _TaikoToken.Contract.Mint(&_TaikoToken.TransactOpts, to, amount) +} + +// Mint is a paid mutator transaction binding the contract method 0x40c10f19. +// +// Solidity: function mint(address to, uint256 amount) returns() +func (_TaikoToken *TaikoTokenTransactorSession) Mint(to common.Address, amount *big.Int) (*types.Transaction, error) { + return _TaikoToken.Contract.Mint(&_TaikoToken.TransactOpts, to, amount) +} + // Pause is a paid mutator transaction binding the contract method 0x8456cb59. // // Solidity: function pause() returns() diff --git a/cmd/flags/prover.go b/cmd/flags/prover.go index 8e3ceb385..c042fd5bd 100644 --- a/cmd/flags/prover.go +++ b/cmd/flags/prover.go @@ -148,6 +148,13 @@ var ( Value: false, Category: proverCategory, } + // Max slippage allowed + MaxAcceptableBlockSlippage = &cli.Uint64Flag{ + Name: "prover.blockSlippage", + Usage: "Maximum accepted slippage difference for blockID for accepting proving a block", + Value: 1024, + Category: proverCategory, + } ) // All prover flags. @@ -179,4 +186,5 @@ var ProverFlags = MergeFlags(CommonFlags, []cli.Flag{ ProverCapacity, MaxExpiry, TaikoTokenAddress, + MaxAcceptableBlockSlippage, }) diff --git a/pkg/rpc/utils.go b/pkg/rpc/utils.go index b105bb354..a64c62e51 100644 --- a/pkg/rpc/utils.go +++ b/pkg/rpc/utils.go @@ -20,7 +20,7 @@ import ( ) var ( - ZeroAddress = common.HexToAddress("0x0000000000000000000000000000000000000000") + ZeroAddress common.Address waitReceiptPollingInterval = 3 * time.Second defaultWaitReceiptTimeout = 1 * time.Minute ) @@ -127,13 +127,21 @@ func WaitReceipt( } } -// NeedNewProof checks whether the L2 block still needs a new proof. -func NeedNewProof( +// BlockProofStatus represents the proving status of the given L2 block. +type BlockProofStatus struct { + IsSubmitted bool + Invalid bool + CurrentTransitionState *bindings.TaikoDataTransitionState + ParentBlock *types.Header +} + +// GetBlockProofStatus checks whether the L2 block still needs a new proof or a new contest. +func GetBlockProofStatus( ctx context.Context, cli *Client, id *big.Int, proverAddress common.Address, -) (bool, error) { +) (*BlockProofStatus, error) { ctxWithTimeout, cancel := ctxWithTimeoutOrDefault(ctx, defaultTimeout) defer cancel() @@ -141,18 +149,18 @@ func NeedNewProof( if id.Cmp(common.Big1) == 0 { header, err := cli.L2.HeaderByNumber(ctxWithTimeout, common.Big0) if err != nil { - return false, err + return nil, err } parent = header } else { parentL1Origin, err := cli.WaitL1Origin(ctxWithTimeout, new(big.Int).Sub(id, common.Big1)) if err != nil { - return false, err + return nil, err } if parent, err = cli.L2.HeaderByHash(ctxWithTimeout, parentL1Origin.L2BlockHash); err != nil { - return false, err + return nil, err } } @@ -163,29 +171,46 @@ func NeedNewProof( ) if err != nil { if !strings.Contains(encoding.TryParsingCustomError(err).Error(), "L1_TRANSITION_NOT_FOUND") { - return false, encoding.TryParsingCustomError(err) + return nil, encoding.TryParsingCustomError(err) } - return true, nil + return &BlockProofStatus{IsSubmitted: false}, nil } l1Origin, err := cli.WaitL1Origin(ctxWithTimeout, id) if err != nil { - return false, err + return nil, err + } + + l2SignalService, err := cli.TaikoL2.SignalService(&bind.CallOpts{Context: ctx, BlockNumber: id}) + if err != nil { + return nil, err + } + + root, err := cli.GetStorageRoot(ctx, cli.L2GethClient, l2SignalService, id) + if err != nil { + return nil, err } - if l1Origin.L2BlockHash != transition.BlockHash { + if l1Origin.L2BlockHash != transition.BlockHash || transition.SignalRoot != root { log.Info( - "Different blockhash detected, try submitting a proof", - "local", common.BytesToHash(l1Origin.L2BlockHash[:]), - "protocol", common.BytesToHash(transition.BlockHash[:]), + "Different block hash or signal root detected, try submitting a contest", + "localBlockHash", common.BytesToHash(l1Origin.L2BlockHash[:]), + "protocolTransitionBlockHash", common.BytesToHash(transition.BlockHash[:]), + "localSignalRoot", root, + "protocolTransitionSignalRoot", common.BytesToHash(transition.SignalRoot[:]), ) - return true, nil + return &BlockProofStatus{ + IsSubmitted: true, + Invalid: true, + CurrentTransitionState: &transition, + ParentBlock: parent, + }, nil } if proverAddress == transition.Prover { log.Info("📬 Block's proof has already been submitted by current prover", "blockID", id) - return false, nil + return &BlockProofStatus{IsSubmitted: true}, nil } log.Info( @@ -195,7 +220,7 @@ func NeedNewProof( "timestamp", transition.Timestamp, ) - return false, nil + return &BlockProofStatus{IsSubmitted: true}, nil } type AccountPoolContent map[string]map[string]*types.Transaction diff --git a/proposer/prover_selector/eth_fee_eoa_selector.go b/proposer/prover_selector/eth_fee_eoa_selector.go index 51c2ab978..7a0870784 100644 --- a/proposer/prover_selector/eth_fee_eoa_selector.go +++ b/proposer/prover_selector/eth_fee_eoa_selector.go @@ -217,7 +217,14 @@ func assignProver( // Ensure prover in response is the same as the one recovered // from the signature - payload, err := encoding.EncodeProverAssignmentPayload(taikoL1Address, txListHash, common.Address{}, expiry, tierFees) + payload, err := encoding.EncodeProverAssignmentPayload( + taikoL1Address, + txListHash, + common.Address{}, + expiry, + result.MaxBlockID, + tierFees, + ) if err != nil { return nil, common.Address{}, err } @@ -240,6 +247,7 @@ func assignProver( "address", result.Prover, "endpoint", endpoint, "tierFees", tierFees, + "maxBlockID", result.MaxBlockID, "expiry", expiry, ) @@ -247,10 +255,11 @@ func assignProver( result.SignedPayload[64] = uint8(uint(result.SignedPayload[64])) + 27 return &encoding.ProverAssignment{ - Prover: result.Prover, - FeeToken: common.Address{}, - TierFees: tierFees, - Expiry: reqBody.Expiry, - Signature: result.SignedPayload, + Prover: result.Prover, + FeeToken: common.Address{}, + TierFees: tierFees, + Expiry: reqBody.Expiry, + MaxBlockId: result.MaxBlockID, + Signature: result.SignedPayload, }, result.Prover, nil } diff --git a/prover/config.go b/prover/config.go index 4699e1cf7..2640cc146 100644 --- a/prover/config.go +++ b/prover/config.go @@ -48,6 +48,7 @@ type Config struct { MinPseZkevmTierFee *big.Int MinSgxAndPseZkevmTierFee *big.Int MaxExpiry time.Duration + MaxBlockSlippage uint64 } // NewConfigFromCliContext creates a new config instance from command line flags. @@ -136,5 +137,6 @@ func NewConfigFromCliContext(c *cli.Context) (*Config, error) { MinPseZkevmTierFee: new(big.Int).SetUint64(c.Uint64(flags.MinPseZkevmTierFee.Name)), MinSgxAndPseZkevmTierFee: new(big.Int).SetUint64(c.Uint64(flags.MinSgxAndPseZkevmTierFee.Name)), MaxExpiry: c.Duration(flags.MaxExpiry.Name), + MaxBlockSlippage: c.Uint64(flags.MaxAcceptableBlockSlippage.Name), }, nil } diff --git a/prover/proof_submitter/interface.go b/prover/proof_submitter/interface.go index d6c0ef38e..1173194c5 100644 --- a/prover/proof_submitter/interface.go +++ b/prover/proof_submitter/interface.go @@ -2,7 +2,9 @@ package submitter import ( "context" + "math/big" + "github.com/ethereum/go-ethereum/common" "github.com/taikoxyz/taiko-client/bindings" proofProducer "github.com/taikoxyz/taiko-client/prover/proof_producer" ) @@ -17,7 +19,10 @@ type Submitter interface { type Contester interface { SubmitContest( ctx context.Context, - blockProposedEvent *bindings.TaikoL1ClientBlockProposed, - transitionProvedEvent *bindings.TaikoL1ClientTransitionProved, + blockID *big.Int, + proposedIn *big.Int, + parentHash common.Hash, + meta *bindings.TaikoDataBlockMetadata, + tier uint16, ) error } diff --git a/prover/proof_submitter/proof_contester.go b/prover/proof_submitter/proof_contester.go index 14163cbd0..c3ffa16d4 100644 --- a/prover/proof_submitter/proof_contester.go +++ b/prover/proof_submitter/proof_contester.go @@ -70,21 +70,24 @@ func NewProofContester( // SubmitContest submits a taikoL1.proveBlock transaction to contest a L2 block transition. func (c *ProofContester) SubmitContest( ctx context.Context, - blockProposedEvent *bindings.TaikoL1ClientBlockProposed, - transitionProvedEvent *bindings.TaikoL1ClientTransitionProved, + blockID *big.Int, + proposedIn *big.Int, + parentHash common.Hash, + meta *bindings.TaikoDataBlockMetadata, + tier uint16, ) error { // Ensure the transition has not been contested yet. transition, err := c.rpc.TaikoL1.GetTransition( &bind.CallOpts{Context: ctx}, - transitionProvedEvent.BlockId.Uint64(), - transitionProvedEvent.Tran.ParentHash, + blockID.Uint64(), + parentHash, ) if err != nil { if !strings.Contains(encoding.TryParsingCustomError(err).Error(), "L1_") { log.Warn( "Failed to get transition", - "blockID", transitionProvedEvent.BlockId, - "parentHash", transitionProvedEvent.Tran.ParentHash, + "blockID", blockID, + "parentHash", parentHash, "error", encoding.TryParsingCustomError(err), ) return nil @@ -94,40 +97,45 @@ func (c *ProofContester) SubmitContest( if transition.Contester != (common.Address{}) { log.Info( "Transaction has already been contested", - "blockID", transitionProvedEvent.BlockId, - "parentHash", transitionProvedEvent.Tran.ParentHash, + "blockID", blockID, + "parentHash", parentHash, "contester", transition.Contester, ) return nil } - header, err := c.rpc.L2.HeaderByNumber(ctx, transitionProvedEvent.BlockId) + header, err := c.rpc.L2.HeaderByNumber(ctx, blockID) if err != nil { return err } - signalRoot, err := c.rpc.GetStorageRoot(ctx, c.rpc.L2GethClient, c.l2SignalService, transitionProvedEvent.BlockId) + signalRoot, err := c.rpc.GetStorageRoot(ctx, c.rpc.L2GethClient, c.l2SignalService, blockID) if err != nil { return fmt.Errorf("failed to get L2 signal service storage root: %w", err) } + l1HeaderProposedIn, err := c.rpc.L1.HeaderByNumber(ctx, proposedIn) + if err != nil { + return err + } + if err := c.txSender.Send( ctx, &proofProducer.ProofWithHeader{ - BlockID: transitionProvedEvent.BlockId, - Meta: &blockProposedEvent.Meta, + BlockID: blockID, + Meta: meta, Header: header, Proof: []byte{}, Opts: &proofProducer.ProofRequestOptions{ - EventL1Hash: blockProposedEvent.Raw.BlockHash, + EventL1Hash: l1HeaderProposedIn.Hash(), SignalRoot: signalRoot, }, - Tier: transitionProvedEvent.Tier, + Tier: tier, }, c.txBuilder.Build( ctx, - transitionProvedEvent.BlockId, - &blockProposedEvent.Meta, + blockID, + meta, &bindings.TaikoDataTransition{ ParentHash: header.ParentHash, BlockHash: header.Hash(), diff --git a/prover/proof_submitter/proof_contester_test.go b/prover/proof_submitter/proof_contester_test.go index 2d0661bdf..45cdaf4c3 100644 --- a/prover/proof_submitter/proof_contester_test.go +++ b/prover/proof_submitter/proof_contester_test.go @@ -5,6 +5,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/taikoxyz/taiko-client/bindings" + "github.com/taikoxyz/taiko-client/bindings/encoding" "github.com/taikoxyz/taiko-client/testutils" ) @@ -12,13 +13,11 @@ func (s *ProofSubmitterTestSuite) TestSubmitContestNoTransition() { s.NotNil( s.contester.SubmitContest( context.Background(), - &bindings.TaikoL1ClientBlockProposed{}, - &bindings.TaikoL1ClientTransitionProved{ - BlockId: common.Big256, - Tran: bindings.TaikoDataTransition{ - ParentHash: testutils.RandomHash(), - }, - }, + common.Big256, + common.Big1, + testutils.RandomHash(), + &bindings.TaikoDataBlockMetadata{}, + encoding.TierOptimisticID, ), ) } diff --git a/prover/proof_submitter/proof_submitter.go b/prover/proof_submitter/proof_submitter.go index 415be26d4..019589a36 100644 --- a/prover/proof_submitter/proof_submitter.go +++ b/prover/proof_submitter/proof_submitter.go @@ -184,7 +184,6 @@ func (s *ProofSubmitter) SubmitProof( metrics.ProverReceivedProofCounter.Inc(1) - // Validate anchor transaction // Get the corresponding L2 block. block, err := s.rpc.L2.BlockByHash(ctx, proofWithHeader.Header.Hash()) if err != nil { diff --git a/prover/prover.go b/prover/prover.go index 1d7a7d42a..5c56acffd 100644 --- a/prover/prover.go +++ b/prover/prover.go @@ -225,6 +225,7 @@ func InitFromConfig(ctx context.Context, p *Prover, cfg *Config) (err error) { MinPseZkevmTierFee: p.cfg.MinPseZkevmTierFee, MinSgxAndPseZkevmTierFee: p.cfg.MinSgxAndPseZkevmTierFee, MaxExpiry: p.cfg.MaxExpiry, + MaxBlockSlippage: p.cfg.MaxBlockSlippage, CapacityManager: p.capacityManager, TaikoL1Address: p.cfg.TaikoL1Address, Rpc: p.rpc, @@ -359,7 +360,6 @@ func (p *Prover) onBlockProposed( // If there are newly generated proofs, we need to submit them as soon as possible. if len(p.proofGenerationCh) > 0 { log.Info("onBlockProposed early return", "proofGenerationChannelLength", len(p.proofGenerationCh)) - end() return nil } @@ -458,7 +458,7 @@ func (p *Prover) onBlockProposed( } // Check whether the block's proof is still needed. - needNewProof, err := rpc.NeedNewProof( + proofStatus, err := rpc.GetBlockProofStatus( p.ctx, p.rpc, event.BlockId, @@ -468,8 +468,22 @@ func (p *Prover) onBlockProposed( return fmt.Errorf("failed to check whether the L2 block needs a new proof: %w", err) } - if !needNewProof { - return nil + if proofStatus.IsSubmitted { + // If there is already a proof submitted and there is no need to contest + // it, we skip proving this block here. + if !proofStatus.Invalid || !p.cfg.ContesterMode { + return nil + } + + // The proof submitted to protocol is invalid. + return p.handleInvalidProof( + ctx, event.BlockId, + new(big.Int).SetUint64(event.Raw.BlockNumber), + proofStatus.ParentBlock.Hash(), + proofStatus.CurrentTransitionState.Contester, + &event.Meta, + proofStatus.CurrentTransitionState.Tier, + ) } log.Info( @@ -604,6 +618,44 @@ func (p *Prover) onBlockProposed( return nil } +// handleInvalidProof handles the case when the proof submitted to protocol is invalid. +func (p *Prover) handleInvalidProof( + ctx context.Context, + blockID *big.Int, + proposedIn *big.Int, + parentHash common.Hash, + contester common.Address, + meta *bindings.TaikoDataBlockMetadata, + tier uint16, +) error { + // The proof submitted to protocol is invalid. + log.Info( + "Invalid proof detected", + "blockID", blockID, + "parent", parentHash, + ) + + // If there is no contester, we submit a contest to protocol. + if contester == rpc.ZeroAddress { + log.Info( + "Try submitting a contest", + "blockID", blockID, + "parent", parentHash, + ) + + return p.proofContester.SubmitContest(ctx, blockID, proposedIn, parentHash, meta, tier) + } + + log.Info( + "Try submitting a higher tier proof", + "blockID", blockID, + "parent", parentHash, + ) + + // If there is already a contester, we try submitting a proof with a higher tier here. + return p.requestProofByBlockID(blockID, proposedIn, tier+1, nil) +} + // submitProofOp performs a proof submission operation. func (p *Prover) submitProofOp(ctx context.Context, proofWithHeader *proofProducer.ProofWithHeader) { go func() { @@ -921,7 +973,14 @@ func (p *Prover) requestProofByBlockID( } if transitionProvedEvent != nil { - return p.proofContester.SubmitContest(ctx, event, transitionProvedEvent) + return p.proofContester.SubmitContest( + ctx, + event.BlockId, + new(big.Int).SetUint64(event.Raw.BlockNumber), + transitionProvedEvent.Tran.ParentHash, + &event.Meta, + transitionProvedEvent.Tier, + ) } // If there is no proof submitter selected, skip proving it. @@ -997,12 +1056,26 @@ func (p *Prover) onProvingWindowExpired(ctx context.Context, e *bindings.TaikoL1 return nil } // Check if we still need to generate a new proof for that block. - needNewProof, err := rpc.NeedNewProof(ctx, p.rpc, e.BlockId, p.proverAddress) + proofStatus, err := rpc.GetBlockProofStatus(ctx, p.rpc, e.BlockId, p.proverAddress) if err != nil { return err } - if !needNewProof { - return nil + if proofStatus.IsSubmitted { + // If there is already a proof submitted and there is no need to contest + // it, we skip proving this block here. + if !proofStatus.Invalid || !p.cfg.ContesterMode { + return nil + } + + return p.handleInvalidProof( + ctx, + e.BlockId, + new(big.Int).SetUint64(e.Raw.BlockNumber), + proofStatus.ParentBlock.Hash(), + proofStatus.CurrentTransitionState.Contester, + &e.Meta, + proofStatus.CurrentTransitionState.Tier, + ) } return p.requestProofByBlockID(e.BlockId, new(big.Int).SetUint64(e.Raw.BlockNumber), e.Meta.MinTier, nil) diff --git a/prover/prover_test.go b/prover/prover_test.go index 97115102d..4cb546ac6 100644 --- a/prover/prover_test.go +++ b/prover/prover_test.go @@ -259,19 +259,27 @@ func (s *ProverTestSuite) TestContestWrongBlocks() { s.Equal(header.Hash(), common.BytesToHash(contestedEvent.Tran.BlockHash[:])) s.Equal(header.ParentHash, common.BytesToHash(contestedEvent.Tran.ParentHash[:])) - approvedSink := make(chan *bindings.GuardianProverApproved) - approvedSub, err := s.p.rpc.GuardianProver.WatchApproved(nil, approvedSink, []uint64{}) - s.Nil(err) - defer func() { - approvedSub.Unsubscribe() - close(approvedSink) - }() - s.Nil(s.p.onTransitionContested(context.Background(), contestedEvent)) - s.Nil(s.p.selectSubmitter(encoding.TierGuardianID).SubmitProof(context.Background(), <-s.p.proofGenerationCh)) - approvedEvent := <-approvedSink - s.Equal(header.Number.Uint64(), approvedEvent.BlockId) + if contestedEvent.Tier >= encoding.TierSgxAndPseZkevmID { + approvedSink := make(chan *bindings.GuardianProverApproved) + approvedSub, err := s.p.rpc.GuardianProver.WatchApproved(nil, approvedSink, []uint64{}) + s.Nil(err) + defer func() { + approvedSub.Unsubscribe() + close(approvedSink) + }() + + s.Nil(s.p.selectSubmitter(encoding.TierGuardianID).SubmitProof(context.Background(), <-s.p.proofGenerationCh)) + approvedEvent := <-approvedSink + + s.Equal(header.Number.Uint64(), approvedEvent.BlockId) + return + } + + s.Nil(s.p.selectSubmitter(contestedEvent.Tier+1).SubmitProof(context.Background(), <-s.p.proofGenerationCh)) + event = <-sink + s.Equal(header.Number.Uint64(), event.BlockId.Uint64()) } func (s *ProverTestSuite) TestProveExpiredUnassignedBlock() { diff --git a/prover/server/api.go b/prover/server/api.go index 79fb1e92e..805b21ac7 100644 --- a/prover/server/api.go +++ b/prover/server/api.go @@ -73,6 +73,7 @@ func (srv *ProverServer) GetStatus(c echo.Context) error { type ProposeBlockResponse struct { SignedPayload []byte `json:"signedPayload"` Prover common.Address `json:"prover"` + MaxBlockID uint64 `json:"maxBlockID"` } // CreateAssignment handles a block proof assignment request, decides if this prover wants to @@ -180,11 +181,18 @@ func (srv *ProverServer) CreateAssignment(c echo.Context) error { return echo.NewHTTPError(http.StatusUnprocessableEntity, "prover does not have capacity") } + l1Head, err := srv.rpc.L1.BlockNumber(c.Request().Context()) + if err != nil { + log.Error("Failed to get L1 block head", "error", err) + return echo.NewHTTPError(http.StatusUnprocessableEntity, err) + } + encoded, err := encoding.EncodeProverAssignmentPayload( srv.taikoL1Address, req.TxListHash, req.FeeToken, req.Expiry, + l1Head+srv.maxSlippage, req.TierFees, ) if err != nil { @@ -200,5 +208,6 @@ func (srv *ProverServer) CreateAssignment(c echo.Context) error { return c.JSON(http.StatusOK, &ProposeBlockResponse{ SignedPayload: signed, Prover: srv.proverAddress, + MaxBlockID: l1Head + srv.maxSlippage, }) } diff --git a/prover/server/server.go b/prover/server/server.go index c2a69c399..3b0c9a466 100644 --- a/prover/server/server.go +++ b/prover/server/server.go @@ -38,6 +38,7 @@ type ProverServer struct { minPseZkevmTierFee *big.Int minSgxAndPseZkevmTierFee *big.Int maxExpiry time.Duration + maxSlippage uint64 capacityManager *capacity.CapacityManager taikoL1Address common.Address rpc *rpc.Client @@ -53,6 +54,7 @@ type NewProverServerOpts struct { MinPseZkevmTierFee *big.Int MinSgxAndPseZkevmTierFee *big.Int MaxExpiry time.Duration + MaxBlockSlippage uint64 CapacityManager *capacity.CapacityManager TaikoL1Address common.Address Rpc *rpc.Client @@ -71,6 +73,7 @@ func New(opts *NewProverServerOpts) (*ProverServer, error) { minPseZkevmTierFee: opts.MinPseZkevmTierFee, minSgxAndPseZkevmTierFee: opts.MinSgxAndPseZkevmTierFee, maxExpiry: opts.MaxExpiry, + maxSlippage: opts.MaxBlockSlippage, capacityManager: opts.CapacityManager, taikoL1Address: opts.TaikoL1Address, rpc: opts.Rpc,