Skip to content

Commit 2460fc9

Browse files
committed
Fixed re-applying of blocks after failed extension appending
1 parent 4d25283 commit 2460fc9

File tree

4 files changed

+142
-8
lines changed

4 files changed

+142
-8
lines changed

node/src/main/scala/com/wavesplatform/database/RocksDBWriter.scala

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -725,17 +725,13 @@ class RocksDBWriter(
725725
this.generationPeriodOf(h).foreach { currPeriod => // None checked in Caches
726726
if (nextCommittedGenerators.nonEmpty) {
727727
val nextPeriod = currPeriod.next
728-
729728
rw.put(Keys.committedGenerators(nextPeriod, h), Some(nextCommittedGenerators))
730-
731-
// TODO: Option to not store
732729
rw.put(Keys.commitmentTransactions(nextPeriod, h), commitmentTransactionIds)
733730
}
734731

735732
if (conflictGenerators.nonEmpty) rw.put(Keys.conflictGenerators(currPeriod, h), conflictGenerators)
736733
}
737734

738-
// TODO: Option to not store
739735
rw.put(Keys.generatorBalances(h, rdb.apiHandle), Some(generatorSet.map(x => x.index -> x.balance)))
740736

741737
// TODO: height
@@ -1002,6 +998,7 @@ class RocksDBWriter(
1002998

1003999
log.debug(s"Rolling back to block $targetBlockId at $targetHeight")
10041000

1001+
var currentCommittedGenerators = Option.empty[Seq[(Address, BlsPublicKey)]]
10051002
val discardedBlocks: DiscardedBlocks =
10061003
for (currentHeightInt <- height until targetHeight.toInt by -1; currentHeight = Height(currentHeightInt)) yield {
10071004
val balancesToInvalidate = Seq.newBuilder[(Address, Asset)]
@@ -1141,8 +1138,26 @@ class RocksDBWriter(
11411138
rw.delete(Keys.transactionStateSnapshotAt(currentHeight, num, rdb.txSnapshotHandle))
11421139
}
11431140

1144-
rw.delete(Keys.generatorBalances(currentHeight, rdb.apiHandle))
1141+
// Finality
1142+
var generatorSet = Seq.empty[GeneratorInfo]
11451143
currentPeriod.foreach { currentPeriod =>
1144+
val exactCurrentCommittedGenerators = currentCommittedGenerators.getOrElse {
1145+
val r = committedGenerators(currentPeriod) // The value is probably in the cache
1146+
currentCommittedGenerators = Some(r)
1147+
r
1148+
}.lift // Always has a value for indexes in currentGeneratorBalances
1149+
1150+
val currentGeneratorBalancesKey = Keys.generatorBalances(currentHeight, rdb.apiHandle)
1151+
val currentGeneratorBalances = rw.get(currentGeneratorBalancesKey).getOrElse(Seq.empty) // Always Some here
1152+
generatorSet = for {
1153+
(gi, b) <- currentGeneratorBalances
1154+
(addr, blsPk) <- exactCurrentCommittedGenerators(gi.toInt)
1155+
} yield GeneratorInfo(gi, addr, blsPk, b)
1156+
1157+
// The next discarded block is on a previous period, thus we need to load committed generators
1158+
if (currentHeight == currentPeriod.start) currentCommittedGenerators = None
1159+
1160+
rw.delete(currentGeneratorBalancesKey)
11461161
rw.delete(Keys.conflictGenerators(currentPeriod, currentHeight))
11471162

11481163
val nextPeriod = currentPeriod.next
@@ -1192,7 +1207,7 @@ class RocksDBWriter(
11921207
Some(BlockSnapshot(block.id(), loadTxStateSnapshotsWithStatus(currentHeight, rdb, block.transactionData)))
11931208
} else None
11941209

1195-
DiscardedBlock(block, Caches.toHitSource(discardedMeta), snapshot, Seq.empty) // TODO: generatorBalances
1210+
DiscardedBlock(block, Caches.toHitSource(discardedMeta), snapshot, generatorSet)
11961211
}
11971212

11981213
balancesToInvalidate.result().foreach(discardBalance)

node/src/main/scala/com/wavesplatform/mining/Miner.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ class MinerImpl(
323323
case Some(blockId) =>
324324
def waitUntilBlockAppended(block: BlockId): Task[Unit] =
325325
if (blockchainUpdater.lastBlockId.contains(block)) Task.unit
326-
else Task.defer(waitUntilBlockAppended(block)).delayExecution(100.millis)
326+
else Task.defer(waitUntilBlockAppended(block)).delayExecution(1.second)
327327

328328
waitUntilBlockAppended(blockId)
329329

node/src/main/scala/com/wavesplatform/state/BlockchainUpdaterImpl.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ class BlockchainUpdaterImpl(
488488
)
489489
)
490490
} else None
491-
DiscardedBlock(block, ng.hitSource, snapshot, generatorSet = Seq.empty)
491+
DiscardedBlock(block, ng.hitSource, snapshot, generatorSet = ng.finalizationState.generatorSet)
492492
}.toSeq
493493
blocks ++ liquidBlockData
494494
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package com.wavesplatform.finalization
2+
3+
import cats.syntax.option.*
4+
import com.wavesplatform.api.common.CommonGeneratorsApi.GeneratorEntry
5+
import com.wavesplatform.block.{Block, FinalizationVoting}
6+
import com.wavesplatform.db.WithState.AddrWithBalance
7+
import com.wavesplatform.features.BlockchainFeatures
8+
import com.wavesplatform.network.{ExtensionBlocks, InvalidBlockStorage, PeerDatabase}
9+
import com.wavesplatform.state.*
10+
import com.wavesplatform.state.appender.ExtensionAppender
11+
import com.wavesplatform.test.DomainPresets.WavesSettingsOps
12+
import com.wavesplatform.test.produce
13+
import com.wavesplatform.transaction.{Transaction, TxHelpers}
14+
import monix.execution.Scheduler.Implicits.global
15+
import org.scalactic.Prettifier
16+
17+
class ExtensionAppenderAfterFinalizationSpec extends BaseFinalizationSpec {
18+
private val defaultSettings = DomainPresets.DeterministicFinality
19+
.addFeatures(BlockchainFeatures.SmallerMinimalGeneratingBalance)
20+
.setFeaturesHeight(BlockchainFeatures.DeterministicFinality -> 3)
21+
.configure(
22+
_.copy(
23+
generationPeriodLength = 2,
24+
lightNodeBlockFieldsAbsenceInterval = 0
25+
)
26+
)
27+
28+
private val committedGenerator1 = TxHelpers.signer(0)
29+
30+
private val committedGenerator2 = TxHelpers.signer(1)
31+
private val committedGenerator2Idx = GeneratorIndex(1)
32+
33+
private val committedGenerator3 = TxHelpers.signer(2)
34+
private val committedGenerator3Idx = GeneratorIndex(2)
35+
36+
private val allGenerators = Seq(committedGenerator1, committedGenerator2, committedGenerator3)
37+
38+
"Should re-append blocks of original branch and preserve generators info if failed to append of better fork" in withDomain(
39+
defaultSettings,
40+
AddrWithBalance.enoughBalances(allGenerators*)
41+
) { d =>
42+
def mkCommitments(period: GenerationPeriod = d.blockchain.currentGenerationPeriod.value.next): Seq[Transaction] =
43+
allGenerators.map(x => TxHelpers.commitToGeneration(period.start, x))
44+
45+
def appendBlock(txs: Seq[Transaction] = Nil): Block = {
46+
val b = d.createBlock(txs, generator = committedGenerator1, strictTime = true)
47+
d.appender.appendBlock(b)
48+
b
49+
}
50+
51+
def appendMicroBlock(fv: FinalizationVoting): Unit =
52+
d.appendMicroBlockE(
53+
d.createMicroBlock(signer = committedGenerator1.some, finalizationVoting = fv.some)(TxHelpers.transfer(committedGenerator3))
54+
)
55+
56+
val genesisBlock = d.lastBlock
57+
58+
log.debug("Append block 2 - the first common block")
59+
val altChainBlock1 = d.createBlock(generator = committedGenerator2, strictTime = true)
60+
appendBlock()
61+
val commonBlockHeight = Height(d.blockchain.height)
62+
63+
log.debug("Append block 3 (activation height)")
64+
val altChainBlock2 = d.createBlock(ref = altChainBlock1.id().some, generator = committedGenerator2, strictTime = true)
65+
appendBlock()
66+
67+
log.debug("Append block 4")
68+
appendBlock()
69+
70+
log.debug("Append block 5 with commitments")
71+
appendBlock(mkCommitments())
72+
73+
log.debug("Period 1 with generators")
74+
log.debug("Append block 6")
75+
val endorsedBlockOfPeriod1 = appendBlock()
76+
77+
log.debug("Append block 7 with commitments and conflicting endorsement")
78+
appendBlock(mkCommitments())
79+
appendMicroBlock(mkFinalizationVoting().withConflict(committedGenerator3, committedGenerator3Idx, endorsedBlockOfPeriod1.id()))
80+
81+
log.debug("Period 2 without generators")
82+
log.debug("Append block 8")
83+
appendBlock()
84+
log.debug("Append block 9 with commitments")
85+
appendBlock(mkCommitments())
86+
87+
log.debug("Period 3 with generators")
88+
log.debug("Append block 10 with conflicting endorsement")
89+
val endorsedBlockOfPeriod2 = appendBlock(mkCommitments(d.blockchain.generationPeriodOf(Height(13)).value))
90+
appendMicroBlock(mkFinalizationVoting().withConflict(committedGenerator2, committedGenerator2Idx, endorsedBlockOfPeriod2.id()))
91+
val lastBlockId = d.lastBlockId
92+
93+
def getGenerators = (1 to d.blockchain.height).map(i => d.generatorsApi.generators(Height(i)))
94+
val mainChainBlockGenerators = getGenerators
95+
96+
log.debug("Try to append an extension with a wrong block")
97+
val extensionAppender =
98+
ExtensionAppender(d.blockchain, d.utxPool, d.posSelector, d.testTime, InvalidBlockStorage.NoOp, PeerDatabase.NoOp, global)(null, _)
99+
100+
val altChain = Seq(genesisBlock, altChainBlock1, altChainBlock2)
101+
extensionAppender(ExtensionBlocks(d.blockchain.score + 1, altChain, Map.empty)).runSyncUnsafe() should produce("is invalid")
102+
103+
log.debug("Checks")
104+
withClue("Restored: ") {
105+
d.lastBlockId shouldBe lastBlockId
106+
}
107+
108+
val mainChainBlockGeneratorsAfterRestore = getGenerators
109+
110+
{
111+
given Prettifier = {
112+
case o: IndexedSeq[Seq[GeneratorEntry]] => o.mkString("\n")
113+
case o => o.toString
114+
}
115+
116+
mainChainBlockGeneratorsAfterRestore shouldBe mainChainBlockGenerators
117+
}
118+
}
119+
}

0 commit comments

Comments
 (0)