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

Commit

Permalink
feat(proposer): new flag to propose empty blocks (#175)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidtaikocha authored Mar 7, 2023
1 parent e500039 commit 6621a5c
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 33 deletions.
6 changes: 6 additions & 0 deletions cmd/flags/proposer.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ var (
Value: "Comma separated accounts to treat as locals (priority inclusion)",
Category: proposerCategory,
}
ProposeEmptyBlocksInterval = &cli.StringFlag{
Name: "proposeEmptyBlockInterval",
Usage: "Time interval to propose empty blocks",
Category: proposerCategory,
}
)

// All proposer flags.
Expand All @@ -58,4 +63,5 @@ var ProposerFlags = MergeFlags(CommonFlags, []cli.Flag{
ShufflePoolContent,
CommitSlot,
TxPoolLocals,
ProposeEmptyBlocksInterval,
})
50 changes: 30 additions & 20 deletions proposer/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,17 @@ import (

// Config contains all configurations to initialize a Taiko proposer.
type Config struct {
L1Endpoint string
L2Endpoint string
TaikoL1Address common.Address
TaikoL2Address common.Address
L1ProposerPrivKey *ecdsa.PrivateKey
L2SuggestedFeeRecipient common.Address
ProposeInterval *time.Duration
ShufflePoolContent bool
CommitSlot uint64
LocalAddresses []common.Address
L1Endpoint string
L2Endpoint string
TaikoL1Address common.Address
TaikoL2Address common.Address
L1ProposerPrivKey *ecdsa.PrivateKey
L2SuggestedFeeRecipient common.Address
ProposeInterval *time.Duration
ShufflePoolContent bool
CommitSlot uint64
LocalAddresses []common.Address
ProposeEmptyBlocksInterval *time.Duration
}

// NewConfigFromCliContext initializes a Config instance from
Expand All @@ -45,6 +46,14 @@ func NewConfigFromCliContext(c *cli.Context) (*Config, error) {
}
proposingInterval = &interval
}
var proposeEmptyBlocksInterval *time.Duration
if c.IsSet(flags.ProposeEmptyBlocksInterval.Name) {
interval, err := time.ParseDuration(c.String(flags.ProposeEmptyBlocksInterval.Name))
if err != nil {
return nil, fmt.Errorf("invalid proposing empty blocks interval: %w", err)
}
proposeEmptyBlocksInterval = &interval
}

l2SuggestedFeeRecipient := c.String(flags.L2SuggestedFeeRecipient.Name)
if !common.IsHexAddress(l2SuggestedFeeRecipient) {
Expand All @@ -63,15 +72,16 @@ func NewConfigFromCliContext(c *cli.Context) (*Config, error) {
}

return &Config{
L1Endpoint: c.String(flags.L1WSEndpoint.Name),
L2Endpoint: c.String(flags.L2HTTPEndpoint.Name),
TaikoL1Address: common.HexToAddress(c.String(flags.TaikoL1Address.Name)),
TaikoL2Address: common.HexToAddress(c.String(flags.TaikoL2Address.Name)),
L1ProposerPrivKey: l1ProposerPrivKey,
L2SuggestedFeeRecipient: common.HexToAddress(l2SuggestedFeeRecipient),
ProposeInterval: proposingInterval,
ShufflePoolContent: c.Bool(flags.ShufflePoolContent.Name),
CommitSlot: c.Uint64(flags.CommitSlot.Name),
LocalAddresses: localAddresses,
L1Endpoint: c.String(flags.L1WSEndpoint.Name),
L2Endpoint: c.String(flags.L2HTTPEndpoint.Name),
TaikoL1Address: common.HexToAddress(c.String(flags.TaikoL1Address.Name)),
TaikoL2Address: common.HexToAddress(c.String(flags.TaikoL2Address.Name)),
L1ProposerPrivKey: l1ProposerPrivKey,
L2SuggestedFeeRecipient: common.HexToAddress(l2SuggestedFeeRecipient),
ProposeInterval: proposingInterval,
ShufflePoolContent: c.Bool(flags.ShufflePoolContent.Name),
CommitSlot: c.Uint64(flags.CommitSlot.Name),
LocalAddresses: localAddresses,
ProposeEmptyBlocksInterval: proposeEmptyBlocksInterval,
}, nil
}
56 changes: 50 additions & 6 deletions proposer/proposer.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package proposer
import (
"context"
"crypto/ecdsa"
"errors"
"fmt"
"math/big"
"math/rand"
Expand All @@ -23,6 +24,10 @@ import (
"github.com/urfave/cli/v2"
)

var (
errNoNewTxs = errors.New("no new transactions")
)

// Proposer keep proposing new transactions from L2 execution engine's tx pool at a fixed interval.
type Proposer struct {
// RPC clients
Expand All @@ -33,10 +38,11 @@ type Proposer struct {
l2SuggestedFeeRecipient common.Address

// Proposing configurations
proposingInterval *time.Duration
proposingTimer *time.Timer
commitSlot uint64
locals []common.Address
proposingInterval *time.Duration
proposeEmptyBlocksInterval *time.Duration
proposingTimer *time.Timer
commitSlot uint64
locals []common.Address

// Protocol configurations
protocolConfigs *bindings.TaikoDataConfig
Expand Down Expand Up @@ -64,6 +70,7 @@ func InitFromConfig(ctx context.Context, p *Proposer, cfg *Config) (err error) {
p.l1ProposerPrivKey = cfg.L1ProposerPrivKey
p.l2SuggestedFeeRecipient = cfg.L2SuggestedFeeRecipient
p.proposingInterval = cfg.ProposeInterval
p.proposeEmptyBlocksInterval = cfg.ProposeEmptyBlocksInterval
p.wg = sync.WaitGroup{}
p.locals = cfg.LocalAddresses
p.commitSlot = cfg.CommitSlot
Expand Down Expand Up @@ -105,6 +112,7 @@ func (p *Proposer) eventLoop() {
p.wg.Done()
}()

var lastNonEmptyBlockProposedAt = time.Now()
for {
p.updateProposingTicker()

Expand All @@ -115,9 +123,27 @@ func (p *Proposer) eventLoop() {
metrics.ProposerProposeEpochCounter.Inc(1)

if err := p.ProposeOp(p.ctx); err != nil {
log.Error("Proposing operation error", "error", err)
if !errors.Is(err, errNoNewTxs) {
log.Error("Proposing operation error", "error", err)
continue
}

if p.proposeEmptyBlocksInterval != nil {
if time.Now().Before(lastNonEmptyBlockProposedAt.Add(*p.proposeEmptyBlocksInterval)) {
continue
}

if err := p.ProposeEmptyBlockOp(p.ctx); err != nil {
log.Error("Proposing an empty block operation error", "error", err)
}

lastNonEmptyBlockProposedAt = time.Now()
}

continue
}

lastNonEmptyBlockProposedAt = time.Now()
}
}
}
Expand Down Expand Up @@ -154,7 +180,11 @@ func (p *Proposer) ProposeOp(ctx context.Context) error {
return fmt.Errorf("failed to fetch transaction pool content: %w", err)
}

log.Info("Transactions count", "count", len(txLists))
log.Info("Transactions lists count", "count", len(txLists))

if len(txLists) == 0 {
return errNoNewTxs
}

var commitTxListResQueue []*commitTxListRes
for i, txs := range txLists {
Expand Down Expand Up @@ -300,6 +330,20 @@ func (p *Proposer) ProposeTxList(
return nil
}

// ProposeEmptyBlockOp performs a proposing one empty block operation.
func (p *Proposer) ProposeEmptyBlockOp(ctx context.Context) error {
meta, commitTx, err := p.CommitTxList(ctx, []byte{}, 21000, 0)
if err != nil {
return fmt.Errorf("failed to commit an empty block: %w", err)
}

if err := p.ProposeTxList(ctx, meta, commitTx, []byte{}, 0); err != nil {
return fmt.Errorf("failed to propose an empty block: %w", err)
}

return nil
}

// updateProposingTicker updates the internal proposing timer.
func (p *Proposer) updateProposingTicker() {
if p.proposingTimer != nil {
Expand Down
6 changes: 5 additions & 1 deletion proposer/proposer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func (s *ProposerTestSuite) TestName() {

func (s *ProposerTestSuite) TestProposeOp() {
// Nothing to propose
s.Nil(s.p.ProposeOp(context.Background()))
s.EqualError(errNoNewTxs, s.p.ProposeOp(context.Background()).Error())

// Propose txs in L2 execution engine's mempool
sink := make(chan *bindings.TaikoL1ClientBlockProposed)
Expand Down Expand Up @@ -103,6 +103,10 @@ func (s *ProposerTestSuite) TestProposeOp() {
s.Equal(types.ReceiptStatusSuccessful, receipt.Status)
}

func (s *ProposerTestSuite) TestProposeEmptyBlockOp() {
s.Nil(s.p.ProposeEmptyBlockOp(context.Background()))
}

func (s *ProposerTestSuite) TestCommitTxList() {
txListBytes := testutils.RandomBytes(1024)
gasLimit := uint64(102400)
Expand Down
7 changes: 2 additions & 5 deletions testutils/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,14 @@ func ProposeAndInsertEmptyBlocks(
}()

// Zero byte txList
meta, commitTx, err := proposer.CommitTxList(context.Background(), []byte{}, 1024, 0)
s.Nil(err)

s.Nil(proposer.ProposeTxList(context.Background(), meta, commitTx, []byte{}, 0))
s.Nil(proposer.ProposeEmptyBlockOp(context.Background()))

// RLP encoded empty list
var emptyTxs []types.Transaction
encoded, err := rlp.EncodeToBytes(emptyTxs)
s.Nil(err)

meta, commitTx, err = proposer.CommitTxList(context.Background(), encoded, 1024, 0)
meta, commitTx, err := proposer.CommitTxList(context.Background(), encoded, 1024, 0)
s.Nil(err)

s.Nil(proposer.ProposeTxList(context.Background(), meta, commitTx, encoded, 0))
Expand Down
1 change: 1 addition & 0 deletions testutils/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type CalldataSyncer interface {
type Proposer interface {
utils.SubcommandApplication
ProposeOp(ctx context.Context) error
ProposeEmptyBlockOp(ctx context.Context) error
CommitTxList(ctx context.Context, txListBytes []byte, gasLimit uint64, splittedIdx int) (
*bindings.TaikoDataBlockMetadata,
*types.Transaction,
Expand Down
2 changes: 1 addition & 1 deletion version/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package version

// Version info.
var (
Version = "0.4.0"
Version = "0.5.0"
Meta = "dev"
)

Expand Down

0 comments on commit 6621a5c

Please sign in to comment.