erigon-pulse/common/changeset/account_changeset_utils.go

195 lines
4.8 KiB
Go

package changeset
import (
"bytes"
"encoding/binary"
"fmt"
"sort"
"github.com/ledgerwatch/turbo-geth/common"
)
// walkAccountChangeSet iterates the account bytes with the keys of provided size
func walkAccountChangeSet(b []byte, keyLen uint32, f func(k, v []byte) error) error {
if len(b) == 0 {
return nil
}
if len(b) < 4 {
return fmt.Errorf("decode: input too short (%d bytes)", len(b))
}
n := binary.BigEndian.Uint32(b[0:4])
if n == 0 {
return nil
}
valOffset := 4 + n*keyLen + 4*n
if uint32(len(b)) < valOffset {
fmt.Println("walkAccounts account")
return fmt.Errorf("decode: input too short (%d bytes, expected at least %d bytes)", len(b), valOffset)
}
totalValLength := binary.BigEndian.Uint32(b[valOffset-4 : valOffset])
if uint32(len(b)) < valOffset+totalValLength {
return fmt.Errorf("decode: input too short (%d bytes, expected at least %d bytes)", len(b), valOffset+totalValLength)
}
for i := uint32(0); i < n; i++ {
key := b[4+i*keyLen : 4+(i+1)*keyLen]
idx0 := uint32(0)
if i > 0 {
idx0 = binary.BigEndian.Uint32(b[4+n*keyLen+4*(i-1) : 4+n*keyLen+4*i])
}
idx1 := binary.BigEndian.Uint32(b[4+n*keyLen+4*i : 4+n*keyLen+4*(i+1)])
val := b[valOffset+idx0 : valOffset+idx1]
err := f(key, val)
if err != nil {
return err
}
}
return nil
}
func findInAccountChangeSetBytes(b []byte, k []byte, keyLen int) ([]byte, error) {
if len(b) == 0 {
return nil, ErrNotFound
}
if len(b) < 8 {
return nil, fmt.Errorf("decode: input too short (%d bytes)", len(b))
}
n := int(binary.BigEndian.Uint32(b[0:]))
if n == 0 {
return nil, ErrNotFound
}
valOffset := 4 + n*keyLen + 4*n
if len(b) < valOffset {
return nil, fmt.Errorf("decode: input too short (%d bytes, expected at least %d bytes)", len(b), valOffset)
}
totalValLength := int(binary.BigEndian.Uint32(b[valOffset-4:]))
if len(b) < valOffset+totalValLength {
return nil, fmt.Errorf("decode: input too short (%d bytes, expected at least %d bytes)", len(b), valOffset+totalValLength)
}
id := sort.Search(n, func(i int) bool {
res := bytes.Compare(b[4+i*keyLen:4+(i+1)*keyLen], k)
return res >= 0
})
if id >= n {
return nil, ErrNotFound
}
if !bytes.Equal(b[4+id*keyLen:4+(id+1)*keyLen], k) {
return nil, ErrNotFound
}
idx0 := 0
if id > 0 {
idx0 = int(binary.BigEndian.Uint32(b[4+n*keyLen+4*(id-1):]))
}
idx1 := int(binary.BigEndian.Uint32(b[4+n*keyLen+4*id:]))
return b[valOffset+idx0 : valOffset+idx1], nil
}
func decodeAccountsWithKeyLen(b []byte, keyLen uint32, h *ChangeSet) error {
h.Changes = make([]Change, 0)
if len(b) == 0 {
return nil
}
if len(b) < 4 {
return fmt.Errorf("decode: input too short (%d bytes)", len(b))
}
numOfAccounts := binary.BigEndian.Uint32(b[0:4])
if numOfAccounts == 0 {
return nil
}
h.Changes = make([]Change, numOfAccounts)
valOffset := 4 + numOfAccounts*keyLen + 4*numOfAccounts
if uint32(len(b)) < valOffset {
fmt.Println("DecodeAccounts account")
return fmt.Errorf("decode: input too short (%d bytes, expected at least %d bytes)", len(b), valOffset)
}
totalValLength := binary.BigEndian.Uint32(b[valOffset-4 : valOffset])
if uint32(len(b)) < valOffset+totalValLength {
return fmt.Errorf("decode: input too short (%d bytes, expected at least %d bytes)", len(b), valOffset+totalValLength)
}
for i := uint32(0); i < numOfAccounts; i++ {
key := b[4+i*keyLen : 4+(i+1)*keyLen]
idx0 := uint32(0)
if i > 0 {
idx0 = binary.BigEndian.Uint32(b[4+numOfAccounts*keyLen+4*(i-1) : 4+numOfAccounts*keyLen+4*i])
}
idx1 := binary.BigEndian.Uint32(b[4+numOfAccounts*keyLen+4*i : 4+numOfAccounts*keyLen+4*(i+1)])
val := b[valOffset+idx0 : valOffset+idx1]
h.Changes[i].Key = common.CopyBytes(key)
h.Changes[i].Value = common.CopyBytes(val)
}
sort.Sort(h)
return nil
}
/*
AccountChangeSet is serialized in the following manner in order to facilitate binary search:
1. The number of keys N (uint32, 4 bytes).
2. Contiguous array of keys (N*M bytes).
3. Contiguous array of accumulating value indexes:
len(val0), len(val0)+len(val1), ..., len(val0)+len(val1)+...+len(val_{N-1})
(4*N bytes since the lengths are treated as uint32).
4. Contiguous array of values.
uint32 integers are serialized as big-endian.
*/
func encodeAccounts(s *ChangeSet) ([]byte, error) {
sort.Sort(s)
buf := new(bytes.Buffer)
intArr := make([]byte, 4)
n := s.Len()
binary.BigEndian.PutUint32(intArr, uint32(n))
_, err := buf.Write(intArr)
if err != nil {
return nil, err
}
for i := 0; i < n; i++ {
_, err = buf.Write(s.Changes[i].Key)
if err != nil {
return nil, err
}
}
var l int
for i := 0; i < n; i++ {
l += len(s.Changes[i].Value)
binary.BigEndian.PutUint32(intArr, uint32(l))
_, err = buf.Write(intArr)
if err != nil {
return nil, err
}
}
for i := 0; i < n; i++ {
_, err = buf.Write(s.Changes[i].Value)
if err != nil {
return nil, err
}
}
return buf.Bytes(), nil
}