mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-12-25 03:56:34 +00:00
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/io"
|
||||||
"github.com/CityOfZion/neo-go/pkg/network"
|
"github.com/CityOfZion/neo-go/pkg/network"
|
||||||
"github.com/CityOfZion/neo-go/pkg/network/metrics"
|
"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/pkg/errors"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
@ -341,18 +341,18 @@ func startServer(ctx *cli.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
server, err := network.NewServer(serverConfig, chain, log)
|
serv, err := network.NewServer(serverConfig, chain, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(fmt.Errorf("failed to create network server: %v", err), 1)
|
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)
|
errChan := make(chan error)
|
||||||
|
|
||||||
go server.Start(errChan)
|
go serv.Start(errChan)
|
||||||
go rpcServer.Start(errChan)
|
go rpcServer.Start(errChan)
|
||||||
|
|
||||||
fmt.Println(logo())
|
fmt.Println(logo())
|
||||||
fmt.Println(server.UserAgent)
|
fmt.Println(serv.UserAgent)
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
|
||||||
var shutdownErr error
|
var shutdownErr error
|
||||||
|
@ -364,7 +364,7 @@ Main:
|
||||||
cancel()
|
cancel()
|
||||||
|
|
||||||
case <-grace.Done():
|
case <-grace.Done():
|
||||||
server.Shutdown()
|
serv.Shutdown()
|
||||||
if serverErr := rpcServer.Shutdown(); serverErr != nil {
|
if serverErr := rpcServer.Shutdown(); serverErr != nil {
|
||||||
shutdownErr = errors.Wrap(serverErr, "Error encountered whilst shutting down server")
|
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/compiler"
|
||||||
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
|
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
|
||||||
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
|
"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/smartcontract"
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
"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.
|
// TODO: Fix the missing neo-go.yml file with the `init` command when the package manager is in place.
|
||||||
if !ctx.Bool("skip-details") {
|
if !ctx.Bool("skip-details") {
|
||||||
details := parseContractDetails()
|
details := parseContractDetails()
|
||||||
details.ReturnType = rpc.ByteArray
|
details.ReturnType = request.ByteArray
|
||||||
details.Parameters = make([]rpc.StackParamType, 2)
|
details.Parameters = make([]request.StackParamType, 2)
|
||||||
details.Parameters[0] = rpc.String
|
details.Parameters[0] = request.String
|
||||||
details.Parameters[1] = rpc.Array
|
details.Parameters[1] = request.Array
|
||||||
|
|
||||||
project := &ProjectConfig{Contract: details}
|
project := &ProjectConfig{Contract: details}
|
||||||
b, err := yaml.Marshal(project)
|
b, err := yaml.Marshal(project)
|
||||||
|
@ -362,7 +364,7 @@ func invokeInternal(ctx *cli.Context, withMethod bool, signAndPush bool) error {
|
||||||
operation string
|
operation string
|
||||||
params = make([]smartcontract.Parameter, 0)
|
params = make([]smartcontract.Parameter, 0)
|
||||||
paramsStart = 1
|
paramsStart = 1
|
||||||
resp *rpc.InvokeScriptResponse
|
resp *response.InvokeResult
|
||||||
wif *keys.WIF
|
wif *keys.WIF
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -398,34 +400,34 @@ func invokeInternal(ctx *cli.Context, withMethod bool, signAndPush bool) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
client, err := rpc.NewClient(context.TODO(), endpoint, rpc.ClientOptions{})
|
c, err := client.New(context.TODO(), endpoint, client.Options{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(err, 1)
|
return cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if withMethod {
|
if withMethod {
|
||||||
resp, err = client.InvokeFunction(script, operation, params)
|
resp, err = c.InvokeFunction(script, operation, params)
|
||||||
} else {
|
} else {
|
||||||
resp, err = client.Invoke(script, params)
|
resp, err = c.Invoke(script, params)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(err, 1)
|
return cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
if signAndPush {
|
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)
|
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 {
|
if err != nil {
|
||||||
return cli.NewExitError(fmt.Errorf("bad script returned from the RPC node: %v", err), 1)
|
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 {
|
if err != nil {
|
||||||
return cli.NewExitError(fmt.Errorf("failed to push invocation tx: %v", err), 1)
|
return cli.NewExitError(fmt.Errorf("failed to push invocation tx: %v", err), 1)
|
||||||
}
|
}
|
||||||
fmt.Printf("Sent invocation transaction %s\n", txHash.StringLE())
|
fmt.Printf("Sent invocation transaction %s\n", txHash.StringLE())
|
||||||
} else {
|
} else {
|
||||||
b, err := json.MarshalIndent(resp.Result, "", " ")
|
b, err := json.MarshalIndent(resp, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(err, 1)
|
return cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
|
@ -451,18 +453,18 @@ func testInvokeScript(ctx *cli.Context) error {
|
||||||
return cli.NewExitError(err, 1)
|
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 {
|
if err != nil {
|
||||||
return cli.NewExitError(err, 1)
|
return cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
scriptHex := hex.EncodeToString(b)
|
scriptHex := hex.EncodeToString(b)
|
||||||
resp, err := client.InvokeScript(scriptHex)
|
resp, err := c.InvokeScript(scriptHex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(err, 1)
|
return cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err = json.MarshalIndent(resp.Result, "", " ")
|
b, err = json.MarshalIndent(resp, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(err, 1)
|
return cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
|
@ -475,11 +477,11 @@ func testInvokeScript(ctx *cli.Context) error {
|
||||||
// ProjectConfig contains project metadata.
|
// ProjectConfig contains project metadata.
|
||||||
type ProjectConfig struct {
|
type ProjectConfig struct {
|
||||||
Version uint
|
Version uint
|
||||||
Contract rpc.ContractDetails `yaml:"project"`
|
Contract request.ContractDetails `yaml:"project"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseContractDetails() rpc.ContractDetails {
|
func parseContractDetails() request.ContractDetails {
|
||||||
details := rpc.ContractDetails{}
|
details := request.ContractDetails{}
|
||||||
reader := bufio.NewReader(os.Stdin)
|
reader := bufio.NewReader(os.Stdin)
|
||||||
|
|
||||||
fmt.Print("Author: ")
|
fmt.Print("Author: ")
|
||||||
|
@ -571,17 +573,17 @@ func contractDeploy(ctx *cli.Context) error {
|
||||||
return cli.NewExitError(fmt.Errorf("bad config: %v", err), 1)
|
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 {
|
if err != nil {
|
||||||
return cli.NewExitError(err, 1)
|
return cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
txScript, err := rpc.CreateDeploymentScript(avm, &conf.Contract)
|
txScript, err := request.CreateDeploymentScript(avm, &conf.Contract)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(fmt.Errorf("failed to create deployment script: %v", err), 1)
|
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 {
|
if err != nil {
|
||||||
return cli.NewExitError(fmt.Errorf("failed to push invocation tx: %v", err), 1)
|
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/crypto/keys"
|
||||||
"github.com/CityOfZion/neo-go/pkg/encoding/address"
|
"github.com/CityOfZion/neo-go/pkg/encoding/address"
|
||||||
"github.com/CityOfZion/neo-go/pkg/network"
|
"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"
|
"github.com/stretchr/testify/require"
|
||||||
"go.uber.org/zap/zaptest"
|
"go.uber.org/zap/zaptest"
|
||||||
)
|
)
|
||||||
|
@ -55,7 +55,7 @@ func prepareData(t *testing.B) []*transaction.Transaction {
|
||||||
|
|
||||||
for n := 0; n < t.N; n++ {
|
for n := 0; n < t.N; n++ {
|
||||||
tx := getTX(t, wif)
|
tx := getTX(t, wif)
|
||||||
require.NoError(t, rpc.SignTx(tx, wif))
|
require.NoError(t, request.SignTx(tx, wif))
|
||||||
data = append(data, tx)
|
data = append(data, tx)
|
||||||
}
|
}
|
||||||
return data
|
return data
|
||||||
|
|
|
@ -190,12 +190,25 @@ func (s *Server) Shutdown() {
|
||||||
// UnconnectedPeers returns a list of peers that are in the discovery peer list
|
// UnconnectedPeers returns a list of peers that are in the discovery peer list
|
||||||
// but are not connected to the server.
|
// but are not connected to the server.
|
||||||
func (s *Server) UnconnectedPeers() []string {
|
func (s *Server) UnconnectedPeers() []string {
|
||||||
return []string{}
|
return s.discovery.UnconnectedPeers()
|
||||||
}
|
}
|
||||||
|
|
||||||
// BadPeers returns a list of peers the are flagged as "bad" peers.
|
// BadPeers returns a list of peers the are flagged as "bad" peers.
|
||||||
func (s *Server) BadPeers() []string {
|
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
|
// run is a goroutine that starts another goroutine to manage protocol specifics
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package rpc
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -14,6 +14,8 @@ import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/core/state"
|
"github.com/CityOfZion/neo-go/pkg/core/state"
|
||||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||||
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
|
"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/CityOfZion/neo-go/pkg/util"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
@ -37,13 +39,13 @@ type Client struct {
|
||||||
wifMu *sync.Mutex
|
wifMu *sync.Mutex
|
||||||
wif *keys.WIF
|
wif *keys.WIF
|
||||||
balancerMu *sync.Mutex
|
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
|
// All Values are optional. If any duration is not specified
|
||||||
// a default of 3 seconds will be used.
|
// a default of 3 seconds will be used.
|
||||||
type ClientOptions struct {
|
type Options struct {
|
||||||
Cert string
|
Cert string
|
||||||
Key string
|
Key string
|
||||||
CACert string
|
CACert string
|
||||||
|
@ -55,8 +57,8 @@ type ClientOptions struct {
|
||||||
Version string
|
Version string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient returns a new Client ready to use.
|
// New returns a new Client ready to use.
|
||||||
func NewClient(ctx context.Context, endpoint string, opts ClientOptions) (*Client, error) {
|
func New(ctx context.Context, endpoint string, opts Options) (*Client, error) {
|
||||||
url, err := url.Parse(endpoint)
|
url, err := url.Parse(endpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -121,14 +123,14 @@ func (c *Client) SetWIF(wif string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Balancer is a getter for balance field.
|
// Balancer is a getter for balance field.
|
||||||
func (c *Client) Balancer() BalanceGetter {
|
func (c *Client) Balancer() request.BalanceGetter {
|
||||||
c.balancerMu.Lock()
|
c.balancerMu.Lock()
|
||||||
defer c.balancerMu.Unlock()
|
defer c.balancerMu.Unlock()
|
||||||
return c.balancer
|
return c.balancer
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetBalancer is a setter for balance field.
|
// SetBalancer is a setter for balance field.
|
||||||
func (c *Client) SetBalancer(b BalanceGetter) {
|
func (c *Client) SetBalancer(b request.BalanceGetter) {
|
||||||
c.balancerMu.Lock()
|
c.balancerMu.Lock()
|
||||||
defer c.balancerMu.Unlock()
|
defer c.balancerMu.Unlock()
|
||||||
|
|
||||||
|
@ -161,13 +163,10 @@ func (c *Client) CalculateInputs(address string, asset util.Uint256, cost util.F
|
||||||
var utxos state.UnspentBalances
|
var utxos state.UnspentBalances
|
||||||
|
|
||||||
resp, err := c.GetUnspents(address)
|
resp, err := c.GetUnspents(address)
|
||||||
if err != nil || resp.Error != nil {
|
if err != nil {
|
||||||
if err == nil {
|
|
||||||
err = fmt.Errorf("remote returned %d: %s", resp.Error.Code, resp.Error.Message)
|
|
||||||
}
|
|
||||||
return nil, util.Fixed8(0), errors.Wrapf(err, "cannot get balance for address %v", address)
|
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) {
|
if asset.Equals(ubi.AssetHash) {
|
||||||
utxos = ubi.Unspents
|
utxos = ubi.Unspents
|
||||||
break
|
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 (
|
var (
|
||||||
r = request{
|
r = request.Raw{
|
||||||
JSONRPC: c.version,
|
JSONRPC: c.version,
|
||||||
Method: method,
|
Method: method,
|
||||||
Params: p.values,
|
RawParams: p.Values,
|
||||||
ID: 1,
|
ID: 1,
|
||||||
}
|
}
|
||||||
buf = new(bytes.Buffer)
|
buf = new(bytes.Buffer)
|
||||||
|
raw = &response.Raw{}
|
||||||
)
|
)
|
||||||
|
|
||||||
if err := json.NewEncoder(buf).Encode(r); err != nil {
|
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()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
// The node might send us proper JSON anyway, so look there first and if
|
||||||
return fmt.Errorf("remote responded with a non 200 response: %d", resp.StatusCode)
|
// 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.
|
// 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 (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -8,11 +8,38 @@ import (
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/core/state"
|
"github.com/CityOfZion/neo-go/pkg/core/state"
|
||||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
"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"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
errs "github.com/pkg/errors"
|
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.
|
// GetBalance performs a request to get balance for the address specified.
|
||||||
func (s NeoScanServer) GetBalance(address string) ([]*Unspent, error) {
|
func (s NeoScanServer) GetBalance(address string) ([]*Unspent, error) {
|
||||||
var (
|
var (
|
||||||
|
@ -57,7 +84,7 @@ func (s NeoScanServer) CalculateInputs(address string, assetIDUint util.Uint256,
|
||||||
err error
|
err error
|
||||||
us []*Unspent
|
us []*Unspent
|
||||||
assetUnspent Unspent
|
assetUnspent Unspent
|
||||||
assetID = wrappers.GlobalAssets[assetIDUint.StringLE()]
|
assetID = result.GlobalAssets[assetIDUint.StringLE()]
|
||||||
)
|
)
|
||||||
if us, err = s.GetBalance(address); err != nil {
|
if us, err = s.GetBalance(address); err != nil {
|
||||||
return nil, util.Fixed8(0), errs.Wrapf(err, "Cannot get balance for address %v", address)
|
return nil, util.Fixed8(0), errs.Wrapf(err, "Cannot get balance for address %v", address)
|
|
@ -1,12 +1,14 @@
|
||||||
package rpc
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/core"
|
"github.com/CityOfZion/neo-go/pkg/core"
|
||||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||||
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
|
"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/smartcontract"
|
||||||
"github.com/CityOfZion/neo-go/pkg/util"
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -17,11 +19,11 @@ import (
|
||||||
// missing output wrapper at the moment, thus commented out
|
// missing output wrapper at the moment, thus commented out
|
||||||
// func (c *Client) getBlock(indexOrHash interface{}, verbose bool) (*response, error) {
|
// func (c *Client) getBlock(indexOrHash interface{}, verbose bool) (*response, error) {
|
||||||
// var (
|
// var (
|
||||||
// params = newParams(indexOrHash)
|
// params = request.NewRawParams(indexOrHash)
|
||||||
// resp = &response{}
|
// resp = &response{}
|
||||||
// )
|
// )
|
||||||
// if verbose {
|
// if verbose {
|
||||||
// params = newParams(indexOrHash, 1)
|
// params = request.NewRawParams(indexOrHash, 1)
|
||||||
// }
|
// }
|
||||||
// if err := c.performRequest("getblock", params, resp); err != nil {
|
// if err := c.performRequest("getblock", params, resp); err != nil {
|
||||||
// return nil, err
|
// return nil, err
|
||||||
|
@ -30,10 +32,10 @@ import (
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// GetAccountState returns detailed information about a NEO account.
|
// 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 (
|
var (
|
||||||
params = newParams(address)
|
params = request.NewRawParams(address)
|
||||||
resp = &AccountStateResponse{}
|
resp = &result.AccountState{}
|
||||||
)
|
)
|
||||||
if err := c.performRequest("getaccountstate", params, resp); err != nil {
|
if err := c.performRequest("getaccountstate", params, resp); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -42,10 +44,10 @@ func (c *Client) GetAccountState(address string) (*AccountStateResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUnspents returns UTXOs for the given NEO account.
|
// 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 (
|
var (
|
||||||
params = newParams(address)
|
params = request.NewRawParams(address)
|
||||||
resp = &UnspentResponse{}
|
resp = &result.Unspents{}
|
||||||
)
|
)
|
||||||
if err := c.performRequest("getunspents", params, resp); err != nil {
|
if err := c.performRequest("getunspents", params, resp); err != nil {
|
||||||
return nil, err
|
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.
|
// 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.
|
// 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 (
|
var (
|
||||||
params = newParams(script)
|
params = request.NewRawParams(script)
|
||||||
resp = &InvokeScriptResponse{}
|
resp = &response.InvokeResult{}
|
||||||
)
|
)
|
||||||
if err := c.performRequest("invokescript", params, resp); err != nil {
|
if err := c.performRequest("invokescript", params, resp); err != nil {
|
||||||
return nil, err
|
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
|
// InvokeFunction returns the results after calling the smart contract scripthash
|
||||||
// with the given operation and parameters.
|
// with the given operation and parameters.
|
||||||
// NOTE: this is test invoke and will not affect the blockchain.
|
// 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 (
|
var (
|
||||||
p = newParams(script, operation, params)
|
p = request.NewRawParams(script, operation, params)
|
||||||
resp = &InvokeScriptResponse{}
|
resp = &response.InvokeResult{}
|
||||||
)
|
)
|
||||||
if err := c.performRequest("invokefunction", p, resp); err != nil {
|
if err := c.performRequest("invokefunction", p, resp); err != nil {
|
||||||
return nil, err
|
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
|
// Invoke returns the results after calling the smart contract scripthash
|
||||||
// with the given parameters.
|
// 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 (
|
var (
|
||||||
p = newParams(script, params)
|
p = request.NewRawParams(script, params)
|
||||||
resp = &InvokeScriptResponse{}
|
resp = &response.InvokeResult{}
|
||||||
)
|
)
|
||||||
if err := c.performRequest("invoke", p, resp); err != nil {
|
if err := c.performRequest("invoke", p, resp); err != nil {
|
||||||
return nil, err
|
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
|
// missing output wrapper at the moment, thus commented out
|
||||||
// func (c *Client) getRawTransaction(hash string, verbose bool) (*response, error) {
|
// func (c *Client) getRawTransaction(hash string, verbose bool) (*response, error) {
|
||||||
// var (
|
// var (
|
||||||
// params = newParams(hash, verbose)
|
// params = request.NewRawParams(hash, verbose)
|
||||||
// resp = &response{}
|
// resp = &response{}
|
||||||
// )
|
// )
|
||||||
// if err := c.performRequest("getrawtransaction", params, resp); err != nil {
|
// 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.
|
// The given hex string needs to be signed with a keypair.
|
||||||
// When the result of the response object is true, the TX has successfully
|
// When the result of the response object is true, the TX has successfully
|
||||||
// been broadcasted to the network.
|
// been broadcasted to the network.
|
||||||
func (c *Client) sendRawTransaction(rawTX *transaction.Transaction) (*response, error) {
|
func (c *Client) sendRawTransaction(rawTX *transaction.Transaction) error {
|
||||||
var (
|
var (
|
||||||
params = newParams(hex.EncodeToString(rawTX.Bytes()))
|
params = request.NewRawParams(hex.EncodeToString(rawTX.Bytes()))
|
||||||
resp = &response{}
|
resp bool
|
||||||
)
|
)
|
||||||
if err := c.performRequest("sendrawtransaction", params, resp); err != nil {
|
if err := c.performRequest("sendrawtransaction", params, &resp); err != nil {
|
||||||
return nil, err
|
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.
|
// SendToAddress sends an amount of specific asset to a given address.
|
||||||
// This call requires open wallet. (`wif` key in client struct.)
|
// 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.
|
// 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 (
|
var (
|
||||||
err error
|
err error
|
||||||
rawTx *transaction.Transaction
|
rawTx *transaction.Transaction
|
||||||
txParams = ContractTxParams{
|
txParams = request.ContractTxParams{
|
||||||
assetID: asset,
|
AssetID: asset,
|
||||||
address: address,
|
Address: address,
|
||||||
value: amount,
|
Value: amount,
|
||||||
wif: c.WIF(),
|
WIF: c.WIF(),
|
||||||
balancer: c.Balancer(),
|
Balancer: c.Balancer(),
|
||||||
}
|
}
|
||||||
resp *response
|
resp util.Uint256
|
||||||
response = &SendToAddressResponse{}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if rawTx, err = CreateRawContractTransaction(txParams); err != nil {
|
if rawTx, err = request.CreateRawContractTransaction(txParams); err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to create raw transaction for `sendtoaddress`")
|
return resp, errors.Wrap(err, "failed to create raw transaction for `sendtoaddress`")
|
||||||
}
|
}
|
||||||
if resp, err = c.sendRawTransaction(rawTx); err != nil {
|
if err = c.sendRawTransaction(rawTx); err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to send raw transaction")
|
return resp, errors.Wrap(err, "failed to send raw transaction")
|
||||||
}
|
}
|
||||||
response.Error = resp.Error
|
return rawTx.Hash(), nil
|
||||||
response.ID = resp.ID
|
|
||||||
response.JSONRPC = resp.JSONRPC
|
|
||||||
response.Result = &TxResponse{
|
|
||||||
TxID: rawTx.Hash().StringLE(),
|
|
||||||
}
|
|
||||||
return response, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignAndPushInvocationTx signs and pushes given script as an invocation
|
// 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()
|
fromAddress := wif.PrivateKey.Address()
|
||||||
|
|
||||||
if gas > 0 {
|
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")
|
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")
|
return txHash, errors.Wrap(err, "failed to sign tx")
|
||||||
}
|
}
|
||||||
txHash = tx.Hash()
|
txHash = tx.Hash()
|
||||||
resp, err := c.sendRawTransaction(tx)
|
err = c.sendRawTransaction(tx)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return txHash, errors.Wrap(err, "failed sendning tx")
|
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
|
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 (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -29,12 +29,13 @@ type (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// These are parameter types accepted by RPC server.
|
||||||
const (
|
const (
|
||||||
defaultT paramType = iota
|
defaultT paramType = iota
|
||||||
stringT
|
StringT
|
||||||
numberT
|
NumberT
|
||||||
arrayT
|
ArrayT
|
||||||
funcParamT
|
FuncParamT
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p Param) String() string {
|
func (p Param) String() string {
|
||||||
|
@ -123,7 +124,7 @@ func (p Param) GetBytesHex() ([]byte, error) {
|
||||||
func (p *Param) UnmarshalJSON(data []byte) error {
|
func (p *Param) UnmarshalJSON(data []byte) error {
|
||||||
var s string
|
var s string
|
||||||
if err := json.Unmarshal(data, &s); err == nil {
|
if err := json.Unmarshal(data, &s); err == nil {
|
||||||
p.Type = stringT
|
p.Type = StringT
|
||||||
p.Value = s
|
p.Value = s
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -131,7 +132,7 @@ func (p *Param) UnmarshalJSON(data []byte) error {
|
||||||
|
|
||||||
var num float64
|
var num float64
|
||||||
if err := json.Unmarshal(data, &num); err == nil {
|
if err := json.Unmarshal(data, &num); err == nil {
|
||||||
p.Type = numberT
|
p.Type = NumberT
|
||||||
p.Value = int(num)
|
p.Value = int(num)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -142,7 +143,7 @@ func (p *Param) UnmarshalJSON(data []byte) error {
|
||||||
jd.DisallowUnknownFields()
|
jd.DisallowUnknownFields()
|
||||||
var fp FuncParam
|
var fp FuncParam
|
||||||
if err := jd.Decode(&fp); err == nil {
|
if err := jd.Decode(&fp); err == nil {
|
||||||
p.Type = funcParamT
|
p.Type = FuncParamT
|
||||||
p.Value = fp
|
p.Value = fp
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -150,7 +151,7 @@ func (p *Param) UnmarshalJSON(data []byte) error {
|
||||||
|
|
||||||
var ps []Param
|
var ps []Param
|
||||||
if err := json.Unmarshal(data, &ps); err == nil {
|
if err := json.Unmarshal(data, &ps); err == nil {
|
||||||
p.Type = arrayT
|
p.Type = ArrayT
|
||||||
p.Value = ps
|
p.Value = ps
|
||||||
|
|
||||||
return nil
|
return nil
|
|
@ -1,4 +1,4 @@
|
||||||
package rpc
|
package request
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
@ -15,35 +15,35 @@ func TestParam_UnmarshalJSON(t *testing.T) {
|
||||||
msg := `["str1", 123, ["str2", 3], [{"type": "String", "value": "jajaja"}]]`
|
msg := `["str1", 123, ["str2", 3], [{"type": "String", "value": "jajaja"}]]`
|
||||||
expected := Params{
|
expected := Params{
|
||||||
{
|
{
|
||||||
Type: stringT,
|
Type: StringT,
|
||||||
Value: "str1",
|
Value: "str1",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: numberT,
|
Type: NumberT,
|
||||||
Value: 123,
|
Value: 123,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: arrayT,
|
Type: ArrayT,
|
||||||
Value: []Param{
|
Value: []Param{
|
||||||
{
|
{
|
||||||
Type: stringT,
|
Type: StringT,
|
||||||
Value: "str2",
|
Value: "str2",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: numberT,
|
Type: NumberT,
|
||||||
Value: 3,
|
Value: 3,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: arrayT,
|
Type: ArrayT,
|
||||||
Value: []Param{
|
Value: []Param{
|
||||||
{
|
{
|
||||||
Type: funcParamT,
|
Type: FuncParamT,
|
||||||
Value: FuncParam{
|
Value: FuncParam{
|
||||||
Type: String,
|
Type: String,
|
||||||
Value: Param{
|
Value: Param{
|
||||||
Type: stringT,
|
Type: StringT,
|
||||||
Value: "jajaja",
|
Value: "jajaja",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -61,34 +61,34 @@ func TestParam_UnmarshalJSON(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParamGetString(t *testing.T) {
|
func TestParamGetString(t *testing.T) {
|
||||||
p := Param{stringT, "jajaja"}
|
p := Param{StringT, "jajaja"}
|
||||||
str, err := p.GetString()
|
str, err := p.GetString()
|
||||||
assert.Equal(t, "jajaja", str)
|
assert.Equal(t, "jajaja", str)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
p = Param{stringT, int(100500)}
|
p = Param{StringT, int(100500)}
|
||||||
_, err = p.GetString()
|
_, err = p.GetString()
|
||||||
require.NotNil(t, err)
|
require.NotNil(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParamGetInt(t *testing.T) {
|
func TestParamGetInt(t *testing.T) {
|
||||||
p := Param{numberT, int(100500)}
|
p := Param{NumberT, int(100500)}
|
||||||
i, err := p.GetInt()
|
i, err := p.GetInt()
|
||||||
assert.Equal(t, 100500, i)
|
assert.Equal(t, 100500, i)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
p = Param{numberT, "jajaja"}
|
p = Param{NumberT, "jajaja"}
|
||||||
_, err = p.GetInt()
|
_, err = p.GetInt()
|
||||||
require.NotNil(t, err)
|
require.NotNil(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParamGetArray(t *testing.T) {
|
func TestParamGetArray(t *testing.T) {
|
||||||
p := Param{arrayT, []Param{{numberT, 42}}}
|
p := Param{ArrayT, []Param{{NumberT, 42}}}
|
||||||
a, err := p.GetArray()
|
a, err := p.GetArray()
|
||||||
assert.Equal(t, []Param{{numberT, 42}}, a)
|
assert.Equal(t, []Param{{NumberT, 42}}, a)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
p = Param{arrayT, 42}
|
p = Param{ArrayT, 42}
|
||||||
_, err = p.GetArray()
|
_, err = p.GetArray()
|
||||||
require.NotNil(t, err)
|
require.NotNil(t, err)
|
||||||
}
|
}
|
||||||
|
@ -96,16 +96,16 @@ func TestParamGetArray(t *testing.T) {
|
||||||
func TestParamGetUint256(t *testing.T) {
|
func TestParamGetUint256(t *testing.T) {
|
||||||
gas := "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"
|
gas := "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"
|
||||||
u256, _ := util.Uint256DecodeStringLE(gas)
|
u256, _ := util.Uint256DecodeStringLE(gas)
|
||||||
p := Param{stringT, gas}
|
p := Param{StringT, gas}
|
||||||
u, err := p.GetUint256()
|
u, err := p.GetUint256()
|
||||||
assert.Equal(t, u256, u)
|
assert.Equal(t, u256, u)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
p = Param{stringT, 42}
|
p = Param{StringT, 42}
|
||||||
_, err = p.GetUint256()
|
_, err = p.GetUint256()
|
||||||
require.NotNil(t, err)
|
require.NotNil(t, err)
|
||||||
|
|
||||||
p = Param{stringT, "qq2c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"}
|
p = Param{StringT, "qq2c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"}
|
||||||
_, err = p.GetUint256()
|
_, err = p.GetUint256()
|
||||||
require.NotNil(t, err)
|
require.NotNil(t, err)
|
||||||
}
|
}
|
||||||
|
@ -113,16 +113,16 @@ func TestParamGetUint256(t *testing.T) {
|
||||||
func TestParamGetUint160FromHex(t *testing.T) {
|
func TestParamGetUint160FromHex(t *testing.T) {
|
||||||
in := "50befd26fdf6e4d957c11e078b24ebce6291456f"
|
in := "50befd26fdf6e4d957c11e078b24ebce6291456f"
|
||||||
u160, _ := util.Uint160DecodeStringLE(in)
|
u160, _ := util.Uint160DecodeStringLE(in)
|
||||||
p := Param{stringT, in}
|
p := Param{StringT, in}
|
||||||
u, err := p.GetUint160FromHex()
|
u, err := p.GetUint160FromHex()
|
||||||
assert.Equal(t, u160, u)
|
assert.Equal(t, u160, u)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
p = Param{stringT, 42}
|
p = Param{StringT, 42}
|
||||||
_, err = p.GetUint160FromHex()
|
_, err = p.GetUint160FromHex()
|
||||||
require.NotNil(t, err)
|
require.NotNil(t, err)
|
||||||
|
|
||||||
p = Param{stringT, "wwbefd26fdf6e4d957c11e078b24ebce6291456f"}
|
p = Param{StringT, "wwbefd26fdf6e4d957c11e078b24ebce6291456f"}
|
||||||
_, err = p.GetUint160FromHex()
|
_, err = p.GetUint160FromHex()
|
||||||
require.NotNil(t, err)
|
require.NotNil(t, err)
|
||||||
}
|
}
|
||||||
|
@ -130,16 +130,16 @@ func TestParamGetUint160FromHex(t *testing.T) {
|
||||||
func TestParamGetUint160FromAddress(t *testing.T) {
|
func TestParamGetUint160FromAddress(t *testing.T) {
|
||||||
in := "AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"
|
in := "AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"
|
||||||
u160, _ := address.StringToUint160(in)
|
u160, _ := address.StringToUint160(in)
|
||||||
p := Param{stringT, in}
|
p := Param{StringT, in}
|
||||||
u, err := p.GetUint160FromAddress()
|
u, err := p.GetUint160FromAddress()
|
||||||
assert.Equal(t, u160, u)
|
assert.Equal(t, u160, u)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
p = Param{stringT, 42}
|
p = Param{StringT, 42}
|
||||||
_, err = p.GetUint160FromAddress()
|
_, err = p.GetUint160FromAddress()
|
||||||
require.NotNil(t, err)
|
require.NotNil(t, err)
|
||||||
|
|
||||||
p = Param{stringT, "QK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"}
|
p = Param{StringT, "QK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"}
|
||||||
_, err = p.GetUint160FromAddress()
|
_, err = p.GetUint160FromAddress()
|
||||||
require.NotNil(t, err)
|
require.NotNil(t, err)
|
||||||
}
|
}
|
||||||
|
@ -148,19 +148,19 @@ func TestParamGetFuncParam(t *testing.T) {
|
||||||
fp := FuncParam{
|
fp := FuncParam{
|
||||||
Type: String,
|
Type: String,
|
||||||
Value: Param{
|
Value: Param{
|
||||||
Type: stringT,
|
Type: StringT,
|
||||||
Value: "jajaja",
|
Value: "jajaja",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
p := Param{
|
p := Param{
|
||||||
Type: funcParamT,
|
Type: FuncParamT,
|
||||||
Value: fp,
|
Value: fp,
|
||||||
}
|
}
|
||||||
newfp, err := p.GetFuncParam()
|
newfp, err := p.GetFuncParam()
|
||||||
assert.Equal(t, fp, newfp)
|
assert.Equal(t, fp, newfp)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
p = Param{funcParamT, 42}
|
p = Param{FuncParamT, 42}
|
||||||
_, err = p.GetFuncParam()
|
_, err = p.GetFuncParam()
|
||||||
require.NotNil(t, err)
|
require.NotNil(t, err)
|
||||||
}
|
}
|
||||||
|
@ -168,16 +168,16 @@ func TestParamGetFuncParam(t *testing.T) {
|
||||||
func TestParamGetBytesHex(t *testing.T) {
|
func TestParamGetBytesHex(t *testing.T) {
|
||||||
in := "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"
|
in := "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"
|
||||||
inb, _ := hex.DecodeString(in)
|
inb, _ := hex.DecodeString(in)
|
||||||
p := Param{stringT, in}
|
p := Param{StringT, in}
|
||||||
bh, err := p.GetBytesHex()
|
bh, err := p.GetBytesHex()
|
||||||
assert.Equal(t, inb, bh)
|
assert.Equal(t, inb, bh)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
p = Param{stringT, 42}
|
p = Param{StringT, 42}
|
||||||
_, err = p.GetBytesHex()
|
_, err = p.GetBytesHex()
|
||||||
require.NotNil(t, err)
|
require.NotNil(t, err)
|
||||||
|
|
||||||
p = Param{stringT, "qq2c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"}
|
p = Param{StringT, "qq2c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"}
|
||||||
_, err = p.GetBytesHex()
|
_, err = p.GetBytesHex()
|
||||||
require.NotNil(t, err)
|
require.NotNil(t, err)
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package rpc
|
package request
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// Params represents the JSON-RPC params.
|
// Params represents the JSON-RPC params.
|
|
@ -1,4 +1,4 @@
|
||||||
package rpc
|
package request
|
||||||
|
|
||||||
// ContractDetails contains contract metadata.
|
// ContractDetails contains contract metadata.
|
||||||
type ContractDetails struct {
|
type ContractDetails struct {
|
|
@ -1,4 +1,4 @@
|
||||||
package rpc
|
package request
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
|
@ -1,4 +1,4 @@
|
||||||
package rpc
|
package request
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
|
@ -1,4 +1,4 @@
|
||||||
package rpc
|
package request
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -25,7 +25,7 @@ func CreateRawContractTransaction(params ContractTxParams) (*transaction.Transac
|
||||||
fromAddress string
|
fromAddress string
|
||||||
receiverOutput *transaction.Output
|
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()
|
fromAddress = wif.PrivateKey.Address()
|
||||||
|
@ -214,15 +214,15 @@ func CreateFunctionInvocationScript(contract util.Uint160, params Params) ([]byt
|
||||||
script := io.NewBufBinWriter()
|
script := io.NewBufBinWriter()
|
||||||
for i := len(params) - 1; i >= 0; i-- {
|
for i := len(params) - 1; i >= 0; i-- {
|
||||||
switch params[i].Type {
|
switch params[i].Type {
|
||||||
case stringT:
|
case StringT:
|
||||||
emit.String(script.BinWriter, params[i].String())
|
emit.String(script.BinWriter, params[i].String())
|
||||||
case numberT:
|
case NumberT:
|
||||||
num, err := params[i].GetInt()
|
num, err := params[i].GetInt()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
emit.String(script.BinWriter, strconv.Itoa(num))
|
emit.String(script.BinWriter, strconv.Itoa(num))
|
||||||
case arrayT:
|
case ArrayT:
|
||||||
slice, err := params[i].GetArray()
|
slice, err := params[i].GetArray()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
|
@ -1,4 +1,4 @@
|
||||||
package rpc
|
package request
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Definition of types, interfaces and variables
|
Definition of types, interfaces and variables
|
||||||
|
@ -16,14 +16,14 @@ type (
|
||||||
// includes parameters duplication `sendtoaddress` RPC call params
|
// includes parameters duplication `sendtoaddress` RPC call params
|
||||||
// and also some utility data;
|
// and also some utility data;
|
||||||
ContractTxParams struct {
|
ContractTxParams struct {
|
||||||
assetID util.Uint256
|
AssetID util.Uint256
|
||||||
address string
|
Address string
|
||||||
value util.Fixed8
|
Value util.Fixed8
|
||||||
wif keys.WIF // a WIF to send the transaction
|
WIF keys.WIF // a WIF to send the transaction
|
||||||
// since there are many ways to provide unspents,
|
// since there are many ways to provide unspents,
|
||||||
// transaction composer stays agnostic to that how
|
// transaction composer stays agnostic to that how
|
||||||
// unspents was got;
|
// unspents was got;
|
||||||
balancer BalanceGetter
|
Balancer BalanceGetter
|
||||||
}
|
}
|
||||||
|
|
||||||
// BalanceGetter is an interface supporting CalculateInputs() method.
|
// 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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -18,10 +18,13 @@ type (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
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{
|
return &Error{
|
||||||
Code: code,
|
Code: code,
|
||||||
HTTPCode: httpCode,
|
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
|
// NewParseError creates a new error with code
|
||||||
// -32700.:%s
|
// -32700.:%s
|
||||||
func NewParseError(data string, cause error) *Error {
|
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
|
// NewInvalidRequestError creates a new error with
|
||||||
// code -32600.
|
// code -32600.
|
||||||
func NewInvalidRequestError(data string, cause error) *Error {
|
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
|
// NewMethodNotFoundError creates a new error with
|
||||||
// code -32601.
|
// code -32601.
|
||||||
func NewMethodNotFoundError(data string, cause error) *Error {
|
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
|
// NewInvalidParamsError creates a new error with
|
||||||
// code -32602.
|
// code -32602.
|
||||||
func NewInvalidParamsError(data string, cause error) *Error {
|
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
|
// NewInternalServerError creates a new error with
|
||||||
// code -32603.
|
// code -32603.
|
||||||
func NewInternalServerError(data string, cause error) *Error {
|
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
|
// NewRPCError creates a new error with
|
||||||
// code -100
|
// code -100
|
||||||
func NewRPCError(message string, data string, cause error) *Error {
|
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.
|
// 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)
|
return fmt.Sprintf("%s (%d) - %s - %s", e.Message, e.Code, e.Data, e.Cause)
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package wrappers
|
package result
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
|
@ -1,4 +1,4 @@
|
||||||
package wrappers
|
package result
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/core/state"
|
"github.com/CityOfZion/neo-go/pkg/core/state"
|
|
@ -1,4 +1,4 @@
|
||||||
package wrappers
|
package result
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
|
@ -1,4 +1,4 @@
|
||||||
package wrappers
|
package result
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/core/state"
|
"github.com/CityOfZion/neo-go/pkg/core/state"
|
|
@ -1,11 +1,12 @@
|
||||||
package wrappers
|
package result
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
"github.com/CityOfZion/neo-go/pkg/vm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// InvokeResult is used as a wrapper to represent an invokation result.
|
// Invoke represents code invocation result and is used by several RPC calls
|
||||||
type InvokeResult struct {
|
// that invoke functions, scripts and generic bytecode.
|
||||||
|
type Invoke struct {
|
||||||
State string `json:"state"`
|
State string `json:"state"`
|
||||||
GasConsumed string `json:"gas_consumed"`
|
GasConsumed string `json:"gas_consumed"`
|
||||||
Script string `json:"script"`
|
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 (
|
import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
|
@ -1,4 +1,4 @@
|
||||||
package wrappers
|
package result
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/core"
|
"github.com/CityOfZion/neo-go/pkg/core"
|
||||||
|
@ -16,9 +16,9 @@ type TransactionOutputRaw struct {
|
||||||
Size int `json:"size"`
|
Size int `json:"size"`
|
||||||
SysFee util.Fixed8 `json:"sys_fee"`
|
SysFee util.Fixed8 `json:"sys_fee"`
|
||||||
NetFee util.Fixed8 `json:"net_fee"`
|
NetFee util.Fixed8 `json:"net_fee"`
|
||||||
Blockhash util.Uint256 `json:"blockhash"`
|
Blockhash util.Uint256 `json:"blockhash,omitempty"`
|
||||||
Confirmations int `json:"confirmations"`
|
Confirmations int `json:"confirmations,omitempty"`
|
||||||
Timestamp uint32 `json:"blocktime"`
|
Timestamp uint32 `json:"blocktime,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTransactionOutputRaw returns a new ransactionOutputRaw object.
|
// NewTransactionOutputRaw returns a new ransactionOutputRaw object.
|
|
@ -1,4 +1,4 @@
|
||||||
package wrappers
|
package result
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/core"
|
"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"
|
import "github.com/prometheus/client_golang/prometheus"
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package rpc
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -11,10 +12,12 @@ import (
|
||||||
"github.com/CityOfZion/neo-go/pkg/core"
|
"github.com/CityOfZion/neo-go/pkg/core"
|
||||||
"github.com/CityOfZion/neo-go/pkg/core/state"
|
"github.com/CityOfZion/neo-go/pkg/core/state"
|
||||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
"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/io"
|
||||||
"github.com/CityOfZion/neo-go/pkg/network"
|
"github.com/CityOfZion/neo-go/pkg/network"
|
||||||
"github.com/CityOfZion/neo-go/pkg/rpc/result"
|
"github.com/CityOfZion/neo-go/pkg/rpc/request"
|
||||||
"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/CityOfZion/neo-go/pkg/util"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"go.uber.org/zap"
|
"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)
|
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.
|
// New creates a new Server struct.
|
||||||
func NewServer(chain core.Blockchainer, conf config.RPCConfig, coreServer *network.Server, log *zap.Logger) Server {
|
func New(chain core.Blockchainer, conf config.RPCConfig, coreServer *network.Server, log *zap.Logger) Server {
|
||||||
httpServer := &http.Server{
|
httpServer := &http.Server{
|
||||||
Addr: conf.Address + ":" + strconv.FormatUint(uint64(conf.Port), 10),
|
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) {
|
func (s *Server) requestHandler(w http.ResponseWriter, httpRequest *http.Request) {
|
||||||
req := NewRequest()
|
req := request.NewIn()
|
||||||
|
|
||||||
if httpRequest.Method != "POST" {
|
if httpRequest.Method != "POST" {
|
||||||
s.WriteErrorResponse(
|
s.WriteErrorResponse(
|
||||||
req,
|
req,
|
||||||
w,
|
w,
|
||||||
NewInvalidParamsError(
|
response.NewInvalidParamsError(
|
||||||
fmt.Sprintf("Invalid method '%s', please retry with 'POST'", httpRequest.Method), nil,
|
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)
|
err := req.DecodeData(httpRequest.Body)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
reqParams, err := req.Params()
|
reqParams, err := req.Params()
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s.methodHandler(w, req, *reqParams)
|
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",
|
s.log.Debug("processing rpc request",
|
||||||
zap.String("method", req.Method),
|
zap.String("method", req.Method),
|
||||||
zap.String("params", fmt.Sprintf("%v", reqParams)))
|
zap.String("params", fmt.Sprintf("%v", reqParams)))
|
||||||
|
@ -121,38 +124,38 @@ Methods:
|
||||||
|
|
||||||
param, ok := reqParams.Value(0)
|
param, ok := reqParams.Value(0)
|
||||||
if !ok {
|
if !ok {
|
||||||
resultsErr = errInvalidParams
|
resultsErr = response.ErrInvalidParams
|
||||||
break Methods
|
break Methods
|
||||||
}
|
}
|
||||||
|
|
||||||
switch param.Type {
|
switch param.Type {
|
||||||
case stringT:
|
case request.StringT:
|
||||||
var err error
|
var err error
|
||||||
hash, err = param.GetUint256()
|
hash, err = param.GetUint256()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resultsErr = errInvalidParams
|
resultsErr = response.ErrInvalidParams
|
||||||
break Methods
|
break Methods
|
||||||
}
|
}
|
||||||
case numberT:
|
case request.NumberT:
|
||||||
num, err := s.blockHeightFromParam(param)
|
num, err := s.blockHeightFromParam(param)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resultsErr = errInvalidParams
|
resultsErr = response.ErrInvalidParams
|
||||||
break Methods
|
break Methods
|
||||||
}
|
}
|
||||||
hash = s.chain.GetHeaderHash(num)
|
hash = s.chain.GetHeaderHash(num)
|
||||||
default:
|
default:
|
||||||
resultsErr = errInvalidParams
|
resultsErr = response.ErrInvalidParams
|
||||||
break Methods
|
break Methods
|
||||||
}
|
}
|
||||||
|
|
||||||
block, err := s.chain.GetBlock(hash)
|
block, err := s.chain.GetBlock(hash)
|
||||||
if err != nil {
|
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
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(reqParams) == 2 && reqParams[1].Value == 1 {
|
if len(reqParams) == 2 && reqParams[1].Value == 1 {
|
||||||
results = wrappers.NewBlock(block, s.chain)
|
results = result.NewBlock(block, s.chain)
|
||||||
} else {
|
} else {
|
||||||
writer := io.NewBufBinWriter()
|
writer := io.NewBufBinWriter()
|
||||||
block.EncodeBinary(writer.BinWriter)
|
block.EncodeBinary(writer.BinWriter)
|
||||||
|
@ -165,14 +168,14 @@ Methods:
|
||||||
|
|
||||||
case "getblockhash":
|
case "getblockhash":
|
||||||
getblockHashCalled.Inc()
|
getblockHashCalled.Inc()
|
||||||
param, ok := reqParams.ValueWithType(0, numberT)
|
param, ok := reqParams.ValueWithType(0, request.NumberT)
|
||||||
if !ok {
|
if !ok {
|
||||||
resultsErr = errInvalidParams
|
resultsErr = response.ErrInvalidParams
|
||||||
break Methods
|
break Methods
|
||||||
}
|
}
|
||||||
num, err := s.blockHeightFromParam(param)
|
num, err := s.blockHeightFromParam(param)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resultsErr = errInvalidParams
|
resultsErr = response.ErrInvalidParams
|
||||||
break Methods
|
break Methods
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,19 +195,10 @@ Methods:
|
||||||
|
|
||||||
case "getpeers":
|
case "getpeers":
|
||||||
getpeersCalled.Inc()
|
getpeersCalled.Inc()
|
||||||
peers := result.NewPeers()
|
peers := result.NewGetPeers()
|
||||||
for _, addr := range s.coreServer.UnconnectedPeers() {
|
peers.AddUnconnected(s.coreServer.UnconnectedPeers())
|
||||||
peers.AddPeer("unconnected", addr)
|
peers.AddConnected(s.coreServer.ConnectedPeers())
|
||||||
}
|
peers.AddBad(s.coreServer.BadPeers())
|
||||||
|
|
||||||
for _, addr := range s.coreServer.BadPeers() {
|
|
||||||
peers.AddPeer("bad", addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
for addr := range s.coreServer.Peers() {
|
|
||||||
peers.AddPeer("connected", addr.PeerAddr().String())
|
|
||||||
}
|
|
||||||
|
|
||||||
results = peers
|
results = peers
|
||||||
|
|
||||||
case "getstorage":
|
case "getstorage":
|
||||||
|
@ -215,30 +209,30 @@ Methods:
|
||||||
validateaddressCalled.Inc()
|
validateaddressCalled.Inc()
|
||||||
param, ok := reqParams.Value(0)
|
param, ok := reqParams.Value(0)
|
||||||
if !ok {
|
if !ok {
|
||||||
resultsErr = errInvalidParams
|
resultsErr = response.ErrInvalidParams
|
||||||
break Methods
|
break Methods
|
||||||
}
|
}
|
||||||
results = wrappers.ValidateAddress(param.Value)
|
results = validateAddress(param.Value)
|
||||||
|
|
||||||
case "getassetstate":
|
case "getassetstate":
|
||||||
getassetstateCalled.Inc()
|
getassetstateCalled.Inc()
|
||||||
param, ok := reqParams.ValueWithType(0, stringT)
|
param, ok := reqParams.ValueWithType(0, request.StringT)
|
||||||
if !ok {
|
if !ok {
|
||||||
resultsErr = errInvalidParams
|
resultsErr = response.ErrInvalidParams
|
||||||
break Methods
|
break Methods
|
||||||
}
|
}
|
||||||
|
|
||||||
paramAssetID, err := param.GetUint256()
|
paramAssetID, err := param.GetUint256()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resultsErr = errInvalidParams
|
resultsErr = response.ErrInvalidParams
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
as := s.chain.GetAssetState(paramAssetID)
|
as := s.chain.GetAssetState(paramAssetID)
|
||||||
if as != nil {
|
if as != nil {
|
||||||
results = wrappers.NewAssetState(as)
|
results = result.NewAssetState(as)
|
||||||
} else {
|
} else {
|
||||||
resultsErr = NewRPCError("Unknown asset", "", nil)
|
resultsErr = response.NewRPCError("Unknown asset", "", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
case "getaccountstate":
|
case "getaccountstate":
|
||||||
|
@ -275,7 +269,7 @@ Methods:
|
||||||
results, resultsErr = s.sendrawtransaction(reqParams)
|
results, resultsErr = s.sendrawtransaction(reqParams)
|
||||||
|
|
||||||
default:
|
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 {
|
if resultsErr != nil {
|
||||||
|
@ -286,27 +280,27 @@ Methods:
|
||||||
s.WriteResponse(req, w, results)
|
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)
|
param, ok := ps.Value(0)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errInvalidParams
|
return nil, response.ErrInvalidParams
|
||||||
}
|
}
|
||||||
|
|
||||||
scriptHash, err := param.GetUint160FromHex()
|
scriptHash, err := param.GetUint160FromHex()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errInvalidParams
|
return nil, response.ErrInvalidParams
|
||||||
}
|
}
|
||||||
|
|
||||||
scriptHash = scriptHash.Reverse()
|
scriptHash = scriptHash.Reverse()
|
||||||
|
|
||||||
param, ok = ps.Value(1)
|
param, ok = ps.Value(1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errInvalidParams
|
return nil, response.ErrInvalidParams
|
||||||
}
|
}
|
||||||
|
|
||||||
key, err := param.GetBytesHex()
|
key, err := param.GetBytesHex()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errInvalidParams
|
return nil, response.ErrInvalidParams
|
||||||
}
|
}
|
||||||
|
|
||||||
item := s.chain.GetStorageItem(scriptHash.Reverse(), key)
|
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
|
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 resultsErr error
|
||||||
var results interface{}
|
var results interface{}
|
||||||
|
|
||||||
if param0, ok := reqParams.Value(0); !ok {
|
if param0, ok := reqParams.Value(0); !ok {
|
||||||
return nil, errInvalidParams
|
return nil, response.ErrInvalidParams
|
||||||
} else if txHash, err := param0.GetUint256(); err != nil {
|
} else if txHash, err := param0.GetUint256(); err != nil {
|
||||||
resultsErr = errInvalidParams
|
resultsErr = response.ErrInvalidParams
|
||||||
} else if tx, height, err := s.chain.GetTransaction(txHash); err != nil {
|
} else if tx, height, err := s.chain.GetTransaction(txHash); err != nil {
|
||||||
err = errors.Wrapf(err, "Invalid transaction hash: %s", txHash)
|
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 {
|
} else if len(reqParams) >= 2 {
|
||||||
_header := s.chain.GetHeaderHash(int(height))
|
_header := s.chain.GetHeaderHash(int(height))
|
||||||
header, err := s.chain.GetHeader(_header)
|
header, err := s.chain.GetHeader(_header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resultsErr = NewInvalidParamsError(err.Error(), err)
|
resultsErr = response.NewInvalidParamsError(err.Error(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
param1, _ := reqParams.Value(1)
|
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" {
|
if v == 0 || v == "0" || v == 0.0 || v == false || v == "false" {
|
||||||
results = hex.EncodeToString(tx.Bytes())
|
results = hex.EncodeToString(tx.Bytes())
|
||||||
} else {
|
} else {
|
||||||
results = wrappers.NewTransactionOutputRaw(tx, header, s.chain)
|
results = result.NewTransactionOutputRaw(tx, header, s.chain)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
results = wrappers.NewTransactionOutputRaw(tx, header, s.chain)
|
results = result.NewTransactionOutputRaw(tx, header, s.chain)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
results = hex.EncodeToString(tx.Bytes())
|
results = hex.EncodeToString(tx.Bytes())
|
||||||
|
@ -354,70 +348,70 @@ func (s *Server) getrawtransaction(reqParams Params) (interface{}, error) {
|
||||||
return results, resultsErr
|
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)
|
p, ok := ps.Value(0)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errInvalidParams
|
return nil, response.ErrInvalidParams
|
||||||
}
|
}
|
||||||
|
|
||||||
h, err := p.GetUint256()
|
h, err := p.GetUint256()
|
||||||
if err != nil {
|
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 {
|
if !ok {
|
||||||
return nil, errInvalidParams
|
return nil, response.ErrInvalidParams
|
||||||
}
|
}
|
||||||
|
|
||||||
num, err := p.GetInt()
|
num, err := p.GetInt()
|
||||||
if err != nil || num < 0 {
|
if err != nil || num < 0 {
|
||||||
return nil, errInvalidParams
|
return nil, response.ErrInvalidParams
|
||||||
}
|
}
|
||||||
|
|
||||||
tx, _, err := s.chain.GetTransaction(h)
|
tx, _, err := s.chain.GetTransaction(h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewInvalidParamsError(err.Error(), err)
|
return nil, response.NewInvalidParamsError(err.Error(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if num >= len(tx.Outputs) {
|
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]
|
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).
|
// 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{}
|
var results interface{}
|
||||||
|
|
||||||
param, ok := reqParams.ValueWithType(0, stringT)
|
param, ok := reqParams.ValueWithType(0, request.StringT)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errInvalidParams
|
return nil, response.ErrInvalidParams
|
||||||
} else if scriptHash, err := param.GetUint160FromHex(); err != nil {
|
} else if scriptHash, err := param.GetUint160FromHex(); err != nil {
|
||||||
return nil, errInvalidParams
|
return nil, response.ErrInvalidParams
|
||||||
} else {
|
} else {
|
||||||
cs := s.chain.GetContractState(scriptHash)
|
cs := s.chain.GetContractState(scriptHash)
|
||||||
if cs != nil {
|
if cs != nil {
|
||||||
results = wrappers.NewContractState(cs)
|
results = result.NewContractState(cs)
|
||||||
} else {
|
} else {
|
||||||
return nil, NewRPCError("Unknown contract", "", nil)
|
return nil, response.NewRPCError("Unknown contract", "", nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getAccountState returns account state either in short or full (unspents included) form.
|
// 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 resultsErr error
|
||||||
var results interface{}
|
var results interface{}
|
||||||
|
|
||||||
param, ok := reqParams.ValueWithType(0, stringT)
|
param, ok := reqParams.ValueWithType(0, request.StringT)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errInvalidParams
|
return nil, response.ErrInvalidParams
|
||||||
} else if scriptHash, err := param.GetUint160FromAddress(); err != nil {
|
} else if scriptHash, err := param.GetUint160FromAddress(); err != nil {
|
||||||
return nil, errInvalidParams
|
return nil, response.ErrInvalidParams
|
||||||
} else {
|
} else {
|
||||||
as := s.chain.GetAccountState(scriptHash)
|
as := s.chain.GetAccountState(scriptHash)
|
||||||
if as == nil {
|
if as == nil {
|
||||||
|
@ -426,35 +420,35 @@ func (s *Server) getAccountState(reqParams Params, unspents bool) (interface{},
|
||||||
if unspents {
|
if unspents {
|
||||||
str, err := param.GetString()
|
str, err := param.GetString()
|
||||||
if err != nil {
|
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 {
|
} else {
|
||||||
results = wrappers.NewAccountState(as)
|
results = result.NewAccountState(as)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return results, resultsErr
|
return results, resultsErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// invoke implements the `invoke` RPC call.
|
// invoke implements the `invoke` RPC call.
|
||||||
func (s *Server) invoke(reqParams Params) (interface{}, error) {
|
func (s *Server) invoke(reqParams request.Params) (interface{}, error) {
|
||||||
scriptHashHex, ok := reqParams.ValueWithType(0, stringT)
|
scriptHashHex, ok := reqParams.ValueWithType(0, request.StringT)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errInvalidParams
|
return nil, response.ErrInvalidParams
|
||||||
}
|
}
|
||||||
scriptHash, err := scriptHashHex.GetUint160FromHex()
|
scriptHash, err := scriptHashHex.GetUint160FromHex()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
sliceP, ok := reqParams.ValueWithType(1, arrayT)
|
sliceP, ok := reqParams.ValueWithType(1, request.ArrayT)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errInvalidParams
|
return nil, response.ErrInvalidParams
|
||||||
}
|
}
|
||||||
slice, err := sliceP.GetArray()
|
slice, err := sliceP.GetArray()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
script, err := CreateInvocationScript(scriptHash, slice)
|
script, err := request.CreateInvocationScript(scriptHash, slice)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -462,16 +456,16 @@ func (s *Server) invoke(reqParams Params) (interface{}, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// invokescript implements the `invokescript` RPC call.
|
// invokescript implements the `invokescript` RPC call.
|
||||||
func (s *Server) invokeFunction(reqParams Params) (interface{}, error) {
|
func (s *Server) invokeFunction(reqParams request.Params) (interface{}, error) {
|
||||||
scriptHashHex, ok := reqParams.ValueWithType(0, stringT)
|
scriptHashHex, ok := reqParams.ValueWithType(0, request.StringT)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errInvalidParams
|
return nil, response.ErrInvalidParams
|
||||||
}
|
}
|
||||||
scriptHash, err := scriptHashHex.GetUint160FromHex()
|
scriptHash, err := scriptHashHex.GetUint160FromHex()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
script, err := CreateFunctionInvocationScript(scriptHash, reqParams[1:])
|
script, err := request.CreateFunctionInvocationScript(scriptHash, reqParams[1:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -479,14 +473,14 @@ func (s *Server) invokeFunction(reqParams Params) (interface{}, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// invokescript implements the `invokescript` RPC call.
|
// 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 {
|
if len(reqParams) < 1 {
|
||||||
return nil, errInvalidParams
|
return nil, response.ErrInvalidParams
|
||||||
}
|
}
|
||||||
|
|
||||||
script, err := reqParams[0].GetBytesHex()
|
script, err := reqParams[0].GetBytesHex()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errInvalidParams
|
return nil, response.ErrInvalidParams
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.runScriptInVM(script), nil
|
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
|
// runScriptInVM runs given script in a new test VM and returns the invocation
|
||||||
// result.
|
// result.
|
||||||
func (s *Server) runScriptInVM(script []byte) *wrappers.InvokeResult {
|
func (s *Server) runScriptInVM(script []byte) *result.Invoke {
|
||||||
vm, _ := s.chain.GetTestVM()
|
vm, _ := s.chain.GetTestVM()
|
||||||
vm.SetGasLimit(s.config.MaxGasInvoke)
|
vm.SetGasLimit(s.config.MaxGasInvoke)
|
||||||
vm.LoadScript(script)
|
vm.LoadScript(script)
|
||||||
_ = vm.Run()
|
_ = vm.Run()
|
||||||
result := &wrappers.InvokeResult{
|
result := &result.Invoke{
|
||||||
State: vm.State(),
|
State: vm.State(),
|
||||||
GasConsumed: vm.GasConsumed().String(),
|
GasConsumed: vm.GasConsumed().String(),
|
||||||
Script: hex.EncodeToString(script),
|
Script: hex.EncodeToString(script),
|
||||||
|
@ -508,14 +502,14 @@ func (s *Server) runScriptInVM(script []byte) *wrappers.InvokeResult {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) sendrawtransaction(reqParams Params) (interface{}, error) {
|
func (s *Server) sendrawtransaction(reqParams request.Params) (interface{}, error) {
|
||||||
var resultsErr error
|
var resultsErr error
|
||||||
var results interface{}
|
var results interface{}
|
||||||
|
|
||||||
if len(reqParams) < 1 {
|
if len(reqParams) < 1 {
|
||||||
return nil, errInvalidParams
|
return nil, response.ErrInvalidParams
|
||||||
} else if byteTx, err := reqParams[0].GetBytesHex(); err != nil {
|
} else if byteTx, err := reqParams[0].GetBytesHex(); err != nil {
|
||||||
return nil, errInvalidParams
|
return nil, response.ErrInvalidParams
|
||||||
} else {
|
} else {
|
||||||
r := io.NewBinReaderFromBuf(byteTx)
|
r := io.NewBinReaderFromBuf(byteTx)
|
||||||
tx := &transaction.Transaction{}
|
tx := &transaction.Transaction{}
|
||||||
|
@ -542,14 +536,14 @@ func (s *Server) sendrawtransaction(reqParams Params) (interface{}, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resultsErr = NewInternalServerError(err.Error(), err)
|
resultsErr = response.NewInternalServerError(err.Error(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return results, resultsErr
|
return results, resultsErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Server) blockHeightFromParam(param *Param) (int, error) {
|
func (s *Server) blockHeightFromParam(param *request.Param) (int, error) {
|
||||||
num, err := param.GetInt()
|
num, err := param.GetInt()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
|
@ -560,3 +554,87 @@ func (s Server) blockHeightFromParam(param *Param) (int, error) {
|
||||||
}
|
}
|
||||||
return num, nil
|
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 (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -14,7 +14,8 @@ import (
|
||||||
|
|
||||||
"github.com/CityOfZion/neo-go/pkg/core"
|
"github.com/CityOfZion/neo-go/pkg/core"
|
||||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
"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/CityOfZion/neo-go/pkg/util"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -43,23 +44,23 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
{
|
{
|
||||||
name: "positive",
|
name: "positive",
|
||||||
params: `["AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU"]`,
|
params: `["AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU"]`,
|
||||||
result: func(e *executor) interface{} { return &GetAccountStateResponse{} },
|
result: func(e *executor) interface{} { return &result.AccountState{} },
|
||||||
check: func(t *testing.T, e *executor, result interface{}) {
|
check: func(t *testing.T, e *executor, acc interface{}) {
|
||||||
res, ok := result.(*GetAccountStateResponse)
|
res, ok := acc.(*result.AccountState)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
assert.Equal(t, 1, len(res.Result.Balances))
|
assert.Equal(t, 1, len(res.Balances))
|
||||||
assert.Equal(t, false, res.Result.Frozen)
|
assert.Equal(t, false, res.IsFrozen)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "positive null",
|
name: "positive null",
|
||||||
params: `["AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"]`,
|
params: `["AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"]`,
|
||||||
result: func(e *executor) interface{} { return &GetAccountStateResponse{} },
|
result: func(e *executor) interface{} { return &result.AccountState{} },
|
||||||
check: func(t *testing.T, e *executor, result interface{}) {
|
check: func(t *testing.T, e *executor, acc interface{}) {
|
||||||
res, ok := result.(*GetAccountStateResponse)
|
res, ok := acc.(*result.AccountState)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
assert.Equal(t, 0, len(res.Result.Balances))
|
assert.Equal(t, 0, len(res.Balances))
|
||||||
assert.Equal(t, false, res.Result.Frozen)
|
assert.Equal(t, false, res.IsFrozen)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -77,13 +78,13 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
{
|
{
|
||||||
name: "positive",
|
name: "positive",
|
||||||
params: `["1a696b32e239dd5eace3f025cac0a193a5746a27"]`,
|
params: `["1a696b32e239dd5eace3f025cac0a193a5746a27"]`,
|
||||||
result: func(e *executor) interface{} { return &GetContractStateResponce{} },
|
result: func(e *executor) interface{} { return &result.ContractState{} },
|
||||||
check: func(t *testing.T, e *executor, result interface{}) {
|
check: func(t *testing.T, e *executor, cs interface{}) {
|
||||||
res, ok := result.(*GetContractStateResponce)
|
res, ok := cs.(*result.ContractState)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
assert.Equal(t, byte(0), res.Result.Version)
|
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.Result.ScriptHash)
|
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.Result.CodeVersion)
|
assert.Equal(t, "0.99", res.CodeVersion)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -106,21 +107,17 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
{
|
{
|
||||||
name: "positive",
|
name: "positive",
|
||||||
params: `["1a696b32e239dd5eace3f025cac0a193a5746a27", "746573746b6579"]`,
|
params: `["1a696b32e239dd5eace3f025cac0a193a5746a27", "746573746b6579"]`,
|
||||||
result: func(e *executor) interface{} { return &StringResultResponse{} },
|
result: func(e *executor) interface{} {
|
||||||
check: func(t *testing.T, e *executor, result interface{}) {
|
v := hex.EncodeToString([]byte("testvalue"))
|
||||||
res, ok := result.(*StringResultResponse)
|
return &v
|
||||||
require.True(t, ok)
|
|
||||||
assert.Equal(t, hex.EncodeToString([]byte("testvalue")), res.Result)
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "missing key",
|
name: "missing key",
|
||||||
params: `["1a696b32e239dd5eace3f025cac0a193a5746a27", "7465"]`,
|
params: `["1a696b32e239dd5eace3f025cac0a193a5746a27", "7465"]`,
|
||||||
result: func(e *executor) interface{} { return &StringResultResponse{} },
|
result: func(e *executor) interface{} {
|
||||||
check: func(t *testing.T, e *executor, result interface{}) {
|
v := ""
|
||||||
res, ok := result.(*StringResultResponse)
|
return &v
|
||||||
require.True(t, ok)
|
|
||||||
assert.Equal(t, "", res.Result)
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -148,12 +145,12 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
{
|
{
|
||||||
name: "positive",
|
name: "positive",
|
||||||
params: `["602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"]`,
|
params: `["602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"]`,
|
||||||
result: func(e *executor) interface{} { return &GetAssetResponse{} },
|
result: func(e *executor) interface{} { return &result.AssetState{} },
|
||||||
check: func(t *testing.T, e *executor, result interface{}) {
|
check: func(t *testing.T, e *executor, as interface{}) {
|
||||||
res, ok := result.(*GetAssetResponse)
|
res, ok := as.(*result.AssetState)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
assert.Equal(t, "00", res.Result.Owner)
|
assert.Equal(t, "00", res.Owner)
|
||||||
assert.Equal(t, "AWKECj9RD8rS8RPcpCgYVjk1DeYyHwxZm3", res.Result.Admin)
|
assert.Equal(t, "AWKECj9RD8rS8RPcpCgYVjk1DeYyHwxZm3", res.Admin)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -176,7 +173,8 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
{
|
{
|
||||||
params: "[]",
|
params: "[]",
|
||||||
result: func(e *executor) interface{} {
|
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",
|
name: "positive",
|
||||||
params: "[1, 1]",
|
params: "[1, 1]",
|
||||||
result: func(e *executor) interface{} { return &GetBlockResponse{} },
|
result: func(e *executor) interface{} { return &result.Block{} },
|
||||||
check: func(t *testing.T, e *executor, result interface{}) {
|
check: func(t *testing.T, e *executor, blockRes interface{}) {
|
||||||
res, ok := result.(*GetBlockResponse)
|
res, ok := blockRes.(*result.Block)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
|
|
||||||
block, err := e.chain.GetBlock(e.chain.GetHeaderHash(1))
|
block, err := e.chain.GetBlock(e.chain.GetHeaderHash(1))
|
||||||
require.NoErrorf(t, err, "could not get block")
|
require.NoErrorf(t, err, "could not get block")
|
||||||
|
|
||||||
assert.Equal(t, block.Hash(), res.Result.Hash)
|
assert.Equal(t, block.Hash(), res.Hash)
|
||||||
for i := range res.Result.Tx {
|
for i := range res.Tx {
|
||||||
tx := res.Result.Tx[i]
|
tx := res.Tx[i]
|
||||||
require.Equal(t, transaction.MinerType, tx.Type)
|
require.Equal(t, transaction.MinerType, tx.Type)
|
||||||
|
|
||||||
miner, ok := block.Transactions[i].Data.(*transaction.MinerTX)
|
miner, ok := block.Transactions[i].Data.(*transaction.MinerTX)
|
||||||
|
@ -269,22 +267,21 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
"getblockcount": {
|
"getblockcount": {
|
||||||
{
|
{
|
||||||
params: "[]",
|
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": {
|
"getblockhash": {
|
||||||
{
|
{
|
||||||
params: "[1]",
|
params: "[1]",
|
||||||
result: func(e *executor) interface{} { return "" },
|
result: func(e *executor) interface{} {
|
||||||
check: func(t *testing.T, e *executor, result interface{}) {
|
// We don't have `t` here for proper handling, but
|
||||||
res, ok := result.(*StringResultResponse)
|
// error here would lead to panic down below.
|
||||||
require.True(t, ok)
|
block, _ := e.chain.GetBlock(e.chain.GetHeaderHash(1))
|
||||||
|
|
||||||
block, err := e.chain.GetBlock(e.chain.GetHeaderHash(1))
|
|
||||||
require.NoErrorf(t, err, "could not get block")
|
|
||||||
|
|
||||||
expectedHash := "0x" + block.Hash().StringLE()
|
expectedHash := "0x" + block.Hash().StringLE()
|
||||||
assert.Equal(t, expectedHash, res.Result)
|
return &expectedHash
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -301,25 +298,20 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
"getconnectioncount": {
|
"getconnectioncount": {
|
||||||
{
|
{
|
||||||
params: "[]",
|
params: "[]",
|
||||||
result: func(*executor) interface{} { return 0 },
|
result: func(*executor) interface{} {
|
||||||
|
v := 0
|
||||||
|
return &v
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"getpeers": {
|
"getpeers": {
|
||||||
{
|
{
|
||||||
params: "[]",
|
params: "[]",
|
||||||
result: func(*executor) interface{} {
|
result: func(*executor) interface{} {
|
||||||
return &GetPeersResponse{
|
return &result.GetPeers{
|
||||||
Jsonrpc: defaultJSONRPC,
|
Unconnected: []result.Peer{},
|
||||||
Result: struct {
|
Connected: []result.Peer{},
|
||||||
Unconnected []int `json:"unconnected"`
|
Bad: []result.Peer{},
|
||||||
Connected []int `json:"connected"`
|
|
||||||
Bad []int `json:"bad"`
|
|
||||||
}{
|
|
||||||
Unconnected: []int{},
|
|
||||||
Connected: []int{},
|
|
||||||
Bad: []int{},
|
|
||||||
},
|
|
||||||
ID: defaultID,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -345,33 +337,33 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
{
|
{
|
||||||
name: "positive",
|
name: "positive",
|
||||||
params: `["AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU"]`,
|
params: `["AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU"]`,
|
||||||
result: func(e *executor) interface{} { return &GetUnspents{} },
|
result: func(e *executor) interface{} { return &result.Unspents{} },
|
||||||
check: func(t *testing.T, e *executor, result interface{}) {
|
check: func(t *testing.T, e *executor, unsp interface{}) {
|
||||||
res, ok := result.(*GetUnspents)
|
res, ok := unsp.(*result.Unspents)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
require.Equal(t, 1, len(res.Result.Balance))
|
require.Equal(t, 1, len(res.Balance))
|
||||||
assert.Equal(t, 1, len(res.Result.Balance[0].Unspents))
|
assert.Equal(t, 1, len(res.Balance[0].Unspents))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "positive null",
|
name: "positive null",
|
||||||
params: `["AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"]`,
|
params: `["AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"]`,
|
||||||
result: func(e *executor) interface{} { return &GetUnspents{} },
|
result: func(e *executor) interface{} { return &result.Unspents{} },
|
||||||
check: func(t *testing.T, e *executor, result interface{}) {
|
check: func(t *testing.T, e *executor, unsp interface{}) {
|
||||||
res, ok := result.(*GetUnspents)
|
res, ok := unsp.(*result.Unspents)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
require.Equal(t, 0, len(res.Result.Balance))
|
require.Equal(t, 0, len(res.Balance))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"getversion": {
|
"getversion": {
|
||||||
{
|
{
|
||||||
params: "[]",
|
params: "[]",
|
||||||
result: func(*executor) interface{} { return &GetVersionResponse{} },
|
result: func(*executor) interface{} { return &result.Version{} },
|
||||||
check: func(t *testing.T, e *executor, result interface{}) {
|
check: func(t *testing.T, e *executor, ver interface{}) {
|
||||||
resp, ok := result.(*GetVersionResponse)
|
resp, ok := ver.(*result.Version)
|
||||||
require.True(t, ok)
|
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",
|
name: "positive",
|
||||||
params: `["50befd26fdf6e4d957c11e078b24ebce6291456f", [{"type": "String", "value": "qwerty"}]]`,
|
params: `["50befd26fdf6e4d957c11e078b24ebce6291456f", [{"type": "String", "value": "qwerty"}]]`,
|
||||||
result: func(e *executor) interface{} { return &InvokeFunctionResponse{} },
|
result: func(e *executor) interface{} { return &InvokeFunctionResult{} },
|
||||||
check: func(t *testing.T, e *executor, result interface{}) {
|
check: func(t *testing.T, e *executor, inv interface{}) {
|
||||||
res, ok := result.(*InvokeFunctionResponse)
|
res, ok := inv.(*InvokeFunctionResult)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
assert.Equal(t, "06717765727479676f459162ceeb248b071ec157d9e4f6fd26fdbe50", res.Result.Script)
|
assert.Equal(t, "06717765727479676f459162ceeb248b071ec157d9e4f6fd26fdbe50", res.Script)
|
||||||
assert.NotEqual(t, "", res.Result.State)
|
assert.NotEqual(t, "", res.State)
|
||||||
assert.NotEqual(t, 0, res.Result.GasConsumed)
|
assert.NotEqual(t, 0, res.GasConsumed)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -418,13 +410,13 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
{
|
{
|
||||||
name: "positive",
|
name: "positive",
|
||||||
params: `["50befd26fdf6e4d957c11e078b24ebce6291456f", "test", []]`,
|
params: `["50befd26fdf6e4d957c11e078b24ebce6291456f", "test", []]`,
|
||||||
result: func(e *executor) interface{} { return &InvokeFunctionResponse{} },
|
result: func(e *executor) interface{} { return &InvokeFunctionResult{} },
|
||||||
check: func(t *testing.T, e *executor, result interface{}) {
|
check: func(t *testing.T, e *executor, inv interface{}) {
|
||||||
res, ok := result.(*InvokeFunctionResponse)
|
res, ok := inv.(*InvokeFunctionResult)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
assert.NotEqual(t, "", res.Result.Script)
|
assert.NotEqual(t, "", res.Script)
|
||||||
assert.NotEqual(t, "", res.Result.State)
|
assert.NotEqual(t, "", res.State)
|
||||||
assert.NotEqual(t, 0, res.Result.GasConsumed)
|
assert.NotEqual(t, 0, res.GasConsumed)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -452,13 +444,13 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
{
|
{
|
||||||
name: "positive",
|
name: "positive",
|
||||||
params: `["51c56b0d48656c6c6f2c20776f726c6421680f4e656f2e52756e74696d652e4c6f67616c7566"]`,
|
params: `["51c56b0d48656c6c6f2c20776f726c6421680f4e656f2e52756e74696d652e4c6f67616c7566"]`,
|
||||||
result: func(e *executor) interface{} { return &InvokeFunctionResponse{} },
|
result: func(e *executor) interface{} { return &InvokeFunctionResult{} },
|
||||||
check: func(t *testing.T, e *executor, result interface{}) {
|
check: func(t *testing.T, e *executor, inv interface{}) {
|
||||||
res, ok := result.(*InvokeFunctionResponse)
|
res, ok := inv.(*InvokeFunctionResult)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
assert.NotEqual(t, "", res.Result.Script)
|
assert.NotEqual(t, "", res.Script)
|
||||||
assert.NotEqual(t, "", res.Result.State)
|
assert.NotEqual(t, "", res.State)
|
||||||
assert.NotEqual(t, 0, res.Result.GasConsumed)
|
assert.NotEqual(t, 0, res.GasConsumed)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -481,11 +473,9 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
{
|
{
|
||||||
name: "positive",
|
name: "positive",
|
||||||
params: `["d1001b00046e616d6567d3d8602814a429a91afdbaa3914884a1c90c733101201cc9c05cefffe6cdd7b182816a9152ec218d2ec000000141403387ef7940a5764259621e655b3c621a6aafd869a611ad64adcc364d8dd1edf84e00a7f8b11b630a377eaef02791d1c289d711c08b7ad04ff0d6c9caca22cfe6232103cbb45da6072c14761c9da545749d9cfd863f860c351066d16df480602a2024c6ac"]`,
|
params: `["d1001b00046e616d6567d3d8602814a429a91afdbaa3914884a1c90c733101201cc9c05cefffe6cdd7b182816a9152ec218d2ec000000141403387ef7940a5764259621e655b3c621a6aafd869a611ad64adcc364d8dd1edf84e00a7f8b11b630a377eaef02791d1c289d711c08b7ad04ff0d6c9caca22cfe6232103cbb45da6072c14761c9da545749d9cfd863f860c351066d16df480602a2024c6ac"]`,
|
||||||
result: func(e *executor) interface{} { return &SendTXResponse{} },
|
result: func(e *executor) interface{} {
|
||||||
check: func(t *testing.T, e *executor, result interface{}) {
|
v := true
|
||||||
res, ok := result.(*SendTXResponse)
|
return &v
|
||||||
require.True(t, ok)
|
|
||||||
assert.True(t, res.Result)
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -513,25 +503,21 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
{
|
{
|
||||||
name: "positive",
|
name: "positive",
|
||||||
params: `["AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i"]`,
|
params: `["AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i"]`,
|
||||||
result: func(*executor) interface{} { return &ValidateAddrResponse{} },
|
result: func(*executor) interface{} { return &result.ValidateAddress{} },
|
||||||
check: func(t *testing.T, e *executor, result interface{}) {
|
check: func(t *testing.T, e *executor, va interface{}) {
|
||||||
res, ok := result.(*ValidateAddrResponse)
|
res, ok := va.(*result.ValidateAddress)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
assert.Equal(t, "AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i", res.Result.Address)
|
assert.Equal(t, "AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i", res.Address)
|
||||||
assert.True(t, res.Result.IsValid)
|
assert.True(t, res.IsValid)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "negative",
|
name: "negative",
|
||||||
params: "[1]",
|
params: "[1]",
|
||||||
result: func(*executor) interface{} {
|
result: func(*executor) interface{} {
|
||||||
return &ValidateAddrResponse{
|
return &result.ValidateAddress{
|
||||||
Jsonrpc: defaultJSONRPC,
|
|
||||||
Result: wrappers.ValidateAddressResponse{
|
|
||||||
Address: float64(1),
|
Address: float64(1),
|
||||||
IsValid: false,
|
IsValid: false,
|
||||||
},
|
|
||||||
ID: defaultID,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -551,14 +537,14 @@ func TestRPC(t *testing.T) {
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
body := doRPCCall(fmt.Sprintf(rpc, method, tc.params), handler, 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 {
|
if tc.fail {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
expected, res := tc.getResultPair(e)
|
expected, res := tc.getResultPair(e)
|
||||||
err := json.Unmarshal(body, res)
|
err := json.Unmarshal(result, res)
|
||||||
require.NoErrorf(t, err, "could not parse response: %s", body)
|
require.NoErrorf(t, err, "could not parse response: %s", result)
|
||||||
|
|
||||||
if tc.check == nil {
|
if tc.check == nil {
|
||||||
assert.Equal(t, expected, res)
|
assert.Equal(t, expected, res)
|
||||||
|
@ -575,11 +561,11 @@ func TestRPC(t *testing.T) {
|
||||||
TXHash := block.Transactions[1].Hash()
|
TXHash := block.Transactions[1].Hash()
|
||||||
rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s"]}"`, TXHash.StringLE())
|
rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s"]}"`, TXHash.StringLE())
|
||||||
body := doRPCCall(rpc, handler, t)
|
body := doRPCCall(rpc, handler, t)
|
||||||
checkErrResponse(t, body, false)
|
result := checkErrGetResult(t, body, false)
|
||||||
var res StringResultResponse
|
var res string
|
||||||
err := json.Unmarshal(body, &res)
|
err := json.Unmarshal(result, &res)
|
||||||
require.NoErrorf(t, err, "could not parse response: %s", body)
|
require.NoErrorf(t, err, "could not parse response: %s", result)
|
||||||
assert.Equal(t, "400000455b7b226c616e67223a227a682d434e222c226e616d65223a22e5b08fe89a81e882a1227d2c7b226c616e67223a22656e222c226e616d65223a22416e745368617265227d5d0000c16ff28623000000da1745e9b549bd0bfa1a569971c77eba30cd5a4b00000000", res.Result)
|
assert.Equal(t, "400000455b7b226c616e67223a227a682d434e222c226e616d65223a22e5b08fe89a81e882a1227d2c7b226c616e67223a22656e222c226e616d65223a22416e745368617265227d5d0000c16ff28623000000da1745e9b549bd0bfa1a569971c77eba30cd5a4b00000000", res)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("getrawtransaction 2 arguments", func(t *testing.T) {
|
t.Run("getrawtransaction 2 arguments", func(t *testing.T) {
|
||||||
|
@ -587,11 +573,11 @@ func TestRPC(t *testing.T) {
|
||||||
TXHash := block.Transactions[1].Hash()
|
TXHash := block.Transactions[1].Hash()
|
||||||
rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s", 0]}"`, TXHash.StringLE())
|
rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s", 0]}"`, TXHash.StringLE())
|
||||||
body := doRPCCall(rpc, handler, t)
|
body := doRPCCall(rpc, handler, t)
|
||||||
checkErrResponse(t, body, false)
|
result := checkErrGetResult(t, body, false)
|
||||||
var res StringResultResponse
|
var res string
|
||||||
err := json.Unmarshal(body, &res)
|
err := json.Unmarshal(result, &res)
|
||||||
require.NoErrorf(t, err, "could not parse response: %s", body)
|
require.NoErrorf(t, err, "could not parse response: %s", result)
|
||||||
assert.Equal(t, "400000455b7b226c616e67223a227a682d434e222c226e616d65223a22e5b08fe89a81e882a1227d2c7b226c616e67223a22656e222c226e616d65223a22416e745368617265227d5d0000c16ff28623000000da1745e9b549bd0bfa1a569971c77eba30cd5a4b00000000", res.Result)
|
assert.Equal(t, "400000455b7b226c616e67223a227a682d434e222c226e616d65223a22e5b08fe89a81e882a1227d2c7b226c616e67223a22656e222c226e616d65223a22416e745368617265227d5d0000c16ff28623000000da1745e9b549bd0bfa1a569971c77eba30cd5a4b00000000", res)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("gettxout", func(t *testing.T) {
|
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]}"`,
|
rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "gettxout", "params": [%s, %d]}"`,
|
||||||
`"`+tx.Hash().StringLE()+`"`, 0)
|
`"`+tx.Hash().StringLE()+`"`, 0)
|
||||||
body := doRPCCall(rpc, handler, t)
|
body := doRPCCall(rpc, handler, t)
|
||||||
checkErrResponse(t, body, false)
|
res := checkErrGetResult(t, body, false)
|
||||||
|
|
||||||
var result GetTxOutResponse
|
var txOut result.TransactionOutput
|
||||||
err := json.Unmarshal(body, &result)
|
err := json.Unmarshal(res, &txOut)
|
||||||
require.NoErrorf(t, err, "could not parse response: %s", body)
|
require.NoErrorf(t, err, "could not parse response: %s", res)
|
||||||
assert.Equal(t, 0, result.Result.N)
|
assert.Equal(t, 0, txOut.N)
|
||||||
assert.Equal(t, "0x9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc5", result.Result.Asset)
|
assert.Equal(t, "0x9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc5", txOut.Asset)
|
||||||
assert.Equal(t, util.Fixed8FromInt64(100000000), result.Result.Value)
|
assert.Equal(t, util.Fixed8FromInt64(100000000), txOut.Value)
|
||||||
assert.Equal(t, "AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU", result.Result.Address)
|
assert.Equal(t, "AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU", txOut.Address)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tc rpcTestCase) getResultPair(e *executor) (expected interface{}, res interface{}) {
|
func (tc rpcTestCase) getResultPair(e *executor) (expected interface{}, res interface{}) {
|
||||||
expected = tc.result(e)
|
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())
|
resVal := reflect.New(reflect.TypeOf(expected).Elem())
|
||||||
res = resVal.Interface()
|
return expected, resVal.Interface()
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
func checkErrGetResult(t *testing.T, body []byte, expectingFail bool) json.RawMessage {
|
||||||
}
|
var resp response.Raw
|
||||||
|
err := json.Unmarshal(body, &resp)
|
||||||
func checkErrResponse(t *testing.T, body []byte, expectingFail bool) {
|
|
||||||
var errresp ErrorResponse
|
|
||||||
err := json.Unmarshal(body, &errresp)
|
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
if expectingFail {
|
if expectingFail {
|
||||||
assert.NotEqual(t, 0, errresp.Error.Code)
|
assert.NotEqual(t, 0, resp.Error.Code)
|
||||||
assert.NotEqual(t, "", errresp.Error.Message)
|
assert.NotEqual(t, "", resp.Error.Message)
|
||||||
} else {
|
} else {
|
||||||
assert.Equal(t, 0, errresp.Error.Code)
|
assert.Nil(t, resp.Error)
|
||||||
assert.Equal(t, "", errresp.Error.Message)
|
|
||||||
}
|
}
|
||||||
|
return resp.Result
|
||||||
}
|
}
|
||||||
|
|
||||||
func doRPCCall(rpcCall string, handler http.HandlerFunc, t *testing.T) []byte {
|
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
|
return Hash160Type, nil
|
||||||
case "hash256":
|
case "hash256":
|
||||||
return Hash256Type, nil
|
return Hash256Type, nil
|
||||||
case "bytes":
|
case "bytes", "bytearray":
|
||||||
return ByteArrayType, nil
|
return ByteArrayType, nil
|
||||||
case "key":
|
case "key":
|
||||||
return PublicKeyType, nil
|
return PublicKeyType, nil
|
||||||
|
|
Loading…
Reference in a new issue