package server import ( "context" "encoding/hex" "testing" "github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/core/fee" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/internal/testchain" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/rpc/client" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/stretchr/testify/require" ) func TestClient_NEP5(t *testing.T) { chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t) defer chain.Close() defer rpcSrv.Shutdown() c, err := client.New(context.Background(), httpSrv.URL, client.Options{Network: netmode.UnitTestNet}) require.NoError(t, err) h, err := util.Uint160DecodeStringLE(testContractHash) require.NoError(t, err) t.Run("Decimals", func(t *testing.T) { d, err := c.NEP5Decimals(h) require.NoError(t, err) require.EqualValues(t, 2, d) }) t.Run("TotalSupply", func(t *testing.T) { s, err := c.NEP5TotalSupply(h) require.NoError(t, err) require.EqualValues(t, 1_000_000, s) }) t.Run("Name", func(t *testing.T) { name, err := c.NEP5Name(h) require.NoError(t, err) require.Equal(t, "Rubl", name) }) t.Run("Symbol", func(t *testing.T) { sym, err := c.NEP5Symbol(h) require.NoError(t, err) require.Equal(t, "RUB", sym) }) t.Run("TokenInfo", func(t *testing.T) { tok, err := c.NEP5TokenInfo(h) require.NoError(t, err) require.Equal(t, h, tok.Hash) require.Equal(t, "Rubl", tok.Name) require.Equal(t, "RUB", tok.Symbol) require.EqualValues(t, 2, tok.Decimals) }) t.Run("BalanceOf", func(t *testing.T) { acc := testchain.PrivateKeyByID(0).GetScriptHash() b, err := c.NEP5BalanceOf(h, acc) require.NoError(t, err) require.EqualValues(t, 877, b) }) } func TestAddNetworkFee(t *testing.T) { chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t) defer chain.Close() defer rpcSrv.Shutdown() c, err := client.New(context.Background(), httpSrv.URL, client.Options{Network: testchain.Network()}) require.NoError(t, err) getAccounts := func(t *testing.T, n int) []*wallet.Account { accs := make([]*wallet.Account, n) var err error for i := range accs { accs[i], err = wallet.NewAccount() require.NoError(t, err) } return accs } feePerByte := chain.FeePerByte() t.Run("Invalid", func(t *testing.T) { tx := transaction.New(testchain.Network(), []byte{byte(opcode.PUSH1)}, 0) accs := getAccounts(t, 2) tx.Signers = []transaction.Signer{{ Account: accs[0].PrivateKey().GetScriptHash(), Scopes: transaction.CalledByEntry, }} require.Error(t, c.AddNetworkFee(tx, 10, accs[0], accs[1])) }) t.Run("Simple", func(t *testing.T) { tx := transaction.New(testchain.Network(), []byte{byte(opcode.PUSH1)}, 0) accs := getAccounts(t, 1) tx.Signers = []transaction.Signer{{ Account: accs[0].PrivateKey().GetScriptHash(), Scopes: transaction.CalledByEntry, }} require.NoError(t, c.AddNetworkFee(tx, 10, accs[0])) require.NoError(t, accs[0].SignTx(tx)) cFee, _ := fee.Calculate(accs[0].Contract.Script) require.Equal(t, int64(io.GetVarSize(tx))*feePerByte+cFee+10, tx.NetworkFee) }) t.Run("Multi", func(t *testing.T) { tx := transaction.New(testchain.Network(), []byte{byte(opcode.PUSH1)}, 0) accs := getAccounts(t, 4) pubs := keys.PublicKeys{accs[1].PrivateKey().PublicKey(), accs[2].PrivateKey().PublicKey(), accs[3].PrivateKey().PublicKey()} require.NoError(t, accs[1].ConvertMultisig(2, pubs)) require.NoError(t, accs[2].ConvertMultisig(2, pubs)) tx.Signers = []transaction.Signer{ { Account: accs[0].PrivateKey().GetScriptHash(), Scopes: transaction.CalledByEntry, }, { Account: hash.Hash160(accs[1].Contract.Script), Scopes: transaction.Global, }, } require.NoError(t, c.AddNetworkFee(tx, 10, accs[0], accs[1])) require.NoError(t, accs[0].SignTx(tx)) require.NoError(t, accs[1].SignTx(tx)) require.NoError(t, accs[2].SignTx(tx)) cFee, _ := fee.Calculate(accs[0].Contract.Script) cFeeM, _ := fee.Calculate(accs[1].Contract.Script) require.Equal(t, int64(io.GetVarSize(tx))*feePerByte+cFee+cFeeM+10, tx.NetworkFee) }) t.Run("Contract", func(t *testing.T) { tx := transaction.New(testchain.Network(), []byte{byte(opcode.PUSH1)}, 0) priv := testchain.PrivateKeyByID(0) acc1, err := wallet.NewAccountFromWIF(priv.WIF()) require.NoError(t, err) acc1.Contract.Deployed = true acc1.Contract.Script, _ = hex.DecodeString(verifyContractAVM) h, _ := util.Uint160DecodeStringLE(verifyContractHash) tx.ValidUntilBlock = chain.BlockHeight() + 10 t.Run("Valid", func(t *testing.T) { acc0, err := wallet.NewAccountFromWIF(priv.WIF()) require.NoError(t, err) tx.Signers = []transaction.Signer{ { Account: acc0.PrivateKey().GetScriptHash(), Scopes: transaction.CalledByEntry, }, { Account: h, Scopes: transaction.Global, }, } require.NoError(t, c.AddNetworkFee(tx, 10, acc0, acc1)) require.NoError(t, acc0.SignTx(tx)) tx.Scripts = append(tx.Scripts, transaction.Witness{}) require.NoError(t, chain.VerifyTx(tx)) }) t.Run("Invalid", func(t *testing.T) { acc0, err := wallet.NewAccount() require.NoError(t, err) tx.Signers = []transaction.Signer{ { Account: acc0.PrivateKey().GetScriptHash(), Scopes: transaction.CalledByEntry, }, { Account: h, Scopes: transaction.Global, }, } require.Error(t, c.AddNetworkFee(tx, 10, acc0, acc1)) }) t.Run("InvalidContract", func(t *testing.T) { acc0, err := wallet.NewAccountFromWIF(priv.WIF()) require.NoError(t, err) tx.Signers = []transaction.Signer{ { Account: acc0.PrivateKey().GetScriptHash(), Scopes: transaction.CalledByEntry, }, { Account: util.Uint160{}, Scopes: transaction.Global, }, } require.Error(t, c.AddNetworkFee(tx, 10, acc0, acc1)) }) }) } func TestSignAndPushInvocationTx(t *testing.T) { chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t) defer chain.Close() defer rpcSrv.Shutdown() c, err := client.New(context.Background(), httpSrv.URL, client.Options{Network: testchain.Network()}) require.NoError(t, err) priv := testchain.PrivateKey(0) acc, err := wallet.NewAccountFromWIF(priv.WIF()) require.NoError(t, err) h, err := c.SignAndPushInvocationTx([]byte{byte(opcode.PUSH1)}, acc, 30, 0, []transaction.Signer{{ Account: priv.GetScriptHash(), Scopes: transaction.CalledByEntry, }}) require.NoError(t, err) mp := chain.GetMemPool() tx, ok := mp.TryGetValue(h) require.True(t, ok) require.Equal(t, h, tx.Hash()) require.EqualValues(t, 30, tx.SystemFee) } func TestPing(t *testing.T) { chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t) defer chain.Close() c, err := client.New(context.Background(), httpSrv.URL, client.Options{Network: testchain.Network()}) require.NoError(t, err) require.NoError(t, c.Ping()) require.NoError(t, rpcSrv.Shutdown()) httpSrv.Close() require.Error(t, c.Ping()) } func TestCreateTxFromScript(t *testing.T) { chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t) defer chain.Close() defer rpcSrv.Shutdown() c, err := client.New(context.Background(), httpSrv.URL, client.Options{Network: testchain.Network()}) require.NoError(t, err) priv := testchain.PrivateKey(0) acc, err := wallet.NewAccountFromWIF(priv.WIF()) require.NoError(t, err) t.Run("NoSystemFee", func(t *testing.T) { tx, err := c.CreateTxFromScript([]byte{byte(opcode.PUSH1)}, acc, -1, 10) require.NoError(t, err) require.True(t, tx.ValidUntilBlock > chain.BlockHeight()) require.EqualValues(t, 30, tx.SystemFee) // PUSH1 require.True(t, len(tx.Signers) == 1) require.Equal(t, acc.PrivateKey().GetScriptHash(), tx.Signers[0].Account) }) t.Run("ProvideSystemFee", func(t *testing.T) { tx, err := c.CreateTxFromScript([]byte{byte(opcode.PUSH1)}, acc, 123, 10) require.NoError(t, err) require.True(t, tx.ValidUntilBlock > chain.BlockHeight()) require.EqualValues(t, 123, tx.SystemFee) require.True(t, len(tx.Signers) == 1) require.Equal(t, acc.PrivateKey().GetScriptHash(), tx.Signers[0].Account) }) } func TestCreateNEP5TransferTx(t *testing.T) { chain, rpcSrv, httpSrv := initServerWithInMemoryChain(t) defer chain.Close() defer rpcSrv.Shutdown() c, err := client.New(context.Background(), httpSrv.URL, client.Options{Network: testchain.Network()}) require.NoError(t, err) priv := testchain.PrivateKeyByID(0) acc, err := wallet.NewAccountFromWIF(priv.WIF()) require.NoError(t, err) tx, err := c.CreateNEP5TransferTx(acc, util.Uint160{}, client.GasContractHash, 1000, 0) require.NoError(t, err) require.NoError(t, acc.SignTx(tx)) require.NoError(t, chain.VerifyTx(tx)) v := chain.GetTestVM(tx) v.LoadScriptWithFlags(tx.Script, smartcontract.All) require.NoError(t, v.Run()) }