Skip to content
This repository was archived by the owner on Dec 4, 2024. It is now read-only.

Commit 820e21a

Browse files
PostBlock optimization (#1972)
* PostBlock optimization * Remove unnecessary logs * Update last processed block in db * Lint fix * Smooth-out code * Concurrent read/write fix * Code cleanup * Comments fix * Comments fix - remove DBTransaction interface * Comments fix - introduce common code for eventGetter and eventProvider * Rebase fix
1 parent 1395ddb commit 820e21a

30 files changed

+1029
-697
lines changed

consensus/polybft/checkpoint_manager.go

+41-38
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"bytes"
55
"fmt"
66
"math/big"
7-
"sort"
87
"strconv"
98

109
"github.com/0xPolygon/polygon-edge/consensus/polybft/contractsapi"
@@ -17,6 +16,7 @@ import (
1716
"github.com/0xPolygon/polygon-edge/types"
1817
hclog "github.com/hashicorp/go-hclog"
1918
"github.com/umbracle/ethgo"
19+
bolt "go.etcd.io/bbolt"
2020
)
2121

2222
var (
@@ -28,6 +28,7 @@ var (
2828
)
2929

3030
type CheckpointManager interface {
31+
EventSubscriber
3132
PostBlock(req *PostBlockRequest) error
3233
BuildEventRoot(epoch uint64) (types.Hash, error)
3334
GenerateExitProof(exitID uint64) (types.Proof, error)
@@ -45,6 +46,15 @@ func (d *dummyCheckpointManager) GenerateExitProof(exitID uint64) (types.Proof,
4546
return types.Proof{}, nil
4647
}
4748

49+
// EventSubscriber implementation
50+
func (d *dummyCheckpointManager) GetLogFilters() map[types.Address][]types.Hash {
51+
return make(map[types.Address][]types.Hash)
52+
}
53+
func (d *dummyCheckpointManager) ProcessLog(header *types.Header,
54+
log *ethgo.Log, dbTx *bolt.Tx) error {
55+
return nil
56+
}
57+
4858
var _ CheckpointManager = (*checkpointManager)(nil)
4959

5060
// checkpointManager encapsulates logic for checkpoint data submission
@@ -67,23 +77,13 @@ type checkpointManager struct {
6777
logger hclog.Logger
6878
// state boltDb instance
6979
state *State
70-
// eventGetter gets exit events (missed or current) from blocks
71-
eventGetter *eventsGetter[*ExitEvent]
7280
}
7381

7482
// newCheckpointManager creates a new instance of checkpointManager
7583
func newCheckpointManager(key ethgo.Key, checkpointOffset uint64,
7684
checkpointManagerSC types.Address, txRelayer txrelayer.TxRelayer,
7785
blockchain blockchainBackend, backend polybftBackend, logger hclog.Logger,
7886
state *State) *checkpointManager {
79-
retry := &eventsGetter[*ExitEvent]{
80-
blockchain: blockchain,
81-
isValidLogFn: func(l *types.Log) bool {
82-
return l.Address == contracts.L2StateSenderContract
83-
},
84-
parseEventFn: parseExitEvent,
85-
}
86-
8787
return &checkpointManager{
8888
key: key,
8989
blockchain: blockchain,
@@ -93,7 +93,6 @@ func newCheckpointManager(key ethgo.Key, checkpointOffset uint64,
9393
checkpointManagerAddr: checkpointManagerSC,
9494
logger: logger,
9595
state: state,
96-
eventGetter: retry,
9796
}
9897
}
9998

@@ -282,33 +281,8 @@ func (c *checkpointManager) isCheckpointBlock(blockNumber uint64, isEpochEndingB
282281
}
283282

284283
// PostBlock is called on every insert of finalized block (either from consensus or syncer)
285-
// It will read any exit event that happened in block and insert it to state boltDb
284+
// It sends a checkpoint if given block is checkpoint block and block proposer is given validator
286285
func (c *checkpointManager) PostBlock(req *PostBlockRequest) error {
287-
block := req.FullBlock.Block.Number()
288-
289-
lastBlock, err := c.state.CheckpointStore.getLastSaved()
290-
if err != nil {
291-
return fmt.Errorf("could not get last processed block for exit events. Error: %w", err)
292-
}
293-
294-
exitEvents, err := c.eventGetter.getFromBlocks(lastBlock, req.FullBlock)
295-
if err != nil {
296-
return err
297-
}
298-
299-
sort.Slice(exitEvents, func(i, j int) bool {
300-
// keep events in sequential order
301-
return exitEvents[i].ID.Cmp(exitEvents[j].ID) < 0
302-
})
303-
304-
if err := c.state.CheckpointStore.insertExitEvents(exitEvents); err != nil {
305-
return err
306-
}
307-
308-
if err := c.state.CheckpointStore.updateLastSaved(block); err != nil {
309-
return err
310-
}
311-
312286
if c.isCheckpointBlock(req.FullBlock.Block.Header.Number, req.IsEpochEndingBlock) &&
313287
bytes.Equal(c.key.Address().Bytes(), req.FullBlock.Block.Header.Miner) {
314288
go func(header *types.Header, epochNumber uint64) {
@@ -445,6 +419,35 @@ func (c *checkpointManager) GenerateExitProof(exitID uint64) (types.Proof, error
445419
}, nil
446420
}
447421

422+
// EventSubscriber implementation
423+
424+
// GetLogFilters returns a map of log filters for getting desired events,
425+
// where the key is the address of contract that emits desired events,
426+
// and the value is a slice of signatures of events we want to get.
427+
// This function is the implementation of EventSubscriber interface
428+
func (c *checkpointManager) GetLogFilters() map[types.Address][]types.Hash {
429+
var l2StateSyncedEvent contractsapi.L2StateSyncedEvent
430+
431+
return map[types.Address][]types.Hash{
432+
contracts.L2StateSenderContract: {types.Hash(l2StateSyncedEvent.Sig())},
433+
}
434+
}
435+
436+
// ProcessLog is the implementation of EventSubscriber interface,
437+
// used to handle a log defined in GetLogFilters, provided by event provider
438+
func (c *checkpointManager) ProcessLog(header *types.Header, log *ethgo.Log, dbTx *bolt.Tx) error {
439+
exitEvent, doesMatch, err := parseExitEvent(header, log)
440+
if err != nil {
441+
return err
442+
}
443+
444+
if !doesMatch {
445+
return nil
446+
}
447+
448+
return c.state.CheckpointStore.insertExitEvent(exitEvent, dbTx)
449+
}
450+
448451
// createExitTree creates an exit event merkle tree from provided exit events
449452
func createExitTree(exitEvents []*ExitEvent) (*merkle.MerkleTree, error) {
450453
numOfEvents := len(exitEvents)

consensus/polybft/checkpoint_manager_test.go

-122
Original file line numberDiff line numberDiff line change
@@ -318,128 +318,6 @@ func TestCheckpointManager_IsCheckpointBlock(t *testing.T) {
318318
}
319319
}
320320

321-
func TestCheckpointManager_PostBlock(t *testing.T) {
322-
const (
323-
numOfReceipts = 5
324-
block = 5
325-
epoch = 1
326-
)
327-
328-
state := newTestState(t)
329-
330-
createReceipts := func(startID, endID uint64) []*types.Receipt {
331-
receipts := make([]*types.Receipt, endID-startID)
332-
for i := startID; i < endID; i++ {
333-
receipts[i-startID] = &types.Receipt{Logs: []*types.Log{
334-
createTestLogForExitEvent(t, i),
335-
}}
336-
receipts[i-startID].SetStatus(types.ReceiptSuccess)
337-
}
338-
339-
return receipts
340-
}
341-
342-
extra := &Extra{
343-
Checkpoint: &CheckpointData{
344-
EpochNumber: epoch,
345-
},
346-
}
347-
348-
req := &PostBlockRequest{FullBlock: &types.FullBlock{Block: &types.Block{Header: &types.Header{Number: block}}},
349-
Epoch: epoch}
350-
351-
req.FullBlock.Block.Header.ExtraData = extra.MarshalRLPTo(nil)
352-
353-
blockchain := new(blockchainMock)
354-
checkpointManager := newCheckpointManager(wallet.NewEcdsaSigner(createTestKey(t)), 5, types.ZeroAddress,
355-
nil, blockchain, nil, hclog.NewNullLogger(), state)
356-
357-
t.Run("PostBlock - not epoch ending block", func(t *testing.T) {
358-
require.NoError(t, state.CheckpointStore.updateLastSaved(block-1)) // we got everything till the current block
359-
req.IsEpochEndingBlock = false
360-
req.FullBlock.Receipts = createReceipts(0, 5)
361-
require.NoError(t, checkpointManager.PostBlock(req))
362-
363-
exitEvents, err := state.CheckpointStore.getExitEvents(epoch, func(exitEvent *ExitEvent) bool {
364-
return exitEvent.BlockNumber == block
365-
})
366-
367-
require.NoError(t, err)
368-
require.Len(t, exitEvents, 5)
369-
require.Equal(t, uint64(epoch), exitEvents[0].EpochNumber)
370-
})
371-
372-
t.Run("PostBlock - epoch ending block (exit events are saved to the next epoch)", func(t *testing.T) {
373-
require.NoError(t, state.CheckpointStore.updateLastSaved(block)) // we got everything till the current block
374-
req.IsEpochEndingBlock = true
375-
req.FullBlock.Receipts = createReceipts(5, 10)
376-
extra.Validators = &validator.ValidatorSetDelta{}
377-
req.FullBlock.Block.Header.ExtraData = extra.MarshalRLPTo(nil)
378-
req.FullBlock.Block.Header.Number = block + 1
379-
380-
require.NoError(t, checkpointManager.PostBlock(req))
381-
382-
exitEvents, err := state.CheckpointStore.getExitEvents(epoch+1, func(exitEvent *ExitEvent) bool {
383-
return exitEvent.BlockNumber == block+2 // they should be saved in the next epoch and its first block
384-
})
385-
386-
require.NoError(t, err)
387-
require.Len(t, exitEvents, 5)
388-
require.Equal(t, uint64(block+2), exitEvents[0].BlockNumber)
389-
require.Equal(t, uint64(epoch+1), exitEvents[0].EpochNumber)
390-
})
391-
392-
t.Run("PostBlock - there are missing events", func(t *testing.T) {
393-
require.NoError(t, state.CheckpointStore.updateLastSaved(block)) // we are missing one block
394-
395-
missedReceipts := createReceipts(10, 13)
396-
newReceipts := createReceipts(13, 15)
397-
398-
extra := &Extra{
399-
Checkpoint: &CheckpointData{
400-
EpochNumber: epoch + 1,
401-
},
402-
}
403-
404-
blockchain.On("GetHeaderByNumber", uint64(block+1)).Return(&types.Header{
405-
Number: block + 1,
406-
ExtraData: extra.MarshalRLPTo(nil),
407-
Hash: types.BytesToHash([]byte{0, 1, 2, 3}),
408-
}, true)
409-
blockchain.On("GetReceiptsByHash", types.BytesToHash([]byte{0, 1, 2, 3})).Return([]*types.Receipt{}, nil)
410-
blockchain.On("GetHeaderByNumber", uint64(block+2)).Return(&types.Header{
411-
Number: block + 2,
412-
ExtraData: extra.MarshalRLPTo(nil),
413-
Hash: types.BytesToHash([]byte{4, 5, 6, 7}),
414-
}, true)
415-
blockchain.On("GetReceiptsByHash", types.BytesToHash([]byte{4, 5, 6, 7})).Return(missedReceipts, nil)
416-
417-
req.IsEpochEndingBlock = false
418-
req.FullBlock.Block.Header.Number = block + 3 // new block
419-
req.FullBlock.Block.Header.ExtraData = extra.MarshalRLPTo(nil) // same epoch
420-
req.FullBlock.Receipts = newReceipts
421-
require.NoError(t, checkpointManager.PostBlock(req))
422-
423-
exitEvents, err := state.CheckpointStore.getExitEvents(epoch+1, func(exitEvent *ExitEvent) bool {
424-
return exitEvent.BlockNumber == block+2
425-
})
426-
427-
require.NoError(t, err)
428-
// receipts from missed block + events from previous test case that were saved in the next epoch
429-
// since they were in epoch ending block
430-
require.Len(t, exitEvents, len(missedReceipts)+5)
431-
require.Equal(t, extra.Checkpoint.EpochNumber, exitEvents[0].EpochNumber)
432-
433-
exitEvents, err = state.CheckpointStore.getExitEvents(epoch+1, func(exitEvent *ExitEvent) bool {
434-
return exitEvent.BlockNumber == block+3
435-
})
436-
437-
require.NoError(t, err)
438-
require.Len(t, exitEvents, len(newReceipts))
439-
require.Equal(t, extra.Checkpoint.EpochNumber, exitEvents[0].EpochNumber)
440-
})
441-
}
442-
443321
func TestCheckpointManager_BuildEventRoot(t *testing.T) {
444322
t.Parallel()
445323

0 commit comments

Comments
 (0)