Skip to content
This repository was archived by the owner on Jan 24, 2025. It is now read-only.

Commit d5e0427

Browse files
authored
Merge pull request #895 from iotaledger/fix/inx-block-state
Add `BlockAccepted` and `BlockConfirmed` events to blockretainer to fix data race in INX
2 parents 737aa28 + 5c58b00 commit d5e0427

File tree

4 files changed

+49
-18
lines changed

4 files changed

+49
-18
lines changed

Diff for: components/inx/server_blocks.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ func (s *Server) ListenToAcceptedBlocks(_ *inx.NoParams, srv inx.INX_ListenToAcc
7878

7979
wp := workerpool.New("ListenToAcceptedBlocks", workerpool.WithWorkerCount(workerCount)).Start()
8080

81-
unhook := deps.Protocol.Events.Engine.BlockGadget.BlockAccepted.Hook(func(block *blocks.Block) {
81+
unhook := deps.Protocol.Events.Engine.BlockRetainer.BlockAccepted.Hook(func(block *blocks.Block) {
8282
payload, err := inx.WrapBlockMetadata(&api.BlockMetadataResponse{
8383
BlockID: block.ID(),
8484
BlockState: api.BlockStateAccepted,
@@ -118,7 +118,7 @@ func (s *Server) ListenToConfirmedBlocks(_ *inx.NoParams, srv inx.INX_ListenToCo
118118

119119
wp := workerpool.New("ListenToConfirmedBlocks", workerpool.WithWorkerCount(workerCount)).Start()
120120

121-
unhook := deps.Protocol.Events.Engine.BlockGadget.BlockConfirmed.Hook(func(block *blocks.Block) {
121+
unhook := deps.Protocol.Events.Engine.BlockRetainer.BlockConfirmed.Hook(func(block *blocks.Block) {
122122
payload, err := inx.WrapBlockMetadata(&api.BlockMetadataResponse{
123123
BlockID: block.ID(),
124124
BlockState: api.BlockStateConfirmed,

Diff for: pkg/retainer/blockretainer/block_retainer.go

+25-13
Original file line numberDiff line numberDiff line change
@@ -67,26 +67,26 @@ func NewProvider() module.Provider[*engine.Engine, retainer.BlockRetainer] {
6767
asyncOpt := event.WithWorkerPool(r.workerPool)
6868

6969
e.ConstructedEvent().OnTrigger(func() {
70-
e.Events.Booker.BlockBooked.Hook(func(b *blocks.Block) {
71-
if err := r.OnBlockBooked(b); err != nil {
70+
e.Events.Booker.BlockBooked.Hook(func(block *blocks.Block) {
71+
if err := r.OnBlockBooked(block); err != nil {
7272
r.errorHandler(ierrors.Wrap(err, "failed to store on BlockBooked in retainer"))
7373
}
7474
}, asyncOpt)
7575

76-
e.Events.BlockGadget.BlockAccepted.Hook(func(b *blocks.Block) {
77-
if err := r.OnBlockAccepted(b.ID()); err != nil {
76+
e.Events.BlockGadget.BlockAccepted.Hook(func(block *blocks.Block) {
77+
if err := r.OnBlockAccepted(block); err != nil {
7878
r.errorHandler(ierrors.Wrap(err, "failed to store on BlockAccepted in retainer"))
7979
}
8080
}, asyncOpt)
8181

82-
e.Events.BlockGadget.BlockConfirmed.Hook(func(b *blocks.Block) {
83-
if err := r.OnBlockConfirmed(b.ID()); err != nil {
82+
e.Events.BlockGadget.BlockConfirmed.Hook(func(block *blocks.Block) {
83+
if err := r.OnBlockConfirmed(block); err != nil {
8484
r.errorHandler(ierrors.Wrap(err, "failed to store on BlockConfirmed in retainer"))
8585
}
8686
}, asyncOpt)
8787

88-
e.Events.Scheduler.BlockDropped.Hook(func(b *blocks.Block, _ error) {
89-
if err := r.OnBlockDropped(b.ID()); err != nil {
88+
e.Events.Scheduler.BlockDropped.Hook(func(block *blocks.Block, _ error) {
89+
if err := r.OnBlockDropped(block.ID()); err != nil {
9090
r.errorHandler(ierrors.Wrap(err, "failed to store on BlockDropped in retainer"))
9191
}
9292
})
@@ -98,7 +98,7 @@ func NewProvider() module.Provider[*engine.Engine, retainer.BlockRetainer] {
9898
}
9999
}, asyncOpt)
100100

101-
e.Events.BlockRetainer.BlockRetained.LinkTo(r.events.BlockRetained)
101+
e.Events.BlockRetainer.LinkTo(r.events)
102102

103103
r.InitializedEvent().Trigger()
104104
})
@@ -196,12 +196,24 @@ func (r *BlockRetainer) setBlockBooked(blockID iotago.BlockID) error {
196196
return r.UpdateBlockMetadata(blockID, api.BlockStatePending)
197197
}
198198

199-
func (r *BlockRetainer) OnBlockAccepted(blockID iotago.BlockID) error {
200-
return r.UpdateBlockMetadata(blockID, api.BlockStateAccepted)
199+
func (r *BlockRetainer) OnBlockAccepted(block *blocks.Block) error {
200+
if err := r.UpdateBlockMetadata(block.ID(), api.BlockStateAccepted); err != nil {
201+
return err
202+
}
203+
204+
r.events.BlockAccepted.Trigger(block)
205+
206+
return nil
201207
}
202208

203-
func (r *BlockRetainer) OnBlockConfirmed(blockID iotago.BlockID) error {
204-
return r.UpdateBlockMetadata(blockID, api.BlockStateConfirmed)
209+
func (r *BlockRetainer) OnBlockConfirmed(block *blocks.Block) error {
210+
if err := r.UpdateBlockMetadata(block.ID(), api.BlockStateConfirmed); err != nil {
211+
return err
212+
}
213+
214+
r.events.BlockConfirmed.Trigger(block)
215+
216+
return nil
205217
}
206218

207219
func (r *BlockRetainer) OnBlockDropped(blockID iotago.BlockID) error {

Diff for: pkg/retainer/blockretainer/tests/testframework.go

+15-2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ type TestFramework struct {
4040
api iotago.API
4141
test *testing.T
4242

43+
testBlocks map[string]*blocks.Block
4344
testBlockIDs map[string]iotago.BlockID
4445

4546
lastCommittedSlot iotago.SlotIndex
@@ -52,6 +53,7 @@ func NewTestFramework(t *testing.T) *TestFramework {
5253
tf := &TestFramework{
5354
stores: make(map[iotago.SlotIndex]*slotstore.BlockMetadataStore),
5455
lastCommittedSlot: iotago.SlotIndex(0),
56+
testBlocks: make(map[string]*blocks.Block),
5557
testBlockIDs: make(map[string]iotago.BlockID),
5658
api: iotago.V3API(
5759
iotago.NewV3SnapshotProtocolParameters(
@@ -94,6 +96,16 @@ func (tf *TestFramework) finalizeSlot(slot iotago.SlotIndex) {
9496
tf.lastFinalizedSlot = slot
9597
}
9698

99+
func (tf *TestFramework) getBlock(alias string) *blocks.Block {
100+
if block, exists := tf.testBlocks[alias]; exists {
101+
return block
102+
}
103+
104+
require.Errorf(tf.test, nil, "model block not found in the test framework")
105+
106+
return nil
107+
}
108+
97109
func (tf *TestFramework) getBlockID(alias string) iotago.BlockID {
98110
if blkID, exists := tf.testBlockIDs[alias]; exists {
99111
return blkID
@@ -112,6 +124,7 @@ func (tf *TestFramework) createBlock(alias string, slot iotago.SlotIndex) *block
112124

113125
block := blocks.NewBlock(modelBlock)
114126

127+
tf.testBlocks[alias] = block
115128
tf.testBlockIDs[alias] = block.ID()
116129

117130
return block
@@ -142,9 +155,9 @@ func (tf *TestFramework) triggerBlockRetainerAction(alias string, act action) er
142155
case none:
143156
// no action
144157
case eventAccepted:
145-
err = tf.Instance.OnBlockAccepted(tf.getBlockID(alias))
158+
err = tf.Instance.OnBlockAccepted(tf.getBlock(alias))
146159
case eventConfirmed:
147-
err = tf.Instance.OnBlockConfirmed(tf.getBlockID(alias))
160+
err = tf.Instance.OnBlockConfirmed(tf.getBlock(alias))
148161
case eventDropped:
149162
err = tf.Instance.OnBlockDropped(tf.getBlockID(alias))
150163
default:

Diff for: pkg/retainer/events.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,20 @@ import (
1010
type BlockRetainerEvents struct {
1111
// BlockRetained is triggered when a block is stored in the retainer.
1212
BlockRetained *event.Event1[*blocks.Block]
13+
// BlockAccepted is triggered when a block is stored in the retainer caused by a block acceptance event.
14+
BlockAccepted *event.Event1[*blocks.Block]
15+
// BlockConfirmed is triggered when a block is stored in the retainer caused by a block confirmed event.
16+
BlockConfirmed *event.Event1[*blocks.Block]
1317

1418
event.Group[BlockRetainerEvents, *BlockRetainerEvents]
1519
}
1620

1721
// NewBlockRetainerEvents contains the constructor of the Events object (it is generated by a generic factory).
1822
var NewBlockRetainerEvents = event.CreateGroupConstructor(func() (newEvents *BlockRetainerEvents) {
1923
return &BlockRetainerEvents{
20-
BlockRetained: event.New1[*blocks.Block](),
24+
BlockRetained: event.New1[*blocks.Block](),
25+
BlockAccepted: event.New1[*blocks.Block](),
26+
BlockConfirmed: event.New1[*blocks.Block](),
2127
}
2228
})
2329

0 commit comments

Comments
 (0)