diff --git a/txpool/fetch.go b/txpool/fetch.go index 252e4bed2..fc5401118 100644 --- a/txpool/fetch.go +++ b/txpool/fetch.go @@ -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) @@ -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) @@ -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 { diff --git a/txpool/pool.go b/txpool/pool.go index 2c922262d..eefffa86f 100644 --- a/txpool/pool.go +++ b/txpool/pool.go @@ -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) diff --git a/txpool/pool_fuzz_test.go b/txpool/pool_fuzz_test.go index d73aa5d7b..b9249934c 100644 --- a/txpool/pool_fuzz_test.go +++ b/txpool/pool_fuzz_test.go @@ -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) } diff --git a/txpool/txpool_grpc_server.go b/txpool/txpool_grpc_server.go index 1481b9b05..de303b350 100644 --- a/txpool/txpool_grpc_server.go +++ b/txpool/txpool_grpc_server.go @@ -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 } diff --git a/types/txn.go b/types/txn.go index f912a8f6e..6adb9739c 100644 --- a/types/txn.go +++ b/types/txn.go @@ -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) } @@ -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) @@ -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) @@ -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 @@ -502,12 +509,6 @@ 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 @@ -515,6 +516,7 @@ func (ctx *TxParseContext) ParseBlobTransaction(payload []byte, slot *TxSlot, se // 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) diff --git a/types/txn_packets.go b/types/txn_packets.go index 2e7a9800a..58be735a9 100644 --- a/types/txn_packets.go +++ b/types/txn_packets.go @@ -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)) @@ -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)) diff --git a/types/txn_test.go b/types/txn_test.go index 6492b9300..d12fda1a0 100644 --- a/types/txn_test.go +++ b/types/txn_test.go @@ -38,7 +38,7 @@ 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) @@ -46,7 +46,7 @@ func TestParseBlobTransaction(t *testing.T) { 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) @@ -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) @@ -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 != "" { @@ -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) } @@ -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) } diff --git a/types/txn_types_fuzz_test.go b/types/txn_types_fuzz_test.go index 5b37ccace..e9fbcfa4f 100644 --- a/types/txn_types_fuzz_test.go +++ b/types/txn_types_fuzz_test.go @@ -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) }) }