mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-22 11:41:19 +00:00
24987878e4
* Most of the PR changed files are extra and slightly more complicated unit tests. * Fixed Eth1DataVotes not inheriting genesis * Fixed Attestations simulation using wrong slot when reconstructing partecipation * Fixed Copy() operation on BeaconState on Eth1DataVotes * Used correct ListSSZ type for Eth1DataVotes and HistoricalSummaries * Fixed wrong []uint64 deltas on empty slots
112 lines
4.6 KiB
Go
112 lines
4.6 KiB
Go
package ssz2
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"reflect"
|
|
|
|
"github.com/ledgerwatch/erigon-lib/types/ssz"
|
|
)
|
|
|
|
/*
|
|
The function takes the input byte slice buf, the SSZ version, and the schema as variadic arguments.
|
|
It initializes a position pointer position to keep track of the current position in the buf.
|
|
|
|
It creates two empty slices: offsets to store the offsets for dynamic objects, and dynamicObjs to store the dynamic objects themselves.
|
|
It iterates over each element in the schema using a for loop.
|
|
For each element, it performs the following actions based on its type:
|
|
|
|
If the element is a pointer to uint64, it decodes the corresponding value from the buf using
|
|
little-endian encoding and assigns it to the pointer. It then increments the position by 8 bytes.
|
|
|
|
If the element is a byte slice ([]byte), it copies the corresponding data from the buf to the slice.
|
|
It then increments the position by the length of the slice.
|
|
|
|
If the element implements the SizedObjectSSZ interface, it checks if the object is static (fixed size) or dynamic (variable size).
|
|
|
|
- If it's static, it decodes the object from the buf and updates the position accordingly by calling obj.DecodeSSZ and obj.EncodingSizeSSZ.
|
|
- If it's dynamic, it stores the offset (as a 32-bit little-endian integer) in the offsets slice and stores the object itself in the dynamicObjs slice.
|
|
It then increments the position by 4 bytes.
|
|
|
|
After processing all elements in the schema, the function iterates over the dynamic objects stored in the dynamicObjs slice.
|
|
For each dynamic object, it retrieves the corresponding offset and the end offset. If it's the last dynamic object, the end offset is set to the length of the buf.
|
|
It calls obj.DecodeSSZ on the sub-slice of the buf from the offset to the end offset and passes the SSZ version. This decodes the dynamic object.
|
|
Finally, the function returns nil if the decoding process is successful, or an error if an error occurs during decoding.
|
|
The Decode function is used to decode an SSZ-encoded byte slice into the specified schema. It supports decoding of various
|
|
types such as uint64, []byte, and objects that implement the SizedObjectSSZ interface.
|
|
It handles both static (fixed size) and dynamic (variable size) objects based on their respective decoding methods and offsets.
|
|
*/
|
|
func UnmarshalSSZ(buf []byte, version int, schema ...interface{}) (err error) {
|
|
// defer func() {
|
|
// if err2 := recover(); err2 != nil {
|
|
// err = fmt.Errorf("panic while decoding: %v", err2)
|
|
// }
|
|
// }()
|
|
position := 0
|
|
offsets := []int{}
|
|
dynamicObjs := []SizedObjectSSZ{}
|
|
|
|
// Iterate over each element in the schema
|
|
for i, element := range schema {
|
|
switch obj := element.(type) {
|
|
case *uint64:
|
|
if len(buf) < position+8 {
|
|
return ssz.ErrLowBufferSize
|
|
}
|
|
// If the element is a pointer to uint64, decode it from the buf using little-endian encoding
|
|
*obj = binary.LittleEndian.Uint64(buf[position:])
|
|
position += 8
|
|
case []byte:
|
|
if len(buf) < position+len(obj) {
|
|
return ssz.ErrLowBufferSize
|
|
}
|
|
// If the element is a byte slice, copy the corresponding data from the buf to the slice
|
|
copy(obj, buf[position:])
|
|
position += len(obj)
|
|
case SizedObjectSSZ:
|
|
// If the element implements the SizedObjectSSZ interface
|
|
if obj.Static() {
|
|
if len(buf) < position+obj.EncodingSizeSSZ() {
|
|
return ssz.ErrLowBufferSize
|
|
}
|
|
// If the object is static (fixed size), decode it from the buf and update the position
|
|
if err = obj.DecodeSSZ(buf[position:], version); err != nil {
|
|
return fmt.Errorf("static element %d: %w", i, err)
|
|
}
|
|
position += obj.EncodingSizeSSZ()
|
|
} else {
|
|
if len(buf) < position+4 {
|
|
return ssz.ErrLowBufferSize
|
|
}
|
|
// If the object is dynamic (variable size), store the offset and the object in separate slices
|
|
offsets = append(offsets, int(binary.LittleEndian.Uint32(buf[position:])))
|
|
dynamicObjs = append(dynamicObjs, obj)
|
|
position += 4
|
|
}
|
|
default:
|
|
// If the element does not match any supported types, throw panic, will be caught by anti-panic condom
|
|
// and we will have the trace.
|
|
panic(fmt.Errorf("RTFM, bad schema component %d", i))
|
|
}
|
|
}
|
|
|
|
// Iterate over the dynamic objects and decode them using the stored offsets
|
|
for i, obj := range dynamicObjs {
|
|
endOffset := len(buf)
|
|
if i != len(dynamicObjs)-1 {
|
|
endOffset = offsets[i+1]
|
|
}
|
|
if offsets[i] > endOffset {
|
|
return ssz.ErrBadOffset
|
|
}
|
|
if len(buf) < endOffset {
|
|
return ssz.ErrLowBufferSize
|
|
}
|
|
if err = obj.DecodeSSZ(buf[offsets[i]:endOffset], version); err != nil {
|
|
return fmt.Errorf("dynamic element (sz:%d) %d/%s: %w", endOffset-offsets[i], i, reflect.TypeOf(obj), err)
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|