package nep11 import ( "errors" "math/big" "testing" "github.com/google/uuid" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/neorpc/result" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) type testAct struct { err error res *result.Invoke tx *transaction.Transaction txh util.Uint256 vub uint32 } func (t *testAct) Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) { return t.res, t.err } func (t *testAct) MakeRun(script []byte) (*transaction.Transaction, error) { return t.tx, t.err } func (t *testAct) MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error) { return t.tx, t.err } func (t *testAct) SendRun(script []byte) (util.Uint256, uint32, error) { return t.txh, t.vub, t.err } func (t *testAct) CallAndExpandIterator(contract util.Uint160, method string, maxItems int, params ...any) (*result.Invoke, error) { return t.res, t.err } func (t *testAct) TerminateSession(sessionID uuid.UUID) error { return t.err } func (t *testAct) TraverseIterator(sessionID uuid.UUID, iterator *result.Iterator, num int) ([]stackitem.Item, error) { return t.res.Stack, t.err } func TestReaderBalanceOf(t *testing.T) { ta := new(testAct) tr := NewBaseReader(ta, util.Uint160{1, 2, 3}) ta.err = errors.New("") _, err := tr.BalanceOf(util.Uint160{3, 2, 1}) require.Error(t, err) ta.err = nil ta.res = &result.Invoke{ State: "HALT", Stack: []stackitem.Item{ stackitem.Make(100500), }, } bal, err := tr.BalanceOf(util.Uint160{3, 2, 1}) require.NoError(t, err) require.Equal(t, big.NewInt(100500), bal) ta.res = &result.Invoke{ State: "HALT", Stack: []stackitem.Item{ stackitem.Make([]stackitem.Item{}), }, } _, err = tr.BalanceOf(util.Uint160{3, 2, 1}) require.Error(t, err) } func TestReaderProperties(t *testing.T) { ta := new(testAct) tr := NewBaseReader(ta, util.Uint160{1, 2, 3}) ta.err = errors.New("") _, err := tr.Properties([]byte{3, 2, 1}) require.Error(t, err) ta.res = &result.Invoke{ State: "HALT", Stack: []stackitem.Item{ stackitem.Make([]stackitem.Item{}), }, } _, err = tr.Properties([]byte{3, 2, 1}) require.Error(t, err) ta.err = nil ta.res = &result.Invoke{ State: "HALT", Stack: []stackitem.Item{ stackitem.NewMap(), }, } m, err := tr.Properties([]byte{3, 2, 1}) require.NoError(t, err) require.Equal(t, 0, m.Len()) } func TestReaderTokensOfExpanded(t *testing.T) { ta := new(testAct) tr := NewBaseReader(ta, util.Uint160{1, 2, 3}) for name, fun := range map[string]func(int) ([][]byte, error){ "Tokens": tr.TokensExpanded, "TokensOf": func(n int) ([][]byte, error) { return tr.TokensOfExpanded(util.Uint160{1, 2, 3}, n) }, } { t.Run(name, func(t *testing.T) { ta.err = errors.New("") _, err := fun(1) require.Error(t, err) ta.err = nil ta.res = &result.Invoke{ State: "HALT", Stack: []stackitem.Item{ stackitem.Make(100500), }, } _, err = fun(1) require.Error(t, err) ta.res = &result.Invoke{ State: "HALT", Stack: []stackitem.Item{ stackitem.Make([]stackitem.Item{stackitem.Make("one")}), }, } toks, err := fun(1) require.NoError(t, err) require.Equal(t, [][]byte{[]byte("one")}, toks) }) } } func TestReaderTokensOf(t *testing.T) { ta := new(testAct) tr := NewBaseReader(ta, util.Uint160{1, 2, 3}) for name, fun := range map[string]func() (*TokenIterator, error){ "Tokens": tr.Tokens, "TokensOf": func() (*TokenIterator, error) { return tr.TokensOf(util.Uint160{1, 2, 3}) }, } { t.Run(name, func(t *testing.T) { ta.err = errors.New("") _, err := fun() require.Error(t, err) iid := uuid.New() ta.err = nil ta.res = &result.Invoke{ Session: uuid.New(), State: "HALT", Stack: []stackitem.Item{ stackitem.NewInterop(result.Iterator{ ID: &iid, }), }, } iter, err := fun() require.NoError(t, err) ta.res = &result.Invoke{ Stack: []stackitem.Item{ stackitem.Make("one"), stackitem.Make([]stackitem.Item{}), }, } _, err = iter.Next(10) require.Error(t, err) ta.res = &result.Invoke{ Stack: []stackitem.Item{ stackitem.Make("one"), stackitem.Make("two"), }, } vals, err := iter.Next(10) require.NoError(t, err) require.Equal(t, [][]byte{[]byte("one"), []byte("two")}, vals) ta.err = errors.New("") _, err = iter.Next(1) require.Error(t, err) err = iter.Terminate() require.Error(t, err) // Value-based iterator. ta.err = nil ta.res = &result.Invoke{ State: "HALT", Stack: []stackitem.Item{ stackitem.NewInterop(result.Iterator{ Values: []stackitem.Item{ stackitem.Make("one"), stackitem.Make("two"), }, }), }, } iter, err = fun() require.NoError(t, err) ta.err = errors.New("") err = iter.Terminate() require.NoError(t, err) }) } } type tData struct { someInt int someString string } func (d *tData) ToStackItem() (stackitem.Item, error) { return stackitem.NewStruct([]stackitem.Item{ stackitem.Make(d.someInt), stackitem.Make(d.someString), }), nil } func (d *tData) FromStackItem(si stackitem.Item) error { panic("TODO") } func TestTokenTransfer(t *testing.T) { ta := new(testAct) tok := NewBase(ta, util.Uint160{1, 2, 3}) ta.err = errors.New("") _, _, err := tok.Transfer(util.Uint160{3, 2, 1}, []byte{3, 2, 1}, nil) require.Error(t, err) ta.err = nil ta.txh = util.Uint256{1, 2, 3} ta.vub = 42 h, vub, err := tok.Transfer(util.Uint160{3, 2, 1}, []byte{3, 2, 1}, nil) require.NoError(t, err) require.Equal(t, ta.txh, h) require.Equal(t, ta.vub, vub) ta.err = nil ta.txh = util.Uint256{1, 2, 3} ta.vub = 42 h, vub, err = tok.Transfer(util.Uint160{3, 2, 1}, []byte{3, 2, 1}, &tData{ someInt: 5, someString: "ur", }) require.NoError(t, err) require.Equal(t, ta.txh, h) require.Equal(t, ta.vub, vub) _, _, err = tok.Transfer(util.Uint160{3, 2, 1}, []byte{3, 2, 1}, stackitem.NewPointer(123, []byte{123})) require.Error(t, err) } func TestTokenTransferTransaction(t *testing.T) { ta := new(testAct) tok := NewBase(ta, util.Uint160{1, 2, 3}) for _, fun := range []func(to util.Uint160, token []byte, data any) (*transaction.Transaction, error){ tok.TransferTransaction, tok.TransferUnsigned, } { ta.err = errors.New("") _, err := fun(util.Uint160{3, 2, 1}, []byte{3, 2, 1}, nil) require.Error(t, err) ta.err = nil ta.tx = &transaction.Transaction{Nonce: 100500, ValidUntilBlock: 42} tx, err := fun(util.Uint160{3, 2, 1}, []byte{3, 2, 1}, nil) require.NoError(t, err) require.Equal(t, ta.tx, tx) ta.err = nil ta.tx = &transaction.Transaction{Nonce: 100500, ValidUntilBlock: 42} tx, err = fun(util.Uint160{3, 2, 1}, []byte{3, 2, 1}, &tData{ someInt: 5, someString: "ur", }) require.NoError(t, err) require.Equal(t, ta.tx, tx) _, err = fun(util.Uint160{3, 2, 1}, []byte{3, 2, 1}, stackitem.NewInterop(nil)) require.Error(t, err) } } func TestUnwrapKnownProperties(t *testing.T) { _, err := UnwrapKnownProperties(stackitem.NewMap(), errors.New("")) require.Error(t, err) m, err := UnwrapKnownProperties(stackitem.NewMap(), nil) require.NoError(t, err) require.NotNil(t, m) require.Equal(t, 0, len(m)) m, err = UnwrapKnownProperties(stackitem.NewMapWithValue([]stackitem.MapElement{ {Key: stackitem.Make("some"), Value: stackitem.Make("thing")}, }), nil) require.NoError(t, err) require.NotNil(t, m) require.Equal(t, 0, len(m)) m, err = UnwrapKnownProperties(stackitem.NewMapWithValue([]stackitem.MapElement{ {Key: stackitem.Make([]stackitem.Item{}), Value: stackitem.Make("thing")}, }), nil) require.NoError(t, err) require.NotNil(t, m) require.Equal(t, 0, len(m)) _, err = UnwrapKnownProperties(stackitem.NewMapWithValue([]stackitem.MapElement{ {Key: stackitem.Make("name"), Value: stackitem.Make([]stackitem.Item{})}, }), nil) require.Error(t, err) _, err = UnwrapKnownProperties(stackitem.NewMapWithValue([]stackitem.MapElement{ {Key: stackitem.Make("name"), Value: stackitem.Make([]byte{0xff})}, }), nil) require.Error(t, err) m, err = UnwrapKnownProperties(stackitem.NewMapWithValue([]stackitem.MapElement{ {Key: stackitem.Make("name"), Value: stackitem.Make("thing")}, {Key: stackitem.Make("description"), Value: stackitem.Make("good NFT")}, }), nil) require.NoError(t, err) require.NotNil(t, m) require.Equal(t, 2, len(m)) require.Equal(t, "thing", m["name"]) require.Equal(t, "good NFT", m["description"]) }