github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/rpcclient/nns/contract.go (about) 1 // Package nns provide RPC wrappers for the non-native NNS contract. 2 // This is Neo N3 NNS contract wrapper, the source code of the contract can be found here: 3 // https://github.com/neo-project/non-native-contracts/blob/8d72b92e5e5705d763232bcc24784ced0fb8fc87/src/NameService/NameService.cs 4 package nns 5 6 import ( 7 "errors" 8 "fmt" 9 "math/big" 10 "unicode/utf8" 11 12 "github.com/nspcc-dev/neo-go/pkg/core/transaction" 13 "github.com/nspcc-dev/neo-go/pkg/neorpc/result" 14 "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11" 15 "github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap" 16 "github.com/nspcc-dev/neo-go/pkg/smartcontract" 17 "github.com/nspcc-dev/neo-go/pkg/util" 18 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 19 ) 20 21 // MaxNameLength is the max length of domain name. 22 const MaxNameLength = 255 23 24 // SetAdminEvent represents "SetAdmin" event emitted by the contract. 25 type SetAdminEvent struct { 26 Name string 27 OldAdmin util.Uint160 28 NewAdmin util.Uint160 29 } 30 31 // RenewEvent represents "Renew" event emitted by the contract. 32 type RenewEvent struct { 33 Name string 34 OldExpiration *big.Int 35 NewExpiration *big.Int 36 } 37 38 // Invoker is used by ContractReader to call various safe methods. 39 type Invoker interface { 40 nep11.Invoker 41 } 42 43 // Actor is used by Contract to call state-changing methods. 44 type Actor interface { 45 Invoker 46 nep11.Actor 47 48 MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error) 49 MakeRun(script []byte) (*transaction.Transaction, error) 50 MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error) 51 MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error) 52 SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error) 53 SendRun(script []byte) (util.Uint256, uint32, error) 54 } 55 56 // ContractReader implements safe contract methods. 57 type ContractReader struct { 58 nep11.NonDivisibleReader 59 60 invoker Invoker 61 hash util.Uint160 62 } 63 64 // Contract provides full NeoNameService interface, both safe and state-changing methods. 65 type Contract struct { 66 ContractReader 67 nep11.BaseWriter 68 69 actor Actor 70 hash util.Uint160 71 } 72 73 // NewReader creates an instance of ContractReader using provided contract hash and the given Invoker. 74 func NewReader(invoker Invoker, hash util.Uint160) *ContractReader { 75 return &ContractReader{*nep11.NewNonDivisibleReader(invoker, hash), invoker, hash} 76 } 77 78 // New creates an instance of Contract using provided contract hash and the given Actor. 79 func New(actor Actor, hash util.Uint160) *Contract { 80 var nep11ndt = nep11.NewNonDivisible(actor, hash) 81 return &Contract{ContractReader{nep11ndt.NonDivisibleReader, actor, hash}, nep11ndt.BaseWriter, actor, hash} 82 } 83 84 // Roots invokes `roots` method of contract. 85 func (c *ContractReader) Roots() (*RootIterator, error) { 86 sess, iter, err := unwrap.SessionIterator(c.invoker.Call(c.hash, "roots")) 87 if err != nil { 88 return nil, err 89 } 90 91 return &RootIterator{ 92 client: c.invoker, 93 iterator: iter, 94 session: sess, 95 }, nil 96 } 97 98 // RootsExpanded is similar to Roots (uses the same contract 99 // method), but can be useful if the server used doesn't support sessions and 100 // doesn't expand iterators. It creates a script that will get the specified 101 // number of result items from the iterator right in the VM and return them to 102 // you. It's only limited by VM stack and GAS available for RPC invocations. 103 func (c *ContractReader) RootsExpanded(_numOfIteratorItems int) ([]string, error) { 104 arr, err := unwrap.Array(c.invoker.CallAndExpandIterator(c.hash, "roots", _numOfIteratorItems)) 105 if err != nil { 106 return nil, err 107 } 108 109 return itemsToRoots(arr) 110 } 111 112 // GetPrice invokes `getPrice` method of contract. 113 func (c *ContractReader) GetPrice(length uint8) (*big.Int, error) { 114 return unwrap.BigInt(c.invoker.Call(c.hash, "getPrice", length)) 115 } 116 117 // IsAvailable invokes `isAvailable` method of contract. 118 func (c *ContractReader) IsAvailable(name string) (bool, error) { 119 return unwrap.Bool(c.invoker.Call(c.hash, "isAvailable", name)) 120 } 121 122 // GetRecord invokes `getRecord` method of contract. 123 func (c *ContractReader) GetRecord(name string, typev RecordType) (string, error) { 124 return unwrap.UTF8String(c.invoker.Call(c.hash, "getRecord", name, typev)) 125 } 126 127 // GetAllRecords invokes `getAllRecords` method of contract. 128 func (c *ContractReader) GetAllRecords(name string) (*RecordIterator, error) { 129 sess, iter, err := unwrap.SessionIterator(c.invoker.Call(c.hash, "getAllRecords", name)) 130 if err != nil { 131 return nil, err 132 } 133 134 return &RecordIterator{ 135 client: c.invoker, 136 iterator: iter, 137 session: sess, 138 }, nil 139 } 140 141 // GetAllRecordsExpanded is similar to GetAllRecords (uses the same contract 142 // method), but can be useful if the server used doesn't support sessions and 143 // doesn't expand iterators. It creates a script that will get the specified 144 // number of result items from the iterator right in the VM and return them to 145 // you. It's only limited by VM stack and GAS available for RPC invocations. 146 func (c *ContractReader) GetAllRecordsExpanded(name string, _numOfIteratorItems int) ([]RecordState, error) { 147 arr, err := unwrap.Array(c.invoker.CallAndExpandIterator(c.hash, "getAllRecords", _numOfIteratorItems, name)) 148 if err != nil { 149 return nil, err 150 } 151 return itemsToRecords(arr) 152 } 153 154 // Resolve invokes `resolve` method of contract. 155 func (c *ContractReader) Resolve(name string, typev RecordType) (string, error) { 156 return unwrap.UTF8String(c.invoker.Call(c.hash, "resolve", name, int64(typev))) 157 } 158 159 // Update creates a transaction invoking `update` method of the contract. 160 // This transaction is signed and immediately sent to the network. 161 // The values returned are its hash, ValidUntilBlock value and error if any. 162 func (c *Contract) Update(nef []byte, manifest string) (util.Uint256, uint32, error) { 163 return c.actor.SendCall(c.hash, "update", nef, manifest) 164 } 165 166 // UpdateTransaction creates a transaction invoking `update` method of the contract. 167 // This transaction is signed, but not sent to the network, instead it's 168 // returned to the caller. 169 func (c *Contract) UpdateTransaction(nef []byte, manifest string) (*transaction.Transaction, error) { 170 return c.actor.MakeCall(c.hash, "update", nef, manifest) 171 } 172 173 // UpdateUnsigned creates a transaction invoking `update` method of the contract. 174 // This transaction is not signed, it's simply returned to the caller. 175 // Any fields of it that do not affect fees can be changed (ValidUntilBlock, 176 // Nonce), fee values (NetworkFee, SystemFee) can be increased as well. 177 func (c *Contract) UpdateUnsigned(nef []byte, manifest string) (*transaction.Transaction, error) { 178 return c.actor.MakeUnsignedCall(c.hash, "update", nil, nef, manifest) 179 } 180 181 // AddRoot creates a transaction invoking `addRoot` method of the contract. 182 // This transaction is signed and immediately sent to the network. 183 // The values returned are its hash, ValidUntilBlock value and error if any. 184 func (c *Contract) AddRoot(root string) (util.Uint256, uint32, error) { 185 return c.actor.SendCall(c.hash, "addRoot", root) 186 } 187 188 // AddRootTransaction creates a transaction invoking `addRoot` method of the contract. 189 // This transaction is signed, but not sent to the network, instead it's 190 // returned to the caller. 191 func (c *Contract) AddRootTransaction(root string) (*transaction.Transaction, error) { 192 return c.actor.MakeCall(c.hash, "addRoot", root) 193 } 194 195 // AddRootUnsigned creates a transaction invoking `addRoot` method of the contract. 196 // This transaction is not signed, it's simply returned to the caller. 197 // Any fields of it that do not affect fees can be changed (ValidUntilBlock, 198 // Nonce), fee values (NetworkFee, SystemFee) can be increased as well. 199 func (c *Contract) AddRootUnsigned(root string) (*transaction.Transaction, error) { 200 return c.actor.MakeUnsignedCall(c.hash, "addRoot", nil, root) 201 } 202 203 // SetPrice creates a transaction invoking `setPrice` method of the contract. 204 // This transaction is signed and immediately sent to the network. 205 // The values returned are its hash, ValidUntilBlock value and error if any. 206 func (c *Contract) SetPrice(priceList []int64) (util.Uint256, uint32, error) { 207 anyPriceList := make([]any, len(priceList)) 208 for i, price := range priceList { 209 anyPriceList[i] = price 210 } 211 return c.actor.SendCall(c.hash, "setPrice", anyPriceList) 212 } 213 214 // SetPriceTransaction creates a transaction invoking `setPrice` method of the contract. 215 // This transaction is signed, but not sent to the network, instead it's 216 // returned to the caller. 217 func (c *Contract) SetPriceTransaction(priceList []int64) (*transaction.Transaction, error) { 218 anyPriceList := make([]any, len(priceList)) 219 for i, price := range priceList { 220 anyPriceList[i] = price 221 } 222 return c.actor.MakeCall(c.hash, "setPrice", anyPriceList) 223 } 224 225 // SetPriceUnsigned creates a transaction invoking `setPrice` method of the contract. 226 // This transaction is not signed, it's simply returned to the caller. 227 // Any fields of it that do not affect fees can be changed (ValidUntilBlock, 228 // Nonce), fee values (NetworkFee, SystemFee) can be increased as well. 229 func (c *Contract) SetPriceUnsigned(priceList []int64) (*transaction.Transaction, error) { 230 anyPriceList := make([]any, len(priceList)) 231 for i, price := range priceList { 232 anyPriceList[i] = price 233 } 234 return c.actor.MakeUnsignedCall(c.hash, "setPrice", nil, anyPriceList) 235 } 236 237 func (c *Contract) scriptForRegister(name string, owner util.Uint160) ([]byte, error) { 238 return smartcontract.CreateCallWithAssertScript(c.hash, "register", name, owner) 239 } 240 241 // Register creates a transaction invoking `register` method of the contract. 242 // This transaction is signed and immediately sent to the network. 243 // The values returned are its hash, ValidUntilBlock value and error if any. 244 func (c *Contract) Register(name string, owner util.Uint160) (util.Uint256, uint32, error) { 245 script, err := c.scriptForRegister(name, owner) 246 if err != nil { 247 return util.Uint256{}, 0, err 248 } 249 return c.actor.SendRun(script) 250 } 251 252 // RegisterTransaction creates a transaction invoking `register` method of the contract. 253 // This transaction is signed, but not sent to the network, instead it's 254 // returned to the caller. 255 func (c *Contract) RegisterTransaction(name string, owner util.Uint160) (*transaction.Transaction, error) { 256 script, err := c.scriptForRegister(name, owner) 257 if err != nil { 258 return nil, err 259 } 260 return c.actor.MakeRun(script) 261 } 262 263 // RegisterUnsigned creates a transaction invoking `register` method of the contract. 264 // This transaction is not signed, it's simply returned to the caller. 265 // Any fields of it that do not affect fees can be changed (ValidUntilBlock, 266 // Nonce), fee values (NetworkFee, SystemFee) can be increased as well. 267 func (c *Contract) RegisterUnsigned(name string, owner util.Uint160) (*transaction.Transaction, error) { 268 script, err := c.scriptForRegister(name, owner) 269 if err != nil { 270 return nil, err 271 } 272 return c.actor.MakeUnsignedRun(script, nil) 273 } 274 275 // Renew creates a transaction invoking `renew` method of the contract. 276 // This transaction is signed and immediately sent to the network. 277 // The values returned are its hash, ValidUntilBlock value and error if any. 278 func (c *Contract) Renew(name string) (util.Uint256, uint32, error) { 279 return c.actor.SendCall(c.hash, "renew", name) 280 } 281 282 // RenewTransaction creates a transaction invoking `renew` method of the contract. 283 // This transaction is signed, but not sent to the network, instead it's 284 // returned to the caller. 285 func (c *Contract) RenewTransaction(name string) (*transaction.Transaction, error) { 286 return c.actor.MakeCall(c.hash, "renew", name) 287 } 288 289 // RenewUnsigned creates a transaction invoking `renew` method of the contract. 290 // This transaction is not signed, it's simply returned to the caller. 291 // Any fields of it that do not affect fees can be changed (ValidUntilBlock, 292 // Nonce), fee values (NetworkFee, SystemFee) can be increased as well. 293 func (c *Contract) RenewUnsigned(name string) (*transaction.Transaction, error) { 294 return c.actor.MakeUnsignedCall(c.hash, "renew", nil, name) 295 } 296 297 // Renew2 creates a transaction invoking `renew` method of the contract. 298 // This transaction is signed and immediately sent to the network. 299 // The values returned are its hash, ValidUntilBlock value and error if any. 300 func (c *Contract) Renew2(name string, years int64) (util.Uint256, uint32, error) { 301 return c.actor.SendCall(c.hash, "renew", name, years) 302 } 303 304 // Renew2Transaction creates a transaction invoking `renew` method of the contract. 305 // This transaction is signed, but not sent to the network, instead it's 306 // returned to the caller. 307 func (c *Contract) Renew2Transaction(name string, years int64) (*transaction.Transaction, error) { 308 return c.actor.MakeCall(c.hash, "renew", name, years) 309 } 310 311 // Renew2Unsigned creates a transaction invoking `renew` method of the contract. 312 // This transaction is not signed, it's simply returned to the caller. 313 // Any fields of it that do not affect fees can be changed (ValidUntilBlock, 314 // Nonce), fee values (NetworkFee, SystemFee) can be increased as well. 315 func (c *Contract) Renew2Unsigned(name string, years int64) (*transaction.Transaction, error) { 316 return c.actor.MakeUnsignedCall(c.hash, "renew", nil, name, years) 317 } 318 319 // SetAdmin creates a transaction invoking `setAdmin` method of the contract. 320 // This transaction is signed and immediately sent to the network. 321 // The values returned are its hash, ValidUntilBlock value and error if any. 322 func (c *Contract) SetAdmin(name string, admin util.Uint160) (util.Uint256, uint32, error) { 323 return c.actor.SendCall(c.hash, "setAdmin", name, admin) 324 } 325 326 // SetAdminTransaction creates a transaction invoking `setAdmin` method of the contract. 327 // This transaction is signed, but not sent to the network, instead it's 328 // returned to the caller. 329 func (c *Contract) SetAdminTransaction(name string, admin util.Uint160) (*transaction.Transaction, error) { 330 return c.actor.MakeCall(c.hash, "setAdmin", name, admin) 331 } 332 333 // SetAdminUnsigned creates a transaction invoking `setAdmin` method of the contract. 334 // This transaction is not signed, it's simply returned to the caller. 335 // Any fields of it that do not affect fees can be changed (ValidUntilBlock, 336 // Nonce), fee values (NetworkFee, SystemFee) can be increased as well. 337 func (c *Contract) SetAdminUnsigned(name string, admin util.Uint160) (*transaction.Transaction, error) { 338 return c.actor.MakeUnsignedCall(c.hash, "setAdmin", nil, name, admin) 339 } 340 341 // SetRecord creates a transaction invoking `setRecord` method of the contract. 342 // This transaction is signed and immediately sent to the network. 343 // The values returned are its hash, ValidUntilBlock value and error if any. 344 func (c *Contract) SetRecord(name string, typev RecordType, data string) (util.Uint256, uint32, error) { 345 return c.actor.SendCall(c.hash, "setRecord", name, typev, data) 346 } 347 348 // SetRecordTransaction creates a transaction invoking `setRecord` method of the contract. 349 // This transaction is signed, but not sent to the network, instead it's 350 // returned to the caller. 351 func (c *Contract) SetRecordTransaction(name string, typev RecordType, data string) (*transaction.Transaction, error) { 352 return c.actor.MakeCall(c.hash, "setRecord", name, typev, data) 353 } 354 355 // SetRecordUnsigned creates a transaction invoking `setRecord` method of the contract. 356 // This transaction is not signed, it's simply returned to the caller. 357 // Any fields of it that do not affect fees can be changed (ValidUntilBlock, 358 // Nonce), fee values (NetworkFee, SystemFee) can be increased as well. 359 func (c *Contract) SetRecordUnsigned(name string, typev RecordType, data string) (*transaction.Transaction, error) { 360 return c.actor.MakeUnsignedCall(c.hash, "setRecord", nil, name, typev, data) 361 } 362 363 // DeleteRecord creates a transaction invoking `deleteRecord` method of the contract. 364 // This transaction is signed and immediately sent to the network. 365 // The values returned are its hash, ValidUntilBlock value and error if any. 366 func (c *Contract) DeleteRecord(name string, typev RecordType) (util.Uint256, uint32, error) { 367 return c.actor.SendCall(c.hash, "deleteRecord", name, typev) 368 } 369 370 // DeleteRecordTransaction creates a transaction invoking `deleteRecord` method of the contract. 371 // This transaction is signed, but not sent to the network, instead it's 372 // returned to the caller. 373 func (c *Contract) DeleteRecordTransaction(name string, typev RecordType) (*transaction.Transaction, error) { 374 return c.actor.MakeCall(c.hash, "deleteRecord", name, typev) 375 } 376 377 // DeleteRecordUnsigned creates a transaction invoking `deleteRecord` method of the contract. 378 // This transaction is not signed, it's simply returned to the caller. 379 // Any fields of it that do not affect fees can be changed (ValidUntilBlock, 380 // Nonce), fee values (NetworkFee, SystemFee) can be increased as well. 381 func (c *Contract) DeleteRecordUnsigned(name string, typev RecordType) (*transaction.Transaction, error) { 382 return c.actor.MakeUnsignedCall(c.hash, "deleteRecord", nil, name, typev) 383 } 384 385 // SetAdminEventsFromApplicationLog retrieves a set of all emitted events 386 // with "SetAdmin" name from the provided [result.ApplicationLog]. 387 func SetAdminEventsFromApplicationLog(log *result.ApplicationLog) ([]*SetAdminEvent, error) { 388 if log == nil { 389 return nil, errors.New("nil application log") 390 } 391 392 var res []*SetAdminEvent 393 for i, ex := range log.Executions { 394 for j, e := range ex.Events { 395 if e.Name != "SetAdmin" { 396 continue 397 } 398 event := new(SetAdminEvent) 399 err := event.FromStackItem(e.Item) 400 if err != nil { 401 return nil, fmt.Errorf("failed to deserialize SetAdminEvent from stackitem (execution #%d, event #%d): %w", i, j, err) 402 } 403 res = append(res, event) 404 } 405 } 406 407 return res, nil 408 } 409 410 // FromStackItem converts provided [stackitem.Array] to SetAdminEvent or 411 // returns an error if it's not possible to do to so. 412 func (e *SetAdminEvent) FromStackItem(item *stackitem.Array) error { 413 if item == nil { 414 return errors.New("nil item") 415 } 416 arr, ok := item.Value().([]stackitem.Item) 417 if !ok { 418 return errors.New("not an array") 419 } 420 if len(arr) != 3 { 421 return errors.New("wrong number of structure elements") 422 } 423 424 var ( 425 index = -1 426 err error 427 ) 428 index++ 429 e.Name, err = func(item stackitem.Item) (string, error) { 430 b, err := item.TryBytes() 431 if err != nil { 432 return "", err 433 } 434 if !utf8.Valid(b) { 435 return "", errors.New("not a UTF-8 string") 436 } 437 return string(b), nil 438 }(arr[index]) 439 if err != nil { 440 return fmt.Errorf("field Name: %w", err) 441 } 442 443 index++ 444 e.OldAdmin, err = func(item stackitem.Item) (util.Uint160, error) { 445 b, err := item.TryBytes() 446 if err != nil { 447 return util.Uint160{}, err 448 } 449 u, err := util.Uint160DecodeBytesBE(b) 450 if err != nil { 451 return util.Uint160{}, err 452 } 453 return u, nil 454 }(arr[index]) 455 if err != nil { 456 return fmt.Errorf("field OldAdmin: %w", err) 457 } 458 459 index++ 460 e.NewAdmin, err = func(item stackitem.Item) (util.Uint160, error) { 461 b, err := item.TryBytes() 462 if err != nil { 463 return util.Uint160{}, err 464 } 465 u, err := util.Uint160DecodeBytesBE(b) 466 if err != nil { 467 return util.Uint160{}, err 468 } 469 return u, nil 470 }(arr[index]) 471 if err != nil { 472 return fmt.Errorf("field NewAdmin: %w", err) 473 } 474 475 return nil 476 } 477 478 // RenewEventsFromApplicationLog retrieves a set of all emitted events 479 // with "Renew" name from the provided [result.ApplicationLog]. 480 func RenewEventsFromApplicationLog(log *result.ApplicationLog) ([]*RenewEvent, error) { 481 if log == nil { 482 return nil, errors.New("nil application log") 483 } 484 485 var res []*RenewEvent 486 for i, ex := range log.Executions { 487 for j, e := range ex.Events { 488 if e.Name != "Renew" { 489 continue 490 } 491 event := new(RenewEvent) 492 err := event.FromStackItem(e.Item) 493 if err != nil { 494 return nil, fmt.Errorf("failed to deserialize RenewEvent from stackitem (execution #%d, event #%d): %w", i, j, err) 495 } 496 res = append(res, event) 497 } 498 } 499 500 return res, nil 501 } 502 503 // FromStackItem converts provided [stackitem.Array] to RenewEvent or 504 // returns an error if it's not possible to do to so. 505 func (e *RenewEvent) FromStackItem(item *stackitem.Array) error { 506 if item == nil { 507 return errors.New("nil item") 508 } 509 arr, ok := item.Value().([]stackitem.Item) 510 if !ok { 511 return errors.New("not an array") 512 } 513 if len(arr) != 3 { 514 return errors.New("wrong number of structure elements") 515 } 516 517 var ( 518 index = -1 519 err error 520 ) 521 index++ 522 e.Name, err = func(item stackitem.Item) (string, error) { 523 b, err := item.TryBytes() 524 if err != nil { 525 return "", err 526 } 527 if !utf8.Valid(b) { 528 return "", errors.New("not a UTF-8 string") 529 } 530 return string(b), nil 531 }(arr[index]) 532 if err != nil { 533 return fmt.Errorf("field Name: %w", err) 534 } 535 536 index++ 537 e.OldExpiration, err = arr[index].TryInteger() 538 if err != nil { 539 return fmt.Errorf("field OldExpiration: %w", err) 540 } 541 542 index++ 543 e.NewExpiration, err = arr[index].TryInteger() 544 if err != nil { 545 return fmt.Errorf("field NewExpiration: %w", err) 546 } 547 548 return nil 549 }