diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index ff1cd3917..4fc630860 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -37,7 +37,7 @@ type Blockchain struct { // Current index/height of the highest block. // Read access should always be called by BlockHeight(). - // Write access should only happen in persist(). + // Write access should only happen in Persist(). blockHeight uint32 // Number of headers stored in the chain file. @@ -167,7 +167,10 @@ func (bc *Blockchain) Run(ctx context.Context) { op(bc.headerList) bc.headersOpDone <- struct{}{} case <-persistTimer.C: - go bc.persist(ctx) + go func() { + err := bc.Persist(ctx) + log.Warnf("failed to persist blockchain: %s", err) + }() persistTimer.Reset(persistInterval) } } @@ -389,7 +392,8 @@ func (bc *Blockchain) persistBlock(block *Block) error { 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 ( start = time.Now() persisted = 0 diff --git a/pkg/core/blockchain_test.go b/pkg/core/blockchain_test.go index ca96b458e..018bb3096 100644 --- a/pkg/core/blockchain_test.go +++ b/pkg/core/blockchain_test.go @@ -13,6 +13,9 @@ import ( func TestAddHeaders(t *testing.T) { bc := newTestChain(t) + defer func() { + require.NoError(t, bc.Close()) + }() h1 := newBlock(1).Header() h2 := newBlock(2).Header() h3 := newBlock(3).Header() @@ -38,6 +41,9 @@ func TestAddHeaders(t *testing.T) { func TestAddBlock(t *testing.T) { bc := newTestChain(t) + defer func() { + require.NoError(t, bc.Close()) + }() blocks := []*Block{ newBlock(1), newBlock(2), @@ -57,7 +63,7 @@ func TestAddBlock(t *testing.T) { t.Log(bc.blockCache) - if err := bc.persist(context.Background()); err != nil { + if err := bc.Persist(context.Background()); err != nil { t.Fatal(err) } @@ -75,6 +81,9 @@ func TestAddBlock(t *testing.T) { func TestGetHeader(t *testing.T) { bc := newTestChain(t) + defer func() { + require.NoError(t, bc.Close()) + }() block := newBlock(1) err := bc.AddBlock(block) assert.Nil(t, err) @@ -91,6 +100,9 @@ func TestGetHeader(t *testing.T) { func TestGetBlock(t *testing.T) { bc := newTestChain(t) + defer func() { + require.NoError(t, bc.Close()) + }() blocks := makeBlocks(100) for i := 0; i < len(blocks); i++ { @@ -111,6 +123,9 @@ func TestGetBlock(t *testing.T) { func TestHasBlock(t *testing.T) { bc := newTestChain(t) + defer func() { + require.NoError(t, bc.Close()) + }() blocks := makeBlocks(50) for i := 0; i < len(blocks); i++ { @@ -118,7 +133,7 @@ func TestHasBlock(t *testing.T) { t.Fatal(err) } } - assert.Nil(t, bc.persist(context.Background())) + assert.Nil(t, bc.Persist(context.Background())) for i := 0; i < len(blocks); i++ { assert.True(t, bc.HasBlock(blocks[i].Hash())) @@ -131,6 +146,9 @@ func TestHasBlock(t *testing.T) { func TestGetTransaction(t *testing.T) { block := getDecodedBlock(t, 2) bc := newTestChain(t) + defer func() { + require.NoError(t, bc.Close()) + }() assert.Nil(t, bc.AddBlock(block)) assert.Nil(t, bc.persistBlock(block)) @@ -157,5 +175,6 @@ func newTestChain(t *testing.T) *Blockchain { if err != nil { t.Fatal(err) } + go chain.Run(context.Background()) return chain } diff --git a/pkg/core/storage/memory_store.go b/pkg/core/storage/memory_store.go index 2e155b84c..4c9d2f33f 100644 --- a/pkg/core/storage/memory_store.go +++ b/pkg/core/storage/memory_store.go @@ -2,6 +2,7 @@ package storage import ( "encoding/hex" + "strings" "sync" ) @@ -67,6 +68,12 @@ func (s *MemoryStore) PutBatch(batch Batch) error { // Seek implements the Store interface. func (s *MemoryStore) Seek(key []byte, f func(k, v []byte)) { + for k, v := range s.mem { + if strings.Contains(k, hex.EncodeToString(key)) { + decodeString, _ := hex.DecodeString(k) + f(decodeString, v) + } + } } // Batch implements the Batch interface and returns a compatible Batch. diff --git a/pkg/core/storage/memory_store_test.go b/pkg/core/storage/memory_store_test.go index 6d44bb433..1ad760e3e 100644 --- a/pkg/core/storage/memory_store_test.go +++ b/pkg/core/storage/memory_store_test.go @@ -59,3 +59,19 @@ func TestPutBatch(t *testing.T) { assert.Equal(t, value, newVal) require.NoError(t, s.Close()) } + +func TestMemoryStore_Seek(t *testing.T) { + var ( + s = NewMemoryStore() + key = []byte("sparse") + value = []byte("rocks") + ) + + if err := s.Put(key, value); err != nil { + t.Fatal(err) + } + + s.Seek(key, func(k, v []byte) { + assert.Equal(t, value, v) + }) +} diff --git a/pkg/rpc/chains/unit_testnet/000011.ldb b/pkg/rpc/chains/unit_testnet/000011.ldb deleted file mode 100644 index f565db8ac..000000000 Binary files a/pkg/rpc/chains/unit_testnet/000011.ldb and /dev/null differ diff --git a/pkg/rpc/chains/unit_testnet/000012.ldb b/pkg/rpc/chains/unit_testnet/000012.ldb deleted file mode 100644 index af43f28a3..000000000 Binary files a/pkg/rpc/chains/unit_testnet/000012.ldb and /dev/null differ diff --git a/pkg/rpc/chains/unit_testnet/000013.ldb b/pkg/rpc/chains/unit_testnet/000013.ldb deleted file mode 100644 index 579b7110a..000000000 Binary files a/pkg/rpc/chains/unit_testnet/000013.ldb and /dev/null differ diff --git a/pkg/rpc/chains/unit_testnet/000014.ldb b/pkg/rpc/chains/unit_testnet/000014.ldb deleted file mode 100644 index ac132afb9..000000000 Binary files a/pkg/rpc/chains/unit_testnet/000014.ldb and /dev/null differ diff --git a/pkg/rpc/chains/unit_testnet/000017.ldb b/pkg/rpc/chains/unit_testnet/000017.ldb deleted file mode 100644 index ca2bbbda1..000000000 Binary files a/pkg/rpc/chains/unit_testnet/000017.ldb and /dev/null differ diff --git a/pkg/rpc/chains/unit_testnet/000020.ldb b/pkg/rpc/chains/unit_testnet/000020.ldb deleted file mode 100644 index 514153918..000000000 Binary files a/pkg/rpc/chains/unit_testnet/000020.ldb and /dev/null differ diff --git a/pkg/rpc/chains/unit_testnet/000023.ldb b/pkg/rpc/chains/unit_testnet/000023.ldb deleted file mode 100644 index acbcb7708..000000000 Binary files a/pkg/rpc/chains/unit_testnet/000023.ldb and /dev/null differ diff --git a/pkg/rpc/chains/unit_testnet/000024.log b/pkg/rpc/chains/unit_testnet/000024.log deleted file mode 100644 index 0da494651..000000000 Binary files a/pkg/rpc/chains/unit_testnet/000024.log and /dev/null differ diff --git a/pkg/rpc/chains/unit_testnet/000026.ldb b/pkg/rpc/chains/unit_testnet/000026.ldb deleted file mode 100644 index 6f7dfb64e..000000000 Binary files a/pkg/rpc/chains/unit_testnet/000026.ldb and /dev/null differ diff --git a/pkg/rpc/chains/unit_testnet/CURRENT b/pkg/rpc/chains/unit_testnet/CURRENT deleted file mode 100644 index f62209062..000000000 --- a/pkg/rpc/chains/unit_testnet/CURRENT +++ /dev/null @@ -1 +0,0 @@ -MANIFEST-000025 diff --git a/pkg/rpc/chains/unit_testnet/LOCK b/pkg/rpc/chains/unit_testnet/LOCK deleted file mode 100644 index e69de29bb..000000000 diff --git a/pkg/rpc/chains/unit_testnet/LOG b/pkg/rpc/chains/unit_testnet/LOG deleted file mode 100644 index 368ceb5bf..000000000 --- a/pkg/rpc/chains/unit_testnet/LOG +++ /dev/null @@ -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 diff --git a/pkg/rpc/chains/unit_testnet/MANIFEST-000025 b/pkg/rpc/chains/unit_testnet/MANIFEST-000025 deleted file mode 100644 index ed6d6440d..000000000 Binary files a/pkg/rpc/chains/unit_testnet/MANIFEST-000025 and /dev/null differ diff --git a/pkg/rpc/server_helper_test.go b/pkg/rpc/server_helper_test.go new file mode 100644 index 000000000..718ff6cf9 --- /dev/null +++ b/pkg/rpc/server_helper_test.go @@ -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 +} diff --git a/pkg/rpc/server_test.go b/pkg/rpc/server_test.go index 575dafde2..40796bdf9 100644 --- a/pkg/rpc/server_test.go +++ b/pkg/rpc/server_test.go @@ -2,280 +2,193 @@ package rpc import ( "bytes" + "context" + "encoding/json" "fmt" "io/ioutil" "net/http" "net/http/httptest" - "strconv" "strings" "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/require" ) -type tc struct { - rpcCall string - method string - expectedResult string -} +func TestRPC(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) + defer cancel() -var testRPCCases = []tc{ + chain, handler := initServerWithInMemoryChain(ctx, t) - { - rpcCall: `{"jsonrpc": "2.0", "id": 1, "method": "getassetstate", "params": ["602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"] }`, - 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}`, - }, - - { - rpcCall: `{ "jsonrpc": "2.0", "id": 1, "method": "getassetstate", "params": ["c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b"] }`, - method: "getassetstate_2", - 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}`, - }, - - { - 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)), + defer func() { + require.NoError(t, chain.Close()) + }() + t.Run("getbestblockhash", func(t *testing.T) { + rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getbestblockhash", "params": []}` + 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, chain.CurrentBlockHash().ReverseString(), res.Result) }) - for _, tc := range testRPCCases { - t.Run(fmt.Sprintf("method: %s, rpc call: %s", tc.method, tc.rpcCall), func(t *testing.T) { + t.Run("getblock", 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)) - req.Header.Set("Content-Type", "application/json") + t.Run("getblockcount", func(t *testing.T) { + 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() - handler(w, req) - resp := w.Result() - body, err := ioutil.ReadAll(resp.Body) - assert.NoErrorf(t, err, "could not read response from the request: %s", tc.rpcCall) - assert.JSONEq(t, tc.expectedResult, string(bytes.TrimSpace(body))) - }) + t.Run("getblockhash", func(t *testing.T) { + rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getblockhash", "params": [1]}` + 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) + 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 }