mirror of
https://github.com/vacp2p/linea-monorepo.git
synced 2026-01-09 20:27:58 -05:00
* test and fix the encoding-decoding of transaction with an empty to * fix typo * readd the explicit test case * fix: move decompressBlob out of the "no-CI" build tag * fix tag confusion * Fix prover libs CI release (#450) * prover: try to fix native libs arm build --------- Signed-off-by: AlexandreBelling <alexandrebelling8@gmail.com> Co-authored-by: Arya Tabaie <arya.pourtabatabaie@gmail.com> Co-authored-by: Pedro Novais <1478752+jpnovais@users.noreply.github.com>
212 lines
6.2 KiB
Go
212 lines
6.2 KiB
Go
package v1
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/consensys/linea-monorepo/prover/backend/ethereum"
|
|
"github.com/consensys/linea-monorepo/prover/lib/compressor/blob/encode"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/core/types"
|
|
)
|
|
|
|
// EncodeBlockForCompression encodes a block for compression.
|
|
func EncodeBlockForCompression(block *types.Block, w io.Writer) error {
|
|
|
|
if block == nil {
|
|
return fmt.Errorf("block is nil")
|
|
}
|
|
|
|
if block.Transactions() == nil {
|
|
return fmt.Errorf("block has nil transactions")
|
|
}
|
|
|
|
var (
|
|
// timestamp is the timestamp of the encoded block. The timestamp is
|
|
// encoded over a uint32 as this is sufficient in practice.
|
|
timestamp = uint32(block.Time())
|
|
// transactions lists the txs of the encoded block.
|
|
transactions = block.Transactions()
|
|
// blkHash holds the block hash
|
|
blockHash = block.Hash()
|
|
// numTxs holds the number of transaction. We use a uint16 as this is a
|
|
// realistic maximal value to store the transactions.
|
|
numTxs = uint16(len(transactions))
|
|
)
|
|
|
|
binary.Write(w, binary.BigEndian, numTxs)
|
|
binary.Write(w, binary.BigEndian, timestamp)
|
|
w.Write(blockHash[:])
|
|
|
|
for i, tx := range transactions {
|
|
if err := EncodeTxForCompression(tx, w); err != nil {
|
|
return fmt.Errorf("could not encode transaction #%v: %w", i, err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// encodeTransaction encodes a single transaction
|
|
func EncodeTxForCompression(tx *types.Transaction, w io.Writer) error {
|
|
if tx == nil {
|
|
return fmt.Errorf("transactions is nil")
|
|
}
|
|
|
|
var (
|
|
from = ethereum.GetFrom(tx)
|
|
txRlp = ethereum.EncodeTxForSigning(tx)
|
|
_, err1 = w.Write(from[:])
|
|
_, err2 = w.Write(txRlp[:])
|
|
)
|
|
|
|
return errors.Join(err1, err2)
|
|
}
|
|
|
|
// ScanBlockByteLen scans the stream of bytes `b`, expecting to find an encoded
|
|
// block starting from position 0 and returns the length of the block. It returns
|
|
// an error if the scanner goes out of bound.
|
|
func ScanBlockByteLen(b []byte) (int, error) {
|
|
|
|
const (
|
|
// preTxBufSize corresponds to the size of the buffer area used for
|
|
// encoding the block-hash and the timestamp. They are are stored ahead
|
|
// of the transaction
|
|
preTxBufSize = 32 + 4
|
|
|
|
// heuristicMaxNbTxs corresponds to a tacit maximal value that we can
|
|
// expect to be contained in the currently scanned block. The theoritical
|
|
// max value is 2**16 but a value higher than heuristic value is
|
|
// considered "odd" and triggers an error. The check can be easily remove
|
|
// in the future if we decide to make large blocks. This is used as an
|
|
// early fail mechanism.
|
|
heuristicMaxNbTxs = 1 << 10
|
|
)
|
|
|
|
var (
|
|
r = bytes.NewReader(b)
|
|
// decNumTxs corresponds to the number of transaction that are contained
|
|
// in the currently scanned block.
|
|
decNumTxs uint16
|
|
)
|
|
|
|
if err := binary.Read(r, binary.BigEndian, &decNumTxs); err != nil {
|
|
return 0, fmt.Errorf("could not decode nb txs: %w", err)
|
|
}
|
|
|
|
if decNumTxs > heuristicMaxNbTxs {
|
|
return 0, fmt.Errorf("invalid block: the decoded nb tx is %v > %v", decNumTxs, heuristicMaxNbTxs)
|
|
}
|
|
|
|
r.Seek(preTxBufSize, io.SeekCurrent)
|
|
|
|
for i := 0; i < int(decNumTxs); i++ {
|
|
// Pass the tx from address
|
|
r.Seek(int64(len(common.Address{})), io.SeekCurrent)
|
|
|
|
// The transaction of type dynamicFee and accessList have a prefix ahead
|
|
// the RLP encoding. When that is the case, we need to skip it before
|
|
// scanning the RLP prefix of the transaction.
|
|
prefix, err := r.ReadByte()
|
|
if err != nil {
|
|
return 0, fmt.Errorf("could not read the prefix byte of the tx #%v", i)
|
|
}
|
|
|
|
// When the prefix does not match a transaction type, this indicates a
|
|
// legacy transaction and the prefix was in fact the RLP prefix.
|
|
// We unread it, so that [passRlpTx] can access it.
|
|
if int(prefix) > types.DynamicFeeTxType {
|
|
r.UnreadByte()
|
|
}
|
|
|
|
if err := PassRlpList(r); err != nil {
|
|
return 0, fmt.Errorf("failed passing transaction #%v: %w", i, err)
|
|
}
|
|
}
|
|
|
|
return int(r.Size()) - r.Len(), nil
|
|
}
|
|
|
|
// PassRlpList advances the reader through an RLP list assuming the reader is
|
|
// currently pointing to an RLP-encoded list. This is used to scan the encoded
|
|
// number of bytes of an encoded blocks and more specifically to find the
|
|
// boundaries of an RLP-encoded transaction.
|
|
func PassRlpList(r *bytes.Reader) error {
|
|
firstByte, err := r.ReadByte()
|
|
if err != nil {
|
|
return fmt.Errorf("could not read the first byte: %w", err)
|
|
}
|
|
|
|
if firstByte < 0xc0 {
|
|
return fmt.Errorf("not an RLP list, the first byte is `0x%x`", firstByte)
|
|
}
|
|
|
|
if firstByte <= 0xf7 {
|
|
payLoadLen := int(firstByte) - 0xc0
|
|
r.Seek(int64(payLoadLen), io.SeekCurrent)
|
|
return nil
|
|
}
|
|
|
|
if firstByte > 0xf7 {
|
|
l := int(firstByte) - 0xf7
|
|
var buf [8]byte
|
|
r.Read(buf[8-l:])
|
|
payloadLen := binary.BigEndian.Uint64(buf[:])
|
|
r.Seek(int64(payloadLen), io.SeekCurrent)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// DecodeBlockFromUncompressed inverts [EncodeBlockForCompression]. It is primarily meant for
|
|
// testing and ensuring the encoding is bijective.
|
|
func DecodeBlockFromUncompressed(r *bytes.Reader) (encode.DecodedBlockData, error) {
|
|
|
|
var (
|
|
decNumTxs uint16
|
|
decTimestamp uint32
|
|
blockHash common.Hash
|
|
)
|
|
|
|
if err := binary.Read(r, binary.BigEndian, &decNumTxs); err != nil {
|
|
return encode.DecodedBlockData{}, fmt.Errorf("could not decode nb txs: %w", err)
|
|
}
|
|
|
|
if err := binary.Read(r, binary.BigEndian, &decTimestamp); err != nil {
|
|
return encode.DecodedBlockData{}, fmt.Errorf("could not decode timestamp: %w", err)
|
|
}
|
|
|
|
if _, err := r.Read(blockHash[:]); err != nil {
|
|
return encode.DecodedBlockData{}, fmt.Errorf("could not read the block hash: %w", err)
|
|
}
|
|
|
|
numTxs := int(decNumTxs)
|
|
decodedBlk := encode.DecodedBlockData{
|
|
Froms: make([]common.Address, numTxs),
|
|
Txs: make([]types.TxData, numTxs),
|
|
Timestamp: uint64(decTimestamp),
|
|
BlockHash: blockHash,
|
|
}
|
|
|
|
var err error
|
|
for i := 0; i < int(decNumTxs); i++ {
|
|
if decodedBlk.Txs[i], err = DecodeTxFromUncompressed(r, &decodedBlk.Froms[i]); err != nil {
|
|
return encode.DecodedBlockData{}, fmt.Errorf("could not decode transaction #%v: %w", i, err)
|
|
}
|
|
}
|
|
|
|
return decodedBlk, nil
|
|
}
|
|
|
|
func DecodeTxFromUncompressed(r *bytes.Reader, from *common.Address) (types.TxData, error) {
|
|
if _, err := r.Read(from[:]); err != nil {
|
|
return nil, fmt.Errorf("could not read from address: %w", err)
|
|
}
|
|
|
|
return ethereum.DecodeTxFromBytes(r)
|
|
}
|