Skip to content

Commit

Permalink
[ETCM-814] Cover treasury reward transfer with unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
dzajkowski committed May 13, 2021
1 parent 96ceb22 commit ea4c4d9
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 28 deletions.
3 changes: 1 addition & 2 deletions src/main/scala/io/iohk/ethereum/ledger/BlockExecution.scala
Expand Up @@ -32,8 +32,7 @@ class BlockExecution(
val blockExecResult = {
if (block.hasCheckpoint) {
// block with checkpoint is not executed normally - it's not need to do after execution validation
preExecValidationResult
.map(_ => Seq.empty[Receipt])
preExecValidationResult.map(_ => Seq.empty[Receipt])
} else {
for {
_ <- preExecValidationResult
Expand Down
15 changes: 15 additions & 0 deletions src/test/resources/test-genesis-treasury.json
@@ -0,0 +1,15 @@
{
"difficulty": "0x0400",
"extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
"gasLimit": "0xff1388",
"nonce": "0x0000000000000042",
"ommersHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"timestamp": "0x0",
"coinbase": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"alloc": {
"d7a681378321f472adffb9fdded2712f677e0ba9": {"balance": "1000000000000000000000000000000000000000000"},
"0000000000000000000000000000000000eeeeee": {"balance": "10"},
"0000000000000000000000000000000000000123": {"balance": "20"}
}
}
Expand Up @@ -3,7 +3,6 @@ package io.iohk.ethereum.consensus.blocks
import akka.util.ByteString
import io.iohk.ethereum.blockchain.data.GenesisDataLoader
import io.iohk.ethereum.blockchain.sync.EphemBlockchainTestSetup
import io.iohk.ethereum.consensus.blocks.BlockTimestampProvider
import io.iohk.ethereum.consensus.pow.validators.ValidatorsExecutor
import io.iohk.ethereum.consensus.validators._
import io.iohk.ethereum.crypto
Expand All @@ -12,6 +11,7 @@ import io.iohk.ethereum.domain.BlockHeader.HeaderExtraFields
import io.iohk.ethereum.domain.BlockHeader.HeaderExtraFields.{HefEmpty, HefPostEcip1097}
import io.iohk.ethereum.domain.SignedTransaction.FirstByteOfAddress
import io.iohk.ethereum.domain._
import io.iohk.ethereum.ledger.BlockExecutionError.ValidationAfterExecError
import io.iohk.ethereum.ledger.{BlockExecution, BlockQueue, BlockValidation}
import io.iohk.ethereum.mpt.MerklePatriciaTrie.MPTException
import io.iohk.ethereum.utils._
Expand Down Expand Up @@ -496,25 +496,25 @@ class BlockGeneratorSpec extends AnyFlatSpec with Matchers with ScalaCheckProper
}

it should "generate blocks with the correct extra fields" in {
val table = Table[Boolean, Boolean, Boolean, HeaderExtraFields](
("ecip1098Activated", "ecip1097Activated", "selectedOptOut", "expectedExtraFields"),
val table = Table[Boolean, Boolean, HeaderExtraFields](
("ecip1098Activated", "ecip1097Activated", "expectedExtraFields"),
// No ecip activated
(false, false, true, HefEmpty),
(false, false, false, HefEmpty),
(false, false, HefEmpty),
(false, false, HefEmpty),
// ECIP 1098 activated
(true, false, true, HefEmpty),
(true, false, false, HefEmpty),
(true, false, HefEmpty),
(true, false, HefEmpty),
// ECIP 1097 and 1098 activated
(true, true, true, HefPostEcip1097(None)),
(true, true, false, HefPostEcip1097(None))
(true, true, HefPostEcip1097(None)),
(true, true, HefPostEcip1097(None))
)

forAll(table) { case (ecip1098Activated, ecip1097Activated, selectedOptOut, headerExtraFields) =>
forAll(table) { case (ecip1098Activated, ecip1097Activated, headerExtraFields) =>
val testSetup = new TestSetup {
override lazy val blockchainConfig =
baseBlockchainConfig.copy(ecip1098BlockNumber = 1000, ecip1097BlockNumber = 2000)

override lazy val consensusConfig = buildConsensusConfig().copy(treasuryOptOut = selectedOptOut)
override lazy val consensusConfig = buildConsensusConfig()
}
import testSetup._

Expand All @@ -531,7 +531,51 @@ class BlockGeneratorSpec extends AnyFlatSpec with Matchers with ScalaCheckProper

generatedBlock.block.header.extraFields shouldBe headerExtraFields
}
}

it should "generate a failure if treasury transfer was not made" in {
val setup1 = new TestSetup {
override lazy val blockchainConfig = baseBlockchainConfig.copy(ecip1098BlockNumber = 20000000, treasuryAddress = treasuryAccount, customGenesisFileOpt = Some("test-genesis-treasury.json"))
override lazy val consensusConfig = buildConsensusConfig()
}
val block = {
import setup1._
blockGenerator.generateBlock(bestBlock.get, Seq.empty, Address(testAddress), blockGenerator.emptyX, None).pendingBlock
}

val setup2 = new TestSetup {
override lazy val blockchainConfig = baseBlockchainConfig.copy(ecip1098BlockNumber = 1, treasuryAddress = treasuryAccount, customGenesisFileOpt = Some("test-genesis-treasury.json"))
override lazy val consensusConfig = buildConsensusConfig()
}

{
import setup2._

blockExecution.executeAndValidateBlock(block.block, alreadyValidated = true) shouldBe
Left(ValidationAfterExecError("Block has invalid state root hash, expected 47344722e6c52a85685f9c1bb1e0fe66cfaf6be00c1a752f43cc835fb7415e81 but got 41b63e59d34b5b35a040d496582fc587887af79f762210f6cf55c24d2c307d61"))
}
}

it should "generate a failure if treasury transfer was made to a different treasury account" in {
val setup1 = new TestSetup {
override lazy val blockchainConfig = baseBlockchainConfig.copy(ecip1098BlockNumber = 1, treasuryAddress = maliciousAccount, customGenesisFileOpt = Some("test-genesis-treasury.json"))
override lazy val consensusConfig = buildConsensusConfig()
}
val block = {
import setup1._
blockGenerator.generateBlock(bestBlock.get, Seq.empty, Address(testAddress), blockGenerator.emptyX, None).pendingBlock
}

val setup2 = new TestSetup {
override lazy val blockchainConfig = baseBlockchainConfig.copy(ecip1098BlockNumber = 1, treasuryAddress = treasuryAccount, customGenesisFileOpt = Some("test-genesis-treasury.json"))
override lazy val consensusConfig = buildConsensusConfig()
}

{
import setup2._
blockExecution.executeAndValidateBlock(block.block, alreadyValidated = true) shouldBe
Left(ValidationAfterExecError("Block has invalid state root hash, expected 5bfc811dfee1fecaefbaef2dba502082a8cc72e52260368d83ed6e4ebcecae75 but got 41b63e59d34b5b35a040d496582fc587887af79f762210f6cf55c24d2c307d61"))
}
}

trait TestSetup extends EphemBlockchainTestSetup {
Expand All @@ -553,6 +597,10 @@ class BlockGeneratorSpec extends AnyFlatSpec with Matchers with ScalaCheckProper
payload = ByteString.empty
)

//defined in test-genesis-treasury.json
val treasuryAccount = Address(0xeeeeee)
val maliciousAccount = Address(0x123)

lazy val signedTransaction = SignedTransaction.sign(transaction, keyPair, Some(0x3d.toByte))
lazy val duplicatedSignedTransaction =
SignedTransaction.sign(transaction.copy(gasLimit = 2), keyPair, Some(0x3d.toByte))
Expand Down
Expand Up @@ -14,14 +14,21 @@ import org.scalatest.matchers.should.Matchers

class StdBlockValidatorSpec extends AnyFlatSpec with Matchers with SecureRandomBuilder {

"Block" should "created based on valid data" in {
"Block based on valid data" should "pass validation" in {
val block = Block(validBlockHeader, validBlockBody)
val blockWithCheckpoint = Block(validBlockHeaderWithCheckpoint, BlockBody(Nil, Nil))
StdBlockValidator.validate(block, validReceipts) shouldBe Right(BlockValid)
StdBlockValidator.validate(blockWithCheckpoint, Nil) shouldBe Right(BlockValid)
}

it should "return a failure if block with checkpoint body has a tx" in {
it should "correctly handle the case where a block has no receipts" in {
StdBlockValidator.validate(blockWithOutReceipts, Nil) match {
case Right(validated) => succeed
case _ => fail()
}
}

"Invalid block" should "return a failure if block with checkpoint body has a tx" in {
val block = Block(validBlockHeaderWithCheckpoint, validBlockBody)
StdBlockValidator
.validate(block, Nil) shouldBe Left(CheckpointBlockTransactionsNotEmptyError)
Expand All @@ -33,55 +40,48 @@ class StdBlockValidatorSpec extends AnyFlatSpec with Matchers with SecureRandomB
.validate(block, Nil) shouldBe Left(CheckpointBlockOmmersNotEmptyError)
}

"Block" should "return a failure if created based on invalid transactions header" in {
it should "return a failure if created based on invalid transactions header" in {
StdBlockValidator.validate(Block(wrongTransactionsRootHeader, validBlockBody), validReceipts) match {
case Left(BlockTransactionsHashError) => succeed
case _ => fail()
}
}

"Block" should "return a failure if created based on invalid ommers header" in {
it should "return a failure if created based on invalid ommers header" in {
StdBlockValidator.validate(Block(wrongOmmersHashHeader, validBlockBody), validReceipts) match {
case Left(BlockOmmersHashError) => succeed
case _ => fail()
}
}

"Block" should "return a failure if created based on invalid receipts header" in {
it should "return a failure if created based on invalid receipts header" in {
StdBlockValidator.validate(Block(wrongReceiptsHeader, validBlockBody), validReceipts) match {
case Left(BlockReceiptsHashError) => succeed
case _ => fail()
}
}

"Block" should "return a failure if created based on invalid log bloom header" in {
it should "return a failure if created based on invalid log bloom header" in {
StdBlockValidator.validate(Block(wrongLogBloomBlockHeader, validBlockBody), validReceipts) match {
case Left(BlockLogBloomError) => succeed
case _ => fail()
}
}

"Block" should "return a failure if a block body doesn't corresponds to a block header due to wrong tx hash" in {
it should "return a failure if a block body doesn't corresponds to a block header due to wrong tx hash" in {
StdBlockValidator.validateHeaderAndBody(wrongTransactionsRootHeader, validBlockBody) match {
case Left(BlockTransactionsHashError) => succeed
case _ => fail()
}
}

"Block" should "return a failure if a block body doesn't corresponds to a block header due to wrong ommers hash" in {
it should "return a failure if a block body doesn't corresponds to a block header due to wrong ommers hash" in {
StdBlockValidator.validateHeaderAndBody(wrongOmmersHashHeader, validBlockBody) match {
case Left(BlockOmmersHashError) => succeed
case _ => fail()
}
}

"Block" should "correctly handle the case where a block has no receipts" in {
StdBlockValidator.validate(blockWithOutReceipts, Nil) match {
case Right(validated) => succeed
case _ => fail()
}
}

val validBlockHeader = BlockHeader(
parentHash = ByteString(Hex.decode("8345d132564b3660aa5f27c9415310634b50dbc92579c65a0825d9a255227a71")),
ommersHash = ByteString(Hex.decode("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")),
Expand Down

0 comments on commit ea4c4d9

Please sign in to comment.