github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/services/rpcsrv/params/param.go (about) 1 package params 2 3 import ( 4 "bytes" 5 "encoding/base64" 6 "encoding/hex" 7 "encoding/json" 8 "errors" 9 "fmt" 10 "math/big" 11 "strconv" 12 "strings" 13 14 "github.com/google/uuid" 15 "github.com/nspcc-dev/neo-go/pkg/core/transaction" 16 "github.com/nspcc-dev/neo-go/pkg/encoding/address" 17 "github.com/nspcc-dev/neo-go/pkg/neorpc" 18 "github.com/nspcc-dev/neo-go/pkg/smartcontract" 19 "github.com/nspcc-dev/neo-go/pkg/util" 20 ) 21 22 type ( 23 // Param represents a param either passed to 24 // the server or to be sent to a server using 25 // the client. 26 Param struct { 27 json.RawMessage 28 cache any 29 } 30 31 // FuncParam represents a function argument parameter used in the 32 // invokefunction RPC method. 33 FuncParam struct { 34 Type smartcontract.ParamType `json:"type"` 35 Value Param `json:"value"` 36 } 37 38 // FuncParamKV represents a pair of function argument parameters 39 // a slice of which is stored in FuncParam of [smartcontract.MapType] type. 40 FuncParamKV struct { 41 Key FuncParam `json:"key"` 42 Value FuncParam `json:"value"` 43 } 44 ) 45 46 var ( 47 jsonNullBytes = []byte("null") 48 jsonFalseBytes = []byte("false") 49 jsonTrueBytes = []byte("true") 50 errMissingParameter = errors.New("parameter is missing") 51 errNotAString = errors.New("not a string") 52 errNotAnInt = errors.New("not an integer") 53 errNotABool = errors.New("not a boolean") 54 errNotAnArray = errors.New("not an array") 55 ) 56 57 func (p Param) String() string { 58 str, _ := p.GetString() 59 return str 60 } 61 62 // GetStringStrict returns a string value of the parameter. 63 func (p *Param) GetStringStrict() (string, error) { 64 if p == nil { 65 return "", errMissingParameter 66 } 67 if p.IsNull() { 68 return "", errNotAString 69 } 70 if p.cache == nil { 71 var s string 72 err := json.Unmarshal(p.RawMessage, &s) 73 if err != nil { 74 return "", errNotAString 75 } 76 p.cache = s 77 } 78 if s, ok := p.cache.(string); ok { 79 return s, nil 80 } 81 return "", errNotAString 82 } 83 84 // GetString returns a string value of the parameter or tries to cast the parameter to a string value. 85 func (p *Param) GetString() (string, error) { 86 if p == nil { 87 return "", errMissingParameter 88 } 89 if p.IsNull() { 90 return "", errNotAString 91 } 92 if p.cache == nil { 93 var s string 94 err := json.Unmarshal(p.RawMessage, &s) 95 if err == nil { 96 p.cache = s 97 } else { 98 var i int64 99 err = json.Unmarshal(p.RawMessage, &i) 100 if err == nil { 101 p.cache = i 102 } else { 103 var b bool 104 err = json.Unmarshal(p.RawMessage, &b) 105 if err == nil { 106 p.cache = b 107 } else { 108 return "", errNotAString 109 } 110 } 111 } 112 } 113 switch t := p.cache.(type) { 114 case string: 115 return t, nil 116 case int64: 117 return strconv.FormatInt(t, 10), nil 118 case bool: 119 if t { 120 return "true", nil 121 } 122 return "false", nil 123 default: 124 return "", errNotAString 125 } 126 } 127 128 // GetBooleanStrict returns boolean value of the parameter. 129 func (p *Param) GetBooleanStrict() (bool, error) { 130 if p == nil { 131 return false, errMissingParameter 132 } 133 if bytes.Equal(p.RawMessage, jsonTrueBytes) { 134 p.cache = true 135 return true, nil 136 } 137 if bytes.Equal(p.RawMessage, jsonFalseBytes) { 138 p.cache = false 139 return false, nil 140 } 141 return false, errNotABool 142 } 143 144 // GetBoolean returns a boolean value of the parameter or tries to cast the parameter to a bool value. 145 func (p *Param) GetBoolean() (bool, error) { 146 if p == nil { 147 return false, errMissingParameter 148 } 149 if p.IsNull() { 150 return false, errNotABool 151 } 152 var b bool 153 if p.cache == nil { 154 err := json.Unmarshal(p.RawMessage, &b) 155 if err == nil { 156 p.cache = b 157 } else { 158 var s string 159 err = json.Unmarshal(p.RawMessage, &s) 160 if err == nil { 161 p.cache = s 162 } else { 163 var i int64 164 err = json.Unmarshal(p.RawMessage, &i) 165 if err == nil { 166 p.cache = i 167 } else { 168 return false, errNotABool 169 } 170 } 171 } 172 } 173 switch t := p.cache.(type) { 174 case bool: 175 return t, nil 176 case string: 177 return t != "", nil 178 case int64: 179 return t != 0, nil 180 default: 181 return false, errNotABool 182 } 183 } 184 185 // GetIntStrict returns an int value of the parameter if the parameter is an integer. 186 func (p *Param) GetIntStrict() (int, error) { 187 if p == nil { 188 return 0, errMissingParameter 189 } 190 if p.IsNull() { 191 return 0, errNotAnInt 192 } 193 value, err := p.fillIntCache() 194 if err != nil { 195 return 0, err 196 } 197 if i, ok := value.(int64); ok && i == int64(int(i)) { 198 return int(i), nil 199 } 200 return 0, errNotAnInt 201 } 202 203 func (p *Param) fillIntCache() (any, error) { 204 if p.cache != nil { 205 return p.cache, nil 206 } 207 208 // We could also try unmarshalling to uint64, but JSON reliably supports numbers 209 // up to 53 bits in size. 210 var i int64 211 err := json.Unmarshal(p.RawMessage, &i) 212 if err == nil { 213 p.cache = i 214 return i, nil 215 } 216 217 var s string 218 err = json.Unmarshal(p.RawMessage, &s) 219 if err == nil { 220 p.cache = s 221 return s, nil 222 } 223 224 var b bool 225 err = json.Unmarshal(p.RawMessage, &b) 226 if err == nil { 227 p.cache = b 228 return b, nil 229 } 230 return nil, errNotAnInt 231 } 232 233 // GetInt returns an int value of the parameter or tries to cast the parameter to an int value. 234 func (p *Param) GetInt() (int, error) { 235 if p == nil { 236 return 0, errMissingParameter 237 } 238 if p.IsNull() { 239 return 0, errNotAnInt 240 } 241 value, err := p.fillIntCache() 242 if err != nil { 243 return 0, err 244 } 245 switch t := value.(type) { 246 case int64: 247 if t == int64(int(t)) { 248 return int(t), nil 249 } 250 return 0, errNotAnInt 251 case string: 252 return strconv.Atoi(t) 253 case bool: 254 if t { 255 return 1, nil 256 } 257 return 0, nil 258 default: 259 panic("unreachable") 260 } 261 } 262 263 // GetBigInt returns a big-integer value of the parameter. 264 func (p *Param) GetBigInt() (*big.Int, error) { 265 if p == nil { 266 return nil, errMissingParameter 267 } 268 if p.IsNull() { 269 return nil, errNotAnInt 270 } 271 value, err := p.fillIntCache() 272 if err != nil { 273 return nil, err 274 } 275 switch t := value.(type) { 276 case int64: 277 return big.NewInt(t), nil 278 case string: 279 bi, ok := new(big.Int).SetString(t, 10) 280 if !ok { 281 return nil, errNotAnInt 282 } 283 return bi, nil 284 case bool: 285 if t { 286 return big.NewInt(1), nil 287 } 288 return new(big.Int), nil 289 default: 290 panic("unreachable") 291 } 292 } 293 294 // GetArray returns a slice of Params stored in the parameter. 295 func (p *Param) GetArray() ([]Param, error) { 296 if p == nil { 297 return nil, errMissingParameter 298 } 299 if p.IsNull() { 300 return nil, errNotAnArray 301 } 302 if p.cache == nil { 303 a := []Param{} 304 err := json.Unmarshal(p.RawMessage, &a) 305 if err != nil { 306 return nil, errNotAnArray 307 } 308 p.cache = a 309 } 310 if a, ok := p.cache.([]Param); ok { 311 return a, nil 312 } 313 return nil, errNotAnArray 314 } 315 316 // GetUint256 returns a Uint256 value of the parameter. 317 func (p *Param) GetUint256() (util.Uint256, error) { 318 s, err := p.GetString() 319 if err != nil { 320 return util.Uint256{}, err 321 } 322 323 return util.Uint256DecodeStringLE(strings.TrimPrefix(s, "0x")) 324 } 325 326 // GetUint160FromHex returns a Uint160 value of the parameter encoded in hex. 327 func (p *Param) GetUint160FromHex() (util.Uint160, error) { 328 s, err := p.GetString() 329 if err != nil { 330 return util.Uint160{}, err 331 } 332 333 return util.Uint160DecodeStringLE(strings.TrimPrefix(s, "0x")) 334 } 335 336 // GetUint160FromAddress returns a Uint160 value of the parameter that was 337 // supplied as an address. 338 func (p *Param) GetUint160FromAddress() (util.Uint160, error) { 339 s, err := p.GetString() 340 if err != nil { 341 return util.Uint160{}, err 342 } 343 344 return address.StringToUint160(s) 345 } 346 347 // GetUint160FromAddressOrHex returns a Uint160 value of the parameter that was 348 // supplied either as raw hex or as an address. 349 func (p *Param) GetUint160FromAddressOrHex() (util.Uint160, error) { 350 u, err := p.GetUint160FromHex() 351 if err == nil { 352 return u, err 353 } 354 return p.GetUint160FromAddress() 355 } 356 357 // GetFuncParam returns the current parameter as a function call parameter. 358 func (p *Param) GetFuncParam() (FuncParam, error) { 359 if p == nil { 360 return FuncParam{}, errMissingParameter 361 } 362 // This one doesn't need to be cached, it's used only once. 363 fp := FuncParam{} 364 err := json.Unmarshal(p.RawMessage, &fp) 365 return fp, err 366 } 367 368 // GetFuncParamPair returns a pair of function call parameters. 369 func (p *Param) GetFuncParamPair() (FuncParamKV, error) { 370 if p == nil { 371 return FuncParamKV{}, errMissingParameter 372 } 373 // This one doesn't need to be cached, it's used only once. 374 fpp := FuncParamKV{} 375 err := json.Unmarshal(p.RawMessage, &fpp) 376 if err != nil { 377 return FuncParamKV{}, err 378 } 379 380 return fpp, nil 381 } 382 383 // GetBytesHex returns a []byte value of the parameter if 384 // it is a hex-encoded string. 385 func (p *Param) GetBytesHex() ([]byte, error) { 386 s, err := p.GetString() 387 if err != nil { 388 return nil, err 389 } 390 391 return hex.DecodeString(s) 392 } 393 394 // GetBytesBase64 returns a []byte value of the parameter if 395 // it is a base64-encoded string. 396 func (p *Param) GetBytesBase64() ([]byte, error) { 397 s, err := p.GetString() 398 if err != nil { 399 return nil, err 400 } 401 402 return base64.StdEncoding.DecodeString(s) 403 } 404 405 // GetSignerWithWitness returns a neorpc.SignerWithWitness value of the parameter. 406 func (p *Param) GetSignerWithWitness() (neorpc.SignerWithWitness, error) { 407 // This one doesn't need to be cached, it's used only once. 408 c := neorpc.SignerWithWitness{} 409 err := json.Unmarshal(p.RawMessage, &c) 410 if err != nil { 411 return neorpc.SignerWithWitness{}, fmt.Errorf("not a signer: %w", err) 412 } 413 return c, nil 414 } 415 416 // GetSignersWithWitnesses returns a slice of SignerWithWitness with CalledByEntry 417 // scope from an array of Uint160 or an array of serialized transaction.Signer stored 418 // in the parameter. 419 func (p *Param) GetSignersWithWitnesses() ([]transaction.Signer, []transaction.Witness, error) { 420 hashes, err := p.GetArray() 421 if err != nil { 422 return nil, nil, err 423 } 424 if len(hashes) > transaction.MaxAttributes { 425 return nil, nil, errors.New("too many signers") 426 } 427 signers := make([]transaction.Signer, len(hashes)) 428 witnesses := make([]transaction.Witness, len(hashes)) 429 // try to extract hashes first 430 for i, h := range hashes { 431 var u util.Uint160 432 u, err = h.GetUint160FromHex() 433 if err != nil { 434 break 435 } 436 signers[i] = transaction.Signer{ 437 Account: u, 438 Scopes: transaction.CalledByEntry, 439 } 440 } 441 if err != nil { 442 for i, h := range hashes { 443 signerWithWitness, err := h.GetSignerWithWitness() 444 if err != nil { 445 return nil, nil, err 446 } 447 signers[i] = signerWithWitness.Signer 448 witnesses[i] = signerWithWitness.Witness 449 } 450 } 451 return signers, witnesses, nil 452 } 453 454 // IsNull returns whether the parameter represents JSON nil value. 455 func (p *Param) IsNull() bool { 456 return bytes.Equal(p.RawMessage, jsonNullBytes) 457 } 458 459 // GetUUID returns UUID from parameter. 460 func (p *Param) GetUUID() (uuid.UUID, error) { 461 s, err := p.GetString() 462 if err != nil { 463 return uuid.UUID{}, err 464 } 465 id, err := uuid.Parse(s) 466 if err != nil { 467 return uuid.UUID{}, fmt.Errorf("not a valid UUID: %w", err) 468 } 469 return id, nil 470 }