Skip to content

Commit

Permalink
[ETCM-1095] Split Receipt into LegacyReceipt and Type01Receipt
Browse files Browse the repository at this point in the history
  • Loading branch information
strauss-m committed Aug 19, 2021
1 parent 46260ae commit 2a33d1f
Show file tree
Hide file tree
Showing 15 changed files with 216 additions and 58 deletions.
18 changes: 15 additions & 3 deletions src/main/scala/io/iohk/ethereum/db/storage/ReceiptStorage.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import akka.util.ByteString
import boopickle.Default.Pickle
import boopickle.Default.Unpickle
import boopickle.DefaultBasic._
import boopickle.Pickler

import io.iohk.ethereum.crypto.ECDSASignature
import io.iohk.ethereum.db.dataSource.DataSource
import io.iohk.ethereum.db.storage.ReceiptStorage._
import io.iohk.ethereum.domain.Address
Expand Down Expand Up @@ -63,11 +65,21 @@ object ReceiptStorage {
TxLogEntry(address, topics, data)
}(entry => (entry.loggerAddress, entry.logTopics, entry.data))

implicit val receiptPickler: Pickler[Receipt] =
transformPickler[Receipt, (TransactionOutcome, BigInt, ByteString, Seq[TxLogEntry])] {
case (state, gas, filter, logs) => new Receipt(state, gas, filter, logs)
implicit val legacyReceiptPickler: Pickler[LegacyReceipt] =
transformPickler[LegacyReceipt, (TransactionOutcome, BigInt, ByteString, Seq[TxLogEntry])] {
case (state, gas, filter, logs) => LegacyReceipt(state, gas, filter, logs)
} { receipt =>
(receipt.postTransactionStateHash, receipt.cumulativeGasUsed, receipt.logsBloomFilter, receipt.logs)
}

implicit val type01ReceiptPickler: Pickler[Type01Receipt] =
transformPickler[Type01Receipt, (TransactionOutcome, BigInt, ByteString, Seq[TxLogEntry])] {
case (state, gas, filter, logs) => Type01Receipt(LegacyReceipt(state, gas, filter, logs))
} { receipt =>
(receipt.postTransactionStateHash, receipt.cumulativeGasUsed, receipt.logsBloomFilter, receipt.logs)
}

implicit val receiptPickler: Pickler[Receipt] = compositePickler[Receipt]
.addConcreteType[LegacyReceipt]
.addConcreteType[Type01Receipt]
}
48 changes: 42 additions & 6 deletions src/main/scala/io/iohk/ethereum/domain/Receipt.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,51 @@ import org.bouncycastle.util.encoders.Hex

import io.iohk.ethereum.mpt.ByteArraySerializable

sealed trait Receipt {
def postTransactionStateHash: TransactionOutcome
def cumulativeGasUsed: BigInt
def logsBloomFilter: ByteString
def logs: Seq[TxLogEntry]
}

// shared structure for EIP-2930, EIP-1559
abstract class TypedLegacyReceipt(transactionTypeId: Byte, val delegateReceipt: LegacyReceipt) extends Receipt {
def postTransactionStateHash: TransactionOutcome = delegateReceipt.postTransactionStateHash
def cumulativeGasUsed: BigInt = delegateReceipt.cumulativeGasUsed
def logsBloomFilter: ByteString = delegateReceipt.logsBloomFilter
def logs: Seq[TxLogEntry] = delegateReceipt.logs
}

object Receipt {

val byteArraySerializable: ByteArraySerializable[Receipt] = new ByteArraySerializable[Receipt] {

import io.iohk.ethereum.network.p2p.messages.ETH63.ReceiptImplicits._

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

override def toBytes(input: Receipt): Array[Byte] = input.toBytes
}
}

object LegacyReceipt {
def withHashOutcome(
postTransactionStateHash: ByteString,
cumulativeGasUsed: BigInt,
logsBloomFilter: ByteString,
logs: Seq[TxLogEntry]
): Receipt =
Receipt(HashOutcome(postTransactionStateHash), cumulativeGasUsed, logsBloomFilter, logs)
): LegacyReceipt =
LegacyReceipt(HashOutcome(postTransactionStateHash), cumulativeGasUsed, logsBloomFilter, logs)
}

object Type01Receipt {
def withHashOutcome(
postTransactionStateHash: ByteString,
cumulativeGasUsed: BigInt,
logsBloomFilter: ByteString,
logs: Seq[TxLogEntry]
): Type01Receipt =
Type01Receipt(LegacyReceipt.withHashOutcome(postTransactionStateHash, cumulativeGasUsed, logsBloomFilter, logs))
}

/** @param postTransactionStateHash For blocks where block.number >= byzantium-block-number (from config),
Expand All @@ -35,24 +62,33 @@ object Receipt {
*
* More description: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-658.md
*/
case class Receipt(
case class LegacyReceipt(
postTransactionStateHash: TransactionOutcome,
cumulativeGasUsed: BigInt,
logsBloomFilter: ByteString,
logs: Seq[TxLogEntry]
) {
override def toString: String = {
) extends Receipt {
def toPrettyString(prefix: String): String = {
val stateHash = postTransactionStateHash match {
case HashOutcome(hash) => hash.toArray[Byte]
case SuccessOutcome => Array(1.toByte)
case _ => Array(0.toByte)
}

s"Receipt{ " +
s"${prefix}{ " +
s"postTransactionStateHash: ${Hex.toHexString(stateHash)}, " +
s"cumulativeGasUsed: $cumulativeGasUsed, " +
s"logsBloomFilter: ${Hex.toHexString(logsBloomFilter.toArray[Byte])}, " +
s"logs: $logs" +
s"}"
}

override def toString: String = toPrettyString("LegacyReceipt")
}

/** EIP-2930 receipt for Transaction type 1
* @param legacyReceipt
*/
case class Type01Receipt(legacyReceipt: LegacyReceipt) extends TypedLegacyReceipt(Transaction.Type01, legacyReceipt) {
override def toString: String = legacyReceipt.toPrettyString("Type01Receipt")
}
25 changes: 19 additions & 6 deletions src/main/scala/io/iohk/ethereum/ledger/BlockPreparator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -311,12 +311,25 @@ class BlockPreparator(
HashOutcome(newWorld.stateRootHash)
}

val receipt = Receipt(
postTransactionStateHash = transactionOutcome,
cumulativeGasUsed = acumGas + gasUsed,
logsBloomFilter = BloomFilter.create(logs),
logs = logs
)
val receipt = stx.tx match {
case _: LegacyTransaction =>
LegacyReceipt(
postTransactionStateHash = transactionOutcome,
cumulativeGasUsed = acumGas + gasUsed,
logsBloomFilter = BloomFilter.create(logs),
logs = logs
)

case _: TransactionWithAccessList =>
Type01Receipt(
LegacyReceipt(
postTransactionStateHash = transactionOutcome,
cumulativeGasUsed = acumGas + gasUsed,
logsBloomFilter = BloomFilter.create(logs),
logs = logs
)
)
}

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

Expand Down
35 changes: 28 additions & 7 deletions src/main/scala/io/iohk/ethereum/network/p2p/messages/ETH63.scala
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,12 @@ object ETH63 {
case SuccessOutcome => 1.toByte
case _ => 0.toByte
}
RLPList(stateHash, cumulativeGasUsed, logsBloomFilter, RLPList(logs.map(_.toRLPEncodable): _*))
val legacyRLPReceipt =
RLPList(stateHash, cumulativeGasUsed, logsBloomFilter, RLPList(logs.map(_.toRLPEncodable): _*))
receipt match {
case _: LegacyReceipt => legacyRLPReceipt
case _: Type01Receipt => PrefixedRLPEncodable(Transaction.Type01, legacyRLPReceipt)
}
}
}

Expand All @@ -180,25 +185,39 @@ object ETH63 {
}

implicit class ReceiptDec(val bytes: Array[Byte]) extends AnyVal {
def toReceipt: Receipt = ReceiptRLPEncodableDec(rawDecode(bytes)).toReceipt
import BaseETH6XMessages.TypedTransaction._

def toReceipt: Receipt = {
val first = bytes(0)
(first match {
case Transaction.Type01 => PrefixedRLPEncodable(Transaction.Type01, rawDecode(bytes.tail))
case _ => rawDecode(bytes)
}).toReceipt
}

def toReceipts: Seq[Receipt] = rawDecode(bytes) match {
case RLPList(items @ _*) => items.map(_.toReceipt)
case RLPList(items @ _*) => items.toTypedRLPEncodables.map(_.toReceipt)
case _ => throw new RuntimeException("Cannot decode Receipts")
}
}

implicit class ReceiptRLPEncodableDec(val rlpEncodeable: RLPEncodeable) extends AnyVal {
def toReceipt: Receipt = rlpEncodeable match {

def toLegacyReceipt: LegacyReceipt = rlpEncodeable match {
case RLPList(postTransactionStateHash, cumulativeGasUsed, logsBloomFilter, logs: RLPList) =>
val stateHash = postTransactionStateHash match {
case RLPValue(bytes) if bytes.length > 1 => HashOutcome(ByteString(bytes))
case RLPValue(bytes) if bytes.length == 1 && bytes.head == 1 => SuccessOutcome
case _ => FailureOutcome
}
Receipt(stateHash, cumulativeGasUsed, logsBloomFilter, logs.items.map(_.toTxLogEntry))
LegacyReceipt(stateHash, cumulativeGasUsed, logsBloomFilter, logs.items.map(_.toTxLogEntry))
case _ => throw new RuntimeException("Cannot decode Receipt")
}

def toReceipt: Receipt = rlpEncodeable match {
case PrefixedRLPEncodable(Transaction.Type01, legacyReceipt) => Type01Receipt(legacyReceipt.toLegacyReceipt)
case other => other.toLegacyReceipt
}
}
}

Expand All @@ -217,10 +236,12 @@ object ETH63 {

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

def toReceipts: Receipts = rawDecode(bytes) match {
case rlpList: RLPList => Receipts(rlpList.items.collect { case r: RLPList => r.items.map(_.toReceipt) })
case _ => throw new RuntimeException("Cannot decode Receipts")
case rlpList: RLPList =>
Receipts(rlpList.items.collect { case r: RLPList => r.items.toTypedRLPEncodables.map(_.toReceipt) })
case _ => throw new RuntimeException("Cannot decode Receipts")
}
}
}
Expand Down
9 changes: 7 additions & 2 deletions src/test/scala/io/iohk/ethereum/ObjectGenerators.scala
Original file line number Diff line number Diff line change
Expand Up @@ -69,17 +69,22 @@ trait ObjectGenerators {
arrayList <- Gen.nonEmptyListOf(byteArrayOfNItemsGen(size))
} yield byteStringList.zip(arrayList)

def receiptGen(): Gen[Receipt] = for {
def receiptGen(): Gen[Receipt] =
Gen.oneOf(legacyReceiptGen(), type01ReceiptGen())

def legacyReceiptGen(): Gen[LegacyReceipt] = for {
postTransactionStateHash <- byteArrayOfNItemsGen(32)
cumulativeGasUsed <- bigIntGen
logsBloomFilter <- byteArrayOfNItemsGen(256)
} yield Receipt.withHashOutcome(
} yield LegacyReceipt.withHashOutcome(
postTransactionStateHash = ByteString(postTransactionStateHash),
cumulativeGasUsed = cumulativeGasUsed,
logsBloomFilter = ByteString(logsBloomFilter),
logs = Seq()
)

def type01ReceiptGen(): Gen[Type01Receipt] = legacyReceiptGen().map(Type01Receipt(_))

def addressGen: Gen[Address] = byteArrayOfNItemsGen(20).map(Address(_))

def accessListItemGen(): Gen[AccessListItem] = for {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,28 +179,28 @@ class StdBlockValidatorSpec extends AnyFlatSpec with Matchers with SecureRandomB
.header

val validReceipts: Seq[Receipt] = Seq(
Receipt.withHashOutcome(
LegacyReceipt.withHashOutcome(
postTransactionStateHash =
ByteString(Hex.decode("ce0ac687bb90d457b6573d74e4a25ea7c012fee329eb386dbef161c847f9842d")),
cumulativeGasUsed = 21000,
logsBloomFilter = ByteString(Hex.decode("0" * 512)),
logs = Seq[TxLogEntry]()
),
Receipt.withHashOutcome(
LegacyReceipt.withHashOutcome(
postTransactionStateHash =
ByteString(Hex.decode("b927d361126302acaa1fa5e93d0b7e349e278231fe2fc2846bfd54f50377f20a")),
cumulativeGasUsed = 42000,
logsBloomFilter = ByteString(Hex.decode("0" * 512)),
logs = Seq[TxLogEntry]()
),
Receipt.withHashOutcome(
LegacyReceipt.withHashOutcome(
postTransactionStateHash =
ByteString(Hex.decode("1e913d6bdd412d71292173d7908f8792adcf958b84c89575bc871a1decaee56d")),
cumulativeGasUsed = 63000,
logsBloomFilter = ByteString(Hex.decode("0" * 512)),
logs = Seq[TxLogEntry]()
),
Receipt.withHashOutcome(
LegacyReceipt.withHashOutcome(
postTransactionStateHash =
ByteString(Hex.decode("0c6e052bc83482bafaccffc4217adad49f3a9533c69c820966d75ed0154091e6")),
cumulativeGasUsed = 84000,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ class EthTxServiceSpec

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

val fakeReceipt: Receipt = Receipt.withHashOutcome(
val fakeReceipt: LegacyReceipt = LegacyReceipt.withHashOutcome(
postTransactionStateHash = ByteString(Hex.decode("01" * 32)),
cumulativeGasUsed = 43,
logsBloomFilter = ByteString(Hex.decode("00" * 256)),
Expand Down
10 changes: 5 additions & 5 deletions src/test/scala/io/iohk/ethereum/jsonrpc/FilterManagerSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ class FilterManagerSpec
.returning(
Some(
Seq(
Receipt.withHashOutcome(
LegacyReceipt.withHashOutcome(
postTransactionStateHash = ByteString(),
cumulativeGasUsed = 0,
logsBloomFilter = BloomFilter.create(logs2),
Expand Down Expand Up @@ -191,13 +191,13 @@ class FilterManagerSpec
.returning(
Some(
Seq(
Receipt.withHashOutcome(
LegacyReceipt.withHashOutcome(
postTransactionStateHash = ByteString(),
cumulativeGasUsed = 0,
logsBloomFilter = BloomFilter.create(Seq(log4_1)),
logs = Seq(log4_1)
),
Receipt.withHashOutcome(
LegacyReceipt.withHashOutcome(
postTransactionStateHash = ByteString(),
cumulativeGasUsed = 0,
logsBloomFilter = BloomFilter.create(Seq(log4_2)),
Expand Down Expand Up @@ -266,7 +266,7 @@ class FilterManagerSpec
.returning(
Some(
Seq(
Receipt.withHashOutcome(
LegacyReceipt.withHashOutcome(
postTransactionStateHash = ByteString(),
cumulativeGasUsed = 0,
logsBloomFilter = BloomFilter.create(logs),
Expand Down Expand Up @@ -305,7 +305,7 @@ class FilterManagerSpec
PendingBlock(
block2,
Seq(
Receipt.withHashOutcome(
LegacyReceipt.withHashOutcome(
postTransactionStateHash = ByteString(),
cumulativeGasUsed = 0,
logsBloomFilter = BloomFilter.create(logs2),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ class BlockExecutionSpec extends AnyWordSpec with Matchers with ScalaCheckProper

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

// Check valid receipts
resultingReceipts.size shouldBe 1
val Receipt(rootHashReceipt, gasUsedReceipt, logsBloomFilterReceipt, logsReceipt) = resultingReceipts.head
val LegacyReceipt(rootHashReceipt, gasUsedReceipt, logsBloomFilterReceipt, logsReceipt) =
resultingReceipts.head
rootHashReceipt shouldBe HashOutcome(expectedStateRoot)
gasUsedReceipt shouldBe resultingGasUsed
logsBloomFilterReceipt shouldBe BloomFilter.create(logs)
Expand Down Expand Up @@ -607,7 +608,7 @@ class BlockExecutionSpec extends AnyWordSpec with Matchers with ScalaCheckProper
)
val expectedStateRootTx1 = applyChanges(validBlockParentHeader.stateRoot, changesTx1)

val Receipt(rootHashReceipt1, gasUsedReceipt1, logsBloomFilterReceipt1, logsReceipt1) = receipt1
val LegacyReceipt(rootHashReceipt1, gasUsedReceipt1, logsBloomFilterReceipt1, logsReceipt1) = receipt1
rootHashReceipt1 shouldBe HashOutcome(expectedStateRootTx1)
gasUsedReceipt1 shouldBe stx1.tx.tx.gasLimit
logsBloomFilterReceipt1 shouldBe BloomFilter.create(Nil)
Expand All @@ -622,7 +623,7 @@ class BlockExecutionSpec extends AnyWordSpec with Matchers with ScalaCheckProper
)
val expectedStateRootTx2 = applyChanges(expectedStateRootTx1, changesTx2)

val Receipt(rootHashReceipt2, gasUsedReceipt2, logsBloomFilterReceipt2, logsReceipt2) = receipt2
val LegacyReceipt(rootHashReceipt2, gasUsedReceipt2, logsBloomFilterReceipt2, logsReceipt2) = receipt2
rootHashReceipt2 shouldBe HashOutcome(expectedStateRootTx2)
gasUsedReceipt2 shouldBe (transaction1.gasLimit + transaction2.gasLimit)
logsBloomFilterReceipt2 shouldBe BloomFilter.create(Nil)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ class BlockValidationSpec extends AnyWordSpec with Matchers with MockFactory {
)
)

def mkReceipt(stateHash: String, gas: BigInt): Receipt = Receipt.withHashOutcome(
def mkReceipt(stateHash: String, gas: BigInt): Receipt = LegacyReceipt.withHashOutcome(
postTransactionStateHash = hash2ByteString(stateHash),
cumulativeGasUsed = gas,
logsBloomFilter = bloomFilter,
Expand Down
Loading

0 comments on commit 2a33d1f

Please sign in to comment.