unitTest: reworked RPC unit test

earlier we had an issue with failing test in #353 and other one #305.
Reworked these test to have in-memory database. This led to multiple
changes: made some functions like Hash and Persist public(otherwise
it's not possible to control state of the blockchain); removed
unit_tests storage package which was used mainly for leveldb in unit
tests.
I see these tests not really good since they look like e2e tests and
as for me should be run in separate step against dockerized env or
in case we want to check rpc handler we might want to rework it in order
to have interface for proper unit tests.
As for me this patchset at least makes as safe with not removing totally
previous tests and at the same time CircleCI will be happy now.
This commit is contained in:
Vsevolod Brekelov 2019-09-18 18:21:16 +03:00
parent 55dfc0bbbc
commit 100fee164b
17 changed files with 394 additions and 346 deletions

View file

@ -37,7 +37,7 @@ type Blockchain struct {
// Current index/height of the highest block. // Current index/height of the highest block.
// Read access should always be called by BlockHeight(). // Read access should always be called by BlockHeight().
// Write access should only happen in persist(). // Write access should only happen in Persist().
blockHeight uint32 blockHeight uint32
// Number of headers stored in the chain file. // Number of headers stored in the chain file.
@ -167,7 +167,10 @@ func (bc *Blockchain) Run(ctx context.Context) {
op(bc.headerList) op(bc.headerList)
bc.headersOpDone <- struct{}{} bc.headersOpDone <- struct{}{}
case <-persistTimer.C: case <-persistTimer.C:
go bc.persist(ctx) go func() {
err := bc.Persist(ctx)
log.Warnf("failed to persist blockchain: %s", err)
}()
persistTimer.Reset(persistInterval) persistTimer.Reset(persistInterval)
} }
} }
@ -389,7 +392,8 @@ func (bc *Blockchain) persistBlock(block *Block) error {
return nil return nil
} }
func (bc *Blockchain) persist(ctx context.Context) (err error) { //Persist starts persist loop.
func (bc *Blockchain) Persist(ctx context.Context) (err error) {
var ( var (
start = time.Now() start = time.Now()
persisted = 0 persisted = 0

View file

@ -13,6 +13,9 @@ import (
func TestAddHeaders(t *testing.T) { func TestAddHeaders(t *testing.T) {
bc := newTestChain(t) bc := newTestChain(t)
defer func() {
require.NoError(t, bc.Close())
}()
h1 := newBlock(1).Header() h1 := newBlock(1).Header()
h2 := newBlock(2).Header() h2 := newBlock(2).Header()
h3 := newBlock(3).Header() h3 := newBlock(3).Header()
@ -38,6 +41,9 @@ func TestAddHeaders(t *testing.T) {
func TestAddBlock(t *testing.T) { func TestAddBlock(t *testing.T) {
bc := newTestChain(t) bc := newTestChain(t)
defer func() {
require.NoError(t, bc.Close())
}()
blocks := []*Block{ blocks := []*Block{
newBlock(1), newBlock(1),
newBlock(2), newBlock(2),
@ -57,7 +63,7 @@ func TestAddBlock(t *testing.T) {
t.Log(bc.blockCache) t.Log(bc.blockCache)
if err := bc.persist(context.Background()); err != nil { if err := bc.Persist(context.Background()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -75,6 +81,9 @@ func TestAddBlock(t *testing.T) {
func TestGetHeader(t *testing.T) { func TestGetHeader(t *testing.T) {
bc := newTestChain(t) bc := newTestChain(t)
defer func() {
require.NoError(t, bc.Close())
}()
block := newBlock(1) block := newBlock(1)
err := bc.AddBlock(block) err := bc.AddBlock(block)
assert.Nil(t, err) assert.Nil(t, err)
@ -91,6 +100,9 @@ func TestGetHeader(t *testing.T) {
func TestGetBlock(t *testing.T) { func TestGetBlock(t *testing.T) {
bc := newTestChain(t) bc := newTestChain(t)
defer func() {
require.NoError(t, bc.Close())
}()
blocks := makeBlocks(100) blocks := makeBlocks(100)
for i := 0; i < len(blocks); i++ { for i := 0; i < len(blocks); i++ {
@ -111,6 +123,9 @@ func TestGetBlock(t *testing.T) {
func TestHasBlock(t *testing.T) { func TestHasBlock(t *testing.T) {
bc := newTestChain(t) bc := newTestChain(t)
defer func() {
require.NoError(t, bc.Close())
}()
blocks := makeBlocks(50) blocks := makeBlocks(50)
for i := 0; i < len(blocks); i++ { for i := 0; i < len(blocks); i++ {
@ -118,7 +133,7 @@ func TestHasBlock(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
} }
assert.Nil(t, bc.persist(context.Background())) assert.Nil(t, bc.Persist(context.Background()))
for i := 0; i < len(blocks); i++ { for i := 0; i < len(blocks); i++ {
assert.True(t, bc.HasBlock(blocks[i].Hash())) assert.True(t, bc.HasBlock(blocks[i].Hash()))
@ -131,6 +146,9 @@ func TestHasBlock(t *testing.T) {
func TestGetTransaction(t *testing.T) { func TestGetTransaction(t *testing.T) {
block := getDecodedBlock(t, 2) block := getDecodedBlock(t, 2)
bc := newTestChain(t) bc := newTestChain(t)
defer func() {
require.NoError(t, bc.Close())
}()
assert.Nil(t, bc.AddBlock(block)) assert.Nil(t, bc.AddBlock(block))
assert.Nil(t, bc.persistBlock(block)) assert.Nil(t, bc.persistBlock(block))
@ -157,5 +175,6 @@ func newTestChain(t *testing.T) *Blockchain {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
go chain.Run(context.Background())
return chain return chain
} }

View file

@ -1 +0,0 @@
MANIFEST-000025

View file

@ -1,82 +0,0 @@
=============== Nov 26, 2018 (CET) ===============
23:50:38.287098 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
23:50:38.341648 db@open opening
23:50:38.344325 version@stat F·[] S·0B[] Sc·[]
23:50:38.387172 db@janitor F·2 G·0
23:50:38.387311 db@open done T·45.546154ms
23:50:42.538000 memdb@flush N·12831 S·3MiB
23:50:42.617097 memdb@flush created L0@3 N·12831 S·2MiB "\x01\x00\x03..\xaa1\x14,v5970":"\xf0,v1"
23:50:42.617164 version@stat F·[1] S·2MiB[2MiB] Sc·[0.25]
23:50:42.639664 memdb@flush committed F·1 T·101.60375ms
23:50:42.641205 journal@remove removed @1
23:50:51.406280 memdb@flush N·16663 S·3MiB
23:50:51.479245 memdb@flush created L0@5 N·16663 S·3MiB "\x01\x00\x01..\n\xce\xf5,v22520":"\xc1,v12833"
23:50:51.479405 version@stat F·[2] S·5MiB[5MiB] Sc·[0.50]
23:50:51.501823 memdb@flush committed F·1 T·95.469736ms
23:50:51.503989 journal@remove removed @2
23:50:52.558287 table@compaction L0·2 -> L1·0 S·5MiB Q·29522
23:50:52.666973 table@build created L1@6 N·5499 S·2MiB "\x01\x00\x01..\n\xce\xf5,v22520":"\x01q`..\x0e\x88 ,v8859"
23:50:52.735582 table@build created L1@7 N·5499 S·2MiB "\x01qe..\xdfvy,v25919":"\x01\xe4\x15..~e\xd4,v20183"
23:50:52.785741 table@build created L1@8 N·3762 S·990KiB "\x01\xe4\x18..Ew@,v25246":"\xf0,v1"
23:50:52.785895 version@stat F·[0 3] S·4MiB[0B 4MiB] Sc·[0.00 0.05]
23:50:52.808457 table@compaction committed F+1 S-951KiB Ke·0 D·14734 T·250.047198ms
23:50:52.810550 table@remove removed @5
23:50:52.812603 table@remove removed @3
23:51:55.424408 memdb@flush N·25534 S·3MiB
23:51:55.607413 memdb@flush created L0@10 N·25534 S·3MiB "\x01\x00\x03..\xaa1\x14,v36636":"\xc1,v39129"
23:51:55.607695 version@stat F·[1 3] S·8MiB[3MiB 4MiB] Sc·[0.25 0.05]
23:51:55.629931 memdb@flush committed F·1 T·205.445709ms
23:51:55.633098 journal@remove removed @4
23:51:56.911824 table@compaction L0·1 -> L1·3 S·8MiB Q·55136
23:51:57.016224 table@build created L1@11 N·5568 S·2MiB "\x01\x00\x01..\n\xce\xf5,v22520":"\x01r\xb9..;\xcfa,v36196"
23:51:57.085097 table@build created L1@12 N·5559 S·2MiB "\x01r\xb9..\x8c^^,v22386":"\x01\xe6\xe0..\xcf\xf3\xdc,v34688"
23:51:57.149174 table@build created L1@13 N·16401 S·1MiB "\x01\xe6\xe0.. \xa6\xca,v50877":"\xf0,v1"
23:51:57.149348 version@stat F·[0 3] S·5MiB[0B 5MiB] Sc·[0.00 0.06]
23:51:57.171727 table@compaction committed F-1 S-2MiB Ke·0 D·12766 T·259.819169ms
23:51:57.173227 table@remove removed @10
23:51:57.174685 table@remove removed @6
23:51:57.176587 table@remove removed @7
23:51:57.177884 table@remove removed @8
=============== Dec 21, 2018 (CET) ===============
21:48:53.302835 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
21:48:53.322817 version@stat F·[0 3] S·5MiB[0B 5MiB] Sc·[0.00 0.06]
21:48:53.322841 db@open opening
21:48:53.322906 journal@recovery F·1
21:48:53.323457 journal@recovery recovering @9
21:48:53.494374 memdb@flush created L0@14 N·24266 S·2MiB "\x01\x00\x01..\n\xce\xf5,v67975":"\xc1,v61538"
21:48:53.500940 version@stat F·[1 3] S·8MiB[2MiB 5MiB] Sc·[0.25 0.06]
21:48:53.541457 db@janitor F·6 G·0
21:48:53.541547 db@open done T·218.687106ms
=============== Dec 21, 2018 (CET) ===============
21:49:14.115818 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
21:49:14.116034 version@stat F·[1 3] S·8MiB[2MiB 5MiB] Sc·[0.25 0.06]
21:49:14.116045 db@open opening
21:49:14.116086 journal@recovery F·1
21:49:14.116495 journal@recovery recovering @15
21:49:14.256619 memdb@flush created L0@17 N·700 S·149KiB "\x01\x00/..\x02&\xab,v79538":"\xc1,v79297"
21:49:14.257851 version@stat F·[2 3] S·8MiB[2MiB 5MiB] Sc·[0.50 0.06]
21:49:14.539567 db@janitor F·7 G·0
21:49:14.539642 db@open done T·423.57991ms
21:49:14.579206 table@compaction L0·2 -> L1·3 S·8MiB Q·79996
=============== Dec 21, 2018 (CET) ===============
21:54:32.133909 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
21:54:32.134127 version@stat F·[2 3] S·8MiB[2MiB 5MiB] Sc·[0.50 0.06]
21:54:32.134137 db@open opening
21:54:32.134181 journal@recovery F·1
21:54:32.134631 journal@recovery recovering @18
21:54:32.224030 memdb@flush created L0@20 N·700 S·149KiB "\x01\x00/..\x02&\xab,v80239":"\xc1,v79998"
21:54:32.225414 version@stat F·[3 3] S·8MiB[3MiB 5MiB] Sc·[0.75 0.06]
21:54:32.269397 db@janitor F·8 G·0
21:54:32.269495 db@open done T·135.337131ms
21:54:32.307525 table@compaction L0·3 -> L1·3 S·8MiB Q·80697
=============== Dec 21, 2018 (CET) ===============
21:57:38.775640 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
21:57:38.775891 version@stat F·[3 3] S·8MiB[3MiB 5MiB] Sc·[0.75 0.06]
21:57:38.775902 db@open opening
21:57:38.775949 journal@recovery F·1
21:57:38.776389 journal@recovery recovering @21
21:57:38.797001 memdb@flush created L0@23 N·700 S·149KiB "\x01\x00/..\x02&\xab,v80940":"\xc1,v80699"
21:57:38.797463 version@stat F·[4 3] S·8MiB[3MiB 5MiB] Sc·[1.00 0.06]
21:57:38.843345 db@janitor F·9 G·0
21:57:38.843446 db@open done T·67.525965ms
21:57:38.843705 table@compaction L0·4 -> L1·3 S·8MiB Q·81398

View file

@ -0,0 +1,195 @@
package rpc
import (
"context"
"net/http"
"testing"
"time"
"github.com/CityOfZion/neo-go/config"
"github.com/CityOfZion/neo-go/pkg/core"
"github.com/CityOfZion/neo-go/pkg/core/storage"
"github.com/CityOfZion/neo-go/pkg/core/transaction"
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
"github.com/CityOfZion/neo-go/pkg/network"
"github.com/CityOfZion/neo-go/pkg/rpc/result"
"github.com/CityOfZion/neo-go/pkg/rpc/wrappers"
"github.com/CityOfZion/neo-go/pkg/util"
"github.com/stretchr/testify/require"
)
// SendTXResponse struct for testing.
type SendTXResponse struct {
Jsonrpc string `json:"jsonrpc"`
Result bool `json:"result"`
ID int `json:"id"`
}
// ValidateAddrResponse struct for testing.
type ValidateAddrResponse struct {
Jsonrpc string `json:"jsonrpc"`
Result wrappers.ValidateAddressResponse `json:"result"`
ID int `json:"id"`
}
// GetPeersResponse struct for testing.
type GetPeersResponse struct {
Jsonrpc string `json:"jsonrpc"`
Result struct {
Unconnected []int `json:"unconnected"`
Connected []int `json:"connected"`
Bad []int `json:"bad"`
} `json:"result"`
ID int `json:"id"`
}
// GetVersionResponse struct for testing.
type GetVersionResponse struct {
Jsonrpc string `json:"jsonrpc"`
Result result.Version `json:"result"`
ID int `json:"id"`
}
// IntResultResponse struct for testing.
type IntResultResponse struct {
Jsonrpc string `json:"jsonrpc"`
Result int `json:"result"`
ID int `json:"id"`
}
// StringResultResponse struct for testing.
type StringResultResponse struct {
Jsonrpc string `json:"jsonrpc"`
Result string `json:"result"`
ID int `json:"id"`
}
// GetBlockResponse struct for testing.
type GetBlockResponse struct {
Jsonrpc string `json:"jsonrpc"`
Result struct {
Version int `json:"version"`
Previousblockhash string `json:"previousblockhash"`
Merkleroot string `json:"merkleroot"`
Time int `json:"time"`
Height int `json:"height"`
Nonce int `json:"nonce"`
NextConsensus string `json:"next_consensus"`
Script struct {
Invocation string `json:"invocation"`
Verification string `json:"verification"`
} `json:"script"`
Tx []struct {
Type string `json:"type"`
Version int `json:"version"`
Attributes interface{} `json:"attributes"`
Vin interface{} `json:"vin"`
Vout interface{} `json:"vout"`
Scripts interface{} `json:"scripts"`
} `json:"tx"`
Confirmations int `json:"confirmations"`
Nextblockhash string `json:"nextblockhash"`
Hash string `json:"hash"`
} `json:"result"`
ID int `json:"id"`
}
// GetAssetResponse struct for testing.
type GetAssetResponse struct {
Jsonrpc string `json:"jsonrpc"`
Result struct {
AssetID string `json:"assetID"`
AssetType int `json:"assetType"`
Name string `json:"name"`
Amount string `json:"amount"`
Available string `json:"available"`
Precision int `json:"precision"`
Fee int `json:"fee"`
Address string `json:"address"`
Owner string `json:"owner"`
Admin string `json:"admin"`
Issuer string `json:"issuer"`
Expiration int `json:"expiration"`
IsFrozen bool `json:"is_frozen"`
} `json:"result"`
ID int `json:"id"`
}
// GetAccountStateResponse struct for testing.
type GetAccountStateResponse struct {
Jsonrpc string `json:"jsonrpc"`
Result struct {
Version int `json:"version"`
ScriptHash string `json:"script_hash"`
Frozen bool `json:"frozen"`
Votes []interface{} `json:"votes"`
Balances []struct {
Asset string `json:"asset"`
Value string `json:"value"`
} `json:"balances"`
} `json:"result"`
ID int `json:"id"`
}
func initServerWithInMemoryChain(ctx context.Context, t *testing.T) (*core.Blockchain, http.HandlerFunc) {
net := config.ModeUnitTestNet
configPath := "../../config"
cfg, err := config.Load(configPath, net)
require.NoError(t, err, "could not load config")
memoryStore := storage.NewMemoryStore()
chain, err := core.NewBlockchain(memoryStore, cfg.ProtocolConfiguration)
require.NoError(t, err, "could not create chain")
go chain.Run(ctx)
initBlocks(t, chain)
serverConfig := network.NewServerConfig(cfg)
server := network.NewServer(serverConfig, chain)
rpcServer := NewServer(chain, cfg.ApplicationConfiguration.RPCPort, server)
handler := http.HandlerFunc(rpcServer.requestHandler)
return chain, handler
}
func initBlocks(t *testing.T, chain *core.Blockchain) {
blocks := makeBlocks(10)
for i := 0; i < len(blocks); i++ {
require.NoError(t, chain.AddBlock(blocks[i]))
}
require.NoError(t, chain.Persist(context.Background()))
}
func makeBlocks(n int) []*core.Block {
blocks := make([]*core.Block, n)
for i := 0; i < n; i++ {
blocks[i] = newBlock(uint32(i+1), newTX(transaction.MinerType))
}
return blocks
}
func newTX(t transaction.TXType) *transaction.Transaction {
return &transaction.Transaction{
Type: t,
}
}
func newBlock(index uint32, txs ...*transaction.Transaction) *core.Block {
b := &core.Block{
BlockBase: core.BlockBase{
Version: 0,
PrevHash: hash.Sha256([]byte("a")),
MerkleRoot: hash.Sha256([]byte("b")),
Timestamp: uint32(time.Now().UTC().Unix()),
Index: index,
ConsensusData: 1111,
NextConsensus: util.Uint160{},
Script: &transaction.Witness{
VerificationScript: []byte{0x0},
InvocationScript: []byte{0x1},
},
},
Transactions: txs,
}
return b
}

View file

@ -2,280 +2,193 @@ package rpc
import ( import (
"bytes" "bytes"
"context"
"encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"strconv"
"strings" "strings"
"testing" "testing"
"time"
"github.com/CityOfZion/neo-go/config"
"github.com/CityOfZion/neo-go/pkg/core"
"github.com/CityOfZion/neo-go/pkg/core/storage"
"github.com/CityOfZion/neo-go/pkg/network"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
type tc struct { func TestRPC(t *testing.T) {
rpcCall string ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
method string defer cancel()
expectedResult string
}
var testRPCCases = []tc{ chain, handler := initServerWithInMemoryChain(ctx, t)
{ defer func() {
rpcCall: `{"jsonrpc": "2.0", "id": 1, "method": "getassetstate", "params": ["602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"] }`, require.NoError(t, chain.Close())
method: "getassetstate_1", }()
expectedResult: `{"jsonrpc":"2.0","result":{"assetID":"0x602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7","assetType":1,"name":"NEOGas","amount":"100000000","available":"0","precision":8,"fee":0,"address":"0x0000000000000000000000000000000000000000","owner":"00","admin":"AWKECj9RD8rS8RPcpCgYVjk1DeYyHwxZm3","issuer":"AFmseVrdL9f9oyCzZefL9tG6UbvhPbdYzM","expiration":0,"is_frozen":false},"id":1}`, t.Run("getbestblockhash", func(t *testing.T) {
}, rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getbestblockhash", "params": []}`
body := doRPCCall(rpc, handler, t)
{ var res StringResultResponse
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getassetstate", "params": ["c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b"] }`, err := json.Unmarshal(bytes.TrimSpace(body), &res)
method: "getassetstate_2", assert.NoErrorf(t, err, "could not parse response: %s", body)
expectedResult: `{"jsonrpc":"2.0","result":{"assetID":"0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b","assetType":0,"name":"NEO","amount":"100000000","available":"0","precision":0,"fee":0,"address":"0x0000000000000000000000000000000000000000","owner":"00","admin":"Abf2qMs1pzQb8kYk9RuxtUb9jtRKJVuBJt","issuer":"AFmseVrdL9f9oyCzZefL9tG6UbvhPbdYzM","expiration":0,"is_frozen":false},"id":1}`, assert.Equal(t, chain.CurrentBlockHash().ReverseString(), res.Result)
},
{
rpcCall: `{"jsonrpc": "2.0", "id": 1, "method": "getassetstate", "params": ["62c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"] }`,
method: "getassetstate_3",
expectedResult: `{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid Params"},"id":1}`,
},
{
rpcCall: `{"jsonrpc": "2.0", "id": 1, "method": "getassetstate", "params": [123] }`,
method: "getassetstate_4",
expectedResult: `{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid Params"},"id":1}`,
},
{
rpcCall: `{"jsonrpc": "2.0", "id": 1, "method": "getblockhash", "params": [10] }`,
method: "getblockhash_1",
expectedResult: `{"jsonrpc":"2.0","result":"0xd69e7a1f62225a35fed91ca578f33447d93fa0fd2b2f662b957e19c38c1dab1e","id":1}`,
},
{
rpcCall: `{"jsonrpc": "2.0", "id": 1, "method": "getblockhash", "params": [-2] }`,
method: "getblockhash_2",
expectedResult: `{"jsonrpc":"2.0","error":{"code":-32603,"message":"Internal error","data":"Internal server error"},"id":1}`,
},
{
rpcCall: `{"jsonrpc": "2.0", "id": 1, "method": "getblock", "params": [10] }`,
method: "getblock",
expectedResult: `{"jsonrpc":"2.0","result":{"version":0,"previousblockhash":"0x7c5b4c8a70336bf68e8679be7c9a2a15f85c0f6d0e14389019dcc3edfab2bb4b","merkleroot":"0xc027979ad29226b7d34523b1439a64a6cf57fe3f4e823e9d3e90d43934783d26","time":1529926220,"height":10,"nonce":8313828522725096825,"next_consensus":"0xbe48d3a3f5d10013ab9ffee489706078714f1ea2","script":{"invocation":"40ac828e1c2a214e4d356fd2eccc7c7be9ef426f8e4ea67a50464e90ca4367e611c4c5247082b85a7d5ed985cfb90b9af2f1195531038f49c63fb6894b517071ea40b22b83d9457ca5c4c5bb2d8d7e95333820611d447bb171ce7b8af3b999d0a5a61c2301cdd645a33a47defd09c0f237a0afc86e9a84c2fe675d701e4015c0302240a6899296660c612736edc22f8d630927649d4ef1301868079032d80aae6cc1e21622f256497a84a71d7afeeef4c124135f611db24a0f7ab3d2a6886f15db7865","verification":"532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae"},"tx":[{"type":"MinerTransaction","version":0,"attributes":null,"vin":null,"vout":null,"scripts":null}],"confirmations":12338,"nextblockhash":"0x2b1c78633dae7ab81f64362e0828153079a17b018d779d0406491f84c27b086f","hash":"0xd69e7a1f62225a35fed91ca578f33447d93fa0fd2b2f662b957e19c38c1dab1e"},"id":1}`,
},
{
rpcCall: `{"jsonrpc": "2.0", "id": 1, "method": "getblockcount", "params": [] }`,
method: "getblockcount",
expectedResult: `{"jsonrpc":"2.0","result":12349,"id":1}`,
},
{
rpcCall: `{"jsonrpc": "2.0", "id": 1, "method": "getconnectioncount", "params": [] }`,
method: "getconnectioncount",
expectedResult: `{"jsonrpc":"2.0","result":0,"id":1}`,
},
{
rpcCall: `{"jsonrpc": "2.0", "id": 1, "method": "getbestblockhash", "params": [] }`,
method: "getbestblockhash",
expectedResult: `{"jsonrpc":"2.0","result":"877f5f2084181b85ce4726ab0a86bea6cc82cdbcb6f2eb59e6b04d27fd10929c","id":1}`,
},
{
rpcCall: `{"jsonrpc": "2.0", "id": 1, "method": "getpeers", "params": [] }`,
method: "getpeers",
expectedResult: `{"jsonrpc":"2.0","result":{"unconnected":[],"connected":[],"bad":[]},"id":1}`},
// Good case, valid transaction ((param[1]=1 -> verbose = 1))
{
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["f999c36145a41306c846ea80290416143e8e856559818065be3f4e143c60e43a", 1] }`,
method: "getrawtransaction_1",
expectedResult: `{"jsonrpc":"2.0","result":{"type":"ContractTransaction","version":0,"attributes":[{"data":"23ba2703c53263e8d6e522dc32203339dcd8eee9","usage":"Script"}],"vin":[{"txid":"0x539084697cc220916cb5b16d2805945ec9f267aa004b6688fbf15e116c846aff","vout":0}],"vout":[{"address":"AXpNr3SDfLXbPHNdqxYeHK5cYpKMHZxMZ9","asset":"0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b","n":0,"value":"10000"},{"address":"AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y","asset":"0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b","n":1,"value":"99990000"}],"scripts":[{"invocation":"40a88bd1fcfba334b06da0ce1a679f80711895dade50352074e79e438e142dc95528d04a00c579398cb96c7301428669a09286ae790459e05e907c61ab8a1191c6","verification":"21031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4aac"}],"txid":"0xf999c36145a41306c846ea80290416143e8e856559818065be3f4e143c60e43a","size":283,"sys_fee":"0","net_fee":"0","blockhash":"0x6088bf9d3b55c67184f60b00d2e380228f713b4028b24c1719796dcd2006e417","confirmations":2902,"blocktime":1533756500},"id":1}`,
},
// Good case, valid transaction (param[1]=3 -> verbose = 1. Following the C# any number different from 0 is interpreted as verbose = 1)
{
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["f999c36145a41306c846ea80290416143e8e856559818065be3f4e143c60e43a", 3] }`,
method: "getrawtransaction_2",
expectedResult: `{"jsonrpc":"2.0","result":{"type":"ContractTransaction","version":0,"attributes":[{"data":"23ba2703c53263e8d6e522dc32203339dcd8eee9","usage":"Script"}],"vin":[{"txid":"0x539084697cc220916cb5b16d2805945ec9f267aa004b6688fbf15e116c846aff","vout":0}],"vout":[{"address":"AXpNr3SDfLXbPHNdqxYeHK5cYpKMHZxMZ9","asset":"0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b","n":0,"value":"10000"},{"address":"AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y","asset":"0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b","n":1,"value":"99990000"}],"scripts":[{"invocation":"40a88bd1fcfba334b06da0ce1a679f80711895dade50352074e79e438e142dc95528d04a00c579398cb96c7301428669a09286ae790459e05e907c61ab8a1191c6","verification":"21031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4aac"}],"txid":"0xf999c36145a41306c846ea80290416143e8e856559818065be3f4e143c60e43a","size":283,"sys_fee":"0","net_fee":"0","blockhash":"0x6088bf9d3b55c67184f60b00d2e380228f713b4028b24c1719796dcd2006e417","confirmations":2902,"blocktime":1533756500},"id":1}`,
},
// Good case, valid transaction (param[1]="dads" -> verbose = 1. Following the C# any string different from "0", "false" is interpreted as verbose = 1)
{
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["f999c36145a41306c846ea80290416143e8e856559818065be3f4e143c60e43a", "dads"] }`,
method: "getrawtransaction_3",
expectedResult: `{"jsonrpc":"2.0","result":{"type":"ContractTransaction","version":0,"attributes":[{"data":"23ba2703c53263e8d6e522dc32203339dcd8eee9","usage":"Script"}],"vin":[{"txid":"0x539084697cc220916cb5b16d2805945ec9f267aa004b6688fbf15e116c846aff","vout":0}],"vout":[{"address":"AXpNr3SDfLXbPHNdqxYeHK5cYpKMHZxMZ9","asset":"0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b","n":0,"value":"10000"},{"address":"AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y","asset":"0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b","n":1,"value":"99990000"}],"scripts":[{"invocation":"40a88bd1fcfba334b06da0ce1a679f80711895dade50352074e79e438e142dc95528d04a00c579398cb96c7301428669a09286ae790459e05e907c61ab8a1191c6","verification":"21031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4aac"}],"txid":"0xf999c36145a41306c846ea80290416143e8e856559818065be3f4e143c60e43a","size":283,"sys_fee":"0","net_fee":"0","blockhash":"0x6088bf9d3b55c67184f60b00d2e380228f713b4028b24c1719796dcd2006e417","confirmations":2902,"blocktime":1533756500},"id":1}`,
},
// Bad case, invalid transaction
{
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["45a41306c846ea80290416143e8e856559818065be3f4e143c60e43a", 1] }`,
method: "getrawtransaction_4",
expectedResult: `{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid Params"},"id":1}`,
},
// Good case, valid transaction
{
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["f999c36145a41306c846ea80290416143e8e856559818065be3f4e143c60e43a"] }`,
method: "getrawtransaction_5",
expectedResult: `{"jsonrpc":"2.0","result":"8000012023ba2703c53263e8d6e522dc32203339dcd8eee901ff6a846c115ef1fb88664b00aa67f2c95e9405286db1b56c9120c27c698490530000029b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc50010a5d4e8000000affb37f5fdb9c6fec48d9f0eee85af82950f9b4a9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500f01b9b0986230023ba2703c53263e8d6e522dc32203339dcd8eee9014140a88bd1fcfba334b06da0ce1a679f80711895dade50352074e79e438e142dc95528d04a00c579398cb96c7301428669a09286ae790459e05e907c61ab8a1191c62321031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4aac","id":1}`,
},
// Good case, valid transaction (param[1]= 0 -> verbose = 0)
{
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["f999c36145a41306c846ea80290416143e8e856559818065be3f4e143c60e43a", 0] }`,
method: "getrawtransaction_6",
expectedResult: `{"jsonrpc":"2.0","result":"8000012023ba2703c53263e8d6e522dc32203339dcd8eee901ff6a846c115ef1fb88664b00aa67f2c95e9405286db1b56c9120c27c698490530000029b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc50010a5d4e8000000affb37f5fdb9c6fec48d9f0eee85af82950f9b4a9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500f01b9b0986230023ba2703c53263e8d6e522dc32203339dcd8eee9014140a88bd1fcfba334b06da0ce1a679f80711895dade50352074e79e438e142dc95528d04a00c579398cb96c7301428669a09286ae790459e05e907c61ab8a1191c62321031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4aac","id":1}`,
},
// Good case, valid transaction (param[1]="false" -> verbose = 0)
{
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["f999c36145a41306c846ea80290416143e8e856559818065be3f4e143c60e43a", "false"] }`,
method: "getrawtransaction_6_a",
expectedResult: `{"jsonrpc":"2.0","result":"8000012023ba2703c53263e8d6e522dc32203339dcd8eee901ff6a846c115ef1fb88664b00aa67f2c95e9405286db1b56c9120c27c698490530000029b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc50010a5d4e8000000affb37f5fdb9c6fec48d9f0eee85af82950f9b4a9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500f01b9b0986230023ba2703c53263e8d6e522dc32203339dcd8eee9014140a88bd1fcfba334b06da0ce1a679f80711895dade50352074e79e438e142dc95528d04a00c579398cb96c7301428669a09286ae790459e05e907c61ab8a1191c62321031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4aac","id":1}`,
},
// Good case, valid transaction (param[1]=false -> verbose = 0)
{
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["f999c36145a41306c846ea80290416143e8e856559818065be3f4e143c60e43a", false] }`,
method: "getrawtransaction_6_b",
expectedResult: `{"jsonrpc":"2.0","result":"8000012023ba2703c53263e8d6e522dc32203339dcd8eee901ff6a846c115ef1fb88664b00aa67f2c95e9405286db1b56c9120c27c698490530000029b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc50010a5d4e8000000affb37f5fdb9c6fec48d9f0eee85af82950f9b4a9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500f01b9b0986230023ba2703c53263e8d6e522dc32203339dcd8eee9014140a88bd1fcfba334b06da0ce1a679f80711895dade50352074e79e438e142dc95528d04a00c579398cb96c7301428669a09286ae790459e05e907c61ab8a1191c62321031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4aac","id":1}`,
},
// Good case, valid transaction (param[1]="0" -> verbose = 0)
{
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["f999c36145a41306c846ea80290416143e8e856559818065be3f4e143c60e43a", "0"] }`,
method: "getrawtransaction_6_c",
expectedResult: `{"jsonrpc":"2.0","result":"8000012023ba2703c53263e8d6e522dc32203339dcd8eee901ff6a846c115ef1fb88664b00aa67f2c95e9405286db1b56c9120c27c698490530000029b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc50010a5d4e8000000affb37f5fdb9c6fec48d9f0eee85af82950f9b4a9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500f01b9b0986230023ba2703c53263e8d6e522dc32203339dcd8eee9014140a88bd1fcfba334b06da0ce1a679f80711895dade50352074e79e438e142dc95528d04a00c579398cb96c7301428669a09286ae790459e05e907c61ab8a1191c62321031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4aac","id":1}`,
},
// Bad case, param at index 0 not a string
{
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": [123, 0] }`,
method: "getrawtransaction_7",
expectedResult: `{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid Params"},"id":1}`,
},
// Good case, valid address
{
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getaccountstate", "params": ["AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"] }`,
method: "getaccountstate_1",
expectedResult: `{"jsonrpc":"2.0","result":{"version":0,"script_hash":"0xe9eed8dc39332032dc22e5d6e86332c50327ba23","frozen":false,"votes":[],"balances":[{"asset":"0x602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7","value":"72099.9996"},{"asset":"0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b","value":"99989900"}]},"id":1}`,
},
// Bad case, invalid address
{
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getaccountstate", "params": ["AK2nJJpJr6o664CWJKi1QRXjqeic2zR"] }`,
method: "getaccountstate_2",
expectedResult: `{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid Params"},"id":1}`,
},
// Bad case, not string
{
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getaccountstate", "params": [123] }`,
method: "getaccountstate_3",
expectedResult: `{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid Params"},"id":1}`,
},
// Bad case, empty params
{
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getaccountstate", "params": [] }`,
method: "getaccountstate_4",
expectedResult: `{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid Params"},"id":1}`,
},
// Good case, valid address
{
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "validateaddress", "params": ["AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i"] }`,
method: "validateaddress_1",
expectedResult: `{"jsonrpc":"2.0","result":{"address":"AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i","isvalid":true},"id":1}`,
},
// Bad case, invalid address
{
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "validateaddress", "params": ["152f1muMCNa7goXYhYAQC61hxEgGacmncB"] }`,
method: "validateaddress_2",
expectedResult: `{"jsonrpc":"2.0","result":{"address":"152f1muMCNa7goXYhYAQC61hxEgGacmncB","isvalid":false},"id":1}`,
},
// Bad case, not string
{
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "validateaddress", "params": [1] }`,
method: "validateaddress_3",
expectedResult: `{"jsonrpc":"2.0","result":{"address":1,"isvalid":false},"id":1}`,
},
// Bad case, empty params
{
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "validateaddress", "params": [] }`,
method: "validateaddress_4",
expectedResult: `{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid Params"},"id":1}`,
},
// Good case
{
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "sendrawtransaction", "params": ["80000190274d792072617720636f6e7472616374207472616e73616374696f6e206465736372697074696f6e01949354ea0a8b57dfee1e257a1aedd1e0eea2e5837de145e8da9c0f101bfccc8e0100029b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500a3e11100000000ea610aa6db39bd8c8556c9569d94b5e5a5d0ad199b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc5004f2418010000001cc9c05cefffe6cdd7b182816a9152ec218d2ec0014140dbd3cddac5cb2bd9bf6d93701f1a6f1c9dbe2d1b480c54628bbb2a4d536158c747a6af82698edf9f8af1cac3850bcb772bd9c8e4ac38f80704751cc4e0bd0e67232103cbb45da6072c14761c9da545749d9cfd863f860c351066d16df480602a2024c6ac"] }`,
method: "sendrawtransaction_1",
expectedResult: `{"jsonrpc":"2.0","result":true,"id":1}`,
},
{
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "sendrawtransaction", "params": ["d1001b00046e616d6567d3d8602814a429a91afdbaa3914884a1c90c733101201cc9c05cefffe6cdd7b182816a9152ec218d2ec000000141403387ef7940a5764259621e655b3c621a6aafd869a611ad64adcc364d8dd1edf84e00a7f8b11b630a377eaef02791d1c289d711c08b7ad04ff0d6c9caca22cfe6232103cbb45da6072c14761c9da545749d9cfd863f860c351066d16df480602a2024c6ac"] }`,
method: "sendrawtransaction_2",
expectedResult: `{"jsonrpc":"2.0","result":true,"id":1}`,
},
// Bad case, incorrect raw transaction
{
rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "sendrawtransaction", "params": ["0274d792072617720636f6e7472616374207472616e73616374696f6e206465736372697074696f6e01949354ea0a8b57dfee1e257a1aedd1e0eea2e5837de145e8da9c0f101bfccc8e0100029b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500a3e11100000000ea610aa6db39bd8c8556c9569d94b5e5a5d0ad199b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc5004f2418010000001cc9c05cefffe6cdd7b182816a9152ec218d2ec0014140dbd3cddac5cb2bd9bf6d93701f1a6f1c9dbe2d1b480c54628bbb2a4d536158c747a6af82698edf9f8af1cac3850bcb772bd9c8e4ac38f80704751cc4e0bd0e67232103cbb45da6072c14761c9da545749d9cfd863f860c351066d16df480602a2024c6ac"] }`,
method: "sendrawtransaction_1",
expectedResult: `{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid Params"},"id":1}`,
},
}
func TestHandler(t *testing.T) {
// setup rpcServer server
net := config.ModeUnitTestNet
configPath := "../../config"
cfg, err := config.Load(configPath, net)
require.NoError(t, err, "could not load config")
store, err := storage.NewLevelDBStore(cfg.ApplicationConfiguration.DBConfiguration.LevelDBOptions)
assert.Nil(t, err)
chain, err := core.NewBlockchain(store, cfg.ProtocolConfiguration)
require.NoError(t, err, "could not create levelDB chain")
serverConfig := network.NewServerConfig(cfg)
server := network.NewServer(serverConfig, chain)
rpcServer := NewServer(chain, cfg.ApplicationConfiguration.RPCPort, server)
// setup handler
handler := http.HandlerFunc(rpcServer.requestHandler)
testRPCCases = append(testRPCCases, tc{
rpcCall: `{"jsonrpc": "2.0", "id": 1, "method": "getversion", "params": [] }`,
method: "getversion",
expectedResult: fmt.Sprintf(`{"jsonrpc":"2.0","result":{"port":20333,"nonce":%s,"useragent":"/NEO-GO:/"},"id":1}`, strconv.FormatUint(uint64(server.ID()), 10)),
}) })
for _, tc := range testRPCCases { t.Run("getblock", func(t *testing.T) {
t.Run(fmt.Sprintf("method: %s, rpc call: %s", tc.method, tc.rpcCall), func(t *testing.T) { rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getblock", "params": [1]}`
body := doRPCCall(rpc, handler, t)
var res GetBlockResponse
err := json.Unmarshal(bytes.TrimSpace(body), &res)
assert.NoErrorf(t, err, "could not parse response: %s", body)
block, err := chain.GetBlock(chain.GetHeaderHash(1))
assert.NoErrorf(t, err, "could not get block")
expectedHash := "0x" + block.Hash().ReverseString()
assert.Equal(t, expectedHash, res.Result.Hash)
})
req := httptest.NewRequest("POST", "http://0.0.0.0:20333/", strings.NewReader(tc.rpcCall)) t.Run("getblockcount", func(t *testing.T) {
req.Header.Set("Content-Type", "application/json") rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getblockcount", "params": []}`
body := doRPCCall(rpc, handler, t)
var res IntResultResponse
err := json.Unmarshal(bytes.TrimSpace(body), &res)
assert.NoErrorf(t, err, "could not parse response: %s", body)
assert.Equal(t, chain.BlockHeight(), uint32(res.Result))
})
w := httptest.NewRecorder() t.Run("getblockhash", func(t *testing.T) {
handler(w, req) rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getblockhash", "params": [1]}`
resp := w.Result() body := doRPCCall(rpc, handler, t)
body, err := ioutil.ReadAll(resp.Body) var res StringResultResponse
assert.NoErrorf(t, err, "could not read response from the request: %s", tc.rpcCall) err := json.Unmarshal(bytes.TrimSpace(body), &res)
assert.JSONEq(t, tc.expectedResult, string(bytes.TrimSpace(body))) assert.NoErrorf(t, err, "could not parse response: %s", body)
}) block, err := chain.GetBlock(chain.GetHeaderHash(1))
assert.NoErrorf(t, err, "could not get block")
expectedHash := "0x" + block.Hash().ReverseString()
assert.Equal(t, expectedHash, res.Result)
})
} t.Run("getconnectioncount", func(t *testing.T) {
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getconnectioncount", "params": []}`
body := doRPCCall(rpc, handler, t)
var res IntResultResponse
err := json.Unmarshal(bytes.TrimSpace(body), &res)
assert.NoErrorf(t, err, "could not parse response: %s", body)
assert.Equal(t, 0, res.Result)
})
t.Run("getversion", func(t *testing.T) {
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getversion", "params": []}`
body := doRPCCall(rpc, handler, t)
var res GetVersionResponse
err := json.Unmarshal(bytes.TrimSpace(body), &res)
assert.NoErrorf(t, err, "could not parse response: %s", body)
assert.Equal(t, "/NEO-GO:/", res.Result.UserAgent)
})
t.Run("getpeers", func(t *testing.T) {
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getpeers", "params": []}`
body := doRPCCall(rpc, handler, t)
var res GetPeersResponse
err := json.Unmarshal(bytes.TrimSpace(body), &res)
assert.NoErrorf(t, err, "could not parse response: %s", body)
assert.Equal(t, []int{}, res.Result.Bad)
assert.Equal(t, []int{}, res.Result.Unconnected)
assert.Equal(t, []int{}, res.Result.Connected)
})
t.Run("validateaddress_positive", func(t *testing.T) {
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "validateaddress", "params": ["AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i"]}`
body := doRPCCall(rpc, handler, t)
var res ValidateAddrResponse
err := json.Unmarshal(bytes.TrimSpace(body), &res)
assert.NoErrorf(t, err, "could not parse response: %s", body)
assert.Equal(t, true, res.Result.IsValid)
})
t.Run("validateaddress_negative", func(t *testing.T) {
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "validateaddress", "params": [1]}`
body := doRPCCall(rpc, handler, t)
var res ValidateAddrResponse
err := json.Unmarshal(bytes.TrimSpace(body), &res)
assert.NoErrorf(t, err, "could not parse response: %s", body)
assert.Equal(t, false, res.Result.IsValid)
})
t.Run("getassetstate_positive", func(t *testing.T) {
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getassetstate", "params": ["602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"]}`
body := doRPCCall(rpc, handler, t)
var res GetAssetResponse
err := json.Unmarshal(bytes.TrimSpace(body), &res)
assert.NoErrorf(t, err, "could not parse response: %s", body)
assert.Equal(t, "00", res.Result.Owner)
assert.Equal(t, "AWKECj9RD8rS8RPcpCgYVjk1DeYyHwxZm3", res.Result.Admin)
})
t.Run("getassetstate_negative", func(t *testing.T) {
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getassetstate", "params": ["602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de2"]}`
body := doRPCCall(rpc, handler, t)
var res StringResultResponse
err := json.Unmarshal(bytes.TrimSpace(body), &res)
assert.NoErrorf(t, err, "could not parse response: %s", body)
assert.Equal(t, "Invalid assetid", res.Result)
})
t.Run("getaccountstate_positive", func(t *testing.T) {
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getaccountstate", "params": ["AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU"]}`
body := doRPCCall(rpc, handler, t)
var res GetAccountStateResponse
err := json.Unmarshal(bytes.TrimSpace(body), &res)
assert.NoErrorf(t, err, "could not parse response: %s", body)
assert.Equal(t, 1, len(res.Result.Balances))
assert.Equal(t, false, res.Result.Frozen)
})
t.Run("getaccountstate_negative", func(t *testing.T) {
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getaccountstate", "params": ["AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"]}`
body := doRPCCall(rpc, handler, t)
var res StringResultResponse
err := json.Unmarshal(bytes.TrimSpace(body), &res)
assert.NoErrorf(t, err, "could not parse response: %s", body)
assert.Equal(t, "Invalid public account address", res.Result)
})
t.Run("getrawtransaction", func(t *testing.T) {
block, _ := chain.GetBlock(chain.GetHeaderHash(0))
TXHash := block.Transactions[1].Hash()
rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s"]}"`, TXHash.ReverseString())
body := doRPCCall(rpc, handler, t)
var res StringResultResponse
err := json.Unmarshal(bytes.TrimSpace(body), &res)
assert.NoErrorf(t, err, "could not parse response: %s", body)
assert.Equal(t, "400000455b7b226c616e67223a227a682d434e222c226e616d65223a22e5b08fe89a81e882a1227d2c7b226c616e67223a22656e222c226e616d65223a22416e745368617265227d5d0000c16ff28623000000da1745e9b549bd0bfa1a569971c77eba30cd5a4b00000000", res.Result)
})
t.Run("sendrawtransaction_positive", func(t *testing.T) {
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "sendrawtransaction", "params": ["d1001b00046e616d6567d3d8602814a429a91afdbaa3914884a1c90c733101201cc9c05cefffe6cdd7b182816a9152ec218d2ec000000141403387ef7940a5764259621e655b3c621a6aafd869a611ad64adcc364d8dd1edf84e00a7f8b11b630a377eaef02791d1c289d711c08b7ad04ff0d6c9caca22cfe6232103cbb45da6072c14761c9da545749d9cfd863f860c351066d16df480602a2024c6ac"]}`
body := doRPCCall(rpc, handler, t)
var res SendTXResponse
err := json.Unmarshal(bytes.TrimSpace(body), &res)
assert.NoErrorf(t, err, "could not parse response: %s", body)
assert.Equal(t, true, res.Result)
})
t.Run("sendrawtransaction_negative", func(t *testing.T) {
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "sendrawtransaction", "params": ["0274d792072617720636f6e7472616374207472616e73616374696f6e206465736372697074696f6e01949354ea0a8b57dfee1e257a1aedd1e0eea2e5837de145e8da9c0f101bfccc8e0100029b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500a3e11100000000ea610aa6db39bd8c8556c9569d94b5e5a5d0ad199b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc5004f2418010000001cc9c05cefffe6cdd7b182816a9152ec218d2ec0014140dbd3cddac5cb2bd9bf6d93701f1a6f1c9dbe2d1b480c54628bbb2a4d536158c747a6af82698edf9f8af1cac3850bcb772bd9c8e4ac38f80704751cc4e0bd0e67232103cbb45da6072c14761c9da545749d9cfd863f860c351066d16df480602a2024c6ac"]}`
body := doRPCCall(rpc, handler, t)
var res SendTXResponse
err := json.Unmarshal(bytes.TrimSpace(body), &res)
assert.NoErrorf(t, err, "could not parse response: %s", body)
assert.Equal(t, false, res.Result)
})
}
func doRPCCall(rpcCall string, handler http.HandlerFunc, t *testing.T) []byte {
req := httptest.NewRequest("POST", "http://0.0.0.0:20333/", strings.NewReader(rpcCall))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
handler(w, req)
resp := w.Result()
body, err := ioutil.ReadAll(resp.Body)
assert.NoErrorf(t, err, "could not read response from the request: %s", rpcCall)
return body
} }