From 630924e4a331007e135dcfe61e9c48cf190d9647 Mon Sep 17 00:00:00 2001 From: jeff <113397187+cyberhorsey@users.noreply.github.com> Date: Wed, 13 Dec 2023 18:26:06 -0800 Subject: [PATCH] feat(prover): refactor of guardian prover heartbeat signing / sending (#472) --- .gitignore | 1 + cmd/flags/prover.go | 6 + prover/config.go | 163 +++++++------- .../guardian_prover_sender/guardian_prover.go | 209 ++++++++++++++++++ prover/guardian_prover_sender/interface.go | 22 ++ prover/prover.go | 119 ++++------ prover/prover_test.go | 10 +- prover/server/api.go | 81 ------- prover/server/api_test.go | 41 ---- prover/server/server.go | 5 - 10 files changed, 380 insertions(+), 277 deletions(-) create mode 100644 prover/guardian_prover_sender/guardian_prover.go create mode 100644 prover/guardian_prover_sender/interface.go diff --git a/.gitignore b/.gitignore index ba48ed841..3ba7a28a6 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ bin coverage.out prover/dbPath +dbdata # Test binary, built with `go test -c` *.test diff --git a/cmd/flags/prover.go b/cmd/flags/prover.go index 82af62767..293930a54 100644 --- a/cmd/flags/prover.go +++ b/cmd/flags/prover.go @@ -173,6 +173,11 @@ var ( Usage: "Amount to approve TaikoL1 contract for TaikoToken usage", Category: proverCategory, } + GuardianProverHealthCheckServerEndpoint = &cli.StringFlag{ + Name: "prover.guardianProverHealthCheckServerEndpoint", + Usage: "HTTP endpoint for main guardian prover health check server", + Category: proverCategory, + } ) // All prover flags. @@ -191,6 +196,7 @@ var ProverFlags = MergeFlags(CommonFlags, []cli.Flag{ Dummy, GuardianProver, GuardianProofSubmissionDelay, + GuardianProverHealthCheckServerEndpoint, ProofSubmissionMaxRetry, ProveBlockTxReplacementMultiplier, ProveBlockMaxTxGasTipCap, diff --git a/prover/config.go b/prover/config.go index 890b5a787..ba04e01d7 100644 --- a/prover/config.go +++ b/prover/config.go @@ -4,6 +4,7 @@ import ( "crypto/ecdsa" "fmt" "math/big" + "net/url" "time" "github.com/ethereum/go-ethereum/common" @@ -14,44 +15,45 @@ import ( // Config contains the configurations to initialize a Taiko prover. type Config struct { - L1WsEndpoint string - L1HttpEndpoint string - L2WsEndpoint string - L2HttpEndpoint string - TaikoL1Address common.Address - TaikoL2Address common.Address - TaikoTokenAddress common.Address - AssignmentHookAddress common.Address - L1ProverPrivKey *ecdsa.PrivateKey - ZKEvmRpcdEndpoint string - ZkEvmRpcdParamsPath string - StartingBlockID *big.Int - Dummy bool - GuardianProverAddress common.Address - GuardianProofSubmissionDelay time.Duration - ProofSubmissionMaxRetry uint64 - Graffiti string - BackOffMaxRetrys uint64 - BackOffRetryInterval time.Duration - ProveUnassignedBlocks bool - ContesterMode bool - RPCTimeout *time.Duration - WaitReceiptTimeout time.Duration - ProveBlockGasLimit *uint64 - ProveBlockTxReplacementMultiplier uint64 - ProveBlockMaxTxGasTipCap *big.Int - HTTPServerPort uint64 - Capacity uint64 - MinOptimisticTierFee *big.Int - MinSgxTierFee *big.Int - MinPseZkevmTierFee *big.Int - MinSgxAndPseZkevmTierFee *big.Int - MaxExpiry time.Duration - MaxProposedIn uint64 - MaxBlockSlippage uint64 - DatabasePath string - DatabaseCacheSize uint64 - Allowance *big.Int + L1WsEndpoint string + L1HttpEndpoint string + L2WsEndpoint string + L2HttpEndpoint string + TaikoL1Address common.Address + TaikoL2Address common.Address + TaikoTokenAddress common.Address + AssignmentHookAddress common.Address + L1ProverPrivKey *ecdsa.PrivateKey + ZKEvmRpcdEndpoint string + ZkEvmRpcdParamsPath string + StartingBlockID *big.Int + Dummy bool + GuardianProverAddress common.Address + GuardianProofSubmissionDelay time.Duration + ProofSubmissionMaxRetry uint64 + Graffiti string + BackOffMaxRetrys uint64 + BackOffRetryInterval time.Duration + ProveUnassignedBlocks bool + ContesterMode bool + RPCTimeout *time.Duration + WaitReceiptTimeout time.Duration + ProveBlockGasLimit *uint64 + ProveBlockTxReplacementMultiplier uint64 + ProveBlockMaxTxGasTipCap *big.Int + HTTPServerPort uint64 + Capacity uint64 + MinOptimisticTierFee *big.Int + MinSgxTierFee *big.Int + MinPseZkevmTierFee *big.Int + MinSgxAndPseZkevmTierFee *big.Int + MaxExpiry time.Duration + MaxProposedIn uint64 + MaxBlockSlippage uint64 + DatabasePath string + DatabaseCacheSize uint64 + Allowance *big.Int + GuardianProverHealthCheckServerEndpoint *url.URL } // NewConfigFromCliContext creates a new config instance from command line flags. @@ -103,44 +105,53 @@ func NewConfigFromCliContext(c *cli.Context) (*Config, error) { allowance = amt } + var guardianProverHealthCheckServerEndpoint *url.URL + if c.IsSet(flags.GuardianProverHealthCheckServerEndpoint.Name) { + guardianProverHealthCheckServerEndpoint, err = url.Parse(c.String(flags.GuardianProverHealthCheckServerEndpoint.Name)) + if err != nil { + return nil, err + } + } + return &Config{ - L1WsEndpoint: c.String(flags.L1WSEndpoint.Name), - L1HttpEndpoint: c.String(flags.L1HTTPEndpoint.Name), - L2WsEndpoint: c.String(flags.L2WSEndpoint.Name), - L2HttpEndpoint: c.String(flags.L2HTTPEndpoint.Name), - TaikoL1Address: common.HexToAddress(c.String(flags.TaikoL1Address.Name)), - TaikoL2Address: common.HexToAddress(c.String(flags.TaikoL2Address.Name)), - TaikoTokenAddress: common.HexToAddress(c.String(flags.TaikoTokenAddress.Name)), - AssignmentHookAddress: common.HexToAddress(c.String(flags.ProverAssignmentHookAddress.Name)), - L1ProverPrivKey: l1ProverPrivKey, - ZKEvmRpcdEndpoint: c.String(flags.ZkEvmRpcdEndpoint.Name), - ZkEvmRpcdParamsPath: c.String(flags.ZkEvmRpcdParamsPath.Name), - StartingBlockID: startingBlockID, - Dummy: c.Bool(flags.Dummy.Name), - GuardianProverAddress: common.HexToAddress(c.String(flags.GuardianProver.Name)), - GuardianProofSubmissionDelay: c.Duration(flags.GuardianProofSubmissionDelay.Name), - ProofSubmissionMaxRetry: c.Uint64(flags.ProofSubmissionMaxRetry.Name), - Graffiti: c.String(flags.Graffiti.Name), - BackOffMaxRetrys: c.Uint64(flags.BackOffMaxRetrys.Name), - BackOffRetryInterval: c.Duration(flags.BackOffRetryInterval.Name), - ProveUnassignedBlocks: c.Bool(flags.ProveUnassignedBlocks.Name), - ContesterMode: c.Bool(flags.ContesterMode.Name), - RPCTimeout: timeout, - WaitReceiptTimeout: c.Duration(flags.WaitReceiptTimeout.Name), - ProveBlockGasLimit: proveBlockTxGasLimit, - Capacity: c.Uint64(flags.ProverCapacity.Name), - ProveBlockTxReplacementMultiplier: proveBlockTxReplacementMultiplier, - ProveBlockMaxTxGasTipCap: proveBlockMaxTxGasTipCap, - HTTPServerPort: c.Uint64(flags.ProverHTTPServerPort.Name), - MinOptimisticTierFee: new(big.Int).SetUint64(c.Uint64(flags.MinOptimisticTierFee.Name)), - MinSgxTierFee: new(big.Int).SetUint64(c.Uint64(flags.MinSgxTierFee.Name)), - MinPseZkevmTierFee: new(big.Int).SetUint64(c.Uint64(flags.MinPseZkevmTierFee.Name)), - MinSgxAndPseZkevmTierFee: new(big.Int).SetUint64(c.Uint64(flags.MinSgxAndPseZkevmTierFee.Name)), - MaxExpiry: c.Duration(flags.MaxExpiry.Name), - MaxBlockSlippage: c.Uint64(flags.MaxAcceptableBlockSlippage.Name), - MaxProposedIn: c.Uint64(flags.MaxProposedIn.Name), - DatabasePath: c.String(flags.DatabasePath.Name), - DatabaseCacheSize: c.Uint64(flags.DatabaseCacheSize.Name), - Allowance: allowance, + L1WsEndpoint: c.String(flags.L1WSEndpoint.Name), + L1HttpEndpoint: c.String(flags.L1HTTPEndpoint.Name), + L2WsEndpoint: c.String(flags.L2WSEndpoint.Name), + L2HttpEndpoint: c.String(flags.L2HTTPEndpoint.Name), + TaikoL1Address: common.HexToAddress(c.String(flags.TaikoL1Address.Name)), + TaikoL2Address: common.HexToAddress(c.String(flags.TaikoL2Address.Name)), + TaikoTokenAddress: common.HexToAddress(c.String(flags.TaikoTokenAddress.Name)), + AssignmentHookAddress: common.HexToAddress(c.String(flags.ProverAssignmentHookAddress.Name)), + L1ProverPrivKey: l1ProverPrivKey, + ZKEvmRpcdEndpoint: c.String(flags.ZkEvmRpcdEndpoint.Name), + ZkEvmRpcdParamsPath: c.String(flags.ZkEvmRpcdParamsPath.Name), + StartingBlockID: startingBlockID, + Dummy: c.Bool(flags.Dummy.Name), + GuardianProverAddress: common.HexToAddress(c.String(flags.GuardianProver.Name)), + GuardianProofSubmissionDelay: c.Duration(flags.GuardianProofSubmissionDelay.Name), + GuardianProverHealthCheckServerEndpoint: guardianProverHealthCheckServerEndpoint, + ProofSubmissionMaxRetry: c.Uint64(flags.ProofSubmissionMaxRetry.Name), + Graffiti: c.String(flags.Graffiti.Name), + BackOffMaxRetrys: c.Uint64(flags.BackOffMaxRetrys.Name), + BackOffRetryInterval: c.Duration(flags.BackOffRetryInterval.Name), + ProveUnassignedBlocks: c.Bool(flags.ProveUnassignedBlocks.Name), + ContesterMode: c.Bool(flags.ContesterMode.Name), + RPCTimeout: timeout, + WaitReceiptTimeout: c.Duration(flags.WaitReceiptTimeout.Name), + ProveBlockGasLimit: proveBlockTxGasLimit, + Capacity: c.Uint64(flags.ProverCapacity.Name), + ProveBlockTxReplacementMultiplier: proveBlockTxReplacementMultiplier, + ProveBlockMaxTxGasTipCap: proveBlockMaxTxGasTipCap, + HTTPServerPort: c.Uint64(flags.ProverHTTPServerPort.Name), + MinOptimisticTierFee: new(big.Int).SetUint64(c.Uint64(flags.MinOptimisticTierFee.Name)), + MinSgxTierFee: new(big.Int).SetUint64(c.Uint64(flags.MinSgxTierFee.Name)), + MinPseZkevmTierFee: new(big.Int).SetUint64(c.Uint64(flags.MinPseZkevmTierFee.Name)), + MinSgxAndPseZkevmTierFee: new(big.Int).SetUint64(c.Uint64(flags.MinSgxAndPseZkevmTierFee.Name)), + MaxExpiry: c.Duration(flags.MaxExpiry.Name), + MaxBlockSlippage: c.Uint64(flags.MaxAcceptableBlockSlippage.Name), + MaxProposedIn: c.Uint64(flags.MaxProposedIn.Name), + DatabasePath: c.String(flags.DatabasePath.Name), + DatabaseCacheSize: c.Uint64(flags.DatabaseCacheSize.Name), + Allowance: allowance, }, nil } diff --git a/prover/guardian_prover_sender/guardian_prover.go b/prover/guardian_prover_sender/guardian_prover.go new file mode 100644 index 000000000..3601db3bc --- /dev/null +++ b/prover/guardian_prover_sender/guardian_prover.go @@ -0,0 +1,209 @@ +package guardianproversender + +import ( + "bytes" + "context" + "crypto/ecdsa" + "encoding/json" + "fmt" + "math/big" + "net/http" + "net/url" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/labstack/gommon/log" + "github.com/taikoxyz/taiko-client/pkg/rpc" + "github.com/taikoxyz/taiko-client/prover/db" +) + +type healthCheckReq struct { + ProverAddress string `json:"prover"` + HeartBeatSignature []byte `json:"heartBeatSignature"` +} + +type signedBlockReq struct { + BlockID uint64 `json:"blockID"` + BlockHash string `json:"blockHash"` + Signature []byte `json:"signature"` + Prover common.Address `json:"proverAddress"` +} + +type GuardianProverBlockSender struct { + privateKey *ecdsa.PrivateKey + healthCheckServerEndpoint *url.URL + db ethdb.KeyValueStore + rpc *rpc.Client + proverAddress common.Address +} + +func NewGuardianProverBlockSender( + privateKey *ecdsa.PrivateKey, + healthCheckServerEndpoint *url.URL, + db ethdb.KeyValueStore, + rpc *rpc.Client, + proverAddress common.Address, +) *GuardianProverBlockSender { + return &GuardianProverBlockSender{ + privateKey: privateKey, + healthCheckServerEndpoint: healthCheckServerEndpoint, + db: db, + rpc: rpc, + proverAddress: proverAddress, + } +} + +func (s *GuardianProverBlockSender) post(ctx context.Context, route string, req interface{}) error { + body, err := json.Marshal(req) + if err != nil { + return err + } + + resp, err := http.Post( + fmt.Sprintf("%v/%v", s.healthCheckServerEndpoint.String(), route), + "application/json", + bytes.NewBuffer(body)) + if err != nil { + return err + } + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf( + "unable to contract health check server endpoint, status code: %v", resp.StatusCode) + } + + return nil +} + +func (s *GuardianProverBlockSender) SignAndSendBlock(ctx context.Context, blockID *big.Int) error { + signed, blockHash, err := s.sign(ctx, blockID) + if err != nil { + return nil + } + + if signed == nil { + return nil + } + + if err := s.sendSignedBlockReq(ctx, signed, blockHash, blockID); err != nil { + return err + } + + return nil +} + +func (s *GuardianProverBlockSender) sendSignedBlockReq( + ctx context.Context, + signed []byte, + hash common.Hash, + blockID *big.Int, +) error { + if s.healthCheckServerEndpoint == nil { + log.Info("no health check server endpoint set, returning early") + return nil + } + + req := &signedBlockReq{ + BlockID: blockID.Uint64(), + BlockHash: hash.Hex(), + Signature: signed, + Prover: s.proverAddress, + } + + if err := s.post(ctx, "signedBlock", req); err != nil { + return err + } + + log.Info("Guardian prover successfully signed block", "blockID", blockID.Uint64()) + + return nil +} + +func (s *GuardianProverBlockSender) sign(ctx context.Context, blockID *big.Int) ([]byte, common.Hash, error) { + log.Info("Guardian prover signing block", "blockID", blockID.Uint64()) + + head, err := s.rpc.L2.BlockNumber(ctx) + if err != nil { + return nil, common.Hash{}, err + } + + for head < blockID.Uint64() { + log.Info( + "Guardian prover block signing waiting for chain", + "latestBlock", head, + "eventBlockID", blockID.Uint64(), + ) + + if _, err := s.rpc.WaitL1Origin(ctx, blockID); err != nil { + return nil, common.Hash{}, err + } + + head, err = s.rpc.L2.BlockNumber(ctx) + if err != nil { + return nil, common.Hash{}, err + } + } + + header, err := s.rpc.L2.HeaderByNumber(ctx, blockID) + if err != nil { + return nil, common.Hash{}, err + } + + exists, err := s.db.Has(db.BuildBlockKey(header.Time)) + if err != nil { + return nil, common.Hash{}, err + } + + if exists { + log.Info("Guardian prover already signed block", "blockID", blockID.Uint64()) + return nil, common.Hash{}, nil + } + + log.Info( + "Guardian prover block signing caught up", + "latestBlock", head, + "eventBlockID", blockID.Uint64(), + ) + + signed, err := crypto.Sign(header.Hash().Bytes(), s.privateKey) + if err != nil { + return nil, common.Hash{}, err + } + + if err := s.db.Put( + db.BuildBlockKey(header.Time), + db.BuildBlockValue(header.Hash().Bytes(), + signed, + blockID, + ), + ); err != nil { + return nil, common.Hash{}, err + } + + return signed, header.Hash(), nil +} + +func (s *GuardianProverBlockSender) Close() error { + return s.db.Close() +} + +func (s *GuardianProverBlockSender) SendHeartbeat(ctx context.Context) error { + sig, err := crypto.Sign(crypto.Keccak256Hash([]byte("HEART_BEAT")).Bytes(), s.privateKey) + if err != nil { + return err + } + + req := &healthCheckReq{ + HeartBeatSignature: sig, + ProverAddress: s.proverAddress.Hex(), + } + + if err := s.post(ctx, "healthCheck", req); err != nil { + return err + } + + log.Info("successfully sent heartbeat") + + return nil +} diff --git a/prover/guardian_prover_sender/interface.go b/prover/guardian_prover_sender/interface.go new file mode 100644 index 000000000..19abf2220 --- /dev/null +++ b/prover/guardian_prover_sender/interface.go @@ -0,0 +1,22 @@ +package guardianproversender + +import ( + "context" + "math/big" +) + +type BlockSigner interface { + SignAndSendBlock(ctx context.Context, blockID *big.Int) error +} + +type Heartbeater interface { + SendHeartbeat(ctx context.Context) error +} + +// BlockSenderHeartbeater defines an interface that communicates with a central Guardian Prover server, +// sending heartbeats and signed blocks (and in the future, contested blocks). +type BlockSenderHeartbeater interface { + BlockSigner + Heartbeater + Close() error +} diff --git a/prover/prover.go b/prover/prover.go index 9585a59c4..f9de6208c 100644 --- a/prover/prover.go +++ b/prover/prover.go @@ -26,7 +26,7 @@ import ( eventIterator "github.com/taikoxyz/taiko-client/pkg/chain_iterator/event_iterator" "github.com/taikoxyz/taiko-client/pkg/rpc" capacity "github.com/taikoxyz/taiko-client/prover/capacity_manager" - "github.com/taikoxyz/taiko-client/prover/db" + guardianproversender "github.com/taikoxyz/taiko-client/prover/guardian_prover_sender" 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" @@ -45,15 +45,15 @@ type Prover struct { proverAddress common.Address proverPrivateKey *ecdsa.PrivateKey - // Database - db ethdb.KeyValueStore - // Clients rpc *rpc.Client // Prover Server srv *server.ProverServer + // Guardian prover heartbeat and block sending related + guardianProverSender guardianproversender.BlockSenderHeartbeater + // Contract configurations protocolConfigs *bindings.TaikoDataConfig @@ -238,8 +238,6 @@ func InitFromConfig(ctx context.Context, p *Prover, cfg *Config) (err error) { ); err != nil { return err } - - p.db = db } // Prover server @@ -260,6 +258,18 @@ func InitFromConfig(ctx context.Context, p *Prover, cfg *Config) (err error) { DB: db, } + if p.IsGuardianProver() { + proverServerOpts.ProverPrivateKey = p.cfg.L1ProverPrivKey + + p.guardianProverSender = guardianproversender.NewGuardianProverBlockSender( + p.cfg.L1ProverPrivKey, + p.cfg.GuardianProverHealthCheckServerEndpoint, + db, + p.rpc, + p.proverAddress, + ) + } + if p.srv, err = server.New(proverServerOpts); err != nil { return err } @@ -349,6 +359,12 @@ func (p *Prover) Start() error { log.Crit("Failed to start http server", "error", err) } }() + + if p.IsGuardianProver() { + p.wg.Add(1) + go p.heartbeatInterval(p.ctx) + } + go p.eventLoop() return nil @@ -416,8 +432,10 @@ func (p *Prover) eventLoop() { func (p *Prover) Close(ctx context.Context) { p.closeSubscription() - if err := p.db.Close(); err != nil { - log.Error("failed to close database connection", "error", err) + if p.guardianProverSender != nil { + if err := p.guardianProverSender.Close(); err != nil { + log.Error("failed to close database connection", "error", err) + } } if err := p.srv.Shutdown(ctx); err != nil { @@ -465,7 +483,7 @@ func (p *Prover) onBlockProposed( if !p.IsGuardianProver() { return } - if err := p.signBlock(ctx, event.BlockId); err != nil { + if err := p.guardianProverSender.SignAndSendBlock(ctx, event.BlockId); err != nil { log.Error("Guardian prover unable to sign block", "blockID", event.BlockId, "error", err) } }() @@ -1271,74 +1289,29 @@ func (p *Prover) releaseOneCapacity(blockID *big.Int) { } } -// signBlock signs the block data and stores it in the database. -func (p *Prover) signBlock(ctx context.Context, blockID *big.Int) error { - // only guardianProvers should sign blocks - if !p.IsGuardianProver() { - return nil - } +// heartbeatInterval sends a heartbeat to the guardian prover health check server +// on an interval +func (p *Prover) heartbeatInterval(ctx context.Context) { + t := time.NewTicker(12 * time.Second) - log.Info("Guardian prover signing block", "blockID", blockID.Uint64()) + defer func() { + t.Stop() + p.wg.Done() + }() - head, err := p.rpc.L2.BlockNumber(ctx) - if err != nil { - return err + // only guardianProvers should send heartbeat + if !p.IsGuardianProver() { + return } - for head < blockID.Uint64() { - log.Info( - "Guardian prover block signing waiting for chain", - "latestBlock", head, - "eventBlockID", blockID.Uint64(), - ) - - if _, err := p.rpc.WaitL1Origin(ctx, blockID); err != nil { - return err - } - - head, err = p.rpc.L2.BlockNumber(ctx) - if err != nil { - return err + for { + select { + case <-p.ctx.Done(): + return + case <-t.C: + if err := p.guardianProverSender.SendHeartbeat(ctx); err != nil { + log.Error("error sending heartbeat", "error", err) + } } } - - header, err := p.rpc.L2.HeaderByNumber(ctx, blockID) - if err != nil { - return err - } - - exists, err := p.db.Has(db.BuildBlockKey(header.Time)) - if err != nil { - return err - } - - if exists { - log.Info("Guardian prover already signed block", "blockID", blockID.Uint64()) - return nil - } - - log.Info( - "Guardian prover block signing caught up", - "latestBlock", head, - "eventBlockID", blockID.Uint64(), - ) - - signed, err := crypto.Sign(header.Hash().Bytes(), p.proverPrivateKey) - if err != nil { - return err - } - - if err := p.db.Put( - db.BuildBlockKey(header.Time), - db.BuildBlockValue(header.Hash().Bytes(), - signed, - blockID, - ), - ); err != nil { - return err - } - - log.Info("Guardian prover successfully signed block", "blockID", blockID.Uint64()) - - return nil } diff --git a/prover/prover_test.go b/prover/prover_test.go index 0fe8cebe4..f291edc39 100644 --- a/prover/prover_test.go +++ b/prover/prover_test.go @@ -21,6 +21,7 @@ import ( "github.com/taikoxyz/taiko-client/pkg/jwt" "github.com/taikoxyz/taiko-client/pkg/rpc" "github.com/taikoxyz/taiko-client/proposer" + guardianproversender "github.com/taikoxyz/taiko-client/prover/guardian_prover_sender" producer "github.com/taikoxyz/taiko-client/prover/proof_producer" "github.com/taikoxyz/taiko-client/testutils" ) @@ -80,7 +81,14 @@ func (s *ProverTestSuite) SetupTest() { proverServerUrl, ) - p.db = memorydb.New() + p.guardianProverSender = guardianproversender.NewGuardianProverBlockSender( + p.cfg.L1ProverPrivKey, + p.cfg.GuardianProverHealthCheckServerEndpoint, + memorydb.New(), + p.rpc, + p.proverAddress, + ) + s.p = p s.cancel = cancel diff --git a/prover/server/api.go b/prover/server/api.go index 1963aa88c..1a47b25de 100644 --- a/prover/server/api.go +++ b/prover/server/api.go @@ -3,7 +3,6 @@ package server import ( "math/big" "net/http" - "strconv" "time" "github.com/ethereum/go-ethereum/common" @@ -12,7 +11,6 @@ import ( "github.com/labstack/echo/v4" "github.com/taikoxyz/taiko-client/bindings/encoding" "github.com/taikoxyz/taiko-client/pkg/rpc" - "github.com/taikoxyz/taiko-client/prover/db" ) // @title Taiko Prover Server API @@ -42,7 +40,6 @@ type Status struct { MaxExpiry uint64 `json:"maxExpiry"` CurrentCapacity uint64 `json:"currentCapacity"` Prover string `json:"prover"` - HeartBeatSignature []byte `json:"heartBeatSignature"` } // GetStatus handles a query to the current prover server status. @@ -54,11 +51,6 @@ type Status struct { // @Success 200 {object} Status // @Router /status [get] func (srv *ProverServer) GetStatus(c echo.Context) error { - sig, err := crypto.Sign(crypto.Keccak256Hash([]byte("HEART_BEAT")).Bytes(), srv.proverPrivateKey) - if err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, err) - } - return c.JSON(http.StatusOK, &Status{ MinOptimisticTierFee: srv.minOptimisticTierFee.Uint64(), MinSgxTierFee: srv.minSgxTierFee.Uint64(), @@ -66,7 +58,6 @@ func (srv *ProverServer) GetStatus(c echo.Context) error { MaxExpiry: uint64(srv.maxExpiry.Seconds()), CurrentCapacity: srv.capacityManager.ReadCapacity(), Prover: srv.proverAddress.Hex(), - HeartBeatSignature: sig, }) } @@ -216,75 +207,3 @@ func (srv *ProverServer) CreateAssignment(c echo.Context) error { MaxProposedIn: srv.maxProposedIn, }) } - -// SignedBlock represents a signed block. -type SignedBlock struct { - BlockID uint64 `json:"blockID"` - BlockHash string `json:"blockHash"` - Signature string `json:"signature"` - Prover common.Address `json:"proverAddress"` -} - -// GetSignedBlocks handles a query to retrieve the most recent signed blocks from the database. -// -// @Summary Get signed blocks -// @ID get-signed-blocks -// @Accept json -// @Produce json -// @Success 200 {object} []SignedBlock -// @Router /signedBlocks [get] -func (srv *ProverServer) GetSignedBlocks(c echo.Context) error { - var signedBlocks []SignedBlock = []SignedBlock{} - - // start iterator at at provided timestamp or 0 if not defined - start := "0" - if c.QueryParam("start") != "" { - start = c.QueryParam("start") - } - - // if no start timestamp was provided, we can get the latest block, and return - // defaultNumBlocksToReturn blocks signed before latest, if our guardian prover has signed them. - if start == "0" { - latestBlock, err := srv.rpc.L2.BlockByNumber(c.Request().Context(), nil) - if err != nil { - if err != nil { - log.Error("Failed to get latest L2 block", "error", err) - return echo.NewHTTPError(http.StatusInternalServerError, err) - } - } - - // if latestBlock is greater than the number of blocks to return, we only want to return - // the most recent N blocks signed by this guardian prover. - if latestBlock.NumberU64() > defaultNumBlocksToReturn.Uint64() { - blockNum := new(big.Int).Sub(latestBlock.Number(), defaultNumBlocksToReturn) - block, err := srv.rpc.L2.BlockByNumber( - c.Request().Context(), - blockNum, - ) - if err != nil { - log.Error("Failed to get L2 block", "error", err, "blockNum", blockNum) - return echo.NewHTTPError(http.StatusInternalServerError, err) - } - - start = strconv.Itoa(int(block.Time())) - } - } - - // start should be set to a block timestamp latestBlock-numBlocksToReturn blocks ago if - // a start timestamp was not provided. - iter := srv.db.NewIterator([]byte(db.BlockKeyPrefix), []byte(start)) - defer iter.Release() - - for iter.Next() { - signedblockData := db.SignedBlockDataFromValue(iter.Value()) - - signedBlocks = append(signedBlocks, SignedBlock{ - BlockID: signedblockData.BlockID.Uint64(), - BlockHash: signedblockData.BlockHash.Hex(), - Signature: signedblockData.Signature, - Prover: srv.proverAddress, - }) - } - - return c.JSON(http.StatusOK, signedBlocks) -} diff --git a/prover/server/api_test.go b/prover/server/api_test.go index 9fc6e31d1..19f91801e 100644 --- a/prover/server/api_test.go +++ b/prover/server/api_test.go @@ -2,17 +2,13 @@ package server import ( "encoding/json" - "fmt" "io" - "math/big" "net/http" "strings" "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" "github.com/taikoxyz/taiko-client/bindings/encoding" - "github.com/taikoxyz/taiko-client/prover/db" ) func (s *ProverServerTestSuite) TestGetStatusSuccess() { @@ -31,11 +27,7 @@ func (s *ProverServerTestSuite) TestGetStatusSuccess() { s.Equal(s.s.minSgxAndPseZkevmTierFee.Uint64(), status.MinSgxTierFee) s.Equal(uint64(s.s.maxExpiry.Seconds()), status.MaxExpiry) s.Greater(status.CurrentCapacity, uint64(0)) - s.NotEmpty(status.HeartBeatSignature) - pubKey, err := crypto.SigToPub(crypto.Keccak256Hash([]byte("HEART_BEAT")).Bytes(), status.HeartBeatSignature) - s.Nil(err) s.NotEmpty(status.Prover) - s.Equal(status.Prover, crypto.PubkeyToAddress(*pubKey).Hex()) } func (s *ProverServerTestSuite) TestProposeBlockSuccess() { @@ -58,36 +50,3 @@ func (s *ProverServerTestSuite) TestProposeBlockSuccess() { s.Nil(err) s.Contains(string(b), "signedPayload") } - -func (s *ProverServerTestSuite) TestGetSignedBlocks() { - var start uint64 - // create 200, we only expect to get 100 back. - for i := 0; i < 200; i++ { - bigInt := big.NewInt(time.Now().UnixNano()) - if i == 100 { - start = bigInt.Uint64() - } - signed, err := crypto.Sign(common.BigToHash(bigInt).Bytes(), s.s.proverPrivateKey) - s.Nil(err) - key := db.BuildBlockKey(bigInt.Uint64()) - - val := db.BuildBlockValue(common.BigToHash(bigInt).Bytes(), signed, big.NewInt(1)) - - s.Nil(s.s.db.Put(key, val)) - has, err := s.s.db.Has(key) - s.Nil(err) - s.True(has) - } - - res := s.sendReq(fmt.Sprintf("/signedBlocks?start=%v", start)) - s.Equal(http.StatusOK, res.StatusCode) - - signedBlocks := make([]SignedBlock, 0) - - defer res.Body.Close() - b, err := io.ReadAll(res.Body) - s.Nil(err) - s.Nil(json.Unmarshal(b, &signedBlocks)) - - s.Equal(100, len(signedBlocks)) -} diff --git a/prover/server/server.go b/prover/server/server.go index 2b07cbcb0..c712fe291 100644 --- a/prover/server/server.go +++ b/prover/server/server.go @@ -17,10 +17,6 @@ import ( capacity "github.com/taikoxyz/taiko-client/prover/capacity_manager" ) -var ( - defaultNumBlocksToReturn = new(big.Int).SetUint64(100) -) - // @title Taiko Prover API // @version 1.0 // @termsOfService http://swagger.io/terms/ @@ -146,6 +142,5 @@ func (srv *ProverServer) configureRoutes() { srv.echo.GET("/", srv.Health) srv.echo.GET("/healthz", srv.Health) srv.echo.GET("/status", srv.GetStatus) - srv.echo.GET("/signedBlocks", srv.GetSignedBlocks) srv.echo.POST("/assignment", srv.CreateAssignment) }