github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/services/rpcsrv/params/param_test.go (about)

     1  package params
     2  
     3  import (
     4  	"encoding/base64"
     5  	"encoding/hex"
     6  	"encoding/json"
     7  	"fmt"
     8  	"math"
     9  	"math/big"
    10  	"strings"
    11  	"testing"
    12  
    13  	"github.com/nspcc-dev/neo-go/pkg/core/transaction"
    14  	"github.com/nspcc-dev/neo-go/pkg/encoding/address"
    15  	"github.com/nspcc-dev/neo-go/pkg/neorpc"
    16  	"github.com/nspcc-dev/neo-go/pkg/smartcontract"
    17  	"github.com/nspcc-dev/neo-go/pkg/util"
    18  	"github.com/stretchr/testify/assert"
    19  	"github.com/stretchr/testify/require"
    20  )
    21  
    22  func TestParam_UnmarshalJSON(t *testing.T) {
    23  	type testCase struct {
    24  		check              func(t *testing.T, p *Param)
    25  		expectedRawMessage []byte
    26  	}
    27  	msg := `["123", 123, null, ["str2", 3], [{"type": "String", "value": "jajaja"}],
    28  	  {"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569"},
    29  	  {"account": "NYxb4fSZVKAz8YsgaPK2WkT3KcAE9b3Vag", "scopes": "Global"},
    30  	  [{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": "Global"}]]`
    31  	expected := []testCase{
    32  		{
    33  			check: func(t *testing.T, p *Param) {
    34  				expectedS := "123"
    35  				actualS, err := p.GetStringStrict()
    36  				require.NoError(t, err)
    37  				require.Equal(t, expectedS, actualS)
    38  				actualS, err = p.GetString()
    39  				require.NoError(t, err)
    40  				require.Equal(t, expectedS, actualS)
    41  
    42  				expectedI := 123
    43  				_, err = p.GetIntStrict()
    44  				require.Error(t, err)
    45  				actualI, err := p.GetInt()
    46  				require.NoError(t, err)
    47  				require.Equal(t, expectedI, actualI)
    48  
    49  				expectedB := true
    50  				_, err = p.GetBooleanStrict()
    51  				require.Error(t, err)
    52  				actualB, err := p.GetBoolean()
    53  				require.NoError(t, err)
    54  				require.Equal(t, expectedB, actualB)
    55  
    56  				_, err = p.GetArray()
    57  				require.Error(t, err)
    58  			},
    59  			expectedRawMessage: []byte(`"123"`),
    60  		},
    61  		{
    62  			check: func(t *testing.T, p *Param) {
    63  				expectedS := "123"
    64  				_, err := p.GetStringStrict()
    65  				require.Error(t, err)
    66  				actualS, err := p.GetString()
    67  				require.NoError(t, err)
    68  				require.Equal(t, expectedS, actualS)
    69  
    70  				expectedI := 123
    71  				actualI, err := p.GetIntStrict()
    72  				require.NoError(t, err)
    73  				require.Equal(t, expectedI, actualI)
    74  				actualI, err = p.GetInt()
    75  				require.NoError(t, err)
    76  				require.Equal(t, expectedI, actualI)
    77  
    78  				expectedB := true
    79  				_, err = p.GetBooleanStrict()
    80  				require.Error(t, err)
    81  				actualB, err := p.GetBoolean()
    82  				require.NoError(t, err)
    83  				require.Equal(t, expectedB, actualB)
    84  
    85  				_, err = p.GetArray()
    86  				require.Error(t, err)
    87  			},
    88  			expectedRawMessage: []byte(`123`),
    89  		},
    90  		{
    91  			check: func(t *testing.T, p *Param) {
    92  				_, err := p.GetStringStrict()
    93  				require.Error(t, err)
    94  				_, err = p.GetString()
    95  				require.Error(t, err)
    96  
    97  				_, err = p.GetIntStrict()
    98  				require.Error(t, err)
    99  				_, err = p.GetInt()
   100  				require.Error(t, err)
   101  
   102  				_, err = p.GetBooleanStrict()
   103  				require.Error(t, err)
   104  				_, err = p.GetBoolean()
   105  				require.Error(t, err)
   106  
   107  				_, err = p.GetArray()
   108  				require.Error(t, err)
   109  			},
   110  			expectedRawMessage: []byte(`null`),
   111  		},
   112  		{
   113  			check: func(t *testing.T, p *Param) {
   114  				_, err := p.GetStringStrict()
   115  				require.Error(t, err)
   116  				_, err = p.GetString()
   117  				require.Error(t, err)
   118  
   119  				_, err = p.GetIntStrict()
   120  				require.Error(t, err)
   121  				_, err = p.GetInt()
   122  				require.Error(t, err)
   123  
   124  				_, err = p.GetBooleanStrict()
   125  				require.Error(t, err)
   126  				_, err = p.GetBoolean()
   127  				require.Error(t, err)
   128  
   129  				a, err := p.GetArray()
   130  				require.NoError(t, err)
   131  				require.Equal(t, []Param{
   132  					{RawMessage: json.RawMessage(`"str2"`)},
   133  					{RawMessage: json.RawMessage(`3`)},
   134  				}, a)
   135  			},
   136  			expectedRawMessage: []byte(`["str2", 3]`),
   137  		},
   138  		{
   139  			check: func(t *testing.T, p *Param) {
   140  				a, err := p.GetArray()
   141  				require.NoError(t, err)
   142  				require.Equal(t, 1, len(a))
   143  				fp, err := a[0].GetFuncParam()
   144  				require.NoError(t, err)
   145  				require.Equal(t, smartcontract.StringType, fp.Type)
   146  				strVal, err := fp.Value.GetStringStrict()
   147  				require.NoError(t, err)
   148  				require.Equal(t, "jajaja", strVal)
   149  			},
   150  			expectedRawMessage: []byte(`[{"type": "String", "value": "jajaja"}]`),
   151  		},
   152  		{
   153  			check: func(t *testing.T, p *Param) {
   154  				actual, err := p.GetSignerWithWitness()
   155  				require.NoError(t, err)
   156  				expectedAcc, err := util.Uint160DecodeStringLE("cadb3dc2faa3ef14a13b619c9a43124755aa2569")
   157  				require.NoError(t, err)
   158  				require.Equal(t, neorpc.SignerWithWitness{Signer: transaction.Signer{Account: expectedAcc}}, actual)
   159  			},
   160  			expectedRawMessage: []byte(`{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569"}`),
   161  		},
   162  		{
   163  			check: func(t *testing.T, p *Param) {
   164  				actual, err := p.GetSignerWithWitness()
   165  				require.NoError(t, err)
   166  				expectedAcc, err := address.StringToUint160("NYxb4fSZVKAz8YsgaPK2WkT3KcAE9b3Vag")
   167  				require.NoError(t, err)
   168  				require.Equal(t, neorpc.SignerWithWitness{Signer: transaction.Signer{Account: expectedAcc, Scopes: transaction.Global}}, actual)
   169  			},
   170  			expectedRawMessage: []byte(`{"account": "NYxb4fSZVKAz8YsgaPK2WkT3KcAE9b3Vag", "scopes": "Global"}`),
   171  		},
   172  		{
   173  			check: func(t *testing.T, p *Param) {
   174  				actualSigs, actualWtns, err := p.GetSignersWithWitnesses()
   175  				require.NoError(t, err)
   176  				require.Equal(t, 1, len(actualSigs))
   177  				require.Equal(t, 1, len(actualWtns))
   178  				expectedAcc, err := util.Uint160DecodeStringLE("cadb3dc2faa3ef14a13b619c9a43124755aa2569")
   179  				require.NoError(t, err)
   180  				require.Equal(t, transaction.Signer{Account: expectedAcc, Scopes: transaction.Global}, actualSigs[0])
   181  				require.Equal(t, transaction.Witness{}, actualWtns[0])
   182  			},
   183  			expectedRawMessage: []byte(`[{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": "Global"}]`),
   184  		},
   185  	}
   186  
   187  	var ps Params
   188  	require.NoError(t, json.Unmarshal([]byte(msg), &ps))
   189  	require.Equal(t, len(expected), len(ps))
   190  	for i, tc := range expected {
   191  		require.NotNil(t, ps[i])
   192  		require.Equal(t, json.RawMessage(tc.expectedRawMessage), ps[i].RawMessage, i)
   193  		tc.check(t, &ps[i])
   194  	}
   195  }
   196  
   197  func TestGetBigInt(t *testing.T) {
   198  	maxUint64 := new(big.Int).SetUint64(math.MaxUint64)
   199  	minInt64 := big.NewInt(math.MinInt64)
   200  	testCases := []struct {
   201  		raw      string
   202  		expected *big.Int
   203  	}{
   204  		{"true", big.NewInt(1)},
   205  		{"false", new(big.Int)},
   206  		{"42", big.NewInt(42)},
   207  		{`"` + minInt64.String() + `"`, minInt64},
   208  		{`"` + maxUint64.String() + `"`, maxUint64},
   209  		{`"` + minInt64.String() + `000"`, new(big.Int).Mul(minInt64, big.NewInt(1000))},
   210  		{`"` + maxUint64.String() + `000"`, new(big.Int).Mul(maxUint64, big.NewInt(1000))},
   211  		{`"abc"`, nil},
   212  		{`[]`, nil},
   213  		{`null`, nil},
   214  	}
   215  
   216  	for _, tc := range testCases {
   217  		var p Param
   218  		require.NoError(t, json.Unmarshal([]byte(tc.raw), &p))
   219  
   220  		actual, err := p.GetBigInt()
   221  		if tc.expected == nil {
   222  			require.Error(t, err)
   223  			continue
   224  		}
   225  		require.NoError(t, err)
   226  		require.Equal(t, tc.expected, actual)
   227  
   228  		expected := tc.expected.Int64()
   229  		actualInt, err := p.GetInt()
   230  		if !actual.IsInt64() || int64(int(expected)) != expected {
   231  			require.Error(t, err)
   232  		} else {
   233  			require.NoError(t, err)
   234  			require.Equal(t, int(expected), actualInt)
   235  		}
   236  	}
   237  }
   238  
   239  func TestGetWitness(t *testing.T) {
   240  	accountHash, err := util.Uint160DecodeStringLE("cadb3dc2faa3ef14a13b619c9a43124755aa2569")
   241  	require.NoError(t, err)
   242  	addrHash, err := address.StringToUint160("NYxb4fSZVKAz8YsgaPK2WkT3KcAE9b3Vag")
   243  	require.NoError(t, err)
   244  
   245  	testCases := []struct {
   246  		raw        string
   247  		expected   neorpc.SignerWithWitness
   248  		shouldFail bool
   249  	}{
   250  		{
   251  			raw: `{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569"}`,
   252  			expected: neorpc.SignerWithWitness{
   253  				Signer: transaction.Signer{
   254  					Account: accountHash,
   255  					Scopes:  transaction.None,
   256  				},
   257  			},
   258  		},
   259  		{
   260  			raw: `{"account": "NYxb4fSZVKAz8YsgaPK2WkT3KcAE9b3Vag", "scopes": "Global"}`,
   261  			expected: neorpc.SignerWithWitness{
   262  				Signer: transaction.Signer{
   263  					Account: addrHash,
   264  					Scopes:  transaction.Global,
   265  				},
   266  			},
   267  		},
   268  		{
   269  			raw: `{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": "Global"}`,
   270  			expected: neorpc.SignerWithWitness{
   271  				Signer: transaction.Signer{
   272  					Account: accountHash,
   273  					Scopes:  transaction.Global,
   274  				},
   275  			},
   276  		},
   277  		{
   278  			raw: `{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": 128}`,
   279  			expected: neorpc.SignerWithWitness{
   280  				Signer: transaction.Signer{
   281  					Account: accountHash,
   282  					Scopes:  transaction.Global,
   283  				},
   284  			},
   285  		},
   286  		{
   287  			raw: `{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": 0}`,
   288  			expected: neorpc.SignerWithWitness{
   289  				Signer: transaction.Signer{
   290  					Account: accountHash,
   291  					Scopes:  transaction.None,
   292  				},
   293  			},
   294  		},
   295  		{
   296  			raw: `{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": 1}`,
   297  			expected: neorpc.SignerWithWitness{
   298  				Signer: transaction.Signer{
   299  					Account: accountHash,
   300  					Scopes:  transaction.CalledByEntry,
   301  				},
   302  			},
   303  		},
   304  		{
   305  			raw: `{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": 17}`,
   306  			expected: neorpc.SignerWithWitness{
   307  				Signer: transaction.Signer{
   308  					Account: accountHash,
   309  					Scopes:  transaction.CalledByEntry | transaction.CustomContracts,
   310  				},
   311  			},
   312  		},
   313  		{
   314  			raw:        `{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": 178}`,
   315  			shouldFail: true,
   316  		},
   317  		{
   318  			raw:        `{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": 2}`,
   319  			shouldFail: true,
   320  		},
   321  	}
   322  
   323  	for _, tc := range testCases {
   324  		p := Param{RawMessage: json.RawMessage(tc.raw)}
   325  		actual, err := p.GetSignerWithWitness()
   326  		if tc.shouldFail {
   327  			require.Error(t, err, tc.raw)
   328  		} else {
   329  			require.NoError(t, err, tc.raw)
   330  			require.Equal(t, tc.expected, actual)
   331  
   332  			actual, err = p.GetSignerWithWitness() // valid second invocation.
   333  			require.NoError(t, err, tc.raw)
   334  			require.Equal(t, tc.expected, actual)
   335  		}
   336  	}
   337  }
   338  
   339  func TestParamGetUint256(t *testing.T) {
   340  	gas := "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"
   341  	u256, _ := util.Uint256DecodeStringLE(gas)
   342  	p := Param{RawMessage: []byte(fmt.Sprintf(`"%s"`, gas))}
   343  	u, err := p.GetUint256()
   344  	assert.Equal(t, u256, u)
   345  	require.Nil(t, err)
   346  
   347  	p = Param{RawMessage: []byte(fmt.Sprintf(`"0x%s"`, gas))}
   348  	u, err = p.GetUint256()
   349  	require.NoError(t, err)
   350  	assert.Equal(t, u256, u)
   351  
   352  	p = Param{RawMessage: []byte(`42`)}
   353  	_, err = p.GetUint256()
   354  	require.NotNil(t, err)
   355  
   356  	p = Param{RawMessage: []byte(`"qq2c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"`)}
   357  	_, err = p.GetUint256()
   358  	require.NotNil(t, err)
   359  }
   360  
   361  func TestParamGetUint160FromHex(t *testing.T) {
   362  	in := "50befd26fdf6e4d957c11e078b24ebce6291456f"
   363  	u160, _ := util.Uint160DecodeStringLE(in)
   364  	p := Param{RawMessage: []byte(fmt.Sprintf(`"%s"`, in))}
   365  	u, err := p.GetUint160FromHex()
   366  	assert.Equal(t, u160, u)
   367  	require.Nil(t, err)
   368  
   369  	p = Param{RawMessage: []byte(`42`)}
   370  	_, err = p.GetUint160FromHex()
   371  	require.NotNil(t, err)
   372  
   373  	p = Param{RawMessage: []byte(`"wwbefd26fdf6e4d957c11e078b24ebce6291456f"`)}
   374  	_, err = p.GetUint160FromHex()
   375  	require.NotNil(t, err)
   376  }
   377  
   378  func TestParamGetUint160FromAddress(t *testing.T) {
   379  	in := "NPAsqZkx9WhNd4P72uhZxBhLinSuNkxfB8"
   380  	u160, _ := address.StringToUint160(in)
   381  	p := Param{RawMessage: []byte(fmt.Sprintf(`"%s"`, in))}
   382  	u, err := p.GetUint160FromAddress()
   383  	assert.Equal(t, u160, u)
   384  	require.Nil(t, err)
   385  
   386  	p = Param{RawMessage: []byte(`42`)}
   387  	_, err = p.GetUint160FromAddress()
   388  	require.NotNil(t, err)
   389  
   390  	p = Param{RawMessage: []byte(`"QK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"`)}
   391  	_, err = p.GetUint160FromAddress()
   392  	require.NotNil(t, err)
   393  }
   394  
   395  func TestParam_GetUint160FromAddressOrHex(t *testing.T) {
   396  	in := "NPAsqZkx9WhNd4P72uhZxBhLinSuNkxfB8"
   397  	inHex, _ := address.StringToUint160(in)
   398  
   399  	t.Run("Address", func(t *testing.T) {
   400  		p := Param{RawMessage: []byte(fmt.Sprintf(`"%s"`, in))}
   401  		u, err := p.GetUint160FromAddressOrHex()
   402  		require.NoError(t, err)
   403  		require.Equal(t, inHex, u)
   404  	})
   405  
   406  	t.Run("Hex", func(t *testing.T) {
   407  		p := Param{RawMessage: []byte(fmt.Sprintf(`"%s"`, inHex.StringLE()))}
   408  		u, err := p.GetUint160FromAddressOrHex()
   409  		require.NoError(t, err)
   410  		require.Equal(t, inHex, u)
   411  	})
   412  }
   413  
   414  func TestParamGetFuncParam(t *testing.T) {
   415  	fp := FuncParam{
   416  		Type:  smartcontract.StringType,
   417  		Value: Param{RawMessage: []byte(`"jajaja"`)},
   418  	}
   419  	p := Param{RawMessage: []byte(`{"type": "String", "value": "jajaja"}`)}
   420  	newfp, err := p.GetFuncParam()
   421  	assert.Equal(t, fp, newfp)
   422  	require.Nil(t, err)
   423  
   424  	p = Param{RawMessage: []byte(`42`)}
   425  	_, err = p.GetFuncParam()
   426  	require.NotNil(t, err)
   427  }
   428  
   429  func TestParamGetFuncParamPair(t *testing.T) {
   430  	fp := FuncParam{
   431  		Type:  smartcontract.MapType,
   432  		Value: Param{RawMessage: []byte(`[{"key": {"type": "String", "value": "key1"}, "value": {"type": "Integer", "value": 123}}, {"key": {"type": "String", "value": "key2"}, "value": {"type": "Integer", "value": 456}}]`)},
   433  	}
   434  	p := Param{RawMessage: []byte(`{"type": "Map", "value": [{"key": {"type": "String", "value": "key1"}, "value": {"type": "Integer", "value": 123}}, {"key": {"type": "String", "value": "key2"}, "value": {"type": "Integer", "value": 456}}]}`)}
   435  	newfp, err := p.GetFuncParam()
   436  	assert.Equal(t, fp, newfp)
   437  	require.NoError(t, err)
   438  
   439  	kvs, err := newfp.Value.GetArray()
   440  	require.NoError(t, err)
   441  
   442  	p1, err := kvs[0].GetFuncParamPair()
   443  	require.NoError(t, err)
   444  
   445  	fp1Key := FuncParam{
   446  		Type:  smartcontract.StringType,
   447  		Value: Param{RawMessage: []byte(`"key1"`)},
   448  	}
   449  	fp1Value := FuncParam{
   450  		Type:  smartcontract.IntegerType,
   451  		Value: Param{RawMessage: []byte(`123`)},
   452  	}
   453  	assert.Equal(t, fp1Key, p1.Key)
   454  	assert.Equal(t, fp1Value, p1.Value)
   455  
   456  	p2, err := kvs[1].GetFuncParamPair()
   457  	require.NoError(t, err)
   458  
   459  	fp2Key := FuncParam{
   460  		Type:  smartcontract.StringType,
   461  		Value: Param{RawMessage: []byte(`"key2"`)},
   462  	}
   463  	fp2Value := FuncParam{
   464  		Type:  smartcontract.IntegerType,
   465  		Value: Param{RawMessage: []byte(`456`)},
   466  	}
   467  	assert.Equal(t, fp2Key, p2.Key)
   468  	assert.Equal(t, fp2Value, p2.Value)
   469  }
   470  
   471  func TestParamGetBytesHex(t *testing.T) {
   472  	in := "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"
   473  	inb, _ := hex.DecodeString(in)
   474  	p := Param{RawMessage: []byte(fmt.Sprintf(`"%s"`, in))}
   475  	bh, err := p.GetBytesHex()
   476  	assert.Equal(t, inb, bh)
   477  	require.Nil(t, err)
   478  
   479  	p = Param{RawMessage: []byte(`42`)}
   480  	h, err := p.GetBytesHex()
   481  	assert.Equal(t, []byte{0x42}, h) // that's the way how C# works: 42 -> "42" -> []byte{0x42}
   482  	require.Nil(t, err)
   483  
   484  	p = Param{RawMessage: []byte(`"qq2c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"`)}
   485  	_, err = p.GetBytesHex()
   486  	require.NotNil(t, err)
   487  }
   488  
   489  func TestParamGetBytesBase64(t *testing.T) {
   490  	in := "Aj4A8DoW6HB84EXrQu6A05JFFUHuUQ3BjhyL77rFTXQm"
   491  	inb, err := base64.StdEncoding.DecodeString(in)
   492  	require.NoError(t, err)
   493  	p := Param{RawMessage: []byte(fmt.Sprintf(`"%s"`, in))}
   494  	bh, err := p.GetBytesBase64()
   495  	assert.Equal(t, inb, bh)
   496  	require.Nil(t, err)
   497  
   498  	p = Param{RawMessage: []byte(`42`)}
   499  	_, err = p.GetBytesBase64()
   500  	require.NotNil(t, err)
   501  
   502  	p = Param{RawMessage: []byte("@j4A8DoW6HB84EXrQu6A05JFFUHuUQ3BjhyL77rFTXQm")}
   503  	_, err = p.GetBytesBase64()
   504  	require.NotNil(t, err)
   505  }
   506  
   507  func TestParamGetSigner(t *testing.T) {
   508  	c := neorpc.SignerWithWitness{
   509  		Signer: transaction.Signer{
   510  			Account: util.Uint160{1, 2, 3, 4},
   511  			Scopes:  transaction.Global,
   512  		},
   513  		Witness: transaction.Witness{
   514  
   515  			InvocationScript:   []byte{1, 2, 3},
   516  			VerificationScript: []byte{1, 2, 3},
   517  		},
   518  	}
   519  	p := Param{RawMessage: []byte(`{"account": "0x0000000000000000000000000000000004030201", "scopes": "Global", "invocation": "AQID", "verification": "AQID"}`)}
   520  	actual, err := p.GetSignerWithWitness()
   521  	require.NoError(t, err)
   522  	require.Equal(t, c, actual)
   523  
   524  	p = Param{RawMessage: []byte(`"not a signer"`)}
   525  	_, err = p.GetSignerWithWitness()
   526  	require.Error(t, err)
   527  }
   528  
   529  func TestParamGetSigners(t *testing.T) {
   530  	u1 := util.Uint160{1, 2, 3, 4}
   531  	u2 := util.Uint160{5, 6, 7, 8}
   532  	t.Run("from hashes", func(t *testing.T) {
   533  		p := Param{RawMessage: []byte(fmt.Sprintf(`["%s", "%s"]`, u1.StringLE(), u2.StringLE()))}
   534  		actual, _, err := p.GetSignersWithWitnesses()
   535  		require.NoError(t, err)
   536  		require.Equal(t, 2, len(actual))
   537  		require.True(t, u1.Equals(actual[0].Account))
   538  		require.True(t, u2.Equals(actual[1].Account))
   539  	})
   540  
   541  	t.Run("overflow", func(t *testing.T) {
   542  		var hashes = make([]util.Uint256, transaction.MaxAttributes+1)
   543  		msg, err := json.Marshal(hashes)
   544  		require.NoError(t, err)
   545  		p := Param{RawMessage: msg}
   546  		_, _, err = p.GetSignersWithWitnesses()
   547  		require.Error(t, err)
   548  	})
   549  
   550  	t.Run("bad format", func(t *testing.T) {
   551  		p := Param{RawMessage: []byte(`"not a signer"`)}
   552  		_, _, err := p.GetSignersWithWitnesses()
   553  		require.Error(t, err)
   554  	})
   555  }
   556  
   557  func TestParamGetUUID(t *testing.T) {
   558  	t.Run("from null", func(t *testing.T) {
   559  		p := Param{RawMessage: []byte("null")}
   560  		_, err := p.GetUUID()
   561  		require.ErrorIs(t, err, errNotAString)
   562  	})
   563  	t.Run("invalid uuid", func(t *testing.T) {
   564  		p := Param{RawMessage: []byte(`"not-a-uuid"`)}
   565  		_, err := p.GetUUID()
   566  		require.Error(t, err)
   567  		require.True(t, strings.Contains(err.Error(), "not a valid UUID"), err.Error())
   568  	})
   569  	t.Run("compat", func(t *testing.T) {
   570  		expected := "2107da59-4f9c-462c-9c51-7666842519a9"
   571  		p := Param{RawMessage: []byte(fmt.Sprintf(`"%s"`, expected))}
   572  		id, err := p.GetUUID()
   573  		require.NoError(t, err)
   574  		require.Equal(t, id.String(), expected)
   575  	})
   576  }