From 4ab635c552de26de3ec1943b948c002442ecb88f Mon Sep 17 00:00:00 2001 From: David Date: Tue, 7 May 2024 16:01:25 +0800 Subject: [PATCH] feat(prover): introduce `TierGuardianMinority` (#777) Co-authored-by: gavin Co-authored-by: maskpp --- bindings/encoding/struct.go | 14 ++-- cmd/flags/prover.go | 20 +++-- integration_test/entrypoint.sh | 1 + integration_test/test_env.sh | 2 + internal/testutils/suite.go | 17 ++-- pkg/rpc/client.go | 65 ++++++++------- proposer/proposer.go | 4 +- prover/config.go | 9 +- prover/config_test.go | 8 +- prover/event_handler/block_proposed.go | 14 ++-- prover/event_handler/util_test.go | 2 +- prover/init.go | 13 ++- prover/proof_producer/guardian_producer.go | 13 ++- .../proof_producer/guardian_producer_test.go | 83 ++++++++++++++++++- prover/proof_submitter/proof_contester.go | 2 +- prover/proof_submitter/proof_submitter.go | 3 +- .../proof_submitter/proof_submitter_test.go | 3 +- prover/proof_submitter/transaction/builder.go | 32 ++++--- .../transaction/builder_test.go | 2 +- .../transaction/sender_test.go | 1 + prover/prover.go | 39 ++++++--- prover/prover_test.go | 29 ++++--- prover/server/api.go | 6 +- 23 files changed, 269 insertions(+), 113 deletions(-) diff --git a/bindings/encoding/struct.go b/bindings/encoding/struct.go index 1cfe974bf..e230bb94e 100644 --- a/bindings/encoding/struct.go +++ b/bindings/encoding/struct.go @@ -10,15 +10,17 @@ import ( // Tier IDs defined in protocol. var ( - TierOptimisticID uint16 = 100 - TierSgxID uint16 = 200 - TierSgxAndZkVMID uint16 = 300 - TierGuardianID uint16 = 1000 - ProtocolTiers = []uint16{ + TierOptimisticID uint16 = 100 + TierSgxID uint16 = 200 + TierSgxAndZkVMID uint16 = 300 + TierGuardianMinorityID uint16 = 900 + TierGuardianMajorityID uint16 = 1000 + ProtocolTiers = []uint16{ TierOptimisticID, TierSgxID, TierSgxAndZkVMID, - TierGuardianID, + TierGuardianMinorityID, + TierGuardianMajorityID, } GoldenTouchPrivKey = "92954368afd3caa1f3ce3ead0069c1af414054aefe1ef9aeacc1bf426222ce38" ) diff --git a/cmd/flags/prover.go b/cmd/flags/prover.go index e095b439f..1661fbb69 100644 --- a/cmd/flags/prover.go +++ b/cmd/flags/prover.go @@ -4,6 +4,8 @@ import ( "time" "github.com/urfave/cli/v2" + + "github.com/taikoxyz/taiko-client/pkg/rpc" ) // Required flags used by prover. @@ -165,11 +167,18 @@ var ( EnvVars: []string{"PROVER_GUARDIAN_PROVER_HEALTH_CHECK_SERVER_ENDPOINT"}, } // Guardian prover specific flag - GuardianProver = &cli.StringFlag{ - Name: "guardianProver", - Usage: "GuardianProver contract `address`", + GuardianProverMinority = &cli.StringFlag{ + Name: "guardianProverMinority", + Usage: "GuardianProverMinority contract `address`", + Value: rpc.ZeroAddress.Hex(), + Category: proverCategory, + EnvVars: []string{"GUARDIAN_PROVER_MINORITY"}, + } + GuardianProverMajority = &cli.StringFlag{ + Name: "guardianProverMajority", + Usage: "GuardianProverMajority contract `address`", Category: proverCategory, - EnvVars: []string{"GUARDIAN_PROVER"}, + EnvVars: []string{"GUARDIAN_PROVER_MAJORITY"}, } GuardianProofSubmissionDelay = &cli.DurationFlag{ Name: "guardian.submissionDelay", @@ -225,7 +234,8 @@ var ProverFlags = MergeFlags(CommonFlags, []cli.Flag{ MinTaikoTokenBalance, StartingBlockID, Dummy, - GuardianProver, + GuardianProverMinority, + GuardianProverMajority, GuardianProofSubmissionDelay, GuardianProverHealthCheckServerEndpoint, Graffiti, diff --git a/integration_test/entrypoint.sh b/integration_test/entrypoint.sh index abc478791..24fd06227 100755 --- a/integration_test/entrypoint.sh +++ b/integration_test/entrypoint.sh @@ -34,6 +34,7 @@ check_env "ASSIGNMENT_HOOK_ADDRESS" check_env "TIMELOCK_CONTROLLER" check_env "ROLLUP_ADDRESS_MANAGER_CONTRACT_ADDRESS" check_env "GUARDIAN_PROVER_CONTRACT_ADDRESS" +check_env "GUARDIAN_PROVER_MINORITY_ADDRESS" check_env "L1_CONTRACT_OWNER_PRIVATE_KEY" check_env "L1_SECURITY_COUNCIL_PRIVATE_KEY" check_env "L1_PROPOSER_PRIVATE_KEY" diff --git a/integration_test/test_env.sh b/integration_test/test_env.sh index b106695fd..77b155f27 100755 --- a/integration_test/test_env.sh +++ b/integration_test/test_env.sh @@ -15,6 +15,7 @@ export ASSIGNMENT_HOOK_ADDRESS=$(echo "$DEPLOYMENT_JSON" | jq '.assignment_hook' export TIMELOCK_CONTROLLER=$(echo "$DEPLOYMENT_JSON" | jq '.timelock_controller' | sed 's/\"//g') export ROLLUP_ADDRESS_MANAGER_CONTRACT_ADDRESS=$(echo "$DEPLOYMENT_JSON" | jq '.rollup_address_manager' | sed 's/\"//g') export GUARDIAN_PROVER_CONTRACT_ADDRESS=$(echo "$DEPLOYMENT_JSON" | jq '.guardian_prover' | sed 's/\"//g') +export GUARDIAN_PROVER_MINORITY_ADDRESS=$(echo "$DEPLOYMENT_JSON" | jq '.guardian_prover_minority' | sed 's/\"//g') export L1_CONTRACT_OWNER_PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 export L1_SECURITY_COUNCIL_PRIVATE_KEY=0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97 export L1_PROPOSER_PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 @@ -40,6 +41,7 @@ ASSIGNMENT_HOOK_ADDRESS=$ASSIGNMENT_HOOK_ADDRESS TIMELOCK_CONTROLLER=$TIMELOCK_CONTROLLER ROLLUP_ADDRESS_MANAGER_CONTRACT_ADDRESS=$ROLLUP_ADDRESS_MANAGER_CONTRACT_ADDRESS GUARDIAN_PROVER_CONTRACT_ADDRESS=$GUARDIAN_PROVER_CONTRACT_ADDRESS +GUARDIAN_PROVER_MINORITY_ADDRESS=$GUARDIAN_PROVER_MINORITY_ADDRESS L1_CONTRACT_OWNER_PRIVATE_KEY=$L1_CONTRACT_OWNER_PRIVATE_KEY L1_SECURITY_COUNCIL_PRIVATE_KEY=$L1_SECURITY_COUNCIL_PRIVATE_KEY L1_PROPOSER_PRIVATE_KEY=$L1_PROPOSER_PRIVATE_KEY diff --git a/internal/testutils/suite.go b/internal/testutils/suite.go index 0e5e31fff..d4ae6cd8a 100644 --- a/internal/testutils/suite.go +++ b/internal/testutils/suite.go @@ -56,14 +56,15 @@ func (s *ClientTestSuite) SetupTest() { s.NotEmpty(jwtSecret) rpcCli, err := rpc.NewClient(context.Background(), &rpc.ClientConfig{ - 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")), - GuardianProverAddress: common.HexToAddress(os.Getenv("GUARDIAN_PROVER_CONTRACT_ADDRESS")), - L2EngineEndpoint: os.Getenv("L2_EXECUTION_ENGINE_AUTH_ENDPOINT"), - JwtSecret: string(jwtSecret), + 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")), + GuardianProverMajorityAddress: common.HexToAddress(os.Getenv("GUARDIAN_PROVER_CONTRACT_ADDRESS")), + GuardianProverMinorityAddress: common.HexToAddress(os.Getenv("GUARDIAN_PROVER_MINORITY_ADDRESS")), + L2EngineEndpoint: os.Getenv("L2_EXECUTION_ENGINE_AUTH_ENDPOINT"), + JwtSecret: string(jwtSecret), }) s.Nil(err) s.RPCClient = rpcCli diff --git a/pkg/rpc/client.go b/pkg/rpc/client.go index 4b431b4e2..d91d26f29 100644 --- a/pkg/rpc/client.go +++ b/pkg/rpc/client.go @@ -27,27 +27,29 @@ type Client struct { // Beacon clients L1Beacon *BeaconClient // Protocol contracts clients - TaikoL1 *bindings.TaikoL1Client - TaikoL2 *bindings.TaikoL2Client - TaikoToken *bindings.TaikoToken - GuardianProver *bindings.GuardianProver + TaikoL1 *bindings.TaikoL1Client + TaikoL2 *bindings.TaikoL2Client + TaikoToken *bindings.TaikoToken + GuardianProverMajority *bindings.GuardianProver + GuardianProverMinority *bindings.GuardianProver } // ClientConfig contains all configs which will be used to initializing an // RPC client. If not providing L2EngineEndpoint or JwtSecret, then the L2Engine client // won't be initialized. type ClientConfig struct { - L1Endpoint string - L2Endpoint string - L1BeaconEndpoint string - L2CheckPoint string - TaikoL1Address common.Address - TaikoL2Address common.Address - TaikoTokenAddress common.Address - GuardianProverAddress common.Address - L2EngineEndpoint string - JwtSecret string - Timeout time.Duration + L1Endpoint string + L2Endpoint string + L1BeaconEndpoint string + L2CheckPoint string + TaikoL1Address common.Address + TaikoL2Address common.Address + TaikoTokenAddress common.Address + GuardianProverMinorityAddress common.Address + GuardianProverMajorityAddress common.Address + L2EngineEndpoint string + JwtSecret string + Timeout time.Duration } // NewClient initializes all RPC clients used by Taiko client software. @@ -110,16 +112,22 @@ func NewClient(ctx context.Context, cfg *ClientConfig) (*Client, error) { } var ( - taikoToken *bindings.TaikoToken - guardianProver *bindings.GuardianProver + taikoToken *bindings.TaikoToken + guardianProverMajority *bindings.GuardianProver + guardianProverMinority *bindings.GuardianProver ) if cfg.TaikoTokenAddress.Hex() != ZeroAddress.Hex() { if taikoToken, err = bindings.NewTaikoToken(cfg.TaikoTokenAddress, l1Client); err != nil { return nil, err } } - if cfg.GuardianProverAddress.Hex() != ZeroAddress.Hex() { - if guardianProver, err = bindings.NewGuardianProver(cfg.GuardianProverAddress, l1Client); err != nil { + if cfg.GuardianProverMinorityAddress.Hex() != ZeroAddress.Hex() { + if guardianProverMinority, err = bindings.NewGuardianProver(cfg.GuardianProverMinorityAddress, l1Client); err != nil { + return nil, err + } + } + if cfg.GuardianProverMajorityAddress.Hex() != ZeroAddress.Hex() { + if guardianProverMajority, err = bindings.NewGuardianProver(cfg.GuardianProverMajorityAddress, l1Client); err != nil { return nil, err } } @@ -135,15 +143,16 @@ func NewClient(ctx context.Context, cfg *ClientConfig) (*Client, error) { } client := &Client{ - L1: l1Client, - L1Beacon: l1BeaconClient, - L2: l2Client, - L2CheckPoint: l2CheckPoint, - L2Engine: l2AuthClient, - TaikoL1: taikoL1, - TaikoL2: taikoL2, - TaikoToken: taikoToken, - GuardianProver: guardianProver, + L1: l1Client, + L1Beacon: l1BeaconClient, + L2: l2Client, + L2CheckPoint: l2CheckPoint, + L2Engine: l2AuthClient, + TaikoL1: taikoL1, + TaikoL2: taikoL2, + TaikoToken: taikoToken, + GuardianProverMajority: guardianProverMajority, + GuardianProverMinority: guardianProverMinority, } if err := client.ensureGenesisMatched(ctxWithTimeout); err != nil { diff --git a/proposer/proposer.go b/proposer/proposer.go index 3e2d231d2..20a411c46 100644 --- a/proposer/proposer.go +++ b/proposer/proposer.go @@ -412,7 +412,9 @@ func (p *Proposer) initTierFees() error { p.tierFees = append(p.tierFees, encoding.TierFee{Tier: tier.ID, Fee: p.OptimisticTierFee}) case encoding.TierSgxID: p.tierFees = append(p.tierFees, encoding.TierFee{Tier: tier.ID, Fee: p.SgxTierFee}) - case encoding.TierGuardianID: + case encoding.TierGuardianMinorityID: + p.tierFees = append(p.tierFees, encoding.TierFee{Tier: tier.ID, Fee: common.Big0}) + case encoding.TierGuardianMajorityID: // Guardian prover should not charge any fee. p.tierFees = append(p.tierFees, encoding.TierFee{Tier: tier.ID, Fee: common.Big0}) default: diff --git a/prover/config.go b/prover/config.go index 7f78e4128..992779383 100644 --- a/prover/config.go +++ b/prover/config.go @@ -33,7 +33,8 @@ type Config struct { L1ProverPrivKey *ecdsa.PrivateKey StartingBlockID *big.Int Dummy bool - GuardianProverAddress common.Address + GuardianProverMinorityAddress common.Address + GuardianProverMajorityAddress common.Address GuardianProofSubmissionDelay time.Duration Graffiti string BackOffMaxRetries uint64 @@ -101,7 +102,7 @@ func NewConfigFromCliContext(c *cli.Context) (*Config, error) { } // If we are running a guardian prover, we need to prove unassigned blocks and run in contester mode by default. - if c.IsSet(flags.GuardianProver.Name) { + if c.IsSet(flags.GuardianProverMajority.Name) { if err := c.Set(flags.ProveUnassignedBlocks.Name, "true"); err != nil { return nil, err } @@ -117,7 +118,6 @@ func NewConfigFromCliContext(c *cli.Context) (*Config, error) { return nil, errors.New("--prover.l2NodeVersion flag is required if guardian prover is set") } } - var ( raikoL1Endpoint = c.String(flags.RaikoL1Endpoint.Name) raikoL1BeaconEndpoint = c.String(flags.RaikoL1BeaconEndpoint.Name) @@ -175,7 +175,8 @@ func NewConfigFromCliContext(c *cli.Context) (*Config, error) { RaikoL2Endpoint: raikoL2Endpoint, StartingBlockID: startingBlockID, Dummy: c.Bool(flags.Dummy.Name), - GuardianProverAddress: common.HexToAddress(c.String(flags.GuardianProver.Name)), + GuardianProverMinorityAddress: common.HexToAddress(c.String(flags.GuardianProverMinority.Name)), + GuardianProverMajorityAddress: common.HexToAddress(c.String(flags.GuardianProverMajority.Name)), GuardianProofSubmissionDelay: c.Duration(flags.GuardianProofSubmissionDelay.Name), GuardianProverHealthCheckServerEndpoint: guardianProverHealthCheckServerEndpoint, Graffiti: c.String(flags.Graffiti.Name), diff --git a/prover/config_test.go b/prover/config_test.go index 3d8da7f1d..6175cbba6 100644 --- a/prover/config_test.go +++ b/prover/config_test.go @@ -84,7 +84,8 @@ func (s *ProverTestSuite) TestNewConfigFromCliContextGuardianProver() { "--" + flags.MinOptimisticTierFee.Name, fmt.Sprint(minTierFee), "--" + flags.MinSgxTierFee.Name, fmt.Sprint(minTierFee), "--" + flags.ProverCapacity.Name, "8", - "--" + flags.GuardianProver.Name, os.Getenv("GUARDIAN_PROVER_CONTRACT_ADDRESS"), + "--" + flags.GuardianProverMajority.Name, os.Getenv("GUARDIAN_PROVER_CONTRACT_ADDRESS"), + "--" + flags.GuardianProverMinority.Name, os.Getenv("GUARDIAN_PROVER_MINORITY_ADDRESS"), "--" + flags.AssignmentHookAddress.Name, os.Getenv("ASSIGNMENT_HOOK_ADDRESS"), "--" + flags.Graffiti.Name, "", "--" + flags.ProveUnassignedBlocks.Name, @@ -92,6 +93,7 @@ func (s *ProverTestSuite) TestNewConfigFromCliContextGuardianProver() { "--" + flags.Allowance.Name, fmt.Sprint(allowance), "--" + flags.L1NodeVersion.Name, l1NodeVersion, "--" + flags.L2NodeVersion.Name, l2NodeVersion, + "--" + flags.RaikoHostEndpoint.Name, "https://dummy.raiko.xyz", })) } @@ -117,7 +119,8 @@ func (s *ProverTestSuite) SetupApp() *cli.App { &cli.StringFlag{Name: flags.L1ProverPrivKey.Name}, &cli.Uint64Flag{Name: flags.StartingBlockID.Name}, &cli.BoolFlag{Name: flags.Dummy.Name}, - &cli.StringFlag{Name: flags.GuardianProver.Name}, + &cli.StringFlag{Name: flags.GuardianProverMajority.Name}, + &cli.StringFlag{Name: flags.GuardianProverMinority.Name}, &cli.StringFlag{Name: flags.Graffiti.Name}, &cli.BoolFlag{Name: flags.ProveUnassignedBlocks.Name}, &cli.DurationFlag{Name: flags.RPCTimeout.Name}, @@ -130,6 +133,7 @@ func (s *ProverTestSuite) SetupApp() *cli.App { &cli.StringFlag{Name: flags.ContesterMode.Name}, &cli.StringFlag{Name: flags.L1NodeVersion.Name}, &cli.StringFlag{Name: flags.L2NodeVersion.Name}, + &cli.StringFlag{Name: flags.RaikoHostEndpoint.Name}, } app.Flags = append(app.Flags, flags.TxmgrFlags...) app.Action = func(ctx *cli.Context) error { diff --git a/prover/event_handler/block_proposed.go b/prover/event_handler/block_proposed.go index 7192be5a2..492180d51 100644 --- a/prover/event_handler/block_proposed.go +++ b/prover/event_handler/block_proposed.go @@ -43,8 +43,9 @@ type BlockProposedEventHandler struct { backOffMaxRetrys uint64 contesterMode bool proveUnassignedBlocks bool - tierToOverride uint16 - submissionDelay time.Duration + // Guardian prover related. + isGuardian bool + submissionDelay time.Duration } // NewBlockProposedEventHandlerOps is the options for creating a new BlockProposedEventHandler. @@ -79,7 +80,7 @@ func NewBlockProposedEventHandler(opts *NewBlockProposedEventHandlerOps) *BlockP opts.BackOffMaxRetrys, opts.ContesterMode, opts.ProveUnassignedBlocks, - 0, + false, opts.SubmissionDelay, } } @@ -347,8 +348,8 @@ func (h *BlockProposedEventHandler) checkExpirationAndSubmitProof( return err } - if h.tierToOverride != 0 { - tier = h.tierToOverride + if h.isGuardian { + tier = encoding.TierGuardianMinorityID } log.Info( @@ -388,8 +389,7 @@ func NewBlockProposedEventGuardianHandler( opts *NewBlockProposedGuardianEventHandlerOps, ) *BlockProposedGuaridanEventHandler { blockProposedEventHandler := NewBlockProposedEventHandler(opts.NewBlockProposedEventHandlerOps) - // For guardian provers, we only send top tier proofs. - blockProposedEventHandler.tierToOverride = encoding.TierGuardianID + blockProposedEventHandler.isGuardian = true return &BlockProposedGuaridanEventHandler{ BlockProposedEventHandler: blockProposedEventHandler, diff --git a/prover/event_handler/util_test.go b/prover/event_handler/util_test.go index 3e34b5d35..5c2e19965 100644 --- a/prover/event_handler/util_test.go +++ b/prover/event_handler/util_test.go @@ -20,7 +20,7 @@ type ProverEventHandlerTestSuite struct { func (s *ProverEventHandlerTestSuite) TestGetProvingWindowNotFound() { _, err := getProvingWindow(&bindings.TaikoL1ClientBlockProposed{ Meta: bindings.TaikoDataBlockMetadata{ - MinTier: encoding.TierGuardianID + 1, + MinTier: encoding.TierGuardianMajorityID + 1, }, }, []*rpc.TierProviderTierWithID{}) s.ErrorIs(err, errTierNotFound) diff --git a/prover/init.go b/prover/init.go index 5e8dec1e0..02b724ca4 100644 --- a/prover/init.go +++ b/prover/init.go @@ -113,7 +113,7 @@ func (p *Prover) initProofSubmitters( ProofType: proofProducer.ProofTypeSgx, Dummy: p.cfg.Dummy, } - case encoding.TierGuardianID: + case encoding.TierGuardianMinorityID: producer = proofProducer.NewGuardianProofProducer(&proofProducer.SGXProofProducer{ RaikoHostEndpoint: p.cfg.RaikoHostEndpoint, L1Endpoint: p.cfg.RaikoL1Endpoint, @@ -121,7 +121,16 @@ func (p *Prover) initProofSubmitters( L2Endpoint: p.cfg.RaikoL2Endpoint, ProofType: proofProducer.ProofTypeCPU, Dummy: p.cfg.Dummy, - }, p.cfg.EnableLivenessBondProof) + }, encoding.TierGuardianMinorityID, p.cfg.EnableLivenessBondProof) + case encoding.TierGuardianMajorityID: + producer = proofProducer.NewGuardianProofProducer(&proofProducer.SGXProofProducer{ + RaikoHostEndpoint: p.cfg.RaikoHostEndpoint, + L1Endpoint: p.cfg.RaikoL1Endpoint, + L1BeaconEndpoint: p.cfg.RaikoL1BeaconEndpoint, + L2Endpoint: p.cfg.RaikoL2Endpoint, + ProofType: proofProducer.ProofTypeCPU, + Dummy: p.cfg.Dummy, + }, encoding.TierGuardianMajorityID, p.cfg.EnableLivenessBondProof) default: return fmt.Errorf("unsupported tier: %d", tier.ID) } diff --git a/prover/proof_producer/guardian_producer.go b/prover/proof_producer/guardian_producer.go index 6385a98d2..a6b196713 100644 --- a/prover/proof_producer/guardian_producer.go +++ b/prover/proof_producer/guardian_producer.go @@ -9,19 +9,24 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/taikoxyz/taiko-client/bindings" - "github.com/taikoxyz/taiko-client/bindings/encoding" ) // GuardianProofProducer always returns an optimistic (dummy) proof. type GuardianProofProducer struct { returnLivenessBond bool + tier uint16 *SGXProofProducer } -func NewGuardianProofProducer(sgxProofProducer *SGXProofProducer, returnLivenessBond bool) *GuardianProofProducer { +func NewGuardianProofProducer( + sgxProofProducer *SGXProofProducer, + tier uint16, + returnLivenessBond bool, +) *GuardianProofProducer { return &GuardianProofProducer{ SGXProofProducer: sgxProofProducer, returnLivenessBond: returnLivenessBond, + tier: tier, } } @@ -48,7 +53,7 @@ func (g *GuardianProofProducer) RequestProof( Header: header, Proof: crypto.Keccak256([]byte("RETURN_LIVENESS_BOND")), Opts: opts, - Tier: g.Tier(), + Tier: g.tier, }, nil } @@ -64,5 +69,5 @@ func (g *GuardianProofProducer) RequestProof( // Tier implements the ProofProducer interface. func (g *GuardianProofProducer) Tier() uint16 { - return encoding.TierGuardianID + return g.tier } diff --git a/prover/proof_producer/guardian_producer_test.go b/prover/proof_producer/guardian_producer_test.go index 6188bd325..7f295b8dc 100644 --- a/prover/proof_producer/guardian_producer_test.go +++ b/prover/proof_producer/guardian_producer_test.go @@ -33,7 +33,7 @@ func TestGuardianProducerRequestProof(t *testing.T) { } var ( - producer = NewGuardianProofProducer(&SGXProofProducer{Dummy: true}, false) + producer = NewGuardianProofProducer(&SGXProofProducer{Dummy: true}, encoding.TierGuardianMajorityID, false) blockID = common.Big32 ) res, err := producer.RequestProof( @@ -47,7 +47,7 @@ func TestGuardianProducerRequestProof(t *testing.T) { require.Equal(t, res.BlockID, blockID) require.Equal(t, res.Header, header) - require.Equal(t, res.Tier, encoding.TierGuardianID) + require.Equal(t, res.Tier, encoding.TierGuardianMajorityID) require.NotEmpty(t, res.Proof) } @@ -70,7 +70,7 @@ func TestGuardianProducerRequestProofReturnLivenessBond(t *testing.T) { } var ( - producer = NewGuardianProofProducer(&SGXProofProducer{Dummy: true}, true) + producer = NewGuardianProofProducer(&SGXProofProducer{Dummy: true}, encoding.TierGuardianMajorityID, true) blockID = common.Big32 ) res, err := producer.RequestProof( @@ -84,7 +84,82 @@ func TestGuardianProducerRequestProofReturnLivenessBond(t *testing.T) { require.Equal(t, res.BlockID, blockID) require.Equal(t, res.Header, header) - require.Equal(t, res.Tier, encoding.TierGuardianID) + require.Equal(t, res.Tier, encoding.TierGuardianMajorityID) + require.NotEmpty(t, res.Proof) + require.Equal(t, res.Proof, crypto.Keccak256([]byte("RETURN_LIVENESS_BOND"))) +} + +func TestMinorityRequestProof(t *testing.T) { + header := &types.Header{ + ParentHash: randHash(), + UncleHash: randHash(), + Coinbase: common.BytesToAddress(randHash().Bytes()), + Root: randHash(), + TxHash: randHash(), + ReceiptHash: randHash(), + Difficulty: common.Big0, + Number: common.Big256, + GasLimit: 1024, + GasUsed: 1024, + Time: uint64(time.Now().Unix()), + Extra: randHash().Bytes(), + MixDigest: randHash(), + Nonce: types.BlockNonce{}, + } + + var ( + producer = NewGuardianProofProducer(&SGXProofProducer{Dummy: true}, encoding.TierGuardianMinorityID, false) + blockID = common.Big32 + ) + res, err := producer.RequestProof( + context.Background(), + &ProofRequestOptions{}, + blockID, + &bindings.TaikoDataBlockMetadata{}, + header, + ) + require.Nil(t, err) + + require.Equal(t, res.BlockID, blockID) + require.Equal(t, res.Header, header) + require.Equal(t, res.Tier, encoding.TierGuardianMinorityID) + require.NotEmpty(t, res.Proof) +} + +func TestRequestMinorityProofReturnLivenessBond(t *testing.T) { + header := &types.Header{ + ParentHash: randHash(), + UncleHash: randHash(), + Coinbase: common.BytesToAddress(randHash().Bytes()), + Root: randHash(), + TxHash: randHash(), + ReceiptHash: randHash(), + Difficulty: common.Big0, + Number: common.Big256, + GasLimit: 1024, + GasUsed: 1024, + Time: uint64(time.Now().Unix()), + Extra: randHash().Bytes(), + MixDigest: randHash(), + Nonce: types.BlockNonce{}, + } + + var ( + producer = NewGuardianProofProducer(&SGXProofProducer{Dummy: true}, encoding.TierGuardianMinorityID, true) + blockID = common.Big32 + ) + res, err := producer.RequestProof( + context.Background(), + &ProofRequestOptions{}, + blockID, + &bindings.TaikoDataBlockMetadata{}, + header, + ) + require.Nil(t, err) + + require.Equal(t, res.BlockID, blockID) + require.Equal(t, res.Header, header) + require.Equal(t, res.Tier, encoding.TierGuardianMinorityID) require.NotEmpty(t, res.Proof) require.Equal(t, res.Proof, crypto.Keccak256([]byte("RETURN_LIVENESS_BOND"))) } diff --git a/prover/proof_submitter/proof_contester.go b/prover/proof_submitter/proof_contester.go index b073b26ed..ec0a133cc 100644 --- a/prover/proof_submitter/proof_contester.go +++ b/prover/proof_submitter/proof_contester.go @@ -118,7 +118,7 @@ func (c *ProofContester) SubmitContest( Tier: transition.Tier, Data: []byte{}, }, - false, + tier, ), ) } diff --git a/prover/proof_submitter/proof_submitter.go b/prover/proof_submitter/proof_submitter.go index c8b015d9f..93e35a154 100644 --- a/prover/proof_submitter/proof_submitter.go +++ b/prover/proof_submitter/proof_submitter.go @@ -11,7 +11,6 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/taikoxyz/taiko-client/bindings" - "github.com/taikoxyz/taiko-client/bindings/encoding" "github.com/taikoxyz/taiko-client/internal/metrics" "github.com/taikoxyz/taiko-client/pkg/rpc" validator "github.com/taikoxyz/taiko-client/prover/anchor_tx_validator" @@ -170,7 +169,7 @@ func (s *ProofSubmitter) SubmitProof( Tier: proofWithHeader.Tier, Data: proofWithHeader.Proof, }, - proofWithHeader.Tier == encoding.TierGuardianID, + proofWithHeader.Tier, ), ); err != nil { if err.Error() == transaction.ErrUnretryableSubmission.Error() { diff --git a/prover/proof_submitter/proof_submitter_test.go b/prover/proof_submitter/proof_submitter_test.go index f6d8413f8..3355fd6bb 100644 --- a/prover/proof_submitter/proof_submitter_test.go +++ b/prover/proof_submitter/proof_submitter_test.go @@ -45,6 +45,7 @@ func (s *ProofSubmitterTestSuite) SetupTest() { s.RPCClient, common.HexToAddress(os.Getenv("TAIKO_L1_ADDRESS")), common.HexToAddress(os.Getenv("GUARDIAN_PROVER_CONTRACT_ADDRESS")), + common.HexToAddress(os.Getenv("GUARDIAN_PROVER_MINORITY_ADDRESS")), ) l1ProverPrivKey, err := crypto.ToECDSA(common.FromHex(os.Getenv("L1_PROVER_PRIVATE_KEY"))) @@ -192,7 +193,7 @@ func (s *ProofSubmitterTestSuite) TestGuardianSubmitProofs() { for _, e := range events { s.Nil(s.submitter.RequestProof(context.Background(), e)) proofWithHeader := <-s.proofCh - proofWithHeader.Tier = encoding.TierGuardianID + proofWithHeader.Tier = encoding.TierGuardianMajorityID s.Nil(s.submitter.SubmitProof(context.Background(), proofWithHeader)) } } diff --git a/prover/proof_submitter/transaction/builder.go b/prover/proof_submitter/transaction/builder.go index 13a2b388c..7614055aa 100644 --- a/prover/proof_submitter/transaction/builder.go +++ b/prover/proof_submitter/transaction/builder.go @@ -2,6 +2,7 @@ package transaction import ( "errors" + "fmt" "math/big" "github.com/ethereum-optimism/optimism/op-service/txmgr" @@ -16,6 +17,7 @@ import ( var ( ErrUnretryableSubmission = errors.New("unretryable submission error") + ZeroAddress common.Address ) // TxBuilder will build a transaction with the given nonce. @@ -23,18 +25,20 @@ type TxBuilder func(txOpts *bind.TransactOpts) (*txmgr.TxCandidate, error) // ProveBlockTxBuilder is responsible for building ProveBlock transactions. type ProveBlockTxBuilder struct { - rpc *rpc.Client - taikoL1Address common.Address - guardianProverAddress common.Address + rpc *rpc.Client + taikoL1Address common.Address + guardianProverMajorityAddress common.Address + guardianProverMinorityAddress common.Address } // NewProveBlockTxBuilder creates a new ProveBlockTxBuilder instance. func NewProveBlockTxBuilder( rpc *rpc.Client, taikoL1Address common.Address, - guardianProverAddress common.Address, + guardianProverMajorityAddress common.Address, + guardianProverMinorityAddress common.Address, ) *ProveBlockTxBuilder { - return &ProveBlockTxBuilder{rpc, taikoL1Address, guardianProverAddress} + return &ProveBlockTxBuilder{rpc, taikoL1Address, guardianProverMajorityAddress, guardianProverMinorityAddress} } // Build creates a new TaikoL1.ProveBlock transaction with the given nonce. @@ -43,13 +47,14 @@ func (a *ProveBlockTxBuilder) Build( meta *bindings.TaikoDataBlockMetadata, transition *bindings.TaikoDataTransition, tierProof *bindings.TaikoDataTierProof, - guardian bool, + tier uint16, ) TxBuilder { return func(txOpts *bind.TransactOpts) (*txmgr.TxCandidate, error) { var ( - data []byte - to common.Address - err error + data []byte + to common.Address + err error + guardian = tier >= encoding.TierGuardianMinorityID ) log.Info( @@ -73,8 +78,13 @@ func (a *ProveBlockTxBuilder) Build( return nil, ErrUnretryableSubmission } } else { - to = a.guardianProverAddress - + if tier > encoding.TierGuardianMinorityID { + to = a.guardianProverMajorityAddress + } else if tier == encoding.TierGuardianMinorityID && a.guardianProverMinorityAddress != ZeroAddress { + to = a.guardianProverMinorityAddress + } else { + return nil, fmt.Errorf("tier %d need set guardianProverMinorityAddress", tier) + } if data, err = encoding.GuardianProverABI.Pack("approve", *meta, *transition, *tierProof); err != nil { if isSubmitProofTxErrorRetryable(err, blockID) { return nil, err diff --git a/prover/proof_submitter/transaction/builder_test.go b/prover/proof_submitter/transaction/builder_test.go index 389504209..4ddbed027 100644 --- a/prover/proof_submitter/transaction/builder_test.go +++ b/prover/proof_submitter/transaction/builder_test.go @@ -13,7 +13,7 @@ func (s *TransactionTestSuite) TestBuildTxs() { &bindings.TaikoDataBlockMetadata{}, &bindings.TaikoDataTransition{}, &bindings.TaikoDataTierProof{}, - false, + 1, )(&bind.TransactOpts{Nonce: common.Big0, GasLimit: 0, GasTipCap: common.Big0}) s.Nil(err) } diff --git a/prover/proof_submitter/transaction/sender_test.go b/prover/proof_submitter/transaction/sender_test.go index d3f9db5ee..1adb83234 100644 --- a/prover/proof_submitter/transaction/sender_test.go +++ b/prover/proof_submitter/transaction/sender_test.go @@ -40,6 +40,7 @@ func (s *TransactionTestSuite) SetupTest() { s.RPCClient, common.HexToAddress(os.Getenv("TAIKO_L1_ADDRESS")), common.HexToAddress(os.Getenv("GUARDIAN_PROVER_CONTRACT_ADDRESS")), + common.HexToAddress(os.Getenv("GUARDIAN_PROVER_MINORITY_ADDRESS")), ) l1ProverPrivKey, err := crypto.ToECDSA(common.FromHex(os.Getenv("L1_PROVER_PRIVATE_KEY"))) diff --git a/prover/prover.go b/prover/prover.go index a2c56ae09..6647e4676 100644 --- a/prover/prover.go +++ b/prover/prover.go @@ -105,13 +105,14 @@ func InitFromConfig(ctx context.Context, p *Prover, cfg *Config) (err error) { // Clients if p.rpc, err = rpc.NewClient(p.ctx, &rpc.ClientConfig{ - L1Endpoint: cfg.L1WsEndpoint, - L2Endpoint: cfg.L2WsEndpoint, - TaikoL1Address: cfg.TaikoL1Address, - TaikoL2Address: cfg.TaikoL2Address, - TaikoTokenAddress: cfg.TaikoTokenAddress, - GuardianProverAddress: cfg.GuardianProverAddress, - Timeout: cfg.RPCTimeout, + L1Endpoint: cfg.L1WsEndpoint, + L2Endpoint: cfg.L2WsEndpoint, + TaikoL1Address: cfg.TaikoL1Address, + TaikoL2Address: cfg.TaikoL2Address, + TaikoTokenAddress: cfg.TaikoTokenAddress, + GuardianProverMinorityAddress: cfg.GuardianProverMinorityAddress, + GuardianProverMajorityAddress: cfg.GuardianProverMajorityAddress, + Timeout: cfg.RPCTimeout, }); err != nil { return err } @@ -143,7 +144,11 @@ func InitFromConfig(ctx context.Context, p *Prover, cfg *Config) (err error) { } p.sharedState.SetTiers(tiers) - txBuilder := transaction.NewProveBlockTxBuilder(p.rpc, p.cfg.TaikoL1Address, p.cfg.GuardianProverAddress) + txBuilder := transaction.NewProveBlockTxBuilder( + p.rpc, p.cfg.TaikoL1Address, + p.cfg.GuardianProverMajorityAddress, + p.cfg.GuardianProverMinorityAddress, + ) if p.txmgr, err = txmgr.NewSimpleTxManager( "prover", @@ -190,8 +195,14 @@ func InitFromConfig(ctx context.Context, p *Prover, cfg *Config) (err error) { // Guardian prover heartbeat sender if p.IsGuardianProver() && p.cfg.GuardianProverHealthCheckServerEndpoint != nil { // Check guardian prover contract address is correct. - if _, err := p.rpc.GuardianProver.MinGuardians(&bind.CallOpts{Context: ctx}); err != nil { - return fmt.Errorf("failed to get MinGuardians from guardian prover contract: %w", err) + if _, err := p.rpc.GuardianProverMajority.MinGuardians(&bind.CallOpts{Context: ctx}); err != nil { + return fmt.Errorf("failed to get MinGuardians from majority guardian prover contract: %w", err) + } + + if p.rpc.GuardianProverMinority != nil { + if _, err := p.rpc.GuardianProverMinority.MinGuardians(&bind.CallOpts{Context: ctx}); err != nil { + return fmt.Errorf("failed to get MinGuardians from minority guardian prover contract: %w", err) + } } p.guardianProverHeartbeater = guardianProverHeartbeater.New( @@ -378,7 +389,11 @@ func (p *Prover) contestProofOp(req *proofProducer.ContestRequestBody) error { // requestProofOp requests a new proof generation operation. func (p *Prover) requestProofOp(e *bindings.TaikoL1ClientBlockProposed, minTier uint16) error { if p.IsGuardianProver() { - minTier = encoding.TierGuardianID + if minTier > encoding.TierGuardianMinorityID { + minTier = encoding.TierGuardianMajorityID + } else { + minTier = encoding.TierGuardianMinorityID + } } if submitter := p.selectSubmitter(minTier); submitter != nil { if err := submitter.RequestProof(p.ctx, e); err != nil { @@ -456,7 +471,7 @@ func (p *Prover) getSubmitterByTier(tier uint16) proofSubmitter.Submitter { // IsGuardianProver returns true if the current prover is a guardian prover. func (p *Prover) IsGuardianProver() bool { - return p.cfg.GuardianProverAddress != common.Address{} + return p.cfg.GuardianProverMajorityAddress != common.Address{} } // ProverAddress returns the current prover account address. diff --git a/prover/prover_test.go b/prover/prover_test.go index 672f43c4c..3127c3af1 100644 --- a/prover/prover_test.go +++ b/prover/prover_test.go @@ -298,18 +298,23 @@ func (s *ProverTestSuite) TestContestWrongBlocks() { s.Nil(s.p.transitionContestedHandler.Handle(context.Background(), contestedEvent)) - s.p.cfg.GuardianProverAddress = common.HexToAddress(os.Getenv("GUARDIAN_PROVER_CONTRACT_ADDRESS")) + s.p.cfg.GuardianProverMajorityAddress = common.HexToAddress(os.Getenv("GUARDIAN_PROVER_CONTRACT_ADDRESS")) s.True(s.p.IsGuardianProver()) - txBuilder := transaction.NewProveBlockTxBuilder(s.p.rpc, s.p.cfg.TaikoL1Address, s.p.cfg.GuardianProverAddress) + txBuilder := transaction.NewProveBlockTxBuilder( + s.p.rpc, + s.p.cfg.TaikoL1Address, + s.p.cfg.GuardianProverMajorityAddress, + s.p.cfg.GuardianProverMinorityAddress, + ) s.p.proofSubmitters = nil s.Nil(s.p.initProofSubmitters(s.p.txmgr, txBuilder)) - s.p.rpc.GuardianProver, err = bindings.NewGuardianProver(s.p.cfg.GuardianProverAddress, s.p.rpc.L1) + s.p.rpc.GuardianProverMajority, err = bindings.NewGuardianProver(s.p.cfg.GuardianProverMajorityAddress, s.p.rpc.L1) s.Nil(err) approvedSink := make(chan *bindings.GuardianProverGuardianApproval) - approvedSub, err := s.p.rpc.GuardianProver.WatchGuardianApproval( + approvedSub, err := s.p.rpc.GuardianProverMajority.WatchGuardianApproval( nil, approvedSink, []common.Address{}, [](*big.Int){}, []([32]byte){}, ) s.Nil(err) @@ -319,7 +324,7 @@ func (s *ProverTestSuite) TestContestWrongBlocks() { }() req = <-s.p.proofSubmissionCh s.Nil(s.p.requestProofOp(req.Event, req.Tier)) - s.Nil(s.p.selectSubmitter(encoding.TierGuardianID).SubmitProof(context.Background(), <-s.p.proofGenerationCh)) + s.Nil(s.p.selectSubmitter(encoding.TierGuardianMajorityID).SubmitProof(context.Background(), <-s.p.proofGenerationCh)) approvedEvent := <-approvedSink s.Equal(header.Number.Uint64(), approvedEvent.BlockId.Uint64()) @@ -340,7 +345,7 @@ func (s *ProverTestSuite) TestProveExpiredUnassignedBlock() { }() e.AssignedProver = common.BytesToAddress(testutils.RandomHash().Bytes()) - s.p.cfg.GuardianProverAddress = common.Address{} + s.p.cfg.GuardianProverMajorityAddress = common.Address{} s.Nil(s.p.assignmentExpiredHandler.Handle(context.Background(), e)) req := <-s.p.proofSubmissionCh s.Nil(s.p.requestProofOp(req.Event, req.Tier)) @@ -353,21 +358,21 @@ func (s *ProverTestSuite) TestProveExpiredUnassignedBlock() { } func (s *ProverTestSuite) TestSelectSubmitter() { - submitter := s.p.selectSubmitter(encoding.TierGuardianID - 1) + submitter := s.p.selectSubmitter(encoding.TierGuardianMajorityID - 1) s.NotNil(submitter) - s.Equal(encoding.TierGuardianID, submitter.Tier()) + s.Equal(encoding.TierGuardianMajorityID, submitter.Tier()) } func (s *ProverTestSuite) TestSelectSubmitterNotFound() { - submitter := s.p.selectSubmitter(encoding.TierGuardianID + 1) + submitter := s.p.selectSubmitter(encoding.TierGuardianMajorityID + 1) s.Nil(submitter) } func (s *ProverTestSuite) TestGetSubmitterByTier() { - submitter := s.p.getSubmitterByTier(encoding.TierGuardianID) + submitter := s.p.getSubmitterByTier(encoding.TierGuardianMajorityID) s.NotNil(submitter) - s.Equal(encoding.TierGuardianID, submitter.Tier()) - s.Nil(s.p.getSubmitterByTier(encoding.TierGuardianID + 1)) + s.Equal(encoding.TierGuardianMajorityID, submitter.Tier()) + s.Nil(s.p.getSubmitterByTier(encoding.TierGuardianMajorityID + 1)) } func (s *ProverTestSuite) TestProveOp() { diff --git a/prover/server/api.go b/prover/server/api.go index be35869ac..a5895819b 100644 --- a/prover/server/api.go +++ b/prover/server/api.go @@ -148,7 +148,11 @@ func (s *ProverServer) CreateAssignment(c echo.Context) error { // 4. Check if the proof fee meets prover's minimum requirement for each tier. for _, tier := range req.TierFees { - if tier.Tier == encoding.TierGuardianID { + if tier.Tier == encoding.TierGuardianMajorityID { + continue + } + + if tier.Tier == encoding.TierGuardianMinorityID { continue }