Skip to content
This repository has been archived by the owner on May 11, 2024. It is now read-only.

feat(prover): add --prover.minEthBalance and --prover.minTaikoTokenBalance flags #641

Merged
merged 2 commits into from
Mar 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions cmd/flags/prover.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@ var (
Category: proverCategory,
Value: false,
}
MinEthBalance = &cli.Uint64Flag{
Name: "prover.minEthBalance",
Usage: "Minimum ETH balance (in wei) a prover wants to keep",
Category: proverCategory,
Value: 0,
}
MinTaikoTokenBalance = &cli.Uint64Flag{
Name: "prover.minTaikoTokenBalance",
Usage: "Minimum Taiko token balance a prover wants to keep",
Category: proverCategory,
Value: 0,
}
// Tier fee related.
MinOptimisticTierFee = &cli.Uint64Flag{
Name: "minTierFee.optimistic",
Expand Down Expand Up @@ -192,6 +204,8 @@ var ProverFlags = MergeFlags(CommonFlags, []cli.Flag{
MinOptimisticTierFee,
MinSgxTierFee,
MinSgxAndZkVMTierFee,
MinEthBalance,
MinTaikoTokenBalance,
StartingBlockID,
Dummy,
GuardianProver,
Expand Down
2 changes: 2 additions & 0 deletions internal/testutils/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ func (s *ClientTestSuite) NewTestProverServer(
MinOptimisticTierFee: common.Big1,
MinSgxTierFee: common.Big1,
MinSgxAndZkVMTierFee: common.Big1,
MinEthBalance: common.Big1,
MinTaikoTokenBalance: common.Big1,
MaxExpiry: 24 * time.Hour,
TaikoL1Address: common.HexToAddress(os.Getenv("TAIKO_L1_ADDRESS")),
AssignmentHookAddress: common.HexToAddress(os.Getenv("ASSIGNMENT_HOOK_ADDRESS")),
Expand Down
4 changes: 4 additions & 0 deletions prover/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ type Config struct {
MinOptimisticTierFee *big.Int
MinSgxTierFee *big.Int
MinSgxAndZkVMTierFee *big.Int
MinEthBalance *big.Int
MinTaikoTokenBalance *big.Int
MaxExpiry time.Duration
MaxProposedIn uint64
MaxBlockSlippage uint64
Expand Down Expand Up @@ -171,6 +173,8 @@ func NewConfigFromCliContext(c *cli.Context) (*Config, error) {
MinOptimisticTierFee: new(big.Int).SetUint64(c.Uint64(flags.MinOptimisticTierFee.Name)),
MinSgxTierFee: new(big.Int).SetUint64(c.Uint64(flags.MinSgxTierFee.Name)),
MinSgxAndZkVMTierFee: new(big.Int).SetUint64(c.Uint64(flags.MinSgxAndZkVMTierFee.Name)),
MinEthBalance: new(big.Int).SetUint64(c.Uint64(flags.MinEthBalance.Name)),
MinTaikoTokenBalance: new(big.Int).SetUint64(c.Uint64(flags.MinTaikoTokenBalance.Name)),
MaxExpiry: c.Duration(flags.MaxExpiry.Name),
MaxBlockSlippage: c.Uint64(flags.MaxAcceptableBlockSlippage.Name),
MaxProposedIn: c.Uint64(flags.MaxProposedIn.Name),
Expand Down
2 changes: 2 additions & 0 deletions prover/prover.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,8 @@ func InitFromConfig(ctx context.Context, p *Prover, cfg *Config) (err error) {
ProverPrivateKey: p.cfg.L1ProverPrivKey,
MinOptimisticTierFee: p.cfg.MinOptimisticTierFee,
MinSgxTierFee: p.cfg.MinSgxTierFee,
MinEthBalance: p.cfg.MinEthBalance,
MinTaikoTokenBalance: p.cfg.MinTaikoTokenBalance,
MaxExpiry: p.cfg.MaxExpiry,
MaxBlockSlippage: p.cfg.MaxBlockSlippage,
TaikoL1Address: p.cfg.TaikoL1Address,
Expand Down
86 changes: 78 additions & 8 deletions prover/server/api.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package server

import (
"context"
"math/big"
"net/http"
"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"
Expand All @@ -14,6 +16,10 @@ import (
"github.com/taikoxyz/taiko-client/pkg/rpc"
)

const (
rpcTimeout = 1 * time.Minute
)

// @title Taiko Prover Server API
// @version 1.0
// @termsOfService http://swagger.io/terms/
Expand Down Expand Up @@ -100,34 +106,44 @@ func (s *ProverServer) CreateAssignment(c echo.Context) error {
"currentUsedCapacity", len(s.proofSubmissionCh),
)

// 1. Check if the request body is valid.
if req.TxListHash == (common.Hash{}) {
log.Info("Invalid txList hash")
return echo.NewHTTPError(http.StatusUnprocessableEntity, "invalid txList hash")
}

if req.FeeToken != (common.Address{}) {
return echo.NewHTTPError(http.StatusUnprocessableEntity, "only receive ETH")
}

ok, err := rpc.CheckProverBalance(
// 2. Check if the prover has the required minimum on-chain ETH and Taiko token balance.
ok, err := s.checkMinEthAndToken(c.Request().Context())
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, err)
}

if !ok {
return echo.NewHTTPError(http.StatusUnprocessableEntity, "insufficient prover balance")
}

// 3. Check if the prover's token balance is enough to cover the bonds.
if ok, err = rpc.CheckProverBalance(
c.Request().Context(),
s.rpc,
s.proverAddress,
s.assignmentHookAddress,
s.livenessBond,
)
if err != nil {
); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, err)
}

if !ok {
log.Warn(
"Insufficient prover balance, please get more tokens or wait for verification of the blocks you proved",
"Insufficient prover token balance, please get more tokens or wait for verification of the blocks you proved",
"prover", s.proverAddress,
)
return echo.NewHTTPError(http.StatusUnprocessableEntity, "insufficient prover balance")
}

// 4. Check if the proof fee meets prover's minimum requirement for each tier.
for _, tier := range req.TierFees {
if tier.Tier == encoding.TierGuardianID {
continue
Expand Down Expand Up @@ -157,6 +173,7 @@ func (s *ProverServer) CreateAssignment(c echo.Context) error {
}
}

// 5. Check if the expiry is too long.
if req.Expiry > uint64(time.Now().Add(s.maxExpiry).Unix()) {
log.Warn(
"Expiry too long",
Expand All @@ -167,18 +184,18 @@ func (s *ProverServer) CreateAssignment(c echo.Context) error {
return echo.NewHTTPError(http.StatusUnprocessableEntity, "expiry too long")
}

// Check if the prover has any capacity now.
// 6. Check if the prover has any capacity now.
if s.proofSubmissionCh != nil && len(s.proofSubmissionCh) == cap(s.proofSubmissionCh) {
log.Warn("Prover does not have capacity", "capacity", cap(s.proofSubmissionCh))
return echo.NewHTTPError(http.StatusUnprocessableEntity, "prover does not have capacity")
}

// 7. Encode and sign the prover assignment payload.
l1Head, err := s.rpc.L1.BlockNumber(c.Request().Context())
if err != nil {
log.Error("Failed to get L1 block head", "error", err)
return echo.NewHTTPError(http.StatusUnprocessableEntity, err)
}

encoded, err := encoding.EncodeProverAssignmentPayload(
s.protocolConfigs.ChainId,
s.taikoL1Address,
Expand All @@ -200,10 +217,63 @@ func (s *ProverServer) CreateAssignment(c echo.Context) error {
return echo.NewHTTPError(http.StatusInternalServerError, err)
}

// 8. Return the signed payload.
return c.JSON(http.StatusOK, &ProposeBlockResponse{
SignedPayload: signed,
Prover: s.proverAddress,
MaxBlockID: l1Head + s.maxSlippage,
MaxProposedIn: s.maxProposedIn,
})
}

// checkMinEthAndToken checks if the prover has the required minimum on-chain ETH and Taiko token balance.
func (s *ProverServer) checkMinEthAndToken(ctx context.Context) (bool, error) {
ctx, cancel := context.WithTimeout(ctx, rpcTimeout)
defer cancel()

// 1. Check prover's ETH balance.
ethBalance, err := s.rpc.L1.BalanceAt(ctx, s.proverAddress, nil)
if err != nil {
return false, err
}

log.Info(
"Prover's ETH balance",
"balance", ethBalance,
"address", s.proverAddress.Hex(),
)

if ethBalance.Cmp(s.minEthBalance) <= 0 {
log.Warn(
"Prover does not have required minimum on-chain ETH balance",
"providedProver", s.proverAddress.Hex(),
"ethBalance", ethBalance,
"minEthBalance", s.minEthBalance,
)
return false, nil
}

// 2. Check prover's Taiko token balance.
balance, err := s.rpc.TaikoToken.BalanceOf(&bind.CallOpts{Context: ctx}, s.proverAddress)
if err != nil {
return false, err
}

log.Info(
"Prover's Taiko token balance",
"balance", balance.String(),
"address", s.proverAddress.Hex(),
)

if balance.Cmp(s.minTaikoTokenBalance) <= 0 {
log.Warn(
"Prover does not have required on-chain Taiko token balance",
"providedProver", s.proverAddress.Hex(),
"taikoTokenBalance", balance,
"minTaikoTokenBalance", s.minTaikoTokenBalance,
)
return false, nil
}

return true, nil
}
6 changes: 6 additions & 0 deletions prover/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ type ProverServer struct {
minOptimisticTierFee *big.Int
minSgxTierFee *big.Int
minSgxAndZkVMTierFee *big.Int
minEthBalance *big.Int
minTaikoTokenBalance *big.Int
maxExpiry time.Duration
maxSlippage uint64
maxProposedIn uint64
Expand All @@ -54,6 +56,8 @@ type NewProverServerOpts struct {
MinOptimisticTierFee *big.Int
MinSgxTierFee *big.Int
MinSgxAndZkVMTierFee *big.Int
MinEthBalance *big.Int
MinTaikoTokenBalance *big.Int
MaxExpiry time.Duration
MaxBlockSlippage uint64
MaxProposedIn uint64
Expand All @@ -74,6 +78,8 @@ func New(opts *NewProverServerOpts) (*ProverServer, error) {
minOptimisticTierFee: opts.MinOptimisticTierFee,
minSgxTierFee: opts.MinSgxTierFee,
minSgxAndZkVMTierFee: opts.MinSgxAndZkVMTierFee,
minEthBalance: opts.MinEthBalance,
minTaikoTokenBalance: opts.MinTaikoTokenBalance,
maxExpiry: opts.MaxExpiry,
maxProposedIn: opts.MaxProposedIn,
maxSlippage: opts.MaxBlockSlippage,
Expand Down
2 changes: 2 additions & 0 deletions prover/server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ func (s *ProverServerTestSuite) SetupTest() {
MinOptimisticTierFee: common.Big1,
MinSgxTierFee: common.Big1,
MinSgxAndZkVMTierFee: common.Big1,
MinEthBalance: common.Big1,
MinTaikoTokenBalance: common.Big1,
MaxExpiry: time.Hour,
ProofSubmissionCh: make(chan<- proofProducer.ProofRequestBody, 1024),
TaikoL1Address: common.HexToAddress(os.Getenv("TAIKO_L1_ADDRESS")),
Expand Down
Loading