Skip to content

Commit

Permalink
start integrating blob tx parser into txpool (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
roberto-bayardo committed Jan 20, 2023
1 parent 94c7efc commit 31d6171
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 11 deletions.
6 changes: 6 additions & 0 deletions types/blob_txn.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ type BlobTx struct {
Creation bool // true if To field is nil, indicating contract creation
Value uint256.Int
DataLen int // length of the Data in bytes
DataNonZeroLen int
AccessListAddressCount int // number of addresses in access list
AccessListKeyCount int // number of storage keys in access list

Expand Down Expand Up @@ -155,6 +156,11 @@ func (tx *BlobTx) Deserialize(dr *codec.DecodingReader) error {
}
tx.Value = uint256.Int(value)
tx.DataLen = len(data)
for _, byt := range data {
if byt != 0 {
tx.DataNonZeroLen++
}
}
tx.BlobVersionedHashes = [][32]byte(blobVersionedHashes)
tx.AccessListAddressCount = accessList.addresses
tx.AccessListKeyCount = accessList.keys
Expand Down
19 changes: 8 additions & 11 deletions types/blob_txn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ package types
import (
"bytes"
_ "embed"
"encoding/hex"
"strings"
"testing"

"github.com/protolambda/ztyp/codec"

"github.com/ledgerwatch/erigon-lib/common"
)

var (
Expand Down Expand Up @@ -51,24 +52,20 @@ var (

signedBlobTxHex = "45000000010d684b1a6596dfee36d52d5e04f38765dc489270c9f90ee00347422f6ff295e96896a0646b32d9b6a33dd5b537baf4263a34aa619c1ec661eb1296d4de6d831301000000000000000000000000000000000000000000000000000000000000000a000000000000002a000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000041e2010000000000c00000006400000000000000000000000000000000000000000000000000000000000000d5000000db00000000000000000000000000000000000000000000000000000000000000000000007301000001095e7baea6a6c7c4c2dfeb977efac326af552d876162636465660800000060000000000000000000000000000000000000000000000118000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002180000000200000000000000000000000000000000000000000000000000000000000000010657f37554c781402a22917dee2f75def7ab966d7b770905398eba3c44401400000000000000000000000000000000000000000000000000000000deadbeef"

// Same tx as above only with nil To field to test contract creation indicator
// Same tx as above only with nil To field to test contract creation indicator, and no signature
signedBlobTxNoRecipientHex = "45000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000a000000000000002a000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000041e2010000000000c00000006400000000000000000000000000000000000000000000000000000000000000c1000000c700000000000000000000000000000000000000000000000000000000000000000000005f010000006162636465660800000060000000000000000000000000000000000000000000000118000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002180000000200000000000000000000000000000000000000000000000000000000000000010657f37554c781402a22917dee2f75def7ab966d7b770905398eba3c44401400000000000000000000000000000000000000000000000000000000deadbeef"

// blobTxNetworkWrapperHex is an ssz encoded BlobTxNetworkWrapper with 2 valid blobs & a valid
// aggregated kzg proof
// blobTxNetworkWrapperHex is an ssz encoded BlobTxNetworkWrapper with 2 valid blobs +
// versioned hashes, a valid aggregated kzg proof, and all other fields 0.
//go:embed testdata/blobtx.txt
blobTxNetworkWrapperHex string
)

func txFromHex(hexStr string, tx codec.Deserializable) error {
txBytes, err := hex.DecodeString(hexStr)
if err != nil {
return err
}
txBytes := common.MustDecodeHex(hexStr)
buf := bytes.NewReader(txBytes)
dr := codec.NewDecodingReader(buf, uint64(len(txBytes)))
err = tx.Deserialize(dr)
if err != nil {
if err := tx.Deserialize(dr); err != nil {
return err
}
return nil
Expand Down Expand Up @@ -138,7 +135,7 @@ func TestParseBlobTxNetworkWrapper(t *testing.T) {
t.Fatalf("couldn't create test case: %v", err)
}
l1, l2, l3 := len(tx.BlobKZGs), len(tx.Blobs), len(tx.Tx.Message.BlobVersionedHashes)
if l1 != 2 || l2 != 2 || l3 = l3 {
if l1 != 2 || l2 != 2 || l3 != 2 {
t.Errorf("Expected 2 each of kzgs / blobs / hashes, got: %v %v %v", l1, l2, l3)
}
}
39 changes: 39 additions & 0 deletions types/txn.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (

"github.com/holiman/uint256"
"github.com/ledgerwatch/secp256k1"
"github.com/protolambda/ztyp/codec"
"golang.org/x/crypto/sha3"

"github.com/ledgerwatch/erigon-lib/common/length"
Expand Down Expand Up @@ -103,6 +104,7 @@ const (
AccessListTxType int = 1
DynamicFeeTxType int = 2
StarknetTxType int = 3
BlobTxType int = 5
)

var ErrParseTxn = fmt.Errorf("%w transaction", rlp.ErrParse)
Expand Down Expand Up @@ -153,6 +155,13 @@ func (ctx *TxParseContext) ParseTransaction(payload []byte, pos int, slot *TxSlo
// If it is non-legacy transaction, the transaction type follows, and then the the list
if !legacy {
txType = int(payload[p])
if txType == BlobTxType {
// TODO: Parsing the blob transaction requires we know the "scope" of the encoding
// since it is SSZ format. We assume the scope ends at the last byte of the payload
// argument; this currently seems to always be the case, but does not seem to be
// mandated by this function's contract.
return len(payload), ctx.ParseBlobTransaction(payload[p:], slot, sender)
}
if _, err = ctx.Keccak1.Write(payload[p : p+1]); err != nil {
return 0, fmt.Errorf("%w: computing IdHash (hashing type Prefix): %s", ErrParseTxn, err)
}
Expand Down Expand Up @@ -471,6 +480,36 @@ func (ctx *TxParseContext) ParseTransaction(payload []byte, pos int, slot *TxSlo
return p, nil
}

func (ctx *TxParseContext) ParseBlobTransaction(payload []byte, slot *TxSlot, sender []byte) error {
slot.Rlp = payload // includes type byte
ctx.Keccak1.Write(payload)
ctx.Keccak1.(io.Reader).Read(slot.IDHash[:32])

payload = payload[1:]
// payload should now include the SSZ encoded tx and nothing more
tx := BlobTxNetworkWrapper{}
buf := bytes.NewReader(payload)
dr := codec.NewDecodingReader(buf, uint64(len(payload)))
if err := tx.Deserialize(dr); err != nil {
return fmt.Errorf("%w: deserializing blob tx ssz: %s", ErrParseTxn, err)
}
m := tx.Tx.Message
slot.Nonce = m.Nonce
slot.Tip = m.MaxPriorityFeePerGas
slot.FeeCap = m.MaxFeePerGas
slot.Gas = m.Gas
slot.Creation = m.Creation
slot.Value = m.Value
slot.DataLen = m.DataLen
slot.DataNonZeroLen = m.DataNonZeroLen
slot.AlAddrCount = m.AccessListAddressCount
slot.AlStorCount = m.AccessListKeyCount

// TODO: extract sender, validate blobs.

return nil
}

type PeerID *types.H512

type Hashes []byte // flatten list of 32-byte hashes
Expand Down
23 changes: 23 additions & 0 deletions types/txn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,38 @@ package types
import (
"bytes"
"strconv"
"strings"
"testing"

"github.com/holiman/uint256"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/common/hexutility"
"github.com/ledgerwatch/erigon-lib/rlp"
)

func TestParseBlobTransaction(t *testing.T) {
require := require.New(t)
ctx := NewTxParseContext(*new(uint256.Int).SetUint64(1))
tx, txSender := &TxSlot{}, [20]byte{}

// Prepare the payload, which is an RLP encoded string consisting of the blob tx type byte
// followed by the ssz encoded transaction data.
ssz := common.MustDecodeHex(strings.TrimSpace(blobTxNetworkWrapperHex))
stringLen := rlp.StringLen(len(ssz) + 1)
payload := make([]byte, stringLen)
rlp.EncodeString(append([]byte{byte(BlobTxType)}, ssz...), payload)
parseEnd, err := ctx.ParseTransaction(payload, 0, tx, txSender[:], false /* hasEnvelope */, nil)

require.NoError(err)
require.Equal(len(payload), parseEnd)

// TODO: test that tx fields are set properly. Need a better input transaction as this example
// has zeros for all fields other than the blobs.
}

func TestParseTransactionRLP(t *testing.T) {
for _, testSet := range allNetsTestCases {
testSet := testSet
Expand Down

0 comments on commit 31d6171

Please sign in to comment.