Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 84 additions & 44 deletions node/types/blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,77 +120,117 @@ func DecodeTxsFromBytes(txsBytes []byte) (eth.Transactions, error) {
txs := make(eth.Transactions, 0)
for {
var (
firstByte byte
fullTxBytes []byte
innerTx eth.TxData
err error
typeByte byte
err error
)
if err = binary.Read(reader, binary.BigEndian, &firstByte); err != nil {
// if the blob byte array is completely consumed, then break the loop
if err = binary.Read(reader, binary.BigEndian, &typeByte); err != nil {
if err == io.EOF {
break
}
return nil, err
}
// zero byte is found after valid tx bytes, break the loop
if firstByte == 0 {
if typeByte == 0 {
break
}

switch firstByte {
case eth.AccessListTxType:
if err := binary.Read(reader, binary.BigEndian, &firstByte); err != nil {
switch typeByte {
case eth.AccessListTxType, eth.DynamicFeeTxType, eth.SetCodeTxType:
tx, err := decodeTypedTx(typeByte, reader)
if err != nil {
return nil, err
}
innerTx = new(eth.AccessListTx)
case eth.DynamicFeeTxType:
if err := binary.Read(reader, binary.BigEndian, &firstByte); err != nil {
txs = append(txs, tx)

case eth.MorphTxType:
tx, err := decodeMorphTx(reader)
if err != nil {
return nil, err
}
innerTx = new(eth.DynamicFeeTx)
case eth.SetCodeTxType:
if err := binary.Read(reader, binary.BigEndian, &firstByte); err != nil {
return nil, err
txs = append(txs, tx)

default:
if typeByte <= 0xf7 {
return nil, fmt.Errorf("not supported tx type: %d", typeByte)
}
innerTx = new(eth.SetCodeTx)
case eth.MorphTxType:
if err := binary.Read(reader, binary.BigEndian, &firstByte); err != nil {
fullTxBytes, err := extractInnerTxFullBytes(typeByte, reader)
if err != nil {
return nil, err
}
innerTx = new(eth.MorphTx)
default:
if firstByte <= 0xf7 { // legacy tx first byte must be greater than 0xf7(247)
return nil, fmt.Errorf("not supported tx type: %d", firstByte)
var inner eth.LegacyTx
if err = rlp.DecodeBytes(fullTxBytes, &inner); err != nil {
return nil, err
}
innerTx = new(eth.LegacyTx)
txs = append(txs, eth.NewTx(&inner))
}
}
return txs, nil
}

// we support the tx types of LegacyTxType/AccessListTxType/DynamicFeeTxType
//if firstByte == eth.AccessListTxType || firstByte == eth.DynamicFeeTxType {
// // the firstByte here is used to indicate tx type, so skip it
// if err := binary.Read(reader, binary.BigEndian, &firstByte); err != nil {
// return nil, err
// }
//} else if firstByte <= 0xf7 { // legacy tx first byte must be greater than 0xf7(247)
// return nil, fmt.Errorf("not supported tx type: %d", firstByte)
//}
fullTxBytes, err = extractInnerTxFullBytes(firstByte, reader)
if err != nil {
return nil, err
}
if err = rlp.DecodeBytes(fullTxBytes, innerTx); err != nil {
// decodeTypedTx decodes a standard EIP-2718 typed tx (AccessList, DynamicFee, SetCode)
// from the reader. The type byte has already been consumed; the next byte is the RLP prefix.
func decodeTypedTx(typeByte byte, reader io.Reader) (*eth.Transaction, error) {
var rlpPrefix byte
if err := binary.Read(reader, binary.BigEndian, &rlpPrefix); err != nil {
return nil, err
}
rlpBytes, err := extractInnerTxFullBytes(rlpPrefix, reader)
if err != nil {
return nil, err
}
txBinary := make([]byte, 0, 1+len(rlpBytes))
txBinary = append(txBinary, typeByte)
txBinary = append(txBinary, rlpBytes...)

var tx eth.Transaction
if err := tx.UnmarshalBinary(txBinary); err != nil {
return nil, err
}
return &tx, nil
}

// decodeMorphTx decodes a MorphTx from the reader. The type byte (0x7f) has already
// been consumed. MorphTx has two wire formats:
// - V0: type(0x7f) || RLP(fields) — next byte is RLP prefix (>= 0xC0)
// - V1: type(0x7f) || version(0x01) || RLP(fields) — next byte is version, then RLP prefix
func decodeMorphTx(reader io.Reader) (*eth.Transaction, error) {
var nextByte byte
if err := binary.Read(reader, binary.BigEndian, &nextByte); err != nil {
return nil, err
}

var versionPrefix []byte
rlpFirstByte := nextByte
if nextByte != 0 && nextByte < 0xC0 {
// V1+: nextByte is the version byte, read the actual RLP prefix
versionPrefix = []byte{nextByte}
if err := binary.Read(reader, binary.BigEndian, &rlpFirstByte); err != nil {
return nil, err
}
txs = append(txs, eth.NewTx(innerTx))
}
return txs, nil

rlpBytes, err := extractInnerTxFullBytes(rlpFirstByte, reader)
if err != nil {
return nil, err
}

txBinary := make([]byte, 0, 1+len(versionPrefix)+len(rlpBytes))
txBinary = append(txBinary, eth.MorphTxType)
txBinary = append(txBinary, versionPrefix...)
txBinary = append(txBinary, rlpBytes...)

var tx eth.Transaction
if err := tx.UnmarshalBinary(txBinary); err != nil {
return nil, err
}
return &tx, nil
}

func extractInnerTxFullBytes(firstByte byte, reader io.Reader) ([]byte, error) {
//the occupied byte length for storing the size of the following rlp encoded bytes
sizeByteLen := firstByte - 0xf7
if sizeByteLen > 4 {
return nil, fmt.Errorf("invalid RLP size byte length: %d (firstByte=0x%x)", sizeByteLen, firstByte)
}

// the size of the following rlp encoded bytes
sizeByte := make([]byte, sizeByteLen)
if err := binary.Read(reader, binary.BigEndian, sizeByte); err != nil {
return nil, err
Expand Down
49 changes: 49 additions & 0 deletions node/types/blob_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,52 @@ func TestDecodeTxsFromBytes(t *testing.T) {
require.EqualValues(t, legacyContractTx.Hash(), txs[1].Hash())
require.EqualValues(t, contractTx.Hash(), txs[2].Hash())
}

func TestDecodeTxsFromBytes_MorphTxV0(t *testing.T) {
morphV0Bytes := common.FromHex("0x7ff8b7820b02820374835a527f8378d6ff830186a094cfb1186f4e93d60e60a8bdd997427d1f33bc372b80b844a9059cbb000000000000000000000000b055051fb2889be5e9831524f1624941299c49bb0000000000000000000000000000000000000000000000000000000000000064c0068398968080a0953c962c4a4583dadc0ff338166d9f1176a6403a3689b7edcedf583ca401c4cba06ab5c3ef27fa3a8966ae61d994cb658c0d809434ed33dc35f90f9300fcc000c8")
txs, err := DecodeTxsFromBytes(morphV0Bytes)
require.NoError(t, err)
require.EqualValues(t, 1, txs.Len())
require.EqualValues(t, eth.MorphTxType, txs[0].Type())
}

func TestDecodeTxsFromBytes_MorphTxV1(t *testing.T) {
morphV1Bytes := common.FromHex("0x7f01f8a1820b5e808326ff9b8345841b8252089425db2115628f08d952e4aacf06b341c8bc04a7f28080c00480a0deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef936d6f72706820686f6f6469207465737420747801a07c085d4f1dafac10ee14bf91dc549c6fb0b7aad797569881aa78f4461eb92903a070038e7e5551422168f4745b90854affd790c0f3a0886a926e53127ea3d0be65")
txs, err := DecodeTxsFromBytes(morphV1Bytes)
require.NoError(t, err)
require.EqualValues(t, 1, txs.Len())
require.EqualValues(t, eth.MorphTxType, txs[0].Type())
}

func TestDecodeTxsFromBytes_MixedWithMorphTx(t *testing.T) {
morphV0Bytes := common.FromHex("7ff8b7820b02820374835a527f8378d6ff830186a094cfb1186f4e93d60e60a8bdd997427d1f33bc372b80b844a9059cbb000000000000000000000000b055051fb2889be5e9831524f1624941299c49bb0000000000000000000000000000000000000000000000000000000000000064c0068398968080a0953c962c4a4583dadc0ff338166d9f1176a6403a3689b7edcedf583ca401c4cba06ab5c3ef27fa3a8966ae61d994cb658c0d809434ed33dc35f90f9300fcc000c8")
morphV1Bytes := common.FromHex("7f01f8a1820b5e808326ff9b8345841b8252089425db2115628f08d952e4aacf06b341c8bc04a7f28080c00480a0deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef936d6f72706820686f6f6469207465737420747801a07c085d4f1dafac10ee14bf91dc549c6fb0b7aad797569881aa78f4461eb92903a070038e7e5551422168f4745b90854affd790c0f3a0886a926e53127ea3d0be65")

// Generate other tx types
transferTx, err := generateTransferTx(false)
require.NoError(t, err)
transferTxBz, err := transferTx.MarshalBinary()
require.NoError(t, err)

legacyTx, err := generateContractTx(true)
require.NoError(t, err)
legacyTxBz, err := legacyTx.MarshalBinary()
require.NoError(t, err)

// Concatenate: DynamicFee + MorphV0 + Legacy + MorphV1
var txsBytes []byte
txsBytes = append(txsBytes, transferTxBz...)
txsBytes = append(txsBytes, morphV0Bytes...)
txsBytes = append(txsBytes, legacyTxBz...)
txsBytes = append(txsBytes, morphV1Bytes...)

txs, err := DecodeTxsFromBytes(txsBytes)
require.NoError(t, err)
require.EqualValues(t, 4, txs.Len())
require.EqualValues(t, eth.DynamicFeeTxType, txs[0].Type())
require.EqualValues(t, transferTx.Hash(), txs[0].Hash())
require.EqualValues(t, eth.MorphTxType, txs[1].Type())
require.EqualValues(t, eth.LegacyTxType, txs[2].Type())
require.EqualValues(t, legacyTx.Hash(), txs[2].Hash())
require.EqualValues(t, eth.MorphTxType, txs[3].Type())
}
Loading