From 7af28b5948f8a0595e38c0a15c155c13173c2afa Mon Sep 17 00:00:00 2001 From: inphi Date: Sun, 12 Jun 2022 19:40:02 -0400 Subject: [PATCH 1/3] Use minimal transaction format as block codec Ensure that transactions used to build blocks are encoded using their minimal encoding. --- core/beacon/types.go | 2 +- core/types/transaction.go | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/core/beacon/types.go b/core/beacon/types.go index 84da99bcc0fd2..ecdbd72552dbb 100644 --- a/core/beacon/types.go +++ b/core/beacon/types.go @@ -133,7 +133,7 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) { var txs = make([]*types.Transaction, len(enc)) for i, encTx := range enc { var tx types.Transaction - if err := tx.UnmarshalBinary(encTx); err != nil { + if err := tx.UnmarshalMinimal(encTx); err != nil { return nil, fmt.Errorf("invalid transaction %d: %v", i, err) } txs[i] = &tx diff --git a/core/types/transaction.go b/core/types/transaction.go index aa7ef5a948c92..b45e425bfd3c1 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -137,7 +137,8 @@ func (tx *Transaction) EncodeRLP(w io.Writer) error { buf := encodeBufferPool.Get().(*bytes.Buffer) defer encodeBufferPool.Put(buf) buf.Reset() - if err := tx.encodeTyped(buf); err != nil { + // TODO(EIP-4844): Ensure that we use encodeTyped when gossiping transactions + if err := tx.encodeTypedMinimal(buf); err != nil { return err } return rlp.Encode(w, buf.Bytes()) @@ -156,7 +157,12 @@ func (tx *Transaction) encodeTypedMinimal(w io.Writer) error { if _, err := w.Write([]byte{tx.Type()}); err != nil { return err } - return rlp.Encode(w, tx.inner) + // TODO(inphi): clean + if tx.Type() == BlobTxType { + return EncodeSSZ(w, tx.inner.(*SignedBlobTx)) + } else { + return rlp.Encode(w, tx.inner) + } } // MarshalBinary returns the canonical encoding of the transaction. @@ -201,10 +207,9 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error { if b, err = s.Bytes(); err != nil { return err } - inner, wrapData, err := tx.decodeTyped(b) + inner, err := tx.decodeTypedMinimal(b) if err == nil { tx.setDecoded(inner, len(b)) - tx.wrapData = wrapData } return err } @@ -568,7 +573,7 @@ func (s Transactions) EncodeIndex(i int, w *bytes.Buffer) { if tx.Type() == LegacyTxType { rlp.Encode(w, tx.inner) } else { - tx.encodeTyped(w) + tx.encodeTypedMinimal(w) } } From 13c3f67e2eee9427155eff180da13ee7f2905980 Mon Sep 17 00:00:00 2001 From: Michael de Hoog Date: Sat, 11 Jun 2022 19:55:37 -0500 Subject: [PATCH 2/3] Fix missing copy of AccessList for EIP-4844 transactions --- core/types/data_blob_tx.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/types/data_blob_tx.go b/core/types/data_blob_tx.go index a57efab4709a1..906ebb8256c23 100644 --- a/core/types/data_blob_tx.go +++ b/core/types/data_blob_tx.go @@ -360,8 +360,10 @@ func (tx *BlobTxMessage) copy() *BlobTxMessage { To: AddressOptionalSSZ{Address: (*AddressSSZ)(copyAddressPtr((*common.Address)(tx.To.Address)))}, Value: tx.Value, Data: common.CopyBytes(tx.Data), + AccessList: make([]AccessTuple, len(tx.AccessList)), BlobVersionedHashes: make([]common.Hash, len(tx.BlobVersionedHashes)), } + copy(cpy.AccessList, tx.AccessList) copy(cpy.BlobVersionedHashes, tx.BlobVersionedHashes) return cpy From 2ba73e3fe989e23c7d16d52acfceacd66ab0b3c7 Mon Sep 17 00:00:00 2001 From: inphi Date: Sun, 12 Jun 2022 21:43:23 -0400 Subject: [PATCH 3/3] fix blob tx gossip encoding in mempool --- core/types/block.go | 14 ++++---- core/types/transaction.go | 61 +++++++++++++++++++++++++++++++++- eth/handler_eth_test.go | 3 +- eth/protocols/eth/broadcast.go | 4 +-- eth/protocols/eth/handlers.go | 2 +- eth/protocols/eth/peer.go | 2 +- 6 files changed, 73 insertions(+), 13 deletions(-) diff --git a/core/types/block.go b/core/types/block.go index 066b335d0e3bf..747ceb661162c 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -232,25 +232,25 @@ func (tx *minimalTx) EncodeRLP(w io.Writer) error { // a view over a regular transactions slice, to RLP decode/encode the transactions all the minimal way type extBlockTxs []*Transaction -func (txs *extBlockTxs) DecodeRLP(s *rlp.Stream) error { +func (txs extBlockTxs) DecodeRLP(s *rlp.Stream) error { // we need generics to do this nicely... var out []*minimalTx - for i, tx := range *txs { + for i, tx := range txs { out[i] = (*minimalTx)(tx) } if err := s.Decode(&out); err != nil { return fmt.Errorf("failed to decode list of minimal txs: %v", err) } - *txs = make([]*Transaction, len(out)) + txs = make([]*Transaction, len(out)) for i, tx := range out { - (*txs)[i] = (*Transaction)(tx) + txs[i] = (*Transaction)(tx) } return nil } -func (txs *extBlockTxs) EncodeRLP(w io.Writer) error { - out := make([]*minimalTx, len(*txs)) - for i, tx := range *txs { +func (txs extBlockTxs) EncodeRLP(w io.Writer) error { + out := make([]*minimalTx, len(txs)) + for i, tx := range txs { out[i] = (*minimalTx)(tx) } return rlp.Encode(w, &out) diff --git a/core/types/transaction.go b/core/types/transaction.go index b45e425bfd3c1..f6b178cc12bdb 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -137,7 +137,6 @@ func (tx *Transaction) EncodeRLP(w io.Writer) error { buf := encodeBufferPool.Get().(*bytes.Buffer) defer encodeBufferPool.Put(buf) buf.Reset() - // TODO(EIP-4844): Ensure that we use encodeTyped when gossiping transactions if err := tx.encodeTypedMinimal(buf); err != nil { return err } @@ -817,3 +816,63 @@ func copyAddressPtr(a *common.Address) *common.Address { cpy := *a return &cpy } + +// NetworkTransaction is a Transaction wrapper that encodes its maximal representation +type NetworkTransaction struct { + Tx *Transaction +} + +func NewNetworkTransaction(tx *Transaction) *NetworkTransaction { + return &NetworkTransaction{Tx: tx} +} + +// EncodeRLP implements rlp.Encoder +func (tx *NetworkTransaction) EncodeRLP(w io.Writer) error { + if tx.Tx.Type() == LegacyTxType { + return rlp.Encode(w, tx.Tx) + } + // It's an EIP-2718 typed TX envelope. + buf := encodeBufferPool.Get().(*bytes.Buffer) + defer encodeBufferPool.Put(buf) + buf.Reset() + if err := tx.Tx.encodeTyped(buf); err != nil { + return err + } + return rlp.Encode(w, buf.Bytes()) +} + +// DecodeRLP implements rlp.Decoder +func (tx *NetworkTransaction) DecodeRLP(s *rlp.Stream) error { + kind, size, err := s.Kind() + switch { + case err != nil: + return err + case kind == rlp.List: + // It's a legacy transaction. + var inner LegacyTx + err := s.Decode(&inner) + if err == nil { + tx.Tx.setDecoded(&inner, int(rlp.ListSize(size))) + } + return err + default: + // It's an EIP-2718 typed TX envelope. + var b []byte + if b, err = s.Bytes(); err != nil { + return err + } + inner, wrapData, err := tx.Tx.decodeTyped(b) + if err == nil { + tx.Tx.setDecoded(inner, len(b)) + tx.Tx.wrapData = wrapData + } + return err + } +} + +// Hash returns the transaction hash. +func (tx *NetworkTransaction) Hash() common.Hash { + return tx.Tx.Hash() +} + +type NetworkTransactions []*NetworkTransaction diff --git a/eth/handler_eth_test.go b/eth/handler_eth_test.go index dffbfbe612a2b..299f2ac4e98ce 100644 --- a/eth/handler_eth_test.go +++ b/eth/handler_eth_test.go @@ -278,7 +278,8 @@ func testRecvTransactions(t *testing.T, protocol uint) { tx := types.NewTransaction(0, common.Address{}, big.NewInt(0), 100000, big.NewInt(0), nil) tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey) - if err := src.SendTransactions([]*types.Transaction{tx}); err != nil { + ntx := types.NewNetworkTransaction(tx) + if err := src.SendTransactions([]*types.NetworkTransaction{ntx}); err != nil { t.Fatalf("failed to send transaction: %v", err) } select { diff --git a/eth/protocols/eth/broadcast.go b/eth/protocols/eth/broadcast.go index 09330cfdf320a..54558a80f14ab 100644 --- a/eth/protocols/eth/broadcast.go +++ b/eth/protocols/eth/broadcast.go @@ -76,12 +76,12 @@ func (p *Peer) broadcastTransactions() { // Pile transaction until we reach our allowed network limit var ( hashesCount uint64 - txs []*types.Transaction + txs []*types.NetworkTransaction size common.StorageSize ) for i := 0; i < len(queue) && size < maxTxPacketSize; i++ { if tx := p.txpool.Get(queue[i]); tx != nil { - txs = append(txs, tx) + txs = append(txs, types.NewNetworkTransaction(tx)) size += tx.Size() } hashesCount++ diff --git a/eth/protocols/eth/handlers.go b/eth/protocols/eth/handlers.go index c8585dfdf8f20..2261918e74dfd 100644 --- a/eth/protocols/eth/handlers.go +++ b/eth/protocols/eth/handlers.go @@ -474,7 +474,7 @@ func answerGetPooledTransactions(backend Backend, query GetPooledTransactionsPac continue } // If known, encode and queue for response packet - if encoded, err := rlp.EncodeToBytes(tx); err != nil { + if encoded, err := rlp.EncodeToBytes(types.NewNetworkTransaction(tx)); err != nil { log.Error("Failed to encode transaction", "err", err) } else { hashes = append(hashes, hash) diff --git a/eth/protocols/eth/peer.go b/eth/protocols/eth/peer.go index 22674d65b041f..7d28b43d76ee2 100644 --- a/eth/protocols/eth/peer.go +++ b/eth/protocols/eth/peer.go @@ -189,7 +189,7 @@ func (p *Peer) markTransaction(hash common.Hash) { // // The reasons this is public is to allow packages using this protocol to write // tests that directly send messages without having to do the asyn queueing. -func (p *Peer) SendTransactions(txs types.Transactions) error { +func (p *Peer) SendTransactions(txs types.NetworkTransactions) error { // Mark all the transactions as known, but ensure we don't overflow our limits for _, tx := range txs { p.knownTxs.Add(tx.Hash())