920d7c610c
Commit 578ac414d4
was wrong in that it saved
only a part of the block, so depending on how you use blockchain, you may
still see that the block was not really processed properly. To really fix it
this commit introduces intermediate storage layer in form of memStore, which
actually is a MemoryStore that supports full Store API (thus easily fitting
into the existing code) and one extension that allows it to flush its data to
some other Store.
It also changes AddBlock() semantics in that it only accepts now successive
blocks, but when it does it guarantees that they're properly added into the
Blockchain and can be referred to in any way. Pending block queing is now
moved into the server (see 8c0c055ac657813fe3ed10257bce199e9527d5ed).
So the only thing done with persist() now is just a move from memStore to
Store which probably should've always been the case (notice also that
previously headers and some other metadata was written into the Store
bypassing caching/batching mechanism thus leading to some inefficiency).
215 lines
9.2 KiB
Go
215 lines
9.2 KiB
Go
package rpc
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestRPC(t *testing.T) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
|
|
defer cancel()
|
|
|
|
chain, handler := initServerWithInMemoryChain(ctx, t)
|
|
|
|
t.Run("getbestblockhash", func(t *testing.T) {
|
|
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getbestblockhash", "params": []}`
|
|
body := doRPCCall(rpc, handler, t)
|
|
checkErrResponse(t, body, false)
|
|
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)
|
|
})
|
|
|
|
t.Run("getblock", func(t *testing.T) {
|
|
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getblock", "params": [1]}`
|
|
body := doRPCCall(rpc, handler, t)
|
|
checkErrResponse(t, body, false)
|
|
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)
|
|
})
|
|
|
|
t.Run("getblockcount", func(t *testing.T) {
|
|
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getblockcount", "params": []}`
|
|
body := doRPCCall(rpc, handler, t)
|
|
checkErrResponse(t, body, false)
|
|
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))
|
|
})
|
|
|
|
t.Run("getblockhash", func(t *testing.T) {
|
|
rpc := `{"jsonrpc": "2.0", "id": 1, "method": "getblockhash", "params": [1]}`
|
|
body := doRPCCall(rpc, handler, t)
|
|
checkErrResponse(t, body, false)
|
|
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)
|
|
checkErrResponse(t, body, false)
|
|
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)
|
|
checkErrResponse(t, body, false)
|
|
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)
|
|
checkErrResponse(t, body, false)
|
|
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)
|
|
checkErrResponse(t, body, false)
|
|
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)
|
|
checkErrResponse(t, body, false)
|
|
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)
|
|
checkErrResponse(t, body, false)
|
|
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)
|
|
checkErrResponse(t, body, false)
|
|
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)
|
|
checkErrResponse(t, body, false)
|
|
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)
|
|
checkErrResponse(t, body, false)
|
|
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)
|
|
checkErrResponse(t, body, false)
|
|
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)
|
|
checkErrResponse(t, body, false)
|
|
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)
|
|
checkErrResponse(t, body, true)
|
|
})
|
|
}
|
|
|
|
func checkErrResponse(t *testing.T, body []byte, expectingFail bool) {
|
|
var errresp ErrorResponse
|
|
err := json.Unmarshal(bytes.TrimSpace(body), &errresp)
|
|
assert.Nil(t, err)
|
|
if expectingFail {
|
|
assert.NotEqual(t, 0, errresp.Error.Code)
|
|
assert.NotEqual(t, "", errresp.Error.Message)
|
|
} else {
|
|
assert.Equal(t, 0, errresp.Error.Code)
|
|
assert.Equal(t, "", errresp.Error.Message)
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|