github.com/ledgerwatch/erigon-lib@v1.0.0/rlp/parse.go (about)

     1  /*
     2     Copyright 2021 The 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 rlp
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  
    23  	"github.com/holiman/uint256"
    24  
    25  	"github.com/ledgerwatch/erigon-lib/common"
    26  )
    27  
    28  var (
    29  	ErrBase   = fmt.Errorf("rlp")
    30  	ErrParse  = fmt.Errorf("%w parse", ErrBase)
    31  	ErrDecode = fmt.Errorf("%w decode", ErrBase)
    32  )
    33  
    34  func IsRLPError(err error) bool { return errors.Is(err, ErrBase) }
    35  
    36  // BeInt parses Big Endian representation of an integer from given payload at given position
    37  func BeInt(payload []byte, pos, length int) (int, error) {
    38  	var r int
    39  	if pos+length >= len(payload) {
    40  		return 0, fmt.Errorf("%w: unexpected end of payload", ErrParse)
    41  	}
    42  	if length > 0 && payload[pos] == 0 {
    43  		return 0, fmt.Errorf("%w: integer encoding for RLP must not have leading zeros: %x", ErrParse, payload[pos:pos+length])
    44  	}
    45  	for _, b := range payload[pos : pos+length] {
    46  		r = (r << 8) | int(b)
    47  	}
    48  	return r, nil
    49  }
    50  
    51  // Prefix parses RLP Prefix from given payload at given position. It returns the offset and length of the RLP element
    52  // as well as the indication of whether it is a list of string
    53  func Prefix(payload []byte, pos int) (dataPos int, dataLen int, isList bool, err error) {
    54  	if pos < 0 {
    55  		return 0, 0, false, fmt.Errorf("%w: negative position not allowed", ErrParse)
    56  	}
    57  	if pos >= len(payload) {
    58  		return 0, 0, false, fmt.Errorf("%w: unexpected end of payload", ErrParse)
    59  	}
    60  	switch first := payload[pos]; {
    61  	case first < 128:
    62  		dataPos = pos
    63  		dataLen = 1
    64  		isList = false
    65  	case first < 184:
    66  		// Otherwise, if a string is 0-55 bytes long,
    67  		// the RLP encoding consists of a single byte with value 0x80 plus the
    68  		// length of the string followed by the string. The range of the first
    69  		// byte is thus [0x80, 0xB7].
    70  		dataPos = pos + 1
    71  		dataLen = int(first) - 128
    72  		isList = false
    73  		if dataLen == 1 && dataPos < len(payload) && payload[dataPos] < 128 {
    74  			err = fmt.Errorf("%w: non-canonical size information", ErrParse)
    75  		}
    76  	case first < 192:
    77  		// If a string is more than 55 bytes long, the
    78  		// RLP encoding consists of a single byte with value 0xB7 plus the length
    79  		// of the length of the string in binary form, followed by the length of
    80  		// the string, followed by the string. For example, a length-1024 string
    81  		// would be encoded as 0xB90400 followed by the string. The range of
    82  		// the first byte is thus [0xB8, 0xBF].
    83  		beLen := int(first) - 183
    84  		dataPos = pos + 1 + beLen
    85  		dataLen, err = BeInt(payload, pos+1, beLen)
    86  		isList = false
    87  		if dataLen < 56 {
    88  			err = fmt.Errorf("%w: non-canonical size information", ErrParse)
    89  		}
    90  	case first < 248:
    91  		// isList of len < 56
    92  		// If the total payload of a list
    93  		// (i.e. the combined length of all its items) is 0-55 bytes long, the
    94  		// RLP encoding consists of a single byte with value 0xC0 plus the length
    95  		// of the list followed by the concatenation of the RLP encodings of the
    96  		// items. The range of the first byte is thus [0xC0, 0xF7].
    97  		dataPos = pos + 1
    98  		dataLen = int(first) - 192
    99  		isList = true
   100  	default:
   101  		// If the total payload of a list is more than 55 bytes long,
   102  		// the RLP encoding consists of a single byte with value 0xF7
   103  		// plus the length of the length of the payload in binary
   104  		// form, followed by the length of the payload, followed by
   105  		// the concatenation of the RLP encodings of the items. The
   106  		// range of the first byte is thus [0xF8, 0xFF].
   107  		beLen := int(first) - 247
   108  		dataPos = pos + 1 + beLen
   109  		dataLen, err = BeInt(payload, pos+1, beLen)
   110  		isList = true
   111  		if dataLen < 56 {
   112  			err = fmt.Errorf("%w: : non-canonical size information", ErrParse)
   113  		}
   114  	}
   115  	if err == nil {
   116  		if dataPos+dataLen > len(payload) {
   117  			err = fmt.Errorf("%w: unexpected end of payload", ErrParse)
   118  		} else if dataPos+dataLen < 0 {
   119  			err = fmt.Errorf("%w: found too big len", ErrParse)
   120  		}
   121  	}
   122  	return
   123  }
   124  
   125  func List(payload []byte, pos int) (dataPos, dataLen int, err error) {
   126  	dataPos, dataLen, isList, err := Prefix(payload, pos)
   127  	if err != nil {
   128  		return 0, 0, err
   129  	}
   130  	if !isList {
   131  		return 0, 0, fmt.Errorf("%w: must be a list", ErrParse)
   132  	}
   133  	return
   134  }
   135  
   136  func String(payload []byte, pos int) (dataPos, dataLen int, err error) {
   137  	dataPos, dataLen, isList, err := Prefix(payload, pos)
   138  	if err != nil {
   139  		return 0, 0, err
   140  	}
   141  	if isList {
   142  		return 0, 0, fmt.Errorf("%w: must be a string, instead of a list", ErrParse)
   143  	}
   144  	return
   145  }
   146  func StringOfLen(payload []byte, pos, expectedLen int) (dataPos int, err error) {
   147  	dataPos, dataLen, err := String(payload, pos)
   148  	if err != nil {
   149  		return 0, err
   150  	}
   151  	if dataLen != expectedLen {
   152  		return 0, fmt.Errorf("%w: expected string of len %d, got %d", ErrParse, expectedLen, dataLen)
   153  	}
   154  	return
   155  }
   156  
   157  // U64 parses uint64 number from given payload at given position
   158  func U64(payload []byte, pos int) (int, uint64, error) {
   159  	dataPos, dataLen, isList, err := Prefix(payload, pos)
   160  	if err != nil {
   161  		return 0, 0, err
   162  	}
   163  	if isList {
   164  		return 0, 0, fmt.Errorf("%w: uint64 must be a string, not isList", ErrParse)
   165  	}
   166  	if dataLen > 8 {
   167  		return 0, 0, fmt.Errorf("%w: uint64 must not be more than 8 bytes long, got %d", ErrParse, dataLen)
   168  	}
   169  	if dataLen > 0 && payload[dataPos] == 0 {
   170  		return 0, 0, fmt.Errorf("%w: integer encoding for RLP must not have leading zeros: %x", ErrParse, payload[dataPos:dataPos+dataLen])
   171  	}
   172  	var r uint64
   173  	for _, b := range payload[dataPos : dataPos+dataLen] {
   174  		r = (r << 8) | uint64(b)
   175  	}
   176  	return dataPos + dataLen, r, nil
   177  }
   178  
   179  // U32 parses uint64 number from given payload at given position
   180  func U32(payload []byte, pos int) (int, uint32, error) {
   181  	dataPos, dataLen, isList, err := Prefix(payload, pos)
   182  	if err != nil {
   183  		return 0, 0, err
   184  	}
   185  	if isList {
   186  		return 0, 0, fmt.Errorf("%w: uint32 must be a string, not isList", ErrParse)
   187  	}
   188  	if dataLen > 4 {
   189  		return 0, 0, fmt.Errorf("%w: uint32 must not be more than 4 bytes long, got %d", ErrParse, dataLen)
   190  	}
   191  	if dataLen > 0 && payload[dataPos] == 0 {
   192  		return 0, 0, fmt.Errorf("%w: integer encoding for RLP must not have leading zeros: %x", ErrParse, payload[dataPos:dataPos+dataLen])
   193  	}
   194  	var r uint32
   195  	for _, b := range payload[dataPos : dataPos+dataLen] {
   196  		r = (r << 8) | uint32(b)
   197  	}
   198  	return dataPos + dataLen, r, nil
   199  }
   200  
   201  // U256 parses uint256 number from given payload at given position
   202  func U256(payload []byte, pos int, x *uint256.Int) (int, error) {
   203  	dataPos, dataLen, err := String(payload, pos)
   204  	if err != nil {
   205  		return 0, err
   206  	}
   207  	if dataLen > 32 {
   208  		return 0, fmt.Errorf("%w: uint256 must not be more than 32 bytes long, got %d", ErrParse, dataLen)
   209  	}
   210  	if dataLen > 0 && payload[dataPos] == 0 {
   211  		return 0, fmt.Errorf("%w: integer encoding for RLP must not have leading zeros: %x", ErrParse, payload[dataPos:dataPos+dataLen])
   212  	}
   213  	x.SetBytes(payload[dataPos : dataPos+dataLen])
   214  	return dataPos + dataLen, nil
   215  }
   216  
   217  func U256Len(z *uint256.Int) int {
   218  	if z == nil {
   219  		return 1
   220  	}
   221  	nBits := z.BitLen()
   222  	if nBits == 0 {
   223  		return 1
   224  	}
   225  	if nBits <= 7 {
   226  		return 1
   227  	}
   228  	return 1 + common.BitLenToByteLen(nBits)
   229  }
   230  
   231  func ParseHash(payload []byte, pos int, hashbuf []byte) (int, error) {
   232  	pos, err := StringOfLen(payload, pos, 32)
   233  	if err != nil {
   234  		return 0, fmt.Errorf("%s: hash len: %w", ParseHashErrorPrefix, err)
   235  	}
   236  	copy(hashbuf, payload[pos:pos+32])
   237  	return pos + 32, nil
   238  }
   239  
   240  const ParseHashErrorPrefix = "parse hash payload"
   241  
   242  const ParseAnnouncementsErrorPrefix = "parse announcement payload"
   243  
   244  func ParseAnnouncements(payload []byte, pos int) ([]byte, []uint32, []byte, int, error) {
   245  	pos, totalLen, err := List(payload, pos)
   246  	if err != nil {
   247  		return nil, nil, nil, pos, err
   248  	}
   249  	if pos+totalLen > len(payload) {
   250  		return nil, nil, nil, pos, fmt.Errorf("%s: totalLen %d is beyond the end of payload", ParseAnnouncementsErrorPrefix, totalLen)
   251  	}
   252  	pos, typesLen, err := String(payload, pos)
   253  	if err != nil {
   254  		return nil, nil, nil, pos, err
   255  	}
   256  	if pos+typesLen > len(payload) {
   257  		return nil, nil, nil, pos, fmt.Errorf("%s: typesLen %d is beyond the end of payload", ParseAnnouncementsErrorPrefix, typesLen)
   258  	}
   259  	types := payload[pos : pos+typesLen]
   260  	pos += typesLen
   261  	pos, sizesLen, err := List(payload, pos)
   262  	if err != nil {
   263  		return nil, nil, nil, pos, err
   264  	}
   265  	if pos+sizesLen > len(payload) {
   266  		return nil, nil, nil, pos, fmt.Errorf("%s: sizesLen %d is beyond the end of payload", ParseAnnouncementsErrorPrefix, sizesLen)
   267  	}
   268  	sizes := make([]uint32, typesLen)
   269  	for i := 0; i < len(sizes); i++ {
   270  		if pos, sizes[i], err = U32(payload, pos); err != nil {
   271  			return nil, nil, nil, pos, err
   272  		}
   273  	}
   274  	pos, hashesLen, err := List(payload, pos)
   275  	if err != nil {
   276  		return nil, nil, nil, pos, err
   277  	}
   278  	if pos+hashesLen > len(payload) {
   279  		return nil, nil, nil, pos, fmt.Errorf("%s: hashesLen %d is beyond the end of payload", ParseAnnouncementsErrorPrefix, hashesLen)
   280  	}
   281  	hashes := make([]byte, 32*(hashesLen/33))
   282  	for i := 0; i < len(hashes); i += 32 {
   283  		if pos, err = ParseHash(payload, pos, hashes[i:]); err != nil {
   284  			return nil, nil, nil, pos, err
   285  		}
   286  	}
   287  	return types, sizes, hashes, pos, nil
   288  }