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 }