forked from TrueCloudLab/neoneo-go
185 lines
4.4 KiB
Go
185 lines
4.4 KiB
Go
package state
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"math/big"
|
|
"sort"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
|
)
|
|
|
|
// NFTTokenState represents state of nonfungible token.
|
|
type NFTTokenState struct {
|
|
Owner util.Uint160
|
|
Name string
|
|
Description string
|
|
}
|
|
|
|
// NFTAccountState represents state of nonfunglible account.
|
|
type NFTAccountState struct {
|
|
NEP17BalanceState
|
|
Tokens [][]byte
|
|
}
|
|
|
|
// Base returns base class.
|
|
func (s *NFTTokenState) Base() *NFTTokenState {
|
|
return s
|
|
}
|
|
|
|
// ToStackItem converts NFTTokenState to stackitem.
|
|
func (s *NFTTokenState) ToStackItem() stackitem.Item {
|
|
owner := s.Owner
|
|
return stackitem.NewStruct([]stackitem.Item{
|
|
stackitem.NewByteArray(owner.BytesBE()),
|
|
stackitem.NewByteArray([]byte(s.Name)),
|
|
stackitem.NewByteArray([]byte(s.Description)),
|
|
})
|
|
}
|
|
|
|
// EncodeBinary implements io.Serializable.
|
|
func (s *NFTTokenState) EncodeBinary(w *io.BinWriter) {
|
|
stackitem.EncodeBinaryStackItem(s.ToStackItem(), w)
|
|
}
|
|
|
|
// FromStackItem converts stackitem to NFTTokenState.
|
|
func (s *NFTTokenState) FromStackItem(item stackitem.Item) error {
|
|
arr, ok := item.Value().([]stackitem.Item)
|
|
if !ok || len(arr) < 3 {
|
|
return errors.New("invalid stack item")
|
|
}
|
|
|
|
bs, err := arr[0].TryBytes()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
owner, err := util.Uint160DecodeBytesBE(bs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
name, err := stackitem.ToString(arr[1])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
desc, err := stackitem.ToString(arr[2])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
s.Owner = owner
|
|
s.Name = name
|
|
s.Description = desc
|
|
return nil
|
|
}
|
|
|
|
// DecodeBinary implements io.Serializable.
|
|
func (s *NFTTokenState) DecodeBinary(r *io.BinReader) {
|
|
item := stackitem.DecodeBinaryStackItem(r)
|
|
if r.Err == nil {
|
|
r.Err = s.FromStackItem(item)
|
|
}
|
|
}
|
|
|
|
// ToMap converts NFTTokenState to Map stackitem.
|
|
func (s *NFTTokenState) ToMap() *stackitem.Map {
|
|
return stackitem.NewMapWithValue([]stackitem.MapElement{
|
|
{
|
|
Key: stackitem.NewByteArray([]byte("name")),
|
|
Value: stackitem.NewByteArray([]byte(s.Name)),
|
|
},
|
|
{
|
|
Key: stackitem.NewByteArray([]byte("description")),
|
|
Value: stackitem.NewByteArray([]byte(s.Description)),
|
|
},
|
|
})
|
|
}
|
|
|
|
// ID returns token id.
|
|
func (s *NFTTokenState) ID() []byte {
|
|
return []byte(s.Name)
|
|
}
|
|
|
|
// ToStackItem converts NFTAccountState to stackitem.
|
|
func (s *NFTAccountState) ToStackItem() stackitem.Item {
|
|
st := s.NEP17BalanceState.toStackItem().(*stackitem.Struct)
|
|
arr := make([]stackitem.Item, len(s.Tokens))
|
|
for i := range arr {
|
|
arr[i] = stackitem.NewByteArray(s.Tokens[i])
|
|
}
|
|
st.Append(stackitem.NewArray(arr))
|
|
return st
|
|
}
|
|
|
|
// FromStackItem converts stackitem to NFTAccountState.
|
|
func (s *NFTAccountState) FromStackItem(item stackitem.Item) error {
|
|
s.NEP17BalanceState.fromStackItem(item)
|
|
arr := item.Value().([]stackitem.Item)
|
|
if len(arr) < 2 {
|
|
return errors.New("invalid stack item")
|
|
}
|
|
arr, ok := arr[1].Value().([]stackitem.Item)
|
|
if !ok {
|
|
return errors.New("invalid stack item")
|
|
}
|
|
s.Tokens = make([][]byte, len(arr))
|
|
for i := range s.Tokens {
|
|
bs, err := arr[i].TryBytes()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
s.Tokens[i] = bs
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// EncodeBinary implements io.Serializable.
|
|
func (s *NFTAccountState) EncodeBinary(w *io.BinWriter) {
|
|
stackitem.EncodeBinaryStackItem(s.ToStackItem(), w)
|
|
}
|
|
|
|
// DecodeBinary implements io.Serializable.
|
|
func (s *NFTAccountState) DecodeBinary(r *io.BinReader) {
|
|
item := stackitem.DecodeBinaryStackItem(r)
|
|
if r.Err == nil {
|
|
r.Err = s.FromStackItem(item)
|
|
}
|
|
}
|
|
|
|
func (s *NFTAccountState) index(tokenID []byte) (int, bool) {
|
|
lt := len(s.Tokens)
|
|
index := sort.Search(lt, func(i int) bool {
|
|
return bytes.Compare(s.Tokens[i], tokenID) >= 0
|
|
})
|
|
return index, index < lt && bytes.Equal(s.Tokens[index], tokenID)
|
|
}
|
|
|
|
// Add adds token id to the set of account tokens
|
|
// and returns true on success.
|
|
func (s *NFTAccountState) Add(tokenID []byte) bool {
|
|
index, isPresent := s.index(tokenID)
|
|
if isPresent {
|
|
return false
|
|
}
|
|
|
|
s.Balance.Add(&s.Balance, big.NewInt(1))
|
|
s.Tokens = append(s.Tokens, []byte{})
|
|
copy(s.Tokens[index+1:], s.Tokens[index:])
|
|
s.Tokens[index] = tokenID
|
|
return true
|
|
}
|
|
|
|
// Remove removes token id to the set of account tokens
|
|
// and returns true on success.
|
|
func (s *NFTAccountState) Remove(tokenID []byte) bool {
|
|
index, isPresent := s.index(tokenID)
|
|
if !isPresent {
|
|
return false
|
|
}
|
|
|
|
s.Balance.Sub(&s.Balance, big.NewInt(1))
|
|
copy(s.Tokens[index:], s.Tokens[index+1:])
|
|
s.Tokens = s.Tokens[:len(s.Tokens)-1]
|
|
return true
|
|
}
|