github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/state/native_state.go (about)

     1  package state
     2  
     3  import (
     4  	"crypto/elliptic"
     5  	"errors"
     6  	"fmt"
     7  	"math/big"
     8  
     9  	"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
    10  	"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
    11  	"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
    12  )
    13  
    14  // NEP17Balance represents the balance state of a NEP-17-token.
    15  type NEP17Balance struct {
    16  	Balance big.Int
    17  }
    18  
    19  // NEOBalance represents the balance state of a NEO-token.
    20  type NEOBalance struct {
    21  	NEP17Balance
    22  	BalanceHeight  uint32
    23  	VoteTo         *keys.PublicKey
    24  	LastGasPerVote big.Int
    25  }
    26  
    27  // NEP17BalanceFromBytes converts the serialized NEP17Balance to a structure.
    28  func NEP17BalanceFromBytes(b []byte) (*NEP17Balance, error) {
    29  	if len(b) < 4 {
    30  		if len(b) == 0 {
    31  			return new(NEP17Balance), nil
    32  		}
    33  		return nil, errors.New("invalid format")
    34  	}
    35  	if b[0] != byte(stackitem.StructT) {
    36  		return nil, errors.New("not a struct")
    37  	}
    38  	if b[1] != 1 {
    39  		return nil, errors.New("invalid item count")
    40  	}
    41  	if st := stackitem.Type(b[2]); st != stackitem.IntegerT {
    42  		return nil, fmt.Errorf("invalid balance: %s", st)
    43  	}
    44  	if int(b[3]) != len(b[4:]) {
    45  		return nil, errors.New("invalid balance format")
    46  	}
    47  	return &NEP17Balance{Balance: *bigint.FromBytes(b[4:])}, nil
    48  }
    49  
    50  // Bytes returns serialized NEP17Balance.
    51  func (s *NEP17Balance) Bytes(buf []byte) []byte {
    52  	if cap(buf) < 4+bigint.MaxBytesLen {
    53  		buf = make([]byte, 4, 4+bigint.MaxBytesLen)
    54  	} else {
    55  		buf = buf[:4]
    56  	}
    57  	buf[0] = byte(stackitem.StructT)
    58  	buf[1] = 1
    59  	buf[2] = byte(stackitem.IntegerT)
    60  
    61  	data := bigint.ToPreallocatedBytes(&s.Balance, buf[4:])
    62  	buf[3] = byte(len(data)) // max is 33, so we are ok here
    63  	buf = append(buf, data...)
    64  	return buf
    65  }
    66  
    67  func balanceFromBytes(b []byte, item stackitem.Convertible) error {
    68  	if len(b) == 0 {
    69  		return nil
    70  	}
    71  	return stackitem.DeserializeConvertible(b, item)
    72  }
    73  
    74  // ToStackItem implements stackitem.Convertible. It never returns an error.
    75  func (s *NEP17Balance) ToStackItem() (stackitem.Item, error) {
    76  	return stackitem.NewStruct([]stackitem.Item{stackitem.NewBigInteger(&s.Balance)}), nil
    77  }
    78  
    79  // FromStackItem implements stackitem.Convertible.
    80  func (s *NEP17Balance) FromStackItem(item stackitem.Item) error {
    81  	items, ok := item.Value().([]stackitem.Item)
    82  	if !ok {
    83  		return errors.New("not a struct")
    84  	}
    85  	if len(items) < 1 {
    86  		return errors.New("no balance value")
    87  	}
    88  	balance, err := items[0].TryInteger()
    89  	if err != nil {
    90  		return fmt.Errorf("invalid balance: %w", err)
    91  	}
    92  	s.Balance = *balance
    93  	return nil
    94  }
    95  
    96  // NEOBalanceFromBytes converts the serialized NEOBalance to a structure.
    97  func NEOBalanceFromBytes(b []byte) (*NEOBalance, error) {
    98  	balance := new(NEOBalance)
    99  	err := balanceFromBytes(b, balance)
   100  	if err != nil {
   101  		return nil, err
   102  	}
   103  	return balance, nil
   104  }
   105  
   106  // Bytes returns a serialized NEOBalance.
   107  func (s *NEOBalance) Bytes(sc *stackitem.SerializationContext) []byte {
   108  	item, _ := s.ToStackItem() // Never returns an error.
   109  	data, err := sc.Serialize(item, false)
   110  	if err != nil {
   111  		panic(err)
   112  	}
   113  	return data
   114  }
   115  
   116  // ToStackItem implements stackitem.Convertible interface. It never returns an error.
   117  func (s *NEOBalance) ToStackItem() (stackitem.Item, error) {
   118  	var voteItem stackitem.Item
   119  
   120  	if s.VoteTo != nil {
   121  		voteItem = stackitem.NewByteArray(s.VoteTo.Bytes())
   122  	} else {
   123  		voteItem = stackitem.Null{}
   124  	}
   125  	return stackitem.NewStruct([]stackitem.Item{
   126  		stackitem.NewBigInteger(&s.Balance),
   127  		stackitem.NewBigInteger(big.NewInt(int64(s.BalanceHeight))),
   128  		voteItem,
   129  		stackitem.NewBigInteger(&s.LastGasPerVote),
   130  	}), nil
   131  }
   132  
   133  // FromStackItem converts stackitem.Item to NEOBalance.
   134  func (s *NEOBalance) FromStackItem(item stackitem.Item) error {
   135  	structItem, ok := item.Value().([]stackitem.Item)
   136  	if !ok || len(structItem) < 3 {
   137  		return errors.New("invalid stackitem length")
   138  	}
   139  	balance, err := structItem[0].TryInteger()
   140  	if err != nil {
   141  		return fmt.Errorf("invalid balance stackitem: %w", err)
   142  	}
   143  	s.Balance = *balance
   144  	h, err := structItem[1].TryInteger()
   145  	if err != nil {
   146  		return fmt.Errorf("invalid heigh stackitem")
   147  	}
   148  	s.BalanceHeight = uint32(h.Int64())
   149  	if _, ok := structItem[2].(stackitem.Null); ok {
   150  		s.VoteTo = nil
   151  	} else {
   152  		bs, err := structItem[2].TryBytes()
   153  		if err != nil {
   154  			return fmt.Errorf("invalid public key stackitem: %w", err)
   155  		}
   156  		pub, err := keys.NewPublicKeyFromBytes(bs, elliptic.P256())
   157  		if err != nil {
   158  			return fmt.Errorf("invalid public key bytes: %w", err)
   159  		}
   160  		s.VoteTo = pub
   161  	}
   162  	if len(structItem) >= 4 {
   163  		lastGasPerVote, err := structItem[3].TryInteger()
   164  		if err != nil {
   165  			return fmt.Errorf("invalid last vote reward per neo stackitem: %w", err)
   166  		}
   167  		s.LastGasPerVote = *lastGasPerVote
   168  	}
   169  	return nil
   170  }