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  }