Skip to content

Commit 9b04b42

Browse files
committed
[ETCM-1095] Split Receipt into LegacyReceipt and Type01Receipt
1 parent d161ae4 commit 9b04b42

File tree

15 files changed

+216
-58
lines changed

15 files changed

+216
-58
lines changed

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

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import akka.util.ByteString
55
import boopickle.Default.Pickle
66
import boopickle.Default.Unpickle
77
import boopickle.DefaultBasic._
8+
import boopickle.Pickler
89

10+
import io.iohk.ethereum.crypto.ECDSASignature
911
import io.iohk.ethereum.db.dataSource.DataSource
1012
import io.iohk.ethereum.db.storage.ReceiptStorage._
1113
import io.iohk.ethereum.domain.Address
@@ -63,11 +65,21 @@ object ReceiptStorage {
6365
TxLogEntry(address, topics, data)
6466
}(entry => (entry.loggerAddress, entry.logTopics, entry.data))
6567

66-
implicit val receiptPickler: Pickler[Receipt] =
67-
transformPickler[Receipt, (TransactionOutcome, BigInt, ByteString, Seq[TxLogEntry])] {
68-
case (state, gas, filter, logs) => new Receipt(state, gas, filter, logs)
68+
implicit val legacyReceiptPickler: Pickler[LegacyReceipt] =
69+
transformPickler[LegacyReceipt, (TransactionOutcome, BigInt, ByteString, Seq[TxLogEntry])] {
70+
case (state, gas, filter, logs) => LegacyReceipt(state, gas, filter, logs)
6971
} { receipt =>
7072
(receipt.postTransactionStateHash, receipt.cumulativeGasUsed, receipt.logsBloomFilter, receipt.logs)
7173
}
7274

75+
implicit val type01ReceiptPickler: Pickler[Type01Receipt] =
76+
transformPickler[Type01Receipt, (TransactionOutcome, BigInt, ByteString, Seq[TxLogEntry])] {
77+
case (state, gas, filter, logs) => Type01Receipt(LegacyReceipt(state, gas, filter, logs))
78+
} { receipt =>
79+
(receipt.postTransactionStateHash, receipt.cumulativeGasUsed, receipt.logsBloomFilter, receipt.logs)
80+
}
81+
82+
implicit val receiptPickler: Pickler[Receipt] = compositePickler[Receipt]
83+
.addConcreteType[LegacyReceipt]
84+
.addConcreteType[Type01Receipt]
7385
}

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

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,51 @@ import org.bouncycastle.util.encoders.Hex
66

77
import io.iohk.ethereum.mpt.ByteArraySerializable
88

9+
sealed trait Receipt {
10+
def postTransactionStateHash: TransactionOutcome
11+
def cumulativeGasUsed: BigInt
12+
def logsBloomFilter: ByteString
13+
def logs: Seq[TxLogEntry]
14+
}
15+
16+
// shared structure for EIP-2930, EIP-1559
17+
abstract class TypedLegacyReceipt(transactionTypeId: Byte, val delegateReceipt: LegacyReceipt) extends Receipt {
18+
def postTransactionStateHash: TransactionOutcome = delegateReceipt.postTransactionStateHash
19+
def cumulativeGasUsed: BigInt = delegateReceipt.cumulativeGasUsed
20+
def logsBloomFilter: ByteString = delegateReceipt.logsBloomFilter
21+
def logs: Seq[TxLogEntry] = delegateReceipt.logs
22+
}
23+
924
object Receipt {
1025

1126
val byteArraySerializable: ByteArraySerializable[Receipt] = new ByteArraySerializable[Receipt] {
27+
1228
import io.iohk.ethereum.network.p2p.messages.ETH63.ReceiptImplicits._
1329

1430
override def fromBytes(bytes: Array[Byte]): Receipt = bytes.toReceipt
1531

1632
override def toBytes(input: Receipt): Array[Byte] = input.toBytes
1733
}
34+
}
1835

36+
object LegacyReceipt {
1937
def withHashOutcome(
2038
postTransactionStateHash: ByteString,
2139
cumulativeGasUsed: BigInt,
2240
logsBloomFilter: ByteString,
2341
logs: Seq[TxLogEntry]
24-
): Receipt =
25-
Receipt(HashOutcome(postTransactionStateHash), cumulativeGasUsed, logsBloomFilter, logs)
42+
): LegacyReceipt =
43+
LegacyReceipt(HashOutcome(postTransactionStateHash), cumulativeGasUsed, logsBloomFilter, logs)
44+
}
2645

46+
object Type01Receipt {
47+
def withHashOutcome(
48+
postTransactionStateHash: ByteString,
49+
cumulativeGasUsed: BigInt,
50+
logsBloomFilter: ByteString,
51+
logs: Seq[TxLogEntry]
52+
): Type01Receipt =
53+
Type01Receipt(LegacyReceipt.withHashOutcome(postTransactionStateHash, cumulativeGasUsed, logsBloomFilter, logs))
2754
}
2855

2956
/** @param postTransactionStateHash For blocks where block.number >= byzantium-block-number (from config),
@@ -35,24 +62,33 @@ object Receipt {
3562
*
3663
* More description: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-658.md
3764
*/
38-
case class Receipt(
65+
case class LegacyReceipt(
3966
postTransactionStateHash: TransactionOutcome,
4067
cumulativeGasUsed: BigInt,
4168
logsBloomFilter: ByteString,
4269
logs: Seq[TxLogEntry]
43-
) {
44-
override def toString: String = {
70+
) extends Receipt {
71+
def toPrettyString(prefix: String): String = {
4572
val stateHash = postTransactionStateHash match {
4673
case HashOutcome(hash) => hash.toArray[Byte]
4774
case SuccessOutcome => Array(1.toByte)
4875
case _ => Array(0.toByte)
4976
}
5077

51-
s"Receipt{ " +
78+
s"${prefix}{ " +
5279
s"postTransactionStateHash: ${Hex.toHexString(stateHash)}, " +
5380
s"cumulativeGasUsed: $cumulativeGasUsed, " +
5481
s"logsBloomFilter: ${Hex.toHexString(logsBloomFilter.toArray[Byte])}, " +
5582
s"logs: $logs" +
5683
s"}"
5784
}
85+
86+
override def toString: String = toPrettyString("LegacyReceipt")
87+
}
88+
89+
/** EIP-2930 receipt for Transaction type 1
90+
* @param legacyReceipt
91+
*/
92+
case class Type01Receipt(legacyReceipt: LegacyReceipt) extends TypedLegacyReceipt(Transaction.Type01, legacyReceipt) {
93+
override def toString: String = legacyReceipt.toPrettyString("Type01Receipt")
5894
}

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

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -311,12 +311,25 @@ class BlockPreparator(
311311
HashOutcome(newWorld.stateRootHash)
312312
}
313313

314-
val receipt = Receipt(
315-
postTransactionStateHash = transactionOutcome,
316-
cumulativeGasUsed = acumGas + gasUsed,
317-
logsBloomFilter = BloomFilter.create(logs),
318-
logs = logs
319-
)
314+
val receipt = stx.tx match {
315+
case _: LegacyTransaction =>
316+
LegacyReceipt(
317+
postTransactionStateHash = transactionOutcome,
318+
cumulativeGasUsed = acumGas + gasUsed,
319+
logsBloomFilter = BloomFilter.create(logs),
320+
logs = logs
321+
)
322+
323+
case _: TransactionWithAccessList =>
324+
Type01Receipt(
325+
LegacyReceipt(
326+
postTransactionStateHash = transactionOutcome,
327+
cumulativeGasUsed = acumGas + gasUsed,
328+
logsBloomFilter = BloomFilter.create(logs),
329+
logs = logs
330+
)
331+
)
332+
}
320333

321334
log.debug(s"Receipt generated for tx ${stx.hash.toHex}, $receipt")
322335

src/main/scala/io/iohk/ethereum/network/p2p/messages/ETH63.scala

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,12 @@ object ETH63 {
171171
case SuccessOutcome => 1.toByte
172172
case _ => 0.toByte
173173
}
174-
RLPList(stateHash, cumulativeGasUsed, logsBloomFilter, RLPList(logs.map(_.toRLPEncodable): _*))
174+
val legacyRLPReceipt =
175+
RLPList(stateHash, cumulativeGasUsed, logsBloomFilter, RLPList(logs.map(_.toRLPEncodable): _*))
176+
receipt match {
177+
case _: LegacyReceipt => legacyRLPReceipt
178+
case _: Type01Receipt => PrefixedRLPEncodable(Transaction.Type01, legacyRLPReceipt)
179+
}
175180
}
176181
}
177182

@@ -180,25 +185,39 @@ object ETH63 {
180185
}
181186

182187
implicit class ReceiptDec(val bytes: Array[Byte]) extends AnyVal {
183-
def toReceipt: Receipt = ReceiptRLPEncodableDec(rawDecode(bytes)).toReceipt
188+
import BaseETH6XMessages.TypedTransaction._
189+
190+
def toReceipt: Receipt = {
191+
val first = bytes(0)
192+
(first match {
193+
case Transaction.Type01 => PrefixedRLPEncodable(Transaction.Type01, rawDecode(bytes.tail))
194+
case _ => rawDecode(bytes)
195+
}).toReceipt
196+
}
184197

185198
def toReceipts: Seq[Receipt] = rawDecode(bytes) match {
186-
case RLPList(items @ _*) => items.map(_.toReceipt)
199+
case RLPList(items @ _*) => items.toTypedRLPEncodables.map(_.toReceipt)
187200
case _ => throw new RuntimeException("Cannot decode Receipts")
188201
}
189202
}
190203

191204
implicit class ReceiptRLPEncodableDec(val rlpEncodeable: RLPEncodeable) extends AnyVal {
192-
def toReceipt: Receipt = rlpEncodeable match {
205+
206+
def toLegacyReceipt: LegacyReceipt = rlpEncodeable match {
193207
case RLPList(postTransactionStateHash, cumulativeGasUsed, logsBloomFilter, logs: RLPList) =>
194208
val stateHash = postTransactionStateHash match {
195209
case RLPValue(bytes) if bytes.length > 1 => HashOutcome(ByteString(bytes))
196210
case RLPValue(bytes) if bytes.length == 1 && bytes.head == 1 => SuccessOutcome
197211
case _ => FailureOutcome
198212
}
199-
Receipt(stateHash, cumulativeGasUsed, logsBloomFilter, logs.items.map(_.toTxLogEntry))
213+
LegacyReceipt(stateHash, cumulativeGasUsed, logsBloomFilter, logs.items.map(_.toTxLogEntry))
200214
case _ => throw new RuntimeException("Cannot decode Receipt")
201215
}
216+
217+
def toReceipt: Receipt = rlpEncodeable match {
218+
case PrefixedRLPEncodable(Transaction.Type01, legacyReceipt) => Type01Receipt(legacyReceipt.toLegacyReceipt)
219+
case other => other.toLegacyReceipt
220+
}
202221
}
203222
}
204223

@@ -217,10 +236,12 @@ object ETH63 {
217236

218237
implicit class ReceiptsDec(val bytes: Array[Byte]) extends AnyVal {
219238
import ReceiptImplicits._
239+
import BaseETH6XMessages.TypedTransaction._
220240

221241
def toReceipts: Receipts = rawDecode(bytes) match {
222-
case rlpList: RLPList => Receipts(rlpList.items.collect { case r: RLPList => r.items.map(_.toReceipt) })
223-
case _ => throw new RuntimeException("Cannot decode Receipts")
242+
case rlpList: RLPList =>
243+
Receipts(rlpList.items.collect { case r: RLPList => r.items.toTypedRLPEncodables.map(_.toReceipt) })
244+
case _ => throw new RuntimeException("Cannot decode Receipts")
224245
}
225246
}
226247
}

src/test/scala/io/iohk/ethereum/ObjectGenerators.scala

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,17 +69,22 @@ trait ObjectGenerators {
6969
arrayList <- Gen.nonEmptyListOf(byteArrayOfNItemsGen(size))
7070
} yield byteStringList.zip(arrayList)
7171

72-
def receiptGen(): Gen[Receipt] = for {
72+
def receiptGen(): Gen[Receipt] =
73+
Gen.oneOf(legacyReceiptGen(), type01ReceiptGen())
74+
75+
def legacyReceiptGen(): Gen[LegacyReceipt] = for {
7376
postTransactionStateHash <- byteArrayOfNItemsGen(32)
7477
cumulativeGasUsed <- bigIntGen
7578
logsBloomFilter <- byteArrayOfNItemsGen(256)
76-
} yield Receipt.withHashOutcome(
79+
} yield LegacyReceipt.withHashOutcome(
7780
postTransactionStateHash = ByteString(postTransactionStateHash),
7881
cumulativeGasUsed = cumulativeGasUsed,
7982
logsBloomFilter = ByteString(logsBloomFilter),
8083
logs = Seq()
8184
)
8285

86+
def type01ReceiptGen(): Gen[Type01Receipt] = legacyReceiptGen().map(Type01Receipt(_))
87+
8388
def addressGen: Gen[Address] = byteArrayOfNItemsGen(20).map(Address(_))
8489

8590
def accessListItemGen(): Gen[AccessListItem] = for {

src/test/scala/io/iohk/ethereum/consensus/validators/std/StdBlockValidatorSpec.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -179,28 +179,28 @@ class StdBlockValidatorSpec extends AnyFlatSpec with Matchers with SecureRandomB
179179
.header
180180

181181
val validReceipts: Seq[Receipt] = Seq(
182-
Receipt.withHashOutcome(
182+
LegacyReceipt.withHashOutcome(
183183
postTransactionStateHash =
184184
ByteString(Hex.decode("ce0ac687bb90d457b6573d74e4a25ea7c012fee329eb386dbef161c847f9842d")),
185185
cumulativeGasUsed = 21000,
186186
logsBloomFilter = ByteString(Hex.decode("0" * 512)),
187187
logs = Seq[TxLogEntry]()
188188
),
189-
Receipt.withHashOutcome(
189+
LegacyReceipt.withHashOutcome(
190190
postTransactionStateHash =
191191
ByteString(Hex.decode("b927d361126302acaa1fa5e93d0b7e349e278231fe2fc2846bfd54f50377f20a")),
192192
cumulativeGasUsed = 42000,
193193
logsBloomFilter = ByteString(Hex.decode("0" * 512)),
194194
logs = Seq[TxLogEntry]()
195195
),
196-
Receipt.withHashOutcome(
196+
LegacyReceipt.withHashOutcome(
197197
postTransactionStateHash =
198198
ByteString(Hex.decode("1e913d6bdd412d71292173d7908f8792adcf958b84c89575bc871a1decaee56d")),
199199
cumulativeGasUsed = 63000,
200200
logsBloomFilter = ByteString(Hex.decode("0" * 512)),
201201
logs = Seq[TxLogEntry]()
202202
),
203-
Receipt.withHashOutcome(
203+
LegacyReceipt.withHashOutcome(
204204
postTransactionStateHash =
205205
ByteString(Hex.decode("0c6e052bc83482bafaccffc4217adad49f3a9533c69c820966d75ed0154091e6")),
206206
cumulativeGasUsed = 84000,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ class EthTxServiceSpec
428428

429429
val contractCreatingTransactionSender: Address = SignedTransaction.getSender(contractCreatingTransaction).get
430430

431-
val fakeReceipt: Receipt = Receipt.withHashOutcome(
431+
val fakeReceipt: LegacyReceipt = LegacyReceipt.withHashOutcome(
432432
postTransactionStateHash = ByteString(Hex.decode("01" * 32)),
433433
cumulativeGasUsed = 43,
434434
logsBloomFilter = ByteString(Hex.decode("00" * 256)),

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ class FilterManagerSpec
102102
.returning(
103103
Some(
104104
Seq(
105-
Receipt.withHashOutcome(
105+
LegacyReceipt.withHashOutcome(
106106
postTransactionStateHash = ByteString(),
107107
cumulativeGasUsed = 0,
108108
logsBloomFilter = BloomFilter.create(logs2),
@@ -191,13 +191,13 @@ class FilterManagerSpec
191191
.returning(
192192
Some(
193193
Seq(
194-
Receipt.withHashOutcome(
194+
LegacyReceipt.withHashOutcome(
195195
postTransactionStateHash = ByteString(),
196196
cumulativeGasUsed = 0,
197197
logsBloomFilter = BloomFilter.create(Seq(log4_1)),
198198
logs = Seq(log4_1)
199199
),
200-
Receipt.withHashOutcome(
200+
LegacyReceipt.withHashOutcome(
201201
postTransactionStateHash = ByteString(),
202202
cumulativeGasUsed = 0,
203203
logsBloomFilter = BloomFilter.create(Seq(log4_2)),
@@ -266,7 +266,7 @@ class FilterManagerSpec
266266
.returning(
267267
Some(
268268
Seq(
269-
Receipt.withHashOutcome(
269+
LegacyReceipt.withHashOutcome(
270270
postTransactionStateHash = ByteString(),
271271
cumulativeGasUsed = 0,
272272
logsBloomFilter = BloomFilter.create(logs),
@@ -305,7 +305,7 @@ class FilterManagerSpec
305305
PendingBlock(
306306
block2,
307307
Seq(
308-
Receipt.withHashOutcome(
308+
LegacyReceipt.withHashOutcome(
309309
postTransactionStateHash = ByteString(),
310310
cumulativeGasUsed = 0,
311311
logsBloomFilter = BloomFilter.create(logs2),

src/test/scala/io/iohk/ethereum/ledger/BlockExecutionSpec.scala

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ class BlockExecutionSpec extends AnyWordSpec with Matchers with ScalaCheckProper
270270

271271
// Check valid receipts
272272
resultingReceipts.size shouldBe 1
273-
val Receipt(rootHashReceipt, gasUsedReceipt, logsBloomFilterReceipt, logsReceipt) = resultingReceipts.head
273+
val LegacyReceipt(rootHashReceipt, gasUsedReceipt, logsBloomFilterReceipt, logsReceipt) = resultingReceipts.head
274274
rootHashReceipt shouldBe HashOutcome(expectedStateRoot)
275275
gasUsedReceipt shouldBe resultingGasUsed
276276
logsBloomFilterReceipt shouldBe BloomFilter.create(Nil)
@@ -348,7 +348,8 @@ class BlockExecutionSpec extends AnyWordSpec with Matchers with ScalaCheckProper
348348

349349
// Check valid receipts
350350
resultingReceipts.size shouldBe 1
351-
val Receipt(rootHashReceipt, gasUsedReceipt, logsBloomFilterReceipt, logsReceipt) = resultingReceipts.head
351+
val LegacyReceipt(rootHashReceipt, gasUsedReceipt, logsBloomFilterReceipt, logsReceipt) =
352+
resultingReceipts.head
352353
rootHashReceipt shouldBe HashOutcome(expectedStateRoot)
353354
gasUsedReceipt shouldBe resultingGasUsed
354355
logsBloomFilterReceipt shouldBe BloomFilter.create(logs)
@@ -607,7 +608,7 @@ class BlockExecutionSpec extends AnyWordSpec with Matchers with ScalaCheckProper
607608
)
608609
val expectedStateRootTx1 = applyChanges(validBlockParentHeader.stateRoot, changesTx1)
609610

610-
val Receipt(rootHashReceipt1, gasUsedReceipt1, logsBloomFilterReceipt1, logsReceipt1) = receipt1
611+
val LegacyReceipt(rootHashReceipt1, gasUsedReceipt1, logsBloomFilterReceipt1, logsReceipt1) = receipt1
611612
rootHashReceipt1 shouldBe HashOutcome(expectedStateRootTx1)
612613
gasUsedReceipt1 shouldBe stx1.tx.tx.gasLimit
613614
logsBloomFilterReceipt1 shouldBe BloomFilter.create(Nil)
@@ -622,7 +623,7 @@ class BlockExecutionSpec extends AnyWordSpec with Matchers with ScalaCheckProper
622623
)
623624
val expectedStateRootTx2 = applyChanges(expectedStateRootTx1, changesTx2)
624625

625-
val Receipt(rootHashReceipt2, gasUsedReceipt2, logsBloomFilterReceipt2, logsReceipt2) = receipt2
626+
val LegacyReceipt(rootHashReceipt2, gasUsedReceipt2, logsBloomFilterReceipt2, logsReceipt2) = receipt2
626627
rootHashReceipt2 shouldBe HashOutcome(expectedStateRootTx2)
627628
gasUsedReceipt2 shouldBe (transaction1.gasLimit + transaction2.gasLimit)
628629
logsBloomFilterReceipt2 shouldBe BloomFilter.create(Nil)

src/test/scala/io/iohk/ethereum/ledger/BlockValidationSpec.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ class BlockValidationSpec extends AnyWordSpec with Matchers with MockFactory {
127127
)
128128
)
129129

130-
def mkReceipt(stateHash: String, gas: BigInt): Receipt = Receipt.withHashOutcome(
130+
def mkReceipt(stateHash: String, gas: BigInt): Receipt = LegacyReceipt.withHashOutcome(
131131
postTransactionStateHash = hash2ByteString(stateHash),
132132
cumulativeGasUsed = gas,
133133
logsBloomFilter = bloomFilter,

0 commit comments

Comments
 (0)