From 0b023c5c5cd7a233714b821c634eb8685be0c8bc Mon Sep 17 00:00:00 2001 From: Steven Jack Date: Fri, 30 Mar 2018 07:15:03 +0100 Subject: [PATCH] Small RPC improvements (#57) * Few tweaks to improve output of `getblock` * Adds few more fields and corrects witness * Bumps version * Only reverse when marshalling for moment * Adds README for rpc package * Few updates * Typo * Adds link in main readme * Fix readme link --- Makefile | 2 +- README.md | 2 +- VERSION | 2 +- pkg/core/block_base.go | 2 +- pkg/core/transaction/transaction.go | 2 +- pkg/core/transaction/witness.go | 36 ++++++--- pkg/rpc/README.md | 110 ++++++++++++++++++++++++++++ pkg/rpc/server.go | 4 +- pkg/rpc/wrappers/block.go | 33 +++++++++ pkg/util/uint160.go | 4 +- pkg/util/uint256.go | 2 +- 11 files changed, 179 insertions(+), 20 deletions(-) create mode 100644 pkg/rpc/README.md create mode 100644 pkg/rpc/wrappers/block.go diff --git a/Makefile b/Makefile index 11cb0b1b1..1f17bd7ce 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ VERSION = $(shell cat ./VERSION) NETMODE ?= "privnet" build: - @go build -ldflags "-X github.com/CityOfZion/neo-go/pkg/network.Version=${VERSION}-dev -X github.com/CityOfZion/neo-go/pkg/network.BuildTime=${BUILD_TIME}" -o ./bin/neo-go ./cli/main.go + @go build -ldflags "-X github.com/CityOfZion/neo-go/config.Version=${VERSION}-dev -X github.com/CityOfZion/neo-go/config.BuildTime=${BUILD_TIME}" -o ./bin/neo-go ./cli/main.go check-version: git fetch && (! git rev-list ${VERSION}) diff --git a/README.md b/README.md index ac89923d9..ff89e070a 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ This project aims to be a full port of the original C# [NEO project](https://git A complete toolkit for the NEO blockchain, including: - consensus node -- RPC node +- [RPC node & client](https://github.com/CityOfZion/neo-go/tree/master/pkg/rpc/README.md) - RPC client - CLI tool - Smart contract compiler diff --git a/VERSION b/VERSION index 731b95d7f..93d4c1ef0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.35.1 +0.36.0 diff --git a/pkg/core/block_base.go b/pkg/core/block_base.go index fc993e6f2..1823e70b1 100644 --- a/pkg/core/block_base.go +++ b/pkg/core/block_base.go @@ -34,7 +34,7 @@ type BlockBase struct { ConsensusData uint64 `json:"nonce"` // Contract addresss of the next miner - NextConsensus util.Uint160 `json:"nextminer"` + NextConsensus util.Uint160 `json:"next_consensus"` // Padding that is fixed to 1 _ uint8 diff --git a/pkg/core/transaction/transaction.go b/pkg/core/transaction/transaction.go index 50b42210d..aa4c18ca7 100644 --- a/pkg/core/transaction/transaction.go +++ b/pkg/core/transaction/transaction.go @@ -16,7 +16,7 @@ type Transaction struct { Type TXType `json:"type"` // The trading version which is currently 0. - Version uint8 `json:"-"` + Version uint8 `json:"version"` // Data specific to the type of the transaction. // This is always a pointer to a Transaction. diff --git a/pkg/core/transaction/witness.go b/pkg/core/transaction/witness.go index 953b86a5e..de4e60ab5 100644 --- a/pkg/core/transaction/witness.go +++ b/pkg/core/transaction/witness.go @@ -2,6 +2,8 @@ package transaction import ( "encoding/binary" + "encoding/hex" + "encoding/json" "io" "github.com/CityOfZion/neo-go/pkg/util" @@ -9,32 +11,42 @@ import ( // Witness contains 2 scripts. type Witness struct { - InvocationScript []byte `json:"stack"` - VerificationScript []byte `json:"redeem"` + InvocationScript []byte + VerificationScript []byte } // DecodeBinary implements the payload interface. -func (wit *Witness) DecodeBinary(r io.Reader) error { +func (w *Witness) DecodeBinary(r io.Reader) error { lenb := util.ReadVarUint(r) - wit.InvocationScript = make([]byte, lenb) - if err := binary.Read(r, binary.LittleEndian, wit.InvocationScript); err != nil { + w.InvocationScript = make([]byte, lenb) + if err := binary.Read(r, binary.LittleEndian, w.InvocationScript); err != nil { return err } lenb = util.ReadVarUint(r) - wit.VerificationScript = make([]byte, lenb) - return binary.Read(r, binary.LittleEndian, wit.VerificationScript) + w.VerificationScript = make([]byte, lenb) + return binary.Read(r, binary.LittleEndian, w.VerificationScript) } // EncodeBinary implements the payload interface. -func (wit *Witness) EncodeBinary(w io.Writer) error { - if err := util.WriteVarUint(w, uint64(len(wit.InvocationScript))); err != nil { +func (w *Witness) EncodeBinary(writer io.Writer) error { + if err := util.WriteVarUint(writer, uint64(len(w.InvocationScript))); err != nil { return err } - if err := binary.Write(w, binary.LittleEndian, wit.InvocationScript); err != nil { + if err := binary.Write(writer, binary.LittleEndian, w.InvocationScript); err != nil { return err } - if err := util.WriteVarUint(w, uint64(len(wit.VerificationScript))); err != nil { + if err := util.WriteVarUint(writer, uint64(len(w.VerificationScript))); err != nil { return err } - return binary.Write(w, binary.LittleEndian, wit.VerificationScript) + return binary.Write(writer, binary.LittleEndian, w.VerificationScript) +} + +// MarshalJSON implements the json marshaller interface. +func (w *Witness) MarshalJSON() ([]byte, error) { + data := map[string]interface{}{ + "invocation": hex.EncodeToString(w.InvocationScript), + "verification": hex.EncodeToString(w.VerificationScript), + } + + return json.Marshal(data) } diff --git a/pkg/rpc/README.md b/pkg/rpc/README.md new file mode 100644 index 000000000..af2af07eb --- /dev/null +++ b/pkg/rpc/README.md @@ -0,0 +1,110 @@ +# RPC + +## What + +* Structs used by `JSON-RPC` server and for interacting with a `JSON-RPC` endpoint. +* Server for running the `JSON-RPC` protocol based on port in configuration. + +> This package is currently in `alpha` and is subject to change. + +## Reference + +* [JSON-RPC 2.0 Specification](http://www.jsonrpc.org/specification) +* [NEO JSON-RPC 2.0 docs](http://docs.neo.org/en-us/node/api.html) + +## Client + +You can create a new client and start interacting with any NEO node that exposes their +`JSON-RPC` endpoint. See [godocs](https://godoc.org/github.com/CityOfZion/neo-go/pkg/rpc) for example. + +> Not all methods are currently supported in the client, please see table below for supported methods. + +### TODO + +* Merge structs so can be used by both server and client. +* Add missing methods to client. +* Allow client to connect using client cert. + +### Supported methods + +| Method | Implemented | Required to implement | +| ------- | ------------| --------------------- | +| `getblock` | Yes | - | +| `getaccountstate` | Yes | - | +| `invokescript` | Yes | - | +| `invokefunction` | Yes | - | +| `sendrawtransaction` | Yes | - | +| `validateaddress` | No | Handler and result struct | +| `getblocksysfee` | No | Handler and result struct | +| `getcontractstate` | No | Handler and result struct | +| `getrawmempool` | No | Handler and result struct | +| `getrawtransaction` | No | Handler and result struct | +| `getstorage` | No | Handler and result struct | +| `submitblock` | No | Handler and result struct | +| `gettxout` | No | Handler and result struct | +| `invoke` | No | Handler and result struct | +| `getassetstate` | No | Handler and result struct | +| `getpeers` | No | Handler and result struct | +| `getversion` | No | Handler and result struct | +| `getconnectioncount` | No | Handler and result struct | +| `getblockhash` | No | Handler and result struct | +| `getblockcount` | No | Handler and result struct | +| `getbestblockhash` | No | Handler and result struct | + +## Server + +The server is written to support as much of the [JSON-RPC 2.0 Spec](http://www.jsonrpc.org/specification) 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: + +```bash +curl -X POST -d '{"jsonrpc": "2.0", "method": "getversion", "params": [], "id": 1}" http://localhost:20332 +``` + +which would yield the response: + +```json +{ + "jsonrpc" : "2.0", + "id" : 1, + "result" : { + "port" : 20333, + "useragent" : "/NEO-GO:0.36.0-dev/", + "nonce" : 9318417 + } +} +``` + +### Supported methods + +| Method | Implemented | Required to implement | +| ------- | ------------| --------------------- | +| `getblock` | Yes | - | +| `getaccountstate` | No | Result struct & wallet functionality | +| `invokescript` | No | VM | +| `invokefunction` | No | VM | +| `sendrawtransaction` | No | Needs to be implemented in `pkg/core/blockchain.go` | +| `validateaddress` | No | Needs to be implemented in `pkg/core/blockchain.go` | +| `getblocksysfee` | No | N/A | +| `getcontractstate` | No | Needs to be implemented in `pkg/core/blockchain.go` | +| `getrawmempool` | No | Needs to be implemented on in `pkg/network/server.go` | +| `getrawtransaction` | No | Needs to be implemented in `pkg/core/blockchain.go` | +| `getstorage` | No | VM | +| `submitblock` | No | Needs to be implemented in `pkg/core/blockchain.go` | +| `gettxout` | No | Needs to be implemented in `pkg/core/blockchain.go` | +| `invoke` | No | VM | +| `getassetstate` | No | Needs to be implemented in `pkg/core/blockchain.go` | +| `getpeers` | Yes | - | +| `getversion` | Yes | - | +| `getconnectioncount` | Yes | - | +| `getblockhash` | Yes | - | +| `getblockcount` | Yes | - | +| `getbestblockhash` | Yes | - | diff --git a/pkg/rpc/server.go b/pkg/rpc/server.go index 6edba6452..9c9180add 100644 --- a/pkg/rpc/server.go +++ b/pkg/rpc/server.go @@ -9,6 +9,7 @@ import ( "github.com/CityOfZion/neo-go/pkg/core" "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" log "github.com/sirupsen/logrus" ) @@ -133,12 +134,13 @@ Methods: break } - results, err = s.chain.GetBlock(hash) + block, err := s.chain.GetBlock(hash) if err != nil { resultsErr = NewInternalServerError(fmt.Sprintf("Problem locating block with hash: %s", hash), err) break } + results = wrappers.NewBlock(block, s.chain) case "getblockcount": results = s.chain.BlockHeight() diff --git a/pkg/rpc/wrappers/block.go b/pkg/rpc/wrappers/block.go new file mode 100644 index 000000000..6c6a6ef56 --- /dev/null +++ b/pkg/rpc/wrappers/block.go @@ -0,0 +1,33 @@ +package wrappers + +import ( + "github.com/CityOfZion/neo-go/pkg/core" + "github.com/CityOfZion/neo-go/pkg/util" +) + +type ( + // Block wrapper used for the representation of + // core.Block / core.BlockBase on the RPC Server. + Block struct { + *core.Block + Confirmations uint32 `json:"confirmations"` + NextBlockHash util.Uint256 `json:"nextblockhash,omitempty"` + Hash util.Uint256 `json:"hash"` + } +) + +// NewBlock creates a new Block wrapper. +func NewBlock(block *core.Block, chain core.Blockchainer) Block { + blockWrapper := Block{ + Block: block, + Hash: block.Hash(), + } + + hash := chain.GetHeaderHash(int(block.Index) + 1) + if !hash.Equals(util.Uint256{}) { + blockWrapper.NextBlockHash = hash + } + + blockWrapper.Confirmations = chain.BlockHeight() - block.Index - 1 + return blockWrapper +} diff --git a/pkg/util/uint160.go b/pkg/util/uint160.go index 0e093d3f1..f3dfdd306 100644 --- a/pkg/util/uint160.go +++ b/pkg/util/uint160.go @@ -79,5 +79,7 @@ func (u Uint160) Equals(other Uint160) bool { // MarshalJSON implements the json marshaller interface. func (u Uint160) MarshalJSON() ([]byte, error) { - return json.Marshal(u.String()) + return json.Marshal( + fmt.Sprintf("0x%s", hex.EncodeToString(ArrayReverse(u.Bytes()))), + ) } diff --git a/pkg/util/uint256.go b/pkg/util/uint256.go index eef76f02f..46b1a4690 100644 --- a/pkg/util/uint256.go +++ b/pkg/util/uint256.go @@ -61,5 +61,5 @@ func (u Uint256) String() string { // MarshalJSON implements the json marshaller interface. func (u Uint256) MarshalJSON() ([]byte, error) { - return json.Marshal(u.String()) + return json.Marshal(fmt.Sprintf("0x%s", u.String())) }