Skip to content

Commit fb29d58

Browse files
author
Nicolás Tallar
authored
[ETCM-43][ETCM-46] Add opt-out field (#675)
1 parent 12e6b6e commit fb29d58

38 files changed

+347
-121
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ object BlockchainTestConfig {
4242
ethCompatibleStorage = true,
4343
atlantisBlockNumber = Long.MaxValue,
4444
aghartaBlockNumber = Long.MaxValue,
45-
phoenixBlockNumber = Long.MaxValue
45+
phoenixBlockNumber = Long.MaxValue,
46+
ecip1098BlockNumber = Long.MaxValue
4647
)
4748

4849
val FrontierConfig = BaseBlockchainConfig.copy(

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ case class BlockHeaderDef(
5656

5757
def toBlockHeader: BlockHeader =
5858
BlockHeader(parentHash, uncleHash, coinbase, stateRoot, transactionsTrie, receiptTrie, bloom, difficulty, number,
59-
gasLimit, gasUsed, timestamp, extraData, mixHash, nonce
59+
gasLimit, gasUsed, timestamp, extraData, mixHash, nonce, None
6060
)
6161
}
6262

src/ets/scala/io/iohk/ethereum/ets/vm/ScenarioBuilder.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ object ScenarioBuilder {
5151
env.currentTimestamp,
5252
bEmpty,
5353
bEmpty,
54-
bEmpty
54+
bEmpty,
55+
None
5556
)
5657

5758
def prepareWorld(accounts: Map[Address, AccountState], blockNumber: BigInt, exec: Exec): MockWorldState = {

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ class ECIP1017Test extends FlatSpec with Matchers {
4646
atlantisBlockNumber = Long.MaxValue,
4747
aghartaBlockNumber = Long.MaxValue,
4848
phoenixBlockNumber = Long.MaxValue,
49-
petersburgBlockNumber = Long.MaxValue
49+
petersburgBlockNumber = Long.MaxValue,
50+
ecip1098BlockNumber = Long.MaxValue
5051
)
5152
val ec = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(4))
5253

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ class ForksTest extends FlatSpec with Matchers {
4343
atlantisBlockNumber = Long.MaxValue,
4444
aghartaBlockNumber = Long.MaxValue,
4545
phoenixBlockNumber = Long.MaxValue,
46-
petersburgBlockNumber = Long.MaxValue
46+
petersburgBlockNumber = Long.MaxValue,
47+
ecip1098BlockNumber = Long.MaxValue
4748
)
4849

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

src/it/scala/io/iohk/ethereum/txExecTest/util/DumpChainApp.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ object DumpChainApp extends App with NodeKeyBuilder with SecureRandomBuilder wit
103103
class BlockchainMock(genesisHash: ByteString) extends Blockchain {
104104

105105
class FakeHeader() extends BlockHeader(ByteString.empty, ByteString.empty, ByteString.empty, ByteString.empty,
106-
ByteString.empty, ByteString.empty, ByteString.empty, 0, 0, 0, 0, 0, ByteString.empty, ByteString.empty, ByteString.empty) {
106+
ByteString.empty, ByteString.empty, ByteString.empty, 0, 0, 0, 0, 0, ByteString.empty, ByteString.empty, ByteString.empty, None) {
107107
override lazy val hash: ByteString = genesisHash
108108
}
109109

src/main/resources/application.conf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,11 @@ mantis {
237237
# In the case of ethash PoW, this means mining new blocks, as specified by Ethereum.
238238
# In the general case, the semantics are due to the specific consensus implementation.
239239
mining-enabled = false
240+
241+
# Whether or not as a miner we want to support the proto-treasury and send 20% of the block reward to it
242+
# If false then that 20% gets burned
243+
# Doesn't have any effect is ecip1098 is not yet activated
244+
treasury-opt-out = false
240245
}
241246

242247
# This is the section dedicated to Ethash mining.

src/main/resources/chains/base.conf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@
7878
# https://eips.ethereum.org/EIPS/eip-1679
7979
istanbul-block-number = "1000000000000000000"
8080

81+
# Proto-treasury fork block number (ETC only, but deactivated for now)
82+
# https://ecips.ethereumclassic.org/ECIPs/ecip-1098
83+
ecip1098-block-number = "1000000000000000000"
84+
8185
# DAO fork configuration (Ethereum HF/Classic split)
8286
# https://blog.ethereum.org/2016/07/20/hard-fork-completed/
8387
dao {

src/main/resources/chains/eth.conf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@
4545
# https://eips.ethereum.org/EIPS/eip-1679
4646
istanbul-block-number = "9069000"
4747

48+
# Proto-treasury fork block number (ETC only, but deactivated for now)
49+
# https://ecips.ethereumclassic.org/ECIPs/ecip-1098
50+
ecip1098-block-number = "1000000000000000000"
51+
4852
monetary-policy {
4953

5054
# Setting era-duration for eth and ropsten chain to big number is necessery to ensure

src/main/resources/chains/mordor.conf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@
7878
# https://eips.ethereum.org/EIPS/eip-1679
7979
istanbul-block-number = "1000000000000000000"
8080

81+
# Proto-treasury fork block number (ETC only, but deactivated for now)
82+
# https://ecips.ethereumclassic.org/ECIPs/ecip-1098
83+
ecip1098-block-number = "1000000000000000000"
84+
8185
# DAO fork configuration (Ethereum HF/Classic split)
8286
# https://blog.ethereum.org/2016/07/20/hard-fork-completed/
8387
dao = null

src/main/resources/chains/private.conf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
atlantis-block-number = "0"
1212
agharta-block-number = "0"
1313
phoenix-block-number = "0"
14+
# FIXME: Once the testnet is up an running we should determine this value
15+
ecip1098-block-number = "1000000000000000000"
1416

1517
chain-id = "0x2A"
1618
network-id = 42

src/main/resources/chains/ropsten.conf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
# https://github.com/ethereum/pm/issues/53
2020
constantinople-block-number = "4230000"
2121

22+
# Proto-treasury fork block number (ETC only, but deactivated for now)
23+
# https://ecips.ethereumclassic.org/ECIPs/ecip-1098
24+
ecip1098-block-number = "1000000000000000000"
25+
2226
max-code-size = 24576
2327

2428
monetary-policy {

src/main/scala/io/iohk/ethereum/blockchain/data/GenesisDataLoader.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,8 @@ class GenesisDataLoader(
134134
unixTimestamp = BigInt(genesisData.timestamp.replace("0x", ""), 16).toLong,
135135
extraData = genesisData.extraData,
136136
mixHash = genesisData.mixHash.getOrElse(zeros(hashLength)),
137-
nonce = genesisData.nonce
137+
nonce = genesisData.nonce,
138+
treasuryOptOut = None
138139
)
139140

140141
private def zeros(length: Int) =

src/main/scala/io/iohk/ethereum/consensus/ConsensusConfig.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ final case class ConsensusConfig(
2121
coinbase: Address,
2222
headerExtraData: ByteString, // only used in BlockGenerator
2323
blockCacheSize: Int, // only used in BlockGenerator
24-
miningEnabled: Boolean
24+
miningEnabled: Boolean,
25+
treasuryOptOut: Boolean
2526
)
2627

2728
object ConsensusConfig extends Logger {
@@ -32,6 +33,7 @@ object ConsensusConfig extends Logger {
3233
final val HeaderExtraData = "header-extra-data"
3334
final val BlockCacheSize = "block-cashe-size"
3435
final val MiningEnabled = "mining-enabled"
36+
final val TreasuryOptOut = "treasury-opt-out"
3537
}
3638

3739

@@ -69,13 +71,15 @@ object ConsensusConfig extends Logger {
6971
.take(BlockHeaderValidator.MaxExtraDataSize)
7072
val blockCacheSize = config.getInt(Keys.BlockCacheSize)
7173
val miningEnabled = config.getBoolean(Keys.MiningEnabled)
74+
val optOut = config.getBoolean(Keys.TreasuryOptOut)
7275

7376
new ConsensusConfig(
7477
protocol = protocol,
7578
coinbase = coinbase,
7679
headerExtraData = headerExtraData,
7780
blockCacheSize = blockCacheSize,
78-
miningEnabled = miningEnabled
81+
miningEnabled = miningEnabled,
82+
treasuryOptOut = optOut
7983
)
8084
}
8185
}

src/main/scala/io/iohk/ethereum/consensus/blocks/BlockGeneratorSkeleton.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ abstract class BlockGeneratorSkeleton(
5050
beneficiary: Address,
5151
blockTimestamp: Long,
5252
x: Ommers
53-
): BlockHeader =
53+
): BlockHeader = {
54+
val optOut = if(blockNumber >= blockchainConfig.ecip1098BlockNumber) Some(consensusConfig.treasuryOptOut) else None
55+
5456
BlockHeader(
5557
parentHash = parent.header.hash,
5658
ommersHash = ByteString(kec256(x.toBytes: Array[Byte])),
@@ -67,8 +69,10 @@ abstract class BlockGeneratorSkeleton(
6769
unixTimestamp = blockTimestamp,
6870
extraData = blockchainConfig.daoForkConfig.flatMap(daoForkConfig => daoForkConfig.getExtraData(blockNumber)).getOrElse(headerExtraData),
6971
mixHash = ByteString.empty,
70-
nonce = ByteString.empty
72+
nonce = ByteString.empty,
73+
treasuryOptOut = optOut
7174
)
75+
}
7276

7377
protected def prepareHeader(
7478
blockNumber: BigInt, parent: Block,

src/main/scala/io/iohk/ethereum/consensus/validators/BlockHeaderValidator.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ object BlockHeaderError {
3232
case object HeaderGasLimitError extends BlockHeaderError
3333
case object HeaderNumberError extends BlockHeaderError
3434
case object HeaderPoWError extends BlockHeaderError
35+
case class HeaderOptOutError(ecip1098Activated: Boolean, optOutDefined: Boolean) extends BlockHeaderError
3536
}
3637

3738
sealed trait BlockHeaderValid

src/main/scala/io/iohk/ethereum/consensus/validators/BlockHeaderValidatorSkeleton.scala

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ abstract class BlockHeaderValidatorSkeleton(blockchainConfig: BlockchainConfig)
4848
_ <- validateGasUsed(blockHeader)
4949
_ <- validateGasLimit(blockHeader, parentHeader)
5050
_ <- validateNumber(blockHeader, parentHeader)
51+
_ <- validateOptOut(blockHeader)
5152
_ <- validateEvenMore(blockHeader, parentHeader)
5253
} yield BlockHeaderValid
5354
}
@@ -163,7 +164,19 @@ abstract class BlockHeaderValidatorSkeleton(blockchainConfig: BlockchainConfig)
163164
if(blockHeader.number == parentHeader.number + 1) Right(BlockHeaderValid)
164165
else Left(HeaderNumberError)
165166

166-
}
167-
168-
167+
/**
168+
* Validates [[io.iohk.ethereum.domain.BlockHeader.treasuryOptOut]] is only defined if ECIP1098 is enabled at the block's number
169+
*
170+
* @param blockHeader BlockHeader to validate.
171+
* @return BlockHeader if valid, an [[HeaderOptOutError]] otherwise
172+
*/
173+
private def validateOptOut(blockHeader: BlockHeader): Either[BlockHeaderError, BlockHeaderValid] = {
174+
val isEcip1098Activated = blockHeader.number >= blockchainConfig.ecip1098BlockNumber
175+
val isOptOutDefined = blockHeader.treasuryOptOut.isDefined
176+
177+
if (isEcip1098Activated && isOptOutDefined) Right(BlockHeaderValid)
178+
else if (!isEcip1098Activated && !isOptOutDefined) Right(BlockHeaderValid)
179+
else Left(HeaderOptOutError(isEcip1098Activated, isOptOutDefined))
180+
}
169181

182+
}

src/main/scala/io/iohk/ethereum/db/storage/BlockHeadersStorage.scala

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,16 @@ object BlockHeadersStorage {
5252
Long,
5353
ByteString,
5454
ByteString,
55-
ByteString
55+
ByteString,
56+
Option[Boolean]
5657
)
5758

5859
import boopickle.DefaultBasic._
5960

6061
implicit val byteStringPickler: Pickler[ByteString] = transformPickler[ByteString, Array[Byte]](ByteString(_))(_.toArray[Byte])
6162
implicit val blockHeaderPickler: Pickler[BlockHeader] = transformPickler[BlockHeader, BlockHeaderBody]
62-
{ case (ph, oh, b, sr, txr, rr, lb, d, no, gl, gu, ut, ed, mh, n) =>
63-
new BlockHeader(ph, oh, b, sr, txr, rr, lb, d, no, gl, gu, ut, ed, mh, n)
63+
{ case (ph, oh, b, sr, txr, rr, lb, d, no, gl, gu, ut, ed, mh, n, oo) =>
64+
new BlockHeader(ph, oh, b, sr, txr, rr, lb, d, no, gl, gu, ut, ed, mh, n, oo)
6465
}{ blockHeader => (
6566
blockHeader.parentHash,
6667
blockHeader.ommersHash,
@@ -76,6 +77,7 @@ object BlockHeadersStorage {
7677
blockHeader.unixTimestamp,
7778
blockHeader.extraData,
7879
blockHeader.mixHash,
79-
blockHeader.nonce
80+
blockHeader.nonce,
81+
blockHeader.treasuryOptOut
8082
)}
8183
}

src/main/scala/io/iohk/ethereum/domain/BlockHeader.scala

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ case class BlockHeader(
2222
unixTimestamp: Long,
2323
extraData: ByteString,
2424
mixHash: ByteString,
25-
nonce: ByteString) {
25+
nonce: ByteString,
26+
treasuryOptOut: Option[Boolean]) {
2627

2728
override def toString: String = {
2829
s"""BlockHeader {
@@ -40,7 +41,8 @@ case class BlockHeader(
4041
|unixTimestamp: $unixTimestamp,
4142
|extraData: ${Hex.toHexString(extraData.toArray[Byte])}
4243
|mixHash: ${Hex.toHexString(mixHash.toArray[Byte])}
43-
|nonce: ${Hex.toHexString(nonce.toArray[Byte])}
44+
|nonce: ${Hex.toHexString(nonce.toArray[Byte])},
45+
|treasuryOptOut: $treasuryOptOut
4446
|}""".stripMargin
4547
}
4648

@@ -62,9 +64,15 @@ object BlockHeader {
6264

6365
def getEncodedWithoutNonce(blockHeader: BlockHeader): Array[Byte] = {
6466
val rlpEncoded = blockHeader.toRLPEncodable match {
65-
case rlpList: RLPList =>
67+
case rlpList: RLPList if blockHeader.treasuryOptOut.isEmpty =>
68+
// Pre ECIP1098 block
6669
RLPList(rlpList.items.dropRight(2): _*)
6770

71+
case rlpList: RLPList if blockHeader.treasuryOptOut.isDefined =>
72+
// Post ECIP1098 block
73+
val rlpItemsWithoutNonce = rlpList.items.dropRight(3) :+ rlpList.items.last
74+
RLPList(rlpItemsWithoutNonce: _*)
75+
6876
case _ => throw new Exception("BlockHeader cannot be encoded without nonce and mixHash")
6977
}
7078
rlpEncode(rlpEncoded)
@@ -73,8 +81,19 @@ object BlockHeader {
7381
implicit class BlockHeaderEnc(blockHeader: BlockHeader) extends RLPSerializable {
7482
override def toRLPEncodable: RLPEncodeable = {
7583
import blockHeader._
76-
RLPList(parentHash, ommersHash, beneficiary, stateRoot, transactionsRoot, receiptsRoot,
77-
logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce)
84+
treasuryOptOut match {
85+
case Some(definedOptOut) =>
86+
// Post ECIP1098 block, whole block is encoded
87+
val encodedOptOut = if(definedOptOut) 1 else 0
88+
89+
RLPList(parentHash, ommersHash, beneficiary, stateRoot, transactionsRoot, receiptsRoot,
90+
logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce, RLPList(encodedOptOut))
91+
92+
case None =>
93+
// Pre ECIP1098 block, encoding works as if optOut field wasn't defined for backwards compatibility
94+
RLPList(parentHash, ommersHash, beneficiary, stateRoot, transactionsRoot, receiptsRoot,
95+
logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce)
96+
}
7897
}
7998
}
8099

@@ -87,8 +106,23 @@ object BlockHeader {
87106
rlpEncodeable match {
88107
case RLPList(parentHash, ommersHash, beneficiary, stateRoot, transactionsRoot, receiptsRoot,
89108
logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce) =>
109+
// Pre ECIP1098 block, encoding works as if optOut field wasn't defined for backwards compatibility
90110
BlockHeader(parentHash, ommersHash, beneficiary, stateRoot, transactionsRoot, receiptsRoot,
91-
logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce)
111+
logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce, None)
112+
113+
case RLPList(parentHash, ommersHash, beneficiary, stateRoot, transactionsRoot, receiptsRoot,
114+
logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce, RLPList(encodedOptOut)) =>
115+
// Post ECIP1098 block, whole block is encoded
116+
val booleanOptOut =
117+
if ((encodedOptOut: Int) == 1) true
118+
else if ((encodedOptOut: Int) == 0) false
119+
else throw new Exception("BlockHeader cannot be decoded with an invalid opt-out")
120+
121+
BlockHeader(parentHash, ommersHash, beneficiary, stateRoot, transactionsRoot, receiptsRoot,
122+
logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce, Some(booleanOptOut))
123+
124+
case _ =>
125+
throw new Exception("BlockHeader cannot be decoded")
92126
}
93127
}
94128
}

src/main/scala/io/iohk/ethereum/extvm/VMServer.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ class VMServer(messageHandler: MessageHandler)
106106
contextMsg.blockHeader.get.unixTimestamp,
107107
irrelevant,
108108
irrelevant,
109-
irrelevant
109+
irrelevant,
110+
None
110111
)
111112

112113
val blockchainConfig = contextMsg.config.ethereumConfig.map(constructBlockchainConfig).getOrElse(defaultBlockchainConfig)

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ case class BlockchainConfig(
2525
aghartaBlockNumber: BigInt,
2626
phoenixBlockNumber: BigInt,
2727
petersburgBlockNumber: BigInt,
28+
ecip1098BlockNumber: BigInt,
2829

2930
maxCodeSize: Option[BigInt],
3031
difficultyBombPauseBlockNumber: BigInt,
@@ -68,6 +69,7 @@ object BlockchainConfig {
6869
val aghartaBlockNumber: BigInt = BigInt(blockchainConfig.getString("agharta-block-number"))
6970
val phoenixBlockNumber: BigInt = BigInt(blockchainConfig.getString("phoenix-block-number"))
7071
val petersburgBlockNumber: BigInt = BigInt(blockchainConfig.getString("petersburg-block-number"))
72+
val ecip1098BlockNumber: BigInt = BigInt(blockchainConfig.getString("ecip1098-block-number"))
7173

7274
val maxCodeSize: Option[BigInt] = Try(BigInt(blockchainConfig.getString("max-code-size"))).toOption
7375
val difficultyBombPauseBlockNumber: BigInt = BigInt(blockchainConfig.getString("difficulty-bomb-pause-block-number"))
@@ -111,6 +113,7 @@ object BlockchainConfig {
111113
aghartaBlockNumber = aghartaBlockNumber,
112114
phoenixBlockNumber = phoenixBlockNumber,
113115
petersburgBlockNumber = petersburgBlockNumber,
116+
ecip1098BlockNumber = ecip1098BlockNumber,
114117

115118
maxCodeSize = maxCodeSize,
116119
difficultyBombPauseBlockNumber = difficultyBombPauseBlockNumber,

src/test/resources/application.conf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ mantis {
3535
# todo change
3636
constantinople-block-number = "1000000000000000000"
3737

38+
ecip1098-block-number = "1000000000000000000"
39+
3840
difficulty-bomb-pause-block-number = "3000000"
3941

4042
difficulty-bomb-continue-block-number = "5000000"

0 commit comments

Comments
 (0)