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-11-13 13:54:38 +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-08-04 15:16:32 +00:00
"strings"
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"
"github.com/nspcc-dev/neo-go/pkg/util"
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-09-14 14:48:17 +00:00
// Maximum number of elements for get*transfers requests.
maxTransfersLimit = 1000
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 ,
2020-09-14 18:52:05 +00:00
"getalltransfertx" : ( * Server ) . getAllTransferTx ,
2020-03-13 07:29:49 +00:00
"getapplicationlog" : ( * Server ) . getApplicationLog ,
"getassetstate" : ( * Server ) . getAssetState ,
"getbestblockhash" : ( * Server ) . getBestBlockHash ,
"getblock" : ( * Server ) . getBlock ,
"getblockcount" : ( * Server ) . getBlockCount ,
"getblockhash" : ( * Server ) . getBlockHash ,
"getblockheader" : ( * Server ) . getBlockHeader ,
"getblocksysfee" : ( * Server ) . getBlockSysFee ,
2020-10-30 12:36:34 +00:00
"getblocktransfertx" : ( * Server ) . getBlockTransferTx ,
2020-03-13 07:29:49 +00:00
"getclaimable" : ( * Server ) . getClaimable ,
"getconnectioncount" : ( * Server ) . getConnectionCount ,
"getcontractstate" : ( * Server ) . getContractState ,
2020-09-11 12:41:08 +00:00
"getminimumnetworkfee" : ( * Server ) . getMinimumNetworkFee ,
2020-03-13 07:29:49 +00:00
"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 ,
2020-08-04 15:16:32 +00:00
"getutxotransfers" : ( * Server ) . getUTXOTransfers ,
2020-03-13 07:29:49 +00:00
"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 )
2020-06-25 16:31:45 +00:00
if err != http . ErrServerClosed {
2020-03-10 11:56:18 +00:00
s . log . Error ( "failed to start TLS RPC server" , zap . Error ( err ) )
2020-06-25 16:31:45 +00:00
errChan <- err
2020-03-10 11:56:18 +00:00
}
} ( )
}
err := s . ListenAndServe ( )
2020-06-25 16:31:45 +00:00
if err != http . ErrServerClosed {
2020-03-10 11:56:18 +00:00
s . log . Error ( "failed to start RPC server" , zap . Error ( err ) )
2020-06-25 16:31:45 +00:00
errChan <- err
2020-03-10 11:56:18 +00:00
}
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-10-26 17:22:20 +00:00
req := request . NewRequest ( )
2020-05-10 22:00:19 +00:00
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 (
2020-10-26 17:22:20 +00:00
request . NewIn ( ) ,
2020-05-10 22:00:19 +00:00
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
}
2020-10-26 17:22:20 +00:00
resChan := make ( chan response . AbstractResult ) // response.Raw or response.RawBatch
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 (
2020-10-26 17:22:20 +00:00
request . NewIn ( ) ,
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-10-26 17:22:20 +00:00
s . writeHTTPErrorResponse ( request . NewIn ( ) , 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-10-26 17:22:20 +00:00
func ( s * Server ) handleRequest ( req * request . Request , sub * subscriber ) response . AbstractResult {
if req . In != nil {
return s . handleIn ( req . In , sub )
}
resp := make ( response . RawBatch , len ( req . Batch ) )
for i , in := range req . Batch {
resp [ i ] = s . handleIn ( & in , sub )
}
return resp
}
func ( s * Server ) handleIn ( req * request . In , sub * subscriber ) response . Raw {
2020-05-10 22:00:19 +00:00
var res interface { }
var resErr * response . Error
2020-10-26 17:22:20 +00:00
if req . JSONRPC != request . JSONRPCVersion {
return s . packResponseToRaw ( req , nil , response . NewInvalidParamsError ( "Problem parsing JSON" , fmt . Errorf ( "invalid version, expected 2.0 got: '%s'" , req . JSONRPC ) ) )
}
2020-05-10 22:00:19 +00:00
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-10-26 17:22:20 +00:00
func ( s * Server ) handleWsWrites ( ws * websocket . Conn , resChan <- chan response . AbstractResult , 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-10-26 17:22:20 +00:00
func ( s * Server ) handleWsReads ( ws * websocket . Conn , resChan chan <- response . AbstractResult , 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 {
2020-10-26 17:22:20 +00:00
req := request . NewRequest ( )
2020-04-29 12:25:58 +00:00
err := ws . ReadJSON ( req )
if err != nil {
break
}
2020-05-10 22:00:19 +00:00
res := s . handleRequest ( req , subscr )
2020-10-26 17:22:20 +00:00
res . RunForErrors ( func ( jsonErr * response . Error ) {
s . logRequestError ( req , jsonErr )
} )
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-10-30 12:36:34 +00:00
func ( s * Server ) getBlockHashFromParam ( param * request . Param ) ( util . Uint256 , * response . Error ) {
2020-03-13 07:08:30 +00:00
var hash util . Uint256
2020-06-04 11:58:47 +00:00
if param == nil {
2020-10-30 12:36:34 +00:00
return hash , response . ErrInvalidParams
2020-03-13 07:08:30 +00:00
}
switch param . Type {
case request . StringT :
var err error
hash , err = param . GetUint256 ( )
if err != nil {
2020-10-30 12:36:34 +00:00
return hash , response . ErrInvalidParams
2020-03-13 07:08:30 +00:00
}
case request . NumberT :
num , err := s . blockHeightFromParam ( param )
if err != nil {
2020-10-30 12:36:34 +00:00
return hash , response . ErrInvalidParams
2020-03-13 07:08:30 +00:00
}
hash = s . chain . GetHeaderHash ( num )
default :
2020-10-30 12:36:34 +00:00
return hash , response . ErrInvalidParams
}
return hash , nil
}
func ( s * Server ) getBlock ( reqParams request . Params ) ( interface { } , * response . Error ) {
hash , respErr := s . getBlockHashFromParam ( reqParams . Value ( 0 ) )
if respErr != nil {
return nil , respErr
2020-03-13 07:08:30 +00:00
}
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 {
2021-02-15 13:18:03 +00:00
return nil , err
2020-03-13 07:06:52 +00:00
}
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-09-12 21:12:45 +00:00
func getTimestampsAndLimit ( ps request . Params , index int ) ( uint32 , uint32 , int , int , error ) {
2020-08-04 15:16:32 +00:00
var start , end uint32
2020-09-12 21:12:45 +00:00
var limit , page int
2020-09-14 14:48:17 +00:00
limit = maxTransfersLimit
2020-09-12 21:12:45 +00:00
pStart , pEnd , pLimit , pPage := ps . Value ( index ) , ps . Value ( index + 1 ) , ps . Value ( index + 2 ) , ps . Value ( index + 3 )
if pPage != nil {
p , err := pPage . GetInt ( )
if err != nil {
return 0 , 0 , 0 , 0 , err
}
if p < 0 {
return 0 , 0 , 0 , 0 , errors . New ( "can't use negative page" )
}
page = p
}
2020-09-11 19:33:17 +00:00
if pLimit != nil {
l , err := pLimit . GetInt ( )
2020-08-04 15:16:32 +00:00
if err != nil {
2020-09-12 21:12:45 +00:00
return 0 , 0 , 0 , 0 , err
2020-08-04 15:16:32 +00:00
}
2020-09-11 19:33:17 +00:00
if l <= 0 {
2020-09-12 21:12:45 +00:00
return 0 , 0 , 0 , 0 , errors . New ( "can't use negative or zero limit" )
2020-09-11 19:33:17 +00:00
}
2020-09-14 14:48:17 +00:00
if l > maxTransfersLimit {
return 0 , 0 , 0 , 0 , errors . New ( "too big limit requested" )
}
2020-09-11 19:33:17 +00:00
limit = l
2020-08-04 15:16:32 +00:00
}
2020-09-11 19:33:17 +00:00
if pEnd != nil {
val , err := pEnd . GetInt ( )
2020-08-04 15:16:32 +00:00
if err != nil {
2020-09-12 21:12:45 +00:00
return 0 , 0 , 0 , 0 , err
2020-08-04 15:16:32 +00:00
}
end = uint32 ( val )
2020-09-11 19:33:17 +00:00
} else {
end = uint32 ( time . Now ( ) . Unix ( ) )
2020-08-04 15:16:32 +00:00
}
2020-09-11 19:33:17 +00:00
if pStart != nil {
val , err := pStart . GetInt ( )
2020-09-08 09:56:52 +00:00
if err != nil {
2020-09-12 21:12:45 +00:00
return 0 , 0 , 0 , 0 , err
2020-09-08 09:56:52 +00:00
}
2020-09-11 19:33:17 +00:00
start = uint32 ( val )
} else {
start = uint32 ( time . Now ( ) . Add ( - time . Hour * 24 * 7 ) . Unix ( ) )
2020-09-08 09:56:52 +00:00
}
2020-09-12 21:12:45 +00:00
return start , end , limit , page , nil
2020-08-04 15:16:32 +00:00
}
func getAssetMaps ( name string ) ( map [ util . Uint256 ] * result . AssetUTXO , map [ util . Uint256 ] * result . AssetUTXO , error ) {
sent := make ( map [ util . Uint256 ] * result . AssetUTXO )
recv := make ( map [ util . Uint256 ] * result . AssetUTXO )
name = strings . ToLower ( name )
switch name {
case "neo" , "gas" , "" :
default :
return nil , nil , errors . New ( "invalid asset" )
}
if name == "neo" || name == "" {
sent [ core . GoverningTokenID ( ) ] = & result . AssetUTXO {
AssetHash : core . GoverningTokenID ( ) ,
AssetName : "NEO" ,
Transactions : [ ] result . UTXO { } ,
}
recv [ core . GoverningTokenID ( ) ] = & result . AssetUTXO {
AssetHash : core . GoverningTokenID ( ) ,
AssetName : "NEO" ,
Transactions : [ ] result . UTXO { } ,
}
}
if name == "gas" || name == "" {
sent [ core . UtilityTokenID ( ) ] = & result . AssetUTXO {
AssetHash : core . UtilityTokenID ( ) ,
AssetName : "GAS" ,
Transactions : [ ] result . UTXO { } ,
}
recv [ core . UtilityTokenID ( ) ] = & result . AssetUTXO {
AssetHash : core . UtilityTokenID ( ) ,
AssetName : "GAS" ,
Transactions : [ ] result . UTXO { } ,
}
}
return sent , recv , nil
}
func ( s * Server ) getUTXOTransfers ( ps request . Params ) ( interface { } , * response . Error ) {
addr , err := ps . Value ( 0 ) . GetUint160FromAddressOrHex ( )
if err != nil {
return nil , response . NewInvalidParamsError ( "" , err )
}
index := 1
assetName , err := ps . Value ( index ) . GetString ( )
if err == nil {
index ++
}
2020-09-12 21:12:45 +00:00
start , end , limit , page , err := getTimestampsAndLimit ( ps , index )
2020-08-04 15:16:32 +00:00
if err != nil {
return nil , response . NewInvalidParamsError ( "" , err )
}
sent , recv , err := getAssetMaps ( assetName )
if err != nil {
return nil , response . NewInvalidParamsError ( "" , err )
}
tr := new ( state . Transfer )
2020-09-12 21:12:45 +00:00
var resCount , frameCount int
2020-09-08 12:29:07 +00:00
err = s . chain . ForEachTransfer ( addr , tr , func ( ) ( bool , error ) {
2020-09-12 21:12:45 +00:00
// Iterating from newest to oldest, not yet reached required
// time frame, continue looping.
2020-09-08 12:29:07 +00:00
if tr . Timestamp > end {
return true , nil
}
2020-09-12 21:12:45 +00:00
// Iterating from newest to oldest, moved past required
// time frame, stop looping.
if tr . Timestamp < start {
2020-09-08 12:29:07 +00:00
return false , nil
2020-08-04 15:16:32 +00:00
}
2020-09-12 21:12:45 +00:00
frameCount ++
// Using limits, not yet reached required page.
if limit != 0 && page * limit >= frameCount {
return true , nil
}
2020-08-04 15:16:32 +00:00
assetID := core . GoverningTokenID ( )
if ! tr . IsGoverning {
assetID = core . UtilityTokenID ( )
}
2020-08-07 12:37:05 +00:00
m := recv
if tr . IsSent {
m = sent
2020-08-04 15:16:32 +00:00
}
2020-08-07 12:37:05 +00:00
a , ok := m [ assetID ]
if ok {
2020-08-04 15:16:32 +00:00
a . Transactions = append ( a . Transactions , result . UTXO {
Index : tr . Block ,
Timestamp : tr . Timestamp ,
TxHash : tr . Tx ,
Amount : tr . Amount ,
} )
a . TotalAmount += tr . Amount
}
2020-09-12 21:12:45 +00:00
resCount ++
// Using limits, reached limit.
if limit != 0 && resCount >= limit {
return false , nil
}
2020-09-08 12:29:07 +00:00
return true , nil
2020-08-04 15:16:32 +00:00
} )
if err != nil {
return nil , response . NewInternalServerError ( "" , err )
}
res := & result . GetUTXO {
Address : address . Uint160ToString ( addr ) ,
Sent : [ ] result . AssetUTXO { } ,
Received : [ ] result . AssetUTXO { } ,
}
for _ , a := range sent {
res . Sent = append ( res . Sent , * a )
}
for _ , a := range recv {
res . Received = append ( res . Received , * a )
}
return res , 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-07-03 15:10:07 +00:00
u , err := ps . Value ( 0 ) . GetUint160FromAddressOrHex ( )
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-11 15:22:46 +00:00
for h , bal := range as . Trackers {
2021-02-16 17:16:02 +00:00
amount := bal . Balance . String ( )
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-07-03 15:25:18 +00:00
u , err := ps . Value ( 0 ) . GetUint160FromAddressOrHex ( )
2020-03-05 12:16:03 +00:00
if err != nil {
return nil , response . ErrInvalidParams
}
2020-09-12 21:12:45 +00:00
start , end , limit , page , err := getTimestampsAndLimit ( ps , 1 )
2020-08-07 06:42:44 +00:00
if err != nil {
return nil , response . NewInvalidParamsError ( "" , err )
}
2020-03-11 12:03:20 +00:00
bs := & result . NEP5Transfers {
Address : address . Uint160ToString ( u ) ,
Received : [ ] result . NEP5Transfer { } ,
Sent : [ ] result . NEP5Transfer { } ,
}
2020-08-04 13:55:45 +00:00
tr := new ( state . NEP5Transfer )
2020-09-12 21:12:45 +00:00
var resCount , frameCount int
2020-09-08 12:29:07 +00:00
err = s . chain . ForEachNEP5Transfer ( u , tr , func ( ) ( bool , error ) {
2020-09-12 21:12:45 +00:00
// Iterating from newest to oldest, not yet reached required
// time frame, continue looping.
2020-09-08 12:29:07 +00:00
if tr . Timestamp > end {
return true , nil
}
2020-09-12 21:12:45 +00:00
// Iterating from newest to oldest, moved past required
// time frame, stop looping.
if tr . Timestamp < start {
2020-09-08 12:29:07 +00:00
return false , nil
2020-08-07 06:42:44 +00:00
}
2020-09-12 21:12:45 +00:00
frameCount ++
// Using limits, not yet reached required page.
if limit != 0 && page * limit >= frameCount {
return true , nil
}
2020-03-05 12:16:03 +00:00
transfer := result . NEP5Transfer {
Timestamp : tr . Timestamp ,
Asset : tr . Asset ,
Index : tr . Block ,
TxHash : tr . Tx ,
2020-08-03 07:43:53 +00:00
NotifyIndex : tr . Index ,
2020-03-05 12:16:03 +00:00
}
2021-02-16 17:16:02 +00:00
if tr . Amount . Sign ( ) > 0 { // token was received
transfer . Amount = tr . Amount . String ( )
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 )
2020-09-12 21:12:45 +00:00
} else {
2021-02-16 17:16:02 +00:00
tr . Amount . Neg ( tr . Amount )
transfer . Amount = tr . Amount . String ( )
2020-09-12 21:12:45 +00:00
if ! tr . To . Equals ( util . Uint160 { } ) {
transfer . Address = address . Uint160ToString ( tr . To )
}
bs . Sent = append ( bs . Sent , transfer )
2020-03-05 12:16:03 +00:00
}
2020-09-12 21:12:45 +00:00
resCount ++
// Using limits, reached limit.
if limit != 0 && resCount >= limit {
return false , nil
2020-03-05 12:16:03 +00:00
}
2020-09-08 12:29:07 +00:00
return true , nil
2020-03-05 12:16:03 +00:00
} )
if err != nil {
return nil , response . NewInternalServerError ( "invalid NEP5 transfer log" , err )
}
return bs , nil
}
2020-10-30 12:36:34 +00:00
func appendUTXOToTransferTx ( transfer * result . TransferTx , tx * transaction . Transaction , chain core . Blockchainer ) * response . Error {
inouts , err := chain . References ( tx )
if err != nil {
return response . NewInternalServerError ( "invalid tx" , err )
}
for _ , inout := range inouts {
var event result . TransferTxEvent
event . Address = address . Uint160ToString ( inout . Out . ScriptHash )
event . Type = "input"
event . Value = inout . Out . Amount . String ( )
event . Asset = inout . Out . AssetID . StringLE ( )
transfer . Elements = append ( transfer . Elements , event )
}
for _ , out := range tx . Outputs {
var event result . TransferTxEvent
event . Address = address . Uint160ToString ( out . ScriptHash )
event . Type = "output"
event . Value = out . Amount . String ( )
event . Asset = out . AssetID . StringLE ( )
transfer . Elements = append ( transfer . Elements , event )
}
return nil
}
// uint160ToString converts given hash to address, unless it's zero and an empty
// string is returned then.
func uint160ToString ( u util . Uint160 ) string {
if u . Equals ( util . Uint160 { } ) {
return ""
}
return address . Uint160ToString ( u )
}
func appendNEP5ToTransferTx ( transfer * result . TransferTx , nepTr * state . NEP5Transfer ) {
var event result . TransferTxEvent
event . Asset = nepTr . Asset . StringLE ( )
2021-02-16 17:16:02 +00:00
if nepTr . Amount . Sign ( ) > 0 { // token was received
event . Value = nepTr . Amount . String ( )
2020-10-30 12:36:34 +00:00
event . Type = "receive"
event . Address = uint160ToString ( nepTr . From )
} else {
2021-02-16 17:16:02 +00:00
nepTr . Amount . Neg ( nepTr . Amount )
event . Value = nepTr . Amount . String ( )
nepTr . Amount . Neg ( nepTr . Amount )
2020-10-30 12:36:34 +00:00
event . Type = "send"
event . Address = uint160ToString ( nepTr . To )
}
transfer . Events = append ( transfer . Events , event )
}
2020-09-14 18:52:05 +00:00
func ( s * Server ) getAllTransferTx ( ps request . Params ) ( interface { } , * response . Error ) {
var respErr * response . Error
u , err := ps . Value ( 0 ) . GetUint160FromAddressOrHex ( )
if err != nil {
return nil , response . ErrInvalidParams
}
start , end , limit , page , err := getTimestampsAndLimit ( ps , 1 )
if err != nil {
return nil , response . NewInvalidParamsError ( "" , err )
}
var (
utxoCont = make ( chan bool )
nep5Cont = make ( chan bool )
utxoTrs = make ( chan state . Transfer )
nep5Trs = make ( chan state . NEP5Transfer )
)
go func ( ) {
tr := new ( state . Transfer )
_ = s . chain . ForEachTransfer ( u , tr , func ( ) ( bool , error ) {
var cont bool
// Iterating from newest to oldest, not yet reached required
// time frame, continue looping.
if tr . Timestamp > end {
return true , nil
}
// Iterating from newest to oldest, moved past required
// time frame, stop looping.
if tr . Timestamp < start {
return false , nil
}
utxoTrs <- * tr
cont = <- utxoCont
return cont , nil
} )
close ( utxoTrs )
} ( )
go func ( ) {
tr := new ( state . NEP5Transfer )
_ = s . chain . ForEachNEP5Transfer ( u , tr , func ( ) ( bool , error ) {
var cont bool
// Iterating from newest to oldest, not yet reached required
// time frame, continue looping.
if tr . Timestamp > end {
return true , nil
}
// Iterating from newest to oldest, moved past required
// time frame, stop looping.
if tr . Timestamp < start {
return false , nil
}
nep5Trs <- * tr
cont = <- nep5Cont
return cont , nil
} )
close ( nep5Trs )
} ( )
var (
res = make ( [ ] result . TransferTx , 0 , limit )
frameCount int
utxoLast state . Transfer
nep5Last state . NEP5Transfer
haveUtxo , haveNep5 bool
)
utxoLast , haveUtxo = <- utxoTrs
if haveUtxo {
utxoCont <- true
}
nep5Last , haveNep5 = <- nep5Trs
if haveNep5 {
nep5Cont <- true
}
for len ( res ) < limit {
if ! haveUtxo && ! haveNep5 {
break
}
var isNep5 = haveNep5 && ( ! haveUtxo || ( nep5Last . Timestamp > utxoLast . Timestamp ) )
var transfer result . TransferTx
if isNep5 {
transfer . TxID = nep5Last . Tx
transfer . Timestamp = nep5Last . Timestamp
transfer . Index = nep5Last . Block
} else {
transfer . TxID = utxoLast . Tx
transfer . Timestamp = utxoLast . Timestamp
transfer . Index = utxoLast . Block
}
frameCount ++
// Using limits, not yet reached required page. But still need
// to drain inputs for this tx.
skipTx := page * limit >= frameCount
if ! skipTx {
tx , _ , err := s . chain . GetTransaction ( transfer . TxID )
if err != nil {
respErr = response . NewInternalServerError ( "invalid NEP5 transfer log" , err )
break
}
2020-09-15 16:44:24 +00:00
transfer . NetworkFee = s . chain . NetworkFee ( tx ) . String ( )
transfer . SystemFee = s . chain . SystemFee ( tx ) . String ( )
2020-10-30 12:36:34 +00:00
respErr = appendUTXOToTransferTx ( & transfer , tx , s . chain )
if respErr != nil {
2020-09-14 18:52:05 +00:00
break
}
}
// Pick all NEP5 events for this transaction, if there are any.
for haveNep5 && nep5Last . Tx . Equals ( transfer . TxID ) {
if ! skipTx {
2020-10-30 12:36:34 +00:00
appendNEP5ToTransferTx ( & transfer , & nep5Last )
2020-09-14 18:52:05 +00:00
}
nep5Last , haveNep5 = <- nep5Trs
if haveNep5 {
nep5Cont <- true
}
}
// Skip UTXO events, we've already got them from inputs and outputs.
for haveUtxo && utxoLast . Tx . Equals ( transfer . TxID ) {
utxoLast , haveUtxo = <- utxoTrs
if haveUtxo {
utxoCont <- true
}
}
if ! skipTx {
res = append ( res , transfer )
}
}
if haveUtxo {
_ , ok := <- utxoTrs
if ok {
utxoCont <- false
}
}
if haveNep5 {
_ , ok := <- nep5Trs
if ok {
nep5Cont <- false
}
}
if respErr != nil {
return nil , respErr
}
return res , nil
}
2020-09-11 12:41:08 +00:00
func ( s * Server ) getMinimumNetworkFee ( ps request . Params ) ( interface { } , * response . Error ) {
return s . chain . GetConfig ( ) . MinimumNetworkFee , 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 ) {
2020-11-13 13:54:38 +00:00
txHash , err := reqParams . Value ( 0 ) . GetUint256 ( )
if err != nil {
return nil , response . ErrInvalidParams
}
tx , height , err := s . chain . GetTransaction ( txHash )
if err != nil {
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
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-11-13 13:54:38 +00:00
}
if reqParams . Value ( 1 ) . GetBoolean ( ) {
if height == math . MaxUint32 {
return result . NewTransactionOutputRaw ( tx , nil , s . chain ) , nil
}
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-11-13 13:54:38 +00:00
return nil , response . NewInvalidParamsError ( err . Error ( ) , err )
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-11-13 13:54:38 +00:00
return result . NewTransactionOutputRaw ( tx , header , s . chain ) , nil
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-11-13 13:54:38 +00:00
return hex . EncodeToString ( tx . Bytes ( ) ) , nil
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-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 )
2020-11-13 13:54:38 +00:00
if err != nil || height == math . MaxUint32 {
2020-03-05 14:20:50 +00:00
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
}
2020-12-31 10:54:42 +00:00
ucs := s . chain . GetUnspentCoinState ( h )
if ucs == nil {
return nil , response . NewInvalidParamsError ( "invalid tx hash" , errors . New ( "unknown" ) )
2020-02-06 12:02:03 +00:00
}
2020-12-31 10:54:42 +00:00
if num >= len ( ucs . States ) {
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
}
2020-12-31 10:54:42 +00:00
if ucs . States [ num ] . State & state . CoinSpent != 0 {
return nil , nil
}
return result . NewTxOutput ( & ucs . States [ num ] . Output ) , 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-10-30 12:36:34 +00:00
func ( s * Server ) getBlockTransferTx ( ps request . Params ) ( interface { } , * response . Error ) {
var (
res = make ( [ ] result . TransferTx , 0 )
respErr * response . Error
)
hash , respErr := s . getBlockHashFromParam ( ps . Value ( 0 ) )
if respErr != nil {
return nil , respErr
}
block , err := s . chain . GetBlock ( hash )
if err != nil {
return nil , response . NewInternalServerError ( fmt . Sprintf ( "Problem locating block with hash: %s" , hash ) , err )
}
for _ , tx := range block . Transactions {
var transfer = result . TransferTx {
TxID : tx . Hash ( ) ,
Timestamp : block . Timestamp ,
Index : block . Index ,
NetworkFee : s . chain . NetworkFee ( tx ) . String ( ) ,
SystemFee : s . chain . SystemFee ( tx ) . String ( ) ,
}
respErr = appendUTXOToTransferTx ( & transfer , tx , s . chain )
if respErr != nil {
break
}
if tx . Type == transaction . InvocationType {
execRes , err := s . chain . GetAppExecResult ( tx . Hash ( ) )
if err != nil {
respErr = response . NewInternalServerError ( fmt . Sprintf ( "no application log for invocation tx %s" , tx . Hash ( ) ) , err )
break
}
if execRes . VMState != "HALT" {
continue
}
var index uint32
for _ , note := range execRes . Events {
nepTr , err := state . NEP5TransferFromNotification ( note , tx . Hash ( ) , block . Index , block . Timestamp , index )
// It's OK for event to be something different from NEP5 transfer.
if err != nil {
continue
}
transfer . Events = append ( transfer . Events , result . TransferTxEvent {
Asset : nepTr . Asset . StringLE ( ) ,
From : uint160ToString ( nepTr . From ) ,
To : uint160ToString ( nepTr . To ) ,
2021-02-16 17:16:02 +00:00
Value : nepTr . Amount . String ( ) ,
2020-10-30 12:36:34 +00:00
} )
index ++
}
}
if len ( transfer . Elements ) != 0 || len ( transfer . Events ) != 0 {
res = append ( res , transfer )
}
}
if respErr != nil {
return nil , respErr
}
return res , nil
}
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 )
2021-02-15 14:58:39 +00:00
return util . Fixed8FromInt64 ( int64 ( s . chain . GetSystemFeeAmount ( headerHash ) ) ) , nil
2020-02-19 09:44:31 +00:00
}
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 ) {
2021-02-15 14:27:23 +00:00
hash , respErr := s . getBlockHashFromParam ( reqParams . Value ( 0 ) )
if respErr != nil {
return nil , respErr
2020-03-04 17:35:37 +00:00
}
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-08-06 16:25:43 +00:00
hashesForVerifying , err := reqParams . ValueWithType ( 2 , request . ArrayT ) . GetArrayUint160FromHex ( )
if err != nil {
return nil , response . ErrInvalidParams
}
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
}
2020-08-06 16:25:43 +00:00
return s . runScriptInVM ( script , hashesForVerifying ) , nil
2019-11-28 16:08:31 +00:00
}
2020-08-06 16:27:15 +00:00
// invokeFunction implements the `invokefunction` 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-08-06 16:25:43 +00:00
var hashesForVerifying [ ] util . Uint160
hashesForVerifyingIndex := len ( reqParams )
if hashesForVerifyingIndex > 3 {
hashesForVerifying , err = reqParams . ValueWithType ( 3 , request . ArrayT ) . GetArrayUint160FromHex ( )
if err != nil {
return nil , response . ErrInvalidParams
}
hashesForVerifyingIndex --
}
script , err := request . CreateFunctionInvocationScript ( scriptHash , reqParams [ 1 : hashesForVerifyingIndex ] )
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
}
2020-08-06 16:25:43 +00:00
return s . runScriptInVM ( script , hashesForVerifying ) , 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
2020-08-06 16:25:43 +00:00
hashesForVerifying , err := reqParams . ValueWithType ( 1 , request . ArrayT ) . GetArrayUint160FromHex ( )
if err != nil {
return nil , response . ErrInvalidParams
}
return s . runScriptInVM ( script , hashesForVerifying ) , nil
2019-11-28 16:12:23 +00:00
}
// runScriptInVM runs given script in a new test VM and returns the invocation
// result.
2020-08-06 16:25:43 +00:00
func ( s * Server ) runScriptInVM ( script [ ] byte , scriptHashesForVerifying [ ] util . Uint160 ) * result . Invoke {
var tx * transaction . Transaction
if count := len ( scriptHashesForVerifying ) ; count != 0 {
tx := new ( transaction . Transaction )
tx . Attributes = make ( [ ] transaction . Attribute , count )
for i , a := range tx . Attributes {
a . Data = scriptHashesForVerifying [ i ] . BytesBE ( )
a . Usage = transaction . Script
}
}
vm := s . chain . GetTestVM ( tx )
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.
2020-10-26 17:22:20 +00:00
func ( s * Server ) logRequestError ( r * request . Request , jsonErr * response . Error ) {
2020-01-14 12:02:38 +00:00
logFields := [ ] zap . Field {
zap . Error ( jsonErr . Cause ) ,
}
2020-10-26 17:22:20 +00:00
if r . In != nil {
logFields = append ( logFields , zap . String ( "method" , r . In . Method ) )
params , err := r . In . Params ( )
if err == nil {
logFields = append ( logFields , zap . Any ( "params" , params ) )
}
2020-01-14 12:02:38 +00:00
}
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 )
2020-10-26 17:22:20 +00:00
s . writeHTTPServerResponse ( & request . Request { In : r } , w , resp )
2020-01-14 12:02:38 +00:00
}
2020-10-26 17:22:20 +00:00
func ( s * Server ) writeHTTPServerResponse ( r * request . Request , w http . ResponseWriter , resp response . AbstractResult ) {
2020-04-28 19:56:19 +00:00
// Errors can happen in many places and we can only catch ALL of them here.
2020-10-26 17:22:20 +00:00
resp . RunForErrors ( func ( jsonErr * response . Error ) {
s . logRequestError ( r , jsonErr )
} )
if r . In != nil {
resp := resp . ( response . Raw )
if resp . Error != nil {
w . WriteHeader ( resp . Error . HTTPCode )
}
2020-04-28 19:56:19 +00:00
}
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 {
2020-10-26 17:22:20 +00:00
switch {
case r . In != nil :
s . log . Error ( "Error encountered while encoding response" ,
zap . String ( "err" , err . Error ( ) ) ,
zap . String ( "method" , r . In . Method ) )
case r . Batch != nil :
s . log . Error ( "Error encountered while encoding batch response" ,
zap . String ( "err" , err . Error ( ) ) )
}
2020-01-14 12:02:38 +00:00
}
}
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
}