Skip to content

Commit dbe13d2

Browse files
authored
Restore mined transactions to mempool after blocks rollback (#768)
1 parent 39ae2f1 commit dbe13d2

12 files changed

+169
-65
lines changed

blockchain/blockchain.go

+14-8
Original file line numberDiff line numberDiff line change
@@ -2095,10 +2095,10 @@ func (chain *Blockchain) ValidateSubChain(startHeight uint64, blocks []types.Blo
20952095
return nil
20962096
}
20972097

2098-
func (chain *Blockchain) ResetTo(height uint64) error {
2098+
func (chain *Blockchain) ResetTo(height uint64) (revertedTxs []*types.Transaction, err error) {
20992099
prevHead := chain.Head.Height()
21002100
if err := chain.appState.ResetTo(height); err != nil {
2101-
return errors.WithMessage(err, "state is corrupted, try to resync from scratch")
2101+
return nil, errors.WithMessage(err, "state is corrupted, try to resync from scratch")
21022102
}
21032103
chain.setHead(height, nil)
21042104

@@ -2107,11 +2107,17 @@ func (chain *Blockchain) ResetTo(height uint64) error {
21072107
if hash == (common.Hash{}) {
21082108
continue
21092109
}
2110+
block := chain.GetBlock(hash)
2111+
if block != nil {
2112+
for _, tx := range block.Body.Transactions {
2113+
revertedTxs = append(revertedTxs, tx)
2114+
}
2115+
}
21102116
chain.repo.RemoveHeader(hash)
21112117
chain.repo.RemoveCanonicalHash(h)
21122118
}
2113-
2114-
return nil
2119+
chain.bus.Publish(&events.BlockchainResetEvent{Header: chain.Head, RevertedTxs: revertedTxs})
2120+
return revertedTxs, nil
21152121
}
21162122

21172123
func (chain *Blockchain) EnsureIntegrity() error {
@@ -2120,16 +2126,16 @@ func (chain *Blockchain) EnsureIntegrity() error {
21202126
chain.Head.IdentityRoot() != chain.appState.IdentityState.Root() {
21212127
wasReset = true
21222128
resetTo := uint64(0)
2123-
for h, tryCnt := chain.Head.Height()-1, 0; h >= 1 && tryCnt < int(state.MaxSavedStatesCount)+1; h, tryCnt = h-1, tryCnt+1 {
2124-
if chain.appState.State.HasVersion(h) && chain.appState.IdentityState.HasVersion(h) {
2129+
for h, tryCnt := chain.Head.Height()-1, 0; h >= 1 && tryCnt < state.MaxSavedStatesCount+1; h, tryCnt = h-1, tryCnt+1 {
2130+
if chain.appState.State.HasVersion(h) && chain.appState.IdentityState.HasVersion(h) {
21252131
resetTo = h
21262132
break
21272133
}
21282134
}
21292135
if resetTo == 0 {
21302136
return errors.New("state db is corrupted, try to delete idenachain.db folder from your data directory and sync from scratch")
21312137
}
2132-
if err := chain.ResetTo(resetTo); err != nil {
2138+
if _, err := chain.ResetTo(resetTo); err != nil {
21332139
return err
21342140
}
21352141
}
@@ -2239,7 +2245,7 @@ func (chain *Blockchain) ReadSnapshotManifest() *snapshot.Manifest {
22392245
}
22402246
return &snapshot.Manifest{
22412247
Cid: cid,
2242-
CidV2: cidV2,
2248+
CidV2: cidV2,
22432249
Root: root,
22442250
Height: height,
22452251
}

blockchain/blockchain_mocks.go

+28-6
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ import (
1616
"github.com/idena-network/idena-go/secstore"
1717
"github.com/idena-network/idena-go/stats/collector"
1818
"github.com/idena-network/idena-go/subscriptions"
19+
"github.com/shopspring/decimal"
1920
"github.com/tendermint/tm-db"
21+
"math/big"
2022
)
2123

2224
func NewTestBlockchainWithConfig(withIdentity bool, conf *config.ConsensusConf, valConf *config.ValidationConfig, alloc map[common.Address]config.GenesisAllocation, queueSlots int, executableSlots int, executableLimit int, queueLimit int) (*TestBlockchain, *appstate.AppState, *mempool.TxPool, *ecdsa.PrivateKey) {
@@ -67,7 +69,7 @@ func NewTestBlockchainWithConfig(withIdentity bool, conf *config.ConsensusConf,
6769
appState.Initialize(chain.Head.Height())
6870
txPool.Initialize(chain.Head, secStore.GetAddress(), false)
6971

70-
return &TestBlockchain{db, chain}, appState, txPool, key
72+
return &TestBlockchain{db, chain, 0}, appState, txPool, key
7173
}
7274

7375
func NewTestBlockchain(withIdentity bool, alloc map[common.Address]config.GenesisAllocation) (*TestBlockchain, *appstate.AppState, *mempool.TxPool, *ecdsa.PrivateKey) {
@@ -89,7 +91,9 @@ func NewCustomTestBlockchain(blocksCount int, emptyBlocksCount int, key *ecdsa.P
8991
Network: 0x99,
9092
Consensus: consensusCfg,
9193
GenesisConf: &config.GenesisConf{
92-
Alloc: nil,
94+
Alloc: map[common.Address]config.GenesisAllocation{
95+
addr: {Balance: big.NewInt(0).Mul(big.NewInt(100), common.DnaBase)},
96+
},
9397
GodAddress: addr,
9498
FirstCeremonyTime: 4070908800, //01.01.2099
9599
},
@@ -120,15 +124,20 @@ func NewCustomTestBlockchainWithConfig(blocksCount int, emptyBlocksCount int, ke
120124
chain.InitializeChain()
121125
appState.Initialize(chain.Head.Height())
122126

123-
result := &TestBlockchain{db, chain}
124-
result.GenerateBlocks(blocksCount).GenerateEmptyBlocks(emptyBlocksCount)
127+
result := &TestBlockchain{db, chain, 0}
128+
result.GenerateBlocks(blocksCount, 0).GenerateEmptyBlocks(emptyBlocksCount)
125129
txPool.Initialize(chain.Head, secStore.GetAddress(), false)
126130
return result, appState
127131
}
128132

129133
type TestBlockchain struct {
130134
db db.DB
131135
*Blockchain
136+
coinbaseTxNonce uint32
137+
}
138+
139+
func (chain *TestBlockchain) AddTx(tx *types.Transaction) error {
140+
return chain.txpool.AddExternalTxs(tx)
132141
}
133142

134143
func (chain *TestBlockchain) Copy() (*TestBlockchain, *appstate.AppState) {
@@ -164,7 +173,8 @@ func (chain *TestBlockchain) Copy() (*TestBlockchain, *appstate.AppState) {
164173
copy := NewBlockchain(cfg, db, txPool, appState, ipfs.NewMemoryIpfsProxy(), chain.secStore, bus, offline, keyStore, subManager, upgrader)
165174
copy.InitializeChain()
166175
appState.Initialize(copy.Head.Height())
167-
return &TestBlockchain{db, copy}, appState
176+
txPool.Initialize(chain.Head, chain.secStore.GetAddress(), false)
177+
return &TestBlockchain{db, copy, appState.State.GetNonce(chain.secStore.GetAddress())}, appState
168178
}
169179

170180
func (chain *TestBlockchain) addCert(block *types.Block) {
@@ -183,8 +193,20 @@ func (chain *TestBlockchain) addCert(block *types.Block) {
183193
chain.WriteCertificate(block.Header.Hash(), cert.Compress(), true)
184194
}
185195

186-
func (chain *TestBlockchain) GenerateBlocks(count int) *TestBlockchain {
196+
func (chain *TestBlockchain) GenerateBlocks(count int, txsInBlock int) *TestBlockchain {
187197
for i := 0; i < count; i++ {
198+
for j := 0; j < txsInBlock; j++ {
199+
tx := BuildTx(chain.appState, chain.coinBaseAddress, &chain.coinBaseAddress, types.SendTx, decimal.Zero,
200+
decimal.New(20, 0), decimal.Zero, chain.coinbaseTxNonce, 0, nil)
201+
tx, err := chain.secStore.SignTx(tx)
202+
if err != nil {
203+
panic(err)
204+
}
205+
if err = chain.txpool.AddExternalTxs(tx); err != nil {
206+
panic(err)
207+
}
208+
}
209+
188210
block := chain.ProposeBlock([]byte{})
189211
block.Block.Header.ProposedHeader.Time = chain.Head.Time() + 20
190212
err := chain.AddBlock(block.Block, nil, collector.NewStatsCollector())

blockchain/blockchain_test.go

+23-23
Original file line numberDiff line numberDiff line change
@@ -391,10 +391,10 @@ func Test_applyVrfProposerThreshold(t *testing.T) {
391391
chain.GenerateEmptyBlocks(10)
392392
require.Equal(t, 10, chain.appState.State.EmptyBlocksCount())
393393

394-
chain.GenerateBlocks(5)
394+
chain.GenerateBlocks(5, 0)
395395
require.Equal(t, 10, chain.appState.State.EmptyBlocksCount())
396396

397-
chain.GenerateBlocks(100)
397+
chain.GenerateBlocks(100, 0)
398398
require.Equal(t, 0.5, chain.appState.State.VrfProposerThreshold())
399399
}
400400

@@ -557,7 +557,7 @@ func Test_Blockchain_OnlineStatusSwitch(t *testing.T) {
557557
require.NoError(err)
558558

559559
// apply pending status switch
560-
chain.GenerateBlocks(1)
560+
chain.GenerateBlocks(1, 0)
561561
require.Equal(1, len(state.State.StatusSwitchAddresses()))
562562
require.False(state.IdentityState.IsOnline(addr))
563563

@@ -567,26 +567,26 @@ func Test_Blockchain_OnlineStatusSwitch(t *testing.T) {
567567
require.Error(err, "should not validate tx if switch is already pending")
568568

569569
// switch status to online
570-
chain.GenerateBlocks(3)
570+
chain.GenerateBlocks(3, 0)
571571
require.Equal(uint64(10), chain.Head.Height())
572572
require.Zero(len(state.State.StatusSwitchAddresses()))
573573
require.True(state.IdentityState.IsOnline(addr))
574574
require.True(chain.Head.Flags().HasFlag(types.IdentityUpdate))
575575

576576
// fail to switch online again
577-
chain.GenerateBlocks(5)
577+
chain.GenerateBlocks(5, 0)
578578
tx, _ = chain.secStore.SignTx(BuildTx(state, addr, nil, types.OnlineStatusTx, decimal.Zero, decimal.New(20, 0), decimal.Zero, 0, 0, attachments.CreateOnlineStatusAttachment(true)))
579579
err = chain.txpool.AddInternalTx(tx)
580580
require.Error(err, "should not validate tx if identity already has online status")
581581

582582
// add pending request to switch offline
583-
chain.GenerateBlocks(4)
583+
chain.GenerateBlocks(4, 0)
584584
tx, _ = chain.secStore.SignTx(BuildTx(state, addr, nil, types.OnlineStatusTx, decimal.Zero, decimal.New(20, 0), decimal.Zero, 0, 0, attachments.CreateOnlineStatusAttachment(false)))
585585
err = chain.txpool.AddInternalTx(tx)
586586
require.NoError(err)
587587

588588
// switch status to offline
589-
chain.GenerateBlocks(1)
589+
chain.GenerateBlocks(1, 0)
590590
require.Equal(uint64(20), chain.Head.Height())
591591
require.Zero(len(state.State.StatusSwitchAddresses()))
592592
require.False(state.IdentityState.IsOnline(addr))
@@ -596,33 +596,33 @@ func Test_Blockchain_OnlineStatusSwitch(t *testing.T) {
596596
tx, _ = chain.secStore.SignTx(BuildTx(state, addr, nil, types.OnlineStatusTx, decimal.Zero, decimal.New(20, 0), decimal.Zero, 0, 0, attachments.CreateOnlineStatusAttachment(true)))
597597
err = chain.txpool.AddInternalTx(tx)
598598
require.NoError(err)
599-
chain.GenerateBlocks(1)
599+
chain.GenerateBlocks(1, 0)
600600

601601
require.Equal(1, len(state.State.StatusSwitchAddresses()))
602602

603603
// remove pending request to switch online
604604
tx, _ = chain.secStore.SignTx(BuildTx(state, addr, nil, types.OnlineStatusTx, decimal.Zero, decimal.New(20, 0), decimal.Zero, 0, 0, attachments.CreateOnlineStatusAttachment(false)))
605605
err = chain.txpool.AddInternalTx(tx)
606606
require.NoError(err)
607-
chain.GenerateBlocks(1)
607+
chain.GenerateBlocks(1, 0)
608608

609609
require.Zero(len(state.State.StatusSwitchAddresses()))
610610

611611
// 30th block should not update identity statuses, no pending requests
612-
chain.GenerateBlocks(8)
612+
chain.GenerateBlocks(8, 0)
613613
require.Equal(uint64(30), chain.Head.Height())
614614
require.False(state.IdentityState.IsOnline(addr))
615615
require.False(chain.Head.Flags().HasFlag(types.IdentityUpdate))
616616

617-
chain.GenerateBlocks(70)
617+
chain.GenerateBlocks(70, 0)
618618
require.Equal(uint64(100), chain.Head.Height())
619619

620620
tx, _ = chain.secStore.SignTx(BuildTx(state, addr, nil, types.OnlineStatusTx, decimal.Zero, decimal.New(20, 0), decimal.Zero, 0, 0, attachments.CreateOnlineStatusAttachment(true)))
621621
err = chain.txpool.AddInternalTx(tx)
622622
require.Nil(err)
623623

624624
// switch status to online
625-
chain.GenerateBlocks(10)
625+
chain.GenerateBlocks(10, 0)
626626

627627
require.True(state.IdentityState.IsOnline(addr))
628628
state.State.AddDelayedPenalty(common.Address{0x2})
@@ -634,7 +634,7 @@ func Test_Blockchain_OnlineStatusSwitch(t *testing.T) {
634634
tx, _ = chain.secStore.SignTx(BuildTx(state, addr, nil, types.OnlineStatusTx, decimal.Zero, decimal.New(20, 0), decimal.Zero, 0, 0, attachments.CreateOnlineStatusAttachment(true)))
635635
err = chain.txpool.AddInternalTx(tx)
636636
require.Nil(err)
637-
chain.GenerateBlocks(1)
637+
chain.GenerateBlocks(1, 0)
638638

639639
require.Equal([]common.Address{{0x2}, {0x3}}, state.State.DelayedOfflinePenalties())
640640
require.True(state.IdentityState.IsOnline(addr))
@@ -643,7 +643,7 @@ func Test_Blockchain_OnlineStatusSwitch(t *testing.T) {
643643
state.State.AddDelayedPenalty(addr)
644644
state.Commit(nil)
645645
chain.CommitState()
646-
chain.GenerateBlocks(10)
646+
chain.GenerateBlocks(10, 0)
647647

648648
require.False(state.IdentityState.IsOnline(addr))
649649
require.True(state.State.GetPenalty(addr).Sign() > 0)
@@ -695,7 +695,7 @@ func Test_ApplySubmitCeremonyTxs(t *testing.T) {
695695
err = chain.txpool.AddInternalTx(signed)
696696
require.NoError(t, err)
697697

698-
chain.GenerateBlocks(3)
698+
chain.GenerateBlocks(3, 0)
699699

700700
require.True(t, app.State.HasValidationTx(addr, types.SubmitAnswersHashTx))
701701
require.False(t, app.State.HasValidationTx(addr, types.SubmitShortAnswersTx))
@@ -709,7 +709,7 @@ func Test_ApplySubmitCeremonyTxs(t *testing.T) {
709709
err = chain.txpool.AddInternalTx(signed)
710710
require.NoError(t, err)
711711

712-
chain.GenerateBlocks(1)
712+
chain.GenerateBlocks(1, 0)
713713
require.True(t, app.State.HasValidationTx(addr, types.SubmitAnswersHashTx))
714714
require.True(t, app.State.HasValidationTx(addr, types.EvidenceTx))
715715

@@ -736,7 +736,7 @@ func Test_Blockchain_GodAddressInvitesLimit(t *testing.T) {
736736
receiver := crypto.PubkeyToAddress(keyReceiver.PublicKey)
737737
tx, _ := chain.secStore.SignTx(BuildTx(state, addr, &receiver, types.InviteTx, decimal.Zero, decimal.New(2, 0), decimal.Zero, 0, 0, nil))
738738
require.NoError(chain.txpool.AddInternalTx(tx))
739-
chain.GenerateBlocks(1)
739+
chain.GenerateBlocks(1, 0)
740740
}
741741

742742
keyReceiver, _ := crypto.GenerateKey()
@@ -998,18 +998,18 @@ func Test_Delegation(t *testing.T) {
998998
require.NoError(t, addTx(keys[2], types.DelegateTx, 1, 0, &pool2, nil))
999999
require.NoError(t, addTx(keys[3], types.DelegateTx, 1, 0, &pool3, nil))
10001000

1001-
chain.GenerateBlocks(1)
1001+
chain.GenerateBlocks(1, 0)
10021002
require.ErrorIs(t, validation.InvalidRecipient, addTx(pool3Key, types.DelegateTx, 1, 0, &pool3, nil))
10031003
require.NoError(t, addTx(pool3Key, types.DelegateTx, 1, 0, &pool2, nil))
10041004

1005-
chain.GenerateBlocks(1)
1005+
chain.GenerateBlocks(1, 0)
10061006

10071007
require.Len(t, appState.State.Delegations(), 5)
10081008

10091009
require.NoError(t, addTx(keys[0], types.OnlineStatusTx, 2, 0, nil, attachments.CreateOnlineStatusAttachment(false)))
10101010
require.NoError(t, addTx(keys[3], types.OnlineStatusTx, 2, 0, nil, attachments.CreateOnlineStatusAttachment(true)))
10111011

1012-
chain.GenerateBlocks(1)
1012+
chain.GenerateBlocks(1, 0)
10131013
chain.GenerateEmptyBlocks(50)
10141014

10151015
require.False(t, appState.ValidatorsCache.IsOnlineIdentity(addrs[0]))
@@ -1027,7 +1027,7 @@ func Test_Delegation(t *testing.T) {
10271027

10281028
addr3 := crypto.PubkeyToAddress(keys[3].PublicKey)
10291029
require.NoError(t, addTx(pool3Key, types.KillDelegatorTx, 2, 0, &addr3, nil))
1030-
chain.GenerateBlocks(1)
1030+
chain.GenerateBlocks(1, 0)
10311031

10321032
require.Equal(t, 0, appState.ValidatorsCache.PoolSize(pool3))
10331033
require.False(t, appState.ValidatorsCache.IsPool(pool3))
@@ -1042,10 +1042,10 @@ func Test_Delegation(t *testing.T) {
10421042
require.NoError(t, addTx(keys[1], types.UndelegateTx, 1, 1, nil, nil))
10431043
require.NoError(t, addTx(keys[2], types.UndelegateTx, 1, 1, nil, nil))
10441044

1045-
chain.GenerateBlocks(1)
1045+
chain.GenerateBlocks(1, 0)
10461046
require.Len(t, appState.State.Delegations(), 2)
10471047

1048-
chain.GenerateBlocks(50)
1048+
chain.GenerateBlocks(50, 0)
10491049
require.Len(t, appState.State.Delegations(), 0)
10501050

10511051
require.Equal(t, 0, appState.ValidatorsCache.PoolSize(pool2))

consensus/engine.go

+12-2
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,13 @@ func (engine *Engine) loop() {
160160
if err := engine.downloader.SyncBlockchain(engine.forkResolver); err != nil {
161161
engine.synced = false
162162
if engine.forkResolver.HasLoadedFork() {
163-
engine.forkResolver.ApplyFork()
163+
if revertedTxs, err := engine.forkResolver.ApplyFork(); err == nil {
164+
if len(revertedTxs) > 0 {
165+
engine.txpool.AddExternalTxs(revertedTxs...)
166+
}
167+
} else {
168+
engine.log.Warn("fork apply error", "err", err)
169+
}
164170
} else {
165171
engine.log.Warn("syncing error", "err", err)
166172
time.Sleep(time.Second * 5)
@@ -229,8 +235,12 @@ func (engine *Engine) loop() {
229235
engine.log.Info("Binary Ba is failed", "err", err)
230236

231237
if err == ForkDetected {
232-
if err = engine.forkResolver.ApplyFork(); err != nil {
238+
if revertedTxs, err := engine.forkResolver.ApplyFork(); err != nil {
233239
engine.log.Error("error occurred during applying of fork", "err", err)
240+
} else {
241+
if len(revertedTxs) > 0 {
242+
engine.txpool.AddExternalTxs(revertedTxs...)
243+
}
234244
}
235245
}
236246
continue

consensus/fork_resolver.go

+8-7
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ func (resolver *ForkResolver) checkForkSize(fork []types.BlockBundle) error {
144144
return errors.New("fork has worse seed")
145145
}
146146

147-
func (resolver *ForkResolver) applyFork(commonHeight uint64, fork []types.BlockBundle) error {
147+
func (resolver *ForkResolver) applyFork(commonHeight uint64, fork []types.BlockBundle) ([]*types.Transaction, error) {
148148

149149
defer func() {
150150
resolver.applicableFork = nil
@@ -154,21 +154,22 @@ func (resolver *ForkResolver) applyFork(commonHeight uint64, fork []types.BlockB
154154
d.ClearPotentialForks()
155155
}
156156
}()
157-
158-
if err := resolver.chain.ResetTo(commonHeight); err != nil {
159-
return err
157+
var revertedTxs []*types.Transaction
158+
var err error
159+
if revertedTxs, err = resolver.chain.ResetTo(commonHeight); err != nil {
160+
return nil, err
160161
}
161162
for _, bundle := range fork {
162163
if err := resolver.chain.AddBlock(bundle.Block, nil, resolver.statsCollector); err != nil {
163-
return err
164+
return nil, err
164165
}
165166
resolver.chain.WriteCertificate(bundle.Block.Hash(), bundle.Cert, false)
166167
}
167168

168-
return nil
169+
return revertedTxs, nil
169170
}
170171

171-
func (resolver *ForkResolver) ApplyFork() error {
172+
func (resolver *ForkResolver) ApplyFork() (revertedTxs []*types.Transaction, err error) {
172173
if !resolver.HasLoadedFork() {
173174
panic("resolver hasn't applicable fork")
174175
}

0 commit comments

Comments
 (0)