mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-29 07:07:16 +00:00
169 lines
5.1 KiB
Go
169 lines
5.1 KiB
Go
/*
|
|
Copyright 2021 Erigon contributors
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package rlp
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/holiman/uint256"
|
|
)
|
|
|
|
// 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 length > 0 && payload[pos] == 0 {
|
|
return 0, fmt.Errorf("integer encoding for RLP must not have leading zeros: %x", 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) {
|
|
switch first := payload[pos]; {
|
|
case first < 128:
|
|
dataPos = pos
|
|
dataLen = 1
|
|
isList = false
|
|
case first < 184:
|
|
// string of len < 56, and it is non-legacy transaction
|
|
dataPos = pos + 1
|
|
dataLen = int(first) - 128
|
|
isList = false
|
|
case first < 192:
|
|
// string of len >= 56, and it is non-legacy transaction
|
|
beLen := int(first) - 183
|
|
dataPos = pos + 1 + beLen
|
|
dataLen, err = BeInt(payload, pos+1, beLen)
|
|
isList = false
|
|
case first < 248:
|
|
// isList of len < 56, and it is a legacy transaction
|
|
dataPos = pos + 1
|
|
dataLen = int(first) - 192
|
|
isList = true
|
|
default:
|
|
// isList of len >= 56, and it is a legacy transaction
|
|
beLen := int(first) - 247
|
|
dataPos = pos + 1 + beLen
|
|
dataLen, err = BeInt(payload, pos+1, beLen)
|
|
isList = true
|
|
}
|
|
if err != nil {
|
|
if dataPos+dataLen >= len(payload) {
|
|
err = fmt.Errorf("unexpected end of payload")
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func List(payload []byte, pos int) (dataPos int, 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("must be a list")
|
|
}
|
|
return
|
|
}
|
|
|
|
func String(payload []byte, pos int) (dataPos int, 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("must be a string, instead of a list")
|
|
}
|
|
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("expected string of len %d, got %d", 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("uint64 must be a string, not isList")
|
|
}
|
|
if dataLen > 8 {
|
|
return 0, 0, fmt.Errorf("uint64 must not be more than 8 bytes long, got %d", dataLen)
|
|
}
|
|
if dataLen > 0 && payload[dataPos] == 0 {
|
|
return 0, 0, fmt.Errorf("integer encoding for RLP must not have leading zeros: %x", payload[dataPos:dataPos+dataLen])
|
|
}
|
|
var r uint64
|
|
for _, b := range payload[dataPos : dataPos+dataLen] {
|
|
r = (r << 8) | uint64(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("uint256 must not be more than 8 bytes long, got %d", dataLen)
|
|
}
|
|
if dataLen > 0 && payload[dataPos] == 0 {
|
|
return 0, fmt.Errorf("integer encoding for RLP must not have leading zeros: %x", payload[dataPos:dataPos+dataLen])
|
|
}
|
|
x.SetBytes(payload[dataPos : dataPos+dataLen])
|
|
return dataPos + dataLen, nil
|
|
}
|
|
|
|
// ParseHash extracts the next hash from the RLP encoding (payload) from a given position.
|
|
// It appends the hash to the given slice, reusing the space if there is enough capacity
|
|
// The first returned value is the slice where hash is appended to.
|
|
// The second returned value is the new position in the RLP payload after the extraction
|
|
// of the hash.
|
|
func ParseHash(payload []byte, pos int, hashbuf []byte) ([]byte, int, error) {
|
|
dataPos, err := StringOfLen(payload, pos, 32)
|
|
if err != nil {
|
|
return nil, 0, fmt.Errorf("%s: hash len: %w", ParseHashErrorPrefix, err)
|
|
}
|
|
hashbuf = ensureEnoughSize(hashbuf, 32)
|
|
copy(hashbuf, payload[dataPos:dataPos+32])
|
|
return hashbuf, dataPos + 32, nil
|
|
}
|
|
|
|
func ensureEnoughSize(in []byte, size int) []byte {
|
|
if cap(in) < size {
|
|
return make([]byte, size)
|
|
}
|
|
return in[:size] // Reuse the space if it has enough capacity
|
|
}
|
|
|
|
const ParseHashErrorPrefix = "parse hash payload"
|