Skip to content

Commit 02fc7f6

Browse files
author
Nicolás Tallar
authored
[ETCM-44] Treasury block reward distribution (#694)
1 parent 80ca840 commit 02fc7f6

22 files changed

+579
-418
lines changed

insomnia_workspace.json

Lines changed: 374 additions & 301 deletions
Large diffs are not rendered by default.

src/ets/scala/io/iohk/ethereum/ets/blockchain/BlockchainTestConfig.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ object BlockchainTestConfig {
4343
atlantisBlockNumber = Long.MaxValue,
4444
aghartaBlockNumber = Long.MaxValue,
4545
phoenixBlockNumber = Long.MaxValue,
46-
ecip1098BlockNumber = Long.MaxValue
46+
ecip1098BlockNumber = Long.MaxValue,
47+
treasuryAddress = Address(0)
4748
)
4849

4950
val FrontierConfig = BaseBlockchainConfig.copy(

src/it/scala/io/iohk/ethereum/txExecTest/ECIP1017Test.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package io.iohk.ethereum.txExecTest
22

33
import java.util.concurrent.Executors
44

5-
import io.iohk.ethereum.domain.{BlockchainImpl, Receipt, UInt256}
5+
import io.iohk.ethereum.domain.{Address, BlockchainImpl, Receipt, UInt256}
66
import io.iohk.ethereum.ledger._
77
import io.iohk.ethereum.txExecTest.util.FixtureProvider
88
import io.iohk.ethereum.utils.{BlockchainConfig, MonetaryPolicyConfig}
@@ -45,7 +45,8 @@ class ECIP1017Test extends AnyFlatSpec with Matchers {
4545
aghartaBlockNumber = Long.MaxValue,
4646
phoenixBlockNumber = Long.MaxValue,
4747
petersburgBlockNumber = Long.MaxValue,
48-
ecip1098BlockNumber = Long.MaxValue
48+
ecip1098BlockNumber = Long.MaxValue,
49+
treasuryAddress = Address(0)
4950
)
5051
val ec = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(4))
5152

src/it/scala/io/iohk/ethereum/txExecTest/ForksTest.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package io.iohk.ethereum.txExecTest
22

33
import java.util.concurrent.Executors
44

5-
import io.iohk.ethereum.domain.{BlockchainImpl, Receipt, UInt256}
5+
import io.iohk.ethereum.domain.{Address, BlockchainImpl, Receipt, UInt256}
66
import io.iohk.ethereum.ledger.{BlockExecution, BlockQueue, BlockValidation}
77
import io.iohk.ethereum.txExecTest.util.FixtureProvider
88
import io.iohk.ethereum.utils.{BlockchainConfig, MonetaryPolicyConfig}
@@ -44,7 +44,8 @@ class ForksTest extends AnyFlatSpec with Matchers {
4444
aghartaBlockNumber = Long.MaxValue,
4545
phoenixBlockNumber = Long.MaxValue,
4646
petersburgBlockNumber = Long.MaxValue,
47-
ecip1098BlockNumber = Long.MaxValue
47+
ecip1098BlockNumber = Long.MaxValue,
48+
treasuryAddress = Address(0)
4849
)
4950

5051
val noErrors = a[Right[_, Seq[Receipt]]]

src/main/resources/chains/etc-chain.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080

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

8586
# DAO fork configuration (Ethereum HF/Classic split)

src/main/resources/chains/eth-chain.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080

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

8586
# DAO fork configuration (Ethereum HF/Classic split)

src/main/resources/chains/mordor-chain.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080

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

8586
# DAO fork configuration (Ethereum HF/Classic split)

src/main/resources/chains/ropsten-chain.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383

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

8889
# DAO fork configuration (Ethereum HF/Classic split)

src/main/resources/chains/test-chain.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080

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

8586
# DAO fork configuration (Ethereum HF/Classic split)

src/main/resources/chains/testnet-internal-chain.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080

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

8586
# DAO fork configuration (Ethereum HF/Classic split)

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

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import io.iohk.ethereum.domain.UInt256._
66
import io.iohk.ethereum.domain._
77
import io.iohk.ethereum.ledger.BlockExecutionError.{StateBeforeFailure, TxsExecutionError}
88
import io.iohk.ethereum.ledger.Ledger._
9+
import io.iohk.ethereum.ledger.BlockPreparator._
910
import io.iohk.ethereum.utils.{BlockchainConfig, Logger}
1011
import io.iohk.ethereum.vm.{PC => _, _}
1112

@@ -38,33 +39,66 @@ class BlockPreparator(
3839
)
3940

4041
/**
41-
* This function updates state in order to pay rewards based on YP section 11.3
42+
* This function updates the state in order to pay rewards based on YP section 11.3 and with the required
43+
* modifications due to ECIP1097:
44+
* 1. Reward for block is distributed as:
45+
* a. If treasury is disabled or it's has been selfdestructed:
46+
* Pay 100% of it to the miner
47+
* b. If a. isn't true and the miner opted out:
48+
* Pay 80% of it to the miner
49+
* Never generate the 20% else
50+
* c. If a. isn't true and the miner opted in:
51+
* Pay 80% of it to the miner
52+
* Pay 20% of it to the treasury contract
53+
* 2. Miner is payed a reward for the inclusion of ommers
54+
* 3. Ommers's miners are payed a reward for their inclusion in this block
4255
*
43-
* @param block
44-
* @param worldStateProxy
45-
* @return
56+
* @param block the block being processed
57+
* @param worldStateProxy the initial state
58+
* @return the state after paying the apropiate reward to who corresponds
4659
*/
4760
private[ledger] def payBlockReward(block: Block, worldStateProxy: InMemoryWorldStateProxy): InMemoryWorldStateProxy = {
48-
def getAccountToPay(address: Address, ws: InMemoryWorldStateProxy): Account =
49-
ws.getAccount(address).getOrElse(Account.empty(blockchainConfig.accountStartNonce))
50-
5161
val blockNumber = block.header.number
5262

63+
val minerRewardForBlock = blockRewardCalculator.calculateMiningRewardForBlock(blockNumber)
64+
val minerRewardForOmmers = blockRewardCalculator.calculateMiningRewardForOmmers(blockNumber, block.body.uncleNodesList.size)
65+
5366
val minerAddress = Address(block.header.beneficiary)
54-
val minerAccount = getAccountToPay(minerAddress, worldStateProxy)
55-
val minerReward = blockRewardCalculator.calcBlockMinerReward(blockNumber, block.body.uncleNodesList.size)
67+
val treasuryAddress = blockchainConfig.treasuryAddress
68+
val existsTreasuryContract = worldStateProxy.getAccount(treasuryAddress).isDefined
5669

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

60-
block.body.uncleNodesList.foldLeft(afterMinerReward) { (ws, ommer) =>
61-
val ommerAddress = Address(ommer.beneficiary)
62-
val account = getAccountToPay(ommerAddress, ws)
76+
worldAfterMinerReward
77+
} else if (block.header.treasuryOptOut.get) {
78+
val minerReward = minerRewardForOmmers + minerRewardForBlock * MinerRewardPercentageAfterECIP1098 / 100
79+
val worldAfterMinerReward = increaseAccountBalance(minerAddress, UInt256(minerReward))(worldStateProxy)
80+
log.debug(s"Paying block $blockNumber reward of $minerReward to miner with address $minerAddress, miner opted-out of treasury")
81+
82+
worldAfterMinerReward
83+
} else {
84+
val minerReward = minerRewardForOmmers + minerRewardForBlock * MinerRewardPercentageAfterECIP1098 / 100
85+
val worldAfterMinerReward = increaseAccountBalance(minerAddress, UInt256(minerReward))(worldStateProxy)
86+
87+
val treasuryReward = minerRewardForBlock * TreasuryRewardPercentageAfterECIP1098 / 100
88+
val worldAfterTreasuryReward = increaseAccountBalance(treasuryAddress, UInt256(treasuryReward))(worldAfterMinerReward)
89+
90+
log.debug(s"Paying block $blockNumber reward of $minerReward to miner with address $minerAddress" +
91+
s"paying treasury reward of $treasuryReward to treasury with address $treasuryAddress")
6392

64-
val ommerReward = blockRewardCalculator.calcOmmerMinerReward(blockNumber, ommer.number)
93+
worldAfterTreasuryReward
94+
}
95+
96+
block.body.uncleNodesList.foldLeft(worldAfterPayingBlockReward) { (ws, ommer) =>
97+
val ommerAddress = Address(ommer.beneficiary)
98+
val ommerReward = blockRewardCalculator.calculateOmmerRewardForInclusion(blockNumber, ommer.number)
6599

66100
log.debug(s"Paying block $blockNumber reward of $ommerReward to ommer with account address $ommerAddress")
67-
ws.saveAccount(ommerAddress, account.increaseBalance(UInt256(ommerReward)))
101+
increaseAccountBalance(ommerAddress, UInt256(ommerReward))(ws)
68102
}
69103
}
70104

@@ -120,13 +154,16 @@ class BlockPreparator(
120154
}
121155
}
122156

157+
private[ledger] def increaseAccountBalance(address: Address, value: UInt256)(world: InMemoryWorldStateProxy): InMemoryWorldStateProxy = {
158+
val account = world.getAccount(address).getOrElse(Account.empty(blockchainConfig.accountStartNonce)).increaseBalance(value)
159+
world.saveAccount(address, account)
160+
}
161+
123162
private[ledger] def pay(address: Address, value: UInt256, withTouch: Boolean)(world: InMemoryWorldStateProxy): InMemoryWorldStateProxy = {
124163
if (world.isZeroValueTransferToNonExistentAccount(address, value)) {
125164
world
126165
} else {
127-
val account = world.getAccount(address).getOrElse(Account.empty(blockchainConfig.accountStartNonce)).increaseBalance(value)
128-
val savedWorld = world.saveAccount(address, account)
129-
166+
val savedWorld = increaseAccountBalance(address, value)(world)
130167
if (withTouch) savedWorld.touchAccounts(address) else savedWorld
131168
}
132169
}
@@ -320,3 +357,8 @@ class BlockPreparator(
320357
}
321358
}
322359
}
360+
361+
object BlockPreparator {
362+
val TreasuryRewardPercentageAfterECIP1098 = 20
363+
val MinerRewardPercentageAfterECIP1098 = 100 - TreasuryRewardPercentageAfterECIP1098
364+
}

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

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -44,33 +44,44 @@ class BlockRewardCalculator(config: MonetaryPolicyConfig, byzantiumBlockNumber:
4444
val firstEraOmmerMiningRewardDenom: BigInt = 8
4545

4646

47-
def calcBlockMinerReward(blockNumber: BigInt, ommersCount: Int): BigInt = {
48-
val baseReward = calcMinerBaseReward(blockNumber)
49-
val ommersReward = calcMinerRewardPerOmmer(blockNumber) * ommersCount
50-
baseReward + ommersReward
47+
/**
48+
* Calculates the miner reward for the block, that is, without considering the ommers included
49+
*
50+
* @param blockNumber of the mined block
51+
* @return miner reward for the block
52+
*/
53+
def calculateMiningRewardForBlock(blockNumber: BigInt): BigInt = {
54+
val era = eraNumber(blockNumber)
55+
val eraMultiplier = rewardReductionRateNumer.pow(era)
56+
val eraDivisor = rewardReductionRateDenom.pow(era)
57+
newBlockReward(blockNumber) * eraMultiplier / eraDivisor
5158
}
5259

53-
def calcOmmerMinerReward(blockNumber: BigInt, ommerNumber: BigInt): BigInt = {
60+
/**
61+
* Calculates the miner reward for the ommers included on the block
62+
*
63+
* @param blockNumber of the mined block
64+
* @param ommersCount the number of ommers on the block
65+
* @return miner reward for the block ommers
66+
*/
67+
def calculateMiningRewardForOmmers(blockNumber: BigInt, ommersCount: Int): BigInt =
68+
calculateMiningRewardPerOmmer(blockNumber) * ommersCount
69+
70+
/**
71+
* Calculates the ommers reward for the ommers included on the block
72+
*
73+
* @param blockNumber of the mined block
74+
* @param ommerNumber the block number of the ommer
75+
* @return ommer reward
76+
*/
77+
def calculateOmmerRewardForInclusion(blockNumber: BigInt, ommerNumber: BigInt): BigInt = {
5478
val era = eraNumber(blockNumber)
5579

5680
if (era == 0) {
5781
val number = firstEraOmmerMiningRewardMaxNumer - (blockNumber - ommerNumber - 1)
5882
(newBlockReward(blockNumber) * number) / firstEraOmmerMiningRewardDenom
5983
} else
60-
calcMinerBaseReward(blockNumber) * ommerMiningRewardNumer / ommerMiningRewardDenom
61-
}
62-
63-
/**
64-
* Calculates the miner base reward (without considering the ommers included)
65-
*
66-
* @param blockNumber mined block
67-
* @return miner base reward
68-
*/
69-
private def calcMinerBaseReward(blockNumber: BigInt): BigInt = {
70-
val era = eraNumber(blockNumber)
71-
val eraMultiplier = rewardReductionRateNumer.pow(era)
72-
val eraDivisor = rewardReductionRateDenom.pow(era)
73-
newBlockReward(blockNumber) * eraMultiplier / eraDivisor
84+
calculateMiningRewardForBlock(blockNumber) * ommerMiningRewardNumer / ommerMiningRewardDenom
7485
}
7586

7687
/**
@@ -79,8 +90,8 @@ class BlockRewardCalculator(config: MonetaryPolicyConfig, byzantiumBlockNumber:
7990
* @param blockNumber mined block
8091
* @return reward given to the miner for each ommer included
8192
*/
82-
private def calcMinerRewardPerOmmer(blockNumber: BigInt): BigInt = {
83-
calcMinerBaseReward(blockNumber) * ommerInclusionRewardNumer / ommerInclusionRewardDenom
93+
private def calculateMiningRewardPerOmmer(blockNumber: BigInt): BigInt = {
94+
calculateMiningRewardForBlock(blockNumber) * ommerInclusionRewardNumer / ommerInclusionRewardDenom
8495
}
8596

8697
/** era number counting from 0 */

src/main/scala/io/iohk/ethereum/utils/BlockchainConfig.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package io.iohk.ethereum.utils
22

33
import akka.util.ByteString
4-
import io.iohk.ethereum.domain.UInt256
4+
import io.iohk.ethereum.domain.{Address, UInt256}
55
import io.iohk.ethereum.utils.NumericUtils._
66

77
import scala.collection.JavaConverters._
@@ -25,6 +25,7 @@ case class BlockchainConfig(
2525
aghartaBlockNumber: BigInt,
2626
phoenixBlockNumber: BigInt,
2727
petersburgBlockNumber: BigInt,
28+
treasuryAddress: Address,
2829
ecip1098BlockNumber: BigInt,
2930

3031
maxCodeSize: Option[BigInt],
@@ -72,6 +73,7 @@ object BlockchainConfig {
7273
val aghartaBlockNumber: BigInt = BigInt(blockchainConfig.getString("agharta-block-number"))
7374
val phoenixBlockNumber: BigInt = BigInt(blockchainConfig.getString("phoenix-block-number"))
7475
val petersburgBlockNumber: BigInt = BigInt(blockchainConfig.getString("petersburg-block-number"))
76+
val treasuryAddress = Address(blockchainConfig.getString("treasury-address"))
7577
val ecip1098BlockNumber: BigInt = BigInt(blockchainConfig.getString("ecip1098-block-number"))
7678

7779
val maxCodeSize: Option[BigInt] = Try(BigInt(blockchainConfig.getString("max-code-size"))).toOption
@@ -117,6 +119,7 @@ object BlockchainConfig {
117119
aghartaBlockNumber = aghartaBlockNumber,
118120
phoenixBlockNumber = phoenixBlockNumber,
119121
petersburgBlockNumber = petersburgBlockNumber,
122+
treasuryAddress = treasuryAddress,
120123
ecip1098BlockNumber = ecip1098BlockNumber,
121124

122125
maxCodeSize = maxCodeSize,

src/test/scala/io/iohk/ethereum/consensus/BlockGeneratorSpec.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,8 @@ class BlockGeneratorSpec extends AnyFlatSpec with Matchers with ScalaCheckProper
221221
aghartaBlockNumber = Long.MaxValue,
222222
phoenixBlockNumber = Long.MaxValue,
223223
petersburgBlockNumber = Long.MaxValue,
224-
ecip1098BlockNumber = Long.MaxValue
224+
ecip1098BlockNumber = Long.MaxValue,
225+
treasuryAddress = Address(0)
225226
)
226227

227228
override lazy val blockExecution =
@@ -490,7 +491,8 @@ class BlockGeneratorSpec extends AnyFlatSpec with Matchers with ScalaCheckProper
490491
aghartaBlockNumber = Long.MaxValue,
491492
phoenixBlockNumber = Long.MaxValue,
492493
petersburgBlockNumber = Long.MaxValue,
493-
ecip1098BlockNumber = Long.MaxValue
494+
ecip1098BlockNumber = Long.MaxValue,
495+
treasuryAddress = Address(0)
494496
)
495497
override lazy val blockchainConfig = baseBlockchainConfig
496498

src/test/scala/io/iohk/ethereum/consensus/validators/BlockHeaderValidatorSpec.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,8 @@ class BlockHeaderValidatorSpec
431431
aghartaBlockNumber = Long.MaxValue,
432432
phoenixBlockNumber = Long.MaxValue,
433433
petersburgBlockNumber = Long.MaxValue,
434-
ecip1098BlockNumber = Long.MaxValue
434+
ecip1098BlockNumber = Long.MaxValue,
435+
treasuryAddress = Address(0)
435436
)
436437
}
437438

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

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -976,38 +976,6 @@ class EthServiceSpec
976976
override lazy val ledger = mock[Ledger]
977977
override lazy val stxLedger = mock[StxLedger]
978978

979-
override lazy val blockchainConfig = BlockchainConfig(
980-
ethCompatibleStorage = true,
981-
//unused
982-
eip155BlockNumber = 0,
983-
chainId = 0x03.toByte,
984-
networkId = 1,
985-
maxCodeSize = None,
986-
eip161BlockNumber = 0,
987-
frontierBlockNumber = 0,
988-
homesteadBlockNumber = 0,
989-
eip150BlockNumber = 0,
990-
eip160BlockNumber = 0,
991-
eip106BlockNumber = 0,
992-
byzantiumBlockNumber = 0,
993-
constantinopleBlockNumber = 0,
994-
istanbulBlockNumber = 0,
995-
difficultyBombPauseBlockNumber = 0,
996-
difficultyBombContinueBlockNumber = 0,
997-
difficultyBombRemovalBlockNumber = 0,
998-
customGenesisFileOpt = None,
999-
accountStartNonce = UInt256.Zero,
1000-
monetaryPolicyConfig = MonetaryPolicyConfig(0, 0, 0, 0),
1001-
daoForkConfig = None,
1002-
bootstrapNodes = Set(),
1003-
gasTieBreaker = false,
1004-
atlantisBlockNumber = 0,
1005-
aghartaBlockNumber = 0,
1006-
phoenixBlockNumber = 0,
1007-
petersburgBlockNumber = 0,
1008-
ecip1098BlockNumber = 0
1009-
)
1010-
1011979
override lazy val consensus: TestConsensus = buildTestConsensus().withBlockGenerator(blockGenerator)
1012980

1013981
override implicit lazy val system = ActorSystem("EthServiceSpec_System")

0 commit comments

Comments
 (0)