Skip to content

[ETCM-44] Treasury block reward distribution #694

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Sep 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
675 changes: 374 additions & 301 deletions insomnia_workspace.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ object BlockchainTestConfig {
atlantisBlockNumber = Long.MaxValue,
aghartaBlockNumber = Long.MaxValue,
phoenixBlockNumber = Long.MaxValue,
ecip1098BlockNumber = Long.MaxValue
ecip1098BlockNumber = Long.MaxValue,
treasuryAddress = Address(0)
)

val FrontierConfig = BaseBlockchainConfig.copy(
Expand Down
5 changes: 3 additions & 2 deletions src/it/scala/io/iohk/ethereum/txExecTest/ECIP1017Test.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package io.iohk.ethereum.txExecTest

import java.util.concurrent.Executors

import io.iohk.ethereum.domain.{BlockchainImpl, Receipt, UInt256}
import io.iohk.ethereum.domain.{Address, BlockchainImpl, Receipt, UInt256}
import io.iohk.ethereum.ledger._
import io.iohk.ethereum.txExecTest.util.FixtureProvider
import io.iohk.ethereum.utils.{BlockchainConfig, MonetaryPolicyConfig}
Expand Down Expand Up @@ -45,7 +45,8 @@ class ECIP1017Test extends AnyFlatSpec with Matchers {
aghartaBlockNumber = Long.MaxValue,
phoenixBlockNumber = Long.MaxValue,
petersburgBlockNumber = Long.MaxValue,
ecip1098BlockNumber = Long.MaxValue
ecip1098BlockNumber = Long.MaxValue,
treasuryAddress = Address(0)
)
val ec = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(4))

Expand Down
5 changes: 3 additions & 2 deletions src/it/scala/io/iohk/ethereum/txExecTest/ForksTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package io.iohk.ethereum.txExecTest

import java.util.concurrent.Executors

import io.iohk.ethereum.domain.{BlockchainImpl, Receipt, UInt256}
import io.iohk.ethereum.domain.{Address, BlockchainImpl, Receipt, UInt256}
import io.iohk.ethereum.ledger.{BlockExecution, BlockQueue, BlockValidation}
import io.iohk.ethereum.txExecTest.util.FixtureProvider
import io.iohk.ethereum.utils.{BlockchainConfig, MonetaryPolicyConfig}
Expand Down Expand Up @@ -44,7 +44,8 @@ class ForksTest extends AnyFlatSpec with Matchers {
aghartaBlockNumber = Long.MaxValue,
phoenixBlockNumber = Long.MaxValue,
petersburgBlockNumber = Long.MaxValue,
ecip1098BlockNumber = Long.MaxValue
ecip1098BlockNumber = Long.MaxValue,
treasuryAddress = Address(0)
)

val noErrors = a[Right[_, Seq[Receipt]]]
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/chains/etc-chain.conf
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@

# Proto-treasury fork block number (ETC only, but deactivated for now)
# https://ecips.ethereumclassic.org/ECIPs/ecip-1098
treasury-address = "0011223344556677889900112233445566778899"
ecip1098-block-number = "1000000000000000000"

# DAO fork configuration (Ethereum HF/Classic split)
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/chains/eth-chain.conf
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@

# Proto-treasury fork block number (ETC only, but deactivated for now)
# https://ecips.ethereumclassic.org/ECIPs/ecip-1098
treasury-address = "0011223344556677889900112233445566778899"
ecip1098-block-number = "1000000000000000000"

# DAO fork configuration (Ethereum HF/Classic split)
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/chains/mordor-chain.conf
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@

# Proto-treasury fork block number (ETC only, but deactivated for now)
# https://ecips.ethereumclassic.org/ECIPs/ecip-1098
treasury-address = "0011223344556677889900112233445566778899"
ecip1098-block-number = "1000000000000000000"

# DAO fork configuration (Ethereum HF/Classic split)
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/chains/ropsten-chain.conf
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@

# Proto-treasury fork block number (ETC only, but deactivated for now)
# https://ecips.ethereumclassic.org/ECIPs/ecip-1098
treasury-address = "0011223344556677889900112233445566778899"
ecip1098-block-number = "1000000000000000000"

# DAO fork configuration (Ethereum HF/Classic split)
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/chains/test-chain.conf
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@

# Proto-treasury fork block number (ETC only, but deactivated for now)
# https://ecips.ethereumclassic.org/ECIPs/ecip-1098
treasury-address = "0011223344556677889900112233445566778899"
ecip1098-block-number = "1000000000000000000"

# DAO fork configuration (Ethereum HF/Classic split)
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/chains/testnet-internal-chain.conf
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@

# Proto-treasury fork block number (ETC only, but deactivated for now)
# https://ecips.ethereumclassic.org/ECIPs/ecip-1098
treasury-address = "0011223344556677889900112233445566778899"
ecip1098-block-number = "1000000000000000000"

# DAO fork configuration (Ethereum HF/Classic split)
Expand Down
80 changes: 61 additions & 19 deletions src/main/scala/io/iohk/ethereum/ledger/BlockPreparator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import io.iohk.ethereum.domain.UInt256._
import io.iohk.ethereum.domain._
import io.iohk.ethereum.ledger.BlockExecutionError.{StateBeforeFailure, TxsExecutionError}
import io.iohk.ethereum.ledger.Ledger._
import io.iohk.ethereum.ledger.BlockPreparator._
import io.iohk.ethereum.utils.{BlockchainConfig, Logger}
import io.iohk.ethereum.vm.{PC => _, _}

Expand Down Expand Up @@ -38,33 +39,66 @@ class BlockPreparator(
)

/**
* This function updates state in order to pay rewards based on YP section 11.3
* This function updates the state in order to pay rewards based on YP section 11.3 and with the required
* modifications due to ECIP1097:
* 1. Reward for block is distributed as:
* a. If treasury is disabled or it's has been selfdestructed:
* Pay 100% of it to the miner
* b. If a. isn't true and the miner opted out:
* Pay 80% of it to the miner
* Never generate the 20% else
* c. If a. isn't true and the miner opted in:
* Pay 80% of it to the miner
* Pay 20% of it to the treasury contract
* 2. Miner is payed a reward for the inclusion of ommers
* 3. Ommers's miners are payed a reward for their inclusion in this block
*
* @param block
* @param worldStateProxy
* @return
* @param block the block being processed
* @param worldStateProxy the initial state
* @return the state after paying the apropiate reward to who corresponds
*/
private[ledger] def payBlockReward(block: Block, worldStateProxy: InMemoryWorldStateProxy): InMemoryWorldStateProxy = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just minor comment: I would update scaladoc, to reflect all the changes and maybe mention treasury spec.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just did! I'm not sure whether to mention the spec or not as it's not public yet. I think the description I added should be a good enough description without having to refer to it

def getAccountToPay(address: Address, ws: InMemoryWorldStateProxy): Account =
ws.getAccount(address).getOrElse(Account.empty(blockchainConfig.accountStartNonce))

val blockNumber = block.header.number

val minerRewardForBlock = blockRewardCalculator.calculateMiningRewardForBlock(blockNumber)
val minerRewardForOmmers = blockRewardCalculator.calculateMiningRewardForOmmers(blockNumber, block.body.uncleNodesList.size)

val minerAddress = Address(block.header.beneficiary)
val minerAccount = getAccountToPay(minerAddress, worldStateProxy)
val minerReward = blockRewardCalculator.calcBlockMinerReward(blockNumber, block.body.uncleNodesList.size)
val treasuryAddress = blockchainConfig.treasuryAddress
val existsTreasuryContract = worldStateProxy.getAccount(treasuryAddress).isDefined

val afterMinerReward = worldStateProxy.saveAccount(minerAddress, minerAccount.increaseBalance(UInt256(minerReward)))
log.debug(s"Paying block $blockNumber reward of $minerReward to miner with account address $minerAddress")
val worldAfterPayingBlockReward =
if (block.header.treasuryOptOut.isEmpty || !existsTreasuryContract) {
val minerReward = minerRewardForOmmers + minerRewardForBlock
val worldAfterMinerReward = increaseAccountBalance(minerAddress, UInt256(minerReward))(worldStateProxy)
log.debug(s"Paying block $blockNumber reward of $minerReward to miner with address $minerAddress")

block.body.uncleNodesList.foldLeft(afterMinerReward) { (ws, ommer) =>
val ommerAddress = Address(ommer.beneficiary)
val account = getAccountToPay(ommerAddress, ws)
worldAfterMinerReward
} else if (block.header.treasuryOptOut.get) {
val minerReward = minerRewardForOmmers + minerRewardForBlock * MinerRewardPercentageAfterECIP1098 / 100
val worldAfterMinerReward = increaseAccountBalance(minerAddress, UInt256(minerReward))(worldStateProxy)
log.debug(s"Paying block $blockNumber reward of $minerReward to miner with address $minerAddress, miner opted-out of treasury")

worldAfterMinerReward
} else {
val minerReward = minerRewardForOmmers + minerRewardForBlock * MinerRewardPercentageAfterECIP1098 / 100
val worldAfterMinerReward = increaseAccountBalance(minerAddress, UInt256(minerReward))(worldStateProxy)

val treasuryReward = minerRewardForBlock * TreasuryRewardPercentageAfterECIP1098 / 100
val worldAfterTreasuryReward = increaseAccountBalance(treasuryAddress, UInt256(treasuryReward))(worldAfterMinerReward)

log.debug(s"Paying block $blockNumber reward of $minerReward to miner with address $minerAddress" +
s"paying treasury reward of $treasuryReward to treasury with address $treasuryAddress")

val ommerReward = blockRewardCalculator.calcOmmerMinerReward(blockNumber, ommer.number)
worldAfterTreasuryReward
}

block.body.uncleNodesList.foldLeft(worldAfterPayingBlockReward) { (ws, ommer) =>
val ommerAddress = Address(ommer.beneficiary)
val ommerReward = blockRewardCalculator.calculateOmmerRewardForInclusion(blockNumber, ommer.number)

log.debug(s"Paying block $blockNumber reward of $ommerReward to ommer with account address $ommerAddress")
ws.saveAccount(ommerAddress, account.increaseBalance(UInt256(ommerReward)))
increaseAccountBalance(ommerAddress, UInt256(ommerReward))(ws)
}
}

Expand Down Expand Up @@ -120,13 +154,16 @@ class BlockPreparator(
}
}

private[ledger] def increaseAccountBalance(address: Address, value: UInt256)(world: InMemoryWorldStateProxy): InMemoryWorldStateProxy = {
val account = world.getAccount(address).getOrElse(Account.empty(blockchainConfig.accountStartNonce)).increaseBalance(value)
world.saveAccount(address, account)
}

private[ledger] def pay(address: Address, value: UInt256, withTouch: Boolean)(world: InMemoryWorldStateProxy): InMemoryWorldStateProxy = {
if (world.isZeroValueTransferToNonExistentAccount(address, value)) {
world
} else {
val account = world.getAccount(address).getOrElse(Account.empty(blockchainConfig.accountStartNonce)).increaseBalance(value)
val savedWorld = world.saveAccount(address, account)

val savedWorld = increaseAccountBalance(address, value)(world)
if (withTouch) savedWorld.touchAccounts(address) else savedWorld
}
}
Expand Down Expand Up @@ -320,3 +357,8 @@ class BlockPreparator(
}
}
}

object BlockPreparator {
val TreasuryRewardPercentageAfterECIP1098 = 20
val MinerRewardPercentageAfterECIP1098 = 100 - TreasuryRewardPercentageAfterECIP1098
}
53 changes: 32 additions & 21 deletions src/main/scala/io/iohk/ethereum/ledger/BlockRewardCalculator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,33 +44,44 @@ class BlockRewardCalculator(config: MonetaryPolicyConfig, byzantiumBlockNumber:
val firstEraOmmerMiningRewardDenom: BigInt = 8


def calcBlockMinerReward(blockNumber: BigInt, ommersCount: Int): BigInt = {
val baseReward = calcMinerBaseReward(blockNumber)
val ommersReward = calcMinerRewardPerOmmer(blockNumber) * ommersCount
baseReward + ommersReward
/**
* Calculates the miner reward for the block, that is, without considering the ommers included
*
* @param blockNumber of the mined block
* @return miner reward for the block
*/
def calculateMiningRewardForBlock(blockNumber: BigInt): BigInt = {
val era = eraNumber(blockNumber)
val eraMultiplier = rewardReductionRateNumer.pow(era)
val eraDivisor = rewardReductionRateDenom.pow(era)
newBlockReward(blockNumber) * eraMultiplier / eraDivisor
}

def calcOmmerMinerReward(blockNumber: BigInt, ommerNumber: BigInt): BigInt = {
/**
* Calculates the miner reward for the ommers included on the block
*
* @param blockNumber of the mined block
* @param ommersCount the number of ommers on the block
* @return miner reward for the block ommers
*/
def calculateMiningRewardForOmmers(blockNumber: BigInt, ommersCount: Int): BigInt =
calculateMiningRewardPerOmmer(blockNumber) * ommersCount

/**
* Calculates the ommers reward for the ommers included on the block
*
* @param blockNumber of the mined block
* @param ommerNumber the block number of the ommer
* @return ommer reward
*/
def calculateOmmerRewardForInclusion(blockNumber: BigInt, ommerNumber: BigInt): BigInt = {
val era = eraNumber(blockNumber)

if (era == 0) {
val number = firstEraOmmerMiningRewardMaxNumer - (blockNumber - ommerNumber - 1)
(newBlockReward(blockNumber) * number) / firstEraOmmerMiningRewardDenom
} else
calcMinerBaseReward(blockNumber) * ommerMiningRewardNumer / ommerMiningRewardDenom
}

/**
* Calculates the miner base reward (without considering the ommers included)
*
* @param blockNumber mined block
* @return miner base reward
*/
private def calcMinerBaseReward(blockNumber: BigInt): BigInt = {
val era = eraNumber(blockNumber)
val eraMultiplier = rewardReductionRateNumer.pow(era)
val eraDivisor = rewardReductionRateDenom.pow(era)
newBlockReward(blockNumber) * eraMultiplier / eraDivisor
calculateMiningRewardForBlock(blockNumber) * ommerMiningRewardNumer / ommerMiningRewardDenom
}

/**
Expand All @@ -79,8 +90,8 @@ class BlockRewardCalculator(config: MonetaryPolicyConfig, byzantiumBlockNumber:
* @param blockNumber mined block
* @return reward given to the miner for each ommer included
*/
private def calcMinerRewardPerOmmer(blockNumber: BigInt): BigInt = {
calcMinerBaseReward(blockNumber) * ommerInclusionRewardNumer / ommerInclusionRewardDenom
private def calculateMiningRewardPerOmmer(blockNumber: BigInt): BigInt = {
calculateMiningRewardForBlock(blockNumber) * ommerInclusionRewardNumer / ommerInclusionRewardDenom
}

/** era number counting from 0 */
Expand Down
5 changes: 4 additions & 1 deletion src/main/scala/io/iohk/ethereum/utils/BlockchainConfig.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.iohk.ethereum.utils

import akka.util.ByteString
import io.iohk.ethereum.domain.UInt256
import io.iohk.ethereum.domain.{Address, UInt256}
import io.iohk.ethereum.utils.NumericUtils._

import scala.collection.JavaConverters._
Expand All @@ -25,6 +25,7 @@ case class BlockchainConfig(
aghartaBlockNumber: BigInt,
phoenixBlockNumber: BigInt,
petersburgBlockNumber: BigInt,
treasuryAddress: Address,
ecip1098BlockNumber: BigInt,

maxCodeSize: Option[BigInt],
Expand Down Expand Up @@ -72,6 +73,7 @@ object BlockchainConfig {
val aghartaBlockNumber: BigInt = BigInt(blockchainConfig.getString("agharta-block-number"))
val phoenixBlockNumber: BigInt = BigInt(blockchainConfig.getString("phoenix-block-number"))
val petersburgBlockNumber: BigInt = BigInt(blockchainConfig.getString("petersburg-block-number"))
val treasuryAddress = Address(blockchainConfig.getString("treasury-address"))
val ecip1098BlockNumber: BigInt = BigInt(blockchainConfig.getString("ecip1098-block-number"))

val maxCodeSize: Option[BigInt] = Try(BigInt(blockchainConfig.getString("max-code-size"))).toOption
Expand Down Expand Up @@ -117,6 +119,7 @@ object BlockchainConfig {
aghartaBlockNumber = aghartaBlockNumber,
phoenixBlockNumber = phoenixBlockNumber,
petersburgBlockNumber = petersburgBlockNumber,
treasuryAddress = treasuryAddress,
ecip1098BlockNumber = ecip1098BlockNumber,

maxCodeSize = maxCodeSize,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,8 @@ class BlockGeneratorSpec extends AnyFlatSpec with Matchers with ScalaCheckProper
aghartaBlockNumber = Long.MaxValue,
phoenixBlockNumber = Long.MaxValue,
petersburgBlockNumber = Long.MaxValue,
ecip1098BlockNumber = Long.MaxValue
ecip1098BlockNumber = Long.MaxValue,
treasuryAddress = Address(0)
)

override lazy val blockExecution =
Expand Down Expand Up @@ -490,7 +491,8 @@ class BlockGeneratorSpec extends AnyFlatSpec with Matchers with ScalaCheckProper
aghartaBlockNumber = Long.MaxValue,
phoenixBlockNumber = Long.MaxValue,
petersburgBlockNumber = Long.MaxValue,
ecip1098BlockNumber = Long.MaxValue
ecip1098BlockNumber = Long.MaxValue,
treasuryAddress = Address(0)
)
override lazy val blockchainConfig = baseBlockchainConfig

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,8 @@ class BlockHeaderValidatorSpec
aghartaBlockNumber = Long.MaxValue,
phoenixBlockNumber = Long.MaxValue,
petersburgBlockNumber = Long.MaxValue,
ecip1098BlockNumber = Long.MaxValue
ecip1098BlockNumber = Long.MaxValue,
treasuryAddress = Address(0)
)
}

Expand Down
32 changes: 0 additions & 32 deletions src/test/scala/io/iohk/ethereum/jsonrpc/EthServiceSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -976,38 +976,6 @@ class EthServiceSpec
override lazy val ledger = mock[Ledger]
override lazy val stxLedger = mock[StxLedger]

override lazy val blockchainConfig = BlockchainConfig(
ethCompatibleStorage = true,
//unused
eip155BlockNumber = 0,
chainId = 0x03.toByte,
networkId = 1,
maxCodeSize = None,
eip161BlockNumber = 0,
frontierBlockNumber = 0,
homesteadBlockNumber = 0,
eip150BlockNumber = 0,
eip160BlockNumber = 0,
eip106BlockNumber = 0,
byzantiumBlockNumber = 0,
constantinopleBlockNumber = 0,
istanbulBlockNumber = 0,
difficultyBombPauseBlockNumber = 0,
difficultyBombContinueBlockNumber = 0,
difficultyBombRemovalBlockNumber = 0,
customGenesisFileOpt = None,
accountStartNonce = UInt256.Zero,
monetaryPolicyConfig = MonetaryPolicyConfig(0, 0, 0, 0),
daoForkConfig = None,
bootstrapNodes = Set(),
gasTieBreaker = false,
atlantisBlockNumber = 0,
aghartaBlockNumber = 0,
phoenixBlockNumber = 0,
petersburgBlockNumber = 0,
ecip1098BlockNumber = 0
)

override lazy val consensus: TestConsensus = buildTestConsensus().withBlockGenerator(blockGenerator)

override implicit lazy val system = ActorSystem("EthServiceSpec_System")
Expand Down
Loading