mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-22 19:29:39 +00:00
9bdf66a5e0
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>
170 lines
4.5 KiB
Go
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
|
|
}
|