Skip to content

Commit ad0c950

Browse files
committed
build staking view from blockdao if indexer height behind
1 parent 34ba472 commit ad0c950

File tree

8 files changed

+179
-21
lines changed

8 files changed

+179
-21
lines changed

action/protocol/staking/protocol.go

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -74,17 +74,25 @@ type (
7474
ReceiptStatus() uint64
7575
}
7676

77+
ContractStakeViewBuilder interface {
78+
Build(ctx context.Context, target uint64) (ContractStakeView, error)
79+
}
80+
7781
// Protocol defines the protocol of handling staking
7882
Protocol struct {
79-
addr address.Address
80-
config Configuration
81-
candBucketsIndexer *CandidatesBucketsIndexer
82-
contractStakingIndexer ContractStakingIndexerWithBucketType
83-
contractStakingIndexerV2 ContractStakingIndexer
84-
contractStakingIndexerV3 ContractStakingIndexer
85-
voteReviser *VoteReviser
86-
patch *PatchStore
87-
helperCtx HelperCtx
83+
addr address.Address
84+
config Configuration
85+
candBucketsIndexer *CandidatesBucketsIndexer
86+
contractStakingIndexer ContractStakingIndexerWithBucketType
87+
contractStakingIndexerV2 ContractStakingIndexer
88+
contractStakingIndexerV3 ContractStakingIndexer
89+
voteReviser *VoteReviser
90+
patch *PatchStore
91+
helperCtx HelperCtx
92+
contractStakingViewBuilder ContractStakeViewBuilder
93+
contractStakingViewV2Builder ContractStakeViewBuilder
94+
contractStakingViewV3Builder ContractStakeViewBuilder
95+
blockStore BlockStore
8896
}
8997

9098
// Configuration is the staking protocol configuration.
@@ -118,6 +126,12 @@ func WithContractStakingIndexerV3(indexer ContractStakingIndexer) Option {
118126
}
119127
}
120128

129+
func WithBlockStore(bs BlockStore) Option {
130+
return func(p *Protocol) {
131+
p.blockStore = bs
132+
}
133+
}
134+
121135
// FindProtocol return a registered protocol from registry
122136
func FindProtocol(registry *protocol.Registry) *Protocol {
123137
if registry == nil {
@@ -196,6 +210,15 @@ func NewProtocol(
196210
for _, opt := range opts {
197211
opt(p)
198212
}
213+
if p.contractStakingIndexer != nil {
214+
p.contractStakingViewBuilder = NewContractStakeViewBuilder(p.contractStakingIndexer, p.blockStore)
215+
}
216+
if p.contractStakingIndexerV2 != nil {
217+
p.contractStakingViewV2Builder = NewContractStakeViewBuilder(p.contractStakingIndexerV2, p.blockStore)
218+
}
219+
if p.contractStakingIndexerV3 != nil {
220+
p.contractStakingViewV3Builder = NewContractStakeViewBuilder(p.contractStakingIndexerV3, p.blockStore)
221+
}
199222
return p, nil
200223
}
201224

@@ -232,22 +255,22 @@ func (p *Protocol) Start(ctx context.Context, sr protocol.StateReader) (protocol
232255
}
233256

234257
c.contractsStake = &contractStakeView{}
235-
if p.contractStakingIndexer != nil {
236-
view, err := p.contractStakingIndexer.StartView(ctx)
258+
if p.contractStakingViewBuilder != nil {
259+
view, err := p.contractStakingViewBuilder.Build(ctx, height)
237260
if err != nil {
238261
return nil, errors.Wrap(err, "failed to start contract staking indexer")
239262
}
240263
c.contractsStake.v1 = view
241264
}
242-
if p.contractStakingIndexerV2 != nil {
243-
view, err := p.contractStakingIndexerV2.StartView(ctx)
265+
if p.contractStakingViewV2Builder != nil {
266+
view, err := p.contractStakingViewV2Builder.Build(ctx, height)
244267
if err != nil {
245268
return nil, errors.Wrap(err, "failed to start contract staking indexer v2")
246269
}
247270
c.contractsStake.v2 = view
248271
}
249-
if p.contractStakingIndexerV3 != nil {
250-
view, err := p.contractStakingIndexerV3.StartView(ctx)
272+
if p.contractStakingViewV3Builder != nil {
273+
view, err := p.contractStakingViewV3Builder.Build(ctx, height)
251274
if err != nil {
252275
return nil, errors.Wrap(err, "failed to start contract staking indexer v3")
253276
}

action/protocol/staking/protocol_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ func TestProtocol_ActiveCandidates(t *testing.T) {
463463
return blkHeight, nil
464464
}).AnyTimes()
465465
csIndexer.EXPECT().StartView(gomock.Any()).Return(nil, nil)
466+
csIndexer.EXPECT().Height().Return(uint64(0), nil).AnyTimes()
466467

467468
v, err := p.Start(ctx, sm)
468469
require.NoError(err)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package staking
2+
3+
import (
4+
"context"
5+
6+
"github.com/pkg/errors"
7+
8+
"github.com/iotexproject/iotex-core/v2/action"
9+
"github.com/iotexproject/iotex-core/v2/blockchain/block"
10+
)
11+
12+
type (
13+
BlockStore interface {
14+
GetBlockByHeight(uint64) (*block.Block, error)
15+
GetReceipts(uint64) ([]*action.Receipt, error)
16+
}
17+
18+
contractStakeViewBuilder struct {
19+
indexer ContractStakingIndexer
20+
blockdao BlockStore
21+
}
22+
)
23+
24+
func NewContractStakeViewBuilder(
25+
indexer ContractStakingIndexer,
26+
blockdao BlockStore,
27+
) *contractStakeViewBuilder {
28+
return &contractStakeViewBuilder{
29+
indexer: indexer,
30+
blockdao: blockdao,
31+
}
32+
}
33+
34+
func (b *contractStakeViewBuilder) Build(ctx context.Context, height uint64) (ContractStakeView, error) {
35+
view, err := b.indexer.StartView(ctx)
36+
if err != nil {
37+
return nil, err
38+
}
39+
indexerHeight, err := b.indexer.Height()
40+
if err != nil {
41+
return nil, err
42+
}
43+
if indexerHeight == height {
44+
return view, nil
45+
} else if indexerHeight > height {
46+
return nil, errors.Errorf("indexer height %d is greater than requested height %d", indexerHeight, height)
47+
}
48+
if b.blockdao == nil {
49+
return nil, errors.Errorf("blockdao is nil, cannot build view for height %d", height)
50+
}
51+
for h := indexerHeight + 1; h <= height; h++ {
52+
blk, err := b.blockdao.GetBlockByHeight(h)
53+
if err != nil {
54+
return nil, errors.Wrapf(err, "failed to get block at height %d", h)
55+
}
56+
if blk.Receipts == nil {
57+
receipts, err := b.blockdao.GetReceipts(h)
58+
if err != nil {
59+
return nil, errors.Wrapf(err, "failed to get receipts at height %d", h)
60+
}
61+
blk.Receipts = receipts
62+
}
63+
if err = view.BuildWithBlock(ctx, blk); err != nil {
64+
return nil, errors.Wrapf(err, "failed to build view with block at height %d", h)
65+
}
66+
}
67+
return view, nil
68+
}

action/protocol/staking/viewdata.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414

1515
"github.com/iotexproject/iotex-core/v2/action"
1616
"github.com/iotexproject/iotex-core/v2/action/protocol"
17+
"github.com/iotexproject/iotex-core/v2/blockchain/block"
1718
)
1819

1920
type (
@@ -24,6 +25,7 @@ type (
2425
Handle(ctx context.Context, receipt *action.Receipt) error
2526
Commit()
2627
BucketsByCandidate(ownerAddr address.Address) ([]*VoteBucket, error)
28+
BuildWithBlock(ctx context.Context, blk *block.Block) error
2729
}
2830
// ViewData is the data that need to be stored in protocol's view
2931
ViewData struct {

blockindex/contractstaking/indexer.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -204,26 +204,32 @@ func (s *Indexer) PutBlock(ctx context.Context, blk *block.Block) error {
204204
if blk.Height() > expectHeight {
205205
return errors.Errorf("invalid block height %d, expect %d", blk.Height(), expectHeight)
206206
}
207+
handler, err := handleBlock(ctx, blk, &s.config, s.cache)
208+
if err != nil {
209+
return errors.Wrapf(err, "failed to put block %d", blk.Height())
210+
}
211+
return s.commit(handler, blk.Height())
212+
}
213+
214+
func handleBlock(ctx context.Context, blk *block.Block, cfg *Config, cache *contractStakingCache) (*contractStakingEventHandler, error) {
207215
// new event handler for this block
208-
handler := newContractStakingEventHandler(s.cache)
216+
handler := newContractStakingEventHandler(cache)
209217

210218
// handle events of block
211219
for _, receipt := range blk.Receipts {
212220
if receipt.Status != uint64(iotextypes.ReceiptStatus_Success) {
213221
continue
214222
}
215223
for _, log := range receipt.Logs() {
216-
if log.Address != s.config.ContractAddress {
224+
if log.Address != cfg.ContractAddress {
217225
continue
218226
}
219227
if err := handler.HandleEvent(ctx, blk.Height(), log); err != nil {
220-
return err
228+
return handler, err
221229
}
222230
}
223231
}
224-
225-
// commit the result
226-
return s.commit(handler, blk.Height())
232+
return handler, nil
227233
}
228234

229235
func (s *Indexer) commit(handler *contractStakingEventHandler, height uint64) error {

blockindex/contractstaking/stakeview.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ import (
66

77
"github.com/iotexproject/iotex-address/address"
88
"github.com/iotexproject/iotex-proto/golang/iotextypes"
9+
"github.com/pkg/errors"
910

1011
"github.com/iotexproject/iotex-core/v2/action"
1112
"github.com/iotexproject/iotex-core/v2/action/protocol"
1213
"github.com/iotexproject/iotex-core/v2/action/protocol/staking"
14+
"github.com/iotexproject/iotex-core/v2/blockchain/block"
1315
)
1416

1517
type stakeView struct {
@@ -95,3 +97,25 @@ func (s *stakeView) Commit() {
9597
s.dirty = nil
9698
}
9799
}
100+
101+
func (s *stakeView) BuildWithBlock(ctx context.Context, blk *block.Block) error {
102+
s.mu.Lock()
103+
defer s.mu.Unlock()
104+
expectHeight := s.clean.Height() + 1
105+
if expectHeight < s.helper.config.ContractDeployHeight {
106+
expectHeight = s.helper.config.ContractDeployHeight
107+
}
108+
if blk.Height() < expectHeight {
109+
return nil
110+
}
111+
if blk.Height() > expectHeight {
112+
return errors.Errorf("invalid block height %d, expect %d", blk.Height(), expectHeight)
113+
}
114+
115+
handler, err := handleBlock(ctx, blk, &s.helper.config, s.clean)
116+
if err != nil {
117+
return err
118+
}
119+
_, delta := handler.Result()
120+
return s.clean.Merge(delta, blk.Height())
121+
}

chainservice/builder.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,7 @@ func (builder *Builder) registerStakingProtocol() error {
685685
if builder.cs.contractStakingIndexerV3 != nil {
686686
opts = append(opts, staking.WithContractStakingIndexerV3(builder.cs.contractStakingIndexerV3))
687687
}
688+
opts = append(opts, staking.WithBlockStore(builder.cs.blockdao))
688689
stakingProtocol, err := staking.NewProtocol(
689690
staking.HelperCtx{
690691
DepositGas: rewarding.DepositGas,

systemcontractindex/stakingindex/stakeview.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@ import (
55
"sync"
66

77
"github.com/iotexproject/iotex-address/address"
8+
"github.com/pkg/errors"
89

910
"github.com/iotexproject/iotex-core/v2/action"
1011
"github.com/iotexproject/iotex-core/v2/action/protocol"
1112
"github.com/iotexproject/iotex-core/v2/action/protocol/staking"
13+
"github.com/iotexproject/iotex-core/v2/blockchain/block"
14+
"github.com/iotexproject/iotex-core/v2/blockchain/genesis"
1215
)
1316

1417
type stakeView struct {
@@ -63,3 +66,33 @@ func (s *stakeView) Handle(ctx context.Context, receipt *action.Receipt) error {
6366
}
6467

6568
func (s *stakeView) Commit() {}
69+
70+
func (s *stakeView) BuildWithBlock(ctx context.Context, blk *block.Block) error {
71+
s.mu.Lock()
72+
defer s.mu.Unlock()
73+
if blk.Height() != s.height+1 {
74+
return errors.Errorf("block height %d does not match stake view height %d", blk.Height(), s.height+1)
75+
}
76+
g, ok := genesis.ExtractGenesisContext(ctx)
77+
if !ok {
78+
return errors.New("failed to extract genesis context")
79+
}
80+
blkCtx := protocol.BlockCtx{
81+
BlockHeight: blk.Height(),
82+
BlockTimeStamp: blk.Timestamp(),
83+
GasLimit: g.BlockGasLimitByHeight(blk.Height()),
84+
Producer: blk.PublicKey().Address(),
85+
BaseFee: blk.BaseFee(),
86+
ExcessBlobGas: blk.BlobGasUsed(),
87+
}
88+
ctx = protocol.WithBlockCtx(ctx, blkCtx)
89+
muted := s.helper.muteHeight > 0 && blk.Height() >= s.helper.muteHeight
90+
handler := newEventHandler(s.helper.bucketNS, s.cache, blkCtx, s.helper.timestamped, muted)
91+
for _, receipt := range blk.Receipts {
92+
if err := s.helper.handleReceipt(ctx, handler, receipt); err != nil {
93+
return errors.Wrapf(err, "failed to handle receipt at height %d", blk.Height())
94+
}
95+
}
96+
s.height = blk.Height()
97+
return nil
98+
}

0 commit comments

Comments
 (0)