Skip to content

Commit 0bea179

Browse files
author
Nicolas Tallar
committed
[ETCM-43] Add opt-out field
1 parent 7743ce1 commit 0bea179

39 files changed

+301
-110
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
@@ -41,7 +41,8 @@ object BlockchainTestConfig {
4141
ethCompatibleStorage = true,
4242
atlantisBlockNumber = Long.MaxValue,
4343
aghartaBlockNumber = Long.MaxValue,
44-
phoenixBlockNumber = Long.MaxValue
44+
phoenixBlockNumber = Long.MaxValue,
45+
ecip1098BlockNumber = Long.MaxValue
4546
)
4647

4748
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
@@ -102,7 +102,7 @@ object DumpChainApp extends App with NodeKeyBuilder with SecureRandomBuilder wit
102102
class BlockchainMock(genesisHash: ByteString) extends Blockchain {
103103

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

src/main/resources/application.conf

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

240245
# 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+
optOut = 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+
optOut: 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 OptOut = "opt-out"
3537
}
3638

3739

@@ -68,13 +70,15 @@ object ConsensusConfig extends Logger {
6870
.take(BlockHeaderValidator.MaxExtraDataSize)
6971
val blockCacheSize = config.getInt(Keys.BlockCacheSize)
7072
val miningEnabled = config.getBoolean(Keys.MiningEnabled)
73+
val optOut = config.getBoolean(Keys.OptOut)
7174

7275
new ConsensusConfig(
7376
protocol = protocol,
7477
coinbase = coinbase,
7578
headerExtraData = headerExtraData,
7679
blockCacheSize = blockCacheSize,
77-
miningEnabled = miningEnabled
80+
miningEnabled = miningEnabled,
81+
optOut = optOut
7882
)
7983
}
8084
}

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.optOut) 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+
optOut = 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 object HeaderOptOutError extends BlockHeaderError
3536
}
3637

3738
sealed trait BlockHeaderValid

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

Lines changed: 16 additions & 0 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,6 +164,21 @@ abstract class BlockHeaderValidatorSkeleton(blockchainConfig: BlockchainConfig)
163164
if(blockHeader.number == parentHeader.number + 1) Right(BlockHeaderValid)
164165
else Left(HeaderNumberError)
165166

167+
/**
168+
* Validates [[io.iohk.ethereum.domain.BlockHeader.optOut]] 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.optOut.isDefined
176+
177+
if(isEcip1098Activated && isOptOutDefined) Right(BlockHeaderValid)
178+
else if (!isEcip1098Activated && !isOptOutDefined) Right(BlockHeaderValid)
179+
else Left(HeaderOptOutError)
180+
}
181+
166182
}
167183

168184

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,16 @@ object BlockHeadersStorage {
5555
Long,
5656
ByteString,
5757
ByteString,
58-
ByteString
58+
ByteString,
59+
Option[Boolean]
5960
)
6061

6162
import boopickle.DefaultBasic._
6263

6364
implicit val byteStringPickler: Pickler[ByteString] = transformPickler[ByteString, Array[Byte]](ByteString(_))(_.toArray[Byte])
6465
implicit val blockHeaderPickler: Pickler[BlockHeader] = transformPickler[BlockHeader, BlockHeaderBody]
65-
{ case (ph, oh, b, sr, txr, rr, lb, d, no, gl, gu, ut, ed, mh, n) =>
66-
new BlockHeader(ph, oh, b, sr, txr, rr, lb, d, no, gl, gu, ut, ed, mh, n)
66+
{ case (ph, oh, b, sr, txr, rr, lb, d, no, gl, gu, ut, ed, mh, n, oo) =>
67+
new BlockHeader(ph, oh, b, sr, txr, rr, lb, d, no, gl, gu, ut, ed, mh, n, oo)
6768
}{ blockHeader => (
6869
blockHeader.parentHash,
6970
blockHeader.ommersHash,
@@ -79,6 +80,7 @@ object BlockHeadersStorage {
7980
blockHeader.unixTimestamp,
8081
blockHeader.extraData,
8182
blockHeader.mixHash,
82-
blockHeader.nonce
83+
blockHeader.nonce,
84+
blockHeader.optOut
8385
)}
8486
}

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

Lines changed: 35 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+
optOut: 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+
|optOut: $optOut
4446
|}""".stripMargin
4547
}
4648

@@ -58,13 +60,18 @@ case class BlockHeader(
5860

5961
object BlockHeader {
6062

63+
private val NumberOfFields = 16
64+
6165
val emptyOmmerHash = ByteString(Hex.decode("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"))
6266

6367
def getEncodedWithoutNonce(blockHeader: BlockHeader): Array[Byte] = {
6468
val rlpEncoded = blockHeader.toRLPEncodable match {
65-
case rlpList: RLPList =>
69+
case rlpList: RLPList if rlpList.items.length == (NumberOfFields - 1) =>
6670
RLPList(rlpList.items.dropRight(2): _*)
6771

72+
case rlpList: RLPList if rlpList.items.length == NumberOfFields =>
73+
val rlpItemsWithoutNonce = rlpList.items.dropRight(3) :+ rlpList.items.last
74+
RLPList(rlpItemsWithoutNonce: _*)
6875
case _ => throw new Exception("BlockHeader cannot be encoded without nonce and mixHash")
6976
}
7077
rlpEncode(rlpEncoded)
@@ -73,8 +80,17 @@ object BlockHeader {
7380
implicit class BlockHeaderEnc(blockHeader: BlockHeader) extends RLPSerializable {
7481
override def toRLPEncodable: RLPEncodeable = {
7582
import blockHeader._
76-
RLPList(parentHash, ommersHash, beneficiary, stateRoot, transactionsRoot, receiptsRoot,
77-
logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce)
83+
optOut match {
84+
case Some(definedOptOut) =>
85+
val encodedOptOut = if(definedOptOut) 1 else 0
86+
87+
RLPList(parentHash, ommersHash, beneficiary, stateRoot, transactionsRoot, receiptsRoot,
88+
logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce, RLPList(encodedOptOut))
89+
90+
case None =>
91+
RLPList(parentHash, ommersHash, beneficiary, stateRoot, transactionsRoot, receiptsRoot,
92+
logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce)
93+
}
7894
}
7995
}
8096

@@ -88,7 +104,20 @@ object BlockHeader {
88104
case RLPList(parentHash, ommersHash, beneficiary, stateRoot, transactionsRoot, receiptsRoot,
89105
logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce) =>
90106
BlockHeader(parentHash, ommersHash, beneficiary, stateRoot, transactionsRoot, receiptsRoot,
91-
logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce)
107+
logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce, None)
108+
109+
case RLPList(parentHash, ommersHash, beneficiary, stateRoot, transactionsRoot, receiptsRoot,
110+
logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce, RLPList(encodedOptOut)) =>
111+
val booleanOptOut =
112+
if ((encodedOptOut: Int) == 1) true
113+
else if ((encodedOptOut: Int) == 0) false
114+
else throw new Exception("BlockHeader cannot be decoded with an invalid opt-out")
115+
116+
BlockHeader(parentHash, ommersHash, beneficiary, stateRoot, transactionsRoot, receiptsRoot,
117+
logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce, Some(booleanOptOut))
118+
119+
case _ =>
120+
throw new Exception("BlockHeader cannot be decoded")
92121
}
93122
}
94123
}

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)