2020-02-17 12:17:02 +00:00
package server
2018-03-23 20:36:59 +00:00
import (
"context"
2019-02-20 17:39:32 +00:00
"encoding/hex"
2020-01-14 12:02:38 +00:00
"encoding/json"
2018-03-23 20:36:59 +00:00
"fmt"
2020-03-05 12:39:53 +00:00
"math"
2020-03-10 11:56:18 +00:00
"net"
2018-03-23 20:36:59 +00:00
"net/http"
2019-01-25 11:20:35 +00:00
"strconv"
2020-05-10 22:00:19 +00:00
"sync"
2020-04-29 12:25:58 +00:00
"time"
2018-03-23 20:36:59 +00:00
2020-04-29 12:25:58 +00:00
"github.com/gorilla/websocket"
2020-03-03 14:21:42 +00:00
"github.com/nspcc-dev/neo-go/pkg/core"
2020-03-02 17:01:32 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/block"
2020-06-04 08:59:22 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/mpt"
2020-03-03 14:21:42 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
2020-03-05 14:48:30 +00:00
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
2020-03-03 14:21:42 +00:00
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/network"
2020-04-29 12:25:58 +00:00
"github.com/nspcc-dev/neo-go/pkg/rpc"
2020-03-03 14:21:42 +00:00
"github.com/nspcc-dev/neo-go/pkg/rpc/request"
"github.com/nspcc-dev/neo-go/pkg/rpc/response"
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
2020-03-11 12:07:14 +00:00
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
2020-03-03 14:21:42 +00:00
"github.com/nspcc-dev/neo-go/pkg/util"
2020-03-05 12:39:53 +00:00
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
2019-02-08 08:04:38 +00:00
"github.com/pkg/errors"
2019-12-30 08:44:52 +00:00
"go.uber.org/zap"
2018-03-23 20:36:59 +00:00
)
type (
// Server represents the JSON-RPC 2.0 server.
Server struct {
* http . Server
chain core . Blockchainer
2020-03-25 15:30:21 +00:00
config rpc . Config
2018-03-23 20:36:59 +00:00
coreServer * network . Server
2019-12-30 08:44:52 +00:00
log * zap . Logger
2020-03-10 11:56:18 +00:00
https * http . Server
2020-05-10 22:00:19 +00:00
shutdown chan struct { }
subsLock sync . RWMutex
subscribers map [ * subscriber ] bool
subsGroup sync . WaitGroup
blockSubs int
executionSubs int
notificationSubs int
transactionSubs int
blockCh chan * block . Block
executionCh chan * state . AppExecResult
notificationCh chan * state . NotificationEvent
transactionCh chan * transaction . Transaction
2018-03-23 20:36:59 +00:00
}
)
2020-04-29 12:25:58 +00:00
const (
// Message limit for receiving side.
wsReadLimit = 4096
// Disconnection timeout.
wsPongLimit = 60 * time . Second
// Ping period for connection liveness check.
wsPingPeriod = wsPongLimit / 2
// Write deadline.
wsWriteLimit = wsPingPeriod / 2
2020-05-10 22:00:19 +00:00
// Maximum number of subscribers per Server. Each websocket client is
// treated like subscriber, so technically it's a limit on websocket
// connections.
maxSubscribers = 64
2020-04-29 12:25:58 +00:00
)
2020-04-28 19:35:19 +00:00
var rpcHandlers = map [ string ] func ( * Server , request . Params ) ( interface { } , * response . Error ) {
2020-03-13 07:29:49 +00:00
"getaccountstate" : ( * Server ) . getAccountState ,
"getapplicationlog" : ( * Server ) . getApplicationLog ,
"getassetstate" : ( * Server ) . getAssetState ,
"getbestblockhash" : ( * Server ) . getBestBlockHash ,
"getblock" : ( * Server ) . getBlock ,
"getblockcount" : ( * Server ) . getBlockCount ,
"getblockhash" : ( * Server ) . getBlockHash ,
"getblockheader" : ( * Server ) . getBlockHeader ,
"getblocksysfee" : ( * Server ) . getBlockSysFee ,
"getclaimable" : ( * Server ) . getClaimable ,
"getconnectioncount" : ( * Server ) . getConnectionCount ,
"getcontractstate" : ( * Server ) . getContractState ,
"getnep5balances" : ( * Server ) . getNEP5Balances ,
"getnep5transfers" : ( * Server ) . getNEP5Transfers ,
"getpeers" : ( * Server ) . getPeers ,
"getrawmempool" : ( * Server ) . getRawMempool ,
"getrawtransaction" : ( * Server ) . getrawtransaction ,
2020-06-04 08:59:22 +00:00
"getproof" : ( * Server ) . getProof ,
2020-06-04 08:09:07 +00:00
"getstateheight" : ( * Server ) . getStateHeight ,
2020-06-03 15:09:36 +00:00
"getstateroot" : ( * Server ) . getStateRoot ,
2020-03-13 07:29:49 +00:00
"getstorage" : ( * Server ) . getStorage ,
"gettransactionheight" : ( * Server ) . getTransactionHeight ,
"gettxout" : ( * Server ) . getTxOut ,
"getunclaimed" : ( * Server ) . getUnclaimed ,
"getunspents" : ( * Server ) . getUnspents ,
"getvalidators" : ( * Server ) . getValidators ,
"getversion" : ( * Server ) . getVersion ,
"invoke" : ( * Server ) . invoke ,
"invokefunction" : ( * Server ) . invokeFunction ,
"invokescript" : ( * Server ) . invokescript ,
"sendrawtransaction" : ( * Server ) . sendrawtransaction ,
"submitblock" : ( * Server ) . submitBlock ,
"validateaddress" : ( * Server ) . validateAddress ,
2020-06-05 08:51:39 +00:00
"verifyproof" : ( * Server ) . verifyProof ,
2020-03-13 07:29:49 +00:00
}
2020-05-10 22:00:19 +00:00
var rpcWsHandlers = map [ string ] func ( * Server , request . Params , * subscriber ) ( interface { } , * response . Error ) {
"subscribe" : ( * Server ) . subscribe ,
"unsubscribe" : ( * Server ) . unsubscribe ,
}
2020-04-28 19:35:19 +00:00
var invalidBlockHeightError = func ( index int , height int ) * response . Error {
return response . NewRPCError ( fmt . Sprintf ( "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 ) , "" , nil )
2019-11-21 13:42:51 +00:00
}
2018-03-25 10:13:47 +00:00
2020-04-29 12:25:58 +00:00
// upgrader is a no-op websocket.Upgrader that reuses HTTP server buffers and
// doesn't set any Error function.
var upgrader = websocket . Upgrader { }
2020-02-17 12:17:02 +00:00
// New creates a new Server struct.
2020-03-25 15:30:21 +00:00
func New ( chain core . Blockchainer , conf rpc . Config , coreServer * network . Server , log * zap . Logger ) Server {
2019-11-01 10:23:46 +00:00
httpServer := & http . Server {
Addr : conf . Address + ":" + strconv . FormatUint ( uint64 ( conf . Port ) , 10 ) ,
}
2020-03-10 11:56:18 +00:00
var tlsServer * http . Server
if cfg := conf . TLSConfig ; cfg . Enabled {
tlsServer = & http . Server {
Addr : net . JoinHostPort ( cfg . Address , strconv . FormatUint ( uint64 ( cfg . Port ) , 10 ) ) ,
}
}
2018-03-23 20:36:59 +00:00
return Server {
2019-11-01 10:23:46 +00:00
Server : httpServer ,
2018-03-23 20:36:59 +00:00
chain : chain ,
2019-11-01 10:23:46 +00:00
config : conf ,
2018-03-23 20:36:59 +00:00
coreServer : coreServer ,
2019-12-30 08:44:52 +00:00
log : log ,
2020-03-10 11:56:18 +00:00
https : tlsServer ,
2020-05-10 22:00:19 +00:00
shutdown : make ( chan struct { } ) ,
subscribers : make ( map [ * subscriber ] bool ) ,
// These are NOT buffered to preserve original order of events.
blockCh : make ( chan * block . Block ) ,
executionCh : make ( chan * state . AppExecResult ) ,
notificationCh : make ( chan * state . NotificationEvent ) ,
transactionCh : make ( chan * transaction . Transaction ) ,
2018-03-23 20:36:59 +00:00
}
}
2020-05-09 20:59:21 +00:00
// Start creates a new JSON-RPC server listening on the configured port. It's
// supposed to be run as a separate goroutine (like http.Server's Serve) and it
// returns its errors via given errChan.
2018-03-23 20:36:59 +00:00
func ( s * Server ) Start ( errChan chan error ) {
2019-11-01 10:23:46 +00:00
if ! s . config . Enabled {
2019-12-30 08:44:52 +00:00
s . log . Info ( "RPC server is not enabled" )
2019-11-01 10:23:46 +00:00
return
}
2020-04-28 13:56:33 +00:00
s . Handler = http . HandlerFunc ( s . handleHTTPRequest )
2019-12-30 08:44:52 +00:00
s . log . Info ( "starting rpc-server" , zap . String ( "endpoint" , s . Addr ) )
2018-03-23 20:36:59 +00:00
2020-05-10 22:00:19 +00:00
go s . handleSubEvents ( )
2020-03-10 11:56:18 +00:00
if cfg := s . config . TLSConfig ; cfg . Enabled {
2020-04-28 13:56:33 +00:00
s . https . Handler = http . HandlerFunc ( s . handleHTTPRequest )
2020-03-10 11:56:18 +00:00
s . log . Info ( "starting rpc-server (https)" , zap . String ( "endpoint" , s . https . Addr ) )
go func ( ) {
err := s . https . ListenAndServeTLS ( cfg . CertFile , cfg . KeyFile )
if err != nil {
s . log . Error ( "failed to start TLS RPC server" , zap . Error ( err ) )
}
errChan <- err
} ( )
}
err := s . ListenAndServe ( )
if err != nil {
s . log . Error ( "failed to start RPC server" , zap . Error ( err ) )
}
errChan <- err
2018-03-23 20:36:59 +00:00
}
2019-10-22 14:56:03 +00:00
// Shutdown overrides the http.Server Shutdown
2018-03-23 20:36:59 +00:00
// method.
func ( s * Server ) Shutdown ( ) error {
2020-03-10 11:56:18 +00:00
var httpsErr error
2020-05-10 22:00:19 +00:00
// Signal to websocket writer routines and handleSubEvents.
close ( s . shutdown )
2020-03-10 11:56:18 +00:00
if s . config . TLSConfig . Enabled {
s . log . Info ( "shutting down rpc-server (https)" , zap . String ( "endpoint" , s . https . Addr ) )
httpsErr = s . https . Shutdown ( context . Background ( ) )
}
2019-12-30 08:44:52 +00:00
s . log . Info ( "shutting down rpc-server" , zap . String ( "endpoint" , s . Addr ) )
2020-03-10 11:56:18 +00:00
err := s . Server . Shutdown ( context . Background ( ) )
2020-05-10 22:00:19 +00:00
// Wait for handleSubEvents to finish.
<- s . executionCh
2020-03-10 11:56:18 +00:00
if err == nil {
return httpsErr
}
return err
2018-03-23 20:36:59 +00:00
}
2020-04-28 13:56:33 +00:00
func ( s * Server ) handleHTTPRequest ( w http . ResponseWriter , httpRequest * http . Request ) {
2020-05-10 22:00:19 +00:00
req := request . NewIn ( )
2020-04-29 12:25:58 +00:00
if httpRequest . URL . Path == "/ws" && httpRequest . Method == "GET" {
2020-05-10 22:00:19 +00:00
// Technically there is a race between this check and
// s.subscribers modification 20 lines below, but it's tiny
// and not really critical to bother with it. Some additional
// clients may sneak in, no big deal.
s . subsLock . RLock ( )
numOfSubs := len ( s . subscribers )
s . subsLock . RUnlock ( )
if numOfSubs >= maxSubscribers {
s . writeHTTPErrorResponse (
req ,
w ,
response . NewInternalServerError ( "websocket users limit reached" , nil ) ,
)
return
}
2020-04-29 12:25:58 +00:00
ws , err := upgrader . Upgrade ( w , httpRequest , nil )
if err != nil {
s . log . Info ( "websocket connection upgrade failed" , zap . Error ( err ) )
return
}
resChan := make ( chan response . Raw )
2020-05-10 22:00:19 +00:00
subChan := make ( chan * websocket . PreparedMessage , notificationBufSize )
subscr := & subscriber { writer : subChan , ws : ws }
s . subsLock . Lock ( )
s . subscribers [ subscr ] = true
s . subsLock . Unlock ( )
go s . handleWsWrites ( ws , resChan , subChan )
s . handleWsReads ( ws , resChan , subscr )
2020-04-29 12:25:58 +00:00
return
}
2018-03-23 20:36:59 +00:00
if httpRequest . Method != "POST" {
2020-04-28 19:56:19 +00:00
s . writeHTTPErrorResponse (
2019-12-30 08:44:52 +00:00
req ,
2018-03-23 20:36:59 +00:00
w ,
2020-01-14 12:02:38 +00:00
response . NewInvalidParamsError (
2018-03-23 20:36:59 +00:00
fmt . Sprintf ( "Invalid method '%s', please retry with 'POST'" , httpRequest . Method ) , nil ,
) ,
)
return
}
err := req . DecodeData ( httpRequest . Body )
if err != nil {
2020-04-28 19:56:19 +00:00
s . writeHTTPErrorResponse ( req , w , response . NewParseError ( "Problem parsing JSON-RPC request body" , err ) )
2018-03-23 20:36:59 +00:00
return
}
2020-05-10 22:00:19 +00:00
resp := s . handleRequest ( req , nil )
2020-04-28 19:56:19 +00:00
s . writeHTTPServerResponse ( req , w , resp )
2020-04-28 13:56:33 +00:00
}
2020-05-10 22:00:19 +00:00
func ( s * Server ) handleRequest ( req * request . In , sub * subscriber ) response . Raw {
var res interface { }
var resErr * response . Error
2018-03-23 20:36:59 +00:00
reqParams , err := req . Params ( )
if err != nil {
2020-04-28 19:56:19 +00:00
return s . packResponseToRaw ( req , nil , response . NewInvalidParamsError ( "Problem parsing request parameters" , err ) )
2018-03-23 20:36:59 +00:00
}
2020-01-13 13:45:36 +00:00
s . log . Debug ( "processing rpc request" ,
2019-12-30 08:44:52 +00:00
zap . String ( "method" , req . Method ) ,
zap . String ( "params" , fmt . Sprintf ( "%v" , reqParams ) ) )
2018-03-23 20:36:59 +00:00
2020-03-12 17:36:36 +00:00
incCounter ( req . Method )
2020-05-10 22:00:19 +00:00
resErr = response . NewMethodNotFoundError ( fmt . Sprintf ( "Method '%s' not supported" , req . Method ) , nil )
2020-03-13 07:29:49 +00:00
handler , ok := rpcHandlers [ req . Method ]
2020-05-10 22:00:19 +00:00
if ok {
res , resErr = handler ( s , * reqParams )
} else if sub != nil {
handler , ok := rpcWsHandlers [ req . Method ]
if ok {
res , resErr = handler ( s , * reqParams , sub )
}
2018-03-23 20:36:59 +00:00
}
2020-04-28 19:56:19 +00:00
return s . packResponseToRaw ( req , res , resErr )
2020-03-13 07:01:49 +00:00
}
2020-05-10 22:00:19 +00:00
func ( s * Server ) handleWsWrites ( ws * websocket . Conn , resChan <- chan response . Raw , subChan <- chan * websocket . PreparedMessage ) {
2020-04-29 12:25:58 +00:00
pingTicker := time . NewTicker ( wsPingPeriod )
2020-05-12 19:38:29 +00:00
eventloop :
2020-04-29 12:25:58 +00:00
for {
select {
2020-05-10 22:00:19 +00:00
case <- s . shutdown :
2020-05-12 19:38:29 +00:00
break eventloop
2020-05-10 22:00:19 +00:00
case event , ok := <- subChan :
if ! ok {
2020-05-12 19:38:29 +00:00
break eventloop
2020-05-10 22:00:19 +00:00
}
ws . SetWriteDeadline ( time . Now ( ) . Add ( wsWriteLimit ) )
if err := ws . WritePreparedMessage ( event ) ; err != nil {
2020-05-12 19:38:29 +00:00
break eventloop
2020-05-10 22:00:19 +00:00
}
2020-04-29 12:25:58 +00:00
case res , ok := <- resChan :
if ! ok {
2020-05-12 19:38:29 +00:00
break eventloop
2020-04-29 12:25:58 +00:00
}
ws . SetWriteDeadline ( time . Now ( ) . Add ( wsWriteLimit ) )
if err := ws . WriteJSON ( res ) ; err != nil {
2020-05-12 19:38:29 +00:00
break eventloop
2020-04-29 12:25:58 +00:00
}
case <- pingTicker . C :
ws . SetWriteDeadline ( time . Now ( ) . Add ( wsWriteLimit ) )
if err := ws . WriteMessage ( websocket . PingMessage , [ ] byte { } ) ; err != nil {
2020-05-12 19:38:29 +00:00
break eventloop
2020-04-29 12:25:58 +00:00
}
}
}
2020-05-12 19:38:29 +00:00
ws . Close ( )
pingTicker . Stop ( )
// Drain notification channel as there might be some goroutines blocked
// on it.
drainloop :
for {
select {
case _ , ok := <- subChan :
if ! ok {
break drainloop
}
default :
break drainloop
}
}
2020-04-29 12:25:58 +00:00
}
2020-05-10 22:00:19 +00:00
func ( s * Server ) handleWsReads ( ws * websocket . Conn , resChan chan <- response . Raw , subscr * subscriber ) {
2020-04-29 12:25:58 +00:00
ws . SetReadLimit ( wsReadLimit )
ws . SetReadDeadline ( time . Now ( ) . Add ( wsPongLimit ) )
ws . SetPongHandler ( func ( string ) error { ws . SetReadDeadline ( time . Now ( ) . Add ( wsPongLimit ) ) ; return nil } )
2020-05-10 22:00:19 +00:00
requestloop :
2020-04-29 12:25:58 +00:00
for {
req := new ( request . In )
err := ws . ReadJSON ( req )
if err != nil {
break
}
2020-05-10 22:00:19 +00:00
res := s . handleRequest ( req , subscr )
2020-04-29 12:25:58 +00:00
if res . Error != nil {
s . logRequestError ( req , res . Error )
}
2020-05-10 22:00:19 +00:00
select {
case <- s . shutdown :
break requestloop
case resChan <- res :
}
}
s . subsLock . Lock ( )
delete ( s . subscribers , subscr )
for _ , e := range subscr . feeds {
2020-05-13 14:13:33 +00:00
if e . event != response . InvalidEventID {
s . unsubscribeFromChannel ( e . event )
2020-05-10 22:00:19 +00:00
}
2020-04-29 12:25:58 +00:00
}
2020-05-10 22:00:19 +00:00
s . subsLock . Unlock ( )
2020-04-29 12:25:58 +00:00
close ( resChan )
ws . Close ( )
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) getBestBlockHash ( _ request . Params ) ( interface { } , * response . Error ) {
2020-03-13 07:14:48 +00:00
return "0x" + s . chain . CurrentBlockHash ( ) . StringLE ( ) , nil
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) getBlockCount ( _ request . Params ) ( interface { } , * response . Error ) {
2020-03-13 07:14:48 +00:00
return s . chain . BlockHeight ( ) + 1 , nil
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) getConnectionCount ( _ request . Params ) ( interface { } , * response . Error ) {
2020-03-13 07:14:48 +00:00
return s . coreServer . PeerCount ( ) , nil
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) getBlock ( reqParams request . Params ) ( interface { } , * response . Error ) {
2020-03-13 07:08:30 +00:00
var hash util . Uint256
2020-06-04 11:58:47 +00:00
param := reqParams . Value ( 0 )
if param == nil {
2020-03-13 07:08:30 +00:00
return nil , response . ErrInvalidParams
}
switch param . Type {
case request . StringT :
var err error
hash , err = param . GetUint256 ( )
if err != nil {
return nil , response . ErrInvalidParams
}
case request . NumberT :
num , err := s . blockHeightFromParam ( param )
if err != nil {
return nil , response . ErrInvalidParams
}
hash = s . chain . GetHeaderHash ( num )
default :
return nil , response . ErrInvalidParams
}
block , err := s . chain . GetBlock ( hash )
if err != nil {
return nil , response . NewInternalServerError ( fmt . Sprintf ( "Problem locating block with hash: %s" , hash ) , err )
}
2020-06-04 12:43:37 +00:00
if reqParams . Value ( 1 ) . GetBoolean ( ) {
2020-03-13 07:08:30 +00:00
return result . NewBlock ( block , s . chain ) , nil
}
writer := io . NewBufBinWriter ( )
block . EncodeBinary ( writer . BinWriter )
return hex . EncodeToString ( writer . Bytes ( ) ) , nil
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) getBlockHash ( reqParams request . Params ) ( interface { } , * response . Error ) {
2020-06-04 11:58:47 +00:00
param := reqParams . ValueWithType ( 0 , request . NumberT )
if param == nil {
2020-03-13 07:06:52 +00:00
return nil , response . ErrInvalidParams
}
num , err := s . blockHeightFromParam ( param )
if err != nil {
return nil , response . ErrInvalidParams
}
return s . chain . GetHeaderHash ( num ) , nil
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) getVersion ( _ request . Params ) ( interface { } , * response . Error ) {
2020-03-13 07:04:57 +00:00
return result . Version {
Port : s . coreServer . Port ,
Nonce : s . coreServer . ID ( ) ,
UserAgent : s . coreServer . UserAgent ,
} , nil
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) getPeers ( _ request . Params ) ( interface { } , * response . Error ) {
2020-03-13 07:03:58 +00:00
peers := result . NewGetPeers ( )
peers . AddUnconnected ( s . coreServer . UnconnectedPeers ( ) )
peers . AddConnected ( s . coreServer . ConnectedPeers ( ) )
peers . AddBad ( s . coreServer . BadPeers ( ) )
return peers , nil
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) getRawMempool ( _ request . Params ) ( interface { } , * response . Error ) {
2020-03-13 07:03:01 +00:00
mp := s . chain . GetMemPool ( )
hashList := make ( [ ] util . Uint256 , 0 )
for _ , item := range mp . GetVerifiedTransactions ( ) {
hashList = append ( hashList , item . Tx . Hash ( ) )
}
return hashList , nil
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) validateAddress ( reqParams request . Params ) ( interface { } , * response . Error ) {
2020-06-04 11:58:47 +00:00
param := reqParams . Value ( 0 )
if param == nil {
2020-03-13 07:01:49 +00:00
return nil , response . ErrInvalidParams
}
return validateAddress ( param . Value ) , nil
2018-03-25 10:13:47 +00:00
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) getAssetState ( reqParams request . Params ) ( interface { } , * response . Error ) {
2020-06-04 11:58:47 +00:00
paramAssetID , err := reqParams . ValueWithType ( 0 , request . StringT ) . GetUint256 ( )
2020-03-13 07:00:18 +00:00
if err != nil {
return nil , response . ErrInvalidParams
}
as := s . chain . GetAssetState ( paramAssetID )
if as != nil {
return result . NewAssetState ( as ) , nil
}
return nil , response . NewRPCError ( "Unknown asset" , "" , nil )
}
2020-02-21 14:56:28 +00:00
// getApplicationLog returns the contract log based on the specified txid.
2020-04-28 19:35:19 +00:00
func ( s * Server ) getApplicationLog ( reqParams request . Params ) ( interface { } , * response . Error ) {
2020-06-04 11:58:47 +00:00
txHash , err := reqParams . Value ( 0 ) . GetUint256 ( )
2020-02-21 14:56:28 +00:00
if err != nil {
return nil , response . ErrInvalidParams
}
appExecResult , err := s . chain . GetAppExecResult ( txHash )
if err != nil {
return nil , response . NewRPCError ( "Unknown transaction" , "" , nil )
}
tx , _ , err := s . chain . GetTransaction ( txHash )
if err != nil {
return nil , response . NewRPCError ( "Error while getting transaction" , "" , nil )
}
var scriptHash util . Uint160
switch t := tx . Data . ( type ) {
case * transaction . InvocationTX :
scriptHash = hash . Hash160 ( t . Script )
default :
return nil , response . NewRPCError ( "Invalid transaction type" , "" , nil )
}
return result . NewApplicationLog ( appExecResult , scriptHash ) , nil
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) getClaimable ( ps request . Params ) ( interface { } , * response . Error ) {
2020-06-04 11:58:47 +00:00
p := ps . ValueWithType ( 0 , request . StringT )
2020-02-26 12:42:04 +00:00
u , err := p . GetUint160FromAddress ( )
if err != nil {
return nil , response . ErrInvalidParams
}
var unclaimed [ ] state . UnclaimedBalance
if acc := s . chain . GetAccountState ( u ) ; acc != nil {
2020-03-13 10:54:13 +00:00
err := acc . Unclaimed . ForEach ( func ( b * state . UnclaimedBalance ) error {
unclaimed = append ( unclaimed , * b )
return nil
} )
if err != nil {
2020-04-28 19:35:19 +00:00
return nil , response . NewInternalServerError ( "Unclaimed processing failure" , err )
2020-03-13 10:54:13 +00:00
}
2020-02-26 12:42:04 +00:00
}
var sum util . Fixed8
claimable := make ( [ ] result . Claimable , 0 , len ( unclaimed ) )
for _ , ub := range unclaimed {
gen , sys , err := s . chain . CalculateClaimable ( ub . Value , ub . Start , ub . End )
if err != nil {
s . log . Info ( "error while calculating claim bonus" , zap . Error ( err ) )
continue
}
uc := gen . Add ( sys )
sum += uc
claimable = append ( claimable , result . Claimable {
Tx : ub . Tx ,
N : int ( ub . Index ) ,
Value : ub . Value ,
StartHeight : ub . Start ,
EndHeight : ub . End ,
Generated : gen ,
SysFee : sys ,
Unclaimed : uc ,
} )
}
return result . ClaimableInfo {
Spents : claimable ,
Address : p . String ( ) ,
Unclaimed : sum ,
} , nil
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) getNEP5Balances ( ps request . Params ) ( interface { } , * response . Error ) {
2020-06-04 11:58:47 +00:00
u , err := ps . ValueWithType ( 0 , request . StringT ) . GetUint160FromHex ( )
2020-03-05 11:50:06 +00:00
if err != nil {
return nil , response . ErrInvalidParams
}
2020-03-11 15:22:46 +00:00
as := s . chain . GetNEP5Balances ( u )
2020-03-11 12:03:20 +00:00
bs := & result . NEP5Balances {
Address : address . Uint160ToString ( u ) ,
Balances : [ ] result . NEP5Balance { } ,
}
2020-03-05 11:50:06 +00:00
if as != nil {
2020-03-05 12:39:53 +00:00
cache := make ( map [ util . Uint160 ] int64 )
2020-03-11 15:22:46 +00:00
for h , bal := range as . Trackers {
2020-03-05 12:39:53 +00:00
dec , err := s . getDecimals ( h , cache )
if err != nil {
continue
}
amount := amountToString ( bal . Balance , dec )
2020-03-05 11:50:06 +00:00
bs . Balances = append ( bs . Balances , result . NEP5Balance {
Asset : h ,
Amount : amount ,
LastUpdated : bal . LastUpdatedBlock ,
} )
}
}
return bs , nil
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) getNEP5Transfers ( ps request . Params ) ( interface { } , * response . Error ) {
2020-06-04 11:58:47 +00:00
u , err := ps . ValueWithType ( 0 , request . StringT ) . GetUint160FromAddress ( )
2020-03-05 12:16:03 +00:00
if err != nil {
return nil , response . ErrInvalidParams
}
2020-03-11 12:03:20 +00:00
bs := & result . NEP5Transfers {
Address : address . Uint160ToString ( u ) ,
Received : [ ] result . NEP5Transfer { } ,
Sent : [ ] result . NEP5Transfer { } ,
}
2020-03-05 12:16:03 +00:00
lg := s . chain . GetNEP5TransferLog ( u )
2020-03-05 12:39:53 +00:00
cache := make ( map [ util . Uint160 ] int64 )
2020-03-05 12:16:03 +00:00
err = lg . ForEach ( func ( tr * state . NEP5Transfer ) error {
transfer := result . NEP5Transfer {
Timestamp : tr . Timestamp ,
Asset : tr . Asset ,
Index : tr . Block ,
TxHash : tr . Tx ,
}
2020-03-05 12:39:53 +00:00
d , err := s . getDecimals ( tr . Asset , cache )
if err != nil {
return nil
}
2020-03-05 12:16:03 +00:00
if tr . Amount > 0 { // token was received
2020-03-05 12:39:53 +00:00
transfer . Amount = amountToString ( tr . Amount , d )
2020-03-05 12:16:03 +00:00
if ! tr . From . Equals ( util . Uint160 { } ) {
transfer . Address = address . Uint160ToString ( tr . From )
}
bs . Received = append ( bs . Received , transfer )
return nil
}
2020-03-05 12:39:53 +00:00
transfer . Amount = amountToString ( - tr . Amount , d )
2020-03-05 12:16:03 +00:00
if ! tr . From . Equals ( util . Uint160 { } ) {
transfer . Address = address . Uint160ToString ( tr . To )
}
bs . Sent = append ( bs . Sent , transfer )
return nil
} )
if err != nil {
return nil , response . NewInternalServerError ( "invalid NEP5 transfer log" , err )
}
return bs , nil
}
2020-03-05 12:39:53 +00:00
func amountToString ( amount int64 , decimals int64 ) string {
if decimals == 0 {
return strconv . FormatInt ( amount , 10 )
}
pow := int64 ( math . Pow10 ( int ( decimals ) ) )
q := amount / pow
r := amount % pow
if r == 0 {
return strconv . FormatInt ( q , 10 )
}
fs := fmt . Sprintf ( "%%d.%%0%dd" , decimals )
return fmt . Sprintf ( fs , q , r )
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) getDecimals ( h util . Uint160 , cache map [ util . Uint160 ] int64 ) ( int64 , * response . Error ) {
2020-03-05 12:39:53 +00:00
if d , ok := cache [ h ] ; ok {
return d , nil
}
2020-03-11 12:07:14 +00:00
script , err := request . CreateFunctionInvocationScript ( h , request . Params {
{
Type : request . StringT ,
Value : "decimals" ,
} ,
{
Type : request . ArrayT ,
Value : [ ] request . Param { } ,
} ,
} )
if err != nil {
2020-04-28 19:35:19 +00:00
return 0 , response . NewInternalServerError ( "Can't create script" , err )
2020-03-05 12:39:53 +00:00
}
2020-03-11 12:07:14 +00:00
res := s . runScriptInVM ( script )
if res == nil || res . State != "HALT" || len ( res . Stack ) == 0 {
2020-04-28 19:35:19 +00:00
return 0 , response . NewInternalServerError ( "execution error" , errors . New ( "no result" ) )
2020-03-05 12:39:53 +00:00
}
2020-03-11 12:07:14 +00:00
var d int64
switch item := res . Stack [ len ( res . Stack ) - 1 ] ; item . Type {
case smartcontract . IntegerType :
d = item . Value . ( int64 )
case smartcontract . ByteArrayType :
d = emit . BytesToInt ( item . Value . ( [ ] byte ) ) . Int64 ( )
default :
2020-04-28 19:35:19 +00:00
return 0 , response . NewInternalServerError ( "invalid result" , errors . New ( "not an integer" ) )
2020-03-05 12:39:53 +00:00
}
if d < 0 {
2020-04-28 19:35:19 +00:00
return 0 , response . NewInternalServerError ( "incorrect result" , errors . New ( "negative result" ) )
2020-03-05 12:39:53 +00:00
}
cache [ h ] = d
return d , nil
}
2020-06-04 08:59:22 +00:00
func ( s * Server ) getProof ( ps request . Params ) ( interface { } , * response . Error ) {
root , err := ps . Value ( 0 ) . GetUint256 ( )
if err != nil {
return nil , response . ErrInvalidParams
}
sc , err := ps . Value ( 1 ) . GetUint160FromHex ( )
if err != nil {
return nil , response . ErrInvalidParams
}
sc = sc . Reverse ( )
key , err := ps . Value ( 2 ) . GetBytesHex ( )
if err != nil {
return nil , response . ErrInvalidParams
}
skey := mpt . ToNeoStorageKey ( append ( sc . BytesBE ( ) , key ... ) )
proof , err := s . chain . GetStateProof ( root , skey )
return & result . GetProof {
Result : result . ProofWithKey {
Key : skey ,
Proof : proof ,
} ,
Success : err == nil ,
} , nil
}
2020-06-05 08:51:39 +00:00
func ( s * Server ) verifyProof ( ps request . Params ) ( interface { } , * response . Error ) {
root , err := ps . Value ( 0 ) . GetUint256 ( )
if err != nil {
return nil , response . ErrInvalidParams
}
proofStr , err := ps . Value ( 1 ) . GetString ( )
if err != nil {
return nil , response . ErrInvalidParams
}
var p result . ProofWithKey
if err := p . FromString ( proofStr ) ; err != nil {
return nil , response . ErrInvalidParams
}
vp := new ( result . VerifyProof )
val , ok := mpt . VerifyProof ( root , p . Key , p . Proof )
if ok {
var si state . StorageItem
r := io . NewBinReaderFromBuf ( val [ 1 : ] )
si . DecodeBinary ( r )
if r . Err != nil {
return nil , response . NewInternalServerError ( "invalid item in trie" , r . Err )
}
vp . Value = si . Value
}
return vp , nil
}
2020-06-04 08:09:07 +00:00
func ( s * Server ) getStateHeight ( _ request . Params ) ( interface { } , * response . Error ) {
return & result . StateHeight {
2020-06-22 07:49:48 +00:00
BlockHeight : s . chain . BlockHeight ( ) ,
StateHeight : s . chain . StateHeight ( ) ,
2020-06-04 08:09:07 +00:00
} , nil
}
2020-06-03 15:09:36 +00:00
func ( s * Server ) getStateRoot ( ps request . Params ) ( interface { } , * response . Error ) {
p := ps . Value ( 0 )
if p == nil {
return nil , response . NewRPCError ( "Invalid parameter." , "" , nil )
}
var rt * state . MPTRootState
var h util . Uint256
height , err := p . GetInt ( )
if err == nil {
rt , err = s . chain . GetStateRoot ( uint32 ( height ) )
} else if h , err = p . GetUint256 ( ) ; err == nil {
hdr , err := s . chain . GetHeader ( h )
if err == nil {
rt , err = s . chain . GetStateRoot ( hdr . Index )
}
}
if err != nil {
return nil , response . NewRPCError ( "Unknown state root." , "" , err )
}
return rt , nil
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) getStorage ( ps request . Params ) ( interface { } , * response . Error ) {
2020-06-04 11:58:47 +00:00
scriptHash , err := ps . Value ( 0 ) . GetUint160FromHex ( )
2020-01-30 08:03:44 +00:00
if err != nil {
2020-01-14 12:02:38 +00:00
return nil , response . ErrInvalidParams
2020-01-30 08:03:44 +00:00
}
scriptHash = scriptHash . Reverse ( )
2020-06-04 11:58:47 +00:00
key , err := ps . Value ( 1 ) . GetBytesHex ( )
2020-01-30 08:03:44 +00:00
if err != nil {
2020-01-14 12:02:38 +00:00
return nil , response . ErrInvalidParams
2020-01-30 08:03:44 +00:00
}
item := s . chain . GetStorageItem ( scriptHash . Reverse ( ) , key )
if item == nil {
return nil , nil
}
return hex . EncodeToString ( item . Value ) , nil
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) getrawtransaction ( reqParams request . Params ) ( interface { } , * response . Error ) {
var resultsErr * response . Error
Implement rpc server method: sendrawtransaction (#174)
* Added new config attributes: 'SecondsPerBlock','LowPriorityThreshold'
* Added new files:
* Added new method: CompareTo
* Fixed empty Slice case
* Added new methods: LessThan, GreaterThan, Equal, CompareTo
* Added new method: InputIntersection
* Added MaxTransactionSize, GroupOutputByAssetID
* Added ned method: ScriptHash
* Added new method: IsDoubleSpend
* Refactor blockchainer, Added Feer interface, Verify and GetMemPool method
* 1) Added MemPool
2) Added new methods to satisfy the blockchainer interface: IsLowPriority, Verify, GetMemPool
* Added new methods: RelayTxn, RelayDirectly
* Fixed tests
* Implemented RPC server method sendrawtransaction
* Refactor getrawtransaction, sendrawtransaction in separate methods
* Moved 'secondsPerBlock' to config file
* Implemented Kim suggestions:
1) Fixed data race issues
2) refactor Verify method
3) Get rid of unused InputIntersection method due to refactoring Verify method
4) Fixed bug in https://github.com/CityOfZion/neo-go/pull/174#discussion_r264108135
5) minor simplications of the code
* Fixed minor issues related to
1) space
2) getter methods do not need pointer on the receiver
3) error message
4) refactoring CompareTo method in uint256.go
* Fixed small issues
* Use sync.RWMutex instead of sync.Mutex
* Refined (R)Lock/(R)Unlock
* return error instead of bool in Verify methods
2019-03-20 12:30:05 +00:00
var results interface { }
2020-06-04 11:58:47 +00:00
if txHash , err := reqParams . Value ( 0 ) . GetUint256 ( ) ; err != nil {
2020-01-14 12:02:38 +00:00
resultsErr = response . ErrInvalidParams
Implement rpc server method: sendrawtransaction (#174)
* Added new config attributes: 'SecondsPerBlock','LowPriorityThreshold'
* Added new files:
* Added new method: CompareTo
* Fixed empty Slice case
* Added new methods: LessThan, GreaterThan, Equal, CompareTo
* Added new method: InputIntersection
* Added MaxTransactionSize, GroupOutputByAssetID
* Added ned method: ScriptHash
* Added new method: IsDoubleSpend
* Refactor blockchainer, Added Feer interface, Verify and GetMemPool method
* 1) Added MemPool
2) Added new methods to satisfy the blockchainer interface: IsLowPriority, Verify, GetMemPool
* Added new methods: RelayTxn, RelayDirectly
* Fixed tests
* Implemented RPC server method sendrawtransaction
* Refactor getrawtransaction, sendrawtransaction in separate methods
* Moved 'secondsPerBlock' to config file
* Implemented Kim suggestions:
1) Fixed data race issues
2) refactor Verify method
3) Get rid of unused InputIntersection method due to refactoring Verify method
4) Fixed bug in https://github.com/CityOfZion/neo-go/pull/174#discussion_r264108135
5) minor simplications of the code
* Fixed minor issues related to
1) space
2) getter methods do not need pointer on the receiver
3) error message
4) refactoring CompareTo method in uint256.go
* Fixed small issues
* Use sync.RWMutex instead of sync.Mutex
* Refined (R)Lock/(R)Unlock
* return error instead of bool in Verify methods
2019-03-20 12:30:05 +00:00
} else if tx , height , err := s . chain . GetTransaction ( txHash ) ; err != nil {
err = errors . Wrapf ( err , "Invalid transaction hash: %s" , txHash )
2020-01-14 12:02:38 +00:00
return nil , response . NewRPCError ( "Unknown transaction" , err . Error ( ) , err )
2020-06-04 12:43:37 +00:00
} else if reqParams . Value ( 1 ) . GetBoolean ( ) {
Implement rpc server method: sendrawtransaction (#174)
* Added new config attributes: 'SecondsPerBlock','LowPriorityThreshold'
* Added new files:
* Added new method: CompareTo
* Fixed empty Slice case
* Added new methods: LessThan, GreaterThan, Equal, CompareTo
* Added new method: InputIntersection
* Added MaxTransactionSize, GroupOutputByAssetID
* Added ned method: ScriptHash
* Added new method: IsDoubleSpend
* Refactor blockchainer, Added Feer interface, Verify and GetMemPool method
* 1) Added MemPool
2) Added new methods to satisfy the blockchainer interface: IsLowPriority, Verify, GetMemPool
* Added new methods: RelayTxn, RelayDirectly
* Fixed tests
* Implemented RPC server method sendrawtransaction
* Refactor getrawtransaction, sendrawtransaction in separate methods
* Moved 'secondsPerBlock' to config file
* Implemented Kim suggestions:
1) Fixed data race issues
2) refactor Verify method
3) Get rid of unused InputIntersection method due to refactoring Verify method
4) Fixed bug in https://github.com/CityOfZion/neo-go/pull/174#discussion_r264108135
5) minor simplications of the code
* Fixed minor issues related to
1) space
2) getter methods do not need pointer on the receiver
3) error message
4) refactoring CompareTo method in uint256.go
* Fixed small issues
* Use sync.RWMutex instead of sync.Mutex
* Refined (R)Lock/(R)Unlock
* return error instead of bool in Verify methods
2019-03-20 12:30:05 +00:00
_header := s . chain . GetHeaderHash ( int ( height ) )
header , err := s . chain . GetHeader ( _header )
if err != nil {
2020-01-14 12:02:38 +00:00
resultsErr = response . NewInvalidParamsError ( err . Error ( ) , err )
2020-06-04 12:43:37 +00:00
} else {
2020-01-13 10:21:44 +00:00
results = result . NewTransactionOutputRaw ( tx , header , s . chain )
Implement rpc server method: sendrawtransaction (#174)
* Added new config attributes: 'SecondsPerBlock','LowPriorityThreshold'
* Added new files:
* Added new method: CompareTo
* Fixed empty Slice case
* Added new methods: LessThan, GreaterThan, Equal, CompareTo
* Added new method: InputIntersection
* Added MaxTransactionSize, GroupOutputByAssetID
* Added ned method: ScriptHash
* Added new method: IsDoubleSpend
* Refactor blockchainer, Added Feer interface, Verify and GetMemPool method
* 1) Added MemPool
2) Added new methods to satisfy the blockchainer interface: IsLowPriority, Verify, GetMemPool
* Added new methods: RelayTxn, RelayDirectly
* Fixed tests
* Implemented RPC server method sendrawtransaction
* Refactor getrawtransaction, sendrawtransaction in separate methods
* Moved 'secondsPerBlock' to config file
* Implemented Kim suggestions:
1) Fixed data race issues
2) refactor Verify method
3) Get rid of unused InputIntersection method due to refactoring Verify method
4) Fixed bug in https://github.com/CityOfZion/neo-go/pull/174#discussion_r264108135
5) minor simplications of the code
* Fixed minor issues related to
1) space
2) getter methods do not need pointer on the receiver
3) error message
4) refactoring CompareTo method in uint256.go
* Fixed small issues
* Use sync.RWMutex instead of sync.Mutex
* Refined (R)Lock/(R)Unlock
* return error instead of bool in Verify methods
2019-03-20 12:30:05 +00:00
}
} else {
results = hex . EncodeToString ( tx . Bytes ( ) )
}
return results , resultsErr
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) getTransactionHeight ( ps request . Params ) ( interface { } , * response . Error ) {
2020-06-04 11:58:47 +00:00
h , err := ps . Value ( 0 ) . GetUint256 ( )
2020-03-05 14:20:50 +00:00
if err != nil {
return nil , response . ErrInvalidParams
}
_ , height , err := s . chain . GetTransaction ( h )
if err != nil {
return nil , response . NewRPCError ( "unknown transaction" , "" , nil )
}
return height , nil
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) getTxOut ( ps request . Params ) ( interface { } , * response . Error ) {
2020-06-04 11:58:47 +00:00
h , err := ps . Value ( 0 ) . GetUint256 ( )
2020-02-06 12:02:03 +00:00
if err != nil {
2020-01-14 12:02:38 +00:00
return nil , response . ErrInvalidParams
2020-02-06 12:02:03 +00:00
}
2020-06-04 11:58:47 +00:00
num , err := ps . ValueWithType ( 1 , request . NumberT ) . GetInt ( )
2020-02-06 12:02:03 +00:00
if err != nil || num < 0 {
2020-01-14 12:02:38 +00:00
return nil , response . ErrInvalidParams
2020-02-06 12:02:03 +00:00
}
tx , _ , err := s . chain . GetTransaction ( h )
if err != nil {
2020-01-14 12:02:38 +00:00
return nil , response . NewInvalidParamsError ( err . Error ( ) , err )
2020-02-06 12:02:03 +00:00
}
if num >= len ( tx . Outputs ) {
2020-01-14 12:02:38 +00:00
return nil , response . NewInvalidParamsError ( "invalid index" , errors . New ( "too big index" ) )
2020-02-06 12:02:03 +00:00
}
out := tx . Outputs [ num ]
2020-01-13 10:21:44 +00:00
return result . NewTxOutput ( & out ) , nil
2020-02-06 12:02:03 +00:00
}
2020-02-15 16:53:08 +00:00
// getContractState returns contract state (contract information, according to the contract script hash).
2020-04-28 19:35:19 +00:00
func ( s * Server ) getContractState ( reqParams request . Params ) ( interface { } , * response . Error ) {
2020-02-15 16:53:08 +00:00
var results interface { }
2020-06-04 11:58:47 +00:00
scriptHash , err := reqParams . ValueWithType ( 0 , request . StringT ) . GetUint160FromHex ( )
if err != nil {
2020-01-14 12:02:38 +00:00
return nil , response . ErrInvalidParams
2020-06-04 11:58:47 +00:00
}
cs := s . chain . GetContractState ( scriptHash )
if cs != nil {
results = result . NewContractState ( cs )
2020-02-15 16:53:08 +00:00
} else {
2020-06-04 11:58:47 +00:00
return nil , response . NewRPCError ( "Unknown contract" , "" , nil )
2020-02-15 16:53:08 +00:00
}
return results , nil
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) getAccountState ( ps request . Params ) ( interface { } , * response . Error ) {
2020-03-13 07:14:06 +00:00
return s . getAccountStateAux ( ps , false )
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) getUnspents ( ps request . Params ) ( interface { } , * response . Error ) {
2020-03-13 07:14:06 +00:00
return s . getAccountStateAux ( ps , true )
}
2019-11-15 19:04:10 +00:00
// getAccountState returns account state either in short or full (unspents included) form.
2020-04-28 19:35:19 +00:00
func ( s * Server ) getAccountStateAux ( reqParams request . Params , unspents bool ) ( interface { } , * response . Error ) {
var resultsErr * response . Error
2019-11-15 19:04:10 +00:00
var results interface { }
2020-06-04 11:58:47 +00:00
param := reqParams . ValueWithType ( 0 , request . StringT )
scriptHash , err := param . GetUint160FromAddress ( )
if err != nil {
2020-01-14 12:02:38 +00:00
return nil , response . ErrInvalidParams
2020-06-04 11:58:47 +00:00
}
as := s . chain . GetAccountState ( scriptHash )
if as == nil {
as = state . NewAccount ( scriptHash )
}
if unspents {
str , err := param . GetString ( )
if err != nil {
return nil , response . ErrInvalidParams
2019-11-15 19:04:10 +00:00
}
2020-06-04 11:58:47 +00:00
results = result . NewUnspents ( as , s . chain , str )
} else {
results = result . NewAccountState ( as )
2019-11-15 19:04:10 +00:00
}
return results , resultsErr
}
2020-02-19 09:44:31 +00:00
// getBlockSysFee returns the system fees of the block, based on the specified index.
2020-04-28 19:35:19 +00:00
func ( s * Server ) getBlockSysFee ( reqParams request . Params ) ( interface { } , * response . Error ) {
2020-06-04 11:58:47 +00:00
param := reqParams . ValueWithType ( 0 , request . NumberT )
if param == nil {
2020-02-19 09:44:31 +00:00
return 0 , response . ErrInvalidParams
}
num , err := s . blockHeightFromParam ( param )
if err != nil {
return 0 , response . NewRPCError ( "Invalid height" , "" , nil )
}
headerHash := s . chain . GetHeaderHash ( num )
2020-04-28 19:35:19 +00:00
block , errBlock := s . chain . GetBlock ( headerHash )
if errBlock != nil {
return 0 , response . NewRPCError ( errBlock . Error ( ) , "" , nil )
2020-02-19 09:44:31 +00:00
}
var blockSysFee util . Fixed8
for _ , tx := range block . Transactions {
blockSysFee += s . chain . SystemFee ( tx )
}
return blockSysFee , nil
}
2020-03-04 17:35:37 +00:00
// getBlockHeader returns the corresponding block header information according to the specified script hash.
2020-04-28 19:35:19 +00:00
func ( s * Server ) getBlockHeader ( reqParams request . Params ) ( interface { } , * response . Error ) {
2020-06-04 11:58:47 +00:00
hash , err := reqParams . ValueWithType ( 0 , request . StringT ) . GetUint256 ( )
2020-03-04 17:35:37 +00:00
if err != nil {
return nil , response . ErrInvalidParams
}
2020-06-04 12:43:37 +00:00
verbose := reqParams . Value ( 1 ) . GetBoolean ( )
2020-03-04 17:35:37 +00:00
h , err := s . chain . GetHeader ( hash )
if err != nil {
return nil , response . NewRPCError ( "unknown block" , "" , nil )
}
if verbose {
return result . NewHeader ( h , s . chain ) , nil
}
buf := io . NewBufBinWriter ( )
h . EncodeBinary ( buf . BinWriter )
if buf . Err != nil {
2020-04-28 19:35:19 +00:00
return nil , response . NewInternalServerError ( "encoding error" , buf . Err )
2020-03-04 17:35:37 +00:00
}
return hex . EncodeToString ( buf . Bytes ( ) ) , nil
}
2020-03-06 17:38:17 +00:00
// getUnclaimed returns unclaimed GAS amount of the specified address.
2020-04-28 19:35:19 +00:00
func ( s * Server ) getUnclaimed ( ps request . Params ) ( interface { } , * response . Error ) {
2020-06-04 11:58:47 +00:00
u , err := ps . ValueWithType ( 0 , request . StringT ) . GetUint160FromAddress ( )
2020-03-06 17:38:17 +00:00
if err != nil {
return nil , response . ErrInvalidParams
}
acc := s . chain . GetAccountState ( u )
if acc == nil {
return nil , response . NewInternalServerError ( "unknown account" , nil )
}
2020-04-28 19:35:19 +00:00
res , errRes := result . NewUnclaimed ( acc , s . chain )
if errRes != nil {
return nil , response . NewInternalServerError ( "can't create unclaimed response" , errRes )
}
return res , nil
2020-03-06 17:38:17 +00:00
}
2020-03-05 14:48:30 +00:00
// getValidators returns the current NEO consensus nodes information and voting status.
2020-04-28 19:35:19 +00:00
func ( s * Server ) getValidators ( _ request . Params ) ( interface { } , * response . Error ) {
2020-03-05 14:48:30 +00:00
var validators keys . PublicKeys
validators , err := s . chain . GetValidators ( )
if err != nil {
2020-04-28 19:35:19 +00:00
return nil , response . NewRPCError ( "can't get validators" , "" , err )
2020-03-05 14:48:30 +00:00
}
enrollments , err := s . chain . GetEnrollments ( )
if err != nil {
2020-04-28 19:35:19 +00:00
return nil , response . NewRPCError ( "can't get enrollments" , "" , err )
2020-03-05 14:48:30 +00:00
}
var res [ ] result . Validator
for _ , v := range enrollments {
res = append ( res , result . Validator {
PublicKey : * v . PublicKey ,
Votes : v . Votes ,
Active : validators . Contains ( v . PublicKey ) ,
} )
}
return res , nil
}
2019-11-28 16:08:31 +00:00
// invoke implements the `invoke` RPC call.
2020-04-28 19:35:19 +00:00
func ( s * Server ) invoke ( reqParams request . Params ) ( interface { } , * response . Error ) {
2020-06-04 11:58:47 +00:00
scriptHash , err := reqParams . ValueWithType ( 0 , request . StringT ) . GetUint160FromHex ( )
2019-11-28 16:08:31 +00:00
if err != nil {
2020-04-28 19:35:19 +00:00
return nil , response . ErrInvalidParams
2019-11-28 16:08:31 +00:00
}
2020-06-04 11:58:47 +00:00
slice , err := reqParams . ValueWithType ( 1 , request . ArrayT ) . GetArray ( )
2019-11-28 16:08:31 +00:00
if err != nil {
2020-04-28 19:35:19 +00:00
return nil , response . ErrInvalidParams
2019-11-28 16:08:31 +00:00
}
2020-02-17 11:54:53 +00:00
script , err := request . CreateInvocationScript ( scriptHash , slice )
2019-11-28 16:08:31 +00:00
if err != nil {
2020-04-28 19:35:19 +00:00
return nil , response . NewInternalServerError ( "can't create invocation script" , err )
2019-11-28 16:08:31 +00:00
}
2019-11-28 16:12:23 +00:00
return s . runScriptInVM ( script ) , nil
2019-11-28 16:08:31 +00:00
}
2019-11-26 10:13:17 +00:00
// invokescript implements the `invokescript` RPC call.
2020-04-28 19:35:19 +00:00
func ( s * Server ) invokeFunction ( reqParams request . Params ) ( interface { } , * response . Error ) {
2020-06-04 11:58:47 +00:00
scriptHash , err := reqParams . ValueWithType ( 0 , request . StringT ) . GetUint160FromHex ( )
2019-11-26 10:13:17 +00:00
if err != nil {
2020-04-28 19:35:19 +00:00
return nil , response . ErrInvalidParams
2019-11-26 10:13:17 +00:00
}
2020-02-17 11:54:53 +00:00
script , err := request . CreateFunctionInvocationScript ( scriptHash , reqParams [ 1 : ] )
2019-11-26 10:13:17 +00:00
if err != nil {
2020-04-28 19:35:19 +00:00
return nil , response . NewInternalServerError ( "can't create invocation script" , err )
2019-11-26 10:13:17 +00:00
}
2019-11-28 16:12:23 +00:00
return s . runScriptInVM ( script ) , nil
2019-11-26 10:13:17 +00:00
}
2019-10-29 15:31:39 +00:00
// invokescript implements the `invokescript` RPC call.
2020-04-28 19:35:19 +00:00
func ( s * Server ) invokescript ( reqParams request . Params ) ( interface { } , * response . Error ) {
2019-11-21 14:42:02 +00:00
if len ( reqParams ) < 1 {
2020-01-14 12:02:38 +00:00
return nil , response . ErrInvalidParams
2019-10-29 15:31:39 +00:00
}
2019-11-21 14:42:02 +00:00
script , err := reqParams [ 0 ] . GetBytesHex ( )
2019-10-29 15:31:39 +00:00
if err != nil {
2020-01-14 12:02:38 +00:00
return nil , response . ErrInvalidParams
2019-10-29 15:31:39 +00:00
}
2019-11-21 14:42:02 +00:00
2019-11-28 16:12:23 +00:00
return s . runScriptInVM ( script ) , nil
}
// runScriptInVM runs given script in a new test VM and returns the invocation
// result.
2020-01-13 09:27:34 +00:00
func ( s * Server ) runScriptInVM ( script [ ] byte ) * result . Invoke {
2020-04-03 06:49:01 +00:00
vm := s . chain . GetTestVM ( )
2020-01-21 12:42:56 +00:00
vm . SetGasLimit ( s . config . MaxGasInvoke )
2019-10-29 15:31:39 +00:00
vm . LoadScript ( script )
_ = vm . Run ( )
2020-01-13 09:27:34 +00:00
result := & result . Invoke {
2019-10-29 15:31:39 +00:00
State : vm . State ( ) ,
2020-01-21 12:37:47 +00:00
GasConsumed : vm . GasConsumed ( ) . String ( ) ,
2019-11-28 16:12:23 +00:00
Script : hex . EncodeToString ( script ) ,
2020-03-03 10:08:34 +00:00
Stack : vm . Estack ( ) . ToContractParameters ( ) ,
2019-10-29 15:31:39 +00:00
}
2019-11-28 16:12:23 +00:00
return result
2019-10-29 15:31:39 +00:00
}
2020-03-02 17:01:32 +00:00
// submitBlock broadcasts a raw block over the NEO network.
2020-04-28 19:35:19 +00:00
func ( s * Server ) submitBlock ( reqParams request . Params ) ( interface { } , * response . Error ) {
2020-06-04 11:58:47 +00:00
blockBytes , err := reqParams . ValueWithType ( 0 , request . StringT ) . GetBytesHex ( )
2020-03-02 17:01:32 +00:00
if err != nil {
return nil , response . ErrInvalidParams
}
b := block . Block { }
r := io . NewBinReaderFromBuf ( blockBytes )
b . DecodeBinary ( r )
if r . Err != nil {
return nil , response . ErrInvalidParams
}
err = s . chain . AddBlock ( & b )
if err != nil {
switch err {
case core . ErrInvalidBlockIndex , core . ErrAlreadyExists :
return nil , response . ErrAlreadyExists
default :
return nil , response . ErrValidationFailed
}
}
return true , nil
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) sendrawtransaction ( reqParams request . Params ) ( interface { } , * response . Error ) {
var resultsErr * response . Error
Implement rpc server method: sendrawtransaction (#174)
* Added new config attributes: 'SecondsPerBlock','LowPriorityThreshold'
* Added new files:
* Added new method: CompareTo
* Fixed empty Slice case
* Added new methods: LessThan, GreaterThan, Equal, CompareTo
* Added new method: InputIntersection
* Added MaxTransactionSize, GroupOutputByAssetID
* Added ned method: ScriptHash
* Added new method: IsDoubleSpend
* Refactor blockchainer, Added Feer interface, Verify and GetMemPool method
* 1) Added MemPool
2) Added new methods to satisfy the blockchainer interface: IsLowPriority, Verify, GetMemPool
* Added new methods: RelayTxn, RelayDirectly
* Fixed tests
* Implemented RPC server method sendrawtransaction
* Refactor getrawtransaction, sendrawtransaction in separate methods
* Moved 'secondsPerBlock' to config file
* Implemented Kim suggestions:
1) Fixed data race issues
2) refactor Verify method
3) Get rid of unused InputIntersection method due to refactoring Verify method
4) Fixed bug in https://github.com/CityOfZion/neo-go/pull/174#discussion_r264108135
5) minor simplications of the code
* Fixed minor issues related to
1) space
2) getter methods do not need pointer on the receiver
3) error message
4) refactoring CompareTo method in uint256.go
* Fixed small issues
* Use sync.RWMutex instead of sync.Mutex
* Refined (R)Lock/(R)Unlock
* return error instead of bool in Verify methods
2019-03-20 12:30:05 +00:00
var results interface { }
2019-11-21 14:42:02 +00:00
if len ( reqParams ) < 1 {
2020-01-14 12:02:38 +00:00
return nil , response . ErrInvalidParams
2019-11-21 14:42:02 +00:00
} else if byteTx , err := reqParams [ 0 ] . GetBytesHex ( ) ; err != nil {
2020-01-14 12:02:38 +00:00
return nil , response . ErrInvalidParams
Implement rpc server method: sendrawtransaction (#174)
* Added new config attributes: 'SecondsPerBlock','LowPriorityThreshold'
* Added new files:
* Added new method: CompareTo
* Fixed empty Slice case
* Added new methods: LessThan, GreaterThan, Equal, CompareTo
* Added new method: InputIntersection
* Added MaxTransactionSize, GroupOutputByAssetID
* Added ned method: ScriptHash
* Added new method: IsDoubleSpend
* Refactor blockchainer, Added Feer interface, Verify and GetMemPool method
* 1) Added MemPool
2) Added new methods to satisfy the blockchainer interface: IsLowPriority, Verify, GetMemPool
* Added new methods: RelayTxn, RelayDirectly
* Fixed tests
* Implemented RPC server method sendrawtransaction
* Refactor getrawtransaction, sendrawtransaction in separate methods
* Moved 'secondsPerBlock' to config file
* Implemented Kim suggestions:
1) Fixed data race issues
2) refactor Verify method
3) Get rid of unused InputIntersection method due to refactoring Verify method
4) Fixed bug in https://github.com/CityOfZion/neo-go/pull/174#discussion_r264108135
5) minor simplications of the code
* Fixed minor issues related to
1) space
2) getter methods do not need pointer on the receiver
3) error message
4) refactoring CompareTo method in uint256.go
* Fixed small issues
* Use sync.RWMutex instead of sync.Mutex
* Refined (R)Lock/(R)Unlock
* return error instead of bool in Verify methods
2019-03-20 12:30:05 +00:00
} else {
2019-09-16 09:18:13 +00:00
r := io . NewBinReaderFromBuf ( byteTx )
Implement rpc server method: sendrawtransaction (#174)
* Added new config attributes: 'SecondsPerBlock','LowPriorityThreshold'
* Added new files:
* Added new method: CompareTo
* Fixed empty Slice case
* Added new methods: LessThan, GreaterThan, Equal, CompareTo
* Added new method: InputIntersection
* Added MaxTransactionSize, GroupOutputByAssetID
* Added ned method: ScriptHash
* Added new method: IsDoubleSpend
* Refactor blockchainer, Added Feer interface, Verify and GetMemPool method
* 1) Added MemPool
2) Added new methods to satisfy the blockchainer interface: IsLowPriority, Verify, GetMemPool
* Added new methods: RelayTxn, RelayDirectly
* Fixed tests
* Implemented RPC server method sendrawtransaction
* Refactor getrawtransaction, sendrawtransaction in separate methods
* Moved 'secondsPerBlock' to config file
* Implemented Kim suggestions:
1) Fixed data race issues
2) refactor Verify method
3) Get rid of unused InputIntersection method due to refactoring Verify method
4) Fixed bug in https://github.com/CityOfZion/neo-go/pull/174#discussion_r264108135
5) minor simplications of the code
* Fixed minor issues related to
1) space
2) getter methods do not need pointer on the receiver
3) error message
4) refactoring CompareTo method in uint256.go
* Fixed small issues
* Use sync.RWMutex instead of sync.Mutex
* Refined (R)Lock/(R)Unlock
* return error instead of bool in Verify methods
2019-03-20 12:30:05 +00:00
tx := & transaction . Transaction { }
2019-09-16 16:31:49 +00:00
tx . DecodeBinary ( r )
if r . Err != nil {
2020-03-05 17:04:05 +00:00
return nil , response . ErrInvalidParams
Implement rpc server method: sendrawtransaction (#174)
* Added new config attributes: 'SecondsPerBlock','LowPriorityThreshold'
* Added new files:
* Added new method: CompareTo
* Fixed empty Slice case
* Added new methods: LessThan, GreaterThan, Equal, CompareTo
* Added new method: InputIntersection
* Added MaxTransactionSize, GroupOutputByAssetID
* Added ned method: ScriptHash
* Added new method: IsDoubleSpend
* Refactor blockchainer, Added Feer interface, Verify and GetMemPool method
* 1) Added MemPool
2) Added new methods to satisfy the blockchainer interface: IsLowPriority, Verify, GetMemPool
* Added new methods: RelayTxn, RelayDirectly
* Fixed tests
* Implemented RPC server method sendrawtransaction
* Refactor getrawtransaction, sendrawtransaction in separate methods
* Moved 'secondsPerBlock' to config file
* Implemented Kim suggestions:
1) Fixed data race issues
2) refactor Verify method
3) Get rid of unused InputIntersection method due to refactoring Verify method
4) Fixed bug in https://github.com/CityOfZion/neo-go/pull/174#discussion_r264108135
5) minor simplications of the code
* Fixed minor issues related to
1) space
2) getter methods do not need pointer on the receiver
3) error message
4) refactoring CompareTo method in uint256.go
* Fixed small issues
* Use sync.RWMutex instead of sync.Mutex
* Refined (R)Lock/(R)Unlock
* return error instead of bool in Verify methods
2019-03-20 12:30:05 +00:00
}
2020-03-05 17:04:05 +00:00
relayReason := s . coreServer . RelayTxn ( tx )
switch relayReason {
case network . RelaySucceed :
results = true
case network . RelayAlreadyExists :
resultsErr = response . ErrAlreadyExists
case network . RelayOutOfMemory :
resultsErr = response . ErrOutOfMemory
case network . RelayUnableToVerify :
resultsErr = response . ErrUnableToVerify
case network . RelayInvalid :
resultsErr = response . ErrValidationFailed
case network . RelayPolicyFail :
resultsErr = response . ErrPolicyFail
default :
resultsErr = response . ErrUnknown
Implement rpc server method: sendrawtransaction (#174)
* Added new config attributes: 'SecondsPerBlock','LowPriorityThreshold'
* Added new files:
* Added new method: CompareTo
* Fixed empty Slice case
* Added new methods: LessThan, GreaterThan, Equal, CompareTo
* Added new method: InputIntersection
* Added MaxTransactionSize, GroupOutputByAssetID
* Added ned method: ScriptHash
* Added new method: IsDoubleSpend
* Refactor blockchainer, Added Feer interface, Verify and GetMemPool method
* 1) Added MemPool
2) Added new methods to satisfy the blockchainer interface: IsLowPriority, Verify, GetMemPool
* Added new methods: RelayTxn, RelayDirectly
* Fixed tests
* Implemented RPC server method sendrawtransaction
* Refactor getrawtransaction, sendrawtransaction in separate methods
* Moved 'secondsPerBlock' to config file
* Implemented Kim suggestions:
1) Fixed data race issues
2) refactor Verify method
3) Get rid of unused InputIntersection method due to refactoring Verify method
4) Fixed bug in https://github.com/CityOfZion/neo-go/pull/174#discussion_r264108135
5) minor simplications of the code
* Fixed minor issues related to
1) space
2) getter methods do not need pointer on the receiver
3) error message
4) refactoring CompareTo method in uint256.go
* Fixed small issues
* Use sync.RWMutex instead of sync.Mutex
* Refined (R)Lock/(R)Unlock
* return error instead of bool in Verify methods
2019-03-20 12:30:05 +00:00
}
}
return results , resultsErr
}
2020-05-10 22:00:19 +00:00
// subscribe handles subscription requests from websocket clients.
func ( s * Server ) subscribe ( reqParams request . Params , sub * subscriber ) ( interface { } , * response . Error ) {
2020-06-04 11:58:47 +00:00
streamName , err := reqParams . Value ( 0 ) . GetString ( )
2020-05-10 22:00:19 +00:00
if err != nil {
return nil , response . ErrInvalidParams
}
event , err := response . GetEventIDFromString ( streamName )
2020-05-12 19:38:29 +00:00
if err != nil || event == response . MissedEventID {
2020-05-10 22:00:19 +00:00
return nil , response . ErrInvalidParams
}
2020-05-13 14:13:33 +00:00
// Optional filter.
var filter interface { }
2020-06-04 11:58:47 +00:00
if p := reqParams . Value ( 1 ) ; p != nil {
2020-05-13 14:13:33 +00:00
// It doesn't accept filters.
if event == response . BlockEventID {
return nil , response . ErrInvalidParams
}
switch event {
case response . TransactionEventID :
if p . Type != request . TxFilterT {
return nil , response . ErrInvalidParams
}
case response . NotificationEventID :
if p . Type != request . NotificationFilterT {
return nil , response . ErrInvalidParams
}
case response . ExecutionEventID :
if p . Type != request . ExecutionFilterT {
return nil , response . ErrInvalidParams
}
}
filter = p . Value
}
2020-05-10 22:00:19 +00:00
s . subsLock . Lock ( )
defer s . subsLock . Unlock ( )
select {
case <- s . shutdown :
return nil , response . NewInternalServerError ( "server is shutting down" , nil )
default :
}
var id int
for ; id < len ( sub . feeds ) ; id ++ {
2020-05-13 14:13:33 +00:00
if sub . feeds [ id ] . event == response . InvalidEventID {
2020-05-10 22:00:19 +00:00
break
}
}
if id == len ( sub . feeds ) {
return nil , response . NewInternalServerError ( "maximum number of subscriptions is reached" , nil )
}
2020-05-13 14:13:33 +00:00
sub . feeds [ id ] . event = event
sub . feeds [ id ] . filter = filter
2020-05-10 22:00:19 +00:00
s . subscribeToChannel ( event )
return strconv . FormatInt ( int64 ( id ) , 10 ) , nil
}
// subscribeToChannel subscribes RPC server to appropriate chain events if
// it's not yet subscribed for them. It's supposed to be called with s.subsLock
// taken by the caller.
func ( s * Server ) subscribeToChannel ( event response . EventID ) {
switch event {
case response . BlockEventID :
if s . blockSubs == 0 {
s . chain . SubscribeForBlocks ( s . blockCh )
}
s . blockSubs ++
case response . TransactionEventID :
if s . transactionSubs == 0 {
s . chain . SubscribeForTransactions ( s . transactionCh )
}
s . transactionSubs ++
case response . NotificationEventID :
if s . notificationSubs == 0 {
s . chain . SubscribeForNotifications ( s . notificationCh )
}
s . notificationSubs ++
case response . ExecutionEventID :
if s . executionSubs == 0 {
s . chain . SubscribeForExecutions ( s . executionCh )
}
s . executionSubs ++
}
}
// unsubscribe handles unsubscription requests from websocket clients.
func ( s * Server ) unsubscribe ( reqParams request . Params , sub * subscriber ) ( interface { } , * response . Error ) {
2020-06-04 11:58:47 +00:00
id , err := reqParams . Value ( 0 ) . GetInt ( )
2020-05-10 22:00:19 +00:00
if err != nil || id < 0 {
return nil , response . ErrInvalidParams
}
s . subsLock . Lock ( )
defer s . subsLock . Unlock ( )
2020-05-13 14:13:33 +00:00
if len ( sub . feeds ) <= id || sub . feeds [ id ] . event == response . InvalidEventID {
2020-05-10 22:00:19 +00:00
return nil , response . ErrInvalidParams
}
2020-05-13 14:13:33 +00:00
event := sub . feeds [ id ] . event
sub . feeds [ id ] . event = response . InvalidEventID
sub . feeds [ id ] . filter = nil
2020-05-10 22:00:19 +00:00
s . unsubscribeFromChannel ( event )
return true , nil
}
// unsubscribeFromChannel unsubscribes RPC server from appropriate chain events
// if there are no other subscribers for it. It's supposed to be called with
// s.subsLock taken by the caller.
func ( s * Server ) unsubscribeFromChannel ( event response . EventID ) {
switch event {
case response . BlockEventID :
s . blockSubs --
if s . blockSubs == 0 {
s . chain . UnsubscribeFromBlocks ( s . blockCh )
}
case response . TransactionEventID :
s . transactionSubs --
if s . transactionSubs == 0 {
s . chain . UnsubscribeFromTransactions ( s . transactionCh )
}
case response . NotificationEventID :
s . notificationSubs --
if s . notificationSubs == 0 {
s . chain . UnsubscribeFromNotifications ( s . notificationCh )
}
case response . ExecutionEventID :
s . executionSubs --
if s . executionSubs == 0 {
s . chain . UnsubscribeFromExecutions ( s . executionCh )
}
}
}
func ( s * Server ) handleSubEvents ( ) {
2020-05-12 19:38:29 +00:00
b , err := json . Marshal ( response . Notification {
JSONRPC : request . JSONRPCVersion ,
Event : response . MissedEventID ,
Payload : make ( [ ] interface { } , 0 ) ,
} )
if err != nil {
s . log . Error ( "fatal: failed to marshal overflow event" , zap . Error ( err ) )
return
}
overflowMsg , err := websocket . NewPreparedMessage ( websocket . TextMessage , b )
if err != nil {
s . log . Error ( "fatal: failed to prepare overflow message" , zap . Error ( err ) )
return
}
2020-05-10 22:00:19 +00:00
chloop :
for {
var resp = response . Notification {
JSONRPC : request . JSONRPCVersion ,
Payload : make ( [ ] interface { } , 1 ) ,
}
var msg * websocket . PreparedMessage
select {
case <- s . shutdown :
break chloop
case b := <- s . blockCh :
resp . Event = response . BlockEventID
resp . Payload [ 0 ] = b
case execution := <- s . executionCh :
resp . Event = response . ExecutionEventID
resp . Payload [ 0 ] = result . NewApplicationLog ( execution , util . Uint160 { } )
case notification := <- s . notificationCh :
resp . Event = response . NotificationEventID
resp . Payload [ 0 ] = result . StateEventToResultNotification ( * notification )
case tx := <- s . transactionCh :
resp . Event = response . TransactionEventID
resp . Payload [ 0 ] = tx
}
s . subsLock . RLock ( )
subloop :
for sub := range s . subscribers {
2020-05-12 19:38:29 +00:00
if sub . overflown . Load ( ) {
continue
}
2020-05-13 14:13:33 +00:00
for i := range sub . feeds {
if sub . feeds [ i ] . Matches ( & resp ) {
2020-05-10 22:00:19 +00:00
if msg == nil {
2020-05-12 19:38:29 +00:00
b , err = json . Marshal ( resp )
2020-05-10 22:00:19 +00:00
if err != nil {
s . log . Error ( "failed to marshal notification" ,
zap . Error ( err ) ,
zap . String ( "type" , resp . Event . String ( ) ) )
break subloop
}
msg , err = websocket . NewPreparedMessage ( websocket . TextMessage , b )
if err != nil {
s . log . Error ( "failed to prepare notification message" ,
zap . Error ( err ) ,
zap . String ( "type" , resp . Event . String ( ) ) )
break subloop
}
}
2020-05-12 19:38:29 +00:00
select {
case sub . writer <- msg :
default :
sub . overflown . Store ( true )
// MissedEvent is to be delivered eventually.
go func ( sub * subscriber ) {
sub . writer <- overflowMsg
sub . overflown . Store ( false )
} ( sub )
}
2020-05-10 22:00:19 +00:00
// The message is sent only once per subscriber.
break
}
}
}
s . subsLock . RUnlock ( )
}
// It's important to do it with lock held because no subscription routine
// should be running concurrently to this one. And even if one is to run
// after unlock, it'll see closed s.shutdown and won't subscribe.
s . subsLock . Lock ( )
// There might be no subscription in reality, but it's not a problem as
// core.Blockchain allows unsubscribing non-subscribed channels.
s . chain . UnsubscribeFromBlocks ( s . blockCh )
s . chain . UnsubscribeFromTransactions ( s . transactionCh )
s . chain . UnsubscribeFromNotifications ( s . notificationCh )
s . chain . UnsubscribeFromExecutions ( s . executionCh )
s . subsLock . Unlock ( )
drainloop :
for {
select {
case <- s . blockCh :
case <- s . executionCh :
case <- s . notificationCh :
case <- s . transactionCh :
default :
break drainloop
}
}
// It's not required closing these, but since they're drained already
// this is safe and it also allows to give a signal to Shutdown routine.
close ( s . blockCh )
close ( s . transactionCh )
close ( s . notificationCh )
close ( s . executionCh )
}
2020-04-28 19:35:19 +00:00
func ( s * Server ) blockHeightFromParam ( param * request . Param ) ( int , * response . Error ) {
2019-11-26 10:13:17 +00:00
num , err := param . GetInt ( )
if err != nil {
return 0 , nil
}
if num < 0 || num > int ( s . chain . BlockHeight ( ) ) {
return 0 , invalidBlockHeightError ( 0 , num )
}
return num , nil
2018-03-23 20:36:59 +00:00
}
2020-01-13 08:27:22 +00:00
2020-04-28 19:56:19 +00:00
func ( s * Server ) packResponseToRaw ( r * request . In , result interface { } , respErr * response . Error ) response . Raw {
2020-01-14 12:02:38 +00:00
resp := response . Raw {
HeaderAndError : response . HeaderAndError {
Header : response . Header {
JSONRPC : r . JSONRPC ,
ID : r . RawID ,
} ,
} ,
}
2020-04-28 19:56:19 +00:00
if respErr != nil {
resp . Error = respErr
} else {
resJSON , err := json . Marshal ( result )
if err != nil {
s . log . Error ( "failed to marshal result" ,
zap . Error ( err ) ,
zap . String ( "method" , r . Method ) )
resp . Error = response . NewInternalServerError ( "failed to encode result" , err )
} else {
resp . Result = resJSON
}
}
return resp
}
2020-01-14 12:02:38 +00:00
2020-04-28 19:56:19 +00:00
// logRequestError is a request error logger.
func ( s * Server ) logRequestError ( r * request . In , jsonErr * response . Error ) {
2020-01-14 12:02:38 +00:00
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 ... )
}
2020-04-28 19:56:19 +00:00
// writeHTTPErrorResponse writes an error response to the ResponseWriter.
func ( s * Server ) writeHTTPErrorResponse ( r * request . In , w http . ResponseWriter , jsonErr * response . Error ) {
resp := s . packResponseToRaw ( r , nil , jsonErr )
s . writeHTTPServerResponse ( r , w , resp )
2020-01-14 12:02:38 +00:00
}
2020-04-28 19:56:19 +00:00
func ( s * Server ) writeHTTPServerResponse ( r * request . In , w http . ResponseWriter , resp response . Raw ) {
// Errors can happen in many places and we can only catch ALL of them here.
if resp . Error != nil {
s . logRequestError ( r , resp . Error )
w . WriteHeader ( resp . Error . HTTPCode )
}
2020-01-14 12:02:38 +00:00
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 ) )
}
}
2020-01-13 08:27:22 +00:00
// 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
2020-01-13 08:33:04 +00:00
func validateAddress ( addr interface { } ) result . ValidateAddress {
resp := result . ValidateAddress { Address : addr }
2020-01-13 08:27:22 +00:00
if addr , ok := addr . ( string ) ; ok {
_ , err := address . StringToUint160 ( addr )
resp . IsValid = ( err == nil )
}
return resp
}