Skip to content
This repository has been archived by the owner on Sep 23, 2023. It is now read-only.

Commit

Permalink
make ParseTransaction capable of handling wrapped & unwrapped blob txs
Browse files Browse the repository at this point in the history
  • Loading branch information
roberto-bayardo committed Feb 10, 2023
1 parent 02918a3 commit 680b056
Show file tree
Hide file tree
Showing 8 changed files with 34 additions and 32 deletions.
8 changes: 4 additions & 4 deletions txpool/fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -467,10 +467,8 @@ func (f *Fetch) handleStateChanges(ctx context.Context, client StateChangesClien
minedTxs.Resize(uint(len(change.Txs)))
for i := range change.Txs {
minedTxs.Txs[i] = &types2.TxSlot{}
// TODO(eip-4844): Blob transactions here will be "unwrapped" variety, so parsing will fail.
// Ultimately we need to persist the wrapped versions somehow if we would like to replay them.
if err = f.threadSafeParseStateChangeTxn(func(parseContext *types2.TxParseContext) error {
_, err := parseContext.ParseTransaction(change.Txs[i], 0, minedTxs.Txs[i], minedTxs.Senders.At(i), false /* hasEnvelope */, nil)
_, err := parseContext.ParseTransaction(change.Txs[i], 0, minedTxs.Txs[i], minedTxs.Senders.At(i), false /* hasEnvelope */, false /* networkVersion */, nil)
return err
}); err != nil {
log.Warn("stream.Recv", "err", err)
Expand All @@ -483,7 +481,7 @@ func (f *Fetch) handleStateChanges(ctx context.Context, client StateChangesClien
for i := range change.Txs {
unwindTxs.Txs[i] = &types2.TxSlot{}
if err = f.threadSafeParseStateChangeTxn(func(parseContext *types2.TxParseContext) error {
_, err = parseContext.ParseTransaction(change.Txs[i], 0, unwindTxs.Txs[i], unwindTxs.Senders.At(i), false /* hasEnvelope */, nil)
_, err = parseContext.ParseTransaction(change.Txs[i], 0, unwindTxs.Txs[i], unwindTxs.Senders.At(i), false /* hasEnvelope */, false /* networkVersion */, nil)
return err
}); err != nil {
log.Warn("stream.Recv", "err", err)
Expand All @@ -492,6 +490,8 @@ func (f *Fetch) handleStateChanges(ctx context.Context, client StateChangesClien
}
}
}
// TODO(eip-4844): If there are blob txs that need to be unwound, these will not replay properly since we only have the
// unwrapped version here (we would need to re-wrap the tx with its blobs & kzg commitments).
if err := f.db.View(ctx, func(tx kv.Tx) error {
return f.pool.OnNewBlock(ctx, req, unwindTxs, minedTxs, tx)
}); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion txpool/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -1720,7 +1720,7 @@ func (p *TxPool) fromDB(ctx context.Context, tx kv.Tx, coreTx kv.Tx) error {
addr, txRlp := v[:20], v[20:]
txn := &types.TxSlot{}

_, err = parseCtx.ParseTransaction(txRlp, 0, txn, nil, false /* hasEnvelope */, nil)
_, err = parseCtx.ParseTransaction(txRlp, 0, txn, nil, false /* hasEnvelope */, true /* networkVersion */, nil)
if err != nil {
err = fmt.Errorf("err: %w, rlp: %x", err, txRlp)
log.Warn("[txpool] fromDB: parseTransaction", "err", err)
Expand Down
2 changes: 1 addition & 1 deletion txpool/pool_fuzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ func poolsFromFuzzBytes(rawTxNonce, rawValues, rawTips, rawFeeCap, rawSender []b
FeeCap: *uint256.NewInt(feeCap[i%len(feeCap)]),
}
txRlp := fakeRlpTx(txs.Txs[i], senders.At(i%senders.Len()))
_, err := parseCtx.ParseTransaction(txRlp, 0, txs.Txs[i], nil, false, nil)
_, err := parseCtx.ParseTransaction(txRlp, 0, txs.Txs[i], nil, false, true, nil)
if err != nil {
panic(err)
}
Expand Down
2 changes: 1 addition & 1 deletion txpool/txpool_grpc_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ func (s *GrpcServer) Add(ctx context.Context, in *txpool_proto.AddRequest) (*txp
slots.Resize(uint(j + 1))
slots.Txs[j] = &types.TxSlot{}
slots.IsLocal[j] = true
if _, err := parseCtx.ParseTransaction(in.RlpTxs[i], 0, slots.Txs[j], slots.Senders.At(j), false /* hasEnvelope */, func(hash []byte) error {
if _, err := parseCtx.ParseTransaction(in.RlpTxs[i], 0, slots.Txs[j], slots.Senders.At(j), false /* hasEnvelope */, true /* networkVersion */, func(hash []byte) error {
if known, _ := s.txPool.IdHashKnown(tx, hash); known {
return types.ErrAlreadyKnown
}
Expand Down
26 changes: 14 additions & 12 deletions types/txn.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ func (ctx *TxParseContext) ChainIDRequired() *TxParseContext {

// ParseTransaction extracts all the information from the transactions's payload (RLP) necessary to build TxSlot
// it also performs syntactic validation of the transactions
func (ctx *TxParseContext) ParseTransaction(payload []byte, pos int, slot *TxSlot, sender []byte, hasEnvelope bool, validateHash func([]byte) error) (p int, err error) {
func (ctx *TxParseContext) ParseTransaction(payload []byte, pos int, slot *TxSlot, sender []byte, hasEnvelope bool, networkVersion bool, validateHash func([]byte) error) (p int, err error) {
if len(payload) == 0 {
return 0, fmt.Errorf("%w: empty rlp", ErrParseTxn)
}
Expand Down Expand Up @@ -183,7 +183,7 @@ func (ctx *TxParseContext) ParseTransaction(payload []byte, pos int, slot *TxSlo
// 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, validateHash)
return len(payload), ctx.ParseBlobTransaction(payload[p:], slot, sender, networkVersion, validateHash)
}
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 @@ -475,7 +475,7 @@ func (ctx *TxParseContext) ParseTransaction(payload []byte, pos int, slot *TxSlo
return p, nil
}

func (ctx *TxParseContext) ParseBlobTransaction(payload []byte, slot *TxSlot, sender []byte, validateHash func([]byte) error) error {
func (ctx *TxParseContext) ParseBlobTransaction(payload []byte, slot *TxSlot, sender []byte, networkVersion bool, validateHash func([]byte) error) error {
slot.Rlp = payload // includes type byte
slot.Size = uint32(len(payload))
ctx.Keccak1.Write(payload)
Expand All @@ -485,12 +485,19 @@ func (ctx *TxParseContext) ParseBlobTransaction(payload []byte, slot *TxSlot, se
return err
}
}

payload = payload[1:]
// payload should now include the SSZ encoded tx and nothing more
tx := wrapper{}
if err := tx.Deserialize(payload); err != nil {
return fmt.Errorf("%w: deserializing blob tx ssz: %s", ErrParseTxn, err)
if networkVersion {
if err := tx.Deserialize(payload); err != nil {
return fmt.Errorf("%w: deserializing blob tx wrapper ssz: %s", ErrParseTxn, err)
}
slot.Blobs = tx.numBlobHashes
if err := tx.VerifyBlobs(payload); err != nil {
return fmt.Errorf("%w: blob verification failed: %s", ErrParseTxn, err)
}
} else if err := tx.DeserializeTx(payload, 0, len(payload)); err != nil {
return fmt.Errorf("%w: deserializing signed blob tx ssz: %s", ErrParseTxn, err)
}
slot.Nonce = tx.nonce
slot.Tip = tx.maxPriorityFeePerGas
Expand All @@ -502,19 +509,14 @@ func (ctx *TxParseContext) ParseBlobTransaction(payload []byte, slot *TxSlot, se
slot.DataNonZeroLen = tx.dataNonZeroLen
slot.AlAddrCount = tx.accessListAddressCount
slot.AlStorCount = tx.accessListKeyCount
slot.Blobs = tx.numBlobHashes

err := tx.VerifyBlobs(payload)
if err != nil {
return fmt.Errorf("%w: blob verification failed: %s", ErrParseTxn, err)
}

if !ctx.withSender {
return nil
}

// Verify the tx signature
vByte := payload[tx.sigOffset]
var err error
ctx.R, err = readUint256(payload, tx.sigOffset+1)
if err != nil {
return fmt.Errorf("%w: failed to extract sig.R: %s", ErrParseTxn, err)
Expand Down
4 changes: 2 additions & 2 deletions types/txn_packets.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ func ParseTransactions(payload []byte, pos int, ctx *TxParseContext, txSlots *Tx
for i := 0; pos < len(payload); i++ {
txSlots.Resize(uint(i + 1))
txSlots.Txs[i] = &TxSlot{}
pos, err = ctx.ParseTransaction(payload, pos, txSlots.Txs[i], txSlots.Senders.At(i), true /* hasEnvelope */, validateHash)
pos, err = ctx.ParseTransaction(payload, pos, txSlots.Txs[i], txSlots.Senders.At(i), true /* hasEnvelope */, true /* network version */, validateHash)
if err != nil {
if errors.Is(err, ErrRejected) {
txSlots.Resize(uint(i))
Expand Down Expand Up @@ -206,7 +206,7 @@ func ParsePooledTransactions66(payload []byte, pos int, ctx *TxParseContext, txS
for i := 0; p < len(payload); i++ {
txSlots.Resize(uint(i + 1))
txSlots.Txs[i] = &TxSlot{}
p, err = ctx.ParseTransaction(payload, p, txSlots.Txs[i], txSlots.Senders.At(i), true /* hasEnvelope */, validateHash)
p, err = ctx.ParseTransaction(payload, p, txSlots.Txs[i], txSlots.Senders.At(i), true /* hasEnvelope */, true /* networkVersion */, validateHash)
if err != nil {
if errors.Is(err, ErrRejected) {
txSlots.Resize(uint(i))
Expand Down
20 changes: 10 additions & 10 deletions types/txn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,15 @@ func TestParseBlobTransaction(t *testing.T) {

ssz := hexutility.MustDecodeHex(strings.TrimSpace(blobTxHex))
payload := append([]byte{byte(BlobTxType)}, ssz...)
parseEnd, err := ctx.ParseTransaction(payload, 0, tx, txSender[:], false /* hasEnvelope */, nil)
parseEnd, err := ctx.ParseTransaction(payload, 0, tx, txSender[:], false /* hasEnvelope */, true /* networkVersion */, nil)
require.NoError(err)
require.Equal(len(payload), parseEnd)

// Same test only with an rlp tx envelope.
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[:], true /* hasEnvelope */, nil)
parseEnd, err = ctx.ParseTransaction(payload, 0, tx, txSender[:], true /* hasEnvelope */, true /* networkVersion */, nil)

if tx.Nonce != 10 {
t.Errorf("Expected nonce == 10, got: %v", tx.Nonce)
Expand Down Expand Up @@ -79,7 +79,7 @@ func TestParseBlobTransaction(t *testing.T) {
// One more test with the no-blob tx & known sender
ssz = hexutility.MustDecodeHex(blobTxCreateHex)
payload = append([]byte{byte(BlobTxType)}, ssz...)
parseEnd, err = ctx.ParseTransaction(payload, 0, tx, txSender[:], false /* hasEnvelope */, nil)
parseEnd, err = ctx.ParseTransaction(payload, 0, tx, txSender[:], false /* hasEnvelope */, true /* networkVersion */, nil)
require.NoError(err)
require.Equal(len(payload), parseEnd)
sender := fmt.Sprintf("%x", txSender)
Expand All @@ -99,7 +99,7 @@ func TestParseTransactionRLP(t *testing.T) {
tt := tt
t.Run(strconv.Itoa(i), func(t *testing.T) {
payload := hexutility.MustDecodeHex(tt.PayloadStr)
parseEnd, err := ctx.ParseTransaction(payload, 0, tx, txSender[:], false /* hasEnvelope */, nil)
parseEnd, err := ctx.ParseTransaction(payload, 0, tx, txSender[:], false /* hasEnvelope */, true /* networkVersion */, nil)
require.NoError(err)
require.Equal(len(payload), parseEnd)
if tt.SignHashStr != "" {
Expand Down Expand Up @@ -134,19 +134,19 @@ func TestTransactionSignatureValidity1(t *testing.T) {

tx, txSender := &TxSlot{}, [20]byte{}
validTxn := hexutility.MustDecodeHex("f83f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870b801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a3664935301")
_, err := ctx.ParseTransaction(validTxn, 0, tx, txSender[:], false /* hasEnvelope */, nil)
_, err := ctx.ParseTransaction(validTxn, 0, tx, txSender[:], false /* hasEnvelope */, true /* networkVersion */, nil)
assert.NoError(t, err)

preEip2Txn := hexutility.MustDecodeHex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870b801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a07fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1")
_, err = ctx.ParseTransaction(preEip2Txn, 0, tx, txSender[:], false /* hasEnvelope */, nil)
_, err = ctx.ParseTransaction(preEip2Txn, 0, tx, txSender[:], false /* hasEnvelope */, true /* networkVersion */, nil)
assert.NoError(t, err)

// Now enforce EIP-2
ctx.WithAllowPreEip2s(false)
_, err = ctx.ParseTransaction(validTxn, 0, tx, txSender[:], false /* hasEnvelope */, nil)
_, err = ctx.ParseTransaction(validTxn, 0, tx, txSender[:], false /* hasEnvelope */, true /*networkVersion */, nil)
assert.NoError(t, err)

_, err = ctx.ParseTransaction(preEip2Txn, 0, tx, txSender[:], false /* hasEnvelope */, nil)
_, err = ctx.ParseTransaction(preEip2Txn, 0, tx, txSender[:], false /* hasEnvelope */, true /*networkVersion */, nil)
assert.Error(t, err)
}

Expand All @@ -156,12 +156,12 @@ func TestTransactionSignatureValidity2(t *testing.T) {
ctx := NewTxParseContext(*chainId)
slot, sender := &TxSlot{}, [20]byte{}
rlp := hexutility.MustDecodeHex("02f8720513844190ab00848321560082520894cab441d2f45a3fee83d15c6b6b6c36a139f55b6288054607fc96a6000080c001a0dffe4cb5651e663d0eac8c4d002de734dd24db0f1109b062d17da290a133cc02a0913fb9f53f7a792bcd9e4d7cced1b8545d1ab82c77432b0bc2e9384ba6c250c5")
_, err := ctx.ParseTransaction(rlp, 0, slot, sender[:], false /* hasEnvelope */, nil)
_, err := ctx.ParseTransaction(rlp, 0, slot, sender[:], false /* hasEnvelope */, true /* networkVersion */, nil)
assert.Error(t, err)

// Only legacy transactions can happen before EIP-2
ctx.WithAllowPreEip2s(true)
_, err = ctx.ParseTransaction(rlp, 0, slot, sender[:], false /* hasEnvelope */, nil)
_, err = ctx.ParseTransaction(rlp, 0, slot, sender[:], false /* hasEnvelope */, true /* networkVersion */, nil)
assert.Error(t, err)
}

Expand Down
2 changes: 1 addition & 1 deletion types/txn_types_fuzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ func FuzzParseTx(f *testing.F) {
ctx := NewTxParseContext(*u256.N1)
txn := &TxSlot{}
sender := make([]byte, 20)
_, _ = ctx.ParseTransaction(in, pos, txn, sender, false, nil)
_, _ = ctx.ParseTransaction(in, pos, txn, sender, false, true, nil)
})
}

0 comments on commit 680b056

Please sign in to comment.