From 550d8958d787502b6723b30e957eb39d527ddc0f Mon Sep 17 00:00:00 2001 From: antonis19 Date: Fri, 29 Nov 2024 13:17:24 +0100 Subject: [PATCH] Move erigon-lib/rlp to erigon-lib/rlp2 (#12915) Since the change here : https://github.com/erigontech/erigon/pull/12879 was too big and had to be reverted since it caused the pectra kurtosis tests to fail due to rlp errors, I am breaking it down into a sequence of smaller PRs. This RP moves `erigon-lib/rlp` to `erigon-lib/rlp2` . In a next PR I will move `rlp` to `erigon-lib/rlp` . Co-authored-by: antonis19 --- core/types/access_list_tx.go | 2 +- core/types/accounts/account.go | 2 +- core/types/authorization.go | 2 +- core/types/blob_tx.go | 2 +- core/types/block.go | 2 +- core/types/dynamic_fee_tx.go | 2 +- core/types/legacy_tx.go | 2 +- core/types/set_code_tx.go | 2 +- core/types/transaction.go | 2 +- erigon-lib/commitment/hex_patricia_hashed.go | 2 +- erigon-lib/crypto/crypto.go | 2 +- erigon-lib/rlp/commitment.go | 284 ------------------ erigon-lib/rlp/parse.go | 288 ------------------ erigon-lib/rlp/parse_test.go | 108 ------- erigon-lib/rlp2/decoder.go | 293 ------------------ erigon-lib/{rlp => rlp2}/encode.go | 0 erigon-lib/{rlp => rlp2}/encode_test.go | 0 erigon-lib/rlp2/encodel.go | 298 ------------------- erigon-lib/rlp2/encoder.go | 207 ------------- erigon-lib/rlp2/parse.go | 11 +- erigon-lib/rlp2/readme.md | 11 - erigon-lib/rlp2/types.go | 75 ----- erigon-lib/rlp2/unmarshaler.go | 207 ------------- erigon-lib/rlp2/unmarshaler_test.go | 82 ----- erigon-lib/rlp2/util.go | 100 ------- eth/protocols/eth/protocol.go | 2 +- txnprovider/txpool/fetch.go | 2 +- txnprovider/txpool/pool_fuzz_test.go | 2 +- txnprovider/txpool/pool_txn_packets.go | 2 +- txnprovider/txpool/pool_txn_parser.go | 2 +- txnprovider/txpool/send.go | 2 +- 31 files changed, 22 insertions(+), 1976 deletions(-) delete mode 100644 erigon-lib/rlp/commitment.go delete mode 100644 erigon-lib/rlp/parse.go delete mode 100644 erigon-lib/rlp/parse_test.go delete mode 100644 erigon-lib/rlp2/decoder.go rename erigon-lib/{rlp => rlp2}/encode.go (100%) rename erigon-lib/{rlp => rlp2}/encode_test.go (100%) delete mode 100644 erigon-lib/rlp2/encodel.go delete mode 100644 erigon-lib/rlp2/encoder.go delete mode 100644 erigon-lib/rlp2/readme.md delete mode 100644 erigon-lib/rlp2/types.go delete mode 100644 erigon-lib/rlp2/unmarshaler.go delete mode 100644 erigon-lib/rlp2/unmarshaler_test.go delete mode 100644 erigon-lib/rlp2/util.go diff --git a/core/types/access_list_tx.go b/core/types/access_list_tx.go index 24f09377caa..9a80fb6c5c3 100644 --- a/core/types/access_list_tx.go +++ b/core/types/access_list_tx.go @@ -31,7 +31,7 @@ import ( "github.com/erigontech/erigon-lib/chain" libcommon "github.com/erigontech/erigon-lib/common" - rlp2 "github.com/erigontech/erigon-lib/rlp" + rlp2 "github.com/erigontech/erigon-lib/rlp2" "github.com/erigontech/erigon/rlp" ) diff --git a/core/types/accounts/account.go b/core/types/accounts/account.go index 736bc1e2152..0fb527e09ce 100644 --- a/core/types/accounts/account.go +++ b/core/types/accounts/account.go @@ -26,7 +26,7 @@ import ( libcommon "github.com/erigontech/erigon-lib/common" "github.com/erigontech/erigon-lib/crypto" - rlp2 "github.com/erigontech/erigon-lib/rlp" + rlp2 "github.com/erigontech/erigon-lib/rlp2" "github.com/erigontech/erigon/rlp" ) diff --git a/core/types/authorization.go b/core/types/authorization.go index ed9036b2db6..68c1e3e3300 100644 --- a/core/types/authorization.go +++ b/core/types/authorization.go @@ -11,7 +11,7 @@ import ( libcommon "github.com/erigontech/erigon-lib/common" "github.com/erigontech/erigon-lib/common/length" "github.com/erigontech/erigon-lib/crypto" - rlp2 "github.com/erigontech/erigon-lib/rlp" + rlp2 "github.com/erigontech/erigon-lib/rlp2" "github.com/erigontech/erigon/params" "github.com/erigontech/erigon/rlp" ) diff --git a/core/types/blob_tx.go b/core/types/blob_tx.go index 72845624e3e..3a6001eb6ac 100644 --- a/core/types/blob_tx.go +++ b/core/types/blob_tx.go @@ -27,7 +27,7 @@ import ( "github.com/erigontech/erigon-lib/chain" libcommon "github.com/erigontech/erigon-lib/common" "github.com/erigontech/erigon-lib/common/fixedgas" - rlp2 "github.com/erigontech/erigon-lib/rlp" + rlp2 "github.com/erigontech/erigon-lib/rlp2" "github.com/erigontech/erigon/rlp" ) diff --git a/core/types/block.go b/core/types/block.go index 1a549caba0a..d961784dc60 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -35,7 +35,7 @@ import ( libcommon "github.com/erigontech/erigon-lib/common" "github.com/erigontech/erigon-lib/common/hexutil" "github.com/erigontech/erigon-lib/common/hexutility" - rlp2 "github.com/erigontech/erigon-lib/rlp" + rlp2 "github.com/erigontech/erigon-lib/rlp2" "github.com/erigontech/erigon/common" "github.com/erigontech/erigon/rlp" ) diff --git a/core/types/dynamic_fee_tx.go b/core/types/dynamic_fee_tx.go index 225467caa4d..a03a53e017f 100644 --- a/core/types/dynamic_fee_tx.go +++ b/core/types/dynamic_fee_tx.go @@ -29,7 +29,7 @@ import ( "github.com/erigontech/erigon-lib/chain" libcommon "github.com/erigontech/erigon-lib/common" - rlp2 "github.com/erigontech/erigon-lib/rlp" + rlp2 "github.com/erigontech/erigon-lib/rlp2" "github.com/erigontech/erigon/rlp" ) diff --git a/core/types/legacy_tx.go b/core/types/legacy_tx.go index 1cffe818772..97eedfe655d 100644 --- a/core/types/legacy_tx.go +++ b/core/types/legacy_tx.go @@ -29,7 +29,7 @@ import ( "github.com/erigontech/erigon-lib/chain" libcommon "github.com/erigontech/erigon-lib/common" - rlp2 "github.com/erigontech/erigon-lib/rlp" + rlp2 "github.com/erigontech/erigon-lib/rlp2" "github.com/erigontech/erigon/rlp" ) diff --git a/core/types/set_code_tx.go b/core/types/set_code_tx.go index fc6542f6375..94af792f54b 100644 --- a/core/types/set_code_tx.go +++ b/core/types/set_code_tx.go @@ -27,7 +27,7 @@ import ( "github.com/erigontech/erigon-lib/chain" libcommon "github.com/erigontech/erigon-lib/common" - rlp2 "github.com/erigontech/erigon-lib/rlp" + rlp2 "github.com/erigontech/erigon-lib/rlp2" "github.com/erigontech/erigon/params" "github.com/erigontech/erigon/rlp" ) diff --git a/core/types/transaction.go b/core/types/transaction.go index 4e912cac451..c159865ddbe 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -36,7 +36,7 @@ import ( "github.com/erigontech/erigon-lib/common/math" libcrypto "github.com/erigontech/erigon-lib/crypto" "github.com/erigontech/erigon-lib/log/v3" - rlp2 "github.com/erigontech/erigon-lib/rlp" + rlp2 "github.com/erigontech/erigon-lib/rlp2" "github.com/erigontech/erigon/rlp" ) diff --git a/erigon-lib/commitment/hex_patricia_hashed.go b/erigon-lib/commitment/hex_patricia_hashed.go index 10bf5375eab..faf9c68d6b3 100644 --- a/erigon-lib/commitment/hex_patricia_hashed.go +++ b/erigon-lib/commitment/hex_patricia_hashed.go @@ -44,7 +44,7 @@ import ( "github.com/erigontech/erigon-lib/common" "github.com/erigontech/erigon-lib/common/length" - "github.com/erigontech/erigon-lib/rlp" + rlp "github.com/erigontech/erigon-lib/rlp2" ) // keccakState wraps sha3.state. In addition to the usual hash methods, it also supports diff --git a/erigon-lib/crypto/crypto.go b/erigon-lib/crypto/crypto.go index b1e017d2ff2..066b78332fd 100644 --- a/erigon-lib/crypto/crypto.go +++ b/erigon-lib/crypto/crypto.go @@ -39,7 +39,7 @@ import ( "github.com/erigontech/erigon-lib/common/hexutil" "github.com/erigontech/erigon-lib/common/math" "github.com/erigontech/erigon-lib/crypto/cryptopool" - "github.com/erigontech/erigon-lib/rlp" + rlp "github.com/erigontech/erigon-lib/rlp2" ) // SignatureLength indicates the byte length required to carry a signature with recovery id. diff --git a/erigon-lib/rlp/commitment.go b/erigon-lib/rlp/commitment.go deleted file mode 100644 index 7a3b744df07..00000000000 --- a/erigon-lib/rlp/commitment.go +++ /dev/null @@ -1,284 +0,0 @@ -// Copyright 2022 The Erigon Authors -// This file is part of Erigon. -// -// Erigon is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Erigon is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Erigon. If not, see . - -package rlp - -import "io" - -// RLP-related utilities necessary for computing commitments for state root hash - -// generateRlpPrefixLenDouble calculates the length of RLP prefix to encode a string of bytes of length l "twice", -// meaning that it is the prefix for rlp(rlp(data)) -func generateRlpPrefixLenDouble(l int, firstByte byte) int { - if l < 2 { - // firstByte only matters when there is 1 byte to encode - if firstByte >= 0x80 { - return 2 - } - return 0 - } - if l < 55 { - return 2 - } - if l < 56 { // 2 + 1 - return 3 - } - if l < 254 { - return 4 - } - if l < 256 { - return 5 - } - if l < 65533 { - return 6 - } - if l < 65536 { - return 7 - } - return 8 -} - -func multiByteHeaderPrefixOfLen(l int) byte { - // > If a string is more than 55 bytes long, the - // > RLP encoding consists of a single byte with value 0xB7 plus the length - // > of the length of the string in binary form, followed by the length of - // > the string, followed by the string. For example, a length-1024 string - // > would be encoded as 0xB90400 followed by the string. The range of - // > the first byte is thus [0xB8, 0xBF]. - // - // see package rlp/decode.go:887 - return byte(0xB7 + l) -} - -func generateByteArrayLen(buffer []byte, pos int, l int) int { - if l < 56 { - buffer[pos] = byte(0x80 + l) - pos++ - } else if l < 256 { - // len(vn) can be encoded as 1 byte - buffer[pos] = multiByteHeaderPrefixOfLen(1) - pos++ - buffer[pos] = byte(l) - pos++ - } else if l < 65536 { - // len(vn) is encoded as two bytes - buffer[pos] = multiByteHeaderPrefixOfLen(2) - pos++ - buffer[pos] = byte(l >> 8) - pos++ - buffer[pos] = byte(l & 255) - pos++ - } else { - // len(vn) is encoded as three bytes - buffer[pos] = multiByteHeaderPrefixOfLen(3) - pos++ - buffer[pos] = byte(l >> 16) - pos++ - buffer[pos] = byte((l >> 8) & 255) - pos++ - buffer[pos] = byte(l & 255) - pos++ - } - return pos -} - -func generateByteArrayLenDouble(buffer []byte, pos int, l int) int { - if l < 55 { - // After first wrapping, the length will be l + 1 < 56 - buffer[pos] = byte(0x80 + l + 1) - pos++ - buffer[pos] = byte(0x80 + l) - pos++ - } else if l < 56 { - buffer[pos] = multiByteHeaderPrefixOfLen(1) - pos++ - buffer[pos] = byte(l + 1) - pos++ - buffer[pos] = byte(0x80 + l) - pos++ - } else if l < 254 { - // After first wrapping, the length will be l + 2 < 256 - buffer[pos] = multiByteHeaderPrefixOfLen(1) - pos++ - buffer[pos] = byte(l + 2) - pos++ - buffer[pos] = multiByteHeaderPrefixOfLen(1) - pos++ - buffer[pos] = byte(l) - pos++ - } else if l < 256 { - // First wrapping is 2 bytes, second wrapping 3 bytes - buffer[pos] = multiByteHeaderPrefixOfLen(2) - pos++ - buffer[pos] = byte((l + 2) >> 8) - pos++ - buffer[pos] = byte((l + 2) & 255) - pos++ - buffer[pos] = multiByteHeaderPrefixOfLen(1) - pos++ - buffer[pos] = byte(l) - pos++ - } else if l < 65533 { - // Both wrappings are 3 bytes - buffer[pos] = multiByteHeaderPrefixOfLen(2) - pos++ - buffer[pos] = byte((l + 3) >> 8) - pos++ - buffer[pos] = byte((l + 3) & 255) - pos++ - buffer[pos] = multiByteHeaderPrefixOfLen(2) - pos++ - buffer[pos] = byte(l >> 8) - pos++ - buffer[pos] = byte(l & 255) - pos++ - } else if l < 65536 { - // First wrapping is 3 bytes, second wrapping is 4 bytes - buffer[pos] = multiByteHeaderPrefixOfLen(3) - pos++ - buffer[pos] = byte((l + 3) >> 16) - pos++ - buffer[pos] = byte(((l + 3) >> 8) & 255) - pos++ - buffer[pos] = byte((l + 3) & 255) - pos++ - buffer[pos] = multiByteHeaderPrefixOfLen(2) - pos++ - buffer[pos] = byte((l >> 8) & 255) - pos++ - buffer[pos] = byte(l & 255) - pos++ - } else { - // Both wrappings are 4 bytes - buffer[pos] = multiByteHeaderPrefixOfLen(3) - pos++ - buffer[pos] = byte((l + 4) >> 16) - pos++ - buffer[pos] = byte(((l + 4) >> 8) & 255) - pos++ - buffer[pos] = byte((l + 4) & 255) - pos++ - buffer[pos] = multiByteHeaderPrefixOfLen(3) - pos++ - buffer[pos] = byte(l >> 16) - pos++ - buffer[pos] = byte((l >> 8) & 255) - pos++ - buffer[pos] = byte(l & 255) - pos++ - } - return pos -} - -func generateRlpPrefixLen(l int) int { - if l < 2 { - return 0 - } - if l < 56 { - return 1 - } - if l < 256 { - return 2 - } - if l < 65536 { - return 3 - } - return 4 -} - -// RlpSerializable is a value that can be double-RLP coded. -type RlpSerializable interface { - ToDoubleRLP(io.Writer, []byte) error - DoubleRLPLen() int - RawBytes() []byte -} - -type RlpSerializableBytes []byte - -func (b RlpSerializableBytes) ToDoubleRLP(w io.Writer, prefixBuf []byte) error { - return encodeBytesAsRlpToWriter(b, w, generateByteArrayLenDouble, prefixBuf) -} - -func (b RlpSerializableBytes) RawBytes() []byte { - return b -} - -func (b RlpSerializableBytes) DoubleRLPLen() int { - if len(b) < 1 { - return 0 - } - return generateRlpPrefixLenDouble(len(b), b[0]) + len(b) -} - -type RlpEncodedBytes []byte - -func (b RlpEncodedBytes) ToDoubleRLP(w io.Writer, prefixBuf []byte) error { - return encodeBytesAsRlpToWriter(b, w, generateByteArrayLen, prefixBuf) -} - -func (b RlpEncodedBytes) RawBytes() []byte { - return b -} - -func (b RlpEncodedBytes) DoubleRLPLen() int { - return generateRlpPrefixLen(len(b)) + len(b) -} - -func encodeBytesAsRlpToWriter(source []byte, w io.Writer, prefixGenFunc func([]byte, int, int) int, prefixBuf []byte) error { - // > 1 byte, write a prefix or prefixes first - if len(source) > 1 || (len(source) == 1 && source[0] >= 0x80) { - prefixLen := prefixGenFunc(prefixBuf, 0, len(source)) - - if _, err := w.Write(prefixBuf[:prefixLen]); err != nil { - return err - } - } - - _, err := w.Write(source) - return err -} - -func EncodeByteArrayAsRlp(raw []byte, w io.Writer, prefixBuf []byte) (int, error) { - err := encodeBytesAsRlpToWriter(raw, w, generateByteArrayLen, prefixBuf) - if err != nil { - return 0, err - } - return generateRlpPrefixLen(len(raw)) + len(raw), nil -} - -func GenerateStructLen(buffer []byte, l int) int { - if l < 56 { - buffer[0] = byte(192 + l) - return 1 - } - if l < 256 { - // l can be encoded as 1 byte - buffer[1] = byte(l) - buffer[0] = byte(247 + 1) - return 2 - } - if l < 65536 { - buffer[2] = byte(l & 255) - buffer[1] = byte(l >> 8) - buffer[0] = byte(247 + 2) - return 3 - } - buffer[3] = byte(l & 255) - buffer[2] = byte((l >> 8) & 255) - buffer[1] = byte(l >> 16) - buffer[0] = byte(247 + 3) - return 4 -} diff --git a/erigon-lib/rlp/parse.go b/erigon-lib/rlp/parse.go deleted file mode 100644 index 09545f4347e..00000000000 --- a/erigon-lib/rlp/parse.go +++ /dev/null @@ -1,288 +0,0 @@ -// Copyright 2021 The Erigon Authors -// This file is part of Erigon. -// -// Erigon is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Erigon is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Erigon. If not, see . - -package rlp - -import ( - "errors" - "fmt" - - "github.com/holiman/uint256" - - "github.com/erigontech/erigon-lib/common" -) - -var ( - ErrBase = errors.New("rlp") - ErrParse = fmt.Errorf("%w parse", ErrBase) - ErrDecode = fmt.Errorf("%w decode", ErrBase) -) - -func IsRLPError(err error) bool { return errors.Is(err, ErrBase) } - -// BeInt parses Big Endian representation of an integer from given payload at given position -func BeInt(payload []byte, pos, length int) (int, error) { - var r int - if pos+length >= len(payload) { - return 0, fmt.Errorf("%w: unexpected end of payload", ErrParse) - } - if length > 0 && payload[pos] == 0 { - return 0, fmt.Errorf("%w: integer encoding for RLP must not have leading zeros: %x", ErrParse, payload[pos:pos+length]) - } - for _, b := range payload[pos : pos+length] { - r = (r << 8) | int(b) - } - return r, nil -} - -// Prefix parses RLP Prefix from given payload at given position. It returns the offset and length of the RLP element -// as well as the indication of whether it is a list of string -func Prefix(payload []byte, pos int) (dataPos int, dataLen int, isList bool, err error) { - if pos < 0 { - return 0, 0, false, fmt.Errorf("%w: negative position not allowed", ErrParse) - } - if pos >= len(payload) { - return 0, 0, false, fmt.Errorf("%w: unexpected end of payload", ErrParse) - } - switch first := payload[pos]; { - case first < 128: - dataPos = pos - dataLen = 1 - isList = false - case first < 184: - // Otherwise, if a string is 0-55 bytes long, - // the RLP encoding consists of a single byte with value 0x80 plus the - // length of the string followed by the string. The range of the first - // byte is thus [0x80, 0xB7]. - dataPos = pos + 1 - dataLen = int(first) - 128 - isList = false - if dataLen == 1 && dataPos < len(payload) && payload[dataPos] < 128 { - err = fmt.Errorf("%w: non-canonical size information", ErrParse) - } - case first < 192: - // If a string is more than 55 bytes long, the - // RLP encoding consists of a single byte with value 0xB7 plus the length - // of the length of the string in binary form, followed by the length of - // the string, followed by the string. For example, a length-1024 string - // would be encoded as 0xB90400 followed by the string. The range of - // the first byte is thus [0xB8, 0xBF]. - beLen := int(first) - 183 - dataPos = pos + 1 + beLen - dataLen, err = BeInt(payload, pos+1, beLen) - isList = false - if dataLen < 56 { - err = fmt.Errorf("%w: non-canonical size information", ErrParse) - } - case first < 248: - // isList of len < 56 - // If the total payload of a list - // (i.e. the combined length of all its items) is 0-55 bytes long, the - // RLP encoding consists of a single byte with value 0xC0 plus the length - // of the list followed by the concatenation of the RLP encodings of the - // items. The range of the first byte is thus [0xC0, 0xF7]. - dataPos = pos + 1 - dataLen = int(first) - 192 - isList = true - default: - // If the total payload of a list is more than 55 bytes long, - // the RLP encoding consists of a single byte with value 0xF7 - // plus the length of the length of the payload in binary - // form, followed by the length of the payload, followed by - // the concatenation of the RLP encodings of the items. The - // range of the first byte is thus [0xF8, 0xFF]. - beLen := int(first) - 247 - dataPos = pos + 1 + beLen - dataLen, err = BeInt(payload, pos+1, beLen) - isList = true - if dataLen < 56 { - err = fmt.Errorf("%w: : non-canonical size information", ErrParse) - } - } - if err == nil { - if dataPos+dataLen > len(payload) { - err = fmt.Errorf("%w: unexpected end of payload", ErrParse) - } else if dataPos+dataLen < 0 { - err = fmt.Errorf("%w: found too big len", ErrParse) - } - } - return -} - -func List(payload []byte, pos int) (dataPos, dataLen int, err error) { - dataPos, dataLen, isList, err := Prefix(payload, pos) - if err != nil { - return 0, 0, err - } - if !isList { - return 0, 0, fmt.Errorf("%w: must be a list", ErrParse) - } - return -} - -func String(payload []byte, pos int) (dataPos, dataLen int, err error) { - dataPos, dataLen, isList, err := Prefix(payload, pos) - if err != nil { - return 0, 0, err - } - if isList { - return 0, 0, fmt.Errorf("%w: must be a string, instead of a list", ErrParse) - } - return -} -func StringOfLen(payload []byte, pos, expectedLen int) (dataPos int, err error) { - dataPos, dataLen, err := String(payload, pos) - if err != nil { - return 0, err - } - if dataLen != expectedLen { - return 0, fmt.Errorf("%w: expected string of len %d, got %d", ErrParse, expectedLen, dataLen) - } - return -} - -// U64 parses uint64 number from given payload at given position -func U64(payload []byte, pos int) (int, uint64, error) { - dataPos, dataLen, isList, err := Prefix(payload, pos) - if err != nil { - return 0, 0, err - } - if isList { - return 0, 0, fmt.Errorf("%w: uint64 must be a string, not isList", ErrParse) - } - if dataLen > 8 { - return 0, 0, fmt.Errorf("%w: uint64 must not be more than 8 bytes long, got %d", ErrParse, dataLen) - } - if dataLen > 0 && payload[dataPos] == 0 { - return 0, 0, fmt.Errorf("%w: integer encoding for RLP must not have leading zeros: %x", ErrParse, payload[dataPos:dataPos+dataLen]) - } - var r uint64 - for _, b := range payload[dataPos : dataPos+dataLen] { - r = (r << 8) | uint64(b) - } - return dataPos + dataLen, r, nil -} - -// U32 parses uint64 number from given payload at given position -func U32(payload []byte, pos int) (int, uint32, error) { - dataPos, dataLen, isList, err := Prefix(payload, pos) - if err != nil { - return 0, 0, err - } - if isList { - return 0, 0, fmt.Errorf("%w: uint32 must be a string, not isList", ErrParse) - } - if dataLen > 4 { - return 0, 0, fmt.Errorf("%w: uint32 must not be more than 4 bytes long, got %d", ErrParse, dataLen) - } - if dataLen > 0 && payload[dataPos] == 0 { - return 0, 0, fmt.Errorf("%w: integer encoding for RLP must not have leading zeros: %x", ErrParse, payload[dataPos:dataPos+dataLen]) - } - var r uint32 - for _, b := range payload[dataPos : dataPos+dataLen] { - r = (r << 8) | uint32(b) - } - return dataPos + dataLen, r, nil -} - -// U256 parses uint256 number from given payload at given position -func U256(payload []byte, pos int, x *uint256.Int) (int, error) { - dataPos, dataLen, err := String(payload, pos) - if err != nil { - return 0, err - } - if dataLen > 32 { - return 0, fmt.Errorf("%w: uint256 must not be more than 32 bytes long, got %d", ErrParse, dataLen) - } - if dataLen > 0 && payload[dataPos] == 0 { - return 0, fmt.Errorf("%w: integer encoding for RLP must not have leading zeros: %x", ErrParse, payload[dataPos:dataPos+dataLen]) - } - x.SetBytes(payload[dataPos : dataPos+dataLen]) - return dataPos + dataLen, nil -} - -func U256Len(z *uint256.Int) int { - if z == nil { - return 1 - } - nBits := z.BitLen() - if nBits == 0 { - return 1 - } - if nBits <= 7 { - return 1 - } - return 1 + common.BitLenToByteLen(nBits) -} - -func ParseHash(payload []byte, pos int, hashbuf []byte) (int, error) { - pos, err := StringOfLen(payload, pos, 32) - if err != nil { - return 0, fmt.Errorf("%s: hash len: %w", ParseHashErrorPrefix, err) - } - copy(hashbuf, payload[pos:pos+32]) - return pos + 32, nil -} - -const ParseHashErrorPrefix = "parse hash payload" - -const ParseAnnouncementsErrorPrefix = "parse announcement payload" - -func ParseAnnouncements(payload []byte, pos int) ([]byte, []uint32, []byte, int, error) { - pos, totalLen, err := List(payload, pos) - if err != nil { - return nil, nil, nil, pos, err - } - if pos+totalLen > len(payload) { - return nil, nil, nil, pos, fmt.Errorf("%s: totalLen %d is beyond the end of payload", ParseAnnouncementsErrorPrefix, totalLen) - } - pos, typesLen, err := String(payload, pos) - if err != nil { - return nil, nil, nil, pos, err - } - if pos+typesLen > len(payload) { - return nil, nil, nil, pos, fmt.Errorf("%s: typesLen %d is beyond the end of payload", ParseAnnouncementsErrorPrefix, typesLen) - } - types := payload[pos : pos+typesLen] - pos += typesLen - pos, sizesLen, err := List(payload, pos) - if err != nil { - return nil, nil, nil, pos, err - } - if pos+sizesLen > len(payload) { - return nil, nil, nil, pos, fmt.Errorf("%s: sizesLen %d is beyond the end of payload", ParseAnnouncementsErrorPrefix, sizesLen) - } - sizes := make([]uint32, typesLen) - for i := 0; i < len(sizes); i++ { - if pos, sizes[i], err = U32(payload, pos); err != nil { - return nil, nil, nil, pos, err - } - } - pos, hashesLen, err := List(payload, pos) - if err != nil { - return nil, nil, nil, pos, err - } - if pos+hashesLen > len(payload) { - return nil, nil, nil, pos, fmt.Errorf("%s: hashesLen %d is beyond the end of payload", ParseAnnouncementsErrorPrefix, hashesLen) - } - hashes := make([]byte, 32*(hashesLen/33)) - for i := 0; i < len(hashes); i += 32 { - if pos, err = ParseHash(payload, pos, hashes[i:]); err != nil { - return nil, nil, nil, pos, err - } - } - return types, sizes, hashes, pos, nil -} diff --git a/erigon-lib/rlp/parse_test.go b/erigon-lib/rlp/parse_test.go deleted file mode 100644 index a100d77afb5..00000000000 --- a/erigon-lib/rlp/parse_test.go +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2024 The Erigon Authors -// This file is part of Erigon. -// -// Erigon is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Erigon is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Erigon. If not, see . - -package rlp - -import ( - "fmt" - "testing" - - "github.com/holiman/uint256" - "github.com/stretchr/testify/assert" - - "github.com/erigontech/erigon-lib/common/hexutility" -) - -var parseU64Tests = []struct { - expectErr error - payload []byte - expectPos int - expectRes uint64 -}{ - {payload: hexutility.MustDecodeHex("820400"), expectPos: 3, expectRes: 1024}, - {payload: hexutility.MustDecodeHex("07"), expectPos: 1, expectRes: 7}, - {payload: hexutility.MustDecodeHex("8107"), expectErr: fmt.Errorf("%w: non-canonical size information", ErrParse)}, - {payload: hexutility.MustDecodeHex("B8020004"), expectErr: fmt.Errorf("%w: non-canonical size information", ErrParse)}, - {payload: hexutility.MustDecodeHex("C0"), expectErr: fmt.Errorf("%w: uint64 must be a string, not isList", ErrParse)}, - {payload: hexutility.MustDecodeHex("00"), expectErr: fmt.Errorf("%w: integer encoding for RLP must not have leading zeros: 00", ErrParse)}, - {payload: hexutility.MustDecodeHex("8AFFFFFFFFFFFFFFFFFF7C"), expectErr: fmt.Errorf("%w: uint64 must not be more than 8 bytes long, got 10", ErrParse)}, -} - -var parseU32Tests = []struct { - expectErr error - payload []byte - expectPos int - expectRes uint32 -}{ - {payload: hexutility.MustDecodeHex("820400"), expectPos: 3, expectRes: 1024}, - {payload: hexutility.MustDecodeHex("07"), expectPos: 1, expectRes: 7}, - {payload: hexutility.MustDecodeHex("8107"), expectErr: fmt.Errorf("%w: non-canonical size information", ErrParse)}, - {payload: hexutility.MustDecodeHex("B8020004"), expectErr: fmt.Errorf("%w: non-canonical size information", ErrParse)}, - {payload: hexutility.MustDecodeHex("C0"), expectErr: fmt.Errorf("%w: uint32 must be a string, not isList", ErrParse)}, - {payload: hexutility.MustDecodeHex("00"), expectErr: fmt.Errorf("%w: integer encoding for RLP must not have leading zeros: 00", ErrParse)}, - {payload: hexutility.MustDecodeHex("85FF6738FF7C"), expectErr: fmt.Errorf("%w: uint32 must not be more than 4 bytes long, got 5", ErrParse)}, -} - -var parseU256Tests = []struct { - expectErr error - expectRes *uint256.Int - payload []byte - expectPos int -}{ - {payload: hexutility.MustDecodeHex("8BFFFFFFFFFFFFFFFFFF7C"), expectErr: fmt.Errorf("%w: unexpected end of payload", ErrParse)}, - {payload: hexutility.MustDecodeHex("8AFFFFFFFFFFFFFFFFFF7C"), expectPos: 11, expectRes: new(uint256.Int).SetBytes(hexutility.MustDecodeHex("FFFFFFFFFFFFFFFFFF7C"))}, - {payload: hexutility.MustDecodeHex("85CE05050505"), expectPos: 6, expectRes: new(uint256.Int).SetUint64(0xCE05050505)}, - {payload: hexutility.MustDecodeHex("820400"), expectPos: 3, expectRes: new(uint256.Int).SetUint64(1024)}, - {payload: hexutility.MustDecodeHex("07"), expectPos: 1, expectRes: new(uint256.Int).SetUint64(7)}, - {payload: hexutility.MustDecodeHex("8107"), expectErr: fmt.Errorf("%w: non-canonical size information", ErrParse)}, - {payload: hexutility.MustDecodeHex("B8020004"), expectErr: fmt.Errorf("%w: non-canonical size information", ErrParse)}, - {payload: hexutility.MustDecodeHex("C0"), expectErr: fmt.Errorf("%w: must be a string, instead of a list", ErrParse)}, - {payload: hexutility.MustDecodeHex("00"), expectErr: fmt.Errorf("%w: integer encoding for RLP must not have leading zeros: 00", ErrParse)}, - {payload: hexutility.MustDecodeHex("A101000000000000000000000000000000000000008B000000000000000000000000"), expectErr: fmt.Errorf("%w: uint256 must not be more than 32 bytes long, got 33", ErrParse)}, -} - -func TestPrimitives(t *testing.T) { - for i, tt := range parseU64Tests { - t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - assert := assert.New(t) - pos, res, err := U64(tt.payload, 0) - assert.Equal(tt.expectErr, err) - assert.Equal(tt.expectPos, pos) - assert.Equal(tt.expectRes, res) - }) - } - for i, tt := range parseU32Tests { - t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - assert := assert.New(t) - pos, res, err := U32(tt.payload, 0) - assert.Equal(tt.expectErr, err) - assert.Equal(tt.expectPos, pos) - assert.Equal(tt.expectRes, res) - }) - } - for i, tt := range parseU256Tests { - t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - assert := assert.New(t) - res := new(uint256.Int) - pos, err := U256(tt.payload, 0, res) - assert.Equal(tt.expectErr, err) - assert.Equal(tt.expectPos, pos) - if err == nil { - assert.Equal(tt.expectRes, res) - } - }) - } -} diff --git a/erigon-lib/rlp2/decoder.go b/erigon-lib/rlp2/decoder.go deleted file mode 100644 index 34b8e5821cd..00000000000 --- a/erigon-lib/rlp2/decoder.go +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright 2024 The Erigon Authors -// This file is part of Erigon. -// -// Erigon is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Erigon is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Erigon. If not, see . - -package rlp - -import ( - "errors" - "fmt" - "io" -) - -type Decoder struct { - buf *buf -} - -func NewDecoder(buf []byte) *Decoder { - return &Decoder{ - buf: newBuf(buf, 0), - } -} - -func (d *Decoder) String() string { - return fmt.Sprintf(`left=%x pos=%d`, d.buf.Bytes(), d.buf.off) -} - -func (d *Decoder) Consumed() []byte { - return d.buf.u[:d.buf.off] -} - -func (d *Decoder) Underlying() []byte { - return d.buf.Underlying() -} - -func (d *Decoder) Empty() bool { - return d.buf.empty() -} - -func (d *Decoder) Offset() int { - return d.buf.Offset() -} - -func (d *Decoder) Bytes() []byte { - return d.buf.Bytes() -} - -func (d *Decoder) ReadByte() (n byte, err error) { - return d.buf.ReadByte() -} - -func (d *Decoder) PeekByte() (n byte, err error) { - return d.buf.PeekByte() -} - -func (d *Decoder) Rebase() { - d.buf.u = d.Bytes() - d.buf.off = 0 -} -func (d *Decoder) Fork() *Decoder { - return &Decoder{ - buf: newBuf(d.buf.u, d.buf.off), - } -} - -func (d *Decoder) PeekToken() (Token, error) { - prefix, err := d.PeekByte() - if err != nil { - return TokenUnknown, err - } - return identifyToken(prefix), nil -} - -func (d *Decoder) ElemDec() (*Decoder, Token, error) { - a, t, err := d.Elem() - return NewDecoder(a), t, err -} - -func (d *Decoder) RawElemDec() (*Decoder, Token, error) { - a, t, err := d.RawElem() - return NewDecoder(a), t, err -} - -func (d *Decoder) RawElem() ([]byte, Token, error) { - w := d.buf - start := w.Offset() - // figure out what we are reading - prefix, err := w.ReadByte() - if err != nil { - return nil, TokenUnknown, err - } - token := identifyToken(prefix) - - var ( - sz int - lenSz int - ) - // switch on the token - switch token { - case TokenDecimal: - // in this case, the value is just the byte itself - case TokenShortList: - sz = int(token.Diff(prefix)) - _, err = nextFull(w, sz) - case TokenLongList: - lenSz = int(token.Diff(prefix)) - sz, err = nextBeInt(w, lenSz) - if err != nil { - return nil, token, err - } - _, err = nextFull(w, sz) - case TokenShortBlob: - sz := int(token.Diff(prefix)) - _, err = nextFull(w, sz) - case TokenLongBlob: - lenSz := int(token.Diff(prefix)) - sz, err = nextBeInt(w, lenSz) - if err != nil { - return nil, token, err - } - _, err = nextFull(w, sz) - default: - return nil, token, fmt.Errorf("%w: unknown token", ErrDecode) - } - stop := w.Offset() - //log.Printf("%x %s\n", buf, token) - if err != nil { - return nil, token, err - } - return w.Underlying()[start:stop], token, nil -} - -func (d *Decoder) Elem() ([]byte, Token, error) { - w := d.buf - // figure out what we are reading - prefix, err := w.ReadByte() - if err != nil { - return nil, TokenUnknown, err - } - token := identifyToken(prefix) - - var ( - buf []byte - sz int - lenSz int - ) - // switch on the token - switch token { - case TokenDecimal: - // in this case, the value is just the byte itself - buf = []byte{prefix} - case TokenShortList: - sz = int(token.Diff(prefix)) - buf, err = nextFull(w, sz) - case TokenLongList: - lenSz = int(token.Diff(prefix)) - sz, err = nextBeInt(w, lenSz) - if err != nil { - return nil, token, err - } - buf, err = nextFull(w, sz) - case TokenShortBlob: - sz := int(token.Diff(prefix)) - buf, err = nextFull(w, sz) - case TokenLongBlob: - lenSz := int(token.Diff(prefix)) - sz, err = nextBeInt(w, lenSz) - if err != nil { - return nil, token, err - } - buf, err = nextFull(w, sz) - default: - return nil, token, fmt.Errorf("%w: unknown token", ErrDecode) - } - //log.Printf("%x %s\n", buf, token) - if err != nil { - return nil, token, fmt.Errorf("read data: %w", err) - } - return buf, token, nil -} - -func ReadElem[T any](d *Decoder, fn func(*T, []byte) error, receiver *T) error { - buf, token, err := d.Elem() - if err != nil { - return err - } - switch token { - case TokenDecimal, - TokenShortBlob, - TokenLongBlob, - TokenShortList, - TokenLongList: - return fn(receiver, buf) - default: - return fmt.Errorf("%w: ReadElem found unexpected token", ErrDecode) - } -} - -func (d *Decoder) ForList(fn func(*Decoder) error) error { - // grab the list bytes - buf, token, err := d.Elem() - if err != nil { - return err - } - switch token { - case TokenShortList, TokenLongList: - dec := NewDecoder(buf) - for { - if dec.buf.Len() == 0 { - return nil - } - err := fn(dec) - if errors.Is(err, io.EOF) { - return nil - } - if err != nil { - return err - } - // reset the byte - dec = NewDecoder(dec.Bytes()) - } - default: - return fmt.Errorf("%w: ForList on non-list", ErrDecode) - } -} - -type buf struct { - u []byte - off int -} - -func newBuf(u []byte, off int) *buf { - return &buf{u: u, off: off} -} - -func (b *buf) empty() bool { return len(b.u) <= b.off } - -func (b *buf) PeekByte() (n byte, err error) { - if len(b.u) <= b.off { - return 0, io.EOF - } - return b.u[b.off], nil -} -func (b *buf) ReadByte() (n byte, err error) { - if len(b.u) <= b.off { - return 0, io.EOF - } - b.off++ - return b.u[b.off-1], nil -} - -func (b *buf) Next(n int) (xs []byte) { - m := b.Len() - if n > m { - n = m - } - data := b.u[b.off : b.off+n] - b.off += n - return data -} - -func (b *buf) Offset() int { - return b.off -} - -func (b *buf) Bytes() []byte { - return b.u[b.off:] -} - -func (b *buf) String() string { - if b == nil { - // Special case, useful in debugging. - return "" - } - return string(b.u[b.off:]) -} - -func (b *buf) Len() int { return len(b.u) - b.off } - -func (b *buf) Underlying() []byte { - return b.u -} diff --git a/erigon-lib/rlp/encode.go b/erigon-lib/rlp2/encode.go similarity index 100% rename from erigon-lib/rlp/encode.go rename to erigon-lib/rlp2/encode.go diff --git a/erigon-lib/rlp/encode_test.go b/erigon-lib/rlp2/encode_test.go similarity index 100% rename from erigon-lib/rlp/encode_test.go rename to erigon-lib/rlp2/encode_test.go diff --git a/erigon-lib/rlp2/encodel.go b/erigon-lib/rlp2/encodel.go deleted file mode 100644 index 2a706ff76b7..00000000000 --- a/erigon-lib/rlp2/encodel.go +++ /dev/null @@ -1,298 +0,0 @@ -// Copyright 2021 The Erigon Authors -// This file is part of Erigon. -// -// Erigon is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Erigon is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Erigon. If not, see . - -package rlp - -import ( - "encoding/binary" - "math/bits" - - "github.com/erigontech/erigon-lib/common" -) - -// General design: -// - rlp package doesn't manage memory - and Caller must ensure buffers are big enough. -// - no io.Writer, because it's incompatible with binary.BigEndian functions and Writer can't be used as temporary buffer -// -// Composition: -// - each Encode method does write to given buffer and return written len -// - each Parse accept position in payload and return new position -// -// General rules: -// - functions to calculate prefix len are fast (and pure). it's ok to call them multiple times during encoding of large object for readability. -// - rlp has 2 data types: List and String (bytes array), and low-level funcs are operate with this types. -// - but for convenience and performance - provided higher-level functions (for example for EncodeHash - for []byte of len 32) -// - functions to Parse (Decode) data - using data type as name (without any prefix): rlp.String(), rlp.List, rlp.U64(), rlp.U256() -// - -func ListPrefixLen(dataLen int) int { - if dataLen >= 56 { - return 1 + common.BitLenToByteLen(bits.Len64(uint64(dataLen))) - } - return 1 -} -func EncodeListPrefix(dataLen int, to []byte) int { - if dataLen >= 56 { - _ = to[9] - beLen := common.BitLenToByteLen(bits.Len64(uint64(dataLen))) - binary.BigEndian.PutUint64(to[1:], uint64(dataLen)) - to[8-beLen] = 247 + byte(beLen) - copy(to, to[8-beLen:9]) - return 1 + beLen - } - to[0] = 192 + byte(dataLen) - return 1 -} - -func U32Len(i uint32) int { - if i < 128 { - return 1 - } - return 1 + common.BitLenToByteLen(bits.Len32(i)) -} - -func U64Len(i uint64) int { - if i < 128 { - return 1 - } - return 1 + common.BitLenToByteLen(bits.Len64(i)) -} - -func EncodeU32(i uint32, to []byte) int { - if i == 0 { - to[0] = 128 - return 1 - } - if i < 128 { - to[0] = byte(i) // fits single byte - return 1 - } - - b := to[1:] - var l int - - // writes i to b in big endian byte order, using the least number of bytes needed to represent i. - switch { - case i < (1 << 8): - b[0] = byte(i) - l = 1 - case i < (1 << 16): - b[0] = byte(i >> 8) - b[1] = byte(i) - l = 2 - case i < (1 << 24): - b[0] = byte(i >> 16) - b[1] = byte(i >> 8) - b[2] = byte(i) - l = 3 - default: - b[0] = byte(i >> 24) - b[1] = byte(i >> 16) - b[2] = byte(i >> 8) - b[3] = byte(i) - l = 4 - } - - to[0] = 128 + byte(l) - return 1 + l -} - -func EncodeU64(i uint64, to []byte) int { - if i == 0 { - to[0] = 128 - return 1 - } - if i < 128 { - to[0] = byte(i) // fits single byte - return 1 - } - - b := to[1:] - var l int - - // writes i to b in big endian byte order, using the least number of bytes needed to represent i. - switch { - case i < (1 << 8): - b[0] = byte(i) - l = 1 - case i < (1 << 16): - b[0] = byte(i >> 8) - b[1] = byte(i) - l = 2 - case i < (1 << 24): - b[0] = byte(i >> 16) - b[1] = byte(i >> 8) - b[2] = byte(i) - l = 3 - case i < (1 << 32): - b[0] = byte(i >> 24) - b[1] = byte(i >> 16) - b[2] = byte(i >> 8) - b[3] = byte(i) - l = 4 - case i < (1 << 40): - b[0] = byte(i >> 32) - b[1] = byte(i >> 24) - b[2] = byte(i >> 16) - b[3] = byte(i >> 8) - b[4] = byte(i) - l = 5 - case i < (1 << 48): - b[0] = byte(i >> 40) - b[1] = byte(i >> 32) - b[2] = byte(i >> 24) - b[3] = byte(i >> 16) - b[4] = byte(i >> 8) - b[5] = byte(i) - l = 6 - case i < (1 << 56): - b[0] = byte(i >> 48) - b[1] = byte(i >> 40) - b[2] = byte(i >> 32) - b[3] = byte(i >> 24) - b[4] = byte(i >> 16) - b[5] = byte(i >> 8) - b[6] = byte(i) - l = 7 - default: - b[0] = byte(i >> 56) - b[1] = byte(i >> 48) - b[2] = byte(i >> 40) - b[3] = byte(i >> 32) - b[4] = byte(i >> 24) - b[5] = byte(i >> 16) - b[6] = byte(i >> 8) - b[7] = byte(i) - l = 8 - } - - to[0] = 128 + byte(l) - return 1 + l -} - -func StringLen(s []byte) int { - sLen := len(s) - switch { - case sLen > 56: - beLen := common.BitLenToByteLen(bits.Len(uint(sLen))) - return 1 + beLen + sLen - case sLen == 0: - return 1 - case sLen == 1: - if s[0] < 128 { - return 1 - } - return 1 + sLen - default: // 1 56: - beLen := common.BitLenToByteLen(bits.Len(uint(len(s)))) - binary.BigEndian.PutUint64(to[1:], uint64(len(s))) - _ = to[beLen+len(s)] - - to[8-beLen] = byte(beLen) + 183 - copy(to, to[8-beLen:9]) - copy(to[1+beLen:], s) - return 1 + beLen + len(s) - case len(s) == 0: - to[0] = 128 - return 1 - case len(s) == 1: - if s[0] < 128 { - to[0] = s[0] - return 1 - } - to[0] = 129 - to[1] = s[0] - return 2 - default: // 1. - -package rlp - -import ( - "golang.org/x/exp/constraints" -) - -type EncoderFunc = func(i *Encoder) *Encoder - -type Encoder struct { - buf []byte -} - -func NewEncoder(buf []byte) *Encoder { - return &Encoder{ - buf: buf, - } -} - -// Buffer returns the underlying buffer -func (e *Encoder) Buffer() []byte { - return e.buf -} - -func (e *Encoder) Byte(p byte) *Encoder { - e.buf = append(e.buf, p) - return e -} - -func (e *Encoder) Bytes(p []byte) *Encoder { - e.buf = append(e.buf, p...) - return e -} - -// Str will write a string correctly -func (e *Encoder) Str(str []byte) *Encoder { - if len(str) > 55 { - return e.LongString(str) - } - return e.ShortString(str) -} - -// String will assume your string is less than 56 bytes long, and do no validation as such -func (e *Encoder) ShortString(str []byte) *Encoder { - return e.Byte(TokenShortBlob.Plus(byte(len(str)))).Bytes(str) -} - -// String will assume your string is greater than 55 bytes long, and do no validation as such -func (e *Encoder) LongString(str []byte) *Encoder { - // write the indicator token - e.Byte(byte(TokenLongBlob)) - // write the integer, knowing that we appended n bytes - n := putUint(e, len(str)) - // so we knw the indicator token was n+1 bytes ago. - e.buf[len(e.buf)-(int(n)+1)] += n - // and now add the actual length - e.buf = append(e.buf, str...) - return e -} - -// List will attempt to write the list of encoder funcs to the buf -func (e *Encoder) List(items ...EncoderFunc) *Encoder { - return e.writeList(true, items...) -} - -// ShortList actually calls List -func (e *Encoder) ShortList(items ...EncoderFunc) *Encoder { - return e.writeList(true, items...) -} - -// LongList will assume that your list payload is more than 55 bytes long, and do no validation as such -func (e *Encoder) LongList(items ...EncoderFunc) *Encoder { - return e.writeList(false, items...) -} - -// writeList will first attempt to write a long list with the dat -// if validate is false, it will just format it like the length is above 55 -// if validate is true, it will format it like it is a shrot list -func (e *Encoder) writeList(validate bool, items ...EncoderFunc) *Encoder { - // write the indicator token - e = e.Byte(byte(TokenLongList)) - // now pad 8 bytes - e = e.Bytes(make([]byte, 8)) - // record the length before encoding items - startLength := len(e.buf) - // now write all the items - for _, v := range items { - e = v(e) - } - // the size is the difference in the lengths now - dataSize := len(e.buf) - startLength - if dataSize <= 55 && validate { - // oh it's actually a short string! awkward. let's set that then. - e.buf[startLength-8-1] = TokenShortList.Plus(byte(dataSize)) - // and then copy the data over - copy(e.buf[startLength-8:], e.buf[startLength:startLength+dataSize]) - // and now set the new size - e.buf = e.buf[:startLength+dataSize-8] - // we are done, return - return e - } - // ok, so it's a long string. - // create a new encoder centered at startLength - 8 - enc := NewEncoder(e.buf[startLength-8:]) - // now write using that encoder the size - n := putUint(enc, dataSize) - // and update the token, which we know is at startLength-8-1 - e.buf[startLength-8-1] += n - // the shift to perform now is 8 - n. - shift := int(8 - n) - // if there is a positive shift, then we must perform the shift - if shift > 0 { - // copy the data - copy(e.buf[startLength-shift:], e.buf[startLength:startLength+dataSize]) - // set the new length - e.buf = e.buf[:startLength-shift+dataSize] - } - return e -} - -func putUint[T constraints.Integer](e *Encoder, t T) (size byte) { - i := uint64(t) - switch { - case i < (1 << 8): - e.buf = append(e.buf, byte(i)) - return 1 - case i < (1 << 16): - e.buf = append(e.buf, - byte(i>>8), - byte(i), - ) - return 2 - case i < (1 << 24): - - e.buf = append(e.buf, - byte(i>>16), - byte(i>>8), - byte(i), - ) - return 3 - case i < (1 << 32): - e.buf = append(e.buf, - byte(i>>24), - byte(i>>16), - byte(i>>8), - byte(i), - ) - return 4 - case i < (1 << 40): - e.buf = append(e.buf, - byte(i>>32), - byte(i>>24), - byte(i>>16), - byte(i>>8), - byte(i), - ) - return 5 - case i < (1 << 48): - e.buf = append(e.buf, - byte(i>>40), - byte(i>>32), - byte(i>>24), - byte(i>>16), - byte(i>>8), - byte(i), - ) - return 6 - case i < (1 << 56): - e.buf = append(e.buf, - byte(i>>48), - byte(i>>40), - byte(i>>32), - byte(i>>24), - byte(i>>16), - byte(i>>8), - byte(i), - ) - return 7 - default: - e.buf = append(e.buf, - byte(i>>56), - byte(i>>48), - byte(i>>40), - byte(i>>32), - byte(i>>24), - byte(i>>16), - byte(i>>8), - byte(i), - ) - return 8 - } -} diff --git a/erigon-lib/rlp2/parse.go b/erigon-lib/rlp2/parse.go index bda7935e15a..09545f4347e 100644 --- a/erigon-lib/rlp2/parse.go +++ b/erigon-lib/rlp2/parse.go @@ -26,10 +26,9 @@ import ( ) var ( - ErrBase = errors.New("rlp") - ErrParse = fmt.Errorf("%w parse", ErrBase) - ErrDecode = fmt.Errorf("%w decode", ErrBase) - ErrUnexpectedEOF = fmt.Errorf("%w EOF", ErrBase) + ErrBase = errors.New("rlp") + ErrParse = fmt.Errorf("%w parse", ErrBase) + ErrDecode = fmt.Errorf("%w decode", ErrBase) ) func IsRLPError(err error) bool { return errors.Is(err, ErrBase) } @@ -37,8 +36,8 @@ func IsRLPError(err error) bool { return errors.Is(err, ErrBase) } // BeInt parses Big Endian representation of an integer from given payload at given position func BeInt(payload []byte, pos, length int) (int, error) { var r int - if pos+length > len(payload) { - return 0, ErrUnexpectedEOF + if pos+length >= len(payload) { + return 0, fmt.Errorf("%w: unexpected end of payload", ErrParse) } if length > 0 && payload[pos] == 0 { return 0, fmt.Errorf("%w: integer encoding for RLP must not have leading zeros: %x", ErrParse, payload[pos:pos+length]) diff --git a/erigon-lib/rlp2/readme.md b/erigon-lib/rlp2/readme.md deleted file mode 100644 index 74e9f96eeb4..00000000000 --- a/erigon-lib/rlp2/readme.md +++ /dev/null @@ -1,11 +0,0 @@ -## rlp - - -TERMINOLOGY: - -``` -RLP string = "Blob" // this is so we don't conflict with existing go name for String -RLP list = "List" // luckily we can keep using list name since go doesn't use it -RLP single byte number = "Decimal" // for numbers from 1-127. a special case -``` - diff --git a/erigon-lib/rlp2/types.go b/erigon-lib/rlp2/types.go deleted file mode 100644 index 2bf3c6438f3..00000000000 --- a/erigon-lib/rlp2/types.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2024 The Erigon Authors -// This file is part of Erigon. -// -// Erigon is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Erigon is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Erigon. If not, see . - -package rlp - -import ( - "fmt" - - "github.com/holiman/uint256" -) - -func Bytes(dst *[]byte, src []byte) error { - if len(*dst) < len(src) { - (*dst) = make([]byte, len(src)) - } - copy(*dst, src) - return nil -} -func BytesExact(dst *[]byte, src []byte) error { - if len(*dst) != len(src) { - return fmt.Errorf("%w: BytesExact no match", ErrDecode) - } - copy(*dst, src) - return nil -} - -func Uint256(dst *uint256.Int, src []byte) error { - if len(src) > 32 { - return fmt.Errorf("%w: uint256 must not be more than 32 bytes long, got %d", ErrParse, len(src)) - } - if len(src) > 0 && src[0] == 0 { - return fmt.Errorf("%w: integer encoding for RLP must not have leading zeros: %x", ErrParse, src) - } - dst.SetBytes(src) - return nil -} - -func Uint64(dst *uint64, src []byte) error { - var r uint64 - for _, b := range src { - r = (r << 8) | uint64(b) - } - (*dst) = r - return nil -} - -func IsEmpty(dst *bool, src []byte) error { - if len(src) == 0 { - (*dst) = true - } else { - (*dst) = false - } - return nil -} -func BlobLength(dst *int, src []byte) error { - (*dst) = len(src) - return nil -} - -func Skip(dst *int, src []byte) error { - return nil -} diff --git a/erigon-lib/rlp2/unmarshaler.go b/erigon-lib/rlp2/unmarshaler.go deleted file mode 100644 index 7bcb547e28e..00000000000 --- a/erigon-lib/rlp2/unmarshaler.go +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2024 The Erigon Authors -// This file is part of Erigon. -// -// Erigon is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Erigon is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Erigon. If not, see . - -package rlp - -import ( - "fmt" - "reflect" -) - -type Unmarshaler interface { - UnmarshalRLP(data []byte) error -} - -func Unmarshal(data []byte, val any) error { - buf := newBuf(data, 0) - return unmarshal(buf, val) -} - -func unmarshal(buf *buf, val any) error { - rv := reflect.ValueOf(val) - if rv.Kind() != reflect.Pointer || rv.IsNil() { - return fmt.Errorf("%w: v must be ptr", ErrDecode) - } - v := rv.Elem() - err := reflectAny(buf, v, rv) - if err != nil { - return fmt.Errorf("%w: %w", ErrDecode, err) - } - return nil -} - -func reflectAny(w *buf, v reflect.Value, rv reflect.Value) error { - if um, ok := rv.Interface().(Unmarshaler); ok { - return um.UnmarshalRLP(w.Bytes()) - } - // figure out what we are reading - prefix, err := w.ReadByte() - if err != nil { - return err - } - token := identifyToken(prefix) - // switch - switch token { - case TokenDecimal: - // in this case, the value is just the byte itself - switch v.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - v.SetInt(int64(prefix)) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - v.SetUint(uint64(prefix)) - case reflect.Invalid: - // do nothing - default: - return fmt.Errorf("%w: decimal must be unmarshal into integer type", ErrDecode) - } - case TokenShortBlob: - sz := int(token.Diff(prefix)) - str, err := nextFull(w, sz) - if err != nil { - return err - } - return putBlob(str, v, rv) - case TokenLongBlob: - lenSz := int(token.Diff(prefix)) - sz, err := nextBeInt(w, lenSz) - if err != nil { - return err - } - str, err := nextFull(w, sz) - if err != nil { - return err - } - return putBlob(str, v, rv) - case TokenShortList: - sz := int(token.Diff(prefix)) - buf, err := nextFull(w, sz) - if err != nil { - return err - } - return reflectList(newBuf(buf, 0), v, rv) - case TokenLongList: - lenSz := int(token.Diff(prefix)) - sz, err := nextBeInt(w, lenSz) - if err != nil { - return err - } - buf, err := nextFull(w, sz) - if err != nil { - return err - } - return reflectList(newBuf(buf, 0), v, rv) - case TokenUnknown: - return fmt.Errorf("%w: unknown token", ErrDecode) - } - return nil -} - -func putBlob(w []byte, v reflect.Value, rv reflect.Value) error { - switch v.Kind() { - case reflect.String: - v.SetString(string(w)) - case reflect.Slice: - if v.Type().Elem().Kind() != reflect.Uint8 { - return fmt.Errorf("%w: need to use uint8 as underlying if want slice output from longstring", ErrDecode) - } - v.SetBytes(w) - case reflect.Array: - if v.Type().Elem().Kind() != reflect.Uint8 { - return fmt.Errorf("%w: need to use uint8 as underlying if want array output from longstring", ErrDecode) - } - reflect.Copy(v, reflect.ValueOf(w)) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - val, err := BeInt(w, 0, len(w)) - if err != nil { - return err - } - v.SetInt(int64(val)) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - val, err := BeInt(w, 0, len(w)) - if err != nil { - return err - } - v.SetUint(uint64(val)) - case reflect.Invalid: - // do nothing - return nil - } - return nil -} - -func reflectList(w *buf, v reflect.Value, rv reflect.Value) error { - switch v.Kind() { - case reflect.Invalid: - // do nothing - return nil - case reflect.Map: - rv1 := reflect.New(v.Type().Key()) - v1 := rv1.Elem() - err := reflectAny(w, v1, rv1) - if err != nil { - return err - } - rv2 := reflect.New(v.Type().Elem()) - v2 := rv2.Elem() - err = reflectAny(w, v2, rv2) - if err != nil { - return err - } - v.SetMapIndex(rv1, rv2) - case reflect.Struct: - for idx := 0; idx < v.NumField(); idx++ { - // Decode into element. - rv1 := v.Field(idx).Addr() - rt1 := v.Type().Field(idx) - v1 := rv1.Elem() - shouldSet := rt1.IsExported() - if shouldSet { - err := reflectAny(w, v1, rv1) - if err != nil { - return err - } - } - } - case reflect.Array, reflect.Slice: - idx := 0 - for { - if idx >= v.Cap() { - v.Grow(1) - } - if idx >= v.Len() { - v.SetLen(idx + 1) - } - if idx < v.Len() { - // Decode into element. - rv1 := v.Index(idx) - v1 := rv1.Elem() - err := reflectAny(w, v1, rv1) - if err != nil { - return err - } - } else { - // Ran out of fixed array: skip. - rv1 := reflect.Value{} - err := reflectAny(w, rv1, rv1) - if err != nil { - return err - } - } - idx++ - } - } - return nil -} diff --git a/erigon-lib/rlp2/unmarshaler_test.go b/erigon-lib/rlp2/unmarshaler_test.go deleted file mode 100644 index 3887f93d011..00000000000 --- a/erigon-lib/rlp2/unmarshaler_test.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2024 The Erigon Authors -// This file is part of Erigon. -// -// Erigon is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Erigon is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Erigon. If not, see . - -package rlp_test - -import ( - "testing" - - rlp "github.com/erigontech/erigon-lib/rlp2" - "github.com/stretchr/testify/require" -) - -type plusOne int - -func (p *plusOne) UnmarshalRLP(data []byte) error { - var s int - err := rlp.Unmarshal(data, &s) - if err != nil { - return err - } - (*p) = plusOne(s + 1) - return nil -} - -func TestDecoder(t *testing.T) { - - type simple struct { - Key string - Value string - } - - t.Run("ShortString", func(t *testing.T) { - t.Run("ToString", func(t *testing.T) { - bts := []byte{0x83, 'd', 'o', 'g'} - var s string - err := rlp.Unmarshal(bts, &s) - require.NoError(t, err) - require.EqualValues(t, "dog", s) - }) - t.Run("ToBytes", func(t *testing.T) { - bts := []byte{0x83, 'd', 'o', 'g'} - var s []byte - err := rlp.Unmarshal(bts, &s) - require.NoError(t, err) - require.EqualValues(t, []byte("dog"), s) - }) - t.Run("ToInt", func(t *testing.T) { - bts := []byte{0x82, 0x04, 0x00} - var s int - err := rlp.Unmarshal(bts, &s) - require.NoError(t, err) - require.EqualValues(t, 1024, s) - }) - t.Run("ToIntUnmarshaler", func(t *testing.T) { - bts := []byte{0x82, 0x04, 0x00} - var s plusOne - err := rlp.Unmarshal(bts, &s) - require.NoError(t, err) - require.EqualValues(t, plusOne(1025), s) - }) - t.Run("ToSimpleStruct", func(t *testing.T) { - bts := []byte{0xc8, 0x83, 'c', 'a', 't', 0x83, 'd', 'o', 'g'} - var s simple - err := rlp.Unmarshal(bts, &s) - require.NoError(t, err) - require.EqualValues(t, simple{Key: "cat", Value: "dog"}, s) - }) - }) -} diff --git a/erigon-lib/rlp2/util.go b/erigon-lib/rlp2/util.go deleted file mode 100644 index c3d1de93d81..00000000000 --- a/erigon-lib/rlp2/util.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2024 The Erigon Authors -// This file is part of Erigon. -// -// Erigon is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Erigon is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with Erigon. If not, see . - -package rlp - -type Token int32 - -func (T Token) String() string { - switch T { - case TokenDecimal: - return "decimal" - case TokenShortBlob: - return "short_blob" - case TokenLongBlob: - return "long_blob" - case TokenShortList: - return "short_list" - case TokenLongList: - return "long_list" - case TokenEOF: - return "eof" - case TokenUnknown: - return "unknown" - default: - return "nan" - } -} - -func (T Token) Plus(n byte) byte { - return byte(T) + n -} - -func (T Token) Diff(n byte) byte { - return n - byte(T) -} - -func (T Token) IsListType() bool { - return T == TokenLongList || T == TokenShortList -} - -func (T Token) IsBlobType() bool { - return T == TokenLongBlob || T == TokenShortBlob -} - -const ( - TokenDecimal Token = 0x00 - TokenShortBlob Token = 0x80 - TokenLongBlob Token = 0xb7 - TokenShortList Token = 0xc0 - TokenLongList Token = 0xf7 - - TokenUnknown Token = 0xff01 - TokenEOF Token = 0xdead -) - -func identifyToken(b byte) Token { - switch { - case b <= 127: - return TokenDecimal - case b >= 128 && b <= 183: - return TokenShortBlob - case b >= 184 && b <= 191: - return TokenLongBlob - case b >= 192 && b <= 247: - return TokenShortList - case b >= 248: - return TokenLongList - } - return TokenUnknown -} - -// BeInt parses Big Endian representation of an integer from given payload at given position -func nextBeInt(w *buf, length int) (int, error) { - dat, err := nextFull(w, length) - if err != nil { - return 0, ErrUnexpectedEOF - } - return BeInt(dat, 0, length) -} - -func nextFull(dat *buf, size int) ([]byte, error) { - d := dat.Next(size) - if len(d) != size { - return nil, ErrUnexpectedEOF - } - return d, nil -} diff --git a/eth/protocols/eth/protocol.go b/eth/protocols/eth/protocol.go index f17e6cbc85a..efae4740533 100644 --- a/eth/protocols/eth/protocol.go +++ b/eth/protocols/eth/protocol.go @@ -27,7 +27,7 @@ import ( libcommon "github.com/erigontech/erigon-lib/common" "github.com/erigontech/erigon-lib/direct" proto_sentry "github.com/erigontech/erigon-lib/gointerfaces/sentryproto" - rlp2 "github.com/erigontech/erigon-lib/rlp" + rlp2 "github.com/erigontech/erigon-lib/rlp2" "github.com/erigontech/erigon/core/forkid" "github.com/erigontech/erigon/core/types" diff --git a/txnprovider/txpool/fetch.go b/txnprovider/txpool/fetch.go index 21838e85798..2277381620d 100644 --- a/txnprovider/txpool/fetch.go +++ b/txnprovider/txpool/fetch.go @@ -33,7 +33,7 @@ import ( sentry "github.com/erigontech/erigon-lib/gointerfaces/sentryproto" "github.com/erigontech/erigon-lib/kv" "github.com/erigontech/erigon-lib/log/v3" - "github.com/erigontech/erigon-lib/rlp" + rlp "github.com/erigontech/erigon-lib/rlp2" ) // Fetch connects to sentry and implements eth/66 protocol regarding the transaction diff --git a/txnprovider/txpool/pool_fuzz_test.go b/txnprovider/txpool/pool_fuzz_test.go index 4f90167f48c..fbdbc860f19 100644 --- a/txnprovider/txpool/pool_fuzz_test.go +++ b/txnprovider/txpool/pool_fuzz_test.go @@ -39,7 +39,7 @@ import ( "github.com/erigontech/erigon-lib/kv/memdb" "github.com/erigontech/erigon-lib/kv/temporal/temporaltest" "github.com/erigontech/erigon-lib/log/v3" - "github.com/erigontech/erigon-lib/rlp" + rlp "github.com/erigontech/erigon-lib/rlp2" "github.com/erigontech/erigon/txnprovider/txpool/txpoolcfg" ) diff --git a/txnprovider/txpool/pool_txn_packets.go b/txnprovider/txpool/pool_txn_packets.go index 25cf5a28c4a..26dec533275 100644 --- a/txnprovider/txpool/pool_txn_packets.go +++ b/txnprovider/txpool/pool_txn_packets.go @@ -22,7 +22,7 @@ import ( "github.com/erigontech/erigon-lib/common" "github.com/erigontech/erigon-lib/common/length" - "github.com/erigontech/erigon-lib/rlp" + rlp "github.com/erigontech/erigon-lib/rlp2" ) type NewPooledTransactionHashesPacket [][length.Hash]byte diff --git a/txnprovider/txpool/pool_txn_parser.go b/txnprovider/txpool/pool_txn_parser.go index 645658a6312..274ef637999 100644 --- a/txnprovider/txpool/pool_txn_parser.go +++ b/txnprovider/txpool/pool_txn_parser.go @@ -34,7 +34,7 @@ import ( "github.com/erigontech/erigon-lib/common/length" "github.com/erigontech/erigon-lib/common/u256" "github.com/erigontech/erigon-lib/crypto" - "github.com/erigontech/erigon-lib/rlp" + rlp "github.com/erigontech/erigon-lib/rlp2" ) const ( diff --git a/txnprovider/txpool/send.go b/txnprovider/txpool/send.go index f5e25f1c75c..52b16c0e542 100644 --- a/txnprovider/txpool/send.go +++ b/txnprovider/txpool/send.go @@ -28,7 +28,7 @@ import ( "github.com/erigontech/erigon-lib/gointerfaces/sentryproto" "github.com/erigontech/erigon-lib/log/v3" "github.com/erigontech/erigon-lib/p2p/sentry" - "github.com/erigontech/erigon-lib/rlp" + rlp "github.com/erigontech/erigon-lib/rlp2" ) // Send - does send concrete P2P messages to Sentry. Same as Fetch but for outbound traffic