@@ -2,9 +2,7 @@ package io.iohk.ethereum.domain
2
2
3
3
import akka .util .ByteString
4
4
import io .iohk .ethereum .crypto .kec256
5
- import io .iohk .ethereum .rlp .RLPImplicitConversions ._
6
- import io .iohk .ethereum .rlp .RLPImplicits ._
7
- import io .iohk .ethereum .rlp .{RLPEncodeable , RLPList , RLPSerializable , rawDecode , encode => rlpEncode }
5
+ import io .iohk .ethereum .rlp .{RLPDecoder , RLPEncodeable , RLPEncoder , RLPList , RLPSerializable , rawDecode , encode => rlpEncode }
8
6
import org .bouncycastle .util .encoders .Hex
9
7
10
8
case class BlockHeader (
@@ -23,7 +21,8 @@ case class BlockHeader(
23
21
extraData : ByteString ,
24
22
mixHash : ByteString ,
25
23
nonce : ByteString ,
26
- treasuryOptOut : Option [Boolean ]) {
24
+ treasuryOptOut : Option [Boolean ],
25
+ checkpoint : Option [Checkpoint ] = None ) {
27
26
28
27
override def toString : String = {
29
28
s """ BlockHeader {
@@ -43,6 +42,7 @@ case class BlockHeader(
43
42
|mixHash: ${Hex .toHexString(mixHash.toArray[Byte ])}
44
43
|nonce: ${Hex .toHexString(nonce.toArray[Byte ])},
45
44
|treasuryOptOut: $treasuryOptOut
45
+ |withCheckpoint: ${checkpoint.isDefined}
46
46
|} """ .stripMargin
47
47
}
48
48
@@ -54,72 +54,116 @@ case class BlockHeader(
54
54
55
55
lazy val hashAsHexString : String = Hex .toHexString(hash.toArray)
56
56
57
+ val hasCheckpoint : Boolean = checkpoint.isDefined
58
+
57
59
def idTag : String =
58
60
s " $number: $hashAsHexString"
59
61
}
60
62
61
63
object BlockHeader {
62
64
65
+ import Checkpoint ._
66
+ import io .iohk .ethereum .rlp .RLPImplicitConversions ._
67
+ import io .iohk .ethereum .rlp .RLPImplicits ._
68
+
69
+ private implicit val checkpointOptionDecoder = implicitly[RLPDecoder [Option [Checkpoint ]]]
70
+ private implicit val checkpointOptionEncoder = implicitly[RLPEncoder [Option [Checkpoint ]]]
71
+
63
72
val emptyOmmerHash = ByteString (Hex .decode(" 1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347" ))
64
73
65
74
def getEncodedWithoutNonce (blockHeader : BlockHeader ): Array [Byte ] = {
66
75
val rlpEncoded = blockHeader.toRLPEncodable match {
67
- case rlpList : RLPList if blockHeader.treasuryOptOut.isEmpty =>
68
- // Pre ECIP1098 block
69
- RLPList (rlpList.items.dropRight(2 ): _* )
76
+ case rlpList : RLPList if blockHeader.checkpoint.isDefined =>
77
+ // post ECIP1098 & ECIP1097 block
78
+ val rlpItemsWithoutNonce = rlpList.items.dropRight(4 ) ++ rlpList.items.takeRight(2 )
79
+ RLPList (rlpItemsWithoutNonce : _* )
70
80
71
81
case rlpList : RLPList if blockHeader.treasuryOptOut.isDefined =>
72
- // Post ECIP1098 block
82
+ // Post ECIP1098 block without checkpoint
73
83
val rlpItemsWithoutNonce = rlpList.items.dropRight(3 ) :+ rlpList.items.last
74
84
RLPList (rlpItemsWithoutNonce : _* )
75
85
86
+ case rlpList : RLPList if blockHeader.treasuryOptOut.isEmpty =>
87
+ // Pre ECIP1098 & ECIP1097 block
88
+ RLPList (rlpList.items.dropRight(2 ): _* )
89
+
76
90
case _ => throw new Exception (" BlockHeader cannot be encoded without nonce and mixHash" )
77
91
}
78
92
rlpEncode(rlpEncoded)
79
93
}
80
94
81
95
implicit class BlockHeaderEnc (blockHeader : BlockHeader ) extends RLPSerializable {
96
+ private def encodeOptOut (definedOptOut : Boolean ) = {
97
+ val encodedOptOut = if (definedOptOut) 1 else 0
98
+ RLPList (encodedOptOut)
99
+ }
82
100
override def toRLPEncodable : RLPEncodeable = {
83
101
import blockHeader ._
84
- treasuryOptOut match {
85
- case Some (definedOptOut) =>
86
- // Post ECIP1098 block, whole block is encoded
87
- val encodedOptOut = if (definedOptOut) 1 else 0
102
+ (treasuryOptOut, checkpoint) match {
103
+ case (Some (definedOptOut), Some (_)) =>
104
+ // Post ECIP1098 & ECIP1097 block, block with treasury enabled and checkpoint is encoded
105
+ RLPList (parentHash, ommersHash, beneficiary, stateRoot, transactionsRoot, receiptsRoot,
106
+ logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce, encodeOptOut(definedOptOut), checkpoint)
88
107
108
+ case (Some (definedOptOut), None ) =>
109
+ // Post ECIP1098 block, Pre ECIP1097 or without checkpoint, block with treasury enabled is encoded
89
110
RLPList (parentHash, ommersHash, beneficiary, stateRoot, transactionsRoot, receiptsRoot,
90
- logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce, RLPList (encodedOptOut ))
111
+ logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce, encodeOptOut(definedOptOut ))
91
112
92
- case None =>
93
- // Pre ECIP1098 block, encoding works as if optOut field wasn't defined for backwards compatibility
113
+ case (None , Some (_)) =>
114
+ // Post ECIP1097 block with checkpoint, treasury disabled, block with checkpoint is encoded
115
+ RLPList (parentHash, ommersHash, beneficiary, stateRoot, transactionsRoot, receiptsRoot,
116
+ logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce, RLPList (), checkpoint)
117
+
118
+ case _ =>
119
+ // Pre ECIP1098 and ECIP1097 block, encoding works as if optOut and checkpoint fields weren't defined for backwards compatibility
94
120
RLPList (parentHash, ommersHash, beneficiary, stateRoot, transactionsRoot, receiptsRoot,
95
121
logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce)
96
122
}
97
123
}
98
124
}
99
125
100
- implicit class BlockheaderDec (val bytes : Array [Byte ]) extends AnyVal {
101
- def toBlockHeader : BlockHeader = BlockheaderEncodableDec (rawDecode(bytes)).toBlockHeader
126
+ implicit class BlockHeaderByteArrayDec (val bytes : Array [Byte ]) extends AnyVal {
127
+ def toBlockHeader : BlockHeader = BlockHeaderDec (rawDecode(bytes)).toBlockHeader
102
128
}
103
129
104
- implicit class BlockheaderEncodableDec (val rlpEncodeable : RLPEncodeable ) extends AnyVal {
130
+ implicit class BlockHeaderDec (val rlpEncodeable : RLPEncodeable ) extends AnyVal {
131
+ private def decodeOptOut (encodedOptOut : RLPEncodeable ): Option [Boolean ] = {
132
+ val booleanOptOut = {
133
+ if ((encodedOptOut : Int ) == 1 ) true
134
+ else if ((encodedOptOut : Int ) == 0 ) false
135
+ else throw new Exception (" BlockHeader cannot be decoded with an invalid opt-out" )
136
+ }
137
+ Some (booleanOptOut)
138
+ }
105
139
def toBlockHeader : BlockHeader = {
106
140
rlpEncodeable match {
107
141
case RLPList (parentHash, ommersHash, beneficiary, stateRoot, transactionsRoot, receiptsRoot,
108
- 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
142
+ logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce, RLPList (encodedOptOut), encodedCheckpoint) =>
143
+ // Post ECIP1098 & ECIP1097 block with checkpoint, whole block is encoded
144
+ BlockHeader (parentHash, ommersHash, beneficiary, stateRoot, transactionsRoot, receiptsRoot,
145
+ logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce,
146
+ decodeOptOut(encodedOptOut), checkpointOptionDecoder.decode(encodedCheckpoint))
147
+
148
+ case RLPList (parentHash, ommersHash, beneficiary, stateRoot, transactionsRoot, receiptsRoot,
149
+ logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce, RLPList (), encodedCheckpoint) =>
150
+ // Post ECIP1098 & ECIP1097 block with checkpoint and treasury disabled
110
151
BlockHeader (parentHash, ommersHash, beneficiary, stateRoot, transactionsRoot, receiptsRoot,
111
- logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce, None )
152
+ logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce, None , checkpointOptionDecoder.decode(encodedCheckpoint))
153
+
112
154
113
155
case RLPList (parentHash, ommersHash, beneficiary, stateRoot, transactionsRoot, receiptsRoot,
114
156
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" )
157
+ // Post ECIP1098 block without checkpoint
158
+ BlockHeader (parentHash, ommersHash, beneficiary, stateRoot, transactionsRoot, receiptsRoot,
159
+ logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce, decodeOptOut(encodedOptOut))
120
160
161
+
162
+ case RLPList (parentHash, ommersHash, beneficiary, stateRoot, transactionsRoot, receiptsRoot,
163
+ logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce) =>
164
+ // Pre ECIP1098 and ECIP1097 block, decoding works as if optOut and checkpoint fields weren't defined for backwards compatibility
121
165
BlockHeader (parentHash, ommersHash, beneficiary, stateRoot, transactionsRoot, receiptsRoot,
122
- logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce, Some (booleanOptOut) )
166
+ logsBloom, difficulty, number, gasLimit, gasUsed, unixTimestamp, extraData, mixHash, nonce, None , None )
123
167
124
168
case _ =>
125
169
throw new Exception (" BlockHeader cannot be decoded" )
0 commit comments