github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/rpcclient/neo/neo.go (about) 1 /* 2 Package neo provides an RPC-based wrapper for the NEOToken contract. 3 4 Safe methods are encapsulated into ContractReader structure while Contract provides 5 various methods to perform state-changing calls. 6 */ 7 package neo 8 9 import ( 10 "crypto/elliptic" 11 "fmt" 12 "math/big" 13 14 "github.com/google/uuid" 15 "github.com/nspcc-dev/neo-go/pkg/core/native/nativehashes" 16 "github.com/nspcc-dev/neo-go/pkg/core/state" 17 "github.com/nspcc-dev/neo-go/pkg/core/transaction" 18 "github.com/nspcc-dev/neo-go/pkg/crypto/keys" 19 "github.com/nspcc-dev/neo-go/pkg/neorpc/result" 20 "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17" 21 "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" 22 "github.com/nspcc-dev/neo-go/pkg/smartcontract" 23 "github.com/nspcc-dev/neo-go/pkg/util" 24 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 25 ) 26 27 const ( 28 setGasMethod = "setGasPerBlock" 29 setRegMethod = "setRegisterPrice" 30 ) 31 32 // Invoker is used by ContractReader to perform read-only calls. 33 type Invoker interface { 34 nep17.Invoker 35 36 CallAndExpandIterator(contract util.Uint160, method string, maxItems int, params ...any) (*result.Invoke, error) 37 TerminateSession(sessionID uuid.UUID) error 38 TraverseIterator(sessionID uuid.UUID, iterator *result.Iterator, num int) ([]stackitem.Item, error) 39 } 40 41 // Actor is used by Contract to create and send transactions. 42 type Actor interface { 43 nep17.Actor 44 Invoker 45 46 Run(script []byte) (*result.Invoke, error) 47 MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error) 48 MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error) 49 MakeUnsignedUncheckedRun(script []byte, sysFee int64, attrs []transaction.Attribute) (*transaction.Transaction, error) 50 SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) 51 Sign(tx *transaction.Transaction) error 52 SignAndSend(tx *transaction.Transaction) (util.Uint256, uint32, error) 53 } 54 55 // ContractReader represents safe (read-only) methods of NEO. It can be 56 // used to query various data. 57 type ContractReader struct { 58 nep17.TokenReader 59 60 invoker Invoker 61 } 62 63 // Contract provides full NEO interface, both safe and state-changing methods. 64 type Contract struct { 65 ContractReader 66 nep17.TokenWriter 67 68 actor Actor 69 } 70 71 // CandidateStateEvent represents a CandidateStateChanged NEO event. 72 type CandidateStateEvent struct { 73 Key *keys.PublicKey 74 Registered bool 75 Votes *big.Int 76 } 77 78 // CommitteeChangedEvent represents a CommitteeChanged NEO event. 79 type CommitteeChangedEvent struct { 80 Old []keys.PublicKey 81 New []keys.PublicKey 82 } 83 84 // VoteEvent represents a Vote NEO event. 85 type VoteEvent struct { 86 Account util.Uint160 87 From *keys.PublicKey 88 To *keys.PublicKey 89 Amount *big.Int 90 } 91 92 // ValidatorIterator is used for iterating over GetAllCandidates results. 93 type ValidatorIterator struct { 94 client Invoker 95 session uuid.UUID 96 iterator result.Iterator 97 } 98 99 // Hash stores the hash of the native NEOToken contract. 100 var Hash = nativehashes.NeoToken 101 102 // NewReader creates an instance of ContractReader to get data from the NEO 103 // contract. 104 func NewReader(invoker Invoker) *ContractReader { 105 return &ContractReader{*nep17.NewReader(invoker, Hash), invoker} 106 } 107 108 // New creates an instance of Contract to perform state-changing actions in the 109 // NEO contract. 110 func New(actor Actor) *Contract { 111 nep := nep17.New(actor, Hash) 112 return &Contract{ContractReader{nep.TokenReader, actor}, nep.TokenWriter, actor} 113 } 114 115 // GetAccountState returns current NEO balance state for the account which 116 // includes balance and voting data. It can return nil balance with no error 117 // if the account given has no NEO. 118 func (c *ContractReader) GetAccountState(account util.Uint160) (*state.NEOBalance, error) { 119 itm, err := unwrap.Item(c.invoker.Call(Hash, "getAccountState", account)) 120 if err != nil { 121 return nil, err 122 } 123 if _, ok := itm.(stackitem.Null); ok { 124 return nil, nil 125 } 126 res := new(state.NEOBalance) 127 err = res.FromStackItem(itm) 128 if err != nil { 129 return nil, err 130 } 131 return res, nil 132 } 133 134 // GetAllCandidates returns an iterator that allows to retrieve all registered 135 // validators from it. It depends on the server to provide proper session-based 136 // iterator, but can also work with expanded one. 137 func (c *ContractReader) GetAllCandidates() (*ValidatorIterator, error) { 138 sess, iter, err := unwrap.SessionIterator(c.invoker.Call(Hash, "getAllCandidates")) 139 if err != nil { 140 return nil, err 141 } 142 143 return &ValidatorIterator{ 144 client: c.invoker, 145 iterator: iter, 146 session: sess, 147 }, nil 148 } 149 150 // GetAllCandidatesExpanded is similar to GetAllCandidates (uses the same NEO 151 // method), but can be useful if the server used doesn't support sessions and 152 // doesn't expand iterators. It creates a script that will get num of result 153 // items from the iterator right in the VM and return them to you. It's only 154 // limited by VM stack and GAS available for RPC invocations. 155 func (c *ContractReader) GetAllCandidatesExpanded(num int) ([]result.Validator, error) { 156 arr, err := unwrap.Array(c.invoker.CallAndExpandIterator(Hash, "getAllCandidates", num)) 157 if err != nil { 158 return nil, err 159 } 160 return itemsToValidators(arr) 161 } 162 163 // Next returns the next set of elements from the iterator (up to num of them). 164 // It can return less than num elements in case iterator doesn't have that many 165 // or zero elements if the iterator has no more elements or the session is 166 // expired. 167 func (v *ValidatorIterator) Next(num int) ([]result.Validator, error) { 168 items, err := v.client.TraverseIterator(v.session, &v.iterator, num) 169 if err != nil { 170 return nil, err 171 } 172 return itemsToValidators(items) 173 } 174 175 // Terminate closes the iterator session used by ValidatorIterator (if it's 176 // session-based). 177 func (v *ValidatorIterator) Terminate() error { 178 if v.iterator.ID == nil { 179 return nil 180 } 181 return v.client.TerminateSession(v.session) 182 } 183 184 // GetCandidates returns the list of validators with their vote count. This 185 // method is mostly useful for historic invocations because the RPC protocol 186 // provides direct getcandidates call that returns more data and works faster. 187 // The contract only returns up to 256 candidates in response to this method, so 188 // if there are more of them on the network you will get a truncated result, use 189 // GetAllCandidates to solve this problem. 190 func (c *ContractReader) GetCandidates() ([]result.Validator, error) { 191 arr, err := unwrap.Array(c.invoker.Call(Hash, "getCandidates")) 192 if err != nil { 193 return nil, err 194 } 195 return itemsToValidators(arr) 196 } 197 198 func itemsToValidators(arr []stackitem.Item) ([]result.Validator, error) { 199 res := make([]result.Validator, len(arr)) 200 for i, itm := range arr { 201 str, ok := itm.Value().([]stackitem.Item) 202 if !ok { 203 return nil, fmt.Errorf("item #%d is not a structure", i) 204 } 205 if len(str) != 2 { 206 return nil, fmt.Errorf("item #%d has wrong length", i) 207 } 208 b, err := str[0].TryBytes() 209 if err != nil { 210 return nil, fmt.Errorf("item #%d has wrong key: %w", i, err) 211 } 212 k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256()) 213 if err != nil { 214 return nil, fmt.Errorf("item #%d has wrong key: %w", i, err) 215 } 216 votes, err := str[1].TryInteger() 217 if err != nil { 218 return nil, fmt.Errorf("item #%d has wrong votes: %w", i, err) 219 } 220 if !votes.IsInt64() { 221 return nil, fmt.Errorf("item #%d has too big number of votes", i) 222 } 223 res[i].PublicKey = *k 224 res[i].Votes = votes.Int64() 225 } 226 return res, nil 227 } 228 229 // GetCommittee returns the list of committee member public keys. This 230 // method is mostly useful for historic invocations because the RPC protocol 231 // provides direct getcommittee call that works faster. 232 func (c *ContractReader) GetCommittee() (keys.PublicKeys, error) { 233 return unwrap.ArrayOfPublicKeys(c.invoker.Call(Hash, "getCommittee")) 234 } 235 236 // GetCommitteeAddress returns the committee address. 237 func (c *ContractReader) GetCommitteeAddress() (util.Uint160, error) { 238 return unwrap.Uint160(c.invoker.Call(Hash, "getCommitteeAddress")) 239 } 240 241 // GetNextBlockValidators returns the list of validator keys that will sign the 242 // next block. This method is mostly useful for historic invocations because the 243 // RPC protocol provides direct getnextblockvalidators call that provides more 244 // data and works faster. 245 func (c *ContractReader) GetNextBlockValidators() (keys.PublicKeys, error) { 246 return unwrap.ArrayOfPublicKeys(c.invoker.Call(Hash, "getNextBlockValidators")) 247 } 248 249 // GetGasPerBlock returns the amount of GAS generated in each block. 250 func (c *ContractReader) GetGasPerBlock() (int64, error) { 251 return unwrap.Int64(c.invoker.Call(Hash, "getGasPerBlock")) 252 } 253 254 // GetRegisterPrice returns the price of candidate key registration. 255 func (c *ContractReader) GetRegisterPrice() (int64, error) { 256 return unwrap.Int64(c.invoker.Call(Hash, "getRegisterPrice")) 257 } 258 259 // UnclaimedGas allows to calculate the amount of GAS that will be generated if 260 // any NEO state change ("claim") is to happen for the given account at the given 261 // block number. This method is mostly useful for historic invocations because 262 // the RPC protocol provides direct getunclaimedgas method that works faster. 263 func (c *ContractReader) UnclaimedGas(account util.Uint160, end uint32) (*big.Int, error) { 264 return unwrap.BigInt(c.invoker.Call(Hash, "unclaimedGas", account, end)) 265 } 266 267 // RegisterCandidate creates and sends a transaction that adds the given key to 268 // the list of candidates that can be voted for. The return result from the 269 // "registerCandidate" method is checked to be true, so transaction fails (with 270 // FAULT state) if not successful. Notice that for this call to work it must be 271 // witnessed by the simple account derived from the given key, so use an 272 // appropriate Actor. The returned values are transaction hash, its 273 // ValidUntilBlock value and an error if any. 274 // 275 // Notice that unlike for all other methods the script for this one is not 276 // test-executed in its final form because most networks have registration price 277 // set to be much higher than typical RPC server allows to spend during 278 // test-execution. This adds some risk that it might fail on-chain, but in 279 // practice it's not likely to happen if signers are set up correctly. 280 func (c *Contract) RegisterCandidate(k *keys.PublicKey) (util.Uint256, uint32, error) { 281 tx, err := c.RegisterCandidateUnsigned(k) 282 if err != nil { 283 return util.Uint256{}, 0, err 284 } 285 return c.actor.SignAndSend(tx) 286 } 287 288 // RegisterCandidateTransaction creates a transaction that adds the given key to 289 // the list of candidates that can be voted for. The return result from the 290 // "registerCandidate" method is checked to be true, so transaction fails (with 291 // FAULT state) if not successful. Notice that for this call to work it must be 292 // witnessed by the simple account derived from the given key, so use an 293 // appropriate Actor. The transaction is signed, but not sent to the network, 294 // instead it's returned to the caller. 295 // 296 // Notice that unlike for all other methods the script for this one is not 297 // test-executed in its final form because most networks have registration price 298 // set to be much higher than typical RPC server allows to spend during 299 // test-execution. This adds some risk that it might fail on-chain, but in 300 // practice it's not likely to happen if signers are set up correctly. 301 func (c *Contract) RegisterCandidateTransaction(k *keys.PublicKey) (*transaction.Transaction, error) { 302 tx, err := c.RegisterCandidateUnsigned(k) 303 if err != nil { 304 return nil, err 305 } 306 err = c.actor.Sign(tx) 307 if err != nil { 308 return nil, err 309 } 310 return tx, nil 311 } 312 313 // RegisterCandidateUnsigned creates a transaction that adds the given key to 314 // the list of candidates that can be voted for. The return result from the 315 // "registerCandidate" method is checked to be true, so transaction fails (with 316 // FAULT state) if not successful. Notice that for this call to work it must be 317 // witnessed by the simple account derived from the given key, so use an 318 // appropriate Actor. The transaction is not signed and just returned to the 319 // caller. 320 // 321 // Notice that unlike for all other methods the script for this one is not 322 // test-executed in its final form because most networks have registration price 323 // set to be much higher than typical RPC server allows to spend during 324 // test-execution. This adds some risk that it might fail on-chain, but in 325 // practice it's not likely to happen if signers are set up correctly. 326 func (c *Contract) RegisterCandidateUnsigned(k *keys.PublicKey) (*transaction.Transaction, error) { 327 // It's an unregister script intentionally. 328 r, err := c.actor.Run(regScript(true, k)) 329 if err != nil { 330 return nil, err 331 } 332 regPrice, err := c.GetRegisterPrice() 333 if err != nil { 334 return nil, err 335 } 336 return c.actor.MakeUnsignedUncheckedRun(regScript(false, k), r.GasConsumed+regPrice, nil) 337 } 338 339 // UnregisterCandidate creates and sends a transaction that removes the key from 340 // the list of candidates that can be voted for. The return result from the 341 // "unregisterCandidate" method is checked to be true, so transaction fails (with 342 // FAULT state) if not successful. Notice that for this call to work it must be 343 // witnessed by the simple account derived from the given key, so use an 344 // appropriate Actor. The returned values are transaction hash, its 345 // ValidUntilBlock value and an error if any. 346 func (c *Contract) UnregisterCandidate(k *keys.PublicKey) (util.Uint256, uint32, error) { 347 return c.actor.SendRun(regScript(true, k)) 348 } 349 350 // UnregisterCandidateTransaction creates a transaction that removes the key from 351 // the list of candidates that can be voted for. The return result from the 352 // "unregisterCandidate" method is checked to be true, so transaction fails (with 353 // FAULT state) if not successful. Notice that for this call to work it must be 354 // witnessed by the simple account derived from the given key, so use an 355 // appropriate Actor. The transaction is signed, but not sent to the network, 356 // instead it's returned to the caller. 357 func (c *Contract) UnregisterCandidateTransaction(k *keys.PublicKey) (*transaction.Transaction, error) { 358 return c.actor.MakeRun(regScript(true, k)) 359 } 360 361 // UnregisterCandidateUnsigned creates a transaction that removes the key from 362 // the list of candidates that can be voted for. The return result from the 363 // "unregisterCandidate" method is checked to be true, so transaction fails (with 364 // FAULT state) if not successful. Notice that for this call to work it must be 365 // witnessed by the simple account derived from the given key, so use an 366 // appropriate Actor. The transaction is not signed and just returned to the 367 // caller. 368 func (c *Contract) UnregisterCandidateUnsigned(k *keys.PublicKey) (*transaction.Transaction, error) { 369 return c.actor.MakeUnsignedRun(regScript(true, k), nil) 370 } 371 372 func regScript(unreg bool, k *keys.PublicKey) []byte { 373 var method = "registerCandidate" 374 375 if unreg { 376 method = "unregisterCandidate" 377 } 378 379 // We know parameters exactly (unlike with nep17.Transfer), so this can't fail. 380 script, _ := smartcontract.CreateCallWithAssertScript(Hash, method, k.Bytes()) 381 return script 382 } 383 384 // Vote creates and sends a transaction that casts a vote from the given account 385 // to the given key which can be nil (in which case any previous vote is removed). 386 // The return result from the "vote" method is checked to be true, so transaction 387 // fails (with FAULT state) if voting is not successful. The returned values are 388 // transaction hash, its ValidUntilBlock value and an error if any. 389 func (c *Contract) Vote(account util.Uint160, voteTo *keys.PublicKey) (util.Uint256, uint32, error) { 390 return c.actor.SendRun(voteScript(account, voteTo)) 391 } 392 393 // VoteTransaction creates a transaction that casts a vote from the given account 394 // to the given key which can be nil (in which case any previous vote is removed). 395 // The return result from the "vote" method is checked to be true, so transaction 396 // fails (with FAULT state) if voting is not successful. The transaction is signed, 397 // but not sent to the network, instead it's returned to the caller. 398 func (c *Contract) VoteTransaction(account util.Uint160, voteTo *keys.PublicKey) (*transaction.Transaction, error) { 399 return c.actor.MakeRun(voteScript(account, voteTo)) 400 } 401 402 // VoteUnsigned creates a transaction that casts a vote from the given account 403 // to the given key which can be nil (in which case any previous vote is removed). 404 // The return result from the "vote" method is checked to be true, so transaction 405 // fails (with FAULT state) if voting is not successful. The transaction is not 406 // signed and just returned to the caller. 407 func (c *Contract) VoteUnsigned(account util.Uint160, voteTo *keys.PublicKey) (*transaction.Transaction, error) { 408 return c.actor.MakeUnsignedRun(voteScript(account, voteTo), nil) 409 } 410 411 func voteScript(account util.Uint160, voteTo *keys.PublicKey) []byte { 412 var param any 413 414 if voteTo != nil { 415 param = voteTo.Bytes() 416 } 417 // We know parameters exactly (unlike with nep17.Transfer), so this can't fail. 418 script, _ := smartcontract.CreateCallWithAssertScript(Hash, "vote", account, param) 419 return script 420 } 421 422 // SetGasPerBlock creates and sends a transaction that sets the new amount of 423 // GAS to be generated in each block. The action is successful when transaction 424 // ends in HALT state. Notice that this setting can be changed only by the 425 // network's committee, so use an appropriate Actor. The returned values are 426 // transaction hash, its ValidUntilBlock value and an error if any. 427 func (c *Contract) SetGasPerBlock(gas int64) (util.Uint256, uint32, error) { 428 return c.actor.SendCall(Hash, setGasMethod, gas) 429 } 430 431 // SetGasPerBlockTransaction creates a transaction that sets the new amount of 432 // GAS to be generated in each block. The action is successful when transaction 433 // ends in HALT state. Notice that this setting can be changed only by the 434 // network's committee, so use an appropriate Actor. The transaction is signed, 435 // but not sent to the network, instead it's returned to the caller. 436 func (c *Contract) SetGasPerBlockTransaction(gas int64) (*transaction.Transaction, error) { 437 return c.actor.MakeCall(Hash, setGasMethod, gas) 438 } 439 440 // SetGasPerBlockUnsigned creates a transaction that sets the new amount of 441 // GAS to be generated in each block. The action is successful when transaction 442 // ends in HALT state. Notice that this setting can be changed only by the 443 // network's committee, so use an appropriate Actor. The transaction is not 444 // signed and just returned to the caller. 445 func (c *Contract) SetGasPerBlockUnsigned(gas int64) (*transaction.Transaction, error) { 446 return c.actor.MakeUnsignedCall(Hash, setGasMethod, nil, gas) 447 } 448 449 // SetRegisterPrice creates and sends a transaction that sets the new candidate 450 // registration price (in GAS). The action is successful when transaction 451 // ends in HALT state. Notice that this setting can be changed only by the 452 // network's committee, so use an appropriate Actor. The returned values are 453 // transaction hash, its ValidUntilBlock value and an error if any. 454 func (c *Contract) SetRegisterPrice(price int64) (util.Uint256, uint32, error) { 455 return c.actor.SendCall(Hash, setRegMethod, price) 456 } 457 458 // SetRegisterPriceTransaction creates a transaction that sets the new candidate 459 // registration price (in GAS). The action is successful when transaction 460 // ends in HALT state. Notice that this setting can be changed only by the 461 // network's committee, so use an appropriate Actor. The transaction is signed, 462 // but not sent to the network, instead it's returned to the caller. 463 func (c *Contract) SetRegisterPriceTransaction(price int64) (*transaction.Transaction, error) { 464 return c.actor.MakeCall(Hash, setRegMethod, price) 465 } 466 467 // SetRegisterPriceUnsigned creates a transaction that sets the new candidate 468 // registration price (in GAS). The action is successful when transaction 469 // ends in HALT state. Notice that this setting can be changed only by the 470 // network's committee, so use an appropriate Actor. The transaction is not 471 // signed and just returned to the caller. 472 func (c *Contract) SetRegisterPriceUnsigned(price int64) (*transaction.Transaction, error) { 473 return c.actor.MakeUnsignedCall(Hash, setRegMethod, nil, price) 474 }