Skip to content

Commit ece004a

Browse files
committed
Return only the child of the checkpoint block
1 parent 6c88bad commit ece004a

File tree

2 files changed

+45
-11
lines changed

2 files changed

+45
-11
lines changed

src/main/scala/io/iohk/ethereum/jsonrpc/CheckpointingService.scala

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,12 @@ class CheckpointingService(
2121

2222
def getLatestBlock(req: GetLatestBlockRequest): ServiceResponse[GetLatestBlockResponse] = {
2323
lazy val bestBlockNum = blockchain.getBestBlockNumber()
24-
lazy val blockToReturnNum = bestBlockNum - bestBlockNum % req.checkpointingInterval
25-
lazy val isValidParent = req.parentCheckpoint.forall(blockchain.getBlockHeaderByHash(_).isDefined)
24+
lazy val blockToReturnNum =
25+
if (req.checkpointingInterval != 0)
26+
bestBlockNum - bestBlockNum % req.checkpointingInterval
27+
else bestBlockNum
28+
lazy val isValidParent =
29+
req.parentCheckpoint.forall(blockchain.getBlockHeaderByHash(_).exists(_.number < blockToReturnNum))
2630

2731
Task {
2832
blockchain.getBlockByNumber(blockToReturnNum)
@@ -31,7 +35,7 @@ class CheckpointingService(
3135
Task.now(Right(GetLatestBlockResponse(Some(BlockInfo(b.hash, b.number)))))
3236

3337
case Some(_) =>
34-
log.debug("Parent checkpoint is not found in a local blockchain")
38+
log.debug("No checkpoint candidate found for a specified parent")
3539
Task.now(Right(GetLatestBlockResponse(None)))
3640

3741
case None =>
@@ -59,10 +63,10 @@ class CheckpointingService(
5963
}
6064

6165
object CheckpointingService {
62-
case class GetLatestBlockRequest(checkpointingInterval: Int, parentCheckpoint: Option[ByteString])
63-
case class GetLatestBlockResponse(block: Option[BlockInfo])
64-
case class BlockInfo(hash: ByteString, number: BigInt)
66+
final case class GetLatestBlockRequest(checkpointingInterval: Int, parentCheckpoint: Option[ByteString])
67+
final case class GetLatestBlockResponse(block: Option[BlockInfo])
68+
final case class BlockInfo(hash: ByteString, number: BigInt)
6569

66-
case class PushCheckpointRequest(hash: ByteString, signatures: List[ECDSASignature])
67-
case class PushCheckpointResponse()
70+
final case class PushCheckpointRequest(hash: ByteString, signatures: List[ECDSASignature])
71+
final case class PushCheckpointResponse()
6872
}

src/test/scala/io/iohk/ethereum/jsonrpc/CheckpointingServiceSpec.scala

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import io.iohk.ethereum.jsonrpc.CheckpointingService._
99
import io.iohk.ethereum.ledger.Ledger
1010
import io.iohk.ethereum.{Fixtures, NormalPatience, WithActorSystemShutDown}
1111
import monix.execution.Scheduler.Implicits.global
12-
import org.scalacheck.Gen
12+
import org.scalacheck.{Gen, Shrink}
1313
import org.scalamock.scalatest.MockFactory
1414
import org.scalatest.concurrent.ScalaFutures
1515
import org.scalatest.flatspec.AnyFlatSpecLike
@@ -57,9 +57,11 @@ class CheckpointingServiceSpec
5757
n <- Gen.choose(0, k - 1) // distance from best block to checkpointed block
5858
} yield (k, m, n)
5959

60-
val previousCheckpoint = Fixtures.Blocks.ValidBlock.block
60+
val previousCheckpoint = Fixtures.Blocks.Block3125369.block
6161
val hash = previousCheckpoint.hash
6262

63+
implicit val noShrink: Shrink[Int] = Shrink.shrinkAny
64+
6365
forAll(nums) { case (k, m, n) =>
6466
val checkpointedBlockNum: BigInt = k * m
6567
val bestBlockNum: BigInt = checkpointedBlockNum + n
@@ -70,14 +72,42 @@ class CheckpointingServiceSpec
7072
val expectedResponse = GetLatestBlockResponse(Some(BlockInfo(block.hash, block.number)))
7173

7274
(blockchain.getBestBlockNumber _).expects().returning(bestBlockNum)
73-
(blockchain.getBlockHeaderByHash _).expects(hash).returning(Some(previousCheckpoint.header))
75+
(blockchain.getBlockHeaderByHash _).expects(hash).returning(Some(previousCheckpoint.header.copy(number = 0)))
7476
(blockchain.getBlockByNumber _).expects(checkpointedBlockNum).returning(Some(block))
7577
val result = service.getLatestBlock(request)
7678

7779
result.runSyncUnsafe() shouldEqual Right(expectedResponse)
7880
}
7981
}
8082

83+
it should "not return a block that is at the same height as the passed parent checkpoint block" in new TestSetup {
84+
val nums = for {
85+
k <- Gen.choose[Int](1, 10) // checkpointing interval
86+
m <- Gen.choose(0, 1000) // number of checkpoints in the chain
87+
n <- Gen.choose(0, k - 1) // distance from best block to checkpointed block
88+
} yield (k, m, n)
89+
90+
val previousCheckpoint = Fixtures.Blocks.ValidBlock.block
91+
val hash = previousCheckpoint.hash
92+
93+
implicit val noShrink: Shrink[Int] = Shrink.shrinkAny
94+
95+
forAll(nums) { case (k, m, n) =>
96+
val checkpointedBlockNum: BigInt = k * m
97+
val bestBlockNum: BigInt = checkpointedBlockNum + n
98+
99+
val request = GetLatestBlockRequest(k, Some(hash))
100+
val expectedResponse = GetLatestBlockResponse(None)
101+
102+
(blockchain.getBestBlockNumber _).expects().returning(bestBlockNum)
103+
(blockchain.getBlockHeaderByHash _).expects(hash).returning(Some(previousCheckpoint.header.copy(number = bestBlockNum)))
104+
(blockchain.getBlockByNumber _).expects(*).returning(Some(previousCheckpoint))
105+
val result = service.getLatestBlock(request)
106+
107+
result.runSyncUnsafe() shouldEqual Right(expectedResponse)
108+
}
109+
}
110+
81111
it should "return an empty response if the descendant is not a part of a local blockchain" in new TestSetup {
82112
val nums = for {
83113
k <- Gen.choose[Int](1, 10) // checkpointing interval

0 commit comments

Comments
 (0)