github.com/ledgerwatch/erigon-lib@v1.0.0/types/txn_packets.go (about)

     1  /*
     2     Copyright 2021 Erigon contributors
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package types
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  
    23  	"github.com/ledgerwatch/erigon-lib/common"
    24  	"github.com/ledgerwatch/erigon-lib/common/length"
    25  	"github.com/ledgerwatch/erigon-lib/rlp"
    26  )
    27  
    28  type NewPooledTransactionHashesPacket [][length.Hash]byte
    29  
    30  // ParseHashesCount looks at the RLP length Prefix for list of 32-byte hashes
    31  // and returns number of hashes in the list to expect
    32  func ParseHashesCount(payload []byte, pos int) (count int, dataPos int, err error) {
    33  	dataPos, dataLen, err := rlp.List(payload, pos)
    34  	if err != nil {
    35  		return 0, 0, fmt.Errorf("%s: hashes len: %w", rlp.ParseHashErrorPrefix, err)
    36  	}
    37  	if dataLen%33 != 0 {
    38  		return 0, 0, fmt.Errorf("%s: hashes len must be multiple of 33", rlp.ParseHashErrorPrefix)
    39  	}
    40  	return dataLen / 33, dataPos, nil
    41  }
    42  
    43  // EncodeHashes produces RLP encoding of given number of hashes, as RLP list
    44  // It appends encoding to the given given slice (encodeBuf), reusing the space
    45  // there is there is enough capacity.
    46  // The first returned value is the slice where encodinfg
    47  func EncodeHashes(hashes []byte, encodeBuf []byte) []byte {
    48  	hashesLen := len(hashes) / length.Hash * 33
    49  	dataLen := hashesLen
    50  	encodeBuf = common.EnsureEnoughSize(encodeBuf, rlp.ListPrefixLen(hashesLen)+dataLen)
    51  	rlp.EncodeHashes(hashes, encodeBuf)
    52  	return encodeBuf
    53  }
    54  
    55  // ParseHash extracts the next hash from the RLP encoding (payload) from a given position.
    56  // It appends the hash to the given slice, reusing the space if there is enough capacity
    57  // The first returned value is the slice where hash is appended to.
    58  // The second returned value is the new position in the RLP payload after the extraction
    59  // of the hash.
    60  func ParseHash(payload []byte, pos int, hashbuf []byte) ([]byte, int, error) {
    61  	hashbuf = common.EnsureEnoughSize(hashbuf, length.Hash)
    62  	pos, err := rlp.ParseHash(payload, pos, hashbuf)
    63  	if err != nil {
    64  		return nil, 0, fmt.Errorf("%s: hash len: %w", rlp.ParseHashErrorPrefix, err)
    65  	}
    66  	return hashbuf, pos, nil
    67  }
    68  
    69  // EncodeGetPooledTransactions66 produces encoding of GetPooledTransactions66 packet
    70  func EncodeGetPooledTransactions66(hashes []byte, requestID uint64, encodeBuf []byte) ([]byte, error) {
    71  	pos := 0
    72  	hashesLen := len(hashes) / length.Hash * 33
    73  	dataLen := rlp.ListPrefixLen(hashesLen) + hashesLen + rlp.U64Len(requestID)
    74  	encodeBuf = common.EnsureEnoughSize(encodeBuf, rlp.ListPrefixLen(dataLen)+dataLen)
    75  	// Length Prefix for the entire structure
    76  	pos += rlp.EncodeListPrefix(dataLen, encodeBuf[pos:])
    77  	pos += rlp.EncodeU64(requestID, encodeBuf[pos:])
    78  	pos += rlp.EncodeHashes(hashes, encodeBuf[pos:])
    79  	_ = pos
    80  	return encodeBuf, nil
    81  }
    82  
    83  func ParseGetPooledTransactions66(payload []byte, pos int, hashbuf []byte) (requestID uint64, hashes []byte, newPos int, err error) {
    84  	pos, _, err = rlp.List(payload, pos)
    85  	if err != nil {
    86  		return 0, hashes, 0, err
    87  	}
    88  
    89  	pos, requestID, err = rlp.U64(payload, pos)
    90  	if err != nil {
    91  		return 0, hashes, 0, err
    92  	}
    93  	var hashesCount int
    94  	hashesCount, pos, err = ParseHashesCount(payload, pos)
    95  	if err != nil {
    96  		return 0, hashes, 0, err
    97  	}
    98  	hashes = common.EnsureEnoughSize(hashbuf, length.Hash*hashesCount)
    99  
   100  	for i := 0; i < hashesCount; i++ {
   101  		pos, err = rlp.ParseHash(payload, pos, hashes[i*length.Hash:])
   102  		if err != nil {
   103  			return 0, hashes, 0, err
   104  		}
   105  	}
   106  	return requestID, hashes, pos, nil
   107  }
   108  
   109  // == Pooled transactions ==
   110  
   111  // TODO(eip-4844) wrappedWithBlobs = true?
   112  func EncodePooledTransactions66(txsRlp [][]byte, requestID uint64, encodeBuf []byte) []byte {
   113  	pos := 0
   114  	txsRlpLen := 0
   115  	for i := range txsRlp {
   116  		_, _, isLegacy, _ := rlp.Prefix(txsRlp[i], 0)
   117  		if isLegacy {
   118  			txsRlpLen += len(txsRlp[i])
   119  		} else {
   120  			txsRlpLen += rlp.StringLen(txsRlp[i])
   121  		}
   122  	}
   123  	dataLen := rlp.U64Len(requestID) + rlp.ListPrefixLen(txsRlpLen) + txsRlpLen
   124  
   125  	encodeBuf = common.EnsureEnoughSize(encodeBuf, rlp.ListPrefixLen(dataLen)+dataLen)
   126  
   127  	// Length Prefix for the entire structure
   128  	pos += rlp.EncodeListPrefix(dataLen, encodeBuf[pos:])
   129  	pos += rlp.EncodeU64(requestID, encodeBuf[pos:])
   130  	pos += rlp.EncodeListPrefix(txsRlpLen, encodeBuf[pos:])
   131  	for i := range txsRlp {
   132  		_, _, isLegacy, _ := rlp.Prefix(txsRlp[i], 0)
   133  		if isLegacy {
   134  			copy(encodeBuf[pos:], txsRlp[i])
   135  			pos += len(txsRlp[i])
   136  		} else {
   137  			pos += rlp.EncodeString(txsRlp[i], encodeBuf[pos:])
   138  		}
   139  	}
   140  	_ = pos
   141  	return encodeBuf
   142  }
   143  
   144  // TODO(eip-4844) wrappedWithBlobs = false?
   145  func EncodeTransactions(txsRlp [][]byte, encodeBuf []byte) []byte {
   146  	pos := 0
   147  	dataLen := 0
   148  	for i := range txsRlp {
   149  		_, _, isLegacy, _ := rlp.Prefix(txsRlp[i], 0)
   150  		if isLegacy {
   151  			dataLen += len(txsRlp[i])
   152  		} else {
   153  			dataLen += rlp.StringLen(txsRlp[i])
   154  		}
   155  	}
   156  
   157  	encodeBuf = common.EnsureEnoughSize(encodeBuf, rlp.ListPrefixLen(dataLen)+dataLen)
   158  	// Length Prefix for the entire structure
   159  	pos += rlp.EncodeListPrefix(dataLen, encodeBuf[pos:])
   160  	for i := range txsRlp {
   161  		_, _, isLegacy, _ := rlp.Prefix(txsRlp[i], 0)
   162  		if isLegacy {
   163  			copy(encodeBuf[pos:], txsRlp[i])
   164  			pos += len(txsRlp[i])
   165  		} else {
   166  			pos += rlp.EncodeString(txsRlp[i], encodeBuf[pos:])
   167  		}
   168  	}
   169  	_ = pos
   170  	return encodeBuf
   171  }
   172  
   173  func ParseTransactions(payload []byte, pos int, ctx *TxParseContext, txSlots *TxSlots, validateHash func([]byte) error) (newPos int, err error) {
   174  	pos, _, err = rlp.List(payload, pos)
   175  	if err != nil {
   176  		return 0, err
   177  	}
   178  
   179  	for i := 0; pos < len(payload); i++ {
   180  		txSlots.Resize(uint(i + 1))
   181  		txSlots.Txs[i] = &TxSlot{}
   182  		pos, err = ctx.ParseTransaction(payload, pos, txSlots.Txs[i], txSlots.Senders.At(i), true /* hasEnvelope */, true /* wrappedWithBlobs */, validateHash)
   183  		if err != nil {
   184  			if errors.Is(err, ErrRejected) {
   185  				txSlots.Resize(uint(i))
   186  				i--
   187  				continue
   188  			}
   189  			return 0, err
   190  		}
   191  	}
   192  	return pos, nil
   193  }
   194  
   195  func ParsePooledTransactions66(payload []byte, pos int, ctx *TxParseContext, txSlots *TxSlots, validateHash func([]byte) error) (requestID uint64, newPos int, err error) {
   196  	p, _, err := rlp.List(payload, pos)
   197  	if err != nil {
   198  		return requestID, 0, err
   199  	}
   200  	p, requestID, err = rlp.U64(payload, p)
   201  	if err != nil {
   202  		return requestID, 0, err
   203  	}
   204  	p, _, err = rlp.List(payload, p)
   205  	if err != nil {
   206  		return requestID, 0, err
   207  	}
   208  
   209  	for i := 0; p < len(payload); i++ {
   210  		txSlots.Resize(uint(i + 1))
   211  		txSlots.Txs[i] = &TxSlot{}
   212  		p, err = ctx.ParseTransaction(payload, p, txSlots.Txs[i], txSlots.Senders.At(i), true /* hasEnvelope */, true /* wrappedWithBlobs */, validateHash)
   213  		if err != nil {
   214  			if errors.Is(err, ErrRejected) {
   215  				txSlots.Resize(uint(i))
   216  				i--
   217  				continue
   218  			}
   219  			return requestID, 0, err
   220  		}
   221  	}
   222  	return requestID, p, nil
   223  }