neo-go/pkg/rpc/client/rpc_test.go
2020-07-27 11:08:01 +03:00

1342 lines
52 KiB
Go

package client
import (
"context"
"encoding/base64"
"encoding/hex"
"fmt"
"math/big"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/gorilla/websocket"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/native"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"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/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/internal/testserdes"
"github.com/nspcc-dev/neo-go/pkg/rpc/request"
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
type rpcClientTestCase struct {
name string
invoke func(c *Client) (interface{}, error)
serverResponse string
result func(c *Client) interface{}
check func(t *testing.T, c *Client, result interface{})
}
const hexB1 = "000000008aaab19c43c4ca2870c3e616b123f1b689866f44b138ae4d6a5352e54442fd56401107247de4e35599d1feffaf6e763972f738d2858b0a22ad06523867e4dcf921f7c1c67201000001000000abec5362f11e75b6e02e407bb98d63675d14384101fd08010c400f01eb6371a135527ddb205a0dae1f69d2d324837cce128bead9033091883f9ce21e6d759a40f690746592b021d397f088e3b7b417fcef73cd1bfdc2800769520c4084f7ae5d9f58a09aa56eeb2f282744a59a82d17e3be0eb1c7f54e6e8df79620e2608191bac57280a836db2ec2a776d07e43f16bc76c47d348b0fcd09d56c7c320c402b95b4e39ec35162a640795ff223e92dec95390a072eb457f6a4323052f80731517e0df029e4a457204f777f5261c6a4a88d46d4c3abdec635a6eed580d6c77f0c40b9735745b1dd795a258c31d8e6fa87d5cfc2e9b3a4890d610d33bcf833b64c58b0c5cea17f3a7128f1065ed193e636971590f193f28bdd1eeebbbc1fe6e7cee594130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb030057040000000000000002000000abec5362f11e75b6e02e407bb98d63675d14384100000000000000003e5f0d0000000000b00400000001abec5362f11e75b6e02e407bb98d63675d14384101590218ddf5050c14316e851039019d39dfc2c37d6c3fee19fd5809870c14abec5362f11e75b6e02e407bb98d63675d14384113c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b523801fd08010c40ae6fc04fe4b6c22218ca9617c98d607d9ec9b1faf8cfdc3391bec485ae76b11adc6cc6abeb31a50b536ea8073e674d62a5566fce5e0a0ceb0718cb971c1ae3d00c40603071b725a58d052cad7afd88e99b27baab931afd5bb50d16e224335aab450170aabe251d3c0c6ad3f31dd7e9b89b209baabe5a1e2fa588bd8118f9e2a6960f0c40d72afcf39e663dba2d70fb8c36a09d1a6a6ad0d2fd38c857a8e7dc71e2b98711324e0d2ec641fe6896ba63ba80d3ea341c1aad11e082fb188ee07e215b4031b10c409afb2808b60286a56343b7ffcef28bb2ab0c595603e7323b5e5b0b9c1c10edfa5c40754d921865cb6fd71668a206b37a1eb10c0029a9fcd3a856aed07742cd3f94130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb0003000000abec5362f11e75b6e02e407bb98d63675d1438410000000000000000de6e0d0000000000b00400000001abec5362f11e75b6e02e407bb98d63675d143841015d0300e87648170000000c14316e851039019d39dfc2c37d6c3fee19fd5809870c14abec5362f11e75b6e02e407bb98d63675d14384113c00c087472616e736665720c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b523801fd08010c408710e7b5d8ac6cd8d09d6fb35bf2dcce5f5fb38595ddb6d04a570bc925bc2c55a73cdf5f3cb5a1feb0acc4f26c8bbca6c43df3b4a98b8c3c2c809c2f096eb25a0c40c186c102cbf72313fd94df5077fc5bbefcd32227ed2159a47a46594877fa39f330d8223b45aa24aff005bebb5b2427a50c8c4de8618e7d7b00d73d836c44942e0c40a243b5b565bd4bc2f0bb112f7624f6b3514c12413c5a95230819face3f760f03fcb96b188f98038a3f251686b53a88d69744f4e4a985e6297003a80cdb169e800c404a951e61ac99d5ee31831d111754adb711b4a9060a9524fe383a90771843cdb096382674027a87f2a15a83bd77deb2b2ab1fe8b3de6e546293ef3df9b1129e2894130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"
const hexTxMoveNeo = "0002000000abec5362f11e75b6e02e407bb98d63675d14384100000000000000003e5f0d0000000000b00400000001abec5362f11e75b6e02e407bb98d63675d14384101590218ddf5050c14316e851039019d39dfc2c37d6c3fee19fd5809870c14abec5362f11e75b6e02e407bb98d63675d14384113c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b523801fd08010c40ae6fc04fe4b6c22218ca9617c98d607d9ec9b1faf8cfdc3391bec485ae76b11adc6cc6abeb31a50b536ea8073e674d62a5566fce5e0a0ceb0718cb971c1ae3d00c40603071b725a58d052cad7afd88e99b27baab931afd5bb50d16e224335aab450170aabe251d3c0c6ad3f31dd7e9b89b209baabe5a1e2fa588bd8118f9e2a6960f0c40d72afcf39e663dba2d70fb8c36a09d1a6a6ad0d2fd38c857a8e7dc71e2b98711324e0d2ec641fe6896ba63ba80d3ea341c1aad11e082fb188ee07e215b4031b10c409afb2808b60286a56343b7ffcef28bb2ab0c595603e7323b5e5b0b9c1c10edfa5c40754d921865cb6fd71668a206b37a1eb10c0029a9fcd3a856aed07742cd3f94130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb"
const b1Verbose = `{"id":5,"jsonrpc":"2.0","result":{"size":1681,"nextblockhash":"0x45f62d72e37b074ecdc9f687222b0f91ec98d6d9af4429a2caa2e076a9196b0d","confirmations":6,"hash":"0x4f2c5539b0213ea444608cc217c5cb191255c1858ccd051ad9a36f08df26a288","version":0,"previousblockhash":"0x56fd4244e552536a4dae38b1446f8689b6f123b116e6c37028cac4439cb1aa8a","merkleroot":"0xf9dce467385206ad220a8b85d238f77239766eaffffed19955e3e47d24071140","time":1592472500001,"index":1,"nextconsensus":"Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2","witnesses":[{"invocation":"DEAPAetjcaE1Un3bIFoNrh9p0tMkg3zOEovq2QMwkYg/nOIebXWaQPaQdGWSsCHTl/CI47e0F/zvc80b/cKAB2lSDECE965dn1igmqVu6y8oJ0SlmoLRfjvg6xx/VObo33liDiYIGRusVygKg22y7Cp3bQfkPxa8dsR9NIsPzQnVbHwyDEArlbTjnsNRYqZAeV/yI+kt7JU5CgcutFf2pDIwUvgHMVF+DfAp5KRXIE93f1JhxqSojUbUw6vexjWm7tWA1sd/DEC5c1dFsd15WiWMMdjm+ofVz8Lps6SJDWENM7z4M7ZMWLDFzqF/OnEo8QZe0ZPmNpcVkPGT8ovdHu67vB/m587l","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBMHOzuw=="}],"consensusdata":{"primary":0,"nonce":"0000000000000457"},"tx":[{"hash":"0x7fb05b593cf4b1eb2d9a283c5488ca1bfe61191b5775bafa43b8647e7b20f22c","size":575,"version":0,"nonce":2,"sender":"Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2","sysfee":"0","netfee":"876350","validuntilblock":1200,"attributes":[],"cosigners":[{"account":"0x4138145d67638db97b402ee0b6751ef16253ecab","scopes":"CalledByEntry"}],"script":"Ahjd9QUMFDFuhRA5AZ0538LDfWw/7hn9WAmHDBSr7FNi8R51tuAuQHu5jWNnXRQ4QRPADAh0cmFuc2ZlcgwUiXcg2M129PAKv6N8Dt2InCCP3ptBYn1bUjg=","witnesses":[{"invocation":"DECub8BP5LbCIhjKlhfJjWB9nsmx+vjP3DORvsSFrnaxGtxsxqvrMaULU26oBz5nTWKlVm/OXgoM6wcYy5ccGuPQDEBgMHG3JaWNBSytev2I6ZsnuquTGv1btQ0W4iQzWqtFAXCqviUdPAxq0/Md1+m4myCbqr5aHi+liL2BGPnippYPDEDXKvzznmY9ui1w+4w2oJ0aamrQ0v04yFeo59xx4rmHETJODS7GQf5olrpjuoDT6jQcGq0R4IL7GI7gfiFbQDGxDECa+ygItgKGpWNDt//O8ouyqwxZVgPnMjteWwucHBDt+lxAdU2SGGXLb9cWaKIGs3oesQwAKan806hWrtB3Qs0/","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBMHOzuw=="}]},{"hash":"0xb661d5e4d9e41c3059b068f8abb6f1566a47ec800879e34c0ebd2799781a2760","size":579,"version":0,"nonce":3,"sender":"Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2","sysfee":"0","netfee":"880350","validuntilblock":1200,"attributes":[],"cosigners":[{"account":"0x4138145d67638db97b402ee0b6751ef16253ecab","scopes":"CalledByEntry"}],"script":"AwDodkgXAAAADBQxboUQOQGdOd/Cw31sP+4Z/VgJhwwUq+xTYvEedbbgLkB7uY1jZ10UOEETwAwIdHJhbnNmZXIMFDt9NxHG8Mz5sdypA9G/odiW8SOMQWJ9W1I4","witnesses":[{"invocation":"DECHEOe12Kxs2NCdb7Nb8tzOX1+zhZXdttBKVwvJJbwsVac83188taH+sKzE8myLvKbEPfO0qYuMPCyAnC8JbrJaDEDBhsECy/cjE/2U31B3/Fu+/NMiJ+0hWaR6RllId/o58zDYIjtFqiSv8AW+u1skJ6UMjE3oYY59ewDXPYNsRJQuDECiQ7W1Zb1LwvC7ES92JPazUUwSQTxalSMIGfrOP3YPA/y5axiPmAOKPyUWhrU6iNaXRPTkqYXmKXADqAzbFp6ADEBKlR5hrJnV7jGDHREXVK23EbSpBgqVJP44OpB3GEPNsJY4JnQCeofyoVqDvXfesrKrH+iz3m5UYpPvPfmxEp4o","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBMHOzuw=="}]}]}}`
const hexHeader1 = "000000008aaab19c43c4ca2870c3e616b123f1b689866f44b138ae4d6a5352e54442fd56401107247de4e35599d1feffaf6e763972f738d2858b0a22ad06523867e4dcf921f7c1c67201000001000000abec5362f11e75b6e02e407bb98d63675d14384101fd08010c400f01eb6371a135527ddb205a0dae1f69d2d324837cce128bead9033091883f9ce21e6d759a40f690746592b021d397f088e3b7b417fcef73cd1bfdc2800769520c4084f7ae5d9f58a09aa56eeb2f282744a59a82d17e3be0eb1c7f54e6e8df79620e2608191bac57280a836db2ec2a776d07e43f16bc76c47d348b0fcd09d56c7c320c402b95b4e39ec35162a640795ff223e92dec95390a072eb457f6a4323052f80731517e0df029e4a457204f777f5261c6a4a88d46d4c3abdec635a6eed580d6c77f0c40b9735745b1dd795a258c31d8e6fa87d5cfc2e9b3a4890d610d33bcf833b64c58b0c5cea17f3a7128f1065ed193e636971590f193f28bdd1eeebbbc1fe6e7cee594130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb00"
const header1Verbose = `{"id":5,"jsonrpc":"2.0","result":{"hash":"0x4f2c5539b0213ea444608cc217c5cb191255c1858ccd051ad9a36f08df26a288","size":518,"version":0,"previousblockhash":"0x56fd4244e552536a4dae38b1446f8689b6f123b116e6c37028cac4439cb1aa8a","merkleroot":"0xf9dce467385206ad220a8b85d238f77239766eaffffed19955e3e47d24071140","time":1592472500001,"index":1,"nextconsensus":"Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2","witnesses":[{"invocation":"DEAPAetjcaE1Un3bIFoNrh9p0tMkg3zOEovq2QMwkYg/nOIebXWaQPaQdGWSsCHTl/CI47e0F/zvc80b/cKAB2lSDECE965dn1igmqVu6y8oJ0SlmoLRfjvg6xx/VObo33liDiYIGRusVygKg22y7Cp3bQfkPxa8dsR9NIsPzQnVbHwyDEArlbTjnsNRYqZAeV/yI+kt7JU5CgcutFf2pDIwUvgHMVF+DfAp5KRXIE93f1JhxqSojUbUw6vexjWm7tWA1sd/DEC5c1dFsd15WiWMMdjm+ofVz8Lps6SJDWENM7z4M7ZMWLDFzqF/OnEo8QZe0ZPmNpcVkPGT8ovdHu67vB/m587l","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBMHOzuw=="}],"confirmations":6,"nextblockhash":"0x45f62d72e37b074ecdc9f687222b0f91ec98d6d9af4429a2caa2e076a9196b0d"}}`
const txMoveNeoVerbose = `{"id":5,"jsonrpc":"2.0","result":{"blockhash":"0x4f2c5539b0213ea444608cc217c5cb191255c1858ccd051ad9a36f08df26a288","confirmations":6,"blocktime":1592472500001,"hash":"0x7fb05b593cf4b1eb2d9a283c5488ca1bfe61191b5775bafa43b8647e7b20f22c","size":575,"version":0,"nonce":2,"sender":"Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2","sysfee":"0","netfee":"876350","validuntilblock":1200,"attributes":[],"cosigners":[{"account":"0x4138145d67638db97b402ee0b6751ef16253ecab","scopes":"CalledByEntry"}],"script":"Ahjd9QUMFDFuhRA5AZ0538LDfWw/7hn9WAmHDBSr7FNi8R51tuAuQHu5jWNnXRQ4QRPADAh0cmFuc2ZlcgwUiXcg2M129PAKv6N8Dt2InCCP3ptBYn1bUjg=","witnesses":[{"invocation":"DECub8BP5LbCIhjKlhfJjWB9nsmx+vjP3DORvsSFrnaxGtxsxqvrMaULU26oBz5nTWKlVm/OXgoM6wcYy5ccGuPQDEBgMHG3JaWNBSytev2I6ZsnuquTGv1btQ0W4iQzWqtFAXCqviUdPAxq0/Md1+m4myCbqr5aHi+liL2BGPnippYPDEDXKvzznmY9ui1w+4w2oJ0aamrQ0v04yFeo59xx4rmHETJODS7GQf5olrpjuoDT6jQcGq0R4IL7GI7gfiFbQDGxDECa+ygItgKGpWNDt//O8ouyqwxZVgPnMjteWwucHBDt+lxAdU2SGGXLb9cWaKIGs3oesQwAKan806hWrtB3Qs0/","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBMHOzuw=="}]}}`
// getResultBlock1 returns data for block number 1 which is used by several tests.
func getResultBlock1() *result.Block {
binB, err := hex.DecodeString(hexB1)
if err != nil {
panic(err)
}
b := block.New(netmode.UnitTestNet)
err = testserdes.DecodeBinary(binB, b)
if err != nil {
panic(err)
}
b2Hash, err := util.Uint256DecodeStringLE("45f62d72e37b074ecdc9f687222b0f91ec98d6d9af4429a2caa2e076a9196b0d")
if err != nil {
panic(err)
}
return &result.Block{
Block: *b,
BlockMetadata: result.BlockMetadata{
Size: 1681,
NextBlockHash: &b2Hash,
Confirmations: 6,
},
}
}
func getTxMoveNeo() *result.TransactionOutputRaw {
b1 := getResultBlock1()
txBin, err := hex.DecodeString(hexTxMoveNeo)
if err != nil {
panic(err)
}
tx, err := transaction.NewTransactionFromBytes(netmode.UnitTestNet, txBin)
if err != nil {
panic(err)
}
return &result.TransactionOutputRaw{
Transaction: *tx,
TransactionMetadata: result.TransactionMetadata{
Timestamp: b1.Timestamp,
Blockhash: b1.Block.Hash(),
Confirmations: int(b1.Confirmations),
},
}
}
// rpcClientTestCases contains `serverResponse` json data fetched from examples
// published in official C# JSON-RPC API v2.10.3 reference
// (see https://docs.neo.org/docs/en-us/reference/rpc/latest-version/api.html)
var rpcClientTestCases = map[string][]rpcClientTestCase{
"getapplicationlog": {
{
name: "positive",
invoke: func(c *Client) (interface{}, error) {
return c.GetApplicationLog(util.Uint256{})
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"txid":"0x17145a039fca704fcdbeb46e6b210af98a1a9e5b9768e46ffc38f71c79ac2521","trigger":"Application","vmstate":"HALT","gasconsumed":"1","stack":[{"type":"Integer","value":1}],"notifications":[]}}`,
result: func(c *Client) interface{} {
txHash, err := util.Uint256DecodeStringLE("17145a039fca704fcdbeb46e6b210af98a1a9e5b9768e46ffc38f71c79ac2521")
if err != nil {
panic(err)
}
return &result.ApplicationLog{
TxHash: txHash,
Trigger: "Application",
VMState: "HALT",
GasConsumed: 1,
Stack: []smartcontract.Parameter{{Type: smartcontract.IntegerType, Value: int64(1)}},
Events: []result.NotificationEvent{},
}
},
},
},
"getbestblockhash": {
{
name: "positive",
invoke: func(c *Client) (interface{}, error) {
return c.GetBestBlockHash()
},
serverResponse: `{"jsonrpc":"2.0","id":1,"result":"0x773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"}`,
result: func(c *Client) interface{} {
result, err := util.Uint256DecodeStringLE("773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e")
if err != nil {
panic(err)
}
return result
},
},
},
"getblock": {
{
name: "byIndex_positive",
invoke: func(c *Client) (interface{}, error) {
return c.GetBlockByIndex(1)
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":"` + hexB1 + `"}`,
result: func(c *Client) interface{} {
b := getResultBlock1()
return &b.Block
},
},
{
name: "byIndex_verbose_positive",
invoke: func(c *Client) (i interface{}, err error) {
return c.GetBlockByIndexVerbose(1)
},
serverResponse: b1Verbose,
result: func(c *Client) interface{} {
return getResultBlock1()
},
},
{
name: "byHash_positive",
invoke: func(c *Client) (interface{}, error) {
hash, err := util.Uint256DecodeStringLE("d151651e86680a7ecbc87babf3346a42e7bc9974414ce192c9c22ac4f2e9d043")
if err != nil {
panic(err)
}
return c.GetBlockByHash(hash)
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":"` + hexB1 + `"}`,
result: func(c *Client) interface{} {
b := getResultBlock1()
return &b.Block
},
},
{
name: "byHash_verbose_positive",
invoke: func(c *Client) (i interface{}, err error) {
hash, err := util.Uint256DecodeStringLE("86fe1061140b2ea791b0739fb9732abc6e5e47de4927228a1ac41de3d93eb7cb")
if err != nil {
panic(err)
}
return c.GetBlockByHashVerbose(hash)
},
serverResponse: b1Verbose,
result: func(c *Client) interface{} {
return getResultBlock1()
},
},
},
"getblockcount": {
{
name: "positive",
invoke: func(c *Client) (interface{}, error) {
return c.GetBlockCount()
},
serverResponse: `{"jsonrpc":"2.0","id":1,"result":991991}`,
result: func(c *Client) interface{} {
return uint32(991991)
},
},
},
"getblockhash": {
{
name: "positive",
invoke: func(c *Client) (interface{}, error) {
return c.GetBlockHash(1)
},
serverResponse: `{"jsonrpc":"2.0","id":1,"result":"0x4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2"}`,
result: func(c *Client) interface{} {
hash, err := util.Uint256DecodeStringLE("4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2")
if err != nil {
panic(err)
}
return hash
},
},
},
"getblockheader": {
{
name: "positive",
invoke: func(c *Client) (interface{}, error) {
hash, err := util.Uint256DecodeStringLE("68e4bd688b852e807eef13a0ff7da7b02223e359a35153667e88f9cb4a3b0801")
if err != nil {
panic(err)
}
return c.GetBlockHeader(hash)
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":"` + hexHeader1 + `"}`,
result: func(c *Client) interface{} {
b := getResultBlock1()
return b.Header()
},
},
{
name: "verbose_positive",
invoke: func(c *Client) (i interface{}, err error) {
hash, err := util.Uint256DecodeStringLE("cbb73ed9e31dc41a8a222749de475e6ebc2a73b99f73b091a72e0b146110fe86")
if err != nil {
panic(err)
}
return c.GetBlockHeaderVerbose(hash)
},
serverResponse: header1Verbose,
result: func(c *Client) interface{} {
b := getResultBlock1()
return &result.Header{
Hash: b.Hash(),
Size: 518,
Version: b.Version,
NextBlockHash: b.NextBlockHash,
PrevBlockHash: b.PrevHash,
MerkleRoot: b.MerkleRoot,
Timestamp: b.Timestamp,
Index: b.Index,
NextConsensus: address.Uint160ToString(b.NextConsensus),
Witnesses: []transaction.Witness{b.Script},
Confirmations: 6,
}
},
},
},
"getblocksysfee": {
{
name: "positive",
invoke: func(c *Client) (interface{}, error) {
return c.GetBlockSysFee(1)
},
serverResponse: `{"jsonrpc":"2.0","id":1,"result":"195500"}`,
result: func(c *Client) interface{} {
return util.Fixed8FromInt64(195500)
},
},
},
"getconnectioncount": {
{
name: "positive",
invoke: func(c *Client) (interface{}, error) {
return c.GetConnectionCount()
},
serverResponse: `{"jsonrpc":"2.0","id":1,"result":10}`,
result: func(c *Client) interface{} {
return 10
},
},
},
"getcontractstate": {
{
name: "positive",
invoke: func(c *Client) (interface{}, error) {
hash, err := util.Uint160DecodeStringLE("1b4357bff5a01bdf2a6581247cf9ed1e24629176")
if err != nil {
panic(err)
}
return c.GetContractState(hash)
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"id":0,"script":"VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==","manifest":{"abi":{"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","methods":[],"events":[]},"groups":[],"features":{"payable":false,"storage":true},"permissions":null,"trusts":[],"safemethods":[],"extra":null},"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176"}}`,
result: func(c *Client) interface{} {
script, err := base64.StdEncoding.DecodeString("VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==")
if err != nil {
panic(err)
}
m := manifest.NewManifest(hash.Hash160(script))
m.Features = smartcontract.HasStorage
cs := &state.Contract{
ID: 0,
Script: script,
Manifest: *m,
}
_ = cs.ScriptHash()
return cs
},
},
},
"getFeePerByte": {
{
name: "positive",
invoke: func(c *Client) (interface{}, error) {
return c.GetFeePerByte()
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"10c00c0d676574466565506572427974650c149a61a46eec97b89306d7ce81f15b462091d0093241627d5b52","stack":[{"type":"Integer","value":"1000"}]}}`,
result: func(c *Client) interface{} {
return int64(1000)
},
},
},
"getMaxTransacctionsPerBlock": {
{
name: "positive",
invoke: func(c *Client) (interface{}, error) {
return c.GetMaxTransactionsPerBlock()
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"10c00c1a6765744d61785472616e73616374696f6e73506572426c6f636b0c149a61a46eec97b89306d7ce81f15b462091d0093241627d5b52","stack":[{"type":"Integer","value":"512"}]}}`,
result: func(c *Client) interface{} {
return int64(512)
},
},
},
"getMaxBlockSize": {
{
name: "positive",
invoke: func(c *Client) (interface{}, error) {
return c.GetMaxBlockSize()
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"10c00c0f6765744d6178426c6f636b53697a650c149a61a46eec97b89306d7ce81f15b462091d0093241627d5b52","stack":[{"type":"Integer","value":"262144"}]}}`,
result: func(c *Client) interface{} {
return int64(262144)
},
},
},
"getBlockedAccounts": {
{
name: "positive",
invoke: func(c *Client) (interface{}, error) {
return c.GetBlockedAccounts()
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"state":"HALT","gasconsumed":"2007390","script":"10c00c12676574426c6f636b65644163636f756e74730c149a61a46eec97b89306d7ce81f15b462091d0093241627d5b52","stack":[{"type":"Array","value":[]}]}}`,
result: func(c *Client) interface{} {
return native.BlockedAccounts{}
},
},
},
"getnep5balances": {
{
name: "positive",
invoke: func(c *Client) (interface{}, error) {
hash, err := util.Uint160DecodeStringLE("1aada0032aba1ef6d1f07bbd8bec1d85f5380fb3")
if err != nil {
panic(err)
}
return c.GetNEP5Balances(hash)
},
serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"balance":[{"assethash":"a48b6e1291ba24211ad11bb90ae2a10bf1fcd5a8","amount":"50000000000","lastupdatedblock":251604}],"address":"AY6eqWjsUFCzsVELG7yG72XDukKvC34p2w"}}`,
result: func(c *Client) interface{} {
hash, err := util.Uint160DecodeStringLE("a48b6e1291ba24211ad11bb90ae2a10bf1fcd5a8")
if err != nil {
panic(err)
}
return &result.NEP5Balances{
Balances: []result.NEP5Balance{{
Asset: hash,
Amount: "50000000000",
LastUpdated: 251604,
}},
Address: "AY6eqWjsUFCzsVELG7yG72XDukKvC34p2w",
}
},
},
},
"getnep5transfers": {
{
name: "positive",
invoke: func(c *Client) (interface{}, error) {
return c.GetNEP5Transfers("AbHgdBaWEnHkCiLtDZXjhvhaAK2cwFh5pF")
},
serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"sent":[],"received":[{"timestamp":1555651816,"assethash":"600c4f5200db36177e3e8a09e9f18e2fc7d12a0f","transferaddress":"AYwgBNMepiv5ocGcyNT4mA8zPLTQ8pDBis","amount":"1000000","blockindex":436036,"transfernotifyindex":0,"txhash":"df7683ece554ecfb85cf41492c5f143215dd43ef9ec61181a28f922da06aba58"}],"address":"AbHgdBaWEnHkCiLtDZXjhvhaAK2cwFh5pF"}}`,
result: func(c *Client) interface{} {
assetHash, err := util.Uint160DecodeStringLE("600c4f5200db36177e3e8a09e9f18e2fc7d12a0f")
if err != nil {
panic(err)
}
txHash, err := util.Uint256DecodeStringLE("df7683ece554ecfb85cf41492c5f143215dd43ef9ec61181a28f922da06aba58")
if err != nil {
panic(err)
}
return &result.NEP5Transfers{
Sent: []result.NEP5Transfer{},
Received: []result.NEP5Transfer{
{
Timestamp: 1555651816,
Asset: assetHash,
Address: "AYwgBNMepiv5ocGcyNT4mA8zPLTQ8pDBis",
Amount: "1000000",
Index: 436036,
NotifyIndex: 0,
TxHash: txHash,
},
},
Address: "AbHgdBaWEnHkCiLtDZXjhvhaAK2cwFh5pF",
}
},
},
},
"getpeers": {
{
name: "positive",
invoke: func(c *Client) (interface{}, error) {
return c.GetPeers()
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"unconnected":[{"address":"172.200.0.1","port":"20333"}],"connected":[{"address":"127.0.0.1","port":"20335"}],"bad":[{"address":"172.200.0.254","port":"20332"}]}}`,
result: func(c *Client) interface{} {
return &result.GetPeers{
Unconnected: result.Peers{
{
Address: "172.200.0.1",
Port: "20333",
},
},
Connected: result.Peers{
{
Address: "127.0.0.1",
Port: "20335",
},
},
Bad: result.Peers{
{
Address: "172.200.0.254",
Port: "20332",
},
},
}
},
},
},
"getrawmempool": {
{
name: "positive",
invoke: func(c *Client) (interface{}, error) {
return c.GetRawMemPool()
},
serverResponse: `{"jsonrpc":"2.0","id":1,"result":["0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e"]}`,
result: func(c *Client) interface{} {
hash, err := util.Uint256DecodeStringLE("9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e")
if err != nil {
panic(err)
}
return []util.Uint256{hash}
},
},
},
"getrawtransaction": {
{
name: "positive",
invoke: func(c *Client) (i interface{}, err error) {
hash, err := util.Uint256DecodeStringLE("ca23bd5df3249836849309ca2afe972bfd288b0a7ae61302c8fd545daa8bffd6")
if err != nil {
panic(err)
}
return c.GetRawTransaction(hash)
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":"` + hexTxMoveNeo + `"}`,
result: func(c *Client) interface{} {
tx := getTxMoveNeo()
return &tx.Transaction
},
},
{
name: "verbose_positive",
invoke: func(c *Client) (interface{}, error) {
hash, err := util.Uint256DecodeStringLE("8185b0db7ed77190b93ac8bd44896822cd8f3cfcf702b3f50131e0efd200ef96")
if err != nil {
panic(err)
}
out, err := c.GetRawTransactionVerbose(hash)
if err != nil {
return nil, err
}
out.Transaction.FeePerByte() // set fee per byte
return out, nil
},
serverResponse: txMoveNeoVerbose,
result: func(c *Client) interface{} {
return getTxMoveNeo()
},
},
},
"getstorage": {
{
name: "by hash, positive",
invoke: func(c *Client) (interface{}, error) {
hash, err := util.Uint160DecodeStringLE("03febccf81ac85e3d795bc5cbd4e84e907812aa3")
if err != nil {
panic(err)
}
key, err := hex.DecodeString("5065746572")
if err != nil {
panic(err)
}
return c.GetStorageByHash(hash, key)
},
serverResponse: `{"jsonrpc":"2.0","id":1,"result":"4c696e"}`,
result: func(c *Client) interface{} {
value, err := hex.DecodeString("4c696e")
if err != nil {
panic(err)
}
return value
},
},
{
name: "by ID, positive",
invoke: func(c *Client) (interface{}, error) {
key, err := hex.DecodeString("5065746572")
if err != nil {
panic(err)
}
return c.GetStorageByID(-1, key)
},
serverResponse: `{"jsonrpc":"2.0","id":1,"result":"4c696e"}`,
result: func(c *Client) interface{} {
value, err := hex.DecodeString("4c696e")
if err != nil {
panic(err)
}
return value
},
},
},
"gettransactionheight": {
{
name: "positive",
invoke: func(c *Client) (interface{}, error) {
hash, err := util.Uint256DecodeStringLE("cb6ddb5f99d6af4c94a6c396d5294472f2eebc91a2c933e0f527422296fa9fb2")
if err != nil {
panic(err)
}
return c.GetTransactionHeight(hash)
},
serverResponse: `{"jsonrpc":"2.0","id":1,"result":1}`,
result: func(c *Client) interface{} {
return uint32(1)
},
},
},
"getunclaimedgas": {
{
name: "positive",
invoke: func(c *Client) (interface{}, error) {
return c.GetUnclaimedGas("NMipL5VsNoLUBUJKPKLhxaEbPQVCZnyJyB")
},
serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"address":"NMipL5VsNoLUBUJKPKLhxaEbPQVCZnyJyB","unclaimed":"897299680935"}}`,
result: func(c *Client) interface{} {
addr, err := address.StringToUint160("NMipL5VsNoLUBUJKPKLhxaEbPQVCZnyJyB")
if err != nil {
panic(errors.Wrap(err, "failed to parse UnclaimedGas address"))
}
return result.UnclaimedGas{
Address: addr,
Unclaimed: *big.NewInt(897299680935),
}
},
},
},
"getvalidators": {
{
name: "positive",
invoke: func(c *Client) (interface{}, error) {
return c.GetValidators()
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":[{"publickey":"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2","votes":"0","active":true},{"publickey":"02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e","votes":"0","active":true},{"publickey":"03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699","votes":"0","active":true},{"publickey":"02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62","votes":"0","active":true}]}`,
result: func(c *Client) interface{} { return []result.Validator{} },
check: func(t *testing.T, c *Client, uns interface{}) {
res, ok := uns.([]result.Validator)
require.True(t, ok)
assert.Equal(t, 4, len(res))
},
},
},
"getversion": {
{
name: "positive",
invoke: func(c *Client) (interface{}, error) {
return c.GetVersion()
},
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"tcpport":20332,"wsport":20342,"nonce":2153672787,"useragent":"/NEO-GO:0.73.1-pre-273-ge381358/"}}`,
result: func(c *Client) interface{} {
return &result.Version{
TCPPort: uint16(20332),
WSPort: uint16(20342),
Nonce: 2153672787,
UserAgent: "/NEO-GO:0.73.1-pre-273-ge381358/",
}
},
},
},
"invokefunction": {
{
name: "positive",
invoke: func(c *Client) (interface{}, error) {
hash, err := util.Uint160DecodeStringLE("91b83e96f2a7c4fdf0c1688441ec61986c7cae26")
if err != nil {
panic(err)
}
contr, err := util.Uint160DecodeStringLE("af7c7328eee5a275a3bcaee2bf0cf662b5e739be")
if err != nil {
panic(err)
}
return c.InvokeFunction(contr, "balanceOf", []smartcontract.Parameter{
{
Type: smartcontract.Hash160Type,
Value: hash,
},
}, []transaction.Cosigner{{
Account: util.Uint160{1, 2, 3},
}})
},
serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"script":"1426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf","state":"HALT","gasconsumed":"31100000","stack":[{"type":"ByteArray","value":"JivsCEQy"}],"tx":"d101361426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf000000000000000000000000"}}`,
result: func(c *Client) interface{} {
bytes, err := hex.DecodeString("262bec084432")
if err != nil {
panic(err)
}
return &result.Invoke{
State: "HALT",
GasConsumed: 31100000,
Script: "1426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf",
Stack: []smartcontract.Parameter{
{
Type: smartcontract.ByteArrayType,
Value: bytes,
},
},
}
},
},
},
"invokescript": {
{
name: "positive",
invoke: func(c *Client) (interface{}, error) {
script, err := hex.DecodeString("00046e616d656724058e5e1b6008847cd662728549088a9ee82191")
if err != nil {
panic(err)
}
return c.InvokeScript(script, []transaction.Cosigner{{
Account: util.Uint160{1, 2, 3},
}})
},
serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"script":"00046e616d656724058e5e1b6008847cd662728549088a9ee82191","state":"HALT","gasconsumed":"16100000","stack":[{"type":"ByteArray","value":"TkVQNSBHQVM="}],"tx":"d1011b00046e616d656724058e5e1b6008847cd662728549088a9ee82191000000000000000000000000"}}`,
result: func(c *Client) interface{} {
bytes, err := hex.DecodeString("4e45503520474153")
if err != nil {
panic(err)
}
return &result.Invoke{
State: "HALT",
GasConsumed: 16100000,
Script: "00046e616d656724058e5e1b6008847cd662728549088a9ee82191",
Stack: []smartcontract.Parameter{
{
Type: smartcontract.ByteArrayType,
Value: bytes,
},
},
}
},
},
},
"sendrawtransaction": {
{
name: "positive",
invoke: func(c *Client) (interface{}, error) {
return c.SendRawTransaction(transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0))
},
serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"hash":"0x72159b0cf1221110daad6e1df6ef4ff03012173b63c86910bd7134deb659c875"}}`,
result: func(c *Client) interface{} {
h, err := util.Uint256DecodeStringLE("72159b0cf1221110daad6e1df6ef4ff03012173b63c86910bd7134deb659c875")
if err != nil {
panic(fmt.Errorf("can't decode `sendrawtransaction` result hash: %v", err))
}
return h
},
},
},
"submitblock": {
{
name: "positive",
invoke: func(c *Client) (interface{}, error) {
return c.SubmitBlock(block.Block{
Base: block.Base{},
Transactions: nil,
Trimmed: false,
})
},
serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"hash":"0x1bdea8f80eb5bd97fade38d5e7fb93b02c9d3e01394e9f4324218132293f7ea6"}}`,
result: func(c *Client) interface{} {
h, err := util.Uint256DecodeStringLE("1bdea8f80eb5bd97fade38d5e7fb93b02c9d3e01394e9f4324218132293f7ea6")
if err != nil {
panic(fmt.Errorf("can't decode `submitblock` result hash: %v", err))
}
return h
},
},
},
"validateaddress": {
{
name: "positive",
invoke: func(c *Client) (interface{}, error) {
return nil, c.ValidateAddress("AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i")
},
serverResponse: `{"jsonrpc":"2.0","id":1,"result":{"address":"AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i","isvalid":true}}`,
result: func(c *Client) interface{} {
// no error expected
return nil
},
},
},
}
type rpcClientErrorCase struct {
name string
invoke func(c *Client) (interface{}, error)
}
var rpcClientErrorCases = map[string][]rpcClientErrorCase{
`{"jsonrpc":"2.0","id":1,"result":"not-a-hex-string"}`: {
{
name: "getblock_not_a_hex_response",
invoke: func(c *Client) (interface{}, error) {
return c.GetBlockByIndex(1)
},
},
{
name: "getblockheader_not_a_hex_response",
invoke: func(c *Client) (interface{}, error) {
hash, err := util.Uint256DecodeStringLE("e93d17a52967f9e69314385482bf86f85260e811b46bf4d4b261a7f4135a623c")
if err != nil {
panic(err)
}
return c.GetBlockHeader(hash)
},
},
{
name: "getrawtransaction_not_a_hex_response",
invoke: func(c *Client) (interface{}, error) {
hash, err := util.Uint256DecodeStringLE("e93d17a52967f9e69314385482bf86f85260e811b46bf4d4b261a7f4135a623c")
if err != nil {
panic(err)
}
return c.GetRawTransaction(hash)
},
},
{
name: "getstoragebyhash_not_a_hex_response",
invoke: func(c *Client) (interface{}, error) {
hash, err := util.Uint160DecodeStringLE("03febccf81ac85e3d795bc5cbd4e84e907812aa3")
if err != nil {
panic(err)
}
key, err := hex.DecodeString("5065746572")
if err != nil {
panic(err)
}
return c.GetStorageByHash(hash, key)
},
},
{
name: "getstoragebyid_not_a_hex_response",
invoke: func(c *Client) (interface{}, error) {
key, err := hex.DecodeString("5065746572")
if err != nil {
panic(err)
}
return c.GetStorageByID(-1, key)
},
},
},
`{"jsonrpc":"2.0","id":1,"result":"01"}`: {
{
name: "getblock_decodebin_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetBlockByIndex(1)
},
},
{
name: "getheader_decodebin_err",
invoke: func(c *Client) (interface{}, error) {
hash, err := util.Uint256DecodeStringLE("e93d17a52967f9e69314385482bf86f85260e811b46bf4d4b261a7f4135a623c")
if err != nil {
panic(err)
}
return c.GetBlockHeader(hash)
},
},
{
name: "getrawtransaction_decodebin_err",
invoke: func(c *Client) (interface{}, error) {
hash, err := util.Uint256DecodeStringLE("e93d17a52967f9e69314385482bf86f85260e811b46bf4d4b261a7f4135a623c")
if err != nil {
panic(err)
}
return c.GetRawTransaction(hash)
},
},
},
`{"jsonrpc":"2.0","id":1,"result":false}`: {
{
name: "sendrawtransaction_bad_server_answer",
invoke: func(c *Client) (interface{}, error) {
return c.SendRawTransaction(transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0))
},
},
{
name: "submitblock_bad_server_answer",
invoke: func(c *Client) (interface{}, error) {
return c.SubmitBlock(block.Block{
Base: block.Base{},
Transactions: nil,
Trimmed: false,
})
},
},
{
name: "validateaddress_bad_server_answer",
invoke: func(c *Client) (interface{}, error) {
return nil, c.ValidateAddress("AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i")
},
},
},
`{"id":1,"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid Params"}}`: {
{
name: "getapplicationlog_invalid_params_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetApplicationLog(util.Uint256{})
},
},
{
name: "getbestblockhash_invalid_params_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetBestBlockHash()
},
},
{
name: "getblock_byindex_invalid_params_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetBlockByIndex(1)
},
},
{
name: "getblock_byindex_verbose_invalid_params_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetBlockByIndexVerbose(1)
},
},
{
name: "getblock_byhash_invalid_params_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetBlockByHash(util.Uint256{})
},
},
{
name: "getblock_byhash_verbose_invalid_params_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetBlockByHashVerbose(util.Uint256{})
},
},
{
name: "getblockhash_invalid_params_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetBlockHash(0)
},
},
{
name: "getblockheader_invalid_params_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetBlockHeader(util.Uint256{})
},
},
{
name: "getblockheader_verbose_invalid_params_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetBlockHeaderVerbose(util.Uint256{})
},
},
{
name: "getblocksysfee_invalid_params_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetBlockSysFee(1)
},
},
{
name: "getconnectioncount_invalid_params_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetConnectionCount()
},
},
{
name: "getcontractstate_invalid_params_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetContractState(util.Uint160{})
},
},
{
name: "getnep5balances_invalid_params_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetNEP5Balances(util.Uint160{})
},
},
{
name: "getnep5transfers_invalid_params_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetNEP5Transfers("")
},
},
{
name: "getrawtransaction_invalid_params_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetRawTransaction(util.Uint256{})
},
},
{
name: "getrawtransaction_verbose_invalid_params_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetRawTransactionVerbose(util.Uint256{})
},
},
{
name: "getstoragebyhash_invalid_params_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetStorageByHash(util.Uint160{}, []byte{})
},
},
{
name: "getstoragebyid_invalid_params_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetStorageByID(-1, []byte{})
},
},
{
name: "gettransactionheight_invalid_params_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetTransactionHeight(util.Uint256{})
},
},
{
name: "getunclaimedgas_invalid_params_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetUnclaimedGas("")
},
},
{
name: "invokefunction_invalid_params_error",
invoke: func(c *Client) (interface{}, error) {
return c.InvokeFunction(util.Uint160{}, "", []smartcontract.Parameter{}, nil)
},
},
{
name: "invokescript_invalid_params_error",
invoke: func(c *Client) (interface{}, error) {
return c.InvokeScript([]byte{}, nil)
},
},
{
name: "sendrawtransaction_invalid_params_error",
invoke: func(c *Client) (interface{}, error) {
return c.SendRawTransaction(&transaction.Transaction{})
},
},
{
name: "submitblock_invalid_params_error",
invoke: func(c *Client) (interface{}, error) {
return c.SubmitBlock(block.Block{})
},
},
{
name: "validateaddress_invalid_params_error",
invoke: func(c *Client) (interface{}, error) {
return nil, c.ValidateAddress("")
},
},
},
`{}`: {
{
name: "getapplicationlog_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetApplicationLog(util.Uint256{})
},
},
{
name: "getbestblockhash_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetBestBlockHash()
},
},
{
name: "getblock_byindex_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetBlockByIndex(1)
},
},
{
name: "getblock_byindex_verbose_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetBlockByIndexVerbose(1)
},
},
{
name: "getblock_byhash_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetBlockByHash(util.Uint256{})
},
},
{
name: "getblock_byhash_verbose_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetBlockByHashVerbose(util.Uint256{})
},
},
{
name: "getblockcount_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetBlockCount()
},
},
{
name: "getblockhash_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetBlockHash(1)
},
},
{
name: "getblockheader_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetBlockHeader(util.Uint256{})
},
},
{
name: "getblockheader_verbose_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetBlockHeaderVerbose(util.Uint256{})
},
},
{
name: "getblocksysfee_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetBlockSysFee(1)
},
},
{
name: "getconnectioncount_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetConnectionCount()
},
},
{
name: "getcontractstate_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetContractState(util.Uint160{})
},
},
{
name: "getnep5balances_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetNEP5Balances(util.Uint160{})
},
},
{
name: "getnep5transfers_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetNEP5Transfers("")
},
},
{
name: "getpeers_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetPeers()
},
},
{
name: "getrawmempool_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetRawMemPool()
},
},
{
name: "getrawtransaction_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetRawTransaction(util.Uint256{})
},
},
{
name: "getrawtransaction_verbose_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetRawTransactionVerbose(util.Uint256{})
},
},
{
name: "getstoragebyhash_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetStorageByHash(util.Uint160{}, []byte{})
},
},
{
name: "getstoragebyid_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetStorageByID(-1, []byte{})
},
},
{
name: "gettransactionheight_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetTransactionHeight(util.Uint256{})
},
},
{
name: "getunclaimedgas_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetUnclaimedGas("")
},
},
{
name: "getvalidators_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetValidators()
},
},
{
name: "getversion_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.GetVersion()
},
},
{
name: "invokefunction_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.InvokeFunction(util.Uint160{}, "", []smartcontract.Parameter{}, nil)
},
},
{
name: "invokescript_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.InvokeScript([]byte{}, nil)
},
},
{
name: "sendrawtransaction_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.SendRawTransaction(transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0))
},
},
{
name: "submitblock_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return c.SubmitBlock(block.Block{
Base: block.Base{},
Transactions: nil,
Trimmed: false,
})
},
},
{
name: "validateaddress_unmarshalling_error",
invoke: func(c *Client) (interface{}, error) {
return nil, c.ValidateAddress("")
},
},
},
}
func TestRPCClients(t *testing.T) {
t.Run("Client", func(t *testing.T) {
testRPCClient(t, func(ctx context.Context, endpoint string, opts Options) (*Client, error) {
return New(ctx, endpoint, opts)
})
})
t.Run("WSClient", func(t *testing.T) {
testRPCClient(t, func(ctx context.Context, endpoint string, opts Options) (*Client, error) {
wsc, err := NewWS(ctx, httpURLtoWS(endpoint), opts)
require.NoError(t, err)
return &wsc.Client, nil
})
})
}
func testRPCClient(t *testing.T, newClient func(context.Context, string, Options) (*Client, error)) {
for method, testBatch := range rpcClientTestCases {
t.Run(method, func(t *testing.T) {
for _, testCase := range testBatch {
t.Run(testCase.name, func(t *testing.T) {
srv := initTestServer(t, testCase.serverResponse)
defer srv.Close()
endpoint := srv.URL
opts := Options{Network: netmode.UnitTestNet}
c, err := newClient(context.TODO(), endpoint, opts)
if err != nil {
t.Fatal(err)
}
actual, err := testCase.invoke(c)
assert.NoError(t, err)
expected := testCase.result(c)
if testCase.check == nil {
assert.Equal(t, expected, actual)
} else {
testCase.check(t, c, actual)
}
})
}
})
}
for serverResponse, testBatch := range rpcClientErrorCases {
srv := initTestServer(t, serverResponse)
defer srv.Close()
endpoint := srv.URL
opts := Options{Network: netmode.UnitTestNet}
c, err := newClient(context.TODO(), endpoint, opts)
if err != nil {
t.Fatal(err)
}
for _, testCase := range testBatch {
t.Run(testCase.name, func(t *testing.T) {
_, err := testCase.invoke(c)
assert.Error(t, err)
})
}
}
}
func httpURLtoWS(url string) string {
return "ws" + strings.TrimPrefix(url, "http") + "/ws"
}
func initTestServer(t *testing.T, resp string) *httptest.Server {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if req.URL.Path == "/ws" && req.Method == "GET" {
var upgrader = websocket.Upgrader{}
ws, err := upgrader.Upgrade(w, req, nil)
require.NoError(t, err)
for {
ws.SetReadDeadline(time.Now().Add(2 * time.Second))
_, _, err = ws.ReadMessage()
if err != nil {
break
}
ws.SetWriteDeadline(time.Now().Add(2 * time.Second))
err = ws.WriteMessage(1, []byte(resp))
if err != nil {
break
}
}
ws.Close()
return
}
requestHandler(t, w, resp)
}))
return srv
}
func requestHandler(t *testing.T, w http.ResponseWriter, resp string) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
_, err := w.Write([]byte(resp))
if err != nil {
t.Fatalf("Error writing response: %s", err.Error())
}
}
func TestCalculateValidUntilBlock(t *testing.T) {
var (
getBlockCountCalled int
getValidatorsCalled int
)
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
r := request.NewIn()
err := r.DecodeData(req.Body)
if err != nil {
t.Fatalf("Cannot decode request body: %s", req.Body)
}
var response string
switch r.Method {
case "getblockcount":
getBlockCountCalled++
response = `{"jsonrpc":"2.0","id":1,"result":50}`
case "getvalidators":
getValidatorsCalled++
response = `{"id":1,"jsonrpc":"2.0","result":[{"publickey":"02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2","votes":"0","active":true},{"publickey":"02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e","votes":"0","active":true},{"publickey":"03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699","votes":"0","active":true},{"publickey":"02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62","votes":"0","active":true}]}`
default:
t.Fatalf("Bad request method: %s", r.Method)
}
requestHandler(t, w, response)
}))
defer srv.Close()
endpoint := srv.URL
opts := Options{}
c, err := New(context.TODO(), endpoint, opts)
if err != nil {
t.Fatal(err)
}
validUntilBlock, err := c.CalculateValidUntilBlock()
assert.NoError(t, err)
assert.Equal(t, uint32(54), validUntilBlock)
assert.Equal(t, 1, getBlockCountCalled)
assert.Equal(t, 1, getValidatorsCalled)
// check, whether caching is working
validUntilBlock, err = c.CalculateValidUntilBlock()
assert.NoError(t, err)
assert.Equal(t, uint32(54), validUntilBlock)
assert.Equal(t, 2, getBlockCountCalled)
assert.Equal(t, 1, getValidatorsCalled)
}