From 330b92fcf632e0ef3449bb79be17c7b1602ffb32 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 29 Sep 2023 16:11:13 +0800 Subject: [PATCH] feat(proposer): update proposer based on protocol changes --- proposer/proposer.go | 80 ++++++++++--------- proposer/proposer_test.go | 40 ++++------ .../prover_selector/eth_fee_eoa_selector.go | 60 +++++++------- proposer/prover_selector/interface.go | 6 +- 4 files changed, 94 insertions(+), 92 deletions(-) diff --git a/proposer/proposer.go b/proposer/proposer.go index f887be4c8..37e42923e 100644 --- a/proposer/proposer.go +++ b/proposer/proposer.go @@ -43,9 +43,8 @@ type Proposer struct { rpc *rpc.Client // Private keys and account addresses - l1ProposerPrivKey *ecdsa.PrivateKey - l1ProposerAddress common.Address - l2SuggestedFeeRecipient common.Address + proposerPrivKey *ecdsa.PrivateKey + proposerAddress common.Address // Proposing configurations proposingInterval *time.Duration @@ -88,9 +87,8 @@ func (p *Proposer) InitFromCli(ctx context.Context, c *cli.Context) error { // InitFromConfig initializes the proposer instance based on the given configurations. func InitFromConfig(ctx context.Context, p *Proposer, cfg *Config) (err error) { - p.l1ProposerPrivKey = cfg.L1ProposerPrivKey - p.l1ProposerAddress = crypto.PubkeyToAddress(cfg.L1ProposerPrivKey.PublicKey) - p.l2SuggestedFeeRecipient = cfg.L2SuggestedFeeRecipient + p.proposerPrivKey = cfg.L1ProposerPrivKey + p.proposerAddress = crypto.PubkeyToAddress(cfg.L1ProposerPrivKey.PublicKey) p.proposingInterval = cfg.ProposeInterval p.proposeEmptyBlocksInterval = cfg.ProposeEmptyBlocksInterval p.proposeBlockTxGasLimit = cfg.ProposeBlockTxGasLimit @@ -232,7 +230,7 @@ func (p *Proposer) ProposeOp(ctx context.Context) error { txLists, err := p.rpc.GetPoolContent( ctx, - p.L2SuggestedFeeRecipient(), + p.proposerAddress, baseFee, p.protocolConfigs.BlockMaxGasLimit, p.protocolConfigs.BlockMaxTxListBytes.Uint64(), @@ -282,7 +280,7 @@ func (p *Proposer) ProposeOp(ctx context.Context) error { } nonce, err := p.rpc.L1.NonceAt( ctx, - crypto.PubkeyToAddress(p.l1ProposerPrivKey.PublicKey), + crypto.PubkeyToAddress(p.proposerPrivKey.PublicKey), new(big.Int).SetUint64(head), ) if err != nil { @@ -305,13 +303,7 @@ func (p *Proposer) ProposeOp(ctx context.Context) error { } txNonce := nonce + uint64(i) - if err := p.ProposeTxList(ctx, &encoding.TaikoL1BlockMetadataInput{ - Proposer: p.l2SuggestedFeeRecipient, - TxListHash: crypto.Keccak256Hash(txListBytes), - TxListByteStart: common.Big0, - TxListByteEnd: new(big.Int).SetUint64(uint64(len(txListBytes))), - CacheTxListInfo: false, - }, txListBytes, uint(txs.Len()), &txNonce); err != nil { + if err := p.ProposeTxList(ctx, txListBytes, uint(txs.Len()), &txNonce); err != nil { return fmt.Errorf("failed to propose transactions: %w", err) } @@ -336,19 +328,14 @@ func (p *Proposer) ProposeOp(ctx context.Context) error { // sendProposeBlockTx tries to send a TaikoL1.proposeBlock transaction. func (p *Proposer) sendProposeBlockTx( ctx context.Context, - meta *encoding.TaikoL1BlockMetadataInput, txListBytes []byte, nonce *uint64, - assignment []byte, + assignment *encoding.ProverAssignment, fee *big.Int, isReplacement bool, ) (*types.Transaction, error) { // Propose the transactions list - inputs, err := encoding.EncodeProposeBlockInput(meta) - if err != nil { - return nil, err - } - opts, err := getTxOpts(ctx, p.rpc.L1, p.l1ProposerPrivKey, p.rpc.L1ChainID, fee) + opts, err := getTxOpts(ctx, p.rpc.L1, p.proposerPrivKey, p.rpc.L1ChainID, fee) if err != nil { return nil, err } @@ -363,7 +350,7 @@ func (p *Proposer) sendProposeBlockTx( ctx, p.rpc, opts, - p.l1ProposerAddress, + p.proposerAddress, new(big.Int).SetUint64(p.txReplacementTipMultiplier), p.proposeBlockTxGasTipCap, ); err != nil { @@ -371,7 +358,18 @@ func (p *Proposer) sendProposeBlockTx( } } - proposeTx, err := p.rpc.TaikoL1.ProposeBlock(opts, inputs, assignment, txListBytes) + encodedssignment, err := encoding.EncodeProverAssignment(assignment) + if err != nil { + return nil, err + } + + proposeTx, err := p.rpc.TaikoL1.ProposeBlock( + opts, + crypto.Keccak256Hash(txListBytes), + rpc.StringToBytes32(p.cfg.ExtraData), + encodedssignment, + txListBytes, + ) if err != nil { return nil, encoding.TryParsingCustomError(err) } @@ -386,7 +384,11 @@ func (p *Proposer) ProposeTxList( txNum uint, nonce *uint64, ) error { - assignment, fee, err := p.proverSelector.AssignProver(ctx, meta) + signature, prover, fee, err := p.proverSelector.AssignProver( + ctx, + []*encoding.TierFee{}, // TODO: update tier fees + crypto.Keccak256Hash(txListBytes), + ) if err != nil { return err } @@ -400,7 +402,20 @@ func (p *Proposer) ProposeTxList( if ctx.Err() != nil { return nil } - if tx, err = p.sendProposeBlockTx(ctx, meta, txListBytes, nonce, assignment, fee, isReplacement); err != nil { + if tx, err = p.sendProposeBlockTx( + ctx, + txListBytes, + nonce, + &encoding.ProverAssignment{ + Prover: prover, + FeeToken: common.Address{}, + TierFees: []*encoding.TierFee{}, // TODO: update tier fees + Expiry: uint64(proverAssignmentTimeout.Seconds()), + Signature: signature, + }, + fee, + isReplacement, + ); err != nil { log.Warn("Failed to send propose block transaction", "error", encoding.TryParsingCustomError(err)) if strings.Contains(err.Error(), core.ErrNonceTooLow.Error()) { return nil @@ -446,13 +461,7 @@ func (p *Proposer) ProposeTxList( // ProposeEmptyBlockOp performs a proposing one empty block operation. func (p *Proposer) ProposeEmptyBlockOp(ctx context.Context) error { - return p.ProposeTxList(ctx, &encoding.TaikoL1BlockMetadataInput{ - TxListHash: crypto.Keccak256Hash([]byte{}), - Proposer: p.L2SuggestedFeeRecipient(), - TxListByteStart: common.Big0, - TxListByteEnd: common.Big0, - CacheTxListInfo: false, - }, []byte{}, 0, nil) + return p.ProposeTxList(ctx, []byte{}, 0, nil) } // updateProposingTicker updates the internal proposing timer. @@ -478,11 +487,6 @@ func (p *Proposer) Name() string { return "proposer" } -// L2SuggestedFeeRecipient returns the L2 suggested fee recipient of the current proposer. -func (p *Proposer) L2SuggestedFeeRecipient() common.Address { - return p.l2SuggestedFeeRecipient -} - // getTxOpts creates a bind.TransactOpts instance using the given private key. func getTxOpts( ctx context.Context, diff --git a/proposer/proposer_test.go b/proposer/proposer_test.go index 9d7d16086..4508b2a8d 100644 --- a/proposer/proposer_test.go +++ b/proposer/proposer_test.go @@ -104,7 +104,6 @@ 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.Proposer) receipt, err := s.p.rpc.L1.TransactionReceipt(context.Background(), event.Raw.TxHash) s.Nil(err) @@ -132,14 +131,14 @@ func (s *ProposerTestSuite) TestSendProposeBlockTx() { opts, err := getTxOpts( context.Background(), s.p.rpc.L1, - s.p.l1ProposerPrivKey, + s.p.proposerPrivKey, s.RpcClient.L1ChainID, fee, ) s.Nil(err) s.Greater(opts.GasTipCap.Uint64(), uint64(0)) - nonce, err := s.RpcClient.L1.PendingNonceAt(context.Background(), s.p.l1ProposerAddress) + nonce, err := s.RpcClient.L1.PendingNonceAt(context.Background(), s.p.proposerAddress) s.Nil(err) tx := types.NewTransaction( @@ -154,7 +153,7 @@ func (s *ProposerTestSuite) TestSendProposeBlockTx() { s.SetL1Automine(false) defer s.SetL1Automine(true) - signedTx, err := types.SignTx(tx, types.LatestSignerForChainID(s.RpcClient.L1ChainID), s.p.l1ProposerPrivKey) + signedTx, err := types.SignTx(tx, types.LatestSignerForChainID(s.RpcClient.L1ChainID), s.p.proposerPrivKey) s.Nil(err) s.Nil(s.RpcClient.L1.SendTransaction(context.Background(), signedTx)) @@ -162,23 +161,24 @@ func (s *ProposerTestSuite) TestSendProposeBlockTx() { encoded, err := rlp.EncodeToBytes(emptyTxs) s.Nil(err) - meta := &encoding.TaikoL1BlockMetadataInput{ - 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.proverSelector.AssignProver(context.Background(), meta) + signature, prover, fee, err := s.p.proverSelector.AssignProver( + context.Background(), + []*encoding.TierFee{}, + testutils.RandomHash(), + ) s.Nil(err) newTx, err := s.p.sendProposeBlockTx( context.Background(), - meta, encoded, &nonce, - assignment, + &encoding.ProverAssignment{ + Prover: prover, + FeeToken: common.Address{}, + TierFees: []*encoding.TierFee{}, + Expiry: uint64(proverAssignmentTimeout.Seconds()), + Signature: signature, + }, fee, true, ) @@ -187,18 +187,10 @@ func (s *ProposerTestSuite) TestSendProposeBlockTx() { } func (s *ProposerTestSuite) TestAssignProverSuccessFirstRound() { - meta := &encoding.TaikoL1BlockMetadataInput{ - Proposer: 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.proverSelector.AssignProver(context.Background(), meta) + _, _, fee, err := s.p.proverSelector.AssignProver(context.Background(), []*encoding.TierFee{}, testutils.RandomHash()) s.Nil(err) s.Equal(fee.Uint64(), s.p.cfg.BlockProposalFee.Uint64()) diff --git a/proposer/prover_selector/eth_fee_eoa_selector.go b/proposer/prover_selector/eth_fee_eoa_selector.go index c340e9d68..11784c89b 100644 --- a/proposer/prover_selector/eth_fee_eoa_selector.go +++ b/proposer/prover_selector/eth_fee_eoa_selector.go @@ -82,15 +82,16 @@ func (s *ETHFeeEOASelector) ProverEndpoints() []*url.URL { return s.proverEndpoi // 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) { + tierFees []*encoding.TierFee, + txListHash common.Hash, +) ([]byte, common.Address, *big.Int, error) { oracleProverAddress, err := s.rpc.TaikoL1.Resolve0( &bind.CallOpts{Context: ctx}, rpc.StringToBytes32("oracle_prover"), true, ) if err != nil { - return nil, nil, err + return nil, common.Address{}, nil, err } // 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. @@ -111,10 +112,11 @@ func (s *ETHFeeEOASelector) AssignProver( for _, endpoint := range s.shuffleProverEndpoints() { encodedAssignment, proverAddress, err := assignProver( ctx, - meta, endpoint, fee, expiry, + tierFees, + txListHash, s.requestTimeout, oracleProverAddress, ) @@ -123,22 +125,20 @@ func (s *ETHFeeEOASelector) AssignProver( continue } - if proverAddress != encoding.OracleProverAddress { - ok, err := rpc.CheckProverBalance(ctx, s.rpc, proverAddress, s.taikoL1Address, s.protocolConfigs.ProofBond) - if err != nil { - log.Warn("Failed to check prover balance", "endpoint", endpoint, "error", err) - continue - } - if !ok { - continue - } + ok, err := rpc.CheckProverBalance(ctx, s.rpc, proverAddress, s.taikoL1Address, s.protocolConfigs.LivenessBond) + if err != nil { + log.Warn("Failed to check prover balance", "endpoint", endpoint, "error", err) + continue + } + if !ok { + continue } - return encodedAssignment, fee, nil + return encodedAssignment, proverAddress, fee, nil } } - return nil, nil, errUnableToFindProver + return nil, common.Address{}, nil, errUnableToFindProver } // shuffleProverEndpoints shuffles the current selector's prover endpoints. @@ -152,10 +152,11 @@ func (s *ETHFeeEOASelector) shuffleProverEndpoints() []*url.URL { // 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, + tierFees []*encoding.TierFee, + txListHash common.Hash, timeout time.Duration, oracleProverAddress common.Address, ) ([]byte, common.Address, error) { @@ -164,13 +165,19 @@ func assignProver( "endpoint", endpoint, "fee", fee.String(), "expiry", expiry, + "txListHash", txListHash, ) // Send the HTTP request var ( client = resty.New() - reqBody = &encoding.ProposeBlockData{Expiry: expiry, Input: *meta, Fee: fee} - result = server.ProposeBlockResponse{} + reqBody = &server.CreateAssignmentRequestBody{ + FeeToken: (common.Address{}), + TierFees: tierFees, + Expiry: expiry, + TxListHash: txListHash, + } + result = server.ProposeBlockResponse{} ) requestUrl, err := url.JoinPath(endpoint.String(), "/assignment") if err != nil { @@ -196,12 +203,12 @@ func assignProver( // Ensure prover in response is the same as the one recovered // from the signature - encodedBlockData, err := encoding.EncodeProposeBlockData(reqBody) + payload, err := encoding.EncodeProverAssignmentPayload(txListHash, common.Address{}, expiry, tierFees) if err != nil { return nil, common.Address{}, err } - pubKey, err := crypto.SigToPub(crypto.Keccak256Hash(encodedBlockData).Bytes(), result.SignedPayload) + pubKey, err := crypto.SigToPub(crypto.Keccak256Hash(payload).Bytes(), result.SignedPayload) if err != nil { return nil, common.Address{}, err } @@ -217,15 +224,12 @@ func assignProver( // Convert signature to one solidity can recover by adding 27 to 65th byte result.SignedPayload[64] = uint8(uint(result.SignedPayload[64])) + 27 - // If this assignment is to oracle prover, change prover address in assignment to `LibUtils.ORACLE_PROVER` - if oracleProverAddress != (common.Address{}) && result.Prover == oracleProverAddress { - result.Prover = encoding.OracleProverAddress - } - encoded, err := encoding.EncodeProverAssignment(&encoding.ProverAssignment{ - Prover: result.Prover, - Expiry: reqBody.Expiry, - Data: result.SignedPayload, + Prover: result.Prover, + FeeToken: common.Address{}, + TierFees: []*encoding.TierFee{}, // TODO: update tier fees + Expiry: reqBody.Expiry, + Signature: result.SignedPayload, }) if err != nil { return nil, common.Address{}, err diff --git a/proposer/prover_selector/interface.go b/proposer/prover_selector/interface.go index 53ab9df14..04bf0d276 100644 --- a/proposer/prover_selector/interface.go +++ b/proposer/prover_selector/interface.go @@ -5,13 +5,15 @@ import ( "math/big" "net/url" + "github.com/ethereum/go-ethereum/common" "github.com/taikoxyz/taiko-client/bindings/encoding" ) type ProverSelector interface { AssignProver( ctx context.Context, - meta *encoding.TaikoL1BlockMetadataInput, - ) (signedPayload []byte, fee *big.Int, err error) + tierFees []*encoding.TierFee, + txListHash common.Hash, + ) (signedPayload []byte, prover common.Address, fee *big.Int, err error) ProverEndpoints() []*url.URL }