Skip to content

Commit e1d275b

Browse files
authored
Merge pull request #958 from input-output-hk/fix-get-checkpoint-rpc
Return only the child of the checkpoint block
2 parents 6c88bad + 508b754 commit e1d275b

File tree

2 files changed

+40
-10
lines changed

2 files changed

+40
-10
lines changed

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

+12-8
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

+28-2
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ 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

6363
forAll(nums) { case (k, m, n) =>
@@ -70,14 +70,40 @@ class CheckpointingServiceSpec
7070
val expectedResponse = GetLatestBlockResponse(Some(BlockInfo(block.hash, block.number)))
7171

7272
(blockchain.getBestBlockNumber _).expects().returning(bestBlockNum)
73-
(blockchain.getBlockHeaderByHash _).expects(hash).returning(Some(previousCheckpoint.header))
73+
(blockchain.getBlockHeaderByHash _).expects(hash).returning(Some(previousCheckpoint.header.copy(number = 0)))
7474
(blockchain.getBlockByNumber _).expects(checkpointedBlockNum).returning(Some(block))
7575
val result = service.getLatestBlock(request)
7676

7777
result.runSyncUnsafe() shouldEqual Right(expectedResponse)
7878
}
7979
}
8080

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

0 commit comments

Comments
 (0)