forked from TrueCloudLab/neoneo-go
Merge pull request #676 from nspcc-dev/dedup-rpc-types
Split up RPC into separate packages Closes #510.
This commit is contained in:
commit
56f87cd44e
48 changed files with 1046 additions and 1243 deletions
|
@ -14,7 +14,7 @@ import (
|
|||
"github.com/CityOfZion/neo-go/pkg/io"
|
||||
"github.com/CityOfZion/neo-go/pkg/network"
|
||||
"github.com/CityOfZion/neo-go/pkg/network/metrics"
|
||||
"github.com/CityOfZion/neo-go/pkg/rpc"
|
||||
"github.com/CityOfZion/neo-go/pkg/rpc/server"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/urfave/cli"
|
||||
"go.uber.org/zap"
|
||||
|
@ -341,18 +341,18 @@ func startServer(ctx *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
server, err := network.NewServer(serverConfig, chain, log)
|
||||
serv, err := network.NewServer(serverConfig, chain, log)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("failed to create network server: %v", err), 1)
|
||||
}
|
||||
rpcServer := rpc.NewServer(chain, cfg.ApplicationConfiguration.RPC, server, log)
|
||||
rpcServer := server.New(chain, cfg.ApplicationConfiguration.RPC, serv, log)
|
||||
errChan := make(chan error)
|
||||
|
||||
go server.Start(errChan)
|
||||
go serv.Start(errChan)
|
||||
go rpcServer.Start(errChan)
|
||||
|
||||
fmt.Println(logo())
|
||||
fmt.Println(server.UserAgent)
|
||||
fmt.Println(serv.UserAgent)
|
||||
fmt.Println()
|
||||
|
||||
var shutdownErr error
|
||||
|
@ -364,7 +364,7 @@ Main:
|
|||
cancel()
|
||||
|
||||
case <-grace.Done():
|
||||
server.Shutdown()
|
||||
serv.Shutdown()
|
||||
if serverErr := rpcServer.Shutdown(); serverErr != nil {
|
||||
shutdownErr = errors.Wrap(serverErr, "Error encountered whilst shutting down server")
|
||||
}
|
||||
|
|
|
@ -14,7 +14,9 @@ import (
|
|||
"github.com/CityOfZion/neo-go/pkg/compiler"
|
||||
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
|
||||
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
|
||||
"github.com/CityOfZion/neo-go/pkg/rpc"
|
||||
"github.com/CityOfZion/neo-go/pkg/rpc/client"
|
||||
"github.com/CityOfZion/neo-go/pkg/rpc/request"
|
||||
"github.com/CityOfZion/neo-go/pkg/rpc/response"
|
||||
"github.com/CityOfZion/neo-go/pkg/smartcontract"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
||||
|
@ -294,10 +296,10 @@ func initSmartContract(ctx *cli.Context) error {
|
|||
// TODO: Fix the missing neo-go.yml file with the `init` command when the package manager is in place.
|
||||
if !ctx.Bool("skip-details") {
|
||||
details := parseContractDetails()
|
||||
details.ReturnType = rpc.ByteArray
|
||||
details.Parameters = make([]rpc.StackParamType, 2)
|
||||
details.Parameters[0] = rpc.String
|
||||
details.Parameters[1] = rpc.Array
|
||||
details.ReturnType = request.ByteArray
|
||||
details.Parameters = make([]request.StackParamType, 2)
|
||||
details.Parameters[0] = request.String
|
||||
details.Parameters[1] = request.Array
|
||||
|
||||
project := &ProjectConfig{Contract: details}
|
||||
b, err := yaml.Marshal(project)
|
||||
|
@ -362,7 +364,7 @@ func invokeInternal(ctx *cli.Context, withMethod bool, signAndPush bool) error {
|
|||
operation string
|
||||
params = make([]smartcontract.Parameter, 0)
|
||||
paramsStart = 1
|
||||
resp *rpc.InvokeScriptResponse
|
||||
resp *response.InvokeResult
|
||||
wif *keys.WIF
|
||||
)
|
||||
|
||||
|
@ -398,34 +400,34 @@ func invokeInternal(ctx *cli.Context, withMethod bool, signAndPush bool) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
client, err := rpc.NewClient(context.TODO(), endpoint, rpc.ClientOptions{})
|
||||
c, err := client.New(context.TODO(), endpoint, client.Options{})
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
||||
if withMethod {
|
||||
resp, err = client.InvokeFunction(script, operation, params)
|
||||
resp, err = c.InvokeFunction(script, operation, params)
|
||||
} else {
|
||||
resp, err = client.Invoke(script, params)
|
||||
resp, err = c.Invoke(script, params)
|
||||
}
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
if signAndPush {
|
||||
if len(resp.Result.Script) == 0 {
|
||||
if len(resp.Script) == 0 {
|
||||
return cli.NewExitError(errors.New("no script returned from the RPC node"), 1)
|
||||
}
|
||||
script, err := hex.DecodeString(resp.Result.Script)
|
||||
script, err := hex.DecodeString(resp.Script)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("bad script returned from the RPC node: %v", err), 1)
|
||||
}
|
||||
txHash, err := client.SignAndPushInvocationTx(script, wif, gas)
|
||||
txHash, err := c.SignAndPushInvocationTx(script, wif, gas)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("failed to push invocation tx: %v", err), 1)
|
||||
}
|
||||
fmt.Printf("Sent invocation transaction %s\n", txHash.StringLE())
|
||||
} else {
|
||||
b, err := json.MarshalIndent(resp.Result, "", " ")
|
||||
b, err := json.MarshalIndent(resp, "", " ")
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
@ -451,18 +453,18 @@ func testInvokeScript(ctx *cli.Context) error {
|
|||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
||||
client, err := rpc.NewClient(context.TODO(), endpoint, rpc.ClientOptions{})
|
||||
c, err := client.New(context.TODO(), endpoint, client.Options{})
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
||||
scriptHex := hex.EncodeToString(b)
|
||||
resp, err := client.InvokeScript(scriptHex)
|
||||
resp, err := c.InvokeScript(scriptHex)
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
||||
b, err = json.MarshalIndent(resp.Result, "", " ")
|
||||
b, err = json.MarshalIndent(resp, "", " ")
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
@ -475,11 +477,11 @@ func testInvokeScript(ctx *cli.Context) error {
|
|||
// ProjectConfig contains project metadata.
|
||||
type ProjectConfig struct {
|
||||
Version uint
|
||||
Contract rpc.ContractDetails `yaml:"project"`
|
||||
Contract request.ContractDetails `yaml:"project"`
|
||||
}
|
||||
|
||||
func parseContractDetails() rpc.ContractDetails {
|
||||
details := rpc.ContractDetails{}
|
||||
func parseContractDetails() request.ContractDetails {
|
||||
details := request.ContractDetails{}
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
|
||||
fmt.Print("Author: ")
|
||||
|
@ -571,17 +573,17 @@ func contractDeploy(ctx *cli.Context) error {
|
|||
return cli.NewExitError(fmt.Errorf("bad config: %v", err), 1)
|
||||
}
|
||||
|
||||
client, err := rpc.NewClient(context.TODO(), endpoint, rpc.ClientOptions{})
|
||||
c, err := client.New(context.TODO(), endpoint, client.Options{})
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
||||
txScript, err := rpc.CreateDeploymentScript(avm, &conf.Contract)
|
||||
txScript, err := request.CreateDeploymentScript(avm, &conf.Contract)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("failed to create deployment script: %v", err), 1)
|
||||
}
|
||||
|
||||
txHash, err := client.SignAndPushInvocationTx(txScript, wif, gas)
|
||||
txHash, err := c.SignAndPushInvocationTx(txScript, wif, gas)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("failed to push invocation tx: %v", err), 1)
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
|
||||
"github.com/CityOfZion/neo-go/pkg/encoding/address"
|
||||
"github.com/CityOfZion/neo-go/pkg/network"
|
||||
"github.com/CityOfZion/neo-go/pkg/rpc"
|
||||
"github.com/CityOfZion/neo-go/pkg/rpc/request"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap/zaptest"
|
||||
)
|
||||
|
@ -55,7 +55,7 @@ func prepareData(t *testing.B) []*transaction.Transaction {
|
|||
|
||||
for n := 0; n < t.N; n++ {
|
||||
tx := getTX(t, wif)
|
||||
require.NoError(t, rpc.SignTx(tx, wif))
|
||||
require.NoError(t, request.SignTx(tx, wif))
|
||||
data = append(data, tx)
|
||||
}
|
||||
return data
|
||||
|
|
|
@ -190,12 +190,25 @@ func (s *Server) Shutdown() {
|
|||
// UnconnectedPeers returns a list of peers that are in the discovery peer list
|
||||
// but are not connected to the server.
|
||||
func (s *Server) UnconnectedPeers() []string {
|
||||
return []string{}
|
||||
return s.discovery.UnconnectedPeers()
|
||||
}
|
||||
|
||||
// BadPeers returns a list of peers the are flagged as "bad" peers.
|
||||
func (s *Server) BadPeers() []string {
|
||||
return []string{}
|
||||
return s.discovery.BadPeers()
|
||||
}
|
||||
|
||||
// ConnectedPeers returns a list of currently connected peers.
|
||||
func (s *Server) ConnectedPeers() []string {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
|
||||
peers := make([]string, 0, len(s.peers))
|
||||
for k := range s.peers {
|
||||
peers = append(peers, k.PeerAddr().String())
|
||||
}
|
||||
|
||||
return peers
|
||||
}
|
||||
|
||||
// run is a goroutine that starts another goroutine to manage protocol specifics
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package rpc
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -14,6 +14,8 @@ import (
|
|||
"github.com/CityOfZion/neo-go/pkg/core/state"
|
||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
|
||||
"github.com/CityOfZion/neo-go/pkg/rpc/request"
|
||||
"github.com/CityOfZion/neo-go/pkg/rpc/response"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
@ -37,13 +39,13 @@ type Client struct {
|
|||
wifMu *sync.Mutex
|
||||
wif *keys.WIF
|
||||
balancerMu *sync.Mutex
|
||||
balancer BalanceGetter
|
||||
balancer request.BalanceGetter
|
||||
}
|
||||
|
||||
// ClientOptions defines options for the RPC client.
|
||||
// Options defines options for the RPC client.
|
||||
// All Values are optional. If any duration is not specified
|
||||
// a default of 3 seconds will be used.
|
||||
type ClientOptions struct {
|
||||
type Options struct {
|
||||
Cert string
|
||||
Key string
|
||||
CACert string
|
||||
|
@ -55,8 +57,8 @@ type ClientOptions struct {
|
|||
Version string
|
||||
}
|
||||
|
||||
// NewClient returns a new Client ready to use.
|
||||
func NewClient(ctx context.Context, endpoint string, opts ClientOptions) (*Client, error) {
|
||||
// New returns a new Client ready to use.
|
||||
func New(ctx context.Context, endpoint string, opts Options) (*Client, error) {
|
||||
url, err := url.Parse(endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -121,14 +123,14 @@ func (c *Client) SetWIF(wif string) error {
|
|||
}
|
||||
|
||||
// Balancer is a getter for balance field.
|
||||
func (c *Client) Balancer() BalanceGetter {
|
||||
func (c *Client) Balancer() request.BalanceGetter {
|
||||
c.balancerMu.Lock()
|
||||
defer c.balancerMu.Unlock()
|
||||
return c.balancer
|
||||
}
|
||||
|
||||
// SetBalancer is a setter for balance field.
|
||||
func (c *Client) SetBalancer(b BalanceGetter) {
|
||||
func (c *Client) SetBalancer(b request.BalanceGetter) {
|
||||
c.balancerMu.Lock()
|
||||
defer c.balancerMu.Unlock()
|
||||
|
||||
|
@ -161,13 +163,10 @@ func (c *Client) CalculateInputs(address string, asset util.Uint256, cost util.F
|
|||
var utxos state.UnspentBalances
|
||||
|
||||
resp, err := c.GetUnspents(address)
|
||||
if err != nil || resp.Error != nil {
|
||||
if err == nil {
|
||||
err = fmt.Errorf("remote returned %d: %s", resp.Error.Code, resp.Error.Message)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, util.Fixed8(0), errors.Wrapf(err, "cannot get balance for address %v", address)
|
||||
}
|
||||
for _, ubi := range resp.Result.Balance {
|
||||
for _, ubi := range resp.Balance {
|
||||
if asset.Equals(ubi.AssetHash) {
|
||||
utxos = ubi.Unspents
|
||||
break
|
||||
|
@ -177,15 +176,16 @@ func (c *Client) CalculateInputs(address string, asset util.Uint256, cost util.F
|
|||
|
||||
}
|
||||
|
||||
func (c *Client) performRequest(method string, p params, v interface{}) error {
|
||||
func (c *Client) performRequest(method string, p request.RawParams, v interface{}) error {
|
||||
var (
|
||||
r = request{
|
||||
JSONRPC: c.version,
|
||||
Method: method,
|
||||
Params: p.values,
|
||||
ID: 1,
|
||||
r = request.Raw{
|
||||
JSONRPC: c.version,
|
||||
Method: method,
|
||||
RawParams: p.Values,
|
||||
ID: 1,
|
||||
}
|
||||
buf = new(bytes.Buffer)
|
||||
raw = &response.Raw{}
|
||||
)
|
||||
|
||||
if err := json.NewEncoder(buf).Encode(r); err != nil {
|
||||
|
@ -202,11 +202,22 @@ func (c *Client) performRequest(method string, p params, v interface{}) error {
|
|||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("remote responded with a non 200 response: %d", resp.StatusCode)
|
||||
// The node might send us proper JSON anyway, so look there first and if
|
||||
// it parses, then it has more relevant data than HTTP error code.
|
||||
err = json.NewDecoder(resp.Body).Decode(raw)
|
||||
if err == nil {
|
||||
if raw.Error != nil {
|
||||
err = raw.Error
|
||||
} else {
|
||||
err = json.Unmarshal(raw.Result, v)
|
||||
}
|
||||
} else if resp.StatusCode != http.StatusOK {
|
||||
err = fmt.Errorf("HTTP %d/%s", resp.StatusCode, http.StatusText(resp.StatusCode))
|
||||
} else {
|
||||
err = errors.Wrap(err, "JSON decoding")
|
||||
}
|
||||
|
||||
return json.NewDecoder(resp.Body).Decode(v)
|
||||
return err
|
||||
}
|
||||
|
||||
// Ping attempts to create a connection to the endpoint.
|
69
pkg/rpc/client/doc.go
Normal file
69
pkg/rpc/client/doc.go
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
Package client implements NEO-specific JSON-RPC 2.0 client.
|
||||
This package is currently in alpha and is subject to change.
|
||||
|
||||
Client
|
||||
|
||||
After creating a client instance with or without a ClientConfig
|
||||
you can interact with the NEO blockchain by its exposed methods.
|
||||
|
||||
Some of the methods also allow to pass a verbose bool. This will
|
||||
return a more pretty printed response from the server instead of
|
||||
a raw hex string.
|
||||
|
||||
An example:
|
||||
endpoint := "http://seed5.bridgeprotocol.io:10332"
|
||||
opts := client.Options{}
|
||||
|
||||
c, err := client.New(context.TODO(), endpoint, opts)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := c.Ping(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
resp, err := c.GetAccountState("ATySFJAbLW7QHsZGHScLhxq6EyNBxx3eFP")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Println(resp.Result.ScriptHash)
|
||||
log.Println(resp.Result.Balances)
|
||||
|
||||
TODO:
|
||||
Merge structs so can be used by both server and client.
|
||||
Add missing methods to client.
|
||||
Allow client to connect using client cert.
|
||||
More in-depth examples.
|
||||
|
||||
Supported methods
|
||||
|
||||
getblock
|
||||
getaccountstate
|
||||
getunspents
|
||||
invokescript
|
||||
invokefunction
|
||||
sendrawtransaction
|
||||
invoke
|
||||
getrawtransaction
|
||||
|
||||
Unsupported methods
|
||||
|
||||
validateaddress
|
||||
getblocksysfee
|
||||
getcontractstate
|
||||
getrawmempool
|
||||
getstorage
|
||||
submitblock
|
||||
gettxout
|
||||
getassetstate
|
||||
getpeers
|
||||
getversion
|
||||
getconnectioncount
|
||||
getblockhash
|
||||
getblockcount
|
||||
getbestblockhash
|
||||
|
||||
*/
|
||||
package client
|
|
@ -1,4 +1,4 @@
|
|||
package rpc
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -8,11 +8,38 @@ import (
|
|||
|
||||
"github.com/CityOfZion/neo-go/pkg/core/state"
|
||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||
"github.com/CityOfZion/neo-go/pkg/rpc/wrappers"
|
||||
"github.com/CityOfZion/neo-go/pkg/rpc/response/result"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
errs "github.com/pkg/errors"
|
||||
)
|
||||
|
||||
/*
|
||||
Definition of types, helper functions and variables
|
||||
required for calculation of transaction inputs using
|
||||
NeoScan API.
|
||||
*/
|
||||
|
||||
type (
|
||||
// NeoScanServer stores NEOSCAN URL and API path.
|
||||
NeoScanServer struct {
|
||||
URL string // "protocol://host:port/"
|
||||
Path string // path to API endpoint without wallet address
|
||||
}
|
||||
|
||||
// Unspent stores Unspents per asset
|
||||
Unspent struct {
|
||||
Unspent state.UnspentBalances
|
||||
Asset string // "NEO" / "GAS"
|
||||
Amount util.Fixed8 // total unspent of this asset
|
||||
}
|
||||
|
||||
// NeoScanBalance is a struct of NeoScan response to 'get_balance' request
|
||||
NeoScanBalance struct {
|
||||
Balance []*Unspent
|
||||
Address string
|
||||
}
|
||||
)
|
||||
|
||||
// GetBalance performs a request to get balance for the address specified.
|
||||
func (s NeoScanServer) GetBalance(address string) ([]*Unspent, error) {
|
||||
var (
|
||||
|
@ -57,7 +84,7 @@ func (s NeoScanServer) CalculateInputs(address string, assetIDUint util.Uint256,
|
|||
err error
|
||||
us []*Unspent
|
||||
assetUnspent Unspent
|
||||
assetID = wrappers.GlobalAssets[assetIDUint.StringLE()]
|
||||
assetID = result.GlobalAssets[assetIDUint.StringLE()]
|
||||
)
|
||||
if us, err = s.GetBalance(address); err != nil {
|
||||
return nil, util.Fixed8(0), errs.Wrapf(err, "Cannot get balance for address %v", address)
|
|
@ -1,12 +1,14 @@
|
|||
package rpc
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/core"
|
||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
|
||||
"github.com/CityOfZion/neo-go/pkg/rpc/request"
|
||||
"github.com/CityOfZion/neo-go/pkg/rpc/response"
|
||||
"github.com/CityOfZion/neo-go/pkg/rpc/response/result"
|
||||
"github.com/CityOfZion/neo-go/pkg/smartcontract"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
"github.com/pkg/errors"
|
||||
|
@ -17,11 +19,11 @@ import (
|
|||
// missing output wrapper at the moment, thus commented out
|
||||
// func (c *Client) getBlock(indexOrHash interface{}, verbose bool) (*response, error) {
|
||||
// var (
|
||||
// params = newParams(indexOrHash)
|
||||
// params = request.NewRawParams(indexOrHash)
|
||||
// resp = &response{}
|
||||
// )
|
||||
// if verbose {
|
||||
// params = newParams(indexOrHash, 1)
|
||||
// params = request.NewRawParams(indexOrHash, 1)
|
||||
// }
|
||||
// if err := c.performRequest("getblock", params, resp); err != nil {
|
||||
// return nil, err
|
||||
|
@ -30,10 +32,10 @@ import (
|
|||
// }
|
||||
|
||||
// GetAccountState returns detailed information about a NEO account.
|
||||
func (c *Client) GetAccountState(address string) (*AccountStateResponse, error) {
|
||||
func (c *Client) GetAccountState(address string) (*result.AccountState, error) {
|
||||
var (
|
||||
params = newParams(address)
|
||||
resp = &AccountStateResponse{}
|
||||
params = request.NewRawParams(address)
|
||||
resp = &result.AccountState{}
|
||||
)
|
||||
if err := c.performRequest("getaccountstate", params, resp); err != nil {
|
||||
return nil, err
|
||||
|
@ -42,10 +44,10 @@ func (c *Client) GetAccountState(address string) (*AccountStateResponse, error)
|
|||
}
|
||||
|
||||
// GetUnspents returns UTXOs for the given NEO account.
|
||||
func (c *Client) GetUnspents(address string) (*UnspentResponse, error) {
|
||||
func (c *Client) GetUnspents(address string) (*result.Unspents, error) {
|
||||
var (
|
||||
params = newParams(address)
|
||||
resp = &UnspentResponse{}
|
||||
params = request.NewRawParams(address)
|
||||
resp = &result.Unspents{}
|
||||
)
|
||||
if err := c.performRequest("getunspents", params, resp); err != nil {
|
||||
return nil, err
|
||||
|
@ -55,10 +57,10 @@ func (c *Client) GetUnspents(address string) (*UnspentResponse, error) {
|
|||
|
||||
// InvokeScript returns the result of the given script after running it true the VM.
|
||||
// NOTE: This is a test invoke and will not affect the blockchain.
|
||||
func (c *Client) InvokeScript(script string) (*InvokeScriptResponse, error) {
|
||||
func (c *Client) InvokeScript(script string) (*response.InvokeResult, error) {
|
||||
var (
|
||||
params = newParams(script)
|
||||
resp = &InvokeScriptResponse{}
|
||||
params = request.NewRawParams(script)
|
||||
resp = &response.InvokeResult{}
|
||||
)
|
||||
if err := c.performRequest("invokescript", params, resp); err != nil {
|
||||
return nil, err
|
||||
|
@ -69,10 +71,10 @@ func (c *Client) InvokeScript(script string) (*InvokeScriptResponse, error) {
|
|||
// InvokeFunction returns the results after calling the smart contract scripthash
|
||||
// with the given operation and parameters.
|
||||
// NOTE: this is test invoke and will not affect the blockchain.
|
||||
func (c *Client) InvokeFunction(script, operation string, params []smartcontract.Parameter) (*InvokeScriptResponse, error) {
|
||||
func (c *Client) InvokeFunction(script, operation string, params []smartcontract.Parameter) (*response.InvokeResult, error) {
|
||||
var (
|
||||
p = newParams(script, operation, params)
|
||||
resp = &InvokeScriptResponse{}
|
||||
p = request.NewRawParams(script, operation, params)
|
||||
resp = &response.InvokeResult{}
|
||||
)
|
||||
if err := c.performRequest("invokefunction", p, resp); err != nil {
|
||||
return nil, err
|
||||
|
@ -82,10 +84,10 @@ func (c *Client) InvokeFunction(script, operation string, params []smartcontract
|
|||
|
||||
// Invoke returns the results after calling the smart contract scripthash
|
||||
// with the given parameters.
|
||||
func (c *Client) Invoke(script string, params []smartcontract.Parameter) (*InvokeScriptResponse, error) {
|
||||
func (c *Client) Invoke(script string, params []smartcontract.Parameter) (*response.InvokeResult, error) {
|
||||
var (
|
||||
p = newParams(script, params)
|
||||
resp = &InvokeScriptResponse{}
|
||||
p = request.NewRawParams(script, params)
|
||||
resp = &response.InvokeResult{}
|
||||
)
|
||||
if err := c.performRequest("invoke", p, resp); err != nil {
|
||||
return nil, err
|
||||
|
@ -97,7 +99,7 @@ func (c *Client) Invoke(script string, params []smartcontract.Parameter) (*Invok
|
|||
// missing output wrapper at the moment, thus commented out
|
||||
// func (c *Client) getRawTransaction(hash string, verbose bool) (*response, error) {
|
||||
// var (
|
||||
// params = newParams(hash, verbose)
|
||||
// params = request.NewRawParams(hash, verbose)
|
||||
// resp = &response{}
|
||||
// )
|
||||
// if err := c.performRequest("getrawtransaction", params, resp); err != nil {
|
||||
|
@ -110,48 +112,44 @@ func (c *Client) Invoke(script string, params []smartcontract.Parameter) (*Invok
|
|||
// The given hex string needs to be signed with a keypair.
|
||||
// When the result of the response object is true, the TX has successfully
|
||||
// been broadcasted to the network.
|
||||
func (c *Client) sendRawTransaction(rawTX *transaction.Transaction) (*response, error) {
|
||||
func (c *Client) sendRawTransaction(rawTX *transaction.Transaction) error {
|
||||
var (
|
||||
params = newParams(hex.EncodeToString(rawTX.Bytes()))
|
||||
resp = &response{}
|
||||
params = request.NewRawParams(hex.EncodeToString(rawTX.Bytes()))
|
||||
resp bool
|
||||
)
|
||||
if err := c.performRequest("sendrawtransaction", params, resp); err != nil {
|
||||
return nil, err
|
||||
if err := c.performRequest("sendrawtransaction", params, &resp); err != nil {
|
||||
return err
|
||||
}
|
||||
return resp, nil
|
||||
if !resp {
|
||||
return errors.New("sendrawtransaction returned false")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SendToAddress sends an amount of specific asset to a given address.
|
||||
// This call requires open wallet. (`wif` key in client struct.)
|
||||
// If response.Result is `true` then transaction was formed correctly and was written in blockchain.
|
||||
func (c *Client) SendToAddress(asset util.Uint256, address string, amount util.Fixed8) (*SendToAddressResponse, error) {
|
||||
func (c *Client) SendToAddress(asset util.Uint256, address string, amount util.Fixed8) (util.Uint256, error) {
|
||||
var (
|
||||
err error
|
||||
rawTx *transaction.Transaction
|
||||
txParams = ContractTxParams{
|
||||
assetID: asset,
|
||||
address: address,
|
||||
value: amount,
|
||||
wif: c.WIF(),
|
||||
balancer: c.Balancer(),
|
||||
txParams = request.ContractTxParams{
|
||||
AssetID: asset,
|
||||
Address: address,
|
||||
Value: amount,
|
||||
WIF: c.WIF(),
|
||||
Balancer: c.Balancer(),
|
||||
}
|
||||
resp *response
|
||||
response = &SendToAddressResponse{}
|
||||
resp util.Uint256
|
||||
)
|
||||
|
||||
if rawTx, err = CreateRawContractTransaction(txParams); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create raw transaction for `sendtoaddress`")
|
||||
if rawTx, err = request.CreateRawContractTransaction(txParams); err != nil {
|
||||
return resp, errors.Wrap(err, "failed to create raw transaction for `sendtoaddress`")
|
||||
}
|
||||
if resp, err = c.sendRawTransaction(rawTx); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to send raw transaction")
|
||||
if err = c.sendRawTransaction(rawTx); err != nil {
|
||||
return resp, errors.Wrap(err, "failed to send raw transaction")
|
||||
}
|
||||
response.Error = resp.Error
|
||||
response.ID = resp.ID
|
||||
response.JSONRPC = resp.JSONRPC
|
||||
response.Result = &TxResponse{
|
||||
TxID: rawTx.Hash().StringLE(),
|
||||
}
|
||||
return response, nil
|
||||
return rawTx.Hash(), nil
|
||||
}
|
||||
|
||||
// SignAndPushInvocationTx signs and pushes given script as an invocation
|
||||
|
@ -166,22 +164,19 @@ func (c *Client) SignAndPushInvocationTx(script []byte, wif *keys.WIF, gas util.
|
|||
fromAddress := wif.PrivateKey.Address()
|
||||
|
||||
if gas > 0 {
|
||||
if err = AddInputsAndUnspentsToTx(tx, fromAddress, core.UtilityTokenID(), gas, c); err != nil {
|
||||
if err = request.AddInputsAndUnspentsToTx(tx, fromAddress, core.UtilityTokenID(), gas, c); err != nil {
|
||||
return txHash, errors.Wrap(err, "failed to add inputs and unspents to transaction")
|
||||
}
|
||||
}
|
||||
|
||||
if err = SignTx(tx, wif); err != nil {
|
||||
if err = request.SignTx(tx, wif); err != nil {
|
||||
return txHash, errors.Wrap(err, "failed to sign tx")
|
||||
}
|
||||
txHash = tx.Hash()
|
||||
resp, err := c.sendRawTransaction(tx)
|
||||
err = c.sendRawTransaction(tx)
|
||||
|
||||
if err != nil {
|
||||
return txHash, errors.Wrap(err, "failed sendning tx")
|
||||
}
|
||||
if resp.Error != nil {
|
||||
return txHash, fmt.Errorf("remote returned %d: %s", resp.Error.Code, resp.Error.Message)
|
||||
}
|
||||
return txHash, nil
|
||||
}
|
111
pkg/rpc/doc.go
111
pkg/rpc/doc.go
|
@ -1,111 +0,0 @@
|
|||
/*
|
||||
Package rpc implements NEO-specific JSON-RPC 2.0 client and server.
|
||||
This package is currently in alpha and is subject to change.
|
||||
|
||||
Client
|
||||
|
||||
After creating a client instance with or without a ClientConfig
|
||||
you can interact with the NEO blockchain by its exposed methods.
|
||||
|
||||
Some of the methods also allow to pass a verbose bool. This will
|
||||
return a more pretty printed response from the server instead of
|
||||
a raw hex string.
|
||||
|
||||
An example:
|
||||
endpoint := "http://seed5.bridgeprotocol.io:10332"
|
||||
opts := rpc.ClientOptions{}
|
||||
|
||||
client, err := rpc.NewClient(context.TODO(), endpoint, opts)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := client.Ping(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
resp, err := client.GetAccountState("ATySFJAbLW7QHsZGHScLhxq6EyNBxx3eFP")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Println(resp.Result.ScriptHash)
|
||||
log.Println(resp.Result.Balances)
|
||||
|
||||
TODO:
|
||||
Merge structs so can be used by both server and client.
|
||||
Add missing methods to client.
|
||||
Allow client to connect using client cert.
|
||||
More in-depth examples.
|
||||
|
||||
Supported methods
|
||||
|
||||
getblock
|
||||
getaccountstate
|
||||
getunspents
|
||||
invokescript
|
||||
invokefunction
|
||||
sendrawtransaction
|
||||
invoke
|
||||
getrawtransaction
|
||||
|
||||
Unsupported methods
|
||||
|
||||
validateaddress
|
||||
getblocksysfee
|
||||
getcontractstate
|
||||
getrawmempool
|
||||
getstorage
|
||||
submitblock
|
||||
gettxout
|
||||
getassetstate
|
||||
getpeers
|
||||
getversion
|
||||
getconnectioncount
|
||||
getblockhash
|
||||
getblockcount
|
||||
getbestblockhash
|
||||
|
||||
Server
|
||||
|
||||
The server is written to support as much of the JSON-RPC 2.0 Spec as possible.
|
||||
The server is run as part of the node currently.
|
||||
|
||||
TODO:
|
||||
Implement HTTPS server.
|
||||
Add remaining methods (Documented below).
|
||||
Add Swagger spec and test using dredd in circleCI.
|
||||
|
||||
Example call
|
||||
|
||||
An example would be viewing the version of the node:
|
||||
|
||||
$ curl -X POST -d '{"jsonrpc": "2.0", "method": "getversion", "params": [], "id": 1}' http://localhost:20332
|
||||
|
||||
which would yield the response:
|
||||
|
||||
{
|
||||
"jsonrpc" : "2.0",
|
||||
"id" : 1,
|
||||
"result" : {
|
||||
"port" : 20333,
|
||||
"useragent" : "/NEO-GO:0.36.0-dev/",
|
||||
"nonce" : 9318417
|
||||
}
|
||||
}
|
||||
|
||||
Unsupported methods
|
||||
|
||||
getblocksysfee
|
||||
getcontractstate (needs to be implemented in pkg/core/blockchain.go)
|
||||
getrawmempool (needs to be implemented on in pkg/network/server.go)
|
||||
getrawtransaction (needs to be implemented in pkg/core/blockchain.go)
|
||||
getstorage (lacks VM functionality)
|
||||
gettxout (needs to be implemented in pkg/core/blockchain.go)
|
||||
invoke (lacks VM functionality)
|
||||
invokefunction (lacks VM functionality)
|
||||
invokescript (lacks VM functionality)
|
||||
sendrawtransaction (needs to be implemented in pkg/core/blockchain.go)
|
||||
submitblock (needs to be implemented in pkg/core/blockchain.go)
|
||||
|
||||
*/
|
||||
package rpc
|
|
@ -1,33 +0,0 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"github.com/CityOfZion/neo-go/pkg/core/state"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
/*
|
||||
Definition of types, helper functions and variables
|
||||
required for calculation of transaction inputs using
|
||||
NeoScan API.
|
||||
*/
|
||||
|
||||
type (
|
||||
// NeoScanServer stores NEOSCAN URL and API path.
|
||||
NeoScanServer struct {
|
||||
URL string // "protocol://host:port/"
|
||||
Path string // path to API endpoint without wallet address
|
||||
}
|
||||
|
||||
// Unspent stores Unspents per asset
|
||||
Unspent struct {
|
||||
Unspent state.UnspentBalances
|
||||
Asset string // "NEO" / "GAS"
|
||||
Amount util.Fixed8 // total unspent of this asset
|
||||
}
|
||||
|
||||
// NeoScanBalance is a struct of NeoScan response to 'get_balance' request
|
||||
NeoScanBalance struct {
|
||||
Balance []*Unspent
|
||||
Address string
|
||||
}
|
||||
)
|
|
@ -1,128 +0,0 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const (
|
||||
jsonRPCVersion = "2.0"
|
||||
)
|
||||
|
||||
type (
|
||||
// Request represents a standard JSON-RPC 2.0
|
||||
// request: http://www.jsonrpc.org/specification#request_object.
|
||||
Request struct {
|
||||
JSONRPC string `json:"jsonrpc"`
|
||||
Method string `json:"method"`
|
||||
RawParams json.RawMessage `json:"params,omitempty"`
|
||||
RawID json.RawMessage `json:"id,omitempty"`
|
||||
}
|
||||
|
||||
// Response represents a standard JSON-RPC 2.0
|
||||
// response: http://www.jsonrpc.org/specification#response_object.
|
||||
Response struct {
|
||||
JSONRPC string `json:"jsonrpc"`
|
||||
Result interface{} `json:"result,omitempty"`
|
||||
Error *Error `json:"error,omitempty"`
|
||||
ID json.RawMessage `json:"id,omitempty"`
|
||||
}
|
||||
)
|
||||
|
||||
// NewRequest creates a new Request struct.
|
||||
func NewRequest() *Request {
|
||||
return &Request{
|
||||
JSONRPC: jsonRPCVersion,
|
||||
}
|
||||
}
|
||||
|
||||
// DecodeData decodes the given reader into the the request
|
||||
// struct.
|
||||
func (r *Request) DecodeData(data io.ReadCloser) error {
|
||||
defer data.Close()
|
||||
|
||||
err := json.NewDecoder(data).Decode(r)
|
||||
if err != nil {
|
||||
return errors.Errorf("error parsing JSON payload: %s", err)
|
||||
}
|
||||
|
||||
if r.JSONRPC != jsonRPCVersion {
|
||||
return errors.Errorf("invalid version, expected 2.0 got: '%s'", r.JSONRPC)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Params takes a slice of any type and attempts to bind
|
||||
// the params to it.
|
||||
func (r *Request) Params() (*Params, error) {
|
||||
params := Params{}
|
||||
|
||||
err := json.Unmarshal(r.RawParams, ¶ms)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("error parsing params field in payload: %s", err)
|
||||
}
|
||||
|
||||
return ¶ms, nil
|
||||
}
|
||||
|
||||
// WriteErrorResponse writes an error response to the ResponseWriter.
|
||||
func (s *Server) WriteErrorResponse(r *Request, w http.ResponseWriter, err error) {
|
||||
jsonErr, ok := err.(*Error)
|
||||
if !ok {
|
||||
jsonErr = NewInternalServerError("Internal server error", err)
|
||||
}
|
||||
|
||||
response := Response{
|
||||
JSONRPC: r.JSONRPC,
|
||||
Error: jsonErr,
|
||||
ID: r.RawID,
|
||||
}
|
||||
|
||||
logFields := []zap.Field{
|
||||
zap.Error(jsonErr.Cause),
|
||||
zap.String("method", r.Method),
|
||||
}
|
||||
|
||||
params, err := r.Params()
|
||||
if err == nil {
|
||||
logFields = append(logFields, zap.Any("params", params))
|
||||
}
|
||||
|
||||
s.log.Error("Error encountered with rpc request", logFields...)
|
||||
|
||||
w.WriteHeader(jsonErr.HTTPCode)
|
||||
s.writeServerResponse(r, w, response)
|
||||
}
|
||||
|
||||
// WriteResponse encodes the response and writes it to the ResponseWriter.
|
||||
func (s *Server) WriteResponse(r *Request, w http.ResponseWriter, result interface{}) {
|
||||
response := Response{
|
||||
JSONRPC: r.JSONRPC,
|
||||
Result: result,
|
||||
ID: r.RawID,
|
||||
}
|
||||
|
||||
s.writeServerResponse(r, w, response)
|
||||
}
|
||||
|
||||
func (s *Server) writeServerResponse(r *Request, w http.ResponseWriter, response Response) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
if s.config.EnableCORSWorkaround {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With")
|
||||
}
|
||||
|
||||
encoder := json.NewEncoder(w)
|
||||
err := encoder.Encode(response)
|
||||
|
||||
if err != nil {
|
||||
s.log.Error("Error encountered while encoding response",
|
||||
zap.String("err", err.Error()),
|
||||
zap.String("method", r.Method))
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package rpc
|
||||
package request
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -29,12 +29,13 @@ type (
|
|||
}
|
||||
)
|
||||
|
||||
// These are parameter types accepted by RPC server.
|
||||
const (
|
||||
defaultT paramType = iota
|
||||
stringT
|
||||
numberT
|
||||
arrayT
|
||||
funcParamT
|
||||
StringT
|
||||
NumberT
|
||||
ArrayT
|
||||
FuncParamT
|
||||
)
|
||||
|
||||
func (p Param) String() string {
|
||||
|
@ -123,7 +124,7 @@ func (p Param) GetBytesHex() ([]byte, error) {
|
|||
func (p *Param) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(data, &s); err == nil {
|
||||
p.Type = stringT
|
||||
p.Type = StringT
|
||||
p.Value = s
|
||||
|
||||
return nil
|
||||
|
@ -131,7 +132,7 @@ func (p *Param) UnmarshalJSON(data []byte) error {
|
|||
|
||||
var num float64
|
||||
if err := json.Unmarshal(data, &num); err == nil {
|
||||
p.Type = numberT
|
||||
p.Type = NumberT
|
||||
p.Value = int(num)
|
||||
|
||||
return nil
|
||||
|
@ -142,7 +143,7 @@ func (p *Param) UnmarshalJSON(data []byte) error {
|
|||
jd.DisallowUnknownFields()
|
||||
var fp FuncParam
|
||||
if err := jd.Decode(&fp); err == nil {
|
||||
p.Type = funcParamT
|
||||
p.Type = FuncParamT
|
||||
p.Value = fp
|
||||
|
||||
return nil
|
||||
|
@ -150,7 +151,7 @@ func (p *Param) UnmarshalJSON(data []byte) error {
|
|||
|
||||
var ps []Param
|
||||
if err := json.Unmarshal(data, &ps); err == nil {
|
||||
p.Type = arrayT
|
||||
p.Type = ArrayT
|
||||
p.Value = ps
|
||||
|
||||
return nil
|
|
@ -1,4 +1,4 @@
|
|||
package rpc
|
||||
package request
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
|
@ -15,35 +15,35 @@ func TestParam_UnmarshalJSON(t *testing.T) {
|
|||
msg := `["str1", 123, ["str2", 3], [{"type": "String", "value": "jajaja"}]]`
|
||||
expected := Params{
|
||||
{
|
||||
Type: stringT,
|
||||
Type: StringT,
|
||||
Value: "str1",
|
||||
},
|
||||
{
|
||||
Type: numberT,
|
||||
Type: NumberT,
|
||||
Value: 123,
|
||||
},
|
||||
{
|
||||
Type: arrayT,
|
||||
Type: ArrayT,
|
||||
Value: []Param{
|
||||
{
|
||||
Type: stringT,
|
||||
Type: StringT,
|
||||
Value: "str2",
|
||||
},
|
||||
{
|
||||
Type: numberT,
|
||||
Type: NumberT,
|
||||
Value: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: arrayT,
|
||||
Type: ArrayT,
|
||||
Value: []Param{
|
||||
{
|
||||
Type: funcParamT,
|
||||
Type: FuncParamT,
|
||||
Value: FuncParam{
|
||||
Type: String,
|
||||
Value: Param{
|
||||
Type: stringT,
|
||||
Type: StringT,
|
||||
Value: "jajaja",
|
||||
},
|
||||
},
|
||||
|
@ -61,34 +61,34 @@ func TestParam_UnmarshalJSON(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestParamGetString(t *testing.T) {
|
||||
p := Param{stringT, "jajaja"}
|
||||
p := Param{StringT, "jajaja"}
|
||||
str, err := p.GetString()
|
||||
assert.Equal(t, "jajaja", str)
|
||||
require.Nil(t, err)
|
||||
|
||||
p = Param{stringT, int(100500)}
|
||||
p = Param{StringT, int(100500)}
|
||||
_, err = p.GetString()
|
||||
require.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestParamGetInt(t *testing.T) {
|
||||
p := Param{numberT, int(100500)}
|
||||
p := Param{NumberT, int(100500)}
|
||||
i, err := p.GetInt()
|
||||
assert.Equal(t, 100500, i)
|
||||
require.Nil(t, err)
|
||||
|
||||
p = Param{numberT, "jajaja"}
|
||||
p = Param{NumberT, "jajaja"}
|
||||
_, err = p.GetInt()
|
||||
require.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestParamGetArray(t *testing.T) {
|
||||
p := Param{arrayT, []Param{{numberT, 42}}}
|
||||
p := Param{ArrayT, []Param{{NumberT, 42}}}
|
||||
a, err := p.GetArray()
|
||||
assert.Equal(t, []Param{{numberT, 42}}, a)
|
||||
assert.Equal(t, []Param{{NumberT, 42}}, a)
|
||||
require.Nil(t, err)
|
||||
|
||||
p = Param{arrayT, 42}
|
||||
p = Param{ArrayT, 42}
|
||||
_, err = p.GetArray()
|
||||
require.NotNil(t, err)
|
||||
}
|
||||
|
@ -96,16 +96,16 @@ func TestParamGetArray(t *testing.T) {
|
|||
func TestParamGetUint256(t *testing.T) {
|
||||
gas := "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"
|
||||
u256, _ := util.Uint256DecodeStringLE(gas)
|
||||
p := Param{stringT, gas}
|
||||
p := Param{StringT, gas}
|
||||
u, err := p.GetUint256()
|
||||
assert.Equal(t, u256, u)
|
||||
require.Nil(t, err)
|
||||
|
||||
p = Param{stringT, 42}
|
||||
p = Param{StringT, 42}
|
||||
_, err = p.GetUint256()
|
||||
require.NotNil(t, err)
|
||||
|
||||
p = Param{stringT, "qq2c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"}
|
||||
p = Param{StringT, "qq2c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"}
|
||||
_, err = p.GetUint256()
|
||||
require.NotNil(t, err)
|
||||
}
|
||||
|
@ -113,16 +113,16 @@ func TestParamGetUint256(t *testing.T) {
|
|||
func TestParamGetUint160FromHex(t *testing.T) {
|
||||
in := "50befd26fdf6e4d957c11e078b24ebce6291456f"
|
||||
u160, _ := util.Uint160DecodeStringLE(in)
|
||||
p := Param{stringT, in}
|
||||
p := Param{StringT, in}
|
||||
u, err := p.GetUint160FromHex()
|
||||
assert.Equal(t, u160, u)
|
||||
require.Nil(t, err)
|
||||
|
||||
p = Param{stringT, 42}
|
||||
p = Param{StringT, 42}
|
||||
_, err = p.GetUint160FromHex()
|
||||
require.NotNil(t, err)
|
||||
|
||||
p = Param{stringT, "wwbefd26fdf6e4d957c11e078b24ebce6291456f"}
|
||||
p = Param{StringT, "wwbefd26fdf6e4d957c11e078b24ebce6291456f"}
|
||||
_, err = p.GetUint160FromHex()
|
||||
require.NotNil(t, err)
|
||||
}
|
||||
|
@ -130,16 +130,16 @@ func TestParamGetUint160FromHex(t *testing.T) {
|
|||
func TestParamGetUint160FromAddress(t *testing.T) {
|
||||
in := "AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"
|
||||
u160, _ := address.StringToUint160(in)
|
||||
p := Param{stringT, in}
|
||||
p := Param{StringT, in}
|
||||
u, err := p.GetUint160FromAddress()
|
||||
assert.Equal(t, u160, u)
|
||||
require.Nil(t, err)
|
||||
|
||||
p = Param{stringT, 42}
|
||||
p = Param{StringT, 42}
|
||||
_, err = p.GetUint160FromAddress()
|
||||
require.NotNil(t, err)
|
||||
|
||||
p = Param{stringT, "QK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"}
|
||||
p = Param{StringT, "QK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"}
|
||||
_, err = p.GetUint160FromAddress()
|
||||
require.NotNil(t, err)
|
||||
}
|
||||
|
@ -148,19 +148,19 @@ func TestParamGetFuncParam(t *testing.T) {
|
|||
fp := FuncParam{
|
||||
Type: String,
|
||||
Value: Param{
|
||||
Type: stringT,
|
||||
Type: StringT,
|
||||
Value: "jajaja",
|
||||
},
|
||||
}
|
||||
p := Param{
|
||||
Type: funcParamT,
|
||||
Type: FuncParamT,
|
||||
Value: fp,
|
||||
}
|
||||
newfp, err := p.GetFuncParam()
|
||||
assert.Equal(t, fp, newfp)
|
||||
require.Nil(t, err)
|
||||
|
||||
p = Param{funcParamT, 42}
|
||||
p = Param{FuncParamT, 42}
|
||||
_, err = p.GetFuncParam()
|
||||
require.NotNil(t, err)
|
||||
}
|
||||
|
@ -168,16 +168,16 @@ func TestParamGetFuncParam(t *testing.T) {
|
|||
func TestParamGetBytesHex(t *testing.T) {
|
||||
in := "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"
|
||||
inb, _ := hex.DecodeString(in)
|
||||
p := Param{stringT, in}
|
||||
p := Param{StringT, in}
|
||||
bh, err := p.GetBytesHex()
|
||||
assert.Equal(t, inb, bh)
|
||||
require.Nil(t, err)
|
||||
|
||||
p = Param{stringT, 42}
|
||||
p = Param{StringT, 42}
|
||||
_, err = p.GetBytesHex()
|
||||
require.NotNil(t, err)
|
||||
|
||||
p = Param{stringT, "qq2c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"}
|
||||
p = Param{StringT, "qq2c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"}
|
||||
_, err = p.GetBytesHex()
|
||||
require.NotNil(t, err)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package rpc
|
||||
package request
|
||||
|
||||
type (
|
||||
// Params represents the JSON-RPC params.
|
|
@ -1,4 +1,4 @@
|
|||
package rpc
|
||||
package request
|
||||
|
||||
// ContractDetails contains contract metadata.
|
||||
type ContractDetails struct {
|
|
@ -1,4 +1,4 @@
|
|||
package rpc
|
||||
package request
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
|
@ -1,4 +1,4 @@
|
|||
package rpc
|
||||
package request
|
||||
|
||||
import (
|
||||
"encoding/json"
|
|
@ -1,4 +1,4 @@
|
|||
package rpc
|
||||
package request
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
@ -25,7 +25,7 @@ func CreateRawContractTransaction(params ContractTxParams) (*transaction.Transac
|
|||
fromAddress string
|
||||
receiverOutput *transaction.Output
|
||||
|
||||
wif, assetID, toAddress, amount, balancer = params.wif, params.assetID, params.address, params.value, params.balancer
|
||||
wif, assetID, toAddress, amount, balancer = params.WIF, params.AssetID, params.Address, params.Value, params.Balancer
|
||||
)
|
||||
|
||||
fromAddress = wif.PrivateKey.Address()
|
||||
|
@ -214,15 +214,15 @@ func CreateFunctionInvocationScript(contract util.Uint160, params Params) ([]byt
|
|||
script := io.NewBufBinWriter()
|
||||
for i := len(params) - 1; i >= 0; i-- {
|
||||
switch params[i].Type {
|
||||
case stringT:
|
||||
case StringT:
|
||||
emit.String(script.BinWriter, params[i].String())
|
||||
case numberT:
|
||||
case NumberT:
|
||||
num, err := params[i].GetInt()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
emit.String(script.BinWriter, strconv.Itoa(num))
|
||||
case arrayT:
|
||||
case ArrayT:
|
||||
slice, err := params[i].GetArray()
|
||||
if err != nil {
|
||||
return nil, err
|
|
@ -1,4 +1,4 @@
|
|||
package rpc
|
||||
package request
|
||||
|
||||
/*
|
||||
Definition of types, interfaces and variables
|
||||
|
@ -16,14 +16,14 @@ type (
|
|||
// includes parameters duplication `sendtoaddress` RPC call params
|
||||
// and also some utility data;
|
||||
ContractTxParams struct {
|
||||
assetID util.Uint256
|
||||
address string
|
||||
value util.Fixed8
|
||||
wif keys.WIF // a WIF to send the transaction
|
||||
AssetID util.Uint256
|
||||
Address string
|
||||
Value util.Fixed8
|
||||
WIF keys.WIF // a WIF to send the transaction
|
||||
// since there are many ways to provide unspents,
|
||||
// transaction composer stays agnostic to that how
|
||||
// unspents was got;
|
||||
balancer BalanceGetter
|
||||
Balancer BalanceGetter
|
||||
}
|
||||
|
||||
// BalanceGetter is an interface supporting CalculateInputs() method.
|
89
pkg/rpc/request/tx_builder_test.go
Normal file
89
pkg/rpc/request/tx_builder_test.go
Normal file
|
@ -0,0 +1,89 @@
|
|||
package request
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestInvocationScriptCreationGood(t *testing.T) {
|
||||
p := Param{Type: StringT, Value: "50befd26fdf6e4d957c11e078b24ebce6291456f"}
|
||||
contract, err := p.GetUint160FromHex()
|
||||
require.Nil(t, err)
|
||||
|
||||
var paramScripts = []struct {
|
||||
ps Params
|
||||
script string
|
||||
}{{
|
||||
script: "676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
|
||||
}, {
|
||||
ps: Params{{Type: StringT, Value: "transfer"}},
|
||||
script: "087472616e73666572676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
|
||||
}, {
|
||||
ps: Params{{Type: NumberT, Value: 42}},
|
||||
script: "023432676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
|
||||
}, {
|
||||
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{}}},
|
||||
script: "00c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
|
||||
}, {
|
||||
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: ByteArray, Value: Param{Type: StringT, Value: "50befd26fdf6e4d957c11e078b24ebce6291456f"}}}}}},
|
||||
script: "1450befd26fdf6e4d957c11e078b24ebce6291456f51c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
|
||||
}, {
|
||||
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: Signature, Value: Param{Type: StringT, Value: "4edf5005771de04619235d5a4c7a9a11bb78e008541f1da7725f654c33380a3c87e2959a025da706d7255cb3a3fa07ebe9c6559d0d9e6213c68049168eb1056f"}}}}}},
|
||||
script: "404edf5005771de04619235d5a4c7a9a11bb78e008541f1da7725f654c33380a3c87e2959a025da706d7255cb3a3fa07ebe9c6559d0d9e6213c68049168eb1056f51c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
|
||||
}, {
|
||||
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: String, Value: Param{Type: StringT, Value: "50befd26fdf6e4d957c11e078b24ebce6291456f"}}}}}},
|
||||
script: "283530626566643236666466366534643935376331316530373862323465626365363239313435366651c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
|
||||
}, {
|
||||
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: Hash160, Value: Param{Type: StringT, Value: "50befd26fdf6e4d957c11e078b24ebce6291456f"}}}}}},
|
||||
script: "146f459162ceeb248b071ec157d9e4f6fd26fdbe5051c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
|
||||
}, {
|
||||
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: Hash256, Value: Param{Type: StringT, Value: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"}}}}}},
|
||||
script: "20e72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c6051c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
|
||||
}, {
|
||||
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: PublicKey, Value: Param{Type: StringT, Value: "03c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c1"}}}}}},
|
||||
script: "2103c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c151c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
|
||||
}, {
|
||||
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: Integer, Value: Param{Type: NumberT, Value: 42}}}}}},
|
||||
script: "012a51c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
|
||||
}, {
|
||||
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: Boolean, Value: Param{Type: StringT, Value: "true"}}}}}},
|
||||
script: "5151c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
|
||||
}, {
|
||||
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: Boolean, Value: Param{Type: StringT, Value: "false"}}}}}},
|
||||
script: "0051c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
|
||||
}}
|
||||
for _, ps := range paramScripts {
|
||||
script, err := CreateFunctionInvocationScript(contract, ps.ps)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, ps.script, hex.EncodeToString(script))
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvocationScriptCreationBad(t *testing.T) {
|
||||
contract := util.Uint160{}
|
||||
|
||||
var testParams = []Params{
|
||||
{{Type: NumberT, Value: "qwerty"}},
|
||||
{{Type: ArrayT, Value: 42}},
|
||||
{{Type: ArrayT, Value: []Param{{Type: NumberT, Value: 42}}}},
|
||||
{{Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: ByteArray, Value: Param{Type: StringT, Value: "qwerty"}}}}}},
|
||||
{{Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: Signature, Value: Param{Type: StringT, Value: "qwerty"}}}}}},
|
||||
{{Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: String, Value: Param{Type: NumberT, Value: 42}}}}}},
|
||||
{{Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: Hash160, Value: Param{Type: StringT, Value: "qwerty"}}}}}},
|
||||
{{Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: Hash256, Value: Param{Type: StringT, Value: "qwerty"}}}}}},
|
||||
{{Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: PublicKey, Value: Param{Type: NumberT, Value: 42}}}}}},
|
||||
{{Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: PublicKey, Value: Param{Type: StringT, Value: "qwerty"}}}}}},
|
||||
{{Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: Integer, Value: Param{Type: StringT, Value: "qwerty"}}}}}},
|
||||
{{Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: Boolean, Value: Param{Type: NumberT, Value: 42}}}}}},
|
||||
{{Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: Boolean, Value: Param{Type: StringT, Value: "qwerty"}}}}}},
|
||||
{{Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: Unknown, Value: Param{}}}}}},
|
||||
}
|
||||
for _, ps := range testParams {
|
||||
_, err := CreateFunctionInvocationScript(contract, ps)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
}
|
84
pkg/rpc/request/types.go
Normal file
84
pkg/rpc/request/types.go
Normal file
|
@ -0,0 +1,84 @@
|
|||
package request
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
// JSONRPCVersion is the only JSON-RPC protocol version supported.
|
||||
JSONRPCVersion = "2.0"
|
||||
)
|
||||
|
||||
// RawParams is just a slice of abstract values, used to represent parameters
|
||||
// passed from client to server.
|
||||
type RawParams struct {
|
||||
Values []interface{}
|
||||
}
|
||||
|
||||
// NewRawParams creates RawParams from its parameters.
|
||||
func NewRawParams(vals ...interface{}) RawParams {
|
||||
p := RawParams{}
|
||||
p.Values = make([]interface{}, len(vals))
|
||||
for i := 0; i < len(p.Values); i++ {
|
||||
p.Values[i] = vals[i]
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// Raw represents JSON-RPC request.
|
||||
type Raw struct {
|
||||
JSONRPC string `json:"jsonrpc"`
|
||||
Method string `json:"method"`
|
||||
RawParams []interface{} `json:"params"`
|
||||
ID int `json:"id"`
|
||||
}
|
||||
|
||||
// In represents a standard JSON-RPC 2.0
|
||||
// request: http://www.jsonrpc.org/specification#request_object. It's used in
|
||||
// server to represent incoming queries.
|
||||
type In struct {
|
||||
JSONRPC string `json:"jsonrpc"`
|
||||
Method string `json:"method"`
|
||||
RawParams json.RawMessage `json:"params,omitempty"`
|
||||
RawID json.RawMessage `json:"id,omitempty"`
|
||||
}
|
||||
|
||||
// NewIn creates a new Request struct.
|
||||
func NewIn() *In {
|
||||
return &In{
|
||||
JSONRPC: JSONRPCVersion,
|
||||
}
|
||||
}
|
||||
|
||||
// DecodeData decodes the given reader into the the request
|
||||
// struct.
|
||||
func (r *In) DecodeData(data io.ReadCloser) error {
|
||||
defer data.Close()
|
||||
|
||||
err := json.NewDecoder(data).Decode(r)
|
||||
if err != nil {
|
||||
return errors.Errorf("error parsing JSON payload: %s", err)
|
||||
}
|
||||
|
||||
if r.JSONRPC != JSONRPCVersion {
|
||||
return errors.Errorf("invalid version, expected 2.0 got: '%s'", r.JSONRPC)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Params takes a slice of any type and attempts to bind
|
||||
// the params to it.
|
||||
func (r *In) Params() (*Params, error) {
|
||||
params := Params{}
|
||||
|
||||
err := json.Unmarshal(r.RawParams, ¶ms)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("error parsing params field in payload: %s", err)
|
||||
}
|
||||
|
||||
return ¶ms, nil
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package rpc
|
||||
package response
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -18,10 +18,13 @@ type (
|
|||
)
|
||||
|
||||
var (
|
||||
errInvalidParams = NewInvalidParamsError("", nil)
|
||||
// ErrInvalidParams represents a generic 'invalid parameters' error.
|
||||
ErrInvalidParams = NewInvalidParamsError("", nil)
|
||||
)
|
||||
|
||||
func newError(code int64, httpCode int, message string, data string, cause error) *Error {
|
||||
// NewError is an Error constructor that takes Error contents from its
|
||||
// parameters.
|
||||
func NewError(code int64, httpCode int, message string, data string, cause error) *Error {
|
||||
return &Error{
|
||||
Code: code,
|
||||
HTTPCode: httpCode,
|
||||
|
@ -35,40 +38,40 @@ func newError(code int64, httpCode int, message string, data string, cause error
|
|||
// NewParseError creates a new error with code
|
||||
// -32700.:%s
|
||||
func NewParseError(data string, cause error) *Error {
|
||||
return newError(-32700, http.StatusBadRequest, "Parse Error", data, cause)
|
||||
return NewError(-32700, http.StatusBadRequest, "Parse Error", data, cause)
|
||||
}
|
||||
|
||||
// NewInvalidRequestError creates a new error with
|
||||
// code -32600.
|
||||
func NewInvalidRequestError(data string, cause error) *Error {
|
||||
return newError(-32600, http.StatusUnprocessableEntity, "Invalid Request", data, cause)
|
||||
return NewError(-32600, http.StatusUnprocessableEntity, "Invalid Request", data, cause)
|
||||
}
|
||||
|
||||
// NewMethodNotFoundError creates a new error with
|
||||
// code -32601.
|
||||
func NewMethodNotFoundError(data string, cause error) *Error {
|
||||
return newError(-32601, http.StatusMethodNotAllowed, "Method not found", data, cause)
|
||||
return NewError(-32601, http.StatusMethodNotAllowed, "Method not found", data, cause)
|
||||
}
|
||||
|
||||
// NewInvalidParamsError creates a new error with
|
||||
// code -32602.
|
||||
func NewInvalidParamsError(data string, cause error) *Error {
|
||||
return newError(-32602, http.StatusUnprocessableEntity, "Invalid Params", data, cause)
|
||||
return NewError(-32602, http.StatusUnprocessableEntity, "Invalid Params", data, cause)
|
||||
}
|
||||
|
||||
// NewInternalServerError creates a new error with
|
||||
// code -32603.
|
||||
func NewInternalServerError(data string, cause error) *Error {
|
||||
return newError(-32603, http.StatusInternalServerError, "Internal error", data, cause)
|
||||
return NewError(-32603, http.StatusInternalServerError, "Internal error", data, cause)
|
||||
}
|
||||
|
||||
// NewRPCError creates a new error with
|
||||
// code -100
|
||||
func NewRPCError(message string, data string, cause error) *Error {
|
||||
return newError(-100, http.StatusUnprocessableEntity, message, data, cause)
|
||||
return NewError(-100, http.StatusUnprocessableEntity, message, data, cause)
|
||||
}
|
||||
|
||||
// Error implements the error interface.
|
||||
func (e Error) Error() string {
|
||||
func (e *Error) Error() string {
|
||||
return fmt.Sprintf("%s (%d) - %s - %s", e.Message, e.Code, e.Data, e.Cause)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package wrappers
|
||||
package result
|
||||
|
||||
import (
|
||||
"bytes"
|
|
@ -1,4 +1,4 @@
|
|||
package wrappers
|
||||
package result
|
||||
|
||||
import (
|
||||
"github.com/CityOfZion/neo-go/pkg/core/state"
|
|
@ -1,4 +1,4 @@
|
|||
package wrappers
|
||||
package result
|
||||
|
||||
import (
|
||||
"strconv"
|
|
@ -1,4 +1,4 @@
|
|||
package wrappers
|
||||
package result
|
||||
|
||||
import (
|
||||
"github.com/CityOfZion/neo-go/pkg/core/state"
|
|
@ -1,11 +1,12 @@
|
|||
package wrappers
|
||||
package result
|
||||
|
||||
import (
|
||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
||||
)
|
||||
|
||||
// InvokeResult is used as a wrapper to represent an invokation result.
|
||||
type InvokeResult struct {
|
||||
// Invoke represents code invocation result and is used by several RPC calls
|
||||
// that invoke functions, scripts and generic bytecode.
|
||||
type Invoke struct {
|
||||
State string `json:"state"`
|
||||
GasConsumed string `json:"gas_consumed"`
|
||||
Script string `json:"script"`
|
60
pkg/rpc/response/result/peers.go
Normal file
60
pkg/rpc/response/result/peers.go
Normal file
|
@ -0,0 +1,60 @@
|
|||
package result
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type (
|
||||
// GetPeers payload for outputting peers in `getpeers` RPC call.
|
||||
GetPeers struct {
|
||||
Unconnected Peers `json:"unconnected"`
|
||||
Connected Peers `json:"connected"`
|
||||
Bad Peers `json:"bad"`
|
||||
}
|
||||
|
||||
// Peers represent a slice of peers.
|
||||
Peers []Peer
|
||||
|
||||
// Peer represents the peer.
|
||||
Peer struct {
|
||||
Address string `json:"address"`
|
||||
Port string `json:"port"`
|
||||
}
|
||||
)
|
||||
|
||||
// NewGetPeers creates a new GetPeers structure.
|
||||
func NewGetPeers() GetPeers {
|
||||
return GetPeers{
|
||||
Unconnected: []Peer{},
|
||||
Connected: []Peer{},
|
||||
Bad: []Peer{},
|
||||
}
|
||||
}
|
||||
|
||||
// AddUnconnected adds a set of peers to the unconnected peers slice.
|
||||
func (g *GetPeers) AddUnconnected(addrs []string) {
|
||||
g.Unconnected.addPeers(addrs)
|
||||
}
|
||||
|
||||
// AddConnected adds a set of peers to the connected peers slice.
|
||||
func (g *GetPeers) AddConnected(addrs []string) {
|
||||
g.Connected.addPeers(addrs)
|
||||
}
|
||||
|
||||
// AddBad adds a set of peers to the bad peers slice.
|
||||
func (g *GetPeers) AddBad(addrs []string) {
|
||||
g.Bad.addPeers(addrs)
|
||||
}
|
||||
|
||||
// addPeers adds a set of peers to the given peer slice.
|
||||
func (p *Peers) addPeers(addrs []string) {
|
||||
for i := range addrs {
|
||||
addressParts := strings.Split(addrs[i], ":")
|
||||
peer := Peer{
|
||||
Address: addressParts[0],
|
||||
Port: addressParts[1],
|
||||
}
|
||||
|
||||
*p = append(*p, peer)
|
||||
}
|
||||
}
|
26
pkg/rpc/response/result/peers_test.go
Normal file
26
pkg/rpc/response/result/peers_test.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
package result
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetPeers(t *testing.T) {
|
||||
gp := NewGetPeers()
|
||||
require.Equal(t, 0, len(gp.Unconnected))
|
||||
require.Equal(t, 0, len(gp.Connected))
|
||||
require.Equal(t, 0, len(gp.Bad))
|
||||
|
||||
gp.AddUnconnected([]string{"1.1.1.1:53", "8.8.8.8:53", "9.9.9.9:53"})
|
||||
gp.AddConnected([]string{"192.168.0.1:10333"})
|
||||
gp.AddBad([]string{"127.0.0.1:20333"})
|
||||
|
||||
require.Equal(t, 3, len(gp.Unconnected))
|
||||
require.Equal(t, 1, len(gp.Connected))
|
||||
require.Equal(t, 1, len(gp.Bad))
|
||||
require.Equal(t, "192.168.0.1", gp.Connected[0].Address)
|
||||
require.Equal(t, "10333", gp.Connected[0].Port)
|
||||
require.Equal(t, "127.0.0.1", gp.Bad[0].Address)
|
||||
require.Equal(t, "20333", gp.Bad[0].Port)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package wrappers
|
||||
package result
|
||||
|
||||
import (
|
||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
|
@ -1,4 +1,4 @@
|
|||
package wrappers
|
||||
package result
|
||||
|
||||
import (
|
||||
"github.com/CityOfZion/neo-go/pkg/core"
|
||||
|
@ -16,9 +16,9 @@ type TransactionOutputRaw struct {
|
|||
Size int `json:"size"`
|
||||
SysFee util.Fixed8 `json:"sys_fee"`
|
||||
NetFee util.Fixed8 `json:"net_fee"`
|
||||
Blockhash util.Uint256 `json:"blockhash"`
|
||||
Confirmations int `json:"confirmations"`
|
||||
Timestamp uint32 `json:"blocktime"`
|
||||
Blockhash util.Uint256 `json:"blockhash,omitempty"`
|
||||
Confirmations int `json:"confirmations,omitempty"`
|
||||
Timestamp uint32 `json:"blocktime,omitempty"`
|
||||
}
|
||||
|
||||
// NewTransactionOutputRaw returns a new ransactionOutputRaw object.
|
|
@ -1,4 +1,4 @@
|
|||
package wrappers
|
||||
package result
|
||||
|
||||
import (
|
||||
"github.com/CityOfZion/neo-go/pkg/core"
|
9
pkg/rpc/response/result/validate_address.go
Normal file
9
pkg/rpc/response/result/validate_address.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package result
|
||||
|
||||
// ValidateAddress represents result of the `validateaddress` call. Notice that
|
||||
// Address is an interface{} here because server echoes back whatever address
|
||||
// value user has sent to it, even if it's not a string.
|
||||
type ValidateAddress struct {
|
||||
Address interface{} `json:"address"`
|
||||
IsValid bool `json:"isvalid"`
|
||||
}
|
50
pkg/rpc/response/types.go
Normal file
50
pkg/rpc/response/types.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
package response
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/rpc/request"
|
||||
"github.com/CityOfZion/neo-go/pkg/rpc/response/result"
|
||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
||||
)
|
||||
|
||||
// InvokeResult represents the outcome of a script that is
|
||||
// executed by the NEO VM.
|
||||
type InvokeResult struct {
|
||||
State vm.State `json:"state"`
|
||||
GasConsumed string `json:"gas_consumed"`
|
||||
Script string `json:"script"`
|
||||
Stack []request.StackParam
|
||||
}
|
||||
|
||||
// Header is a generic JSON-RPC 2.0 response header (ID and JSON-RPC version).
|
||||
type Header struct {
|
||||
ID json.RawMessage `json:"id"`
|
||||
JSONRPC string `json:"jsonrpc"`
|
||||
}
|
||||
|
||||
// HeaderAndError adds an Error (that can be empty) to the Header, it's used
|
||||
// to construct type-specific responses.
|
||||
type HeaderAndError struct {
|
||||
Header
|
||||
Error *Error `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// Raw represents a standard raw JSON-RPC 2.0
|
||||
// response: http://www.jsonrpc.org/specification#response_object.
|
||||
type Raw struct {
|
||||
HeaderAndError
|
||||
Result json.RawMessage `json:"result,omitempty"`
|
||||
}
|
||||
|
||||
// GetTxOut represents result of `gettxout` RPC call.
|
||||
type GetTxOut struct {
|
||||
HeaderAndError
|
||||
Result *result.TransactionOutput
|
||||
}
|
||||
|
||||
// GetRawTx represents verbose output of `getrawtransaction` RPC call.
|
||||
type GetRawTx struct {
|
||||
HeaderAndError
|
||||
Result *result.TransactionOutputRaw `json:"result"`
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
package result
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type (
|
||||
// Peers payload for outputting peers in `getpeers` RPC call.
|
||||
Peers struct {
|
||||
Unconnected []Peer `json:"unconnected"`
|
||||
Connected []Peer `json:"connected"`
|
||||
Bad []Peer `json:"bad"`
|
||||
}
|
||||
|
||||
// Peer represents the peer.
|
||||
Peer struct {
|
||||
Address string `json:"address"`
|
||||
Port string `json:"port"`
|
||||
}
|
||||
)
|
||||
|
||||
// NewPeers creates a new Peers struct.
|
||||
func NewPeers() Peers {
|
||||
return Peers{
|
||||
Unconnected: []Peer{},
|
||||
Connected: []Peer{},
|
||||
Bad: []Peer{},
|
||||
}
|
||||
}
|
||||
|
||||
// AddPeer adds a peer to the given peer type slice.
|
||||
func (p *Peers) AddPeer(peerType string, addr string) {
|
||||
addressParts := strings.Split(addr, ":")
|
||||
peer := Peer{
|
||||
Address: addressParts[0],
|
||||
Port: addressParts[1],
|
||||
}
|
||||
|
||||
switch peerType {
|
||||
case "unconnected":
|
||||
p.Unconnected = append(
|
||||
p.Unconnected,
|
||||
peer,
|
||||
)
|
||||
|
||||
case "connected":
|
||||
p.Connected = append(
|
||||
p.Connected,
|
||||
peer,
|
||||
)
|
||||
|
||||
case "bad":
|
||||
p.Bad = append(
|
||||
p.Bad,
|
||||
peer,
|
||||
)
|
||||
}
|
||||
}
|
48
pkg/rpc/server/doc.go
Normal file
48
pkg/rpc/server/doc.go
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
Package server implements NEO-specific JSON-RPC 2.0 server.
|
||||
This package is currently in alpha and is subject to change.
|
||||
|
||||
Server
|
||||
|
||||
The server is written to support as much of the JSON-RPC 2.0 Spec as possible.
|
||||
The server is run as part of the node currently.
|
||||
|
||||
TODO:
|
||||
Implement HTTPS server.
|
||||
Add remaining methods (Documented below).
|
||||
Add Swagger spec and test using dredd in circleCI.
|
||||
|
||||
Example call
|
||||
|
||||
An example would be viewing the version of the node:
|
||||
|
||||
$ curl -X POST -d '{"jsonrpc": "2.0", "method": "getversion", "params": [], "id": 1}' http://localhost:20332
|
||||
|
||||
which would yield the response:
|
||||
|
||||
{
|
||||
"jsonrpc" : "2.0",
|
||||
"id" : 1,
|
||||
"result" : {
|
||||
"port" : 20333,
|
||||
"useragent" : "/NEO-GO:0.36.0-dev/",
|
||||
"nonce" : 9318417
|
||||
}
|
||||
}
|
||||
|
||||
Unsupported methods
|
||||
|
||||
getblocksysfee
|
||||
getcontractstate (needs to be implemented in pkg/core/blockchain.go)
|
||||
getrawmempool (needs to be implemented on in pkg/network/server.go)
|
||||
getrawtransaction (needs to be implemented in pkg/core/blockchain.go)
|
||||
getstorage (lacks VM functionality)
|
||||
gettxout (needs to be implemented in pkg/core/blockchain.go)
|
||||
invoke (lacks VM functionality)
|
||||
invokefunction (lacks VM functionality)
|
||||
invokescript (lacks VM functionality)
|
||||
sendrawtransaction (needs to be implemented in pkg/core/blockchain.go)
|
||||
submitblock (needs to be implemented in pkg/core/blockchain.go)
|
||||
|
||||
*/
|
||||
package server
|
|
@ -1,4 +1,4 @@
|
|||
package rpc
|
||||
package server
|
||||
|
||||
import "github.com/prometheus/client_golang/prometheus"
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
package rpc
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
@ -11,10 +12,12 @@ import (
|
|||
"github.com/CityOfZion/neo-go/pkg/core"
|
||||
"github.com/CityOfZion/neo-go/pkg/core/state"
|
||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||
"github.com/CityOfZion/neo-go/pkg/encoding/address"
|
||||
"github.com/CityOfZion/neo-go/pkg/io"
|
||||
"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/rpc/request"
|
||||
"github.com/CityOfZion/neo-go/pkg/rpc/response"
|
||||
"github.com/CityOfZion/neo-go/pkg/rpc/response/result"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
|
@ -35,8 +38,8 @@ var invalidBlockHeightError = func(index int, height int) error {
|
|||
return errors.Errorf("Param at index %d should be greater than or equal to 0 and less then or equal to current block height, got: %d", index, height)
|
||||
}
|
||||
|
||||
// NewServer creates a new Server struct.
|
||||
func NewServer(chain core.Blockchainer, conf config.RPCConfig, coreServer *network.Server, log *zap.Logger) Server {
|
||||
// New creates a new Server struct.
|
||||
func New(chain core.Blockchainer, conf config.RPCConfig, coreServer *network.Server, log *zap.Logger) Server {
|
||||
httpServer := &http.Server{
|
||||
Addr: conf.Address + ":" + strconv.FormatUint(uint64(conf.Port), 10),
|
||||
}
|
||||
|
@ -71,13 +74,13 @@ func (s *Server) Shutdown() error {
|
|||
}
|
||||
|
||||
func (s *Server) requestHandler(w http.ResponseWriter, httpRequest *http.Request) {
|
||||
req := NewRequest()
|
||||
req := request.NewIn()
|
||||
|
||||
if httpRequest.Method != "POST" {
|
||||
s.WriteErrorResponse(
|
||||
req,
|
||||
w,
|
||||
NewInvalidParamsError(
|
||||
response.NewInvalidParamsError(
|
||||
fmt.Sprintf("Invalid method '%s', please retry with 'POST'", httpRequest.Method), nil,
|
||||
),
|
||||
)
|
||||
|
@ -86,20 +89,20 @@ func (s *Server) requestHandler(w http.ResponseWriter, httpRequest *http.Request
|
|||
|
||||
err := req.DecodeData(httpRequest.Body)
|
||||
if err != nil {
|
||||
s.WriteErrorResponse(req, w, NewParseError("Problem parsing JSON-RPC request body", err))
|
||||
s.WriteErrorResponse(req, w, response.NewParseError("Problem parsing JSON-RPC request body", err))
|
||||
return
|
||||
}
|
||||
|
||||
reqParams, err := req.Params()
|
||||
if err != nil {
|
||||
s.WriteErrorResponse(req, w, NewInvalidParamsError("Problem parsing request parameters", err))
|
||||
s.WriteErrorResponse(req, w, response.NewInvalidParamsError("Problem parsing request parameters", err))
|
||||
return
|
||||
}
|
||||
|
||||
s.methodHandler(w, req, *reqParams)
|
||||
}
|
||||
|
||||
func (s *Server) methodHandler(w http.ResponseWriter, req *Request, reqParams Params) {
|
||||
func (s *Server) methodHandler(w http.ResponseWriter, req *request.In, reqParams request.Params) {
|
||||
s.log.Debug("processing rpc request",
|
||||
zap.String("method", req.Method),
|
||||
zap.String("params", fmt.Sprintf("%v", reqParams)))
|
||||
|
@ -121,38 +124,38 @@ Methods:
|
|||
|
||||
param, ok := reqParams.Value(0)
|
||||
if !ok {
|
||||
resultsErr = errInvalidParams
|
||||
resultsErr = response.ErrInvalidParams
|
||||
break Methods
|
||||
}
|
||||
|
||||
switch param.Type {
|
||||
case stringT:
|
||||
case request.StringT:
|
||||
var err error
|
||||
hash, err = param.GetUint256()
|
||||
if err != nil {
|
||||
resultsErr = errInvalidParams
|
||||
resultsErr = response.ErrInvalidParams
|
||||
break Methods
|
||||
}
|
||||
case numberT:
|
||||
case request.NumberT:
|
||||
num, err := s.blockHeightFromParam(param)
|
||||
if err != nil {
|
||||
resultsErr = errInvalidParams
|
||||
resultsErr = response.ErrInvalidParams
|
||||
break Methods
|
||||
}
|
||||
hash = s.chain.GetHeaderHash(num)
|
||||
default:
|
||||
resultsErr = errInvalidParams
|
||||
resultsErr = response.ErrInvalidParams
|
||||
break Methods
|
||||
}
|
||||
|
||||
block, err := s.chain.GetBlock(hash)
|
||||
if err != nil {
|
||||
resultsErr = NewInternalServerError(fmt.Sprintf("Problem locating block with hash: %s", hash), err)
|
||||
resultsErr = response.NewInternalServerError(fmt.Sprintf("Problem locating block with hash: %s", hash), err)
|
||||
break
|
||||
}
|
||||
|
||||
if len(reqParams) == 2 && reqParams[1].Value == 1 {
|
||||
results = wrappers.NewBlock(block, s.chain)
|
||||
results = result.NewBlock(block, s.chain)
|
||||
} else {
|
||||
writer := io.NewBufBinWriter()
|
||||
block.EncodeBinary(writer.BinWriter)
|
||||
|
@ -165,14 +168,14 @@ Methods:
|
|||
|
||||
case "getblockhash":
|
||||
getblockHashCalled.Inc()
|
||||
param, ok := reqParams.ValueWithType(0, numberT)
|
||||
param, ok := reqParams.ValueWithType(0, request.NumberT)
|
||||
if !ok {
|
||||
resultsErr = errInvalidParams
|
||||
resultsErr = response.ErrInvalidParams
|
||||
break Methods
|
||||
}
|
||||
num, err := s.blockHeightFromParam(param)
|
||||
if err != nil {
|
||||
resultsErr = errInvalidParams
|
||||
resultsErr = response.ErrInvalidParams
|
||||
break Methods
|
||||
}
|
||||
|
||||
|
@ -192,19 +195,10 @@ Methods:
|
|||
|
||||
case "getpeers":
|
||||
getpeersCalled.Inc()
|
||||
peers := result.NewPeers()
|
||||
for _, addr := range s.coreServer.UnconnectedPeers() {
|
||||
peers.AddPeer("unconnected", addr)
|
||||
}
|
||||
|
||||
for _, addr := range s.coreServer.BadPeers() {
|
||||
peers.AddPeer("bad", addr)
|
||||
}
|
||||
|
||||
for addr := range s.coreServer.Peers() {
|
||||
peers.AddPeer("connected", addr.PeerAddr().String())
|
||||
}
|
||||
|
||||
peers := result.NewGetPeers()
|
||||
peers.AddUnconnected(s.coreServer.UnconnectedPeers())
|
||||
peers.AddConnected(s.coreServer.ConnectedPeers())
|
||||
peers.AddBad(s.coreServer.BadPeers())
|
||||
results = peers
|
||||
|
||||
case "getstorage":
|
||||
|
@ -215,30 +209,30 @@ Methods:
|
|||
validateaddressCalled.Inc()
|
||||
param, ok := reqParams.Value(0)
|
||||
if !ok {
|
||||
resultsErr = errInvalidParams
|
||||
resultsErr = response.ErrInvalidParams
|
||||
break Methods
|
||||
}
|
||||
results = wrappers.ValidateAddress(param.Value)
|
||||
results = validateAddress(param.Value)
|
||||
|
||||
case "getassetstate":
|
||||
getassetstateCalled.Inc()
|
||||
param, ok := reqParams.ValueWithType(0, stringT)
|
||||
param, ok := reqParams.ValueWithType(0, request.StringT)
|
||||
if !ok {
|
||||
resultsErr = errInvalidParams
|
||||
resultsErr = response.ErrInvalidParams
|
||||
break Methods
|
||||
}
|
||||
|
||||
paramAssetID, err := param.GetUint256()
|
||||
if err != nil {
|
||||
resultsErr = errInvalidParams
|
||||
resultsErr = response.ErrInvalidParams
|
||||
break
|
||||
}
|
||||
|
||||
as := s.chain.GetAssetState(paramAssetID)
|
||||
if as != nil {
|
||||
results = wrappers.NewAssetState(as)
|
||||
results = result.NewAssetState(as)
|
||||
} else {
|
||||
resultsErr = NewRPCError("Unknown asset", "", nil)
|
||||
resultsErr = response.NewRPCError("Unknown asset", "", nil)
|
||||
}
|
||||
|
||||
case "getaccountstate":
|
||||
|
@ -275,7 +269,7 @@ Methods:
|
|||
results, resultsErr = s.sendrawtransaction(reqParams)
|
||||
|
||||
default:
|
||||
resultsErr = NewMethodNotFoundError(fmt.Sprintf("Method '%s' not supported", req.Method), nil)
|
||||
resultsErr = response.NewMethodNotFoundError(fmt.Sprintf("Method '%s' not supported", req.Method), nil)
|
||||
}
|
||||
|
||||
if resultsErr != nil {
|
||||
|
@ -286,27 +280,27 @@ Methods:
|
|||
s.WriteResponse(req, w, results)
|
||||
}
|
||||
|
||||
func (s *Server) getStorage(ps Params) (interface{}, error) {
|
||||
func (s *Server) getStorage(ps request.Params) (interface{}, error) {
|
||||
param, ok := ps.Value(0)
|
||||
if !ok {
|
||||
return nil, errInvalidParams
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
|
||||
scriptHash, err := param.GetUint160FromHex()
|
||||
if err != nil {
|
||||
return nil, errInvalidParams
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
|
||||
scriptHash = scriptHash.Reverse()
|
||||
|
||||
param, ok = ps.Value(1)
|
||||
if !ok {
|
||||
return nil, errInvalidParams
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
|
||||
key, err := param.GetBytesHex()
|
||||
if err != nil {
|
||||
return nil, errInvalidParams
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
|
||||
item := s.chain.GetStorageItem(scriptHash.Reverse(), key)
|
||||
|
@ -317,22 +311,22 @@ func (s *Server) getStorage(ps Params) (interface{}, error) {
|
|||
return hex.EncodeToString(item.Value), nil
|
||||
}
|
||||
|
||||
func (s *Server) getrawtransaction(reqParams Params) (interface{}, error) {
|
||||
func (s *Server) getrawtransaction(reqParams request.Params) (interface{}, error) {
|
||||
var resultsErr error
|
||||
var results interface{}
|
||||
|
||||
if param0, ok := reqParams.Value(0); !ok {
|
||||
return nil, errInvalidParams
|
||||
return nil, response.ErrInvalidParams
|
||||
} else if txHash, err := param0.GetUint256(); err != nil {
|
||||
resultsErr = errInvalidParams
|
||||
resultsErr = response.ErrInvalidParams
|
||||
} else if tx, height, err := s.chain.GetTransaction(txHash); err != nil {
|
||||
err = errors.Wrapf(err, "Invalid transaction hash: %s", txHash)
|
||||
return nil, NewRPCError("Unknown transaction", err.Error(), err)
|
||||
return nil, response.NewRPCError("Unknown transaction", err.Error(), err)
|
||||
} else if len(reqParams) >= 2 {
|
||||
_header := s.chain.GetHeaderHash(int(height))
|
||||
header, err := s.chain.GetHeader(_header)
|
||||
if err != nil {
|
||||
resultsErr = NewInvalidParamsError(err.Error(), err)
|
||||
resultsErr = response.NewInvalidParamsError(err.Error(), err)
|
||||
}
|
||||
|
||||
param1, _ := reqParams.Value(1)
|
||||
|
@ -342,10 +336,10 @@ func (s *Server) getrawtransaction(reqParams Params) (interface{}, error) {
|
|||
if v == 0 || v == "0" || v == 0.0 || v == false || v == "false" {
|
||||
results = hex.EncodeToString(tx.Bytes())
|
||||
} else {
|
||||
results = wrappers.NewTransactionOutputRaw(tx, header, s.chain)
|
||||
results = result.NewTransactionOutputRaw(tx, header, s.chain)
|
||||
}
|
||||
default:
|
||||
results = wrappers.NewTransactionOutputRaw(tx, header, s.chain)
|
||||
results = result.NewTransactionOutputRaw(tx, header, s.chain)
|
||||
}
|
||||
} else {
|
||||
results = hex.EncodeToString(tx.Bytes())
|
||||
|
@ -354,70 +348,70 @@ func (s *Server) getrawtransaction(reqParams Params) (interface{}, error) {
|
|||
return results, resultsErr
|
||||
}
|
||||
|
||||
func (s *Server) getTxOut(ps Params) (interface{}, error) {
|
||||
func (s *Server) getTxOut(ps request.Params) (interface{}, error) {
|
||||
p, ok := ps.Value(0)
|
||||
if !ok {
|
||||
return nil, errInvalidParams
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
|
||||
h, err := p.GetUint256()
|
||||
if err != nil {
|
||||
return nil, errInvalidParams
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
|
||||
p, ok = ps.ValueWithType(1, numberT)
|
||||
p, ok = ps.ValueWithType(1, request.NumberT)
|
||||
if !ok {
|
||||
return nil, errInvalidParams
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
|
||||
num, err := p.GetInt()
|
||||
if err != nil || num < 0 {
|
||||
return nil, errInvalidParams
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
|
||||
tx, _, err := s.chain.GetTransaction(h)
|
||||
if err != nil {
|
||||
return nil, NewInvalidParamsError(err.Error(), err)
|
||||
return nil, response.NewInvalidParamsError(err.Error(), err)
|
||||
}
|
||||
|
||||
if num >= len(tx.Outputs) {
|
||||
return nil, NewInvalidParamsError("invalid index", errors.New("too big index"))
|
||||
return nil, response.NewInvalidParamsError("invalid index", errors.New("too big index"))
|
||||
}
|
||||
|
||||
out := tx.Outputs[num]
|
||||
return wrappers.NewTxOutput(&out), nil
|
||||
return result.NewTxOutput(&out), nil
|
||||
}
|
||||
|
||||
// getContractState returns contract state (contract information, according to the contract script hash).
|
||||
func (s *Server) getContractState(reqParams Params) (interface{}, error) {
|
||||
func (s *Server) getContractState(reqParams request.Params) (interface{}, error) {
|
||||
var results interface{}
|
||||
|
||||
param, ok := reqParams.ValueWithType(0, stringT)
|
||||
param, ok := reqParams.ValueWithType(0, request.StringT)
|
||||
if !ok {
|
||||
return nil, errInvalidParams
|
||||
return nil, response.ErrInvalidParams
|
||||
} else if scriptHash, err := param.GetUint160FromHex(); err != nil {
|
||||
return nil, errInvalidParams
|
||||
return nil, response.ErrInvalidParams
|
||||
} else {
|
||||
cs := s.chain.GetContractState(scriptHash)
|
||||
if cs != nil {
|
||||
results = wrappers.NewContractState(cs)
|
||||
results = result.NewContractState(cs)
|
||||
} else {
|
||||
return nil, NewRPCError("Unknown contract", "", nil)
|
||||
return nil, response.NewRPCError("Unknown contract", "", nil)
|
||||
}
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// getAccountState returns account state either in short or full (unspents included) form.
|
||||
func (s *Server) getAccountState(reqParams Params, unspents bool) (interface{}, error) {
|
||||
func (s *Server) getAccountState(reqParams request.Params, unspents bool) (interface{}, error) {
|
||||
var resultsErr error
|
||||
var results interface{}
|
||||
|
||||
param, ok := reqParams.ValueWithType(0, stringT)
|
||||
param, ok := reqParams.ValueWithType(0, request.StringT)
|
||||
if !ok {
|
||||
return nil, errInvalidParams
|
||||
return nil, response.ErrInvalidParams
|
||||
} else if scriptHash, err := param.GetUint160FromAddress(); err != nil {
|
||||
return nil, errInvalidParams
|
||||
return nil, response.ErrInvalidParams
|
||||
} else {
|
||||
as := s.chain.GetAccountState(scriptHash)
|
||||
if as == nil {
|
||||
|
@ -426,35 +420,35 @@ func (s *Server) getAccountState(reqParams Params, unspents bool) (interface{},
|
|||
if unspents {
|
||||
str, err := param.GetString()
|
||||
if err != nil {
|
||||
return nil, errInvalidParams
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
results = wrappers.NewUnspents(as, s.chain, str)
|
||||
results = result.NewUnspents(as, s.chain, str)
|
||||
} else {
|
||||
results = wrappers.NewAccountState(as)
|
||||
results = result.NewAccountState(as)
|
||||
}
|
||||
}
|
||||
return results, resultsErr
|
||||
}
|
||||
|
||||
// invoke implements the `invoke` RPC call.
|
||||
func (s *Server) invoke(reqParams Params) (interface{}, error) {
|
||||
scriptHashHex, ok := reqParams.ValueWithType(0, stringT)
|
||||
func (s *Server) invoke(reqParams request.Params) (interface{}, error) {
|
||||
scriptHashHex, ok := reqParams.ValueWithType(0, request.StringT)
|
||||
if !ok {
|
||||
return nil, errInvalidParams
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
scriptHash, err := scriptHashHex.GetUint160FromHex()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sliceP, ok := reqParams.ValueWithType(1, arrayT)
|
||||
sliceP, ok := reqParams.ValueWithType(1, request.ArrayT)
|
||||
if !ok {
|
||||
return nil, errInvalidParams
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
slice, err := sliceP.GetArray()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
script, err := CreateInvocationScript(scriptHash, slice)
|
||||
script, err := request.CreateInvocationScript(scriptHash, slice)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -462,16 +456,16 @@ func (s *Server) invoke(reqParams Params) (interface{}, error) {
|
|||
}
|
||||
|
||||
// invokescript implements the `invokescript` RPC call.
|
||||
func (s *Server) invokeFunction(reqParams Params) (interface{}, error) {
|
||||
scriptHashHex, ok := reqParams.ValueWithType(0, stringT)
|
||||
func (s *Server) invokeFunction(reqParams request.Params) (interface{}, error) {
|
||||
scriptHashHex, ok := reqParams.ValueWithType(0, request.StringT)
|
||||
if !ok {
|
||||
return nil, errInvalidParams
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
scriptHash, err := scriptHashHex.GetUint160FromHex()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
script, err := CreateFunctionInvocationScript(scriptHash, reqParams[1:])
|
||||
script, err := request.CreateFunctionInvocationScript(scriptHash, reqParams[1:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -479,14 +473,14 @@ func (s *Server) invokeFunction(reqParams Params) (interface{}, error) {
|
|||
}
|
||||
|
||||
// invokescript implements the `invokescript` RPC call.
|
||||
func (s *Server) invokescript(reqParams Params) (interface{}, error) {
|
||||
func (s *Server) invokescript(reqParams request.Params) (interface{}, error) {
|
||||
if len(reqParams) < 1 {
|
||||
return nil, errInvalidParams
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
|
||||
script, err := reqParams[0].GetBytesHex()
|
||||
if err != nil {
|
||||
return nil, errInvalidParams
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
|
||||
return s.runScriptInVM(script), nil
|
||||
|
@ -494,12 +488,12 @@ func (s *Server) invokescript(reqParams Params) (interface{}, error) {
|
|||
|
||||
// runScriptInVM runs given script in a new test VM and returns the invocation
|
||||
// result.
|
||||
func (s *Server) runScriptInVM(script []byte) *wrappers.InvokeResult {
|
||||
func (s *Server) runScriptInVM(script []byte) *result.Invoke {
|
||||
vm, _ := s.chain.GetTestVM()
|
||||
vm.SetGasLimit(s.config.MaxGasInvoke)
|
||||
vm.LoadScript(script)
|
||||
_ = vm.Run()
|
||||
result := &wrappers.InvokeResult{
|
||||
result := &result.Invoke{
|
||||
State: vm.State(),
|
||||
GasConsumed: vm.GasConsumed().String(),
|
||||
Script: hex.EncodeToString(script),
|
||||
|
@ -508,14 +502,14 @@ func (s *Server) runScriptInVM(script []byte) *wrappers.InvokeResult {
|
|||
return result
|
||||
}
|
||||
|
||||
func (s *Server) sendrawtransaction(reqParams Params) (interface{}, error) {
|
||||
func (s *Server) sendrawtransaction(reqParams request.Params) (interface{}, error) {
|
||||
var resultsErr error
|
||||
var results interface{}
|
||||
|
||||
if len(reqParams) < 1 {
|
||||
return nil, errInvalidParams
|
||||
return nil, response.ErrInvalidParams
|
||||
} else if byteTx, err := reqParams[0].GetBytesHex(); err != nil {
|
||||
return nil, errInvalidParams
|
||||
return nil, response.ErrInvalidParams
|
||||
} else {
|
||||
r := io.NewBinReaderFromBuf(byteTx)
|
||||
tx := &transaction.Transaction{}
|
||||
|
@ -542,14 +536,14 @@ func (s *Server) sendrawtransaction(reqParams Params) (interface{}, error) {
|
|||
}
|
||||
}
|
||||
if err != nil {
|
||||
resultsErr = NewInternalServerError(err.Error(), err)
|
||||
resultsErr = response.NewInternalServerError(err.Error(), err)
|
||||
}
|
||||
}
|
||||
|
||||
return results, resultsErr
|
||||
}
|
||||
|
||||
func (s Server) blockHeightFromParam(param *Param) (int, error) {
|
||||
func (s *Server) blockHeightFromParam(param *request.Param) (int, error) {
|
||||
num, err := param.GetInt()
|
||||
if err != nil {
|
||||
return 0, nil
|
||||
|
@ -560,3 +554,87 @@ func (s Server) blockHeightFromParam(param *Param) (int, error) {
|
|||
}
|
||||
return num, nil
|
||||
}
|
||||
|
||||
// WriteErrorResponse writes an error response to the ResponseWriter.
|
||||
func (s *Server) WriteErrorResponse(r *request.In, w http.ResponseWriter, err error) {
|
||||
jsonErr, ok := err.(*response.Error)
|
||||
if !ok {
|
||||
jsonErr = response.NewInternalServerError("Internal server error", err)
|
||||
}
|
||||
|
||||
resp := response.Raw{
|
||||
HeaderAndError: response.HeaderAndError{
|
||||
Header: response.Header{
|
||||
JSONRPC: r.JSONRPC,
|
||||
ID: r.RawID,
|
||||
},
|
||||
Error: jsonErr,
|
||||
},
|
||||
}
|
||||
|
||||
logFields := []zap.Field{
|
||||
zap.Error(jsonErr.Cause),
|
||||
zap.String("method", r.Method),
|
||||
}
|
||||
|
||||
params, err := r.Params()
|
||||
if err == nil {
|
||||
logFields = append(logFields, zap.Any("params", params))
|
||||
}
|
||||
|
||||
s.log.Error("Error encountered with rpc request", logFields...)
|
||||
|
||||
w.WriteHeader(jsonErr.HTTPCode)
|
||||
s.writeServerResponse(r, w, resp)
|
||||
}
|
||||
|
||||
// WriteResponse encodes the response and writes it to the ResponseWriter.
|
||||
func (s *Server) WriteResponse(r *request.In, w http.ResponseWriter, result interface{}) {
|
||||
resJSON, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
s.log.Error("Error encountered while encoding response",
|
||||
zap.String("err", err.Error()),
|
||||
zap.String("method", r.Method))
|
||||
return
|
||||
}
|
||||
|
||||
resp := response.Raw{
|
||||
HeaderAndError: response.HeaderAndError{
|
||||
Header: response.Header{
|
||||
JSONRPC: r.JSONRPC,
|
||||
ID: r.RawID,
|
||||
},
|
||||
},
|
||||
Result: resJSON,
|
||||
}
|
||||
|
||||
s.writeServerResponse(r, w, resp)
|
||||
}
|
||||
|
||||
func (s *Server) writeServerResponse(r *request.In, w http.ResponseWriter, resp response.Raw) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
if s.config.EnableCORSWorkaround {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With")
|
||||
}
|
||||
|
||||
encoder := json.NewEncoder(w)
|
||||
err := encoder.Encode(resp)
|
||||
|
||||
if err != nil {
|
||||
s.log.Error("Error encountered while encoding response",
|
||||
zap.String("err", err.Error()),
|
||||
zap.String("method", r.Method))
|
||||
}
|
||||
}
|
||||
|
||||
// validateAddress verifies that the address is a correct NEO address
|
||||
// see https://docs.neo.org/en-us/node/cli/2.9.4/api/validateaddress.html
|
||||
func validateAddress(addr interface{}) result.ValidateAddress {
|
||||
resp := result.ValidateAddress{Address: addr}
|
||||
if addr, ok := addr.(string); ok {
|
||||
_, err := address.StringToUint160(addr)
|
||||
resp.IsValid = (err == nil)
|
||||
}
|
||||
return resp
|
||||
}
|
68
pkg/rpc/server/server_helper_test.go
Normal file
68
pkg/rpc/server/server_helper_test.go
Normal file
|
@ -0,0 +1,68 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/CityOfZion/neo-go/config"
|
||||
"github.com/CityOfZion/neo-go/pkg/core"
|
||||
"github.com/CityOfZion/neo-go/pkg/core/block"
|
||||
"github.com/CityOfZion/neo-go/pkg/core/storage"
|
||||
"github.com/CityOfZion/neo-go/pkg/io"
|
||||
"github.com/CityOfZion/neo-go/pkg/network"
|
||||
"github.com/CityOfZion/neo-go/pkg/rpc/request"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap/zaptest"
|
||||
)
|
||||
|
||||
// InvokeFunctionResult struct for testing.
|
||||
type InvokeFunctionResult struct {
|
||||
Script string `json:"script"`
|
||||
State string `json:"state"`
|
||||
GasConsumed string `json:"gas_consumed"`
|
||||
Stack []request.FuncParam `json:"stack"`
|
||||
TX string `json:"tx,omitempty"`
|
||||
}
|
||||
|
||||
func initServerWithInMemoryChain(t *testing.T) (*core.Blockchain, http.HandlerFunc) {
|
||||
var nBlocks uint32
|
||||
|
||||
net := config.ModeUnitTestNet
|
||||
configPath := "../../../config"
|
||||
cfg, err := config.Load(configPath, net)
|
||||
require.NoError(t, err, "could not load config")
|
||||
|
||||
memoryStore := storage.NewMemoryStore()
|
||||
logger := zaptest.NewLogger(t)
|
||||
chain, err := core.NewBlockchain(memoryStore, cfg.ProtocolConfiguration, logger)
|
||||
require.NoError(t, err, "could not create chain")
|
||||
|
||||
go chain.Run()
|
||||
|
||||
// File "./testdata/testblocks.acc" was generated by function core._
|
||||
// ("neo-go/pkg/core/helper_test.go").
|
||||
// To generate new "./testdata/testblocks.acc", follow the steps:
|
||||
// 1. Rename the function
|
||||
// 2. Add specific test-case into "neo-go/pkg/core/blockchain_test.go"
|
||||
// 3. Run tests with `$ make test`
|
||||
f, err := os.Open("testdata/testblocks.acc")
|
||||
require.Nil(t, err)
|
||||
br := io.NewBinReaderFromIO(f)
|
||||
nBlocks = br.ReadU32LE()
|
||||
require.Nil(t, br.Err)
|
||||
for i := 0; i < int(nBlocks); i++ {
|
||||
b := &block.Block{}
|
||||
b.DecodeBinary(br)
|
||||
require.Nil(t, br.Err)
|
||||
require.NoError(t, chain.AddBlock(b))
|
||||
}
|
||||
|
||||
serverConfig := network.NewServerConfig(cfg)
|
||||
server, err := network.NewServer(serverConfig, chain, logger)
|
||||
require.NoError(t, err)
|
||||
rpcServer := New(chain, cfg.ApplicationConfiguration.RPC, server, logger)
|
||||
handler := http.HandlerFunc(rpcServer.requestHandler)
|
||||
|
||||
return chain, handler
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package rpc
|
||||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -14,7 +14,8 @@ import (
|
|||
|
||||
"github.com/CityOfZion/neo-go/pkg/core"
|
||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||
"github.com/CityOfZion/neo-go/pkg/rpc/wrappers"
|
||||
"github.com/CityOfZion/neo-go/pkg/rpc/response"
|
||||
"github.com/CityOfZion/neo-go/pkg/rpc/response/result"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -43,23 +44,23 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
{
|
||||
name: "positive",
|
||||
params: `["AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU"]`,
|
||||
result: func(e *executor) interface{} { return &GetAccountStateResponse{} },
|
||||
check: func(t *testing.T, e *executor, result interface{}) {
|
||||
res, ok := result.(*GetAccountStateResponse)
|
||||
result: func(e *executor) interface{} { return &result.AccountState{} },
|
||||
check: func(t *testing.T, e *executor, acc interface{}) {
|
||||
res, ok := acc.(*result.AccountState)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, 1, len(res.Result.Balances))
|
||||
assert.Equal(t, false, res.Result.Frozen)
|
||||
assert.Equal(t, 1, len(res.Balances))
|
||||
assert.Equal(t, false, res.IsFrozen)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive null",
|
||||
params: `["AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"]`,
|
||||
result: func(e *executor) interface{} { return &GetAccountStateResponse{} },
|
||||
check: func(t *testing.T, e *executor, result interface{}) {
|
||||
res, ok := result.(*GetAccountStateResponse)
|
||||
result: func(e *executor) interface{} { return &result.AccountState{} },
|
||||
check: func(t *testing.T, e *executor, acc interface{}) {
|
||||
res, ok := acc.(*result.AccountState)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, 0, len(res.Result.Balances))
|
||||
assert.Equal(t, false, res.Result.Frozen)
|
||||
assert.Equal(t, 0, len(res.Balances))
|
||||
assert.Equal(t, false, res.IsFrozen)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -77,13 +78,13 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
{
|
||||
name: "positive",
|
||||
params: `["1a696b32e239dd5eace3f025cac0a193a5746a27"]`,
|
||||
result: func(e *executor) interface{} { return &GetContractStateResponce{} },
|
||||
check: func(t *testing.T, e *executor, result interface{}) {
|
||||
res, ok := result.(*GetContractStateResponce)
|
||||
result: func(e *executor) interface{} { return &result.ContractState{} },
|
||||
check: func(t *testing.T, e *executor, cs interface{}) {
|
||||
res, ok := cs.(*result.ContractState)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, byte(0), res.Result.Version)
|
||||
assert.Equal(t, util.Uint160{0x1a, 0x69, 0x6b, 0x32, 0xe2, 0x39, 0xdd, 0x5e, 0xac, 0xe3, 0xf0, 0x25, 0xca, 0xc0, 0xa1, 0x93, 0xa5, 0x74, 0x6a, 0x27}, res.Result.ScriptHash)
|
||||
assert.Equal(t, "0.99", res.Result.CodeVersion)
|
||||
assert.Equal(t, byte(0), res.Version)
|
||||
assert.Equal(t, util.Uint160{0x1a, 0x69, 0x6b, 0x32, 0xe2, 0x39, 0xdd, 0x5e, 0xac, 0xe3, 0xf0, 0x25, 0xca, 0xc0, 0xa1, 0x93, 0xa5, 0x74, 0x6a, 0x27}, res.ScriptHash)
|
||||
assert.Equal(t, "0.99", res.CodeVersion)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -106,21 +107,17 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
{
|
||||
name: "positive",
|
||||
params: `["1a696b32e239dd5eace3f025cac0a193a5746a27", "746573746b6579"]`,
|
||||
result: func(e *executor) interface{} { return &StringResultResponse{} },
|
||||
check: func(t *testing.T, e *executor, result interface{}) {
|
||||
res, ok := result.(*StringResultResponse)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, hex.EncodeToString([]byte("testvalue")), res.Result)
|
||||
result: func(e *executor) interface{} {
|
||||
v := hex.EncodeToString([]byte("testvalue"))
|
||||
return &v
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "missing key",
|
||||
params: `["1a696b32e239dd5eace3f025cac0a193a5746a27", "7465"]`,
|
||||
result: func(e *executor) interface{} { return &StringResultResponse{} },
|
||||
check: func(t *testing.T, e *executor, result interface{}) {
|
||||
res, ok := result.(*StringResultResponse)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "", res.Result)
|
||||
result: func(e *executor) interface{} {
|
||||
v := ""
|
||||
return &v
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -148,12 +145,12 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
{
|
||||
name: "positive",
|
||||
params: `["602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"]`,
|
||||
result: func(e *executor) interface{} { return &GetAssetResponse{} },
|
||||
check: func(t *testing.T, e *executor, result interface{}) {
|
||||
res, ok := result.(*GetAssetResponse)
|
||||
result: func(e *executor) interface{} { return &result.AssetState{} },
|
||||
check: func(t *testing.T, e *executor, as interface{}) {
|
||||
res, ok := as.(*result.AssetState)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "00", res.Result.Owner)
|
||||
assert.Equal(t, "AWKECj9RD8rS8RPcpCgYVjk1DeYyHwxZm3", res.Result.Admin)
|
||||
assert.Equal(t, "00", res.Owner)
|
||||
assert.Equal(t, "AWKECj9RD8rS8RPcpCgYVjk1DeYyHwxZm3", res.Admin)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -176,7 +173,8 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
{
|
||||
params: "[]",
|
||||
result: func(e *executor) interface{} {
|
||||
return "0x" + e.chain.CurrentBlockHash().StringLE()
|
||||
v := "0x" + e.chain.CurrentBlockHash().StringLE()
|
||||
return &v
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -220,17 +218,17 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
{
|
||||
name: "positive",
|
||||
params: "[1, 1]",
|
||||
result: func(e *executor) interface{} { return &GetBlockResponse{} },
|
||||
check: func(t *testing.T, e *executor, result interface{}) {
|
||||
res, ok := result.(*GetBlockResponse)
|
||||
result: func(e *executor) interface{} { return &result.Block{} },
|
||||
check: func(t *testing.T, e *executor, blockRes interface{}) {
|
||||
res, ok := blockRes.(*result.Block)
|
||||
require.True(t, ok)
|
||||
|
||||
block, err := e.chain.GetBlock(e.chain.GetHeaderHash(1))
|
||||
require.NoErrorf(t, err, "could not get block")
|
||||
|
||||
assert.Equal(t, block.Hash(), res.Result.Hash)
|
||||
for i := range res.Result.Tx {
|
||||
tx := res.Result.Tx[i]
|
||||
assert.Equal(t, block.Hash(), res.Hash)
|
||||
for i := range res.Tx {
|
||||
tx := res.Tx[i]
|
||||
require.Equal(t, transaction.MinerType, tx.Type)
|
||||
|
||||
miner, ok := block.Transactions[i].Data.(*transaction.MinerTX)
|
||||
|
@ -269,22 +267,21 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
"getblockcount": {
|
||||
{
|
||||
params: "[]",
|
||||
result: func(e *executor) interface{} { return int(e.chain.BlockHeight() + 1) },
|
||||
result: func(e *executor) interface{} {
|
||||
v := int(e.chain.BlockHeight() + 1)
|
||||
return &v
|
||||
},
|
||||
},
|
||||
},
|
||||
"getblockhash": {
|
||||
{
|
||||
params: "[1]",
|
||||
result: func(e *executor) interface{} { return "" },
|
||||
check: func(t *testing.T, e *executor, result interface{}) {
|
||||
res, ok := result.(*StringResultResponse)
|
||||
require.True(t, ok)
|
||||
|
||||
block, err := e.chain.GetBlock(e.chain.GetHeaderHash(1))
|
||||
require.NoErrorf(t, err, "could not get block")
|
||||
|
||||
result: func(e *executor) interface{} {
|
||||
// We don't have `t` here for proper handling, but
|
||||
// error here would lead to panic down below.
|
||||
block, _ := e.chain.GetBlock(e.chain.GetHeaderHash(1))
|
||||
expectedHash := "0x" + block.Hash().StringLE()
|
||||
assert.Equal(t, expectedHash, res.Result)
|
||||
return &expectedHash
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -301,25 +298,20 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
"getconnectioncount": {
|
||||
{
|
||||
params: "[]",
|
||||
result: func(*executor) interface{} { return 0 },
|
||||
result: func(*executor) interface{} {
|
||||
v := 0
|
||||
return &v
|
||||
},
|
||||
},
|
||||
},
|
||||
"getpeers": {
|
||||
{
|
||||
params: "[]",
|
||||
result: func(*executor) interface{} {
|
||||
return &GetPeersResponse{
|
||||
Jsonrpc: defaultJSONRPC,
|
||||
Result: struct {
|
||||
Unconnected []int `json:"unconnected"`
|
||||
Connected []int `json:"connected"`
|
||||
Bad []int `json:"bad"`
|
||||
}{
|
||||
Unconnected: []int{},
|
||||
Connected: []int{},
|
||||
Bad: []int{},
|
||||
},
|
||||
ID: defaultID,
|
||||
return &result.GetPeers{
|
||||
Unconnected: []result.Peer{},
|
||||
Connected: []result.Peer{},
|
||||
Bad: []result.Peer{},
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -345,33 +337,33 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
{
|
||||
name: "positive",
|
||||
params: `["AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU"]`,
|
||||
result: func(e *executor) interface{} { return &GetUnspents{} },
|
||||
check: func(t *testing.T, e *executor, result interface{}) {
|
||||
res, ok := result.(*GetUnspents)
|
||||
result: func(e *executor) interface{} { return &result.Unspents{} },
|
||||
check: func(t *testing.T, e *executor, unsp interface{}) {
|
||||
res, ok := unsp.(*result.Unspents)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, 1, len(res.Result.Balance))
|
||||
assert.Equal(t, 1, len(res.Result.Balance[0].Unspents))
|
||||
require.Equal(t, 1, len(res.Balance))
|
||||
assert.Equal(t, 1, len(res.Balance[0].Unspents))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive null",
|
||||
params: `["AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"]`,
|
||||
result: func(e *executor) interface{} { return &GetUnspents{} },
|
||||
check: func(t *testing.T, e *executor, result interface{}) {
|
||||
res, ok := result.(*GetUnspents)
|
||||
result: func(e *executor) interface{} { return &result.Unspents{} },
|
||||
check: func(t *testing.T, e *executor, unsp interface{}) {
|
||||
res, ok := unsp.(*result.Unspents)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, 0, len(res.Result.Balance))
|
||||
require.Equal(t, 0, len(res.Balance))
|
||||
},
|
||||
},
|
||||
},
|
||||
"getversion": {
|
||||
{
|
||||
params: "[]",
|
||||
result: func(*executor) interface{} { return &GetVersionResponse{} },
|
||||
check: func(t *testing.T, e *executor, result interface{}) {
|
||||
resp, ok := result.(*GetVersionResponse)
|
||||
result: func(*executor) interface{} { return &result.Version{} },
|
||||
check: func(t *testing.T, e *executor, ver interface{}) {
|
||||
resp, ok := ver.(*result.Version)
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "/NEO-GO:/", resp.Result.UserAgent)
|
||||
require.Equal(t, "/NEO-GO:/", resp.UserAgent)
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -379,13 +371,13 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
{
|
||||
name: "positive",
|
||||
params: `["50befd26fdf6e4d957c11e078b24ebce6291456f", [{"type": "String", "value": "qwerty"}]]`,
|
||||
result: func(e *executor) interface{} { return &InvokeFunctionResponse{} },
|
||||
check: func(t *testing.T, e *executor, result interface{}) {
|
||||
res, ok := result.(*InvokeFunctionResponse)
|
||||
result: func(e *executor) interface{} { return &InvokeFunctionResult{} },
|
||||
check: func(t *testing.T, e *executor, inv interface{}) {
|
||||
res, ok := inv.(*InvokeFunctionResult)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "06717765727479676f459162ceeb248b071ec157d9e4f6fd26fdbe50", res.Result.Script)
|
||||
assert.NotEqual(t, "", res.Result.State)
|
||||
assert.NotEqual(t, 0, res.Result.GasConsumed)
|
||||
assert.Equal(t, "06717765727479676f459162ceeb248b071ec157d9e4f6fd26fdbe50", res.Script)
|
||||
assert.NotEqual(t, "", res.State)
|
||||
assert.NotEqual(t, 0, res.GasConsumed)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -418,13 +410,13 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
{
|
||||
name: "positive",
|
||||
params: `["50befd26fdf6e4d957c11e078b24ebce6291456f", "test", []]`,
|
||||
result: func(e *executor) interface{} { return &InvokeFunctionResponse{} },
|
||||
check: func(t *testing.T, e *executor, result interface{}) {
|
||||
res, ok := result.(*InvokeFunctionResponse)
|
||||
result: func(e *executor) interface{} { return &InvokeFunctionResult{} },
|
||||
check: func(t *testing.T, e *executor, inv interface{}) {
|
||||
res, ok := inv.(*InvokeFunctionResult)
|
||||
require.True(t, ok)
|
||||
assert.NotEqual(t, "", res.Result.Script)
|
||||
assert.NotEqual(t, "", res.Result.State)
|
||||
assert.NotEqual(t, 0, res.Result.GasConsumed)
|
||||
assert.NotEqual(t, "", res.Script)
|
||||
assert.NotEqual(t, "", res.State)
|
||||
assert.NotEqual(t, 0, res.GasConsumed)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -452,13 +444,13 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
{
|
||||
name: "positive",
|
||||
params: `["51c56b0d48656c6c6f2c20776f726c6421680f4e656f2e52756e74696d652e4c6f67616c7566"]`,
|
||||
result: func(e *executor) interface{} { return &InvokeFunctionResponse{} },
|
||||
check: func(t *testing.T, e *executor, result interface{}) {
|
||||
res, ok := result.(*InvokeFunctionResponse)
|
||||
result: func(e *executor) interface{} { return &InvokeFunctionResult{} },
|
||||
check: func(t *testing.T, e *executor, inv interface{}) {
|
||||
res, ok := inv.(*InvokeFunctionResult)
|
||||
require.True(t, ok)
|
||||
assert.NotEqual(t, "", res.Result.Script)
|
||||
assert.NotEqual(t, "", res.Result.State)
|
||||
assert.NotEqual(t, 0, res.Result.GasConsumed)
|
||||
assert.NotEqual(t, "", res.Script)
|
||||
assert.NotEqual(t, "", res.State)
|
||||
assert.NotEqual(t, 0, res.GasConsumed)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -481,11 +473,9 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
{
|
||||
name: "positive",
|
||||
params: `["d1001b00046e616d6567d3d8602814a429a91afdbaa3914884a1c90c733101201cc9c05cefffe6cdd7b182816a9152ec218d2ec000000141403387ef7940a5764259621e655b3c621a6aafd869a611ad64adcc364d8dd1edf84e00a7f8b11b630a377eaef02791d1c289d711c08b7ad04ff0d6c9caca22cfe6232103cbb45da6072c14761c9da545749d9cfd863f860c351066d16df480602a2024c6ac"]`,
|
||||
result: func(e *executor) interface{} { return &SendTXResponse{} },
|
||||
check: func(t *testing.T, e *executor, result interface{}) {
|
||||
res, ok := result.(*SendTXResponse)
|
||||
require.True(t, ok)
|
||||
assert.True(t, res.Result)
|
||||
result: func(e *executor) interface{} {
|
||||
v := true
|
||||
return &v
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -513,25 +503,21 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
{
|
||||
name: "positive",
|
||||
params: `["AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i"]`,
|
||||
result: func(*executor) interface{} { return &ValidateAddrResponse{} },
|
||||
check: func(t *testing.T, e *executor, result interface{}) {
|
||||
res, ok := result.(*ValidateAddrResponse)
|
||||
result: func(*executor) interface{} { return &result.ValidateAddress{} },
|
||||
check: func(t *testing.T, e *executor, va interface{}) {
|
||||
res, ok := va.(*result.ValidateAddress)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i", res.Result.Address)
|
||||
assert.True(t, res.Result.IsValid)
|
||||
assert.Equal(t, "AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i", res.Address)
|
||||
assert.True(t, res.IsValid)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "negative",
|
||||
params: "[1]",
|
||||
result: func(*executor) interface{} {
|
||||
return &ValidateAddrResponse{
|
||||
Jsonrpc: defaultJSONRPC,
|
||||
Result: wrappers.ValidateAddressResponse{
|
||||
Address: float64(1),
|
||||
IsValid: false,
|
||||
},
|
||||
ID: defaultID,
|
||||
return &result.ValidateAddress{
|
||||
Address: float64(1),
|
||||
IsValid: false,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -551,14 +537,14 @@ func TestRPC(t *testing.T) {
|
|||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
body := doRPCCall(fmt.Sprintf(rpc, method, tc.params), handler, t)
|
||||
checkErrResponse(t, body, tc.fail)
|
||||
result := checkErrGetResult(t, body, tc.fail)
|
||||
if tc.fail {
|
||||
return
|
||||
}
|
||||
|
||||
expected, res := tc.getResultPair(e)
|
||||
err := json.Unmarshal(body, res)
|
||||
require.NoErrorf(t, err, "could not parse response: %s", body)
|
||||
err := json.Unmarshal(result, res)
|
||||
require.NoErrorf(t, err, "could not parse response: %s", result)
|
||||
|
||||
if tc.check == nil {
|
||||
assert.Equal(t, expected, res)
|
||||
|
@ -575,11 +561,11 @@ func TestRPC(t *testing.T) {
|
|||
TXHash := block.Transactions[1].Hash()
|
||||
rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s"]}"`, TXHash.StringLE())
|
||||
body := doRPCCall(rpc, handler, t)
|
||||
checkErrResponse(t, body, false)
|
||||
var res StringResultResponse
|
||||
err := json.Unmarshal(body, &res)
|
||||
require.NoErrorf(t, err, "could not parse response: %s", body)
|
||||
assert.Equal(t, "400000455b7b226c616e67223a227a682d434e222c226e616d65223a22e5b08fe89a81e882a1227d2c7b226c616e67223a22656e222c226e616d65223a22416e745368617265227d5d0000c16ff28623000000da1745e9b549bd0bfa1a569971c77eba30cd5a4b00000000", res.Result)
|
||||
result := checkErrGetResult(t, body, false)
|
||||
var res string
|
||||
err := json.Unmarshal(result, &res)
|
||||
require.NoErrorf(t, err, "could not parse response: %s", result)
|
||||
assert.Equal(t, "400000455b7b226c616e67223a227a682d434e222c226e616d65223a22e5b08fe89a81e882a1227d2c7b226c616e67223a22656e222c226e616d65223a22416e745368617265227d5d0000c16ff28623000000da1745e9b549bd0bfa1a569971c77eba30cd5a4b00000000", res)
|
||||
})
|
||||
|
||||
t.Run("getrawtransaction 2 arguments", func(t *testing.T) {
|
||||
|
@ -587,11 +573,11 @@ func TestRPC(t *testing.T) {
|
|||
TXHash := block.Transactions[1].Hash()
|
||||
rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s", 0]}"`, TXHash.StringLE())
|
||||
body := doRPCCall(rpc, handler, t)
|
||||
checkErrResponse(t, body, false)
|
||||
var res StringResultResponse
|
||||
err := json.Unmarshal(body, &res)
|
||||
require.NoErrorf(t, err, "could not parse response: %s", body)
|
||||
assert.Equal(t, "400000455b7b226c616e67223a227a682d434e222c226e616d65223a22e5b08fe89a81e882a1227d2c7b226c616e67223a22656e222c226e616d65223a22416e745368617265227d5d0000c16ff28623000000da1745e9b549bd0bfa1a569971c77eba30cd5a4b00000000", res.Result)
|
||||
result := checkErrGetResult(t, body, false)
|
||||
var res string
|
||||
err := json.Unmarshal(result, &res)
|
||||
require.NoErrorf(t, err, "could not parse response: %s", result)
|
||||
assert.Equal(t, "400000455b7b226c616e67223a227a682d434e222c226e616d65223a22e5b08fe89a81e882a1227d2c7b226c616e67223a22656e222c226e616d65223a22416e745368617265227d5d0000c16ff28623000000da1745e9b549bd0bfa1a569971c77eba30cd5a4b00000000", res)
|
||||
})
|
||||
|
||||
t.Run("gettxout", func(t *testing.T) {
|
||||
|
@ -600,54 +586,35 @@ func TestRPC(t *testing.T) {
|
|||
rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "gettxout", "params": [%s, %d]}"`,
|
||||
`"`+tx.Hash().StringLE()+`"`, 0)
|
||||
body := doRPCCall(rpc, handler, t)
|
||||
checkErrResponse(t, body, false)
|
||||
res := checkErrGetResult(t, body, false)
|
||||
|
||||
var result GetTxOutResponse
|
||||
err := json.Unmarshal(body, &result)
|
||||
require.NoErrorf(t, err, "could not parse response: %s", body)
|
||||
assert.Equal(t, 0, result.Result.N)
|
||||
assert.Equal(t, "0x9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc5", result.Result.Asset)
|
||||
assert.Equal(t, util.Fixed8FromInt64(100000000), result.Result.Value)
|
||||
assert.Equal(t, "AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU", result.Result.Address)
|
||||
var txOut result.TransactionOutput
|
||||
err := json.Unmarshal(res, &txOut)
|
||||
require.NoErrorf(t, err, "could not parse response: %s", res)
|
||||
assert.Equal(t, 0, txOut.N)
|
||||
assert.Equal(t, "0x9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc5", txOut.Asset)
|
||||
assert.Equal(t, util.Fixed8FromInt64(100000000), txOut.Value)
|
||||
assert.Equal(t, "AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU", txOut.Address)
|
||||
})
|
||||
}
|
||||
|
||||
func (tc rpcTestCase) getResultPair(e *executor) (expected interface{}, res interface{}) {
|
||||
expected = tc.result(e)
|
||||
switch exp := expected.(type) {
|
||||
case string:
|
||||
res = new(StringResultResponse)
|
||||
expected = &StringResultResponse{
|
||||
Jsonrpc: defaultJSONRPC,
|
||||
Result: exp,
|
||||
ID: defaultID,
|
||||
}
|
||||
case int:
|
||||
res = new(IntResultResponse)
|
||||
expected = &IntResultResponse{
|
||||
Jsonrpc: defaultJSONRPC,
|
||||
Result: exp,
|
||||
ID: defaultID,
|
||||
}
|
||||
default:
|
||||
resVal := reflect.New(reflect.TypeOf(expected).Elem())
|
||||
res = resVal.Interface()
|
||||
}
|
||||
|
||||
return
|
||||
resVal := reflect.New(reflect.TypeOf(expected).Elem())
|
||||
return expected, resVal.Interface()
|
||||
}
|
||||
|
||||
func checkErrResponse(t *testing.T, body []byte, expectingFail bool) {
|
||||
var errresp ErrorResponse
|
||||
err := json.Unmarshal(body, &errresp)
|
||||
func checkErrGetResult(t *testing.T, body []byte, expectingFail bool) json.RawMessage {
|
||||
var resp response.Raw
|
||||
err := json.Unmarshal(body, &resp)
|
||||
require.Nil(t, err)
|
||||
if expectingFail {
|
||||
assert.NotEqual(t, 0, errresp.Error.Code)
|
||||
assert.NotEqual(t, "", errresp.Error.Message)
|
||||
assert.NotEqual(t, 0, resp.Error.Code)
|
||||
assert.NotEqual(t, "", resp.Error.Message)
|
||||
} else {
|
||||
assert.Equal(t, 0, errresp.Error.Code)
|
||||
assert.Equal(t, "", errresp.Error.Message)
|
||||
assert.Nil(t, resp.Error)
|
||||
}
|
||||
return resp.Result
|
||||
}
|
||||
|
||||
func doRPCCall(rpcCall string, handler http.HandlerFunc, t *testing.T) []byte {
|
|
@ -1,217 +0,0 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/CityOfZion/neo-go/config"
|
||||
"github.com/CityOfZion/neo-go/pkg/core"
|
||||
"github.com/CityOfZion/neo-go/pkg/core/block"
|
||||
"github.com/CityOfZion/neo-go/pkg/core/storage"
|
||||
"github.com/CityOfZion/neo-go/pkg/io"
|
||||
"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"
|
||||
"go.uber.org/zap/zaptest"
|
||||
)
|
||||
|
||||
// ErrorResponse struct represents JSON-RPC error.
|
||||
type ErrorResponse struct {
|
||||
Jsonrpc string `json:"jsonrpc"`
|
||||
Error struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
} `json:"error"`
|
||||
ID int `json:"id"`
|
||||
}
|
||||
|
||||
// SendTXResponse struct for testing.
|
||||
type SendTXResponse struct {
|
||||
Jsonrpc string `json:"jsonrpc"`
|
||||
Result bool `json:"result"`
|
||||
ID int `json:"id"`
|
||||
}
|
||||
|
||||
// InvokeFunctionResponse struct for testing.
|
||||
type InvokeFunctionResponse struct {
|
||||
Jsonrpc string `json:"jsonrpc"`
|
||||
Result struct {
|
||||
Script string `json:"script"`
|
||||
State string `json:"state"`
|
||||
GasConsumed string `json:"gas_consumed"`
|
||||
Stack []FuncParam `json:"stack"`
|
||||
TX string `json:"tx,omitempty"`
|
||||
} `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 wrappers.Block `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"`
|
||||
}
|
||||
|
||||
// GetUnspents struct for testing.
|
||||
type GetUnspents struct {
|
||||
Jsonrpc string `json:"jsonrpc"`
|
||||
Result struct {
|
||||
Balance []struct {
|
||||
Unspents []struct {
|
||||
TxID string `json:"txid"`
|
||||
Index int `json:"n"`
|
||||
Value string `json:"value"`
|
||||
} `json:"unspent"`
|
||||
AssetHash string `json:"asset_hash"`
|
||||
Asset string `json:"asset"`
|
||||
AssetSymbol string `json:"asset_symbol"`
|
||||
Amount string `json:"amount"`
|
||||
} `json:"balance"`
|
||||
Address string `json:"address"`
|
||||
} `json:"result"`
|
||||
ID int `json:"id"`
|
||||
}
|
||||
|
||||
// GetContractStateResponse struct for testing.
|
||||
type GetContractStateResponce struct {
|
||||
Jsonrpc string `json:"jsonrpc"`
|
||||
Result struct {
|
||||
Version byte `json:"version"`
|
||||
ScriptHash util.Uint160 `json:"hash"`
|
||||
Script []byte `json:"script"`
|
||||
ParamList interface{} `json:"parameters"`
|
||||
ReturnType interface{} `json:"returntype"`
|
||||
Name string `json:"name"`
|
||||
CodeVersion string `json:"code_version"`
|
||||
Author string `json:"author"`
|
||||
Email string `json:"email"`
|
||||
Description string `json:"description"`
|
||||
Properties struct {
|
||||
HasStorage bool `json:"storage"`
|
||||
HasDynamicInvoke bool `json:"dynamic_invoke"`
|
||||
IsPayable bool `json:"is_payable"`
|
||||
} `json:"properties"`
|
||||
} `json:"result"`
|
||||
ID int `json:"id"`
|
||||
}
|
||||
|
||||
func initServerWithInMemoryChain(t *testing.T) (*core.Blockchain, http.HandlerFunc) {
|
||||
var nBlocks uint32
|
||||
|
||||
net := config.ModeUnitTestNet
|
||||
configPath := "../../config"
|
||||
cfg, err := config.Load(configPath, net)
|
||||
require.NoError(t, err, "could not load config")
|
||||
|
||||
memoryStore := storage.NewMemoryStore()
|
||||
logger := zaptest.NewLogger(t)
|
||||
chain, err := core.NewBlockchain(memoryStore, cfg.ProtocolConfiguration, logger)
|
||||
require.NoError(t, err, "could not create chain")
|
||||
|
||||
go chain.Run()
|
||||
|
||||
// File "./testdata/testblocks.acc" was generated by function core._
|
||||
// ("neo-go/pkg/core/helper_test.go").
|
||||
// To generate new "./testdata/testblocks.acc", follow the steps:
|
||||
// 1. Rename the function
|
||||
// 2. Add specific test-case into "neo-go/pkg/core/blockchain_test.go"
|
||||
// 3. Run tests with `$ make test`
|
||||
f, err := os.Open("testdata/testblocks.acc")
|
||||
require.Nil(t, err)
|
||||
br := io.NewBinReaderFromIO(f)
|
||||
nBlocks = br.ReadU32LE()
|
||||
require.Nil(t, br.Err)
|
||||
for i := 0; i < int(nBlocks); i++ {
|
||||
b := &block.Block{}
|
||||
b.DecodeBinary(br)
|
||||
require.Nil(t, br.Err)
|
||||
require.NoError(t, chain.AddBlock(b))
|
||||
}
|
||||
|
||||
serverConfig := network.NewServerConfig(cfg)
|
||||
server, err := network.NewServer(serverConfig, chain, logger)
|
||||
require.NoError(t, err)
|
||||
rpcServer := NewServer(chain, cfg.ApplicationConfiguration.RPC, server, logger)
|
||||
handler := http.HandlerFunc(rpcServer.requestHandler)
|
||||
|
||||
return chain, handler
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestInvocationScriptCreationGood(t *testing.T) {
|
||||
p := Param{stringT, "50befd26fdf6e4d957c11e078b24ebce6291456f"}
|
||||
contract, err := p.GetUint160FromHex()
|
||||
require.Nil(t, err)
|
||||
|
||||
var paramScripts = []struct {
|
||||
ps Params
|
||||
script string
|
||||
}{{
|
||||
script: "676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
|
||||
}, {
|
||||
ps: Params{{stringT, "transfer"}},
|
||||
script: "087472616e73666572676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
|
||||
}, {
|
||||
ps: Params{{numberT, 42}},
|
||||
script: "023432676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
|
||||
}, {
|
||||
ps: Params{{stringT, "a"}, {arrayT, []Param{}}},
|
||||
script: "00c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
|
||||
}, {
|
||||
ps: Params{{stringT, "a"}, {arrayT, []Param{{funcParamT, FuncParam{ByteArray, Param{stringT, "50befd26fdf6e4d957c11e078b24ebce6291456f"}}}}}},
|
||||
script: "1450befd26fdf6e4d957c11e078b24ebce6291456f51c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
|
||||
}, {
|
||||
ps: Params{{stringT, "a"}, {arrayT, []Param{{funcParamT, FuncParam{Signature, Param{stringT, "4edf5005771de04619235d5a4c7a9a11bb78e008541f1da7725f654c33380a3c87e2959a025da706d7255cb3a3fa07ebe9c6559d0d9e6213c68049168eb1056f"}}}}}},
|
||||
script: "404edf5005771de04619235d5a4c7a9a11bb78e008541f1da7725f654c33380a3c87e2959a025da706d7255cb3a3fa07ebe9c6559d0d9e6213c68049168eb1056f51c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
|
||||
}, {
|
||||
ps: Params{{stringT, "a"}, {arrayT, []Param{{funcParamT, FuncParam{String, Param{stringT, "50befd26fdf6e4d957c11e078b24ebce6291456f"}}}}}},
|
||||
script: "283530626566643236666466366534643935376331316530373862323465626365363239313435366651c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
|
||||
}, {
|
||||
ps: Params{{stringT, "a"}, {arrayT, []Param{{funcParamT, FuncParam{Hash160, Param{stringT, "50befd26fdf6e4d957c11e078b24ebce6291456f"}}}}}},
|
||||
script: "146f459162ceeb248b071ec157d9e4f6fd26fdbe5051c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
|
||||
}, {
|
||||
ps: Params{{stringT, "a"}, {arrayT, []Param{{funcParamT, FuncParam{Hash256, Param{stringT, "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"}}}}}},
|
||||
script: "20e72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c6051c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
|
||||
}, {
|
||||
ps: Params{{stringT, "a"}, {arrayT, []Param{{funcParamT, FuncParam{PublicKey, Param{stringT, "03c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c1"}}}}}},
|
||||
script: "2103c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c151c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
|
||||
}, {
|
||||
ps: Params{{stringT, "a"}, {arrayT, []Param{{funcParamT, FuncParam{Integer, Param{numberT, 42}}}}}},
|
||||
script: "012a51c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
|
||||
}, {
|
||||
ps: Params{{stringT, "a"}, {arrayT, []Param{{funcParamT, FuncParam{Boolean, Param{stringT, "true"}}}}}},
|
||||
script: "5151c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
|
||||
}, {
|
||||
ps: Params{{stringT, "a"}, {arrayT, []Param{{funcParamT, FuncParam{Boolean, Param{stringT, "false"}}}}}},
|
||||
script: "0051c10161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
|
||||
}}
|
||||
for _, ps := range paramScripts {
|
||||
script, err := CreateFunctionInvocationScript(contract, ps.ps)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, ps.script, hex.EncodeToString(script))
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvocationScriptCreationBad(t *testing.T) {
|
||||
contract := util.Uint160{}
|
||||
|
||||
var testParams = []Params{
|
||||
{{numberT, "qwerty"}},
|
||||
{{arrayT, 42}},
|
||||
{{arrayT, []Param{{numberT, 42}}}},
|
||||
{{arrayT, []Param{{funcParamT, FuncParam{ByteArray, Param{stringT, "qwerty"}}}}}},
|
||||
{{arrayT, []Param{{funcParamT, FuncParam{Signature, Param{stringT, "qwerty"}}}}}},
|
||||
{{arrayT, []Param{{funcParamT, FuncParam{String, Param{numberT, 42}}}}}},
|
||||
{{arrayT, []Param{{funcParamT, FuncParam{Hash160, Param{stringT, "qwerty"}}}}}},
|
||||
{{arrayT, []Param{{funcParamT, FuncParam{Hash256, Param{stringT, "qwerty"}}}}}},
|
||||
{{arrayT, []Param{{funcParamT, FuncParam{PublicKey, Param{numberT, 42}}}}}},
|
||||
{{arrayT, []Param{{funcParamT, FuncParam{PublicKey, Param{stringT, "qwerty"}}}}}},
|
||||
{{arrayT, []Param{{funcParamT, FuncParam{Integer, Param{stringT, "qwerty"}}}}}},
|
||||
{{arrayT, []Param{{funcParamT, FuncParam{Boolean, Param{numberT, 42}}}}}},
|
||||
{{arrayT, []Param{{funcParamT, FuncParam{Boolean, Param{stringT, "qwerty"}}}}}},
|
||||
{{arrayT, []Param{{funcParamT, FuncParam{Unknown, Param{}}}}}},
|
||||
}
|
||||
for _, ps := range testParams {
|
||||
_, err := CreateFunctionInvocationScript(contract, ps)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
}
|
140
pkg/rpc/types.go
140
pkg/rpc/types.go
|
@ -1,140 +0,0 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||
"github.com/CityOfZion/neo-go/pkg/rpc/wrappers"
|
||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
||||
)
|
||||
|
||||
// InvokeScriptResponse stores response for the invoke script call.
|
||||
type InvokeScriptResponse struct {
|
||||
responseHeader
|
||||
Error *Error `json:"error,omitempty"`
|
||||
Result *InvokeResult `json:"result,omitempty"`
|
||||
}
|
||||
|
||||
// InvokeResult represents the outcome of a script that is
|
||||
// executed by the NEO VM.
|
||||
type InvokeResult struct {
|
||||
State vm.State `json:"state"`
|
||||
GasConsumed string `json:"gas_consumed"`
|
||||
Script string `json:"script"`
|
||||
Stack []StackParam
|
||||
}
|
||||
|
||||
// AccountStateResponse holds the getaccountstate response.
|
||||
type AccountStateResponse struct {
|
||||
responseHeader
|
||||
Result *Account `json:"result"`
|
||||
}
|
||||
|
||||
// UnspentResponse represents server response to the `getunspents` command.
|
||||
type UnspentResponse struct {
|
||||
responseHeader
|
||||
Error *Error `json:"error,omitempty"`
|
||||
Result *wrappers.Unspents `json:"result,omitempty"`
|
||||
}
|
||||
|
||||
// Account represents details about a NEO account.
|
||||
type Account struct {
|
||||
Version int `json:"version"`
|
||||
ScriptHash string `json:"script_hash"`
|
||||
Frozen bool
|
||||
// TODO: need to check this field out.
|
||||
Votes []interface{}
|
||||
Balances []*Balance
|
||||
}
|
||||
|
||||
// Balance represents details about a NEO account balance.
|
||||
type Balance struct {
|
||||
Asset string `json:"asset"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type params struct {
|
||||
values []interface{}
|
||||
}
|
||||
|
||||
func newParams(vals ...interface{}) params {
|
||||
p := params{}
|
||||
p.values = make([]interface{}, len(vals))
|
||||
for i := 0; i < len(p.values); i++ {
|
||||
p.values[i] = vals[i]
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
type request struct {
|
||||
JSONRPC string `json:"jsonrpc"`
|
||||
Method string `json:"method"`
|
||||
Params []interface{} `json:"params"`
|
||||
ID int `json:"id"`
|
||||
}
|
||||
|
||||
type responseHeader struct {
|
||||
ID int `json:"id"`
|
||||
JSONRPC string `json:"jsonrpc"`
|
||||
}
|
||||
|
||||
type response struct {
|
||||
responseHeader
|
||||
Error *Error `json:"error"`
|
||||
Result interface{} `json:"result"`
|
||||
}
|
||||
|
||||
// SendToAddressResponse stores response for the sendtoaddress call.
|
||||
type SendToAddressResponse struct {
|
||||
responseHeader
|
||||
Error *Error `json:"error"`
|
||||
Result *TxResponse
|
||||
}
|
||||
|
||||
// GetRawTxResponse represents verbose output of `getrawtransaction` RPC call.
|
||||
type GetRawTxResponse struct {
|
||||
responseHeader
|
||||
Error *Error `json:"error"`
|
||||
Result *RawTxResponse `json:"result"`
|
||||
}
|
||||
|
||||
// GetTxOutResponse represents result of `gettxout` RPC call.
|
||||
type GetTxOutResponse struct {
|
||||
responseHeader
|
||||
Error *Error
|
||||
Result *wrappers.TransactionOutput
|
||||
}
|
||||
|
||||
// RawTxResponse stores transaction with blockchain metadata to be sent as a response.
|
||||
type RawTxResponse struct {
|
||||
TxResponse
|
||||
BlockHash string `json:"blockhash"`
|
||||
Confirmations uint `json:"confirmations"`
|
||||
BlockTime uint `json:"blocktime"`
|
||||
}
|
||||
|
||||
// TxResponse stores transaction to be sent as a response.
|
||||
type TxResponse struct {
|
||||
TxID string `json:"txid"`
|
||||
Size int `json:"size"`
|
||||
Type string `json:"type"` // todo: convert to TransactionType
|
||||
Version int `json:"version"`
|
||||
Attributes []transaction.Attribute `json:"attributes"`
|
||||
Vins []Vin `json:"vin"`
|
||||
Vouts []Vout `json:"vout"`
|
||||
SysFee int `json:"sys_fee"`
|
||||
NetFee int `json:"net_fee"`
|
||||
Scripts []transaction.Witness `json:"scripts"`
|
||||
}
|
||||
|
||||
// Vin represents JSON-serializable tx input.
|
||||
type Vin struct {
|
||||
TxID string `json:"txid"`
|
||||
Vout int `json:"vout"`
|
||||
}
|
||||
|
||||
// Vout represents JSON-serializable tx output.
|
||||
type Vout struct {
|
||||
N int `json:"n"`
|
||||
Asset string `json:"asset"`
|
||||
Value int `json:"value"`
|
||||
Address string `json:"address"`
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
package wrappers
|
||||
|
||||
import (
|
||||
"github.com/CityOfZion/neo-go/pkg/encoding/address"
|
||||
)
|
||||
|
||||
// ValidateAddressResponse represents response to validate address call.
|
||||
type ValidateAddressResponse struct {
|
||||
Address interface{} `json:"address"`
|
||||
IsValid bool `json:"isvalid"`
|
||||
}
|
||||
|
||||
// ValidateAddress verifies that the address is a correct NEO address
|
||||
// see https://docs.neo.org/en-us/node/cli/2.9.4/api/validateaddress.html
|
||||
func ValidateAddress(addr interface{}) ValidateAddressResponse {
|
||||
resp := ValidateAddressResponse{Address: addr}
|
||||
if addr, ok := addr.(string); ok {
|
||||
_, err := address.StringToUint160(addr)
|
||||
resp.IsValid = err == nil
|
||||
}
|
||||
return resp
|
||||
}
|
|
@ -137,7 +137,7 @@ func parseParamType(typ string) (ParamType, error) {
|
|||
return Hash160Type, nil
|
||||
case "hash256":
|
||||
return Hash256Type, nil
|
||||
case "bytes":
|
||||
case "bytes", "bytearray":
|
||||
return ByteArrayType, nil
|
||||
case "key":
|
||||
return PublicKeyType, nil
|
||||
|
|
Loading…
Reference in a new issue