go-lns/pkg/primitives/namestate_binary.go

282 lines
5.7 KiB
Go

// SPDX-License-Identifier: EUPL-1.2
package primitives
import (
"encoding/binary"
"fmt"
)
const (
// MaxNameStateSize is the maximum serialized size of a name state,
// mirroring the JS reference's `NameState.MAX_SIZE` constant.
MaxNameStateSize = 668
// NameStateMaxSize is an alias for MaxNameStateSize.
NameStateMaxSize = MaxNameStateSize
)
// GetSize returns the serialized size of the name state.
func (ns NameState) GetSize() int {
size := 1 + len(ns.Name) + 2 + len(ns.Data) + 4 + 4 + 2
if !outpointIsNull(ns.Owner) {
size += len(ns.Owner.TxHash)
size += sizeVarint64(uint64(ns.Owner.Index))
}
if ns.Value != 0 {
size += sizeVarint64(ns.Value)
}
if ns.Highest != 0 {
size += sizeVarint64(ns.Highest)
}
if ns.Transfer != 0 {
size += 4
}
if ns.Revoked != 0 {
size += 4
}
if ns.Claimed != 0 {
size += 4
}
if ns.Renewals != 0 {
size += sizeVarint64(uint64(ns.Renewals))
}
return size
}
// MarshalBinary serializes the name state using the compact JS reference layout.
//
// The encoding does not include NameHash, which is derived from Name by the
// caller and is not part of the on-wire payload.
func (ns NameState) MarshalBinary() ([]byte, error) {
if len(ns.Name) > 0xff {
return nil, fmt.Errorf("primitives.NameState.MarshalBinary: name too long")
}
if len(ns.Data) > 0xffff {
return nil, fmt.Errorf("primitives.NameState.MarshalBinary: data too long")
}
buf := make([]byte, 0, ns.GetSize())
buf = append(buf, byte(len(ns.Name)))
buf = append(buf, ns.Name...)
var tmp [10]byte
binary.LittleEndian.PutUint16(tmp[:2], uint16(len(ns.Data)))
buf = append(buf, tmp[:2]...)
buf = append(buf, ns.Data...)
binary.LittleEndian.PutUint32(tmp[:4], ns.Height)
buf = append(buf, tmp[:4]...)
binary.LittleEndian.PutUint32(tmp[:4], ns.Renewal)
buf = append(buf, tmp[:4]...)
var field uint16
if !outpointIsNull(ns.Owner) {
field |= 1 << 0
}
if ns.Value != 0 {
field |= 1 << 1
}
if ns.Highest != 0 {
field |= 1 << 2
}
if ns.Transfer != 0 {
field |= 1 << 3
}
if ns.Revoked != 0 {
field |= 1 << 4
}
if ns.Claimed != 0 {
field |= 1 << 5
}
if ns.Renewals != 0 {
field |= 1 << 6
}
if ns.Registered {
field |= 1 << 7
}
if ns.Expired {
field |= 1 << 8
}
if ns.Weak {
field |= 1 << 9
}
binary.LittleEndian.PutUint16(tmp[:2], field)
buf = append(buf, tmp[:2]...)
if !outpointIsNull(ns.Owner) {
buf = append(buf, ns.Owner.TxHash[:]...)
buf = appendVarint(buf, uint64(ns.Owner.Index))
}
if ns.Value != 0 {
buf = appendVarint(buf, ns.Value)
}
if ns.Highest != 0 {
buf = appendVarint(buf, ns.Highest)
}
if ns.Transfer != 0 {
binary.LittleEndian.PutUint32(tmp[:4], ns.Transfer)
buf = append(buf, tmp[:4]...)
}
if ns.Revoked != 0 {
binary.LittleEndian.PutUint32(tmp[:4], ns.Revoked)
buf = append(buf, tmp[:4]...)
}
if ns.Claimed != 0 {
binary.LittleEndian.PutUint32(tmp[:4], ns.Claimed)
buf = append(buf, tmp[:4]...)
}
if ns.Renewals != 0 {
buf = appendVarint(buf, uint64(ns.Renewals))
}
return buf, nil
}
// UnmarshalBinary decodes the compact name-state layout used by the JS reference.
func (ns *NameState) UnmarshalBinary(data []byte) error {
*ns = NameState{}
if len(data) < 1 {
return fmt.Errorf("primitives.NameState.UnmarshalBinary: short buffer")
}
nameLen := int(data[0])
data = data[1:]
if len(data) < nameLen+2+4+4+2 {
return fmt.Errorf("primitives.NameState.UnmarshalBinary: short buffer")
}
ns.Name = append([]byte(nil), data[:nameLen]...)
data = data[nameLen:]
dataLen := int(binary.LittleEndian.Uint16(data[:2]))
data = data[2:]
if len(data) < dataLen+4+4+2 {
return fmt.Errorf("primitives.NameState.UnmarshalBinary: short buffer")
}
ns.Data = append([]byte(nil), data[:dataLen]...)
data = data[dataLen:]
ns.Height = binary.LittleEndian.Uint32(data[:4])
data = data[4:]
ns.Renewal = binary.LittleEndian.Uint32(data[:4])
data = data[4:]
field := binary.LittleEndian.Uint16(data[:2])
data = data[2:]
if field&(1<<0) != 0 {
if len(data) < len(ns.Owner.TxHash) {
return fmt.Errorf("primitives.NameState.UnmarshalBinary: short buffer")
}
copy(ns.Owner.TxHash[:], data[:len(ns.Owner.TxHash)])
data = data[len(ns.Owner.TxHash):]
index, n, err := readVarint(data)
if err != nil {
return fmt.Errorf("primitives.NameState.UnmarshalBinary: %w", err)
}
ns.Owner.Index = uint32(index)
data = data[n:]
}
if field&(1<<1) != 0 {
value, n, err := readVarint(data)
if err != nil {
return fmt.Errorf("primitives.NameState.UnmarshalBinary: %w", err)
}
ns.Value = value
data = data[n:]
}
if field&(1<<2) != 0 {
highest, n, err := readVarint(data)
if err != nil {
return fmt.Errorf("primitives.NameState.UnmarshalBinary: %w", err)
}
ns.Highest = highest
data = data[n:]
}
if field&(1<<3) != 0 {
if len(data) < 4 {
return fmt.Errorf("primitives.NameState.UnmarshalBinary: short buffer")
}
ns.Transfer = binary.LittleEndian.Uint32(data[:4])
data = data[4:]
}
if field&(1<<4) != 0 {
if len(data) < 4 {
return fmt.Errorf("primitives.NameState.UnmarshalBinary: short buffer")
}
ns.Revoked = binary.LittleEndian.Uint32(data[:4])
data = data[4:]
}
if field&(1<<5) != 0 {
if len(data) < 4 {
return fmt.Errorf("primitives.NameState.UnmarshalBinary: short buffer")
}
ns.Claimed = binary.LittleEndian.Uint32(data[:4])
data = data[4:]
}
if field&(1<<6) != 0 {
renewals, n, err := readVarint(data)
if err != nil {
return fmt.Errorf("primitives.NameState.UnmarshalBinary: %w", err)
}
ns.Renewals = uint32(renewals)
data = data[n:]
}
if field&(1<<7) != 0 {
ns.Registered = true
}
if field&(1<<8) != 0 {
ns.Expired = true
}
if field&(1<<9) != 0 {
ns.Weak = true
}
if len(data) != 0 {
return fmt.Errorf("primitives.NameState.UnmarshalBinary: trailing data")
}
return nil
}