Skip to content

Commit b8e013d

Browse files
author
Nicolás Tallar
authored
[FIX] Return executed blocks sorted on execution error (#772)
1 parent 53e6dda commit b8e013d

File tree

4 files changed

+45
-21
lines changed

4 files changed

+45
-21
lines changed

src/main/scala/io/iohk/ethereum/ledger/BlockExecution.scala

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -123,33 +123,33 @@ class BlockExecution(
123123
* @param blocks blocks to be executed
124124
* @param parentTd transaction difficulty of the parent
125125
*
126-
* @return a list of blocks that were correctly executed and an optional [[BlockExecutionError]]
126+
* @return a list of blocks in incremental order that were correctly executed and an optional [[BlockExecutionError]]
127127
*/
128128
def executeAndValidateBlocks(
129129
blocks: List[Block],
130130
parentTd: BigInt
131131
): (List[BlockData], Option[BlockExecutionError]) = {
132132
@tailrec
133133
def go(
134-
executedBlocks: List[BlockData],
135-
remainingBlocks: List[Block],
134+
executedBlocksDecOrder: List[BlockData],
135+
remainingBlocksIncOrder: List[Block],
136136
parentTd: BigInt,
137137
error: Option[BlockExecutionError]
138138
): (List[BlockData], Option[BlockExecutionError]) = {
139-
if (remainingBlocks.isEmpty) {
140-
(executedBlocks.reverse, None)
139+
if (remainingBlocksIncOrder.isEmpty) {
140+
(executedBlocksDecOrder.reverse, None)
141141
} else if (error.isDefined) {
142-
(executedBlocks, error)
142+
(executedBlocksDecOrder.reverse, error)
143143
} else {
144-
val blockToExecute = remainingBlocks.head
144+
val blockToExecute = remainingBlocksIncOrder.head
145145
executeAndValidateBlock(blockToExecute, alreadyValidated = true) match {
146146
case Right(receipts) =>
147147
val td = parentTd + blockToExecute.header.difficulty
148148
val newBlockData = BlockData(blockToExecute, receipts, td)
149149
blockchain.save(newBlockData.block, newBlockData.receipts, newBlockData.td, saveAsBestBlock = true)
150-
go(newBlockData :: executedBlocks, remainingBlocks.tail, td, None)
150+
go(newBlockData :: executedBlocksDecOrder, remainingBlocksIncOrder.tail, td, None)
151151
case Left(executionError) =>
152-
go(executedBlocks, remainingBlocks, 0, Some(executionError))
152+
go(executedBlocksDecOrder, remainingBlocksIncOrder, 0, Some(executionError))
153153
}
154154
}
155155
}

src/test/scala/io/iohk/ethereum/BlockHelpers.scala

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import io.iohk.ethereum.nodebuilder.SecureRandomBuilder
77
import org.bouncycastle.crypto.AsymmetricCipherKeyPair
88
import mouse.all._
99

10-
import scala.math.BigInt
1110
import scala.util.Random
1211

1312
object BlockHelpers extends SecureRandomBuilder {
@@ -37,16 +36,16 @@ object BlockHelpers extends SecureRandomBuilder {
3736
def randomHash(): ByteString =
3837
ObjectGenerators.byteStringOfLengthNGen(32).sample.get
3938

40-
def generateChain(amount: Int, parent: Block, adjustBlock: Block => Block = identity): List[Block] =
41-
(1 to amount).toList.foldLeft[List[Block]](Nil)((generated, _) => {
42-
val theParent = generated.lastOption.getOrElse(parent)
43-
generated :+ (theParent |> generateBlock |> adjustBlock)
44-
})
39+
def generateChain(amount: Int, branchParent: Block, adjustBlock: Block => Block = identity): List[Block] =
40+
(1 to amount).toList.foldLeft[List[Block]](Nil){ (generated, _) =>
41+
val parent = generated.lastOption.getOrElse(branchParent)
42+
generated :+ (parent |> generateBlock |> adjustBlock)
43+
}
4544

46-
def generateBlock(nr: BigInt, parent: Block): Block = {
47-
val header = defaultHeader.copy(
45+
def generateBlock(parent: Block): Block = {
46+
val header = parent.header.copy(
4847
extraData = randomHash(),
49-
number = nr,
48+
number = parent.number + 1,
5049
parentHash = parent.hash,
5150
nonce = ByteString(Random.nextLong())
5251
)
@@ -57,6 +56,4 @@ object BlockHelpers extends SecureRandomBuilder {
5756
Block(header, BlockBody(List(stx.tx), List(ommer)))
5857
}
5958

60-
def generateBlock(parent: Block): Block = generateBlock(parent.number + 1, parent)
61-
6259
}

src/test/scala/io/iohk/ethereum/ledger/BlockExecutionSpec.scala

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import io.iohk.ethereum.crypto.ECDSASignature
88
import io.iohk.ethereum.domain._
99
import io.iohk.ethereum.ledger.Ledger.BlockResult
1010
import io.iohk.ethereum.vm.OutOfGas
11-
import io.iohk.ethereum.{Mocks, ObjectGenerators}
11+
import io.iohk.ethereum.{BlockHelpers, Mocks, ObjectGenerators}
1212
import org.scalatest.matchers.should.Matchers
1313
import org.scalatest.prop.TableFor4
1414
import org.scalatest.wordspec.AnyWordSpec
@@ -91,6 +91,32 @@ class BlockExecutionSpec extends AnyWordSpec with Matchers with ScalaCheckProper
9191
error.isDefined shouldBe true
9292
}
9393

94+
"executing a long branch where the last block is invalid" in new BlockchainSetup {
95+
val chain = BlockHelpers.generateChain(10, validBlockParentBlock)
96+
97+
val mockVm = new MockVM(c =>
98+
createResult(
99+
context = c,
100+
gasUsed = UInt256(0),
101+
gasLimit = UInt256(defaultGasLimit),
102+
gasRefund = UInt256.Zero,
103+
logs = defaultLogs,
104+
addressesToDelete = defaultAddressesToDelete
105+
)
106+
)
107+
val mockValidators = new MockValidatorsFailOnSpecificBlockNumber(chain.last.number)
108+
val newConsensus: TestConsensus = consensus.withVM(mockVm).withValidators(mockValidators)
109+
val blockValidation = new BlockValidation(newConsensus, blockchain, BlockQueue(blockchain, syncConfig))
110+
val blockExecution =
111+
new BlockExecution(blockchain, blockchainConfig, newConsensus.blockPreparator, blockValidation)
112+
113+
val (blocks, error) = blockExecution.executeAndValidateBlocks(chain, defaultBlockHeader.difficulty)
114+
115+
// All blocks but the last should be executed, and they should be returned in incremental order
116+
blocks.map(_.block) shouldBe chain.init
117+
error.isDefined shouldBe true
118+
}
119+
94120
"block with checkpoint and without txs" in new BlockchainSetup {
95121
val checkpoint = ObjectGenerators.fakeCheckpointGen(2, 5).sample.get
96122
val blockWithCheckpoint = new CheckpointBlockGenerator().generate(Block(validBlockParentHeader, validBlockBodyWithNoTxs), checkpoint)

src/test/scala/io/iohk/ethereum/ledger/LedgerTestSetup.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ trait BlockchainSetup extends TestSetup {
157157
val blockchainStorages: storagesInstance.Storages = storagesInstance.storages
158158

159159
val validBlockParentHeader: BlockHeader = defaultBlockHeader.copy(stateRoot = initialWorld.stateRootHash)
160+
val validBlockParentBlock: Block = Block(validBlockParentHeader, BlockBody.empty)
160161
val validBlockHeader: BlockHeader = defaultBlockHeader.copy(
161162
stateRoot = initialWorld.stateRootHash,
162163
parentHash = validBlockParentHeader.hash,

0 commit comments

Comments
 (0)