diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a8710e59d..6619e76a1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v3 with: - go-version: 1.19 + go-version: 1.21 cache: true - name: Lint @@ -52,7 +52,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v3 with: - go-version: 1.19 + go-version: 1.21 cache: true cache-dependency-path: ${{ env.CLIENT_DIR }}/go.sum diff --git a/Dockerfile b/Dockerfile index 3aa050c51..f30426dea 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.19-alpine as builder +FROM golang:1.21-alpine as builder RUN apk add --no-cache gcc musl-dev linux-headers git make diff --git a/bindings/.githead b/bindings/.githead index 0d8a74c9d..ff5b83cf8 100644 --- a/bindings/.githead +++ b/bindings/.githead @@ -1 +1 @@ -c8969b64bbaacf9ec6d239608509424fdc02ee97 +8028a49ea2dfc123b1c818afb722a029d3743e5c diff --git a/bindings/encoding/input.go b/bindings/encoding/input.go index bce39d85b..12ec44ebb 100644 --- a/bindings/encoding/input.go +++ b/bindings/encoding/input.go @@ -22,7 +22,7 @@ var ( Type: "bytes32", }, { - Name: "beneficiary", + Name: "proposer", Type: "address", }, { @@ -76,7 +76,7 @@ var ( Type: "uint32", }, { - Name: "beneficiary", + Name: "proposer", Type: "address", }, { diff --git a/bindings/encoding/input_test.go b/bindings/encoding/input_test.go index fc92098db..7b17cf93e 100644 --- a/bindings/encoding/input_test.go +++ b/bindings/encoding/input_test.go @@ -99,7 +99,7 @@ func TestEncodeBlockMetadata(t *testing.T) { Id: uint64(1), L1Height: uint64(1), L1Hash: abcdBytes, - Beneficiary: common.HexToAddress("0x10020FCb72e27650651B05eD2CEcA493bC807Ba4"), + Proposer: common.HexToAddress("0x10020FCb72e27650651B05eD2CEcA493bC807Ba4"), TxListHash: abcdBytes, TxListByteStart: big.NewInt(0), TxListByteEnd: big.NewInt(1000), @@ -131,7 +131,7 @@ func TestEncodeBlockMetadata(t *testing.T) { Id: uint64(1), L1Height: uint64(1), L1Hash: abcdBytes, - Beneficiary: common.HexToAddress("0x10020FCb72e27650651B05eD2CEcA493bC807Ba4"), + Proposer: common.HexToAddress("0x10020FCb72e27650651B05eD2CEcA493bC807Ba4"), TxListHash: abcdBytes, TxListByteStart: big.NewInt(0), TxListByteEnd: big.NewInt(1000), diff --git a/bindings/encoding/struct.go b/bindings/encoding/struct.go index bd2f6bc85..d0f61fd59 100644 --- a/bindings/encoding/struct.go +++ b/bindings/encoding/struct.go @@ -43,7 +43,7 @@ type TaikoL1Evidence struct { type TaikoL1BlockMetadataInput struct { TxListHash [32]byte - Beneficiary common.Address + Proposer common.Address TxListByteStart *big.Int TxListByteEnd *big.Int CacheTxListInfo bool diff --git a/bindings/encoding/struct_test.go b/bindings/encoding/struct_test.go index a817ad751..00f05a105 100644 --- a/bindings/encoding/struct_test.go +++ b/bindings/encoding/struct_test.go @@ -1,6 +1,7 @@ package encoding import ( + cryptoRand "crypto/rand" "math/big" "math/rand" "testing" @@ -33,7 +34,7 @@ var ( BaseFee: new(big.Int).SetUint64(rand.Uint64()), } testMetaInput = TaikoL1BlockMetadataInput{ - Beneficiary: common.BytesToAddress(randomHash().Bytes()), + Proposer: common.BytesToAddress(randomHash().Bytes()), TxListHash: randomHash(), TxListByteStart: common.Big0, TxListByteEnd: common.Big0, @@ -49,7 +50,7 @@ var ( TxListByteStart: common.Big0, TxListByteEnd: common.Big256, GasLimit: rand.Uint32(), - Beneficiary: common.BytesToAddress(randomHash().Bytes()), + Proposer: common.BytesToAddress(randomHash().Bytes()), DepositsProcessed: []bindings.TaikoDataEthDeposit{}, } ) @@ -122,7 +123,7 @@ func TestToExecutableData(t *testing.T) { // randomHash generates a random blob of data and returns it as a hash. func randomHash() common.Hash { var hash common.Hash - if n, err := rand.Read(hash[:]); n != common.HashLength || err != nil { + if n, err := cryptoRand.Read(hash[:]); n != common.HashLength || err != nil { panic(err) } return hash @@ -131,7 +132,7 @@ func randomHash() common.Hash { // randomBytes generates a random bytes. func randomBytes(size int) (b []byte) { b = make([]byte, size) - if _, err := rand.Read(b); err != nil { + if _, err := cryptoRand.Read(b); err != nil { log.Crit("Generate random bytes error", "error", err) } return diff --git a/bindings/gen_taiko_l1.go b/bindings/gen_taiko_l1.go index d85337358..5d5da1bcc 100644 --- a/bindings/gen_taiko_l1.go +++ b/bindings/gen_taiko_l1.go @@ -38,7 +38,6 @@ type TaikoDataBlock struct { ProposedAt uint64 NextTransitionId uint32 VerifiedTransitionId uint32 - ProofWindow uint16 } // TaikoDataBlockMetadata is an auto generated low-level Go binding around an user-defined struct. @@ -52,7 +51,7 @@ type TaikoDataBlockMetadata struct { TxListByteStart *big.Int TxListByteEnd *big.Int GasLimit uint32 - Beneficiary common.Address + Proposer common.Address DepositsProcessed []TaikoDataEthDeposit } @@ -127,7 +126,7 @@ type TaikoDataTransition struct { // TaikoL1ClientMetaData contains all meta data concerning the TaikoL1Client contract. var TaikoL1ClientMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"name\":\"L1_ALREADY_PROVEN\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ALREADY_PROVEN\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_BLOCK_ID_MISMATCH\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_BLOCK_ID_MISMATCH\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_BLOCK_ID_MISMATCH\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_EVIDENCE_MISMATCH\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_EVIDENCE_MISMATCH\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INSUFFICIENT_TOKEN\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INSUFFICIENT_TOKEN\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_ASSIGNMENT\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_ASSIGNMENT\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_BLOCK_ID\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_BLOCK_ID\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_BLOCK_ID\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_CONFIG\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_CONFIG\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_ETH_DEPOSIT\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_ETH_DEPOSIT\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_EVIDENCE\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_EVIDENCE\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_METADATA\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_METADATA\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_ORACLE_PROVER\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_ORACLE_PROVER\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_PARAM\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_PROOF\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_PROPOSER\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_PROPOSER\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_PROVER\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_PROVER\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_PROVER_SIG\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_PROVER_SIG\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_NOT_PROVEABLE\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_NOT_PROVEABLE\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_SAME_PROOF\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_SAME_PROOF\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_TOO_MANY_BLOCKS\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_TOO_MANY_BLOCKS\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_TRANSITION_NOT_FOUND\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_TRANSITION_NOT_FOUND\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_TX_LIST\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_TX_LIST\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_TX_LIST_HASH\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_TX_LIST_HASH\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_TX_LIST_NOT_EXIST\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_TX_LIST_NOT_EXIST\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_TX_LIST_RANGE\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_TX_LIST_RANGE\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_UNEXPECTED_TRANSITION_ID\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_UNEXPECTED_TRANSITION_ID\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RESOLVER_DENIED\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RESOLVER_INVALID_ADDR\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"name\",\"type\":\"bytes32\"}],\"name\":\"RESOLVER_ZERO_ADDR\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"addressManager\",\"type\":\"address\"}],\"name\":\"AddressManagerChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"blockId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"reward\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"id\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"l1Height\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"l1Hash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"mixHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"txListHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint24\",\"name\":\"txListByteStart\",\"type\":\"uint24\"},{\"internalType\":\"uint24\",\"name\":\"txListByteEnd\",\"type\":\"uint24\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"beneficiary\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"},{\"internalType\":\"uint64\",\"name\":\"id\",\"type\":\"uint64\"}],\"internalType\":\"structTaikoData.EthDeposit[]\",\"name\":\"depositsProcessed\",\"type\":\"tuple[]\"}],\"indexed\":false,\"internalType\":\"structTaikoData.BlockMetadata\",\"name\":\"meta\",\"type\":\"tuple\"}],\"name\":\"BlockProposed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"blockId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"reward\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"id\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"l1Height\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"l1Hash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"mixHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"txListHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint24\",\"name\":\"txListByteStart\",\"type\":\"uint24\"},{\"internalType\":\"uint24\",\"name\":\"txListByteEnd\",\"type\":\"uint24\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"beneficiary\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"},{\"internalType\":\"uint64\",\"name\":\"id\",\"type\":\"uint64\"}],\"internalType\":\"structTaikoData.EthDeposit[]\",\"name\":\"depositsProcessed\",\"type\":\"tuple[]\"}],\"indexed\":false,\"internalType\":\"structTaikoData.BlockMetadata\",\"name\":\"meta\",\"type\":\"tuple\"}],\"name\":\"BlockProposed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"blockId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"signalRoot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"}],\"name\":\"BlockProven\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"blockId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"signalRoot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"}],\"name\":\"BlockProven\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"blockId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"name\":\"BlockVerified\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"blockId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"name\":\"BlockVerified\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"srcHeight\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"signalRoot\",\"type\":\"bytes32\"}],\"name\":\"CrossChainSynced\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"srcHeight\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"signalRoot\",\"type\":\"bytes32\"}],\"name\":\"CrossChainSynced\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"},{\"internalType\":\"uint64\",\"name\":\"id\",\"type\":\"uint64\"}],\"indexed\":false,\"internalType\":\"structTaikoData.EthDeposit\",\"name\":\"deposit\",\"type\":\"tuple\"}],\"name\":\"EthDeposited\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"},{\"internalType\":\"uint64\",\"name\":\"id\",\"type\":\"uint64\"}],\"indexed\":false,\"internalType\":\"structTaikoData.EthDeposit\",\"name\":\"deposit\",\"type\":\"tuple\"}],\"name\":\"EthDeposited\",\"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\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"addressManager\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"canDepositEthToL2\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"}],\"name\":\"depositEtherToL2\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"depositTaikoToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"blockId\",\"type\":\"uint64\"}],\"name\":\"getBlock\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"metaHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"proofBond\",\"type\":\"uint96\"},{\"internalType\":\"uint64\",\"name\":\"blockId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"proposedAt\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"nextTransitionId\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"verifiedTransitionId\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"proofWindow\",\"type\":\"uint16\"}],\"internalType\":\"structTaikoData.Block\",\"name\":\"blk\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"relaySignalRoot\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"blockMaxProposals\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"blockRingBufferSize\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"blockMaxVerificationsPerTx\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"blockMaxGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"blockFeeBaseGas\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"blockMaxTxListBytes\",\"type\":\"uint24\"},{\"internalType\":\"uint256\",\"name\":\"blockTxListExpiry\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"proposerRewardPerSecond\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"proposerRewardMax\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"proofRegularCooldown\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"proofOracleCooldown\",\"type\":\"uint256\"},{\"internalType\":\"uint16\",\"name\":\"proofWindow\",\"type\":\"uint16\"},{\"internalType\":\"uint96\",\"name\":\"proofBond\",\"type\":\"uint96\"},{\"internalType\":\"bool\",\"name\":\"skipProverAssignmentVerificaiton\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"ethDepositRingBufferSize\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"ethDepositMinCountPerBlock\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"ethDepositMaxCountPerBlock\",\"type\":\"uint64\"},{\"internalType\":\"uint96\",\"name\":\"ethDepositMinAmount\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"ethDepositMaxAmount\",\"type\":\"uint96\"},{\"internalType\":\"uint256\",\"name\":\"ethDepositGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"ethDepositMaxFee\",\"type\":\"uint256\"}],\"internalType\":\"structTaikoData.Config\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"blockId\",\"type\":\"uint64\"}],\"name\":\"getCrossChainBlockHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"blockId\",\"type\":\"uint64\"}],\"name\":\"getCrossChainSignalRoot\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStateVariables\",\"outputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"genesisHeight\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"genesisTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"numBlocks\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastVerifiedBlockId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nextEthDepositToProcess\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"numEthDeposits\",\"type\":\"uint64\"}],\"internalType\":\"structTaikoData.StateVariables\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"getTaikoTokenBalance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"blockId\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"}],\"name\":\"getTransition\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"key\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"signalRoot\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"provenAt\",\"type\":\"uint64\"}],\"internalType\":\"structTaikoData.Transition\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"id\",\"type\":\"uint16\"}],\"name\":\"getVerifierName\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addressManager\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"_genesisBlockHash\",\"type\":\"bytes32\"}],\"name\":\"init\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"input\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"assignment\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"txList\",\"type\":\"bytes\"}],\"name\":\"proposeBlock\",\"outputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"id\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"l1Height\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"l1Hash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"mixHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"txListHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint24\",\"name\":\"txListByteStart\",\"type\":\"uint24\"},{\"internalType\":\"uint24\",\"name\":\"txListByteEnd\",\"type\":\"uint24\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"beneficiary\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"},{\"internalType\":\"uint64\",\"name\":\"id\",\"type\":\"uint64\"}],\"internalType\":\"structTaikoData.EthDeposit[]\",\"name\":\"depositsProcessed\",\"type\":\"tuple[]\"}],\"internalType\":\"structTaikoData.BlockMetadata\",\"name\":\"meta\",\"type\":\"tuple\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"blockId\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"input\",\"type\":\"bytes\"}],\"name\":\"proveBlock\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"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\":[{\"internalType\":\"address\",\"name\":\"newAddressManager\",\"type\":\"address\"}],\"name\":\"setAddressManager\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"state\",\"outputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"genesisHeight\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"genesisTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"numEthDeposits\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nextEthDepositToProcess\",\"type\":\"uint64\"}],\"internalType\":\"structTaikoData.SlotA\",\"name\":\"slotA\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"numBlocks\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nextEthDepositToProcess\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastVerifiedAt\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastVerifiedBlockId\",\"type\":\"uint64\"}],\"internalType\":\"structTaikoData.SlotB\",\"name\":\"slotB\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"maxBlocks\",\"type\":\"uint64\"}],\"name\":\"verifyBlocks\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"withdrawTaikoToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]", + ABI: "[{\"inputs\":[],\"name\":\"L1_ALREADY_PROVEN\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_ALREADY_PROVEN\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_BLOCK_ID_MISMATCH\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_BLOCK_ID_MISMATCH\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_BLOCK_ID_MISMATCH\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_EVIDENCE_MISMATCH\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_EVIDENCE_MISMATCH\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INSUFFICIENT_TOKEN\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INSUFFICIENT_TOKEN\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_ASSIGNMENT\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_ASSIGNMENT\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_BLOCK_ID\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_BLOCK_ID\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_BLOCK_ID\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_CONFIG\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_CONFIG\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_ETH_DEPOSIT\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_ETH_DEPOSIT\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_EVIDENCE\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_EVIDENCE\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_METADATA\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_METADATA\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_ORACLE_PROVER\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_ORACLE_PROVER\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_PARAM\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_PROOF\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_PROPOSER\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_PROPOSER\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_PROVER\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_PROVER\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_PROVER_SIG\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_INVALID_PROVER_SIG\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_NOT_PROVEABLE\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_NOT_PROVEABLE\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_SAME_PROOF\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_SAME_PROOF\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_TOO_MANY_BLOCKS\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_TOO_MANY_BLOCKS\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_TRANSITION_NOT_FOUND\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_TRANSITION_NOT_FOUND\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_TX_LIST\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_TX_LIST\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_TX_LIST_HASH\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_TX_LIST_HASH\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_TX_LIST_NOT_EXIST\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_TX_LIST_NOT_EXIST\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_TX_LIST_RANGE\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_TX_LIST_RANGE\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_UNEXPECTED_TRANSITION_ID\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"L1_UNEXPECTED_TRANSITION_ID\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RESOLVER_DENIED\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RESOLVER_INVALID_ADDR\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"name\",\"type\":\"bytes32\"}],\"name\":\"RESOLVER_ZERO_ADDR\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"addressManager\",\"type\":\"address\"}],\"name\":\"AddressManagerChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"blockId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"reward\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"id\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"l1Height\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"l1Hash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"mixHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"txListHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint24\",\"name\":\"txListByteStart\",\"type\":\"uint24\"},{\"internalType\":\"uint24\",\"name\":\"txListByteEnd\",\"type\":\"uint24\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"proposer\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"},{\"internalType\":\"uint64\",\"name\":\"id\",\"type\":\"uint64\"}],\"internalType\":\"structTaikoData.EthDeposit[]\",\"name\":\"depositsProcessed\",\"type\":\"tuple[]\"}],\"indexed\":false,\"internalType\":\"structTaikoData.BlockMetadata\",\"name\":\"meta\",\"type\":\"tuple\"}],\"name\":\"BlockProposed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"blockId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"reward\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"id\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"l1Height\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"l1Hash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"mixHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"txListHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint24\",\"name\":\"txListByteStart\",\"type\":\"uint24\"},{\"internalType\":\"uint24\",\"name\":\"txListByteEnd\",\"type\":\"uint24\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"proposer\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"},{\"internalType\":\"uint64\",\"name\":\"id\",\"type\":\"uint64\"}],\"internalType\":\"structTaikoData.EthDeposit[]\",\"name\":\"depositsProcessed\",\"type\":\"tuple[]\"}],\"indexed\":false,\"internalType\":\"structTaikoData.BlockMetadata\",\"name\":\"meta\",\"type\":\"tuple\"}],\"name\":\"BlockProposed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"blockId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"signalRoot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"}],\"name\":\"BlockProven\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"blockId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"signalRoot\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"}],\"name\":\"BlockProven\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"blockId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"name\":\"BlockVerified\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"blockId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"}],\"name\":\"BlockVerified\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"srcHeight\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"signalRoot\",\"type\":\"bytes32\"}],\"name\":\"CrossChainSynced\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"srcHeight\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"signalRoot\",\"type\":\"bytes32\"}],\"name\":\"CrossChainSynced\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"},{\"internalType\":\"uint64\",\"name\":\"id\",\"type\":\"uint64\"}],\"indexed\":false,\"internalType\":\"structTaikoData.EthDeposit\",\"name\":\"deposit\",\"type\":\"tuple\"}],\"name\":\"EthDeposited\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"},{\"internalType\":\"uint64\",\"name\":\"id\",\"type\":\"uint64\"}],\"indexed\":false,\"internalType\":\"structTaikoData.EthDeposit\",\"name\":\"deposit\",\"type\":\"tuple\"}],\"name\":\"EthDeposited\",\"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\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"addressManager\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"canDepositEthToL2\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"}],\"name\":\"depositEtherToL2\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"depositTaikoToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"blockId\",\"type\":\"uint64\"}],\"name\":\"getBlock\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"metaHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"proofBond\",\"type\":\"uint96\"},{\"internalType\":\"uint64\",\"name\":\"blockId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"proposedAt\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"nextTransitionId\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"verifiedTransitionId\",\"type\":\"uint32\"}],\"internalType\":\"structTaikoData.Block\",\"name\":\"blk\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getConfig\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"relaySignalRoot\",\"type\":\"bool\"},{\"internalType\":\"uint64\",\"name\":\"blockMaxProposals\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"blockRingBufferSize\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"blockMaxVerificationsPerTx\",\"type\":\"uint64\"},{\"internalType\":\"uint32\",\"name\":\"blockMaxGasLimit\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"blockFeeBaseGas\",\"type\":\"uint32\"},{\"internalType\":\"uint24\",\"name\":\"blockMaxTxListBytes\",\"type\":\"uint24\"},{\"internalType\":\"uint256\",\"name\":\"blockTxListExpiry\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"proposerRewardPerSecond\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"proposerRewardMax\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"proofRegularCooldown\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"proofOracleCooldown\",\"type\":\"uint256\"},{\"internalType\":\"uint16\",\"name\":\"proofWindow\",\"type\":\"uint16\"},{\"internalType\":\"uint96\",\"name\":\"proofBond\",\"type\":\"uint96\"},{\"internalType\":\"bool\",\"name\":\"skipProverAssignmentVerificaiton\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"ethDepositRingBufferSize\",\"type\":\"uint256\"},{\"internalType\":\"uint64\",\"name\":\"ethDepositMinCountPerBlock\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"ethDepositMaxCountPerBlock\",\"type\":\"uint64\"},{\"internalType\":\"uint96\",\"name\":\"ethDepositMinAmount\",\"type\":\"uint96\"},{\"internalType\":\"uint96\",\"name\":\"ethDepositMaxAmount\",\"type\":\"uint96\"},{\"internalType\":\"uint256\",\"name\":\"ethDepositGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"ethDepositMaxFee\",\"type\":\"uint256\"}],\"internalType\":\"structTaikoData.Config\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"blockId\",\"type\":\"uint64\"}],\"name\":\"getCrossChainBlockHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"blockId\",\"type\":\"uint64\"}],\"name\":\"getCrossChainSignalRoot\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getStateVariables\",\"outputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"genesisHeight\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"genesisTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"numBlocks\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastVerifiedBlockId\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nextEthDepositToProcess\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"numEthDeposits\",\"type\":\"uint64\"}],\"internalType\":\"structTaikoData.StateVariables\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"getTaikoTokenBalance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"blockId\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"parentHash\",\"type\":\"bytes32\"}],\"name\":\"getTransition\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"key\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"blockHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"signalRoot\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"prover\",\"type\":\"address\"},{\"internalType\":\"uint64\",\"name\":\"provenAt\",\"type\":\"uint64\"}],\"internalType\":\"structTaikoData.Transition\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"id\",\"type\":\"uint16\"}],\"name\":\"getVerifierName\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_addressManager\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"_genesisBlockHash\",\"type\":\"bytes32\"}],\"name\":\"init\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"input\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"assignment\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"txList\",\"type\":\"bytes\"}],\"name\":\"proposeBlock\",\"outputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"id\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"l1Height\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"l1Hash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"mixHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"txListHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint24\",\"name\":\"txListByteStart\",\"type\":\"uint24\"},{\"internalType\":\"uint24\",\"name\":\"txListByteEnd\",\"type\":\"uint24\"},{\"internalType\":\"uint32\",\"name\":\"gasLimit\",\"type\":\"uint32\"},{\"internalType\":\"address\",\"name\":\"proposer\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"recipient\",\"type\":\"address\"},{\"internalType\":\"uint96\",\"name\":\"amount\",\"type\":\"uint96\"},{\"internalType\":\"uint64\",\"name\":\"id\",\"type\":\"uint64\"}],\"internalType\":\"structTaikoData.EthDeposit[]\",\"name\":\"depositsProcessed\",\"type\":\"tuple[]\"}],\"internalType\":\"structTaikoData.BlockMetadata\",\"name\":\"meta\",\"type\":\"tuple\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"blockId\",\"type\":\"uint64\"},{\"internalType\":\"bytes\",\"name\":\"input\",\"type\":\"bytes\"}],\"name\":\"proveBlock\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"chainId\",\"type\":\"uint256\"},{\"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\":[{\"internalType\":\"address\",\"name\":\"newAddressManager\",\"type\":\"address\"}],\"name\":\"setAddressManager\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"state\",\"outputs\":[{\"components\":[{\"internalType\":\"uint64\",\"name\":\"genesisHeight\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"genesisTimestamp\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"numEthDeposits\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nextEthDepositToProcess\",\"type\":\"uint64\"}],\"internalType\":\"structTaikoData.SlotA\",\"name\":\"slotA\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"numBlocks\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"nextEthDepositToProcess\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastVerifiedAt\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"lastVerifiedBlockId\",\"type\":\"uint64\"}],\"internalType\":\"structTaikoData.SlotB\",\"name\":\"slotB\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"maxBlocks\",\"type\":\"uint64\"}],\"name\":\"verifyBlocks\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"withdrawTaikoToken\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]", } // TaikoL1ClientABI is the input ABI used to generate the binding from. @@ -340,7 +339,7 @@ func (_TaikoL1Client *TaikoL1ClientCallerSession) CanDepositEthToL2(amount *big. // GetBlock is a free data retrieval call binding the contract method 0x5fa15e79. // -// Solidity: function getBlock(uint64 blockId) view returns((bytes32,address,uint96,uint64,uint64,uint32,uint32,uint16) blk) +// Solidity: function getBlock(uint64 blockId) view returns((bytes32,address,uint96,uint64,uint64,uint32,uint32) blk) func (_TaikoL1Client *TaikoL1ClientCaller) GetBlock(opts *bind.CallOpts, blockId uint64) (TaikoDataBlock, error) { var out []interface{} err := _TaikoL1Client.contract.Call(opts, &out, "getBlock", blockId) @@ -357,14 +356,14 @@ func (_TaikoL1Client *TaikoL1ClientCaller) GetBlock(opts *bind.CallOpts, blockId // GetBlock is a free data retrieval call binding the contract method 0x5fa15e79. // -// Solidity: function getBlock(uint64 blockId) view returns((bytes32,address,uint96,uint64,uint64,uint32,uint32,uint16) blk) +// Solidity: function getBlock(uint64 blockId) view returns((bytes32,address,uint96,uint64,uint64,uint32,uint32) blk) func (_TaikoL1Client *TaikoL1ClientSession) GetBlock(blockId uint64) (TaikoDataBlock, error) { return _TaikoL1Client.Contract.GetBlock(&_TaikoL1Client.CallOpts, blockId) } // GetBlock is a free data retrieval call binding the contract method 0x5fa15e79. // -// Solidity: function getBlock(uint64 blockId) view returns((bytes32,address,uint96,uint64,uint64,uint32,uint32,uint16) blk) +// Solidity: function getBlock(uint64 blockId) view returns((bytes32,address,uint96,uint64,uint64,uint32,uint32) blk) func (_TaikoL1Client *TaikoL1ClientCallerSession) GetBlock(blockId uint64) (TaikoDataBlock, error) { return _TaikoL1Client.Contract.GetBlock(&_TaikoL1Client.CallOpts, blockId) } diff --git a/cmd/main.go b/cmd/main.go index ea37d4119..db99f174a 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -2,9 +2,7 @@ package main import ( "fmt" - "math/rand" "os" - "time" "github.com/taikoxyz/taiko-client/cmd/flags" "github.com/taikoxyz/taiko-client/cmd/utils" @@ -15,10 +13,6 @@ import ( "github.com/urfave/cli/v2" ) -func init() { - rand.Seed(time.Now().UnixNano()) -} - func main() { app := cli.NewApp() diff --git a/driver/chain_syncer/calldata/syncer.go b/driver/chain_syncer/calldata/syncer.go index 5f618c6b8..cb9db3562 100644 --- a/driver/chain_syncer/calldata/syncer.go +++ b/driver/chain_syncer/calldata/syncer.go @@ -422,11 +422,11 @@ func (s *Syncer) createExecutionPayloads( attributes := &engine.PayloadAttributes{ Timestamp: event.Meta.Timestamp, Random: event.Meta.MixHash, - SuggestedFeeRecipient: event.Meta.Beneficiary, + SuggestedFeeRecipient: event.Meta.Proposer, Withdrawals: withdrawals, BlockMetadata: &engine.BlockMetadata{ HighestBlockID: headBlockID, - Beneficiary: event.Meta.Beneficiary, + Beneficiary: event.Meta.Proposer, GasLimit: uint64(event.Meta.GasLimit) + s.anchorConstructor.GasLimit(), Timestamp: event.Meta.Timestamp, TxList: txListBytes, diff --git a/driver/chain_syncer/calldata/syncer_test.go b/driver/chain_syncer/calldata/syncer_test.go index fb6ac7ace..8186b6353 100644 --- a/driver/chain_syncer/calldata/syncer_test.go +++ b/driver/chain_syncer/calldata/syncer_test.go @@ -2,7 +2,6 @@ package calldata import ( "context" - "fmt" "math/big" "math/rand" "os" @@ -17,23 +16,18 @@ import ( "github.com/taikoxyz/taiko-client/driver/chain_syncer/beaconsync" "github.com/taikoxyz/taiko-client/driver/state" "github.com/taikoxyz/taiko-client/proposer" - "github.com/taikoxyz/taiko-client/prover/http" "github.com/taikoxyz/taiko-client/testutils" ) type CalldataSyncerTestSuite struct { testutils.ClientTestSuite - s *Syncer - p testutils.Proposer - srv *http.Server - cancel context.CancelFunc + s *Syncer + p testutils.Proposer } func (s *CalldataSyncerTestSuite) SetupTest() { s.ClientTestSuite.SetupTest() - port := testutils.RandomPort() - state, err := state.New(context.Background(), s.RpcClient) s.Nil(err) @@ -51,32 +45,27 @@ func (s *CalldataSyncerTestSuite) SetupTest() { l1ProposerPrivKey, err := crypto.ToECDSA(common.Hex2Bytes(os.Getenv("L1_PROPOSER_PRIVATE_KEY"))) s.Nil(err) proposeInterval := 1024 * time.Hour // No need to periodically propose transactions list in unit tests + s.Nil(proposer.InitFromConfig(context.Background(), prop, (&proposer.Config{ - L1Endpoint: os.Getenv("L1_NODE_WS_ENDPOINT"), - L2Endpoint: os.Getenv("L2_EXECUTION_ENGINE_WS_ENDPOINT"), - TaikoL1Address: common.HexToAddress(os.Getenv("TAIKO_L1_ADDRESS")), - TaikoL2Address: common.HexToAddress(os.Getenv("TAIKO_L2_ADDRESS")), - TaikoTokenAddress: common.HexToAddress(os.Getenv("TAIKO_TOKEN_ADDRESS")), - L1ProposerPrivKey: l1ProposerPrivKey, - L2SuggestedFeeRecipient: common.HexToAddress(os.Getenv("L2_SUGGESTED_FEE_RECIPIENT")), - ProposeInterval: &proposeInterval, - MaxProposedTxListsPerEpoch: 1, - WaitReceiptTimeout: 10 * time.Second, - ProverEndpoints: []string{fmt.Sprintf("http://localhost:%v", port)}, - BlockProposalFee: big.NewInt(1000), - BlockProposalFeeIterations: 3, + L1Endpoint: os.Getenv("L1_NODE_WS_ENDPOINT"), + L2Endpoint: os.Getenv("L2_EXECUTION_ENGINE_WS_ENDPOINT"), + TaikoL1Address: common.HexToAddress(os.Getenv("TAIKO_L1_ADDRESS")), + TaikoL2Address: common.HexToAddress(os.Getenv("TAIKO_L2_ADDRESS")), + TaikoTokenAddress: common.HexToAddress(os.Getenv("TAIKO_TOKEN_ADDRESS")), + L1ProposerPrivKey: l1ProposerPrivKey, + L2SuggestedFeeRecipient: common.HexToAddress(os.Getenv("L2_SUGGESTED_FEE_RECIPIENT")), + ProposeInterval: &proposeInterval, + MaxProposedTxListsPerEpoch: 1, + WaitReceiptTimeout: 10 * time.Second, + ProverEndpoints: s.ProverEndpoints, + BlockProposalFee: big.NewInt(1000), + BlockProposalFeeIterations: 3, + BlockProposalFeeIncreasePercentage: common.Big2, }))) - srv, cancel, err := testutils.HTTPServer(&s.ClientTestSuite, port) - s.Nil(err) - - s.srv = srv - s.cancel = cancel - s.p = prop } func (s *CalldataSyncerTestSuite) TestCancelNewSyncer() { - defer s.cancel() ctx, cancel := context.WithCancel(context.Background()) cancel() syncer, err := NewSyncer( @@ -91,14 +80,12 @@ func (s *CalldataSyncerTestSuite) TestCancelNewSyncer() { } func (s *CalldataSyncerTestSuite) TestProcessL1Blocks() { - defer s.cancel() head, err := s.s.rpc.L1.HeaderByNumber(context.Background(), nil) s.Nil(err) s.Nil(s.s.ProcessL1Blocks(context.Background(), head)) } func (s *CalldataSyncerTestSuite) TestProcessL1BlocksReorg() { - defer s.cancel() head, err := s.s.rpc.L1.HeaderByNumber(context.Background(), nil) testutils.ProposeAndInsertEmptyBlocks(&s.ClientTestSuite, s.p, s.s) s.Nil(err) @@ -106,7 +93,6 @@ func (s *CalldataSyncerTestSuite) TestProcessL1BlocksReorg() { } func (s *CalldataSyncerTestSuite) TestOnBlockProposed() { - defer s.cancel() s.Nil(s.s.onBlockProposed( context.Background(), &bindings.TaikoL1ClientBlockProposed{BlockId: common.Big0}, @@ -120,7 +106,6 @@ func (s *CalldataSyncerTestSuite) TestOnBlockProposed() { } func (s *CalldataSyncerTestSuite) TestInsertNewHead() { - defer s.cancel() parent, err := s.s.rpc.L2.HeaderByNumber(context.Background(), nil) s.Nil(err) l1Head, err := s.s.rpc.L1.BlockByNumber(context.Background(), nil) @@ -130,14 +115,14 @@ func (s *CalldataSyncerTestSuite) TestInsertNewHead() { &bindings.TaikoL1ClientBlockProposed{ BlockId: common.Big1, Meta: bindings.TaikoDataBlockMetadata{ - Id: 1, - L1Height: l1Head.NumberU64(), - L1Hash: l1Head.Hash(), - Beneficiary: common.BytesToAddress(testutils.RandomBytes(1024)), - TxListHash: testutils.RandomHash(), - MixHash: testutils.RandomHash(), - GasLimit: rand.Uint32(), - Timestamp: uint64(time.Now().Unix()), + Id: 1, + L1Height: l1Head.NumberU64(), + L1Hash: l1Head.Hash(), + Proposer: common.BytesToAddress(testutils.RandomBytes(1024)), + TxListHash: testutils.RandomHash(), + MixHash: testutils.RandomHash(), + GasLimit: rand.Uint32(), + Timestamp: uint64(time.Now().Unix()), }, }, parent, @@ -153,7 +138,6 @@ func (s *CalldataSyncerTestSuite) TestInsertNewHead() { } func (s *CalldataSyncerTestSuite) TestTreasuryIncomeAllAnchors() { - defer s.cancel() treasury := common.HexToAddress(os.Getenv("TREASURY")) s.NotZero(treasury.Big().Uint64()) @@ -176,7 +160,6 @@ func (s *CalldataSyncerTestSuite) TestTreasuryIncomeAllAnchors() { } func (s *CalldataSyncerTestSuite) TestTreasuryIncome() { - defer s.cancel() treasury := common.HexToAddress(os.Getenv("TREASURY")) s.NotZero(treasury.Big().Uint64()) diff --git a/driver/chain_syncer/chain_syncer_test.go b/driver/chain_syncer/chain_syncer_test.go index 39cd092cc..1ef204ec4 100644 --- a/driver/chain_syncer/chain_syncer_test.go +++ b/driver/chain_syncer/chain_syncer_test.go @@ -2,7 +2,6 @@ package chainSyncer import ( "context" - "fmt" "math/big" "os" @@ -17,7 +16,6 @@ import ( "github.com/taikoxyz/taiko-client/driver/state" "github.com/taikoxyz/taiko-client/pkg/rpc" "github.com/taikoxyz/taiko-client/proposer" - "github.com/taikoxyz/taiko-client/prover/http" "github.com/taikoxyz/taiko-client/testutils" ) @@ -26,15 +24,11 @@ type ChainSyncerTestSuite struct { s *L2ChainSyncer snapshotID string p testutils.Proposer - srv *http.Server - cancel func() } func (s *ChainSyncerTestSuite) SetupTest() { s.ClientTestSuite.SetupTest() - port := testutils.RandomPort() - state, err := state.New(context.Background(), s.RpcClient) s.Nil(err) @@ -53,28 +47,24 @@ func (s *ChainSyncerTestSuite) SetupTest() { l1ProposerPrivKey, err := crypto.ToECDSA(common.Hex2Bytes(os.Getenv("L1_PROPOSER_PRIVATE_KEY"))) s.Nil(err) proposeInterval := 1024 * time.Hour // No need to periodically propose transactions list in unit tests + s.Nil(proposer.InitFromConfig(context.Background(), prop, (&proposer.Config{ - L1Endpoint: os.Getenv("L1_NODE_WS_ENDPOINT"), - L2Endpoint: os.Getenv("L2_EXECUTION_ENGINE_WS_ENDPOINT"), - TaikoL1Address: common.HexToAddress(os.Getenv("TAIKO_L1_ADDRESS")), - TaikoL2Address: common.HexToAddress(os.Getenv("TAIKO_L2_ADDRESS")), - TaikoTokenAddress: common.HexToAddress(os.Getenv("TAIKO_TOKEN_ADDRESS")), - L1ProposerPrivKey: l1ProposerPrivKey, - L2SuggestedFeeRecipient: common.HexToAddress(os.Getenv("L2_SUGGESTED_FEE_RECIPIENT")), - ProposeInterval: &proposeInterval, - MaxProposedTxListsPerEpoch: 1, - WaitReceiptTimeout: 10 * time.Second, - ProverEndpoints: []string{fmt.Sprintf("http://localhost:%v", port)}, - BlockProposalFee: big.NewInt(1000), - BlockProposalFeeIterations: 3, + L1Endpoint: os.Getenv("L1_NODE_WS_ENDPOINT"), + L2Endpoint: os.Getenv("L2_EXECUTION_ENGINE_WS_ENDPOINT"), + TaikoL1Address: common.HexToAddress(os.Getenv("TAIKO_L1_ADDRESS")), + TaikoL2Address: common.HexToAddress(os.Getenv("TAIKO_L2_ADDRESS")), + TaikoTokenAddress: common.HexToAddress(os.Getenv("TAIKO_TOKEN_ADDRESS")), + L1ProposerPrivKey: l1ProposerPrivKey, + L2SuggestedFeeRecipient: common.HexToAddress(os.Getenv("L2_SUGGESTED_FEE_RECIPIENT")), + ProposeInterval: &proposeInterval, + MaxProposedTxListsPerEpoch: 1, + WaitReceiptTimeout: 10 * time.Second, + ProverEndpoints: s.ProverEndpoints, + BlockProposalFee: big.NewInt(1000), + BlockProposalFeeIterations: 3, + BlockProposalFeeIncreasePercentage: common.Big2, }))) - srv, srvCancel, err := testutils.HTTPServer(&s.ClientTestSuite, port) - s.Nil(err) - - s.srv = srv - s.cancel = srvCancel - s.p = prop } @@ -95,7 +85,6 @@ func (s *ChainSyncerTestSuite) TestAheadOfProtocolVerifiedHead2() { testutils.ProposeAndInsertEmptyBlocks(&s.ClientTestSuite, s.p, s.s.calldataSyncer) // NOTE: need to prove the proposed blocks to be verified, writing helper function - // generate transactopts to interact with TaikoL1 contract with. privKey, err := crypto.ToECDSA(common.Hex2Bytes(os.Getenv("L1_PROVER_PRIVATE_KEY"))) s.Nil(err) @@ -108,10 +97,10 @@ func (s *ChainSyncerTestSuite) TestAheadOfProtocolVerifiedHead2() { l2Head, err := s.RpcClient.L2.HeaderByNumber(context.Background(), nil) s.Nil(err) - log.Info("L1HeaderByNumber head:", "number", head.Number) + log.Info("L1HeaderByNumber head", "number", head.Number) // (equiv to s.state.GetL2Head().Number) - log.Info("L2HeaderByNumber head:", "number", l2Head.Number) - log.Info("LatestVerifiedBlock number:", "number", s.s.state.GetLatestVerifiedBlock().ID.Uint64()) + log.Info("L2HeaderByNumber head", "number", l2Head.Number) + log.Info("LatestVerifiedBlock number", "number", s.s.state.GetLatestVerifiedBlock().ID.Uint64()) config, err := s.s.rpc.TaikoL1.GetConfig(&bind.CallOpts{}) s.Nil(err) @@ -124,7 +113,7 @@ func (s *ChainSyncerTestSuite) TestAheadOfProtocolVerifiedHead2() { "evm_increaseTime", config.ProofRegularCooldown.Uint64())) s.NotNil(result) - log.Info("evm time increase:", "number", result) + log.Info("EVM time increase", "number", result) // interact with TaikoL1 contract to allow for verification of L2 blocks tx, err := s.s.rpc.TaikoL1.VerifyBlocks(opts, uint64(3)) @@ -137,9 +126,9 @@ func (s *ChainSyncerTestSuite) TestAheadOfProtocolVerifiedHead2() { l2Head2, err := s.RpcClient.L2.HeaderByNumber(context.Background(), nil) s.Nil(err) - log.Info("L1HeaderByNumber head2:", "number", head2.Number) - log.Info("L2HeaderByNumber head:", "number", l2Head2.Number) - log.Info("LatestVerifiedBlock number:", "number", s.s.state.GetLatestVerifiedBlock().ID.Uint64()) + log.Info("L1HeaderByNumber head2", "number", head2.Number) + log.Info("L2HeaderByNumber head", "number", l2Head2.Number) + log.Info("LatestVerifiedBlock number", "number", s.s.state.GetLatestVerifiedBlock().ID.Uint64()) s.RevertSnapshot() } diff --git a/driver/driver_test.go b/driver/driver_test.go index 925f32702..a543b36fa 100644 --- a/driver/driver_test.go +++ b/driver/driver_test.go @@ -2,7 +2,6 @@ package driver import ( "context" - "fmt" "math/big" "os" "testing" @@ -15,7 +14,6 @@ import ( "github.com/taikoxyz/taiko-client/driver/state" "github.com/taikoxyz/taiko-client/pkg/jwt" "github.com/taikoxyz/taiko-client/proposer" - "github.com/taikoxyz/taiko-client/prover/http" "github.com/taikoxyz/taiko-client/testutils" ) @@ -24,14 +22,11 @@ type DriverTestSuite struct { cancel context.CancelFunc p *proposer.Proposer d *Driver - srv *http.Server } func (s *DriverTestSuite) SetupTest() { s.ClientTestSuite.SetupTest() - port := testutils.RandomPort() - // Init driver jwtSecret, err := jwt.ParseSecretFromFile(os.Getenv("JWT_SECRET")) s.Nil(err) @@ -48,6 +43,7 @@ func (s *DriverTestSuite) SetupTest() { JwtSecret: string(jwtSecret), })) s.d = d + s.cancel = cancel // Init proposer p := new(proposer.Proposer) @@ -57,30 +53,22 @@ func (s *DriverTestSuite) SetupTest() { proposeInterval := 1024 * time.Hour // No need to periodically propose transactions list in unit tests s.Nil(proposer.InitFromConfig(context.Background(), p, (&proposer.Config{ - L1Endpoint: os.Getenv("L1_NODE_WS_ENDPOINT"), - L2Endpoint: os.Getenv("L2_EXECUTION_ENGINE_WS_ENDPOINT"), - TaikoL1Address: common.HexToAddress(os.Getenv("TAIKO_L1_ADDRESS")), - TaikoL2Address: common.HexToAddress(os.Getenv("TAIKO_L2_ADDRESS")), - TaikoTokenAddress: common.HexToAddress(os.Getenv("TAIKO_TOKEN_ADDRESS")), - L1ProposerPrivKey: l1ProposerPrivKey, - L2SuggestedFeeRecipient: common.HexToAddress(os.Getenv("L2_SUGGESTED_FEE_RECIPIENT")), - ProposeInterval: &proposeInterval, // No need to periodically propose transactions list in unit tests - MaxProposedTxListsPerEpoch: 1, - WaitReceiptTimeout: 10 * time.Second, - ProverEndpoints: []string{fmt.Sprintf("http://localhost:%v", port)}, - BlockProposalFee: big.NewInt(1000), - BlockProposalFeeIterations: 3, + L1Endpoint: os.Getenv("L1_NODE_WS_ENDPOINT"), + L2Endpoint: os.Getenv("L2_EXECUTION_ENGINE_WS_ENDPOINT"), + TaikoL1Address: common.HexToAddress(os.Getenv("TAIKO_L1_ADDRESS")), + TaikoL2Address: common.HexToAddress(os.Getenv("TAIKO_L2_ADDRESS")), + TaikoTokenAddress: common.HexToAddress(os.Getenv("TAIKO_TOKEN_ADDRESS")), + L1ProposerPrivKey: l1ProposerPrivKey, + L2SuggestedFeeRecipient: common.HexToAddress(os.Getenv("L2_SUGGESTED_FEE_RECIPIENT")), + ProposeInterval: &proposeInterval, + MaxProposedTxListsPerEpoch: 1, + WaitReceiptTimeout: 10 * time.Second, + ProverEndpoints: s.ProverEndpoints, + BlockProposalFee: big.NewInt(1000), + BlockProposalFeeIterations: 3, + BlockProposalFeeIncreasePercentage: common.Big2, }))) s.p = p - - srv, srvCancel, err := testutils.HTTPServer(&s.ClientTestSuite, port) - s.Nil(err) - - s.srv = srv - s.cancel = func() { - cancel() - srvCancel() - } } func (s *DriverTestSuite) TestName() { diff --git a/go.mod b/go.mod index c1c2be3e6..97b47ff04 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/taikoxyz/taiko-client -go 1.18 +go 1.21 require ( github.com/btcsuite/btcd/btcec/v2 v2.3.2 @@ -8,6 +8,7 @@ require ( github.com/cyberhorsey/webutils v0.0.0-20230314183728-56890c6ddbe7 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 github.com/ethereum/go-ethereum v1.12.2 + github.com/go-resty/resty/v2 v2.7.0 github.com/labstack/echo/v4 v4.11.1 github.com/labstack/gommon v0.4.0 github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 diff --git a/go.sum b/go.sum index a8992374d..4c612b29a 100644 --- a/go.sum +++ b/go.sum @@ -20,6 +20,7 @@ github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuP github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/bazelbuild/rules_go v0.23.2 h1:Wxu7JjqnF78cKZbsBsARLSXx/jlGaSLCnUV3mTlyHvM= +github.com/bazelbuild/rules_go v0.23.2/go.mod h1:MC23Dc/wkXEyk3Wpq6lCqz0ZAYOZDw2DR5y3N1q2i7M= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= @@ -32,6 +33,7 @@ github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8 github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU= +github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= @@ -74,12 +76,14 @@ github.com/cyberhorsey/errors v0.0.0-20220929234051-087d6d8bb841/go.mod h1:qSH/I github.com/cyberhorsey/webutils v0.0.0-20230314183728-56890c6ddbe7 h1:KYOh2RfWAltxYsfD/Ar5D3zB4+AuNQejXW5BvMlGor4= github.com/cyberhorsey/webutils v0.0.0-20230314183728-56890c6ddbe7/go.mod h1:bNNUHadsCy1HleNUToQ/t11vmKI9/+9Taw8K6GyxERo= github.com/d4l3k/messagediff v1.2.1 h1:ZcAIMYsUg0EAp9X+tt8/enBE/Q8Yd5kzPynLyKptt9U= +github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= @@ -100,6 +104,7 @@ github.com/ethereum/c-kzg-4844 v0.3.1/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1 github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= +github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= @@ -107,6 +112,7 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4 github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= +github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ= @@ -116,6 +122,7 @@ github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/ github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= @@ -130,6 +137,8 @@ github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl github.com/go-playground/validator/v10 v10.4.0/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= +github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= @@ -177,6 +186,7 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= @@ -192,9 +202,11 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= +github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw= +github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= @@ -244,6 +256,7 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/labstack/echo/v4 v4.0.0/go.mod h1:tZv7nai5buKSg5h/8E6zz4LsD/Dqh9/91Mvs7Z5Zyno= github.com/labstack/echo/v4 v4.1.15/go.mod h1:GWO5IBVzI371K8XJe50CSvHjQCafK6cw8R/moLhEU6o= github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y= @@ -254,6 +267,7 @@ github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= +github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= @@ -292,6 +306,7 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= +github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= @@ -325,6 +340,7 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/onsi/gomega v1.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg= +github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= @@ -361,6 +377,7 @@ github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4 github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -384,6 +401,7 @@ github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb6 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= +github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -412,6 +430,7 @@ github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7Am github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= +github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= @@ -491,6 +510,7 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= @@ -630,6 +650,7 @@ gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/R gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= diff --git a/pkg/rpc/client.go b/pkg/rpc/client.go index f7e6f8844..2f04b6e0d 100644 --- a/pkg/rpc/client.go +++ b/pkg/rpc/client.go @@ -100,7 +100,7 @@ func NewClient(ctx context.Context, cfg *ClientConfig) (*Client, error) { } var taikoToken *bindings.TaikoToken - if cfg.TaikoTokenAddress.Hex() != zeroAddress.Hex() { + if cfg.TaikoTokenAddress.Hex() != ZeroAddress.Hex() { taikoToken, err = bindings.NewTaikoToken(cfg.TaikoTokenAddress, l1RPC) if err != nil { return nil, err diff --git a/pkg/rpc/utils.go b/pkg/rpc/utils.go index e34c0afb6..e416210f7 100644 --- a/pkg/rpc/utils.go +++ b/pkg/rpc/utils.go @@ -19,9 +19,9 @@ import ( ) var ( + ZeroAddress = common.HexToAddress("0x0000000000000000000000000000000000000000") waitReceiptPollingInterval = 3 * time.Second defaultWaitReceiptTimeout = 1 * time.Minute - zeroAddress = common.HexToAddress("0x0000000000000000000000000000000000000000") ) // GetProtocolStateVariables gets the protocol states from TaikoL1 contract. @@ -195,7 +195,7 @@ func IsArchiveNode(ctx context.Context, client *EthClient, l2GenesisHeight uint6 ctxWithTimeout, cancel := ctxWithTimeoutOrDefault(ctx, defaultTimeout) defer cancel() - if _, err := client.BalanceAt(ctxWithTimeout, zeroAddress, new(big.Int).SetUint64(l2GenesisHeight)); err != nil { + if _, err := client.BalanceAt(ctxWithTimeout, ZeroAddress, new(big.Int).SetUint64(l2GenesisHeight)); err != nil { if strings.Contains(err.Error(), "missing trie node") { return false, nil } diff --git a/pkg/tx_list_validator/tx_list_validator_test.go b/pkg/tx_list_validator/tx_list_validator_test.go index f85b06529..cf7d9a3e9 100644 --- a/pkg/tx_list_validator/tx_list_validator_test.go +++ b/pkg/tx_list_validator/tx_list_validator_test.go @@ -1,14 +1,15 @@ package tx_list_validator import ( + "crypto/rand" "math/big" - "math/rand" "testing" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/stretchr/testify/require" @@ -132,6 +133,8 @@ func rlpEncodedTransactionBytes(l int, signed bool) []byte { func randBytes(l uint64) []byte { b := make([]byte, l) - rand.Read(b) + if _, err := rand.Read(b); err != nil { + log.Crit("Failed to generate random bytes", "error", err) + } return b } diff --git a/proposer/config.go b/proposer/config.go index 7da3c0276..34428ef7c 100644 --- a/proposer/config.go +++ b/proposer/config.go @@ -4,6 +4,7 @@ import ( "crypto/ecdsa" "fmt" "math/big" + "net/url" "strings" "time" @@ -33,9 +34,9 @@ type Config struct { RPCTimeout *time.Duration WaitReceiptTimeout time.Duration ProposeBlockTxGasTipCap *big.Int - ProverEndpoints []string + ProverEndpoints []*url.URL BlockProposalFee *big.Int - BlockProposalFeeIncreasePercentage uint64 + BlockProposalFeeIncreasePercentage *big.Int BlockProposalFeeIterations uint64 } @@ -108,9 +109,14 @@ func NewConfigFromCliContext(c *cli.Context) (*Config, error) { proposeBlockTxGasTipCap = new(big.Int).SetUint64(c.Uint64(flags.ProposeBlockTxGasTipCap.Name)) } - var proverEndpoints []string - - proverEndpoints = append(proverEndpoints, strings.Split(c.String(flags.ProverEndpoints.Name), ",")...) + var proverEndpoints []*url.URL + for _, e := range strings.Split(c.String(flags.ProverEndpoints.Name), ",") { + endpoint, err := url.Parse(e) + if err != nil { + return nil, err + } + proverEndpoints = append(proverEndpoints, endpoint) + } blockProposalFee, ok := new(big.Int).SetString(c.String(flags.BlockProposalFee.Name), 10) if !ok { @@ -138,7 +144,9 @@ func NewConfigFromCliContext(c *cli.Context) (*Config, error) { ProposeBlockTxGasTipCap: proposeBlockTxGasTipCap, ProverEndpoints: proverEndpoints, BlockProposalFee: blockProposalFee, - BlockProposalFeeIncreasePercentage: c.Uint64(flags.BlockProposalFeeIncreasePercentage.Name), - BlockProposalFeeIterations: c.Uint64(flags.BlockProposalFeeIterations.Name), + BlockProposalFeeIncreasePercentage: new(big.Int).SetUint64( + c.Uint64(flags.BlockProposalFeeIncreasePercentage.Name), + ), + BlockProposalFeeIterations: c.Uint64(flags.BlockProposalFeeIterations.Name), }, nil } diff --git a/proposer/config_test.go b/proposer/config_test.go index 9747ef0f4..0555bda14 100644 --- a/proposer/config_test.go +++ b/proposer/config_test.go @@ -50,12 +50,14 @@ func (s *ProposerTestSuite) TestNewConfigFromCliContext() { s.Equal(uint64(5), c.ProposeBlockTxReplacementMultiplier) s.Equal(rpcTimeout, *c.RPCTimeout) s.Equal(10*time.Second, c.WaitReceiptTimeout) - s.Equal(strings.Split(proverEndpoints, ","), c.ProverEndpoints) + for i, e := range strings.Split(proverEndpoints, ",") { + s.Equal(c.ProverEndpoints[i].String(), e) + } fee, _ := new(big.Int).SetString(blockProposalFee, 10) s.Equal(fee, c.BlockProposalFee) - s.Equal(uint64(15), c.BlockProposalFeeIncreasePercentage) + s.Equal(uint64(15), c.BlockProposalFeeIncreasePercentage.Uint64()) s.Equal(uint64(5), c.BlockProposalFeeIterations) s.Nil(new(Proposer).InitFromCli(context.Background(), ctx)) diff --git a/proposer/proposer.go b/proposer/proposer.go index f3b147767..5953f4f5a 100644 --- a/proposer/proposer.go +++ b/proposer/proposer.go @@ -1,16 +1,12 @@ package proposer import ( - "bytes" "context" "crypto/ecdsa" - "encoding/json" "errors" "fmt" - "io" "math/big" "math/rand" - "net/http" "strings" "sync" "time" @@ -27,23 +23,19 @@ import ( "github.com/taikoxyz/taiko-client/bindings/encoding" "github.com/taikoxyz/taiko-client/metrics" "github.com/taikoxyz/taiko-client/pkg/rpc" + selector "github.com/taikoxyz/taiko-client/proposer/prover_selector" "github.com/urfave/cli/v2" "golang.org/x/sync/errgroup" ) var ( errNoNewTxs = errors.New("no new transactions") - errUnableToFindProver = errors.New("unable to find prover") maxSendProposeBlockTxRetry = 10 retryInterval = 12 * time.Second + proverAssignmentTimeout = 90 * time.Minute requestProverServerTimeout = 12 * time.Second ) -type proposeBlockResponse struct { - SignedPayload []byte `json:"signedPayload"` - Prover common.Address `json:"prover"` -} - // Proposer keep proposing new transactions from L2 execution engine's tx pool at a fixed interval. type Proposer struct { // RPC clients @@ -64,15 +56,13 @@ type Proposer struct { proposeBlockTxGasLimit *uint64 txReplacementTipMultiplier uint64 proposeBlockTxGasTipCap *big.Int - proverEndpoints []string + + // Prover selector + proverSelector selector.ProverSelector // Protocol configurations protocolConfigs *bindings.TaikoDataConfig - // Block proposal fee configurations - blockProposalFee *big.Int - blockProposalFeeIncreasePercentage uint64 - // Only for testing purposes CustomProposeOpHook func() error AfterCommitHook func() error @@ -111,9 +101,6 @@ func InitFromConfig(ctx context.Context, p *Proposer, cfg *Config) (err error) { p.proposeBlockTxGasTipCap = cfg.ProposeBlockTxGasTipCap p.ctx = ctx p.waitReceiptTimeout = cfg.WaitReceiptTimeout - p.proverEndpoints = cfg.ProverEndpoints - p.blockProposalFee = cfg.BlockProposalFee - p.blockProposalFeeIncreasePercentage = cfg.BlockProposalFeeIncreasePercentage p.cfg = cfg // RPC clients @@ -138,6 +125,20 @@ func InitFromConfig(ctx context.Context, p *Proposer, cfg *Config) (err error) { log.Info("Protocol configs", "configs", p.protocolConfigs) + if p.proverSelector, err = selector.NewETHFeeEOASelector( + &protocolConfigs, + p.rpc, + cfg.TaikoL1Address, + cfg.BlockProposalFee, + cfg.BlockProposalFeeIncreasePercentage, + cfg.ProverEndpoints, + cfg.BlockProposalFeeIterations, + proverAssignmentTimeout, + requestProverServerTimeout, + ); err != nil { + return err + } + return nil } @@ -304,7 +305,7 @@ func (p *Proposer) ProposeOp(ctx context.Context) error { txNonce := nonce + uint64(i) if err := p.ProposeTxList(ctx, &encoding.TaikoL1BlockMetadataInput{ - Beneficiary: p.l2SuggestedFeeRecipient, + Proposer: p.l2SuggestedFeeRecipient, TxListHash: crypto.Keccak256Hash(txListBytes), TxListByteStart: common.Big0, TxListByteEnd: new(big.Int).SetUint64(uint64(len(txListBytes))), @@ -410,7 +411,7 @@ func (p *Proposer) ProposeTxList( txNum uint, nonce *uint64, ) error { - assignment, fee, err := p.assignProver(ctx, meta) + assignment, fee, err := p.proverSelector.AssignProver(ctx, meta) if err != nil { return err } @@ -469,7 +470,7 @@ func (p *Proposer) ProposeTxList( func (p *Proposer) ProposeEmptyBlockOp(ctx context.Context) error { return p.ProposeTxList(ctx, &encoding.TaikoL1BlockMetadataInput{ TxListHash: crypto.Keccak256Hash([]byte{}), - Beneficiary: p.L2SuggestedFeeRecipient(), + Proposer: p.L2SuggestedFeeRecipient(), TxListByteStart: common.Big0, TxListByteEnd: common.Big0, CacheTxListInfo: false, @@ -494,185 +495,6 @@ func (p *Proposer) updateProposingTicker() { p.proposingTimer = time.NewTimer(duration) } -// assignProver attempts to find a prover who wants to win the block. -// if no provers want to do it for the price we set, we increase the price, and try again. -func (p *Proposer) assignProver( - ctx context.Context, - meta *encoding.TaikoL1BlockMetadataInput, -) ([]byte, *big.Int, error) { - initialFee := p.blockProposalFee - - percentBig := big.NewInt(int64(p.blockProposalFeeIncreasePercentage)) - - // iterate over each configured endpoint, and see if someone wants to accept your block. - // if it is denied, we continue on to the next endpoint. - // if we do not find a prover, we can increase the fee up to a point, or give up. - // TODO(jeff): we loop 3 times, this should be a config var - for i := 0; i < int(p.cfg.BlockProposalFeeIterations); i++ { - fee := new(big.Int).Set(initialFee) - - // increase fee on each failed loop - if i > 0 { - cumulativePercent := new(big.Int).Mul(percentBig, big.NewInt(int64(i))) - increase := new(big.Int).Mul(fee, cumulativePercent) - increase.Div(increase, big.NewInt(100)) - fee.Add(fee, increase) - } - - expiry := uint64(time.Now().Add(90 * time.Minute).Unix()) - - proposeBlockReq := &encoding.ProposeBlockData{ - Expiry: expiry, - Input: *meta, - Fee: fee, - } - - jsonBody, err := json.Marshal(proposeBlockReq) - if err != nil { - return nil, nil, err - } - - r := bytes.NewReader(jsonBody) - - for _, endpoint := range p.proverEndpoints { - log.Info( - "Attempting to assign prover", - "endpoint", endpoint, - "fee", fee.String(), - "expiry", expiry, - ) - client := &http.Client{Timeout: requestProverServerTimeout} - - req, err := http.NewRequestWithContext( - ctx, - "POST", - fmt.Sprintf("%v/%v", endpoint, "proposeBlock"), - r, - ) - if err != nil { - log.Error("Init http request error", "endpoint", endpoint, "err", err) - continue - } - req.Header.Add("Content-Type", "application/json") - - res, err := client.Do(req) - if err != nil { - log.Error("Request prover server error", "endpoint", endpoint, "err", err) - continue - } - - if res.StatusCode != http.StatusOK { - log.Info( - "Prover rejected request to assign block", - "endpoint", endpoint, - "fee", fee.String(), - "expiry", expiry, - ) - continue - } - - resBody, err := io.ReadAll(res.Body) - if err != nil { - log.Error("Read response body error", "endpoint", endpoint, "err", err) - continue - } - - resp := &proposeBlockResponse{} - if err := json.Unmarshal(resBody, resp); err != nil { - log.Error("Unmarshal response body error", "endpoint", endpoint, "err", err) - continue - } - - // ensure prover in response is the same as the one recovered - // from the signature - encodedBlockData, err := encoding.EncodeProposeBlockData(proposeBlockReq) - if err != nil { - log.Error("Encode block data error", "endpoint", endpoint, "error", err) - continue - } - - pubKey, err := crypto.SigToPub(crypto.Keccak256Hash(encodedBlockData).Bytes(), resp.SignedPayload) - if err != nil { - log.Error("Failed to get public key from signature", "endpoint", endpoint, "error", err) - continue - } - - if crypto.PubkeyToAddress(*pubKey).Hex() != resp.Prover.Hex() { - log.Info( - "Assigned prover signature did not recover to provided prover address", - "endpoint", endpoint, - "recoveredAddress", crypto.PubkeyToAddress(*pubKey).Hex(), - "providedProver", resp.Prover.Hex(), - ) - continue - } - - // make sure the prover has the necessary balance either in TaikoL1 token balances - // or, if not, check allowance, as contract will attempt to burn directly after - // if it doesnt have the available tokenbalance in-contract. - taikoTokenBalance, err := p.rpc.TaikoL1.GetTaikoTokenBalance(&bind.CallOpts{Context: ctx}, resp.Prover) - if err != nil { - log.Error( - "Get taiko token balance error", - "endpoint", endpoint, - "providedProver", resp.Prover.Hex(), - "error", err, - ) - continue - } - - if p.protocolConfigs.ProofBond.Cmp(taikoTokenBalance) > 0 { - // check allowance on taikotoken contract - allowance, err := p.rpc.TaikoToken.Allowance(&bind.CallOpts{Context: ctx}, resp.Prover, p.cfg.TaikoL1Address) - if err != nil { - log.Error( - "Get taiko token allowance error", - "endpoint", endpoint, - "providedProver", resp.Prover.Hex(), - "error", err, - ) - continue - } - - if p.protocolConfigs.ProofBond.Cmp(allowance) > 0 { - log.Info( - "Assigned prover does not have required on-chain token balance or allowance", - "endpoint", endpoint, - "providedProver", resp.Prover.Hex(), - "taikoTokenBalance", taikoTokenBalance.String(), - "allowance", allowance.String(), - "proofBond", p.protocolConfigs.ProofBond, - "requiredFee", fee.String(), - ) - continue - } - } - - // convert signature to one solidity can recover by adding 27 to 65th byte - resp.SignedPayload[64] = uint8(uint(resp.SignedPayload[64])) + 27 - - encoded, err := encoding.EncodeProverAssignment(&encoding.ProverAssignment{ - Prover: resp.Prover, - Expiry: proposeBlockReq.Expiry, - Data: resp.SignedPayload, - }) - if err != nil { - return nil, nil, err - } - - log.Info( - "Prover assigned for block", - "prover", resp.Prover.Hex(), - "signedPayload", common.Bytes2Hex(resp.SignedPayload), - ) - - return encoded, fee, nil - } - } - - return nil, nil, errUnableToFindProver -} - // Name returns the application name. func (p *Proposer) Name() string { return "proposer" diff --git a/proposer/proposer_test.go b/proposer/proposer_test.go index 3b162dbbf..e867020e7 100644 --- a/proposer/proposer_test.go +++ b/proposer/proposer_test.go @@ -2,7 +2,6 @@ package proposer import ( "context" - "fmt" "math/big" "os" "testing" @@ -11,12 +10,11 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" "github.com/stretchr/testify/suite" "github.com/taikoxyz/taiko-client/bindings" "github.com/taikoxyz/taiko-client/bindings/encoding" - "github.com/taikoxyz/taiko-client/prover/http" + "github.com/taikoxyz/taiko-client/prover/server" "github.com/taikoxyz/taiko-client/testutils" ) @@ -24,14 +22,12 @@ type ProposerTestSuite struct { testutils.ClientTestSuite p *Proposer cancel context.CancelFunc - srv *http.Server + srv *server.ProverServer } func (s *ProposerTestSuite) SetupTest() { s.ClientTestSuite.SetupTest() - port := testutils.RandomPort() - l1ProposerPrivKey, err := crypto.ToECDSA(common.Hex2Bytes(os.Getenv("L1_PROPOSER_PRIVATE_KEY"))) s.Nil(err) @@ -39,6 +35,7 @@ func (s *ProposerTestSuite) SetupTest() { ctx, cancel := context.WithCancel(context.Background()) proposeInterval := 1024 * time.Hour // No need to periodically propose transactions list in unit tests + s.Nil(InitFromConfig(ctx, p, (&Config{ L1Endpoint: os.Getenv("L1_NODE_WS_ENDPOINT"), L2Endpoint: os.Getenv("L2_EXECUTION_ENGINE_HTTP_ENDPOINT"), @@ -51,9 +48,9 @@ func (s *ProposerTestSuite) SetupTest() { MaxProposedTxListsPerEpoch: 1, ProposeBlockTxReplacementMultiplier: 2, WaitReceiptTimeout: 10 * time.Second, - ProverEndpoints: []string{fmt.Sprintf("http://localhost:%v", port)}, - BlockProposalFee: big.NewInt(100000), - BlockProposalFeeIncreasePercentage: 10, + ProverEndpoints: s.ProverEndpoints, + BlockProposalFee: common.Big256, + BlockProposalFeeIncreasePercentage: common.Big2, BlockProposalFeeIterations: 3, }))) @@ -61,34 +58,14 @@ func (s *ProposerTestSuite) SetupTest() { l1ProverPrivKey, err := crypto.ToECDSA(common.Hex2Bytes(os.Getenv("L1_PROVER_PRIVATE_KEY"))) s.Nil(err) - serverOpts := http.NewServerOpts{ - ProverPrivateKey: l1ProverPrivKey, - MinProofFee: big.NewInt(1), - MaxCapacity: 10, - RequestCurrentCapacityCh: make(chan struct{}), - ReceiveCurrentCapacityCh: make(chan uint64), + serverOpts := &server.NewProverServerOpts{ + ProverPrivateKey: l1ProverPrivKey, + MinProofFee: common.Big1, } - s.srv, err = http.NewServer(serverOpts) + s.srv, err = server.New(serverOpts) s.Nil(err) - go func() { - for { - select { - case <-serverOpts.RequestCurrentCapacityCh: - serverOpts.ReceiveCurrentCapacityCh <- 100 - case <-ctx.Done(): - return - } - } - }() - - go func() { - if err := s.srv.Start(fmt.Sprintf(":%v", port)); err != nil { - log.Crit("error starting prover http server", "error", err) - } - }() - s.p = p s.cancel = cancel } @@ -141,7 +118,7 @@ func (s *ProposerTestSuite) TestProposeOp() { _, isPending, err := s.p.rpc.L1.TransactionByHash(context.Background(), event.Raw.TxHash) s.Nil(err) s.False(isPending) - s.Equal(s.p.l2SuggestedFeeRecipient, event.Meta.Beneficiary) + s.Equal(s.p.l2SuggestedFeeRecipient, event.Meta.Proposer) receipt, err := s.p.rpc.L1.TransactionReceipt(context.Background(), event.Raw.TxHash) s.Nil(err) @@ -200,14 +177,14 @@ func (s *ProposerTestSuite) TestSendProposeBlockTx() { s.Nil(err) meta := &encoding.TaikoL1BlockMetadataInput{ - Beneficiary: s.p.L2SuggestedFeeRecipient(), + Proposer: s.p.L2SuggestedFeeRecipient(), TxListHash: crypto.Keccak256Hash(encoded), TxListByteStart: common.Big0, TxListByteEnd: new(big.Int).SetUint64(uint64(len(encoded))), CacheTxListInfo: false, } - assignment, fee, err := s.p.assignProver(context.Background(), meta) + assignment, fee, err := s.p.proverSelector.AssignProver(context.Background(), meta) s.Nil(err) newTx, err := s.p.sendProposeBlockTx( @@ -223,9 +200,9 @@ func (s *ProposerTestSuite) TestSendProposeBlockTx() { s.Greater(newTx.GasTipCap().Uint64(), tx.GasTipCap().Uint64()) } -func (s *ProposerTestSuite) TestAssignProver_NoProvers() { +func (s *ProposerTestSuite) TestAssignProverSuccessFirstRound() { meta := &encoding.TaikoL1BlockMetadataInput{ - Beneficiary: s.p.L2SuggestedFeeRecipient(), + Proposer: s.p.L2SuggestedFeeRecipient(), TxListHash: testutils.RandomHash(), TxListByteStart: common.Big0, TxListByteEnd: common.Big0, @@ -235,29 +212,10 @@ func (s *ProposerTestSuite) TestAssignProver_NoProvers() { s.SetL1Automine(false) defer s.SetL1Automine(true) - s.p.proverEndpoints = []string{} - - _, _, err := s.p.assignProver(context.Background(), meta) - - s.Equal(err, errUnableToFindProver) -} - -func (s *ProposerTestSuite) TestAssignProver_SuccessFirstRound() { - meta := &encoding.TaikoL1BlockMetadataInput{ - Beneficiary: s.p.L2SuggestedFeeRecipient(), - TxListHash: testutils.RandomHash(), - TxListByteStart: common.Big0, - TxListByteEnd: common.Big0, - CacheTxListInfo: false, - } - - s.SetL1Automine(false) - defer s.SetL1Automine(true) - - _, fee, err := s.p.assignProver(context.Background(), meta) + _, fee, err := s.p.proverSelector.AssignProver(context.Background(), meta) s.Nil(err) - s.Equal(fee.Uint64(), s.p.blockProposalFee.Uint64()) + s.Equal(fee.Uint64(), s.p.cfg.BlockProposalFee.Uint64()) } func (s *ProposerTestSuite) TestUpdateProposingTicker() { @@ -275,22 +233,6 @@ func (s *ProposerTestSuite) TestStartClose() { s.NotPanics(func() { s.p.Close(context.Background()) }) } -// TODO: not working -// func (s *ProposerTestSuite) TestEventLoopEmptyBlock() { -// fiveSecs := 5 * time.Second -// s.p.proposingInterval = &fiveSecs -// s.p.proposeEmptyBlocksInterval = &fiveSecs -// s.p.Start() -// time.Sleep(30 * time.Second) -// s.cancel() -// s.p.Close() -// // check if empty blocks have been proposed? query TaikoL1 contract? -// block, err := s.p.rpc.L2.BlockByNumber(context.Background(), nil) -// s.Nil(err) -// s.Equal(uint64(block.GasLimit()), uint64(21000)) -// s.Equal(block.TxHash(), common.Hash(crypto.Keccak256Hash([]byte{}))) -// } - func TestProposerTestSuite(t *testing.T) { suite.Run(t, new(ProposerTestSuite)) } diff --git a/proposer/prover_selector/eth_fee_eoa_selector.go b/proposer/prover_selector/eth_fee_eoa_selector.go new file mode 100644 index 000000000..c2e9dea7d --- /dev/null +++ b/proposer/prover_selector/eth_fee_eoa_selector.go @@ -0,0 +1,232 @@ +package selector + +import ( + "context" + "errors" + "fmt" + "math/big" + "net/url" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" + "github.com/go-resty/resty/v2" + "github.com/taikoxyz/taiko-client/bindings" + "github.com/taikoxyz/taiko-client/bindings/encoding" + "github.com/taikoxyz/taiko-client/pkg/rpc" + "github.com/taikoxyz/taiko-client/prover/server" +) + +var ( + httpScheme = "http" + errEmptyProverEndpoints = errors.New("empty prover endpoints") + errUnableToFindProver = errors.New("unable to find prover") +) + +// ETHFeeEOASelector is a prover selector implementation which use ETHs as prover fee and +// all provers selected must be EOA accounts. +type ETHFeeEOASelector struct { + protocolConfigs *bindings.TaikoDataConfig + rpc *rpc.Client + taikoL1Address common.Address + feeBase *big.Int + feeIncreasePercentage *big.Int + proverEndpoints []*url.URL + proposalFeeIterations uint64 + proposalExpiry time.Duration + requestTimeout time.Duration +} + +// NewETHFeeEOASelector creates a new ETHFeeEOASelector instance. +func NewETHFeeEOASelector( + protocolConfigs *bindings.TaikoDataConfig, + rpc *rpc.Client, + taikoL1Address common.Address, + feeBase *big.Int, + feeIncreasePercentage *big.Int, + proverEndpoints []*url.URL, + proposalFeeIterations uint64, + proposalExpiry time.Duration, + requestTimeout time.Duration, +) (*ETHFeeEOASelector, error) { + if len(proverEndpoints) == 0 { + return nil, errEmptyProverEndpoints + } + + for _, endpoint := range proverEndpoints { + if endpoint.Scheme != httpScheme { + return nil, fmt.Errorf("invalid prover endpoint %s", endpoint) + } + } + + return ÐFeeEOASelector{ + protocolConfigs, + rpc, + taikoL1Address, + feeBase, + feeIncreasePercentage, + proverEndpoints, + proposalFeeIterations, + proposalExpiry, + requestTimeout, + }, nil +} + +// ProverEndpoints returns all registered prover endpoints. +func (s *ETHFeeEOASelector) ProverEndpoints() []*url.URL { return s.proverEndpoints } + +// AssignProver tries to pick a prover through the registered prover endpoints. +func (s *ETHFeeEOASelector) AssignProver( + ctx context.Context, + meta *encoding.TaikoL1BlockMetadataInput, +) ([]byte, *big.Int, error) { + // Iterate over each configured endpoint, and see if someone wants to accept this block. + // If it is denied, we continue on to the next endpoint. + // If we do not find a prover, we can increase the fee up to a point, or give up. + for i := 0; i < int(s.proposalFeeIterations); i++ { + var ( + fee = s.feeBase + expiry = uint64(time.Now().Add(s.proposalExpiry).Unix()) + ) + + // Increase fee on each failed loop + if i > 0 { + cumulativePercent := new(big.Int).Mul(s.feeIncreasePercentage, big.NewInt(int64(i))) + increase := new(big.Int).Mul(fee, cumulativePercent) + increase.Div(increase, big.NewInt(100)) + fee.Add(fee, increase) + } + for _, endpoint := range s.proverEndpoints { + encodedAssignment, proverAddress, err := assignProver(ctx, meta, endpoint, fee, expiry, s.requestTimeout) + if err != nil { + log.Warn("Failed to assign prover", "endpoint", endpoint, "error", err) + continue + } + + ok, err := s.checkProverBalance(ctx, proverAddress) + if err != nil { + log.Warn("Failed to check prover balance", "endpoint", endpoint, "error", err) + continue + } + if !ok { + continue + } + + return encodedAssignment, fee, nil + } + } + + return nil, nil, errUnableToFindProver +} + +// checkProverBalance checks if the prover has the necessary balance either in TaikoL1 token balances +// or, if not, then check allowance, as contract will attempt to burn directly after +// if it doesnt have the available token balance in-contract. +func (s *ETHFeeEOASelector) checkProverBalance(ctx context.Context, prover common.Address) (bool, error) { + taikoTokenBalance, err := s.rpc.TaikoL1.GetTaikoTokenBalance(&bind.CallOpts{Context: ctx}, prover) + if err != nil { + return false, err + } + + if s.protocolConfigs.ProofBond.Cmp(taikoTokenBalance) > 0 { + // Check allowance on taiko token contract + allowance, err := s.rpc.TaikoToken.Allowance(&bind.CallOpts{Context: ctx}, prover, s.taikoL1Address) + if err != nil { + return false, err + } + + if s.protocolConfigs.ProofBond.Cmp(allowance) > 0 { + log.Info( + "Assigned prover does not have required on-chain token balance or allowance", + "providedProver", prover.Hex(), + "taikoTokenBalance", taikoTokenBalance.String(), + "allowance", allowance.String(), + "proofBond", s.protocolConfigs.ProofBond, + ) + return false, nil + } + } + + return true, nil +} + +// assignProver tries to assign a proof generation task to the given prover by HTTP API. +func assignProver( + ctx context.Context, + meta *encoding.TaikoL1BlockMetadataInput, + endpoint *url.URL, + fee *big.Int, + expiry uint64, + timeout time.Duration, +) ([]byte, common.Address, error) { + log.Info( + "Attempting to assign prover", + "endpoint", endpoint, + "fee", fee.String(), + "expiry", expiry, + ) + + // Send the HTTP request + var ( + client = resty.New() + reqBody = &encoding.ProposeBlockData{Expiry: expiry, Input: *meta, Fee: fee} + result = server.ProposeBlockResponse{} + ) + requestUrl, err := url.JoinPath(endpoint.String(), "/proposeBlock") + if err != nil { + return nil, common.Address{}, err + } + + ctxTimeout, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + resp, err := client.R(). + SetContext(ctxTimeout). + SetHeader("Content-Type", "application/json"). + SetHeader("Accept", "application/json"). + SetBody(reqBody). + SetResult(&result). + Post(requestUrl) + if err != nil { + return nil, common.Address{}, err + } + if !resp.IsSuccess() { + return nil, common.Address{}, fmt.Errorf("unsuccessful response %d", resp.StatusCode()) + } + + // Ensure prover in response is the same as the one recovered + // from the signature + encodedBlockData, err := encoding.EncodeProposeBlockData(reqBody) + if err != nil { + return nil, common.Address{}, err + } + + pubKey, err := crypto.SigToPub(crypto.Keccak256Hash(encodedBlockData).Bytes(), result.SignedPayload) + if err != nil { + return nil, common.Address{}, err + } + + if crypto.PubkeyToAddress(*pubKey).Hex() != result.Prover.Hex() { + return nil, common.Address{}, fmt.Errorf( + "assigned prover signature did not recover to provided prover address %s != %s", + crypto.PubkeyToAddress(*pubKey).Hex(), + result.Prover.Hex(), + ) + } + + // Convert signature to one solidity can recover by adding 27 to 65th byte + result.SignedPayload[64] = uint8(uint(result.SignedPayload[64])) + 27 + + encoded, err := encoding.EncodeProverAssignment(&encoding.ProverAssignment{ + Prover: result.Prover, + Expiry: reqBody.Expiry, + Data: result.SignedPayload, + }) + if err != nil { + return nil, common.Address{}, err + } + + return encoded, result.Prover, nil +} diff --git a/proposer/prover_selector/eth_fee_eoa_selector_test.go b/proposer/prover_selector/eth_fee_eoa_selector_test.go new file mode 100644 index 000000000..634aebb96 --- /dev/null +++ b/proposer/prover_selector/eth_fee_eoa_selector_test.go @@ -0,0 +1,54 @@ +package selector + +import ( + "context" + "net/url" + "os" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/suite" + "github.com/taikoxyz/taiko-client/testutils" +) + +type ProverSelectorTestSuite struct { + testutils.ClientTestSuite + s *ETHFeeEOASelector + proverAddress common.Address +} + +func (s *ProverSelectorTestSuite) SetupTest() { + s.ClientTestSuite.SetupTest() + + l1ProverPrivKey, err := crypto.ToECDSA(common.Hex2Bytes(os.Getenv("L1_PROVER_PRIVATE_KEY"))) + s.Nil(err) + s.proverAddress = crypto.PubkeyToAddress(l1ProverPrivKey.PublicKey) + + protocolConfigs, err := s.RpcClient.TaikoL1.GetConfig(nil) + s.Nil(err) + + s.s, err = NewETHFeeEOASelector( + &protocolConfigs, + s.RpcClient, + common.HexToAddress(os.Getenv("TAIKO_L1_ADDRESS")), + common.Big256, + common.Big2, + []*url.URL{s.ProverEndpoints[0]}, + 32, + 1*time.Minute, + 1*time.Minute, + ) + s.Nil(err) +} + +func (s *ProverSelectorTestSuite) TestCheckProverBalance() { + ok, err := s.s.checkProverBalance(context.Background(), s.proverAddress) + s.Nil(err) + s.True(ok) +} + +func TestProverSelectorTestSuite(t *testing.T) { + suite.Run(t, new(ProverSelectorTestSuite)) +} diff --git a/proposer/prover_selector/interface.go b/proposer/prover_selector/interface.go new file mode 100644 index 000000000..53ab9df14 --- /dev/null +++ b/proposer/prover_selector/interface.go @@ -0,0 +1,17 @@ +package selector + +import ( + "context" + "math/big" + "net/url" + + "github.com/taikoxyz/taiko-client/bindings/encoding" +) + +type ProverSelector interface { + AssignProver( + ctx context.Context, + meta *encoding.TaikoL1BlockMetadataInput, + ) (signedPayload []byte, fee *big.Int, err error) + ProverEndpoints() []*url.URL +} diff --git a/prover/capacity_manager/capacity_manager.go b/prover/capacity_manager/capacity_manager.go new file mode 100644 index 000000000..27355c4fd --- /dev/null +++ b/prover/capacity_manager/capacity_manager.go @@ -0,0 +1,46 @@ +package capacity_manager + +import ( + "sync" +) + +// CapacityManager manages the prover capacity concurrent-safely. +type CapacityManager struct { + capacity uint64 + mutex sync.RWMutex +} + +// New creates a new CapacityManager instance. +func New(capacity uint64) *CapacityManager { + return &CapacityManager{capacity: capacity} +} + +// ReadCapacity reads the current capacity. +func (m *CapacityManager) ReadCapacity() uint64 { + m.mutex.RLock() + defer m.mutex.RUnlock() + + return m.capacity +} + +// ReleaseCapacity releases one capacity. +func (m *CapacityManager) ReleaseOneCapacity() uint64 { + m.mutex.Lock() + defer m.mutex.Unlock() + + m.capacity += 1 + return m.capacity +} + +// TakeOneCapacity takes one capacitĀ·y. +func (m *CapacityManager) TakeOneCapacity() (uint64, bool) { + m.mutex.Lock() + defer m.mutex.Unlock() + + if m.capacity == 0 { + return 0, false + } + + m.capacity -= 1 + return m.capacity, true +} diff --git a/prover/capacity_manager/capacity_manager_test.go b/prover/capacity_manager/capacity_manager_test.go new file mode 100644 index 000000000..1c1213e50 --- /dev/null +++ b/prover/capacity_manager/capacity_manager_test.go @@ -0,0 +1,34 @@ +package capacity_manager + +import ( + "github.com/stretchr/testify/suite" +) + +var ( + testCapacity uint64 = 1024 +) + +type CapacityManagerTestSuite struct { + suite.Suite + m *CapacityManager +} + +func (s *CapacityManagerTestSuite) SetupTest() { + s.m = New(testCapacity) +} + +func (s *CapacityManagerTestSuite) TestReadCapacity() { + s.Equal(testCapacity, s.m.ReadCapacity()) +} + +func (s *CapacityManagerTestSuite) TestReleaseOneCapacity() { + s.Equal(testCapacity+1, s.m.ReleaseOneCapacity()) + s.Equal(testCapacity+1, s.m.ReadCapacity()) +} + +func (s *CapacityManagerTestSuite) TestTakeOneCapacity() { + capacity, ok := s.m.TakeOneCapacity() + s.True(ok) + s.Equal(testCapacity-1, capacity) + s.Equal(testCapacity-1, s.m.ReadCapacity()) +} diff --git a/prover/config_test.go b/prover/config_test.go index 31bad348e..a857e942a 100644 --- a/prover/config_test.go +++ b/prover/config_test.go @@ -6,7 +6,6 @@ import ( "time" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" "github.com/taikoxyz/taiko-client/cmd/flags" "github.com/urfave/cli/v2" ) @@ -22,11 +21,9 @@ var ( minProofFee = "1024" ) -// TODO: fix this test func (s *ProverTestSuite) TestNewConfigFromCliContext_OracleProver() { app := s.SetupApp() app.Action = func(ctx *cli.Context) error { - log.Info("ctx", "ctx", ctx.FlagNames(), "v", ctx.Args()) c, err := NewConfigFromCliContext(ctx) s.Nil(err) s.Equal(l1WsEndpoint, c.L1WsEndpoint) diff --git a/prover/http/propose_block.go b/prover/http/propose_block.go deleted file mode 100644 index f1adad314..000000000 --- a/prover/http/propose_block.go +++ /dev/null @@ -1,68 +0,0 @@ -package http - -import ( - "context" - "net/http" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/labstack/echo/v4" - "github.com/labstack/gommon/log" - "github.com/taikoxyz/taiko-client/bindings/encoding" -) - -type proposeBlockResp struct { - SignedPayload []byte `json:"signedPayload"` - Prover common.Address `json:"prover"` -} - -// ProposeBlock handles a propose block request, decides if this prover wants to -// handle this block, and if so, returns a signed payload the proposer -// can submit onchain. -func (srv *Server) ProposeBlock(c echo.Context) error { - r := &encoding.ProposeBlockData{} - if err := c.Bind(r); err != nil { - return c.JSON(http.StatusUnprocessableEntity, err) - } - - if r.Fee.Cmp(srv.minProofFee) < 0 { - return echo.NewHTTPError(http.StatusUnprocessableEntity, "proof fee too low") - } - - srv.requestCurrentCapacityCh <- struct{}{} - - ctx, cancel := context.WithTimeout(c.Request().Context(), 8*time.Second) - defer cancel() - - for { - select { - case capacity := <-srv.receiveCurrentCapacityCh: - if capacity == 0 { - return echo.NewHTTPError(http.StatusUnprocessableEntity, "prover does not have capacity") - } - - encoded, err := encoding.EncodeProposeBlockData(r) - if err != nil { - return echo.NewHTTPError(http.StatusUnprocessableEntity, err) - } - - hashed := crypto.Keccak256Hash(encoded) - - signed, err := crypto.Sign(hashed.Bytes(), srv.proverPrivateKey) - if err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, err) - } - - resp := &proposeBlockResp{ - SignedPayload: signed, - Prover: srv.proverAddress, - } - - return c.JSON(http.StatusOK, resp) - case <-ctx.Done(): - log.Info("timed out trying to get capacity") - return echo.NewHTTPError(http.StatusUnprocessableEntity, "timed out trying to get capacity") - } - } -} diff --git a/prover/http/propose_block_test.go b/prover/http/propose_block_test.go deleted file mode 100644 index 5acd7a4a2..000000000 --- a/prover/http/propose_block_test.go +++ /dev/null @@ -1,92 +0,0 @@ -package http - -import ( - "crypto/rand" - "math/big" - "net/http" - "net/http/httptest" - "testing" - "time" - - "github.com/cyberhorsey/webutils/testutils" - "github.com/ethereum/go-ethereum/common" - "github.com/labstack/echo/v4" - "github.com/taikoxyz/taiko-client/bindings/encoding" -) - -// randomHash generates a random blob of data and returns it as a hash. -func randomHash() common.Hash { - var hash common.Hash - if n, err := rand.Read(hash[:]); n != common.HashLength || err != nil { - panic(err) - } - return hash -} -func Test_ProposeBlock(t *testing.T) { - srv := newTestServer("") - - tests := []struct { - name string - req *encoding.ProposeBlockData - chResponseFunc func() - wantStatus int - wantBodyRegexpMatches []string - }{ - { - "success", - &encoding.ProposeBlockData{ - Fee: big.NewInt(1000), - Expiry: uint64(time.Now().Unix()), - Input: encoding.TaikoL1BlockMetadataInput{ - Beneficiary: common.BytesToAddress(randomHash().Bytes()), - TxListHash: randomHash(), - TxListByteStart: common.Big0, - TxListByteEnd: common.Big0, - CacheTxListInfo: false, - }, - }, - func() { - srv.receiveCurrentCapacityCh <- 100 - }, - http.StatusOK, - []string{`"signedPayload"`}, - }, - { - "contextTimeout", - &encoding.ProposeBlockData{ - Fee: big.NewInt(1000), - Expiry: uint64(time.Now().Unix()), - Input: encoding.TaikoL1BlockMetadataInput{ - Beneficiary: common.BytesToAddress(randomHash().Bytes()), - TxListHash: randomHash(), - TxListByteStart: common.Big0, - TxListByteEnd: common.Big0, - CacheTxListInfo: false, - }, - }, - nil, - http.StatusUnprocessableEntity, - []string{`{"message":"timed out trying to get capacity"}`}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if tt.chResponseFunc != nil { - go tt.chResponseFunc() - } - - req := testutils.NewUnauthenticatedRequest( - echo.POST, - "/proposeBlock", - tt.req, - ) - - rec := httptest.NewRecorder() - - srv.ServeHTTP(rec, req) - - testutils.AssertStatusAndBody(t, rec, tt.wantStatus, tt.wantBodyRegexpMatches) - }) - } -} diff --git a/prover/http/routes.go b/prover/http/routes.go deleted file mode 100644 index c7195ac42..000000000 --- a/prover/http/routes.go +++ /dev/null @@ -1,8 +0,0 @@ -package http - -func (srv *Server) configureRoutes() { - srv.echo.GET("/healthz", srv.Health) - srv.echo.GET("/", srv.Health) - - srv.echo.POST("/proposeBlock", srv.ProposeBlock) -} diff --git a/prover/http/server.go b/prover/http/server.go deleted file mode 100644 index 0afb3acaf..000000000 --- a/prover/http/server.go +++ /dev/null @@ -1,97 +0,0 @@ -package http - -import ( - "context" - "crypto/ecdsa" - "math/big" - "net/http" - "os" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/labstack/echo/v4/middleware" - - echo "github.com/labstack/echo/v4" -) - -type Server struct { - echo *echo.Echo - proverPrivateKey *ecdsa.PrivateKey - proverAddress common.Address - - // capacity related configs - maxCapacity uint64 - requestCurrentCapacityCh chan struct{} - receiveCurrentCapacityCh chan uint64 - minProofFee *big.Int -} - -type NewServerOpts struct { - ProverPrivateKey *ecdsa.PrivateKey - MaxCapacity uint64 - MinProofFee *big.Int - RequestCurrentCapacityCh chan struct{} - ReceiveCurrentCapacityCh chan uint64 -} - -func NewServer(opts NewServerOpts) (*Server, error) { - address := crypto.PubkeyToAddress(opts.ProverPrivateKey.PublicKey) - srv := &Server{ - proverPrivateKey: opts.ProverPrivateKey, - proverAddress: address, - echo: echo.New(), - maxCapacity: opts.MaxCapacity, - minProofFee: opts.MinProofFee, - requestCurrentCapacityCh: opts.RequestCurrentCapacityCh, - receiveCurrentCapacityCh: opts.ReceiveCurrentCapacityCh, - } - - srv.configureMiddleware() - srv.configureRoutes() - - return srv, nil -} - -// Start starts the HTTP server -func (srv *Server) Start(address string) error { - return srv.echo.Start(address) -} - -// Shutdown shuts down the HTTP server -func (srv *Server) Shutdown(ctx context.Context) error { - return srv.echo.Shutdown(ctx) -} - -// ServeHTTP implements the `http.Handler` interface which serves HTTP requests -func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { - srv.echo.ServeHTTP(w, r) -} - -// Health endpoints for probes -func (srv *Server) Health(c echo.Context) error { - return c.NoContent(http.StatusOK) -} - -func LogSkipper(c echo.Context) bool { - switch c.Request().URL.Path { - case "/healthz": - return true - case "/metrics": - return true - default: - return true - } -} - -func (srv *Server) configureMiddleware() { - srv.echo.Use(middleware.RequestID()) - - srv.echo.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{ - Skipper: LogSkipper, - Format: `{"time":"${time_rfc3339_nano}","level":"INFO","message":{"id":"${id}","remote_ip":"${remote_ip}",` + //nolint:lll - `"host":"${host}","method":"${method}","uri":"${uri}","user_agent":"${user_agent}",` + //nolint:lll - `"response_status":${status},"error":"${error}","latency":${latency},"latency_human":"${latency_human}",` + - `"bytes_in":${bytes_in},"bytes_out":${bytes_out}}}` + "\n", - Output: os.Stdout, - })) -} diff --git a/prover/http/server_test.go b/prover/http/server_test.go deleted file mode 100644 index f89c266b5..000000000 --- a/prover/http/server_test.go +++ /dev/null @@ -1,68 +0,0 @@ -package http - -import ( - "context" - "math/big" - "net/http" - "net/http/httptest" - "os" - "testing" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - echo "github.com/labstack/echo/v4" - "github.com/stretchr/testify/assert" -) - -func newTestServer(url string) *Server { - l1ProverPrivKey, _ := crypto.ToECDSA(common.Hex2Bytes(os.Getenv("L1_PROVER_PRIVATE_KEY"))) - - srv := &Server{ - echo: echo.New(), - proverPrivateKey: l1ProverPrivKey, - maxCapacity: 8, - minProofFee: big.NewInt(1), - requestCurrentCapacityCh: make(chan struct{}, 1024), - receiveCurrentCapacityCh: make(chan uint64, 1024), - } - - srv.configureMiddleware() - srv.configureRoutes() - - return srv -} - -func Test_Health(t *testing.T) { - srv := newTestServer("") - - req, _ := http.NewRequest(echo.GET, "/healthz", nil) - rec := httptest.NewRecorder() - - srv.ServeHTTP(rec, req) - - if rec.Code != http.StatusOK { - t.Fatalf("Test_Health expected code %v, got %v", http.StatusOK, rec.Code) - } -} - -func Test_Root(t *testing.T) { - srv := newTestServer("") - - req, _ := http.NewRequest(echo.GET, "/", nil) - rec := httptest.NewRecorder() - - srv.ServeHTTP(rec, req) - - if rec.Code != http.StatusOK { - t.Fatalf("Test_Root expected code %v, got %v", http.StatusOK, rec.Code) - } -} - -func Test_StartShutdown(t *testing.T) { - srv := newTestServer("") - - go func() { - _ = srv.Start(":3928") - }() - assert.Nil(t, srv.Shutdown(context.Background())) -} diff --git a/prover/proof_producer/dummy_producer.go b/prover/proof_producer/dummy_producer.go index 50235e98c..507c6ce3d 100644 --- a/prover/proof_producer/dummy_producer.go +++ b/prover/proof_producer/dummy_producer.go @@ -30,7 +30,7 @@ func (d *DummyProofProducer) RequestProof( log.Info( "Request dummy proof", "blockID", blockID, - "beneficiary", meta.Beneficiary, + "proposer", meta.Proposer, "height", header.Number, "hash", header.Hash(), ) diff --git a/prover/proof_producer/zkevm_cmd_producer.go b/prover/proof_producer/zkevm_cmd_producer.go index a49b08603..12478e7c0 100644 --- a/prover/proof_producer/zkevm_cmd_producer.go +++ b/prover/proof_producer/zkevm_cmd_producer.go @@ -45,7 +45,7 @@ func (p *ZkevmCmdProducer) RequestProof( log.Info( "Request proof from ZKEVM CMD", "blockID", blockID, - "beneficiary", meta.Beneficiary, + "proposer", meta.Proposer, "height", header.Number, "hash", header.Hash(), "cmd", p.CmdPath, diff --git a/prover/proof_producer/zkevm_rpcd_producer.go b/prover/proof_producer/zkevm_rpcd_producer.go index 4a9b7aaac..2663e0edd 100644 --- a/prover/proof_producer/zkevm_rpcd_producer.go +++ b/prover/proof_producer/zkevm_rpcd_producer.go @@ -131,7 +131,7 @@ func (p *ZkevmRpcdProducer) RequestProof( log.Info( "Request proof from zkevm-chain proverd service", "blockID", blockID, - "beneficiary", meta.Beneficiary, + "proposer", meta.Proposer, "height", header.Number, "hash", header.Hash(), ) diff --git a/prover/proof_submitter/valid_proof_submitter.go b/prover/proof_submitter/valid_proof_submitter.go index 5fe072224..9416dd7bc 100644 --- a/prover/proof_submitter/valid_proof_submitter.go +++ b/prover/proof_submitter/valid_proof_submitter.go @@ -171,7 +171,7 @@ func (s *ValidProofSubmitter) SubmitProof( log.Info( "New valid block proof", "blockID", proofWithHeader.BlockID, - "beneficiary", proofWithHeader.Meta.Beneficiary, + "proposer", proofWithHeader.Meta.Proposer, "hash", proofWithHeader.Header.Hash(), "proof", common.Bytes2Hex(proofWithHeader.ZkProof), "graffiti", common.Bytes2Hex(s.graffiti[:]), diff --git a/prover/proof_submitter/valid_proof_submitter_test.go b/prover/proof_submitter/valid_proof_submitter_test.go index 0cd36ada1..9d749c67f 100644 --- a/prover/proof_submitter/valid_proof_submitter_test.go +++ b/prover/proof_submitter/valid_proof_submitter_test.go @@ -3,7 +3,6 @@ package submitter import ( "bytes" "context" - "fmt" "math/big" "os" "sync" @@ -19,7 +18,6 @@ import ( "github.com/taikoxyz/taiko-client/driver/chain_syncer/calldata" "github.com/taikoxyz/taiko-client/driver/state" "github.com/taikoxyz/taiko-client/proposer" - "github.com/taikoxyz/taiko-client/prover/http" proofProducer "github.com/taikoxyz/taiko-client/prover/proof_producer" "github.com/taikoxyz/taiko-client/testutils" ) @@ -31,15 +29,11 @@ type ProofSubmitterTestSuite struct { proposer *proposer.Proposer validProofCh chan *proofProducer.ProofWithHeader invalidProofCh chan *proofProducer.ProofWithHeader - srv *http.Server - cancel context.CancelFunc } func (s *ProofSubmitterTestSuite) SetupTest() { s.ClientTestSuite.SetupTest() - port := testutils.RandomPort() - l1ProverPrivKey, err := crypto.ToECDSA(common.Hex2Bytes(os.Getenv("L1_PROVER_PRIVATE_KEY"))) s.Nil(err) @@ -82,32 +76,28 @@ func (s *ProofSubmitterTestSuite) SetupTest() { l1ProposerPrivKey, err := crypto.ToECDSA(common.Hex2Bytes(os.Getenv("L1_PROPOSER_PRIVATE_KEY"))) s.Nil(err) proposeInterval := 1024 * time.Hour // No need to periodically propose transactions list in unit tests + s.Nil(proposer.InitFromConfig(context.Background(), prop, (&proposer.Config{ - L1Endpoint: os.Getenv("L1_NODE_WS_ENDPOINT"), - L2Endpoint: os.Getenv("L2_EXECUTION_ENGINE_WS_ENDPOINT"), - TaikoL1Address: common.HexToAddress(os.Getenv("TAIKO_L1_ADDRESS")), - TaikoL2Address: common.HexToAddress(os.Getenv("TAIKO_L2_ADDRESS")), - TaikoTokenAddress: common.HexToAddress(os.Getenv("TAIKO_TOKEN_ADDRESS")), - L1ProposerPrivKey: l1ProposerPrivKey, - L2SuggestedFeeRecipient: common.HexToAddress(os.Getenv("L2_SUGGESTED_FEE_RECIPIENT")), - ProposeInterval: &proposeInterval, // No need to periodically propose transactions list in unit tests - MaxProposedTxListsPerEpoch: 1, - WaitReceiptTimeout: 10 * time.Second, - ProverEndpoints: []string{fmt.Sprintf("http://localhost:%v", port)}, - BlockProposalFee: big.NewInt(1000), - BlockProposalFeeIterations: 3, + L1Endpoint: os.Getenv("L1_NODE_WS_ENDPOINT"), + L2Endpoint: os.Getenv("L2_EXECUTION_ENGINE_WS_ENDPOINT"), + TaikoL1Address: common.HexToAddress(os.Getenv("TAIKO_L1_ADDRESS")), + TaikoL2Address: common.HexToAddress(os.Getenv("TAIKO_L2_ADDRESS")), + TaikoTokenAddress: common.HexToAddress(os.Getenv("TAIKO_TOKEN_ADDRESS")), + L1ProposerPrivKey: l1ProposerPrivKey, + L2SuggestedFeeRecipient: common.HexToAddress(os.Getenv("L2_SUGGESTED_FEE_RECIPIENT")), + ProposeInterval: &proposeInterval, + MaxProposedTxListsPerEpoch: 1, + WaitReceiptTimeout: 10 * time.Second, + ProverEndpoints: s.ProverEndpoints, + BlockProposalFee: big.NewInt(1000), + BlockProposalFeeIterations: 3, + BlockProposalFeeIncreasePercentage: common.Big2, }))) - srv, cancel, err := testutils.HTTPServer(&s.ClientTestSuite, port) - s.Nil(err) - - s.srv = srv - s.cancel = cancel s.proposer = prop } func (s *ProofSubmitterTestSuite) TestValidProofSubmitterRequestProofDeadlineExceeded() { - defer s.cancel() ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() @@ -118,7 +108,6 @@ func (s *ProofSubmitterTestSuite) TestValidProofSubmitterRequestProofDeadlineExc } func (s *ProofSubmitterTestSuite) TestValidProofSubmitterSubmitProofMetadataNotFound() { - defer s.cancel() s.Error( s.validProofSubmitter.SubmitProof( context.Background(), &proofProducer.ProofWithHeader{ @@ -132,7 +121,6 @@ func (s *ProofSubmitterTestSuite) TestValidProofSubmitterSubmitProofMetadataNotF } func (s *ProofSubmitterTestSuite) TestValidSubmitProofs() { - defer s.cancel() events := testutils.ProposeAndInsertEmptyBlocks(&s.ClientTestSuite, s.proposer, s.calldataSyncer) for _, e := range events { @@ -143,7 +131,6 @@ func (s *ProofSubmitterTestSuite) TestValidSubmitProofs() { } func (s *ProofSubmitterTestSuite) TestValidProofSubmitterRequestProofCancelled() { - defer s.cancel() ctx, cancel := context.WithCancel(context.Background()) go func() { time.AfterFunc(2*time.Second, func() { diff --git a/prover/prover.go b/prover/prover.go index 9965eb597..8f2174db9 100644 --- a/prover/prover.go +++ b/prover/prover.go @@ -5,7 +5,7 @@ import ( "errors" "fmt" "math/big" - netHttp "net/http" + "net/http" "strings" "sync" "time" @@ -23,15 +23,15 @@ import ( "github.com/taikoxyz/taiko-client/metrics" eventIterator "github.com/taikoxyz/taiko-client/pkg/chain_iterator/event_iterator" "github.com/taikoxyz/taiko-client/pkg/rpc" - "github.com/taikoxyz/taiko-client/prover/http" + capacity "github.com/taikoxyz/taiko-client/prover/capacity_manager" proofProducer "github.com/taikoxyz/taiko-client/prover/proof_producer" proofSubmitter "github.com/taikoxyz/taiko-client/prover/proof_submitter" + "github.com/taikoxyz/taiko-client/prover/server" "github.com/urfave/cli/v2" ) var ( errNoCapacity = errors.New("no prover capacity available") - zeroAddress = common.HexToAddress("0x0000000000000000000000000000000000000000") ) type cancelFunc func() @@ -46,8 +46,8 @@ type Prover struct { // Clients rpc *rpc.Client - // HTTP Server - srv *http.Server + // Prover Server + srv *server.ProverServer // Contract configurations protocolConfigs *bindings.TaikoDataConfig @@ -88,10 +88,7 @@ type Prover struct { checkProofWindowExpiredInterval time.Duration // capacity-related configs - maxCapacity uint64 - currentCapacity uint64 - requestCurrentCapacityCh chan struct{} - receiveCurrentCapacityCh chan uint64 + capacityManager *capacity.CapacityManager ctx context.Context wg sync.WaitGroup @@ -112,26 +109,10 @@ func InitFromConfig(ctx context.Context, p *Prover, cfg *Config) (err error) { p.cfg = cfg p.ctx = ctx p.currentBlocksBeingProven = make(map[uint64]cancelFunc) - p.currentBlocksBeingProvenMutex = &sync.Mutex{} + p.currentBlocksBeingProvenMutex = new(sync.Mutex) p.currentBlocksWaitingForProofWindow = make(map[uint64]uint64, 0) - p.currentBlocksWaitingForProofWindowMutex = &sync.Mutex{} - p.maxCapacity = cfg.Capacity - p.currentCapacity = cfg.Capacity - p.requestCurrentCapacityCh = make(chan struct{}, 1024) - p.receiveCurrentCapacityCh = make(chan uint64, 1024) - - if !p.cfg.OracleProver { - p.srv, err = http.NewServer(http.NewServerOpts{ - ProverPrivateKey: p.cfg.L1ProverPrivKey, - MaxCapacity: p.cfg.Capacity, - MinProofFee: p.cfg.MinProofFee, - RequestCurrentCapacityCh: p.requestCurrentCapacityCh, - ReceiveCurrentCapacityCh: p.receiveCurrentCapacityCh, - }) - if err != nil { - return err - } - } + p.currentBlocksWaitingForProofWindowMutex = new(sync.Mutex) + p.capacityManager = capacity.New(cfg.Capacity) // Clients if p.rpc, err = rpc.NewClient(p.ctx, &rpc.ClientConfig{ @@ -146,6 +127,17 @@ func InitFromConfig(ctx context.Context, p *Prover, cfg *Config) (err error) { return err } + // Prover server + if !p.cfg.OracleProver { + if p.srv, err = server.New(&server.NewProverServerOpts{ + ProverPrivateKey: p.cfg.L1ProverPrivKey, + MinProofFee: p.cfg.MinProofFee, + CapacityManager: p.capacityManager, + }); err != nil { + return err + } + } + // Configs protocolConfigs, err := p.rpc.TaikoL1.GetConfig(&bind.CallOpts{Context: ctx}) if err != nil { @@ -187,7 +179,6 @@ func InitFromConfig(ctx context.Context, p *Prover, cfg *Config) (err error) { p.oracleProverAddress = oracleProverAddress var producer proofProducer.ProofProducer - if cfg.Dummy { producer = &proofProducer.DummyProofProducer{ RandomDummyProofDelayLowerBound: p.cfg.RandomDummyProofDelayLowerBound, @@ -233,34 +224,16 @@ func (p *Prover) Start() error { p.initSubscription() if !p.cfg.OracleProver { go func() { - if err := p.srv.Start(fmt.Sprintf(":%v", p.cfg.HTTPServerPort)); !errors.Is(err, netHttp.ErrServerClosed) { + if err := p.srv.Start(fmt.Sprintf(":%v", p.cfg.HTTPServerPort)); !errors.Is(err, http.ErrServerClosed) { log.Crit("Failed to start http server", "error", err) } }() } go p.eventLoop() - go p.watchCurrentCapacity() return nil } -func (p *Prover) watchCurrentCapacity() { - p.wg.Add(1) - defer func() { - p.wg.Done() - }() - - for { - select { - case <-p.ctx.Done(): - return - case <-p.requestCurrentCapacityCh: - log.Info("Received request for current capacity", "currentCapacity", p.currentCapacity) - p.receiveCurrentCapacityCh <- p.currentCapacity - } - } -} - // eventLoop starts the main loop of Taiko prover. func (p *Prover) eventLoop() { defer func() { @@ -631,11 +604,9 @@ func (p *Prover) onBlockProposed( p.currentBlocksBeingProvenMutex.Unlock() if !p.cfg.OracleProver { - if p.currentCapacity == 0 { + if _, ok := p.capacityManager.TakeOneCapacity(); !ok { return errNoCapacity } - - p.currentCapacity-- } return p.validProofSubmitter.RequestProof(ctx, event) @@ -679,7 +650,7 @@ func (p *Prover) submitProofOp(ctx context.Context, proofWithHeader *proofProduc if err := backoff.Retry( func() error { if !p.cfg.OracleProver { - p.currentCapacity++ + p.capacityManager.ReleaseOneCapacity() } err := p.validProofSubmitter.SubmitProof(p.ctx, proofWithHeader) @@ -919,7 +890,10 @@ func (p *Prover) checkProofWindowExpired(ctx context.Context, l1Height, blockId if isExpired { log.Debug( - "Block proof window is expired", "blockID", blockId, "l1Height", l1Height) + "Block proof window is expired", + "blockID", blockId, + "l1Height", l1Height, + ) // we should remove this block from being watched regardless of whether the block // has a valid proof @@ -942,16 +916,17 @@ func (p *Prover) checkProofWindowExpired(ctx context.Context, l1Height, blockId return encoding.TryParsingCustomError(err) } - if transition.Prover == zeroAddress { + if transition.Prover == rpc.ZeroAddress { log.Info( "Proof window for proof not assigned to us expired, requesting proof", - "blockID", - blockId, - "l1Height", - l1Height, + "blockID", blockId, + "l1Height", l1Height, ) // we can generate the proof, no proof came in by proof window expiring - if err := p.requestProofForBlockId(new(big.Int).SetUint64(blockId), new(big.Int).SetUint64(l1Height)); err != nil { + if err := p.requestProofForBlockId( + new(big.Int).SetUint64(blockId), + new(big.Int).SetUint64(l1Height), + ); err != nil { return err } } else { @@ -967,18 +942,17 @@ func (p *Prover) checkProofWindowExpired(ctx context.Context, l1Height, blockId if block.Hash() != transition.BlockHash { log.Info( "Invalid proof detected while watching for proof window expiration, requesting proof", - "blockID", - blockId, - "l1Height", - l1Height, - "expectedBlockHash", - block.Hash(), - "transitionBlockHash", - common.Bytes2Hex(transition.BlockHash[:]), + "blockID", blockId, + "l1Height", l1Height, + "expectedBlockHash", block.Hash(), + "transitionBlockHash", common.Bytes2Hex(transition.BlockHash[:]), ) // we can generate the proof, the proof is incorrect since blockHash does not match // the correct one but parentHash/gasUsed are correct. - if err := p.requestProofForBlockId(new(big.Int).SetUint64(blockId), new(big.Int).SetUint64(l1Height)); err != nil { + if err := p.requestProofForBlockId( + new(big.Int).SetUint64(blockId), + new(big.Int).SetUint64(l1Height), + ); err != nil { return err } } @@ -1030,7 +1004,9 @@ func (p *Prover) requestProofForBlockId(blockId *big.Int, l1Height *big.Int) err } if !p.cfg.OracleProver { - p.currentCapacity-- + if _, ok := p.capacityManager.TakeOneCapacity(); !ok { + return errNoCapacity + } } return nil diff --git a/prover/prover_test.go b/prover/prover_test.go index 1e1c74e7c..3deb7e619 100644 --- a/prover/prover_test.go +++ b/prover/prover_test.go @@ -2,9 +2,9 @@ package prover import ( "context" - "fmt" - "math/big" + "net/url" "os" + "strconv" "testing" "time" @@ -16,7 +16,6 @@ import ( "github.com/taikoxyz/taiko-client/driver" "github.com/taikoxyz/taiko-client/pkg/jwt" "github.com/taikoxyz/taiko-client/proposer" - "github.com/taikoxyz/taiko-client/prover/http" producer "github.com/taikoxyz/taiko-client/prover/proof_producer" "github.com/taikoxyz/taiko-client/testutils" ) @@ -29,10 +28,6 @@ type ProverTestSuite struct { proposer *proposer.Proposer } -var ( - port = testutils.RandomPort() -) - func (s *ProverTestSuite) SetupTest() { s.ClientTestSuite.SetupTest() @@ -40,6 +35,10 @@ func (s *ProverTestSuite) SetupTest() { l1ProverPrivKey, err := crypto.ToECDSA(common.Hex2Bytes(os.Getenv("L1_PROVER_PRIVATE_KEY"))) s.Nil(err) + proverServerUrl := testutils.LocalRandomProverEndpoint() + port, err := strconv.Atoi(proverServerUrl.Port()) + s.Nil(err) + ctx, cancel := context.WithCancel(context.Background()) p := new(Prover) s.Nil(InitFromConfig(ctx, p, (&Config{ @@ -56,16 +55,19 @@ func (s *ProverTestSuite) SetupTest() { MaxConcurrentProvingJobs: 1, CheckProofWindowExpiredInterval: 5 * time.Second, ProveUnassignedBlocks: true, - Capacity: 100, - MinProofFee: big.NewInt(1), + Capacity: 1024, + MinProofFee: common.Big1, + HTTPServerPort: uint64(port), }))) + p.srv = testutils.NewTestProverServer( + &s.ClientTestSuite, + l1ProverPrivKey, + p.capacityManager, + proverServerUrl, + ) s.p = p s.cancel = cancel - go func() { - _ = s.p.srv.Start(fmt.Sprintf(":%v", port)) - }() - // Init driver jwtSecret, err := jwt.ParseSecretFromFile(os.Getenv("JWT_SECRET")) s.Nil(err) @@ -90,24 +92,23 @@ func (s *ProverTestSuite) SetupTest() { proposeInterval := 1024 * time.Hour // No need to periodically propose transactions list in unit tests s.Nil(proposer.InitFromConfig(context.Background(), prop, (&proposer.Config{ - L1Endpoint: os.Getenv("L1_NODE_WS_ENDPOINT"), - L2Endpoint: os.Getenv("L2_EXECUTION_ENGINE_WS_ENDPOINT"), - TaikoL1Address: common.HexToAddress(os.Getenv("TAIKO_L1_ADDRESS")), - TaikoL2Address: common.HexToAddress(os.Getenv("TAIKO_L2_ADDRESS")), - TaikoTokenAddress: common.HexToAddress(os.Getenv("TAIKO_TOKEN_ADDRESS")), - L1ProposerPrivKey: l1ProposerPrivKey, - L2SuggestedFeeRecipient: common.HexToAddress(os.Getenv("L2_SUGGESTED_FEE_RECIPIENT")), - ProposeInterval: &proposeInterval, // No need to periodically propose transactions list in unit tests - MaxProposedTxListsPerEpoch: 1, - WaitReceiptTimeout: 10 * time.Second, - ProverEndpoints: []string{fmt.Sprintf("http://localhost:%v", port)}, - BlockProposalFee: big.NewInt(1000), - BlockProposalFeeIterations: 3, + L1Endpoint: os.Getenv("L1_NODE_WS_ENDPOINT"), + L2Endpoint: os.Getenv("L2_EXECUTION_ENGINE_WS_ENDPOINT"), + TaikoL1Address: common.HexToAddress(os.Getenv("TAIKO_L1_ADDRESS")), + TaikoL2Address: common.HexToAddress(os.Getenv("TAIKO_L2_ADDRESS")), + TaikoTokenAddress: common.HexToAddress(os.Getenv("TAIKO_TOKEN_ADDRESS")), + L1ProposerPrivKey: l1ProposerPrivKey, + L2SuggestedFeeRecipient: common.HexToAddress(os.Getenv("L2_SUGGESTED_FEE_RECIPIENT")), + ProposeInterval: &proposeInterval, + MaxProposedTxListsPerEpoch: 1, + WaitReceiptTimeout: 10 * time.Second, + ProverEndpoints: []*url.URL{proverServerUrl}, + BlockProposalFee: common.Big256, + BlockProposalFeeIterations: 3, + BlockProposalFeeIncreasePercentage: common.Big2, }))) s.proposer = prop - - go s.p.watchCurrentCapacity() } func (s *ProverTestSuite) TestName() { @@ -199,12 +200,7 @@ func (s *ProverTestSuite) TestCheckChainVerification() { } func (s *ProverTestSuite) TestStartClose() { - l1ProverPrivKey, err := crypto.ToECDSA(common.Hex2Bytes(os.Getenv("L1_PROVER_PRIVATE_KEY"))) - s.Nil(err) - - s.p.srv, _ = http.NewServer(http.NewServerOpts{ - ProverPrivateKey: l1ProverPrivKey, - }) + s.p.cfg.OracleProver = true s.Nil(s.p.Start()) s.cancel() s.NotPanics(func() { s.p.Close(context.Background()) }) diff --git a/prover/server/propose_block.go b/prover/server/propose_block.go new file mode 100644 index 000000000..5cce33b87 --- /dev/null +++ b/prover/server/propose_block.go @@ -0,0 +1,53 @@ +package server + +import ( + "net/http" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/labstack/echo/v4" + "github.com/labstack/gommon/log" + "github.com/taikoxyz/taiko-client/bindings/encoding" +) + +// ProposeBlockResponse represents the JSON response which will be returned by +// the ProposeBlock request handler. +type ProposeBlockResponse struct { + SignedPayload []byte `json:"signedPayload"` + Prover common.Address `json:"prover"` +} + +// ProposeBlock handles a propose block request, decides if this prover wants to +// handle this block, and if so, returns a signed payload the proposer +// can submit onchain. +func (srv *ProverServer) ProposeBlock(c echo.Context) error { + req := new(encoding.ProposeBlockData) + if err := c.Bind(req); err != nil { + return c.JSON(http.StatusUnprocessableEntity, err) + } + + if req.Fee.Cmp(srv.minProofFee) < 0 { + return echo.NewHTTPError(http.StatusUnprocessableEntity, "proof fee too low") + } + + if srv.capacityManager.ReadCapacity() == 0 { + log.Warn("Prover does not have capacity") + return echo.NewHTTPError(http.StatusUnprocessableEntity, "prover does not have capacity") + } + + encoded, err := encoding.EncodeProposeBlockData(req) + if err != nil { + log.Error("Failed to encode proposeBlock data", "error", err) + return echo.NewHTTPError(http.StatusUnprocessableEntity, err) + } + + signed, err := crypto.Sign(crypto.Keccak256Hash(encoded).Bytes(), srv.proverPrivateKey) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err) + } + + return c.JSON(http.StatusOK, &ProposeBlockResponse{ + SignedPayload: signed, + Prover: srv.proverAddress, + }) +} diff --git a/prover/server/propose_block_test.go b/prover/server/propose_block_test.go new file mode 100644 index 000000000..74659b6eb --- /dev/null +++ b/prover/server/propose_block_test.go @@ -0,0 +1,68 @@ +package server + +import ( + "crypto/rand" + "net/http" + "net/http/httptest" + "time" + + "github.com/cyberhorsey/webutils/testutils" + "github.com/ethereum/go-ethereum/common" + "github.com/labstack/echo/v4" + "github.com/taikoxyz/taiko-client/bindings/encoding" +) + +func (s *ProverServerTestSuite) TestProposeBlockSuccess() { + rec := httptest.NewRecorder() + + s.srv.ServeHTTP(rec, testutils.NewUnauthenticatedRequest( + echo.POST, + "/proposeBlock", + &encoding.ProposeBlockData{ + Fee: common.Big256, + Expiry: uint64(time.Now().Unix()), + Input: encoding.TaikoL1BlockMetadataInput{ + Proposer: common.BytesToAddress(randomHash().Bytes()), + TxListHash: randomHash(), + TxListByteStart: common.Big0, + TxListByteEnd: common.Big0, + CacheTxListInfo: false, + }, + }, + )) + + testutils.AssertStatusAndBody(s.T(), rec, http.StatusOK, []string{"signedPayload"}) +} + +func (s *ProverServerTestSuite) TestProposeBlockTimeout() { + rec := httptest.NewRecorder() + + s.srv.ServeHTTP(rec, testutils.NewUnauthenticatedRequest( + echo.POST, + "/proposeBlock", + &encoding.ProposeBlockData{ + Fee: common.Big256, + Expiry: uint64(time.Now().Unix()), + Input: encoding.TaikoL1BlockMetadataInput{ + Proposer: common.BytesToAddress(randomHash().Bytes()), + TxListHash: randomHash(), + TxListByteStart: common.Big0, + TxListByteEnd: common.Big0, + CacheTxListInfo: false, + }, + }, + )) + + testutils.AssertStatusAndBody( + s.T(), rec, http.StatusUnprocessableEntity, []string{`{"message":"timed out trying to get capacity"}`}, + ) +} + +// randomHash generates a random blob of data and returns it as a hash. +func randomHash() common.Hash { + var hash common.Hash + if n, err := rand.Read(hash[:]); n != common.HashLength || err != nil { + panic(err) + } + return hash +} diff --git a/prover/server/server.go b/prover/server/server.go new file mode 100644 index 000000000..58bdacd4c --- /dev/null +++ b/prover/server/server.go @@ -0,0 +1,104 @@ +package server + +import ( + "context" + "crypto/ecdsa" + "math/big" + "net/http" + "os" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + echo "github.com/labstack/echo/v4" + "github.com/labstack/echo/v4/middleware" + capacity "github.com/taikoxyz/taiko-client/prover/capacity_manager" +) + +// ProverServer represents a prover server instance. +type ProverServer struct { + echo *echo.Echo + proverPrivateKey *ecdsa.PrivateKey + proverAddress common.Address + + // capacity-related configs + capacityManager *capacity.CapacityManager + minProofFee *big.Int +} + +// NewProverServerOpts contains all configurations for creating a prover server instance. +type NewProverServerOpts struct { + ProverPrivateKey *ecdsa.PrivateKey + MinProofFee *big.Int + CapacityManager *capacity.CapacityManager +} + +// New creates a new prover server instance. +func New(opts *NewProverServerOpts) (*ProverServer, error) { + address := crypto.PubkeyToAddress(opts.ProverPrivateKey.PublicKey) + srv := &ProverServer{ + proverPrivateKey: opts.ProverPrivateKey, + proverAddress: address, + echo: echo.New(), + minProofFee: opts.MinProofFee, + capacityManager: opts.CapacityManager, + } + + srv.echo.HideBanner = true + srv.configureMiddleware() + srv.configureRoutes() + + return srv, nil +} + +// Start starts the HTTP server. +func (srv *ProverServer) Start(address string) error { + return srv.echo.Start(address) +} + +// Shutdown shuts down the HTTP server. +func (srv *ProverServer) Shutdown(ctx context.Context) error { + return srv.echo.Shutdown(ctx) +} + +// ServeHTTP implements the `http.Handler` interface which serves HTTP requests. +func (srv *ProverServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { + srv.echo.ServeHTTP(w, r) +} + +// Health endpoints for probes. +func (srv *ProverServer) Health(c echo.Context) error { + return c.NoContent(http.StatusOK) +} + +// LogSkipper implements the `middleware.Skipper` interface. +func LogSkipper(c echo.Context) bool { + switch c.Request().URL.Path { + case "/healthz": + return true + case "/metrics": + return true + default: + return true + } +} + +// configureMiddleware configures the server middlewares. +func (srv *ProverServer) configureMiddleware() { + srv.echo.Use(middleware.RequestID()) + + srv.echo.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{ + Skipper: LogSkipper, + Format: `{"time":"${time_rfc3339_nano}","level":"INFO","message":{"id":"${id}","remote_ip":"${remote_ip}",` + + `"host":"${host}","method":"${method}","uri":"${uri}","user_agent":"${user_agent}",` + + `"response_status":${status},"error":"${error}","latency":${latency},"latency_human":"${latency_human}",` + + `"bytes_in":${bytes_in},"bytes_out":${bytes_out}}}` + "\n", + Output: os.Stdout, + })) +} + +// configureRoutes contains all routes which will be used by prover server. +func (srv *ProverServer) configureRoutes() { + srv.echo.GET("/", srv.Health) + srv.echo.GET("/healthz", srv.Health) + srv.echo.POST("/proposeBlock", srv.ProposeBlock) +} diff --git a/prover/server/server_test.go b/prover/server/server_test.go new file mode 100644 index 000000000..b898517c3 --- /dev/null +++ b/prover/server/server_test.go @@ -0,0 +1,82 @@ +package server + +import ( + "context" + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "os" + + "github.com/cenkalti/backoff/v4" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/go-resty/resty/v2" + echo "github.com/labstack/echo/v4" + "github.com/phayes/freeport" + "github.com/stretchr/testify/suite" +) + +type ProverServerTestSuite struct { + suite.Suite + srv *ProverServer +} + +func (s *ProverServerTestSuite) SetupTest() { + l1ProverPrivKey, err := crypto.ToECDSA(common.Hex2Bytes(os.Getenv("L1_PROVER_PRIVATE_KEY"))) + s.Nil(err) + + srv := &ProverServer{ + echo: echo.New(), + proverPrivateKey: l1ProverPrivKey, + minProofFee: common.Big1, + } + + srv.configureMiddleware() + srv.configureRoutes() + + s.srv = srv +} + +func (s *ProverServerTestSuite) TestHealth() { + s.Equal(http.StatusOK, s.sendReq("/healthz").Code) +} + +func (s *ProverServerTestSuite) TestRoot() { + s.Equal(http.StatusOK, s.sendReq("/").Code) +} + +func (s *ProverServerTestSuite) TestStartShutdown() { + port, err := freeport.GetFreePort() + s.Nil(err) + + url, err := url.Parse(fmt.Sprintf("http://localhost:%v", port)) + s.Nil(err) + + go func() { s.Nil(s.srv.Start(fmt.Sprintf(":%v", port))) }() + + // Wait till the server fully started. + s.Nil(backoff.Retry(func() error { + res, err := resty.New().R().Get(url.String() + "/healthz") + if err != nil { + return err + } + if !res.IsSuccess() { + return fmt.Errorf("invalid response status code: %d", res.StatusCode()) + } + + return nil + }, backoff.NewExponentialBackOff())) + + s.Nil(s.srv.Shutdown(context.Background())) +} + +func (s *ProverServerTestSuite) sendReq(path string) *httptest.ResponseRecorder { + req, err := http.NewRequest(echo.GET, path, nil) + s.Nil(err) + rec := httptest.NewRecorder() + + s.srv.ServeHTTP(rec, req) + + return rec +} diff --git a/testutils/helper.go b/testutils/helper.go index 928d651b2..a1b33aaa6 100644 --- a/testutils/helper.go +++ b/testutils/helper.go @@ -3,12 +3,15 @@ package testutils import ( "context" "crypto/ecdsa" + "crypto/rand" + "errors" "fmt" "math/big" - "math/rand" - "os" + "net/http" + "net/url" "time" + "github.com/cenkalti/backoff/v4" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -16,17 +19,19 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" + "github.com/go-resty/resty/v2" "github.com/phayes/freeport" "github.com/taikoxyz/taiko-client/bindings" "github.com/taikoxyz/taiko-client/bindings/encoding" - "github.com/taikoxyz/taiko-client/prover/http" + capacity "github.com/taikoxyz/taiko-client/prover/capacity_manager" + "github.com/taikoxyz/taiko-client/prover/server" ) func ProposeInvalidTxListBytes(s *ClientTestSuite, proposer Proposer) { invalidTxListBytes := RandomBytes(256) s.Nil(proposer.ProposeTxList(context.Background(), &encoding.TaikoL1BlockMetadataInput{ - Beneficiary: proposer.L2SuggestedFeeRecipient(), + Proposer: proposer.L2SuggestedFeeRecipient(), TxListHash: crypto.Keccak256Hash(invalidTxListBytes), TxListByteStart: common.Big0, TxListByteEnd: new(big.Int).SetUint64(uint64(len(invalidTxListBytes))), @@ -59,7 +64,7 @@ func ProposeAndInsertEmptyBlocks( s.Nil(err) s.Nil(proposer.ProposeTxList(context.Background(), &encoding.TaikoL1BlockMetadataInput{ - Beneficiary: proposer.L2SuggestedFeeRecipient(), + Proposer: proposer.L2SuggestedFeeRecipient(), TxListHash: crypto.Keccak256Hash(encoded), TxListByteStart: common.Big0, TxListByteEnd: new(big.Int).SetUint64(uint64(len(encoded))), @@ -179,43 +184,41 @@ func DepositEtherToL2(s *ClientTestSuite, depositerPrivKey *ecdsa.PrivateKey, re } } -// HTTPServer starts a new prover server that has channel listeners to respond and react +// NewTestProverServer starts a new prover server that has channel listeners to respond and react // to requests for capacity, which provers can call. -func HTTPServer(s *ClientTestSuite, port int) (*http.Server, func(), error) { - l1ProverPrivKey, err := crypto.ToECDSA(common.Hex2Bytes(os.Getenv("L1_PROVER_PRIVATE_KEY"))) - s.Nil(err) - - serverOpts := http.NewServerOpts{ - ProverPrivateKey: l1ProverPrivKey, - MinProofFee: big.NewInt(1), - MaxCapacity: 10, - RequestCurrentCapacityCh: make(chan struct{}), - ReceiveCurrentCapacityCh: make(chan uint64), - } - - srv, err := http.NewServer(serverOpts) +func NewTestProverServer( + s *ClientTestSuite, + proverPrivKey *ecdsa.PrivateKey, + capacityManager *capacity.CapacityManager, + url *url.URL, +) *server.ProverServer { + srv, err := server.New(&server.NewProverServerOpts{ + ProverPrivateKey: proverPrivKey, + MinProofFee: common.Big1, + CapacityManager: capacityManager, + }) s.Nil(err) - ctx, cancel := context.WithCancel(context.Background()) go func() { - for { - select { - case <-serverOpts.RequestCurrentCapacityCh: - serverOpts.ReceiveCurrentCapacityCh <- 100 - case <-ctx.Done(): - return - } + if err := srv.Start(fmt.Sprintf(":%v", url.Port())); !errors.Is(err, http.ErrServerClosed) { + log.Error("Failed to start prover server", "error", err) } }() - go func() { - _ = srv.Start(fmt.Sprintf(":%v", port)) - }() + // Wait till the server fully started. + s.Nil(backoff.Retry(func() error { + res, err := resty.New().R().Get(url.String() + "/healthz") + if err != nil { + return err + } + if !res.IsSuccess() { + return fmt.Errorf("invalid response status code: %d", res.StatusCode()) + } + + return nil + }, backoff.NewExponentialBackOff())) - return srv, func() { - cancel() - _ = srv.Shutdown(ctx) - }, err + return srv } // RandomHash generates a random blob of data and returns it as a hash. @@ -245,6 +248,18 @@ func RandomPort() int { return port } +// LocalRandomProverEndpoint returns a local free random prover endpoint. +func LocalRandomProverEndpoint() *url.URL { + port := RandomPort() + + proverEndpoint, err := url.Parse(fmt.Sprintf("http://localhost:%v", port)) + if err != nil { + log.Crit("Failed to parse local prover endpoint", "err", err) + } + + return proverEndpoint +} + // SignatureFromRSV creates the signature bytes from r,s,v. func SignatureFromRSV(r, s string, v byte) []byte { return append(append(hexutil.MustDecode(r), hexutil.MustDecode(s)...), v) diff --git a/testutils/suite.go b/testutils/suite.go index 83bf62fb4..fc9713782 100644 --- a/testutils/suite.go +++ b/testutils/suite.go @@ -4,6 +4,7 @@ import ( "context" "crypto/ecdsa" "math/big" + "net/url" "os" "github.com/cenkalti/backoff/v4" @@ -14,6 +15,8 @@ import ( "github.com/stretchr/testify/suite" "github.com/taikoxyz/taiko-client/pkg/jwt" "github.com/taikoxyz/taiko-client/pkg/rpc" + capacity "github.com/taikoxyz/taiko-client/prover/capacity_manager" + "github.com/taikoxyz/taiko-client/prover/server" ) type ClientTestSuite struct { @@ -22,6 +25,8 @@ type ClientTestSuite struct { RpcClient *rpc.Client TestAddrPrivKey *ecdsa.PrivateKey TestAddr common.Address + ProverEndpoints []*url.URL + proverServer *server.ProverServer } func (s *ClientTestSuite) SetupTest() { @@ -68,6 +73,9 @@ func (s *ClientTestSuite) SetupTest() { l1ProverPrivKey, err := crypto.ToECDSA(common.Hex2Bytes(os.Getenv("L1_PROVER_PRIVATE_KEY"))) s.Nil(err) + s.ProverEndpoints = []*url.URL{LocalRandomProverEndpoint()} + s.proverServer = NewTestProverServer(s, l1ProverPrivKey, capacity.New(1024), s.ProverEndpoints[0]) + tokenBalance, err := rpcCli.TaikoL1.GetTaikoTokenBalance(nil, crypto.PubkeyToAddress(l1ProverPrivKey.PublicKey)) s.Nil(err) @@ -98,6 +106,7 @@ func (s *ClientTestSuite) TearDownTest() { s.True(revertRes) s.Nil(rpc.SetHead(context.Background(), s.RpcClient.L2RawRPC, common.Big0)) + s.Nil(s.proverServer.Shutdown(context.Background())) } func (s *ClientTestSuite) SetL1Automine(automine bool) {