neo-go/pkg/core/state/native_state.go
Roman Khimov 9bdf66a5e0 state: always deserialize LastGasPerVote
It can be non-zero even if VoteTo is NULL. Fixes state diff with 3.6.0:

  block 41660: value mismatch for key +////xTrvgat3qG/w8hQoD/I4MgUz6rygA==: QQQhAS8hA7yiAAAhAA== vs QQQhAS8hA7yiAAAhB+POSWfBCAE=

Related to #2844.

Signed-off-by: Roman Khimov <roman@nspcc.ru>
2023-09-05 23:44:14 +03:00

170 lines
4.5 KiB
Go

package state
import (
"crypto/elliptic"
"errors"
"fmt"
"math/big"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
// NEP17Balance represents the balance state of a NEP-17-token.
type NEP17Balance struct {
Balance big.Int
}
// NEOBalance represents the balance state of a NEO-token.
type NEOBalance struct {
NEP17Balance
BalanceHeight uint32
VoteTo *keys.PublicKey
LastGasPerVote big.Int
}
// NEP17BalanceFromBytes converts the serialized NEP17Balance to a structure.
func NEP17BalanceFromBytes(b []byte) (*NEP17Balance, error) {
if len(b) < 4 {
if len(b) == 0 {
return new(NEP17Balance), nil
}
return nil, errors.New("invalid format")
}
if b[0] != byte(stackitem.StructT) {
return nil, errors.New("not a struct")
}
if b[1] != 1 {
return nil, errors.New("invalid item count")
}
if st := stackitem.Type(b[2]); st != stackitem.IntegerT {
return nil, fmt.Errorf("invalid balance: %s", st)
}
if int(b[3]) != len(b[4:]) {
return nil, errors.New("invalid balance format")
}
return &NEP17Balance{Balance: *bigint.FromBytes(b[4:])}, nil
}
// Bytes returns serialized NEP17Balance.
func (s *NEP17Balance) Bytes(buf []byte) []byte {
if cap(buf) < 4+bigint.MaxBytesLen {
buf = make([]byte, 4, 4+bigint.MaxBytesLen)
} else {
buf = buf[:4]
}
buf[0] = byte(stackitem.StructT)
buf[1] = 1
buf[2] = byte(stackitem.IntegerT)
data := bigint.ToPreallocatedBytes(&s.Balance, buf[4:])
buf[3] = byte(len(data)) // max is 33, so we are ok here
buf = append(buf, data...)
return buf
}
func balanceFromBytes(b []byte, item stackitem.Convertible) error {
if len(b) == 0 {
return nil
}
return stackitem.DeserializeConvertible(b, item)
}
// ToStackItem implements stackitem.Convertible. It never returns an error.
func (s *NEP17Balance) ToStackItem() (stackitem.Item, error) {
return stackitem.NewStruct([]stackitem.Item{stackitem.NewBigInteger(&s.Balance)}), nil
}
// FromStackItem implements stackitem.Convertible.
func (s *NEP17Balance) FromStackItem(item stackitem.Item) error {
items, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not a struct")
}
if len(items) < 1 {
return errors.New("no balance value")
}
balance, err := items[0].TryInteger()
if err != nil {
return fmt.Errorf("invalid balance: %w", err)
}
s.Balance = *balance
return nil
}
// NEOBalanceFromBytes converts the serialized NEOBalance to a structure.
func NEOBalanceFromBytes(b []byte) (*NEOBalance, error) {
balance := new(NEOBalance)
err := balanceFromBytes(b, balance)
if err != nil {
return nil, err
}
return balance, nil
}
// Bytes returns a serialized NEOBalance.
func (s *NEOBalance) Bytes(sc *stackitem.SerializationContext) []byte {
item, _ := s.ToStackItem() // Never returns an error.
data, err := sc.Serialize(item, false)
if err != nil {
panic(err)
}
return data
}
// ToStackItem implements stackitem.Convertible interface. It never returns an error.
func (s *NEOBalance) ToStackItem() (stackitem.Item, error) {
var voteItem stackitem.Item
if s.VoteTo != nil {
voteItem = stackitem.NewByteArray(s.VoteTo.Bytes())
} else {
voteItem = stackitem.Null{}
}
return stackitem.NewStruct([]stackitem.Item{
stackitem.NewBigInteger(&s.Balance),
stackitem.NewBigInteger(big.NewInt(int64(s.BalanceHeight))),
voteItem,
stackitem.NewBigInteger(&s.LastGasPerVote),
}), nil
}
// FromStackItem converts stackitem.Item to NEOBalance.
func (s *NEOBalance) FromStackItem(item stackitem.Item) error {
structItem, ok := item.Value().([]stackitem.Item)
if !ok || len(structItem) < 3 {
return errors.New("invalid stackitem length")
}
balance, err := structItem[0].TryInteger()
if err != nil {
return fmt.Errorf("invalid balance stackitem: %w", err)
}
s.Balance = *balance
h, err := structItem[1].TryInteger()
if err != nil {
return fmt.Errorf("invalid heigh stackitem")
}
s.BalanceHeight = uint32(h.Int64())
if _, ok := structItem[2].(stackitem.Null); ok {
s.VoteTo = nil
} else {
bs, err := structItem[2].TryBytes()
if err != nil {
return fmt.Errorf("invalid public key stackitem: %w", err)
}
pub, err := keys.NewPublicKeyFromBytes(bs, elliptic.P256())
if err != nil {
return fmt.Errorf("invalid public key bytes: %w", err)
}
s.VoteTo = pub
}
if len(structItem) >= 4 {
lastGasPerVote, err := structItem[3].TryInteger()
if err != nil {
return fmt.Errorf("invalid last vote reward per neo stackitem: %w", err)
}
s.LastGasPerVote = *lastGasPerVote
}
return nil
}