Skip to content

Commit

Permalink
[ETCM-911] Add rlp serialization basics
Browse files Browse the repository at this point in the history
  • Loading branch information
dzajkowski committed Jul 20, 2021
1 parent f4ee454 commit fcf9874
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 69 deletions.
19 changes: 2 additions & 17 deletions src/main/scala/io/iohk/ethereum/domain/SignedTransaction.scala
Expand Up @@ -50,22 +50,7 @@ object SignedTransaction {
val valueForEmptyS = 0

def apply(
tx: LegacyTransaction,
pointSign: Byte,
signatureRandom: ByteString,
signature: ByteString,
chainId: Byte
): SignedTransaction = {
val txSignature = ECDSASignature(
r = new BigInteger(1, signatureRandom.toArray),
s = new BigInteger(1, signature.toArray),
v = pointSign
)
SignedTransaction(tx, txSignature)
}

def apply(
tx: LegacyTransaction,
tx: Transaction,
pointSign: Byte,
signatureRandom: ByteString,
signature: ByteString
Expand All @@ -79,7 +64,7 @@ object SignedTransaction {
}

def sign(
tx: LegacyTransaction,
tx: Transaction,
keyPair: AsymmetricCipherKeyPair,
chainId: Option[Byte]
): SignedTransaction = {
Expand Down
40 changes: 15 additions & 25 deletions src/main/scala/io/iohk/ethereum/domain/Transaction.scala
Expand Up @@ -22,31 +22,10 @@ sealed trait Transaction extends Product with Serializable {
}

object Transaction {
val Type01: Int = 1
val Type01: Byte = 1.toByte
val LegacyThresholdLowerBound: Int = 0xc0
val LegacyThresholdUpperBound: Int = 0xfe

def typed(
nonce: BigInt,
gasPrice: BigInt,
gasLimit: BigInt,
receivingAddress: Address,
value: BigInt,
payload: ByteString,
accessList: Option[List[AccessListItem]]
): Transaction =
TransactionWithAccessList(nonce, gasPrice, gasLimit, Some(receivingAddress), value, payload, accessList)

def legacy(
nonce: BigInt,
gasPrice: BigInt,
gasLimit: BigInt,
receivingAddress: Address,
value: BigInt,
payload: ByteString
): Transaction =
LegacyTransaction(nonce, gasPrice, gasLimit, Some(receivingAddress), value, payload)

def withGasLimit(gl: BigInt): Transaction => Transaction = {
case tx: LegacyTransaction => tx.copy(gasLimit = gl)
case tx: TransactionWithAccessList => tx.copy(gasLimit = gl)
Expand All @@ -56,7 +35,6 @@ object Transaction {
sealed trait TypedTransaction extends Transaction

object LegacyTransaction {

val NonceLength = 32
val GasLength = 32
val ValueLength = 32
Expand All @@ -70,7 +48,6 @@ object LegacyTransaction {
payload: ByteString
): LegacyTransaction =
LegacyTransaction(nonce, gasPrice, gasLimit, Some(receivingAddress), value, payload)

}

case class LegacyTransaction(
Expand All @@ -92,14 +69,27 @@ case class LegacyTransaction(
s"}"
}

object TransactionWithAccessList {
def apply(
nonce: BigInt,
gasPrice: BigInt,
gasLimit: BigInt,
receivingAddress: Address,
value: BigInt,
payload: ByteString,
accessList: List[AccessListItem]
): TransactionWithAccessList =
TransactionWithAccessList(nonce, gasPrice, gasLimit, Some(receivingAddress), value, payload, accessList)
}

case class TransactionWithAccessList(
nonce: BigInt,
gasPrice: BigInt,
gasLimit: BigInt,
receivingAddress: Option[Address],
value: BigInt,
payload: ByteString,
accessList: Option[List[AccessListItem]]
accessList: List[AccessListItem]
) extends TypedTransaction {
override def toString: String =
s"TransactionWithAccessList {" +
Expand Down
Expand Up @@ -17,6 +17,7 @@ import scodec.Err
import scodec.bits.BitVector
import scodec.bits.ByteVector

import io.iohk.ethereum.domain.AccessListItem
import io.iohk.ethereum.rlp
import io.iohk.ethereum.rlp.RLPCodec
import io.iohk.ethereum.rlp.RLPCodec.Ops
Expand Down Expand Up @@ -77,6 +78,8 @@ trait ContentCodecs {
}
)

implicit val accessListItemCodec: RLPCodec[AccessListItem] = ???

// https://github.com/ethereum/devp2p/blob/master/enr.md#rlp-encoding
// content = [seq, k, v, ...]
implicit val enrContentRLPCodec: RLPCodec[EthereumNodeRecord.Content] = {
Expand Down
Expand Up @@ -12,7 +12,6 @@ import io.iohk.ethereum.rlp.RLPImplicitConversions._
import io.iohk.ethereum.rlp.RLPImplicits._
import io.iohk.ethereum.rlp._
import io.iohk.ethereum.utils.ByteStringUtils.ByteStringOps
import io.iohk.ethereum.utils.Config

object BaseETH6XMessages {
object Status {
Expand Down Expand Up @@ -136,24 +135,41 @@ object BaseETH6XMessages {

object SignedTransactions {

lazy val chainId: Byte = Config.blockchains.blockchainConfig.chainId
lazy val chainId: Byte = 1.toByte //Config.blockchains.blockchainConfig.chainId

implicit class SignedTransactionEnc(val signedTx: SignedTransaction) extends RLPSerializable {
override def toRLPEncodable: RLPEncodeable = {
val receivingAddressBytes = signedTx.tx.receivingAddress
.map(_.toArray)
.getOrElse(Array.emptyByteArray)
RLPList(
signedTx.tx.nonce,
signedTx.tx.gasPrice,
signedTx.tx.gasLimit,
receivingAddressBytes,
signedTx.tx.value,
signedTx.tx.payload,
signedTx.signature.v,
signedTx.signature.r,
signedTx.signature.s
)
signedTx.tx match {
case TransactionWithAccessList(nonce, gasPrice, gasLimit, _, value, payload, _) =>
RLPList(
chainId, // TODO improve how chainid is preserved in transactions
nonce,
gasPrice,
gasLimit,
receivingAddressBytes,
value,
payload,
RLPList(), // TODO write an implicit RLPCodec
signedTx.signature.v,
signedTx.signature.r,
signedTx.signature.s
)
case LegacyTransaction(nonce, gasPrice, gasLimit, _, value, payload) =>
RLPList(
nonce,
gasPrice,
gasLimit,
receivingAddressBytes,
value,
payload,
signedTx.signature.v,
signedTx.signature.r,
signedTx.signature.s
)
}
}
}

Expand All @@ -174,6 +190,34 @@ object BaseETH6XMessages {

implicit class SignedTransactionRlpEncodableDec(val rlpEncodeable: RLPEncodeable) extends AnyVal {
def toSignedTransaction: SignedTransaction = rlpEncodeable match {
case RLPList(
_, // TODO improve how chainid is preserved in transactions
nonce,
gasPrice,
gasLimit,
(receivingAddress: RLPValue),
value,
payload,
(accessList: RLPList),
pointSign,
signatureRandom,
signature
) =>
val receivingAddressOpt = if (receivingAddress.bytes.isEmpty) None else Some(Address(receivingAddress.bytes))
SignedTransaction(
TransactionWithAccessList(
nonce,
gasPrice,
gasLimit,
receivingAddressOpt,
value,
payload,
Nil // TODO write an implicit RLPCodec
),
(pointSign: Int).toByte,
signatureRandom,
signature
)
case RLPList(
nonce,
gasPrice,
Expand All @@ -190,16 +234,22 @@ object BaseETH6XMessages {
LegacyTransaction(nonce, gasPrice, gasLimit, receivingAddressOpt, value, payload),
(pointSign: Int).toByte,
signatureRandom,
signature,
chainId
signature
)
case _ =>
throw new RuntimeException("Cannot decode SignedTransaction")
}
}

implicit class SignedTransactionDec(val bytes: Array[Byte]) extends AnyVal {
def toSignedTransaction: SignedTransaction = rawDecode(bytes).toSignedTransaction
def toSignedTransaction: SignedTransaction = {
val first = bytes(0)
(first match {
case Transaction.Type01 => rawDecode(bytes.tail)
// TODO enforce legacy boundaries
case _ => rawDecode(bytes)
}).toSignedTransaction
}
}
}

Expand Down
22 changes: 11 additions & 11 deletions src/main/scala/io/iohk/ethereum/utils/Picklers.scala
@@ -1,22 +1,22 @@
package io.iohk.ethereum.utils

import akka.util.ByteString

import boopickle.DefaultBasic._
import boopickle.Pickler

import io.iohk.ethereum.crypto.ECDSASignature
import io.iohk.ethereum.domain.{
AccessListItem,
Address,
BlockBody,
BlockHeader,
Checkpoint,
LegacyTransaction,
SignedTransaction,
Transaction,
TransactionWithAccessList
}
import io.iohk.ethereum.domain.AccessListItem
import io.iohk.ethereum.domain.Address
import io.iohk.ethereum.domain.BlockBody
import io.iohk.ethereum.domain.BlockHeader
import io.iohk.ethereum.domain.BlockHeader.HeaderExtraFields
import io.iohk.ethereum.domain.BlockHeader.HeaderExtraFields._
import io.iohk.ethereum.domain.Checkpoint
import io.iohk.ethereum.domain.LegacyTransaction
import io.iohk.ethereum.domain.SignedTransaction
import io.iohk.ethereum.domain.Transaction
import io.iohk.ethereum.domain.TransactionWithAccessList

object Picklers {
implicit val byteStringPickler: Pickler[ByteString] =
Expand Down

0 comments on commit fcf9874

Please sign in to comment.