Skip to content

Commit 27c7536

Browse files
tac0turtletac0turtleManav-Aggarwal
committed
chore: empty blocks & lazy aggrgation (#2243)
<!-- Please read and fill out this form before submitting your PR. Please make sure you have reviewed our contributors guide before submitting your first PR. NOTE: PR titles should follow semantic commits: https://www.conventionalcommits.org/en/v1.0.0/ --> <!-- Please provide an explanation of the PR, including the appropriate context, background, goal, and rationale. If there is an issue with this information, please provide a tl;dr and link the issue. Ex: Closes #<issue number> --> --------- Co-authored-by: tac0turtle <you@example.com> Co-authored-by: Manav Aggarwal <manavaggarwal1234@gmail.com>
1 parent fa851f8 commit 27c7536

2 files changed

Lines changed: 91 additions & 2 deletions

File tree

‎block/manager.go‎

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,7 @@ func (m *Manager) publishBlockInternal(ctx context.Context) error {
494494

495495
height, err := m.store.Height(ctx)
496496
if err != nil {
497-
return fmt.Errorf("error while getting store height: %w", err)
497+
return fmt.Errorf("error while getting height: %w", err)
498498
}
499499

500500
newHeight := height + 1
@@ -675,7 +675,74 @@ func (m *Manager) getLastBlockTime() time.Time {
675675
func (m *Manager) createBlock(ctx context.Context, height uint64, lastSignature *types.Signature, lastHeaderHash types.Hash, batchData *BatchData) (*types.SignedHeader, *types.Data, error) {
676676
m.lastStateMtx.RLock()
677677
defer m.lastStateMtx.RUnlock()
678-
return m.execCreateBlock(ctx, height, lastSignature, lastHeaderHash, m.lastState, batchData)
678+
679+
if m.signer == nil {
680+
return nil, nil, fmt.Errorf("signer is nil; cannot create block")
681+
}
682+
683+
key, err := m.signer.GetPublic()
684+
if err != nil {
685+
return nil, nil, fmt.Errorf("failed to get proposer public key: %w", err)
686+
}
687+
688+
// check that the proposer address is the same as the genesis proposer address
689+
address, err := m.signer.GetAddress()
690+
if err != nil {
691+
return nil, nil, fmt.Errorf("failed to get proposer address: %w", err)
692+
}
693+
if !bytes.Equal(m.genesis.ProposerAddress, address) {
694+
return nil, nil, fmt.Errorf("proposer address is not the same as the genesis proposer address %x != %x", address, m.genesis.ProposerAddress)
695+
}
696+
697+
// Determine if this is an empty block
698+
isEmpty := batchData.Batch == nil || len(batchData.Batch.Transactions) == 0
699+
700+
// Set the appropriate data hash based on whether this is an empty block
701+
var dataHash types.Hash
702+
if isEmpty {
703+
dataHash = dataHashForEmptyTxs // Use dataHashForEmptyTxs for empty blocks
704+
} else {
705+
dataHash = convertBatchDataToBytes(batchData.Data)
706+
}
707+
708+
header := &types.SignedHeader{
709+
Header: types.Header{
710+
Version: types.Version{
711+
Block: m.lastState.Version.Block,
712+
App: m.lastState.Version.App,
713+
},
714+
BaseHeader: types.BaseHeader{
715+
ChainID: m.lastState.ChainID,
716+
Height: height,
717+
Time: uint64(batchData.UnixNano()), //nolint:gosec // why is time unix? (tac0turtle)
718+
},
719+
LastHeaderHash: lastHeaderHash,
720+
DataHash: dataHash,
721+
ConsensusHash: make(types.Hash, 32),
722+
AppHash: m.lastState.AppHash,
723+
ProposerAddress: m.genesis.ProposerAddress,
724+
},
725+
Signature: *lastSignature,
726+
Signer: types.Signer{
727+
PubKey: key,
728+
Address: m.genesis.ProposerAddress,
729+
},
730+
}
731+
732+
// Create block data with appropriate transactions
733+
blockData := &types.Data{
734+
Txs: make(types.Txs, 0), // Start with empty transaction list
735+
}
736+
737+
// Only add transactions if this is not an empty block
738+
if !isEmpty {
739+
blockData.Txs = make(types.Txs, len(batchData.Batch.Transactions))
740+
for i := range batchData.Batch.Transactions {
741+
blockData.Txs[i] = types.Tx(batchData.Batch.Transactions[i])
742+
}
743+
}
744+
745+
return header, blockData, nil
679746
}
680747

681748
func (m *Manager) execCreateBlock(ctx context.Context, height uint64, lastSignature *types.Signature, lastHeaderHash types.Hash, lastState types.State, batchData *BatchData) (*types.SignedHeader, *types.Data, error) {

‎specs/lazy-adr/adr-021-lazy-aggregation.md‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,28 @@ Leverage the existing empty batch mechanism and `dataHashForEmptyTxs` to maintai
164164
}
165165
```
166166

167+
4. **Block Production**:
168+
169+
The block production function centralizes the logic for publishing blocks and resetting timers. It records the start time, attempts to publish a block, and then intelligently resets both timers based on the elapsed time. This ensures that block production remains on schedule even if the block creation process takes significant time.
170+
171+
```go
172+
func (m *Manager) produceBlock(ctx context.Context, trigger string, lazyTimer, blockTimer *time.Timer) {
173+
// Record the start time
174+
start := time.Now()
175+
176+
// Attempt to publish the block
177+
if err := m.publishBlock(ctx); err != nil && ctx.Err() == nil {
178+
m.logger.Error("error while publishing block", "trigger", trigger, "error", err)
179+
} else {
180+
m.logger.Debug("Successfully published block", "trigger", trigger)
181+
}
182+
183+
// Reset both timers for the next aggregation window
184+
lazyTimer.Reset(getRemainingSleep(start, m.config.Node.LazyBlockInterval.Duration))
185+
blockTimer.Reset(getRemainingSleep(start, m.config.Node.BlockTime.Duration))
186+
}
187+
```
188+
167189
### Key Changes
168190

169191
1. Return batch with timestamp even when empty, allowing proper block timing

0 commit comments

Comments
 (0)