@ -50,7 +50,7 @@ type Service struct {
Port string
Port string
GetNodeIDs func ( ) [ ] libp2p_peer . ID
GetNodeIDs func ( ) [ ] libp2p_peer . ID
ShardID uint32
ShardID uint32
s torage * Storage
S torage * Storage
server * http . Server
server * http . Server
messageChan chan * msg_pb . Message
messageChan chan * msg_pb . Message
GetAccountBalance func ( common . Address ) ( * big . Int , error )
GetAccountBalance func ( common . Address ) ( * big . Int , error )
@ -67,6 +67,16 @@ func New(selfPeer *p2p.Peer, shardID uint32, GetNodeIDs func() []libp2p_peer.ID,
}
}
}
}
// ServiceAPI is rpc api.
type ServiceAPI struct {
Service * Service
}
// NewServiceAPI returns explorer service api.
func NewServiceAPI ( explorerService * Service ) * ServiceAPI {
return & ServiceAPI { Service : explorerService }
}
// StartService starts explorer service.
// StartService starts explorer service.
func ( s * Service ) StartService ( ) {
func ( s * Service ) StartService ( ) {
utils . Logger ( ) . Info ( ) . Msg ( "Starting explorer service." )
utils . Logger ( ) . Info ( ) . Msg ( "Starting explorer service." )
@ -95,7 +105,7 @@ func GetExplorerPort(nodePort string) string {
// Init is to initialize for ExplorerService.
// Init is to initialize for ExplorerService.
func ( s * Service ) Init ( remove bool ) {
func ( s * Service ) Init ( remove bool ) {
s . s torage = GetStorageInstance ( s . IP , s . Port , remove )
s . S torage = GetStorageInstance ( s . IP , s . Port , remove )
}
}
// Run is to run serving explorer.
// Run is to run serving explorer.
@ -150,7 +160,7 @@ func (s *Service) ReadBlocksFromDB(from, to int) []*types.Block {
continue
continue
}
}
key := GetBlockKey ( i )
key := GetBlockKey ( i )
data , err := storage . db . Get ( [ ] byte ( key ) )
data , err := s . S torage. db . Get ( [ ] byte ( key ) )
if err != nil {
if err != nil {
blocks = append ( blocks , nil )
blocks = append ( blocks , nil )
continue
continue
@ -172,6 +182,12 @@ func (s *Service) GetExplorerBlocks(w http.ResponseWriter, r *http.Request) {
to := r . FormValue ( "to" )
to := r . FormValue ( "to" )
pageParam := r . FormValue ( "page" )
pageParam := r . FormValue ( "page" )
offsetParam := r . FormValue ( "offset" )
offsetParam := r . FormValue ( "offset" )
withSignersParam := r . FormValue ( "with_signers" )
withSigners := false
if withSignersParam == "true" {
withSigners = true
}
data := & Data {
data := & Data {
Blocks : [ ] * Block { } ,
Blocks : [ ] * Block { } ,
}
}
@ -186,7 +202,7 @@ func (s *Service) GetExplorerBlocks(w http.ResponseWriter, r *http.Request) {
w . WriteHeader ( http . StatusBadRequest )
w . WriteHeader ( http . StatusBadRequest )
return
return
}
}
db := s . s torage. GetDB ( )
db := s . S torage. GetDB ( )
fromInt , err := strconv . Atoi ( from )
fromInt , err := strconv . Atoi ( from )
if err != nil {
if err != nil {
utils . Logger ( ) . Warn ( ) . Err ( err ) . Msg ( "invalid from parameter" )
utils . Logger ( ) . Warn ( ) . Err ( err ) . Msg ( "invalid from parameter" )
@ -236,19 +252,21 @@ func (s *Service) GetExplorerBlocks(w http.ResponseWriter, r *http.Request) {
accountBlocks := s . ReadBlocksFromDB ( fromInt , toInt )
accountBlocks := s . ReadBlocksFromDB ( fromInt , toInt )
curEpoch := int64 ( - 1 )
curEpoch := int64 ( - 1 )
committee := & shard . Committee { }
committee := & shard . Committee { }
if withSigners {
if bytes , err := db . Get ( [ ] byte ( GetCommitteeKey ( uint32 ( s . ShardID ) , 0 ) ) ) ; err == nil {
if err = rlp . DecodeBytes ( bytes , committee ) ; err != nil {
utils . Logger ( ) . Warn ( ) . Err ( err ) . Msg ( "cannot read committee for new epoch" )
}
}
}
for id , accountBlock := range accountBlocks {
for id , accountBlock := range accountBlocks {
if id == 0 || id == len ( accountBlocks ) - 1 || accountBlock == nil {
if id == 0 || id == len ( accountBlocks ) - 1 || accountBlock == nil {
continue
continue
}
}
block := NewBlock ( accountBlock , id + fromInt - 1 )
block := NewBlock ( accountBlock , id + fromInt - 1 )
if int64 ( block . Epoch ) > curEpoch {
if withSigners && int64 ( block . Epoch ) > curEpoch {
if bytes , err := db . Get ( [ ] byte ( GetCommitteeKey ( uint32 ( s . ShardID ) , block . Epoch ) ) ) ; err == nil {
if accountBlocks [ id - 1 ] != nil {
committee = & shard . Committee { }
state , err := accountBlocks [ id - 1 ] . Header ( ) . GetShardState ( )
if err = rlp . DecodeBytes ( bytes , committee ) ; err != nil {
utils . Logger ( ) . Warn ( ) . Err ( err ) . Msg ( "cannot read committee for new epoch" )
}
} else {
state , err := accountBlock . Header ( ) . GetShardState ( )
if err == nil {
if err == nil {
for _ , shardCommittee := range state {
for _ , shardCommittee := range state {
if shardCommittee . ShardID == accountBlock . ShardID ( ) {
if shardCommittee . ShardID == accountBlock . ShardID ( ) {
@ -256,28 +274,32 @@ func (s *Service) GetExplorerBlocks(w http.ResponseWriter, r *http.Request) {
break
break
}
}
}
}
} else {
utils . Logger ( ) . Warn ( ) . Err ( err ) . Msg ( "error parsing shard state" )
}
}
}
}
curEpoch = int64 ( block . Epoch )
curEpoch = int64 ( block . Epoch )
}
}
pubkeys := make ( [ ] * bls . PublicKey , len ( committee . NodeList ) )
if withSigners {
for i , validator := range committee . NodeList {
pubkeys := make ( [ ] * bls . PublicKey , len ( committee . NodeList ) )
pubkeys [ i ] = new ( bls . PublicKey )
for i , validator := range committee . NodeList {
validator . BlsPublicKey . ToLibBLSPublicKey ( pubkeys [ i ] )
pubkeys [ i ] = new ( bls . PublicKey )
}
validator . BlsPublicKey . ToLibBLSPublicKey ( pubkeys [ i ] )
mask , err := bls2 . NewMask ( pubkeys , nil )
}
if err == nil && accountBlocks [ id + 1 ] != nil {
mask , err := bls2 . NewMask ( pubkeys , nil )
err = mask . SetMask ( accountBlocks [ id + 1 ] . Header ( ) . LastCommitBitmap ( ) )
if err == nil && accountBlocks [ id + 1 ] != nil {
if err == nil {
err = mask . SetMask ( accountBlocks [ id + 1 ] . Header ( ) . LastCommitBitmap ( ) )
for _ , validator := range committee . NodeList {
if err == nil {
oneAddress , err := common2 . AddressToBech32 ( validator . EcdsaAddress )
for _ , validator := range committee . NodeList {
if err != nil {
oneAddress , err := common2 . AddressToBech32 ( validator . EcdsaAddress )
continue
if err != nil {
}
continue
blsPublicKey := new ( bls . PublicKey )
}
validator . BlsPublicKey . ToLibBLSPublicKey ( blsPublicKey )
blsPublicKey := new ( bls . PublicKey )
if ok , err := mask . KeyEnabled ( blsPublicKey ) ; err == nil && ok {
validator . BlsPublicKey . ToLibBLSPublicKey ( blsPublicKey )
block . Signers = append ( block . Signers , oneAddress )
if ok , err := mask . KeyEnabled ( blsPublicKey ) ; err == nil && ok {
block . Signers = append ( block . Signers , oneAddress )
}
}
}
}
}
}
}
@ -315,12 +337,122 @@ func (s *Service) GetExplorerBlocks(w http.ResponseWriter, r *http.Request) {
}
}
data . Blocks = append ( data . Blocks , block )
data . Blocks = append ( data . Blocks , block )
}
}
if offset * page + offset > len ( data . Blocks ) {
data . Blocks = data . Blocks [ offset * page : ]
} else {
data . Blocks = data . Blocks [ offset * page : offset * page + offset ]
}
}
paginatedBlocks := make ( [ ] * Block , 0 )
// GetExplorerBlocks rpc end-point.
for i := 0 ; i < offset && i + offset * page < len ( data . Blocks ) ; i ++ {
func ( s * ServiceAPI ) GetExplorerBlocks ( ctx context . Context , from , to , page , offset int , withSigners bool ) ( [ ] * Block , error ) {
paginatedBlocks = append ( paginatedBlocks , data . Blocks [ i + offset * page ] )
if offset == 0 {
offset = paginationOffset
}
db := s . Service . Storage . GetDB ( )
if to == 0 {
bytes , err := db . Get ( [ ] byte ( BlockHeightKey ) )
if err == nil {
to , err = strconv . Atoi ( string ( bytes ) )
if err != nil {
utils . Logger ( ) . Info ( ) . Msg ( "failed to fetch block height" )
return nil , err
}
}
}
}
data . Blocks = paginatedBlocks
blocks := make ( [ ] * Block , 0 )
accountBlocks := s . Service . ReadBlocksFromDB ( from , to )
curEpoch := int64 ( - 1 )
committee := & shard . Committee { }
if withSigners {
if bytes , err := db . Get ( [ ] byte ( GetCommitteeKey ( uint32 ( s . Service . ShardID ) , 0 ) ) ) ; err == nil {
if err = rlp . DecodeBytes ( bytes , committee ) ; err != nil {
utils . Logger ( ) . Warn ( ) . Err ( err ) . Msg ( "cannot read committee for new epoch" )
}
}
}
for id , accountBlock := range accountBlocks {
if id == 0 || id == len ( accountBlocks ) - 1 || accountBlock == nil {
continue
}
block := NewBlock ( accountBlock , id + from - 1 )
if withSigners && int64 ( block . Epoch ) > curEpoch {
if accountBlocks [ id - 1 ] != nil {
state , err := accountBlocks [ id - 1 ] . Header ( ) . GetShardState ( )
if err == nil {
for _ , shardCommittee := range state {
if shardCommittee . ShardID == accountBlock . ShardID ( ) {
committee = & shardCommittee
break
}
}
}
}
curEpoch = int64 ( block . Epoch )
}
if withSigners {
pubkeys := make ( [ ] * bls . PublicKey , len ( committee . NodeList ) )
for i , validator := range committee . NodeList {
pubkeys [ i ] = new ( bls . PublicKey )
validator . BlsPublicKey . ToLibBLSPublicKey ( pubkeys [ i ] )
}
mask , err := bls2 . NewMask ( pubkeys , nil )
if err == nil && accountBlocks [ id + 1 ] != nil {
err = mask . SetMask ( accountBlocks [ id + 1 ] . Header ( ) . LastCommitBitmap ( ) )
if err == nil {
for _ , validator := range committee . NodeList {
oneAddress , err := common2 . AddressToBech32 ( validator . EcdsaAddress )
if err != nil {
continue
}
blsPublicKey := new ( bls . PublicKey )
validator . BlsPublicKey . ToLibBLSPublicKey ( blsPublicKey )
if ok , err := mask . KeyEnabled ( blsPublicKey ) ; err == nil && ok {
block . Signers = append ( block . Signers , oneAddress )
}
}
}
}
}
// Populate transactions
for _ , tx := range accountBlock . Transactions ( ) {
transaction := GetTransaction ( tx , accountBlock )
if transaction != nil {
block . TXs = append ( block . TXs , transaction )
}
}
if accountBlocks [ id - 1 ] == nil {
block . BlockTime = int64 ( 0 )
block . PrevBlock = RefBlock {
ID : "" ,
Height : "" ,
}
} else {
block . BlockTime = accountBlock . Time ( ) . Int64 ( ) - accountBlocks [ id - 1 ] . Time ( ) . Int64 ( )
block . PrevBlock = RefBlock {
ID : accountBlocks [ id - 1 ] . Hash ( ) . Hex ( ) ,
Height : strconv . Itoa ( id + from - 2 ) ,
}
}
if accountBlocks [ id + 1 ] == nil {
block . NextBlock = RefBlock {
ID : "" ,
Height : "" ,
}
} else {
block . NextBlock = RefBlock {
ID : accountBlocks [ id + 1 ] . Hash ( ) . Hex ( ) ,
Height : strconv . Itoa ( id + from ) ,
}
}
blocks = append ( blocks , block )
}
if offset * page + offset > len ( blocks ) {
blocks = blocks [ offset * page : ]
} else {
blocks = blocks [ offset * page : offset * page + offset ]
}
return blocks , nil
}
}
// GetExplorerTransaction servers /tx end-point.
// GetExplorerTransaction servers /tx end-point.
@ -339,7 +471,7 @@ func (s *Service) GetExplorerTransaction(w http.ResponseWriter, r *http.Request)
w . WriteHeader ( http . StatusBadRequest )
w . WriteHeader ( http . StatusBadRequest )
return
return
}
}
db := s . s torage. GetDB ( )
db := s . S torage. GetDB ( )
bytes , err := db . Get ( [ ] byte ( GetTXKey ( id ) ) )
bytes , err := db . Get ( [ ] byte ( GetTXKey ( id ) ) )
if err != nil {
if err != nil {
utils . Logger ( ) . Warn ( ) . Err ( err ) . Str ( "id" , id ) . Msg ( "cannot read TX" )
utils . Logger ( ) . Warn ( ) . Err ( err ) . Str ( "id" , id ) . Msg ( "cannot read TX" )
@ -355,6 +487,26 @@ func (s *Service) GetExplorerTransaction(w http.ResponseWriter, r *http.Request)
data . TX = * tx
data . TX = * tx
}
}
// GetExplorerTransaction rpc end-point.
func ( s * ServiceAPI ) GetExplorerTransaction ( ctx context . Context , id string ) ( * Transaction , error ) {
if id == "" {
utils . Logger ( ) . Warn ( ) . Msg ( "invalid id parameter" )
return nil , nil
}
db := s . Service . Storage . GetDB ( )
bytes , err := db . Get ( [ ] byte ( GetTXKey ( id ) ) )
if err != nil {
utils . Logger ( ) . Warn ( ) . Err ( err ) . Str ( "id" , id ) . Msg ( "cannot read TX" )
return nil , err
}
tx := new ( Transaction )
if rlp . DecodeBytes ( bytes , tx ) != nil {
utils . Logger ( ) . Warn ( ) . Str ( "id" , id ) . Msg ( "cannot convert data from DB" )
return nil , err
}
return tx , nil
}
// GetExplorerCommittee servers /comittee end-point.
// GetExplorerCommittee servers /comittee end-point.
func ( s * Service ) GetExplorerCommittee ( w http . ResponseWriter , r * http . Request ) {
func ( s * Service ) GetExplorerCommittee ( w http . ResponseWriter , r * http . Request ) {
w . Header ( ) . Set ( "Content-Type" , "application/json" )
w . Header ( ) . Set ( "Content-Type" , "application/json" )
@ -385,7 +537,7 @@ func (s *Service) GetExplorerCommittee(w http.ResponseWriter, r *http.Request) {
return
return
}
}
// fetch current epoch if epoch is 0
// fetch current epoch if epoch is 0
db := s . s torage. GetDB ( )
db := s . S torage. GetDB ( )
if epoch == 0 {
if epoch == 0 {
bytes , err := db . Get ( [ ] byte ( BlockHeightKey ) )
bytes , err := db . Get ( [ ] byte ( BlockHeightKey ) )
blockHeight , err := strconv . Atoi ( string ( bytes ) )
blockHeight , err := strconv . Atoi ( string ( bytes ) )
@ -435,6 +587,56 @@ func (s *Service) GetExplorerCommittee(w http.ResponseWriter, r *http.Request) {
}
}
}
}
// GetExplorerCommittee rpc end-point.
func ( s * ServiceAPI ) GetExplorerCommittee ( ctx context . Context , shardID uint32 , epoch uint64 ) ( * Committee , error ) {
if s . Service . ShardID != uint32 ( shardID ) {
utils . Logger ( ) . Warn ( ) . Msg ( "incorrect shard id" )
return nil , nil
}
// fetch current epoch if epoch is 0
db := s . Service . Storage . GetDB ( )
if epoch == 0 {
bytes , err := db . Get ( [ ] byte ( BlockHeightKey ) )
blockHeight , err := strconv . Atoi ( string ( bytes ) )
if err != nil {
utils . Logger ( ) . Warn ( ) . Err ( err ) . Msg ( "cannot decode block height from DB" )
return nil , err
}
key := GetBlockKey ( blockHeight )
data , err := db . Get ( [ ] byte ( key ) )
block := new ( types . Block )
if rlp . DecodeBytes ( data , block ) != nil {
utils . Logger ( ) . Warn ( ) . Err ( err ) . Msg ( "cannot get block from db" )
return nil , err
}
epoch = block . Epoch ( ) . Uint64 ( )
}
bytes , err := db . Get ( [ ] byte ( GetCommitteeKey ( uint32 ( shardID ) , epoch ) ) )
if err != nil {
utils . Logger ( ) . Warn ( ) . Err ( err ) . Msg ( "cannot read committee" )
return nil , err
}
committee := & shard . Committee { }
if err := rlp . DecodeBytes ( bytes , committee ) ; err != nil {
utils . Logger ( ) . Warn ( ) . Err ( err ) . Msg ( "cannot decode committee data from DB" )
return nil , err
}
validators := & Committee { }
for _ , validator := range committee . NodeList {
validatorBalance := big . NewInt ( 0 )
validatorBalance , err := s . Service . GetAccountBalance ( validator . EcdsaAddress )
if err != nil {
continue
}
oneAddress , err := common2 . AddressToBech32 ( validator . EcdsaAddress )
if err != nil {
continue
}
validators . Validators = append ( validators . Validators , & Validator { Address : oneAddress , Balance : validatorBalance } )
}
return validators , nil
}
// GetExplorerAddress serves /address end-point.
// GetExplorerAddress serves /address end-point.
func ( s * Service ) GetExplorerAddress ( w http . ResponseWriter , r * http . Request ) {
func ( s * Service ) GetExplorerAddress ( w http . ResponseWriter , r * http . Request ) {
w . Header ( ) . Set ( "Content-Type" , "application/json" )
w . Header ( ) . Set ( "Content-Type" , "application/json" )
@ -493,24 +695,20 @@ func (s *Service) GetExplorerAddress(w http.ResponseWriter, r *http.Request) {
balance , err := s . GetAccountBalance ( address )
balance , err := s . GetAccountBalance ( address )
if err == nil {
if err == nil {
balanceAddr = balance
balanceAddr = balance
data . Address . Balance = balance
}
}
}
}
db := s . s torage. GetDB ( )
db := s . S torage. GetDB ( )
bytes , err := db . Get ( [ ] byte ( key ) )
bytes , err := db . Get ( [ ] byte ( key ) )
if err != nil {
utils . Logger ( ) . Warn ( ) . Err ( err ) . Str ( "id" , id ) . Msg ( "cannot read address from db" )
return
}
if err = rlp . DecodeBytes ( bytes , & data . Address ) ; err != nil {
if err = rlp . DecodeBytes ( bytes , & data . Address ) ; err != nil {
utils . Logger ( ) . Warn ( ) . Str ( "id" , id ) . Msg ( "cannot convert data from DB" )
utils . Logger ( ) . Warn ( ) . Str ( "id" , id ) . Msg ( "cannot convert data from DB" )
w . WriteHeader ( http . StatusInternalServerError )
w . WriteHeader ( http . StatusInternalServerError )
return
return
}
}
if balanceAddr . Cmp ( big . NewInt ( 0 ) ) != 0 {
data . Address . Balance = balanceAddr
data . Address . Balance = balanceAddr
}
switch txView {
switch txView {
case txViewNone :
case txViewNone :
data . Address . TXs = nil
data . Address . TXs = nil
@ -531,11 +729,80 @@ func (s *Service) GetExplorerAddress(w http.ResponseWriter, r *http.Request) {
}
}
data . Address . TXs = sentTXs
data . Address . TXs = sentTXs
}
}
paginatedTXs := make ( [ ] * Transaction , 0 )
if offset * page + offset > len ( data . Address . TXs ) {
for i := 0 ; i < offset && i + offset * page < len ( data . Address . TXs ) ; i ++ {
data . Address . TXs = data . Address . TXs [ offset * page : ]
paginatedTXs = append ( paginatedTXs , data . Address . TXs [ i + offset * page ] )
} else {
data . Address . TXs = data . Address . TXs [ offset * page : offset * page + offset ]
}
}
// GetExplorerAddress rpc end-point.
func ( s * ServiceAPI ) GetExplorerAddress ( ctx context . Context , id , txView string , page , offset int ) ( * Address , error ) {
if offset == 0 {
offset = paginationOffset
}
if txView == "" {
txView = txViewNone
}
utils . Logger ( ) . Info ( ) . Str ( "Address" , id ) . Msg ( "Querying address" )
if id == "" {
utils . Logger ( ) . Warn ( ) . Msg ( "missing address id param" )
return nil , nil
}
address := & Address { }
address . ID = id
// Try to populate the banace by directly calling get balance.
// Check the balance from blockchain rather than local DB dump
balanceAddr := big . NewInt ( 0 )
if s . Service . GetAccountBalance != nil {
addr := common2 . ParseAddr ( id )
balance , err := s . Service . GetAccountBalance ( addr )
if err == nil {
balanceAddr = balance
address . Balance = balance
}
}
key := GetAddressKey ( id )
db := s . Service . Storage . GetDB ( )
bytes , err := db . Get ( [ ] byte ( key ) )
if err != nil {
utils . Logger ( ) . Warn ( ) . Err ( err ) . Str ( "id" , id ) . Msg ( "cannot read address from db" )
return address , nil
}
if err = rlp . DecodeBytes ( bytes , & address ) ; err != nil {
utils . Logger ( ) . Warn ( ) . Str ( "id" , id ) . Msg ( "cannot convert data from DB" )
return nil , err
}
address . Balance = balanceAddr
switch txView {
case txViewNone :
address . TXs = nil
case Received :
receivedTXs := make ( [ ] * Transaction , 0 )
for _ , tx := range address . TXs {
if tx . Type == Received {
receivedTXs = append ( receivedTXs , tx )
}
}
address . TXs = receivedTXs
case Sent :
sentTXs := make ( [ ] * Transaction , 0 )
for _ , tx := range address . TXs {
if tx . Type == Sent {
sentTXs = append ( sentTXs , tx )
}
}
address . TXs = sentTXs
}
if offset * page + offset > len ( address . TXs ) {
address . TXs = address . TXs [ offset * page : ]
} else {
address . TXs = address . TXs [ offset * page : offset * page + offset ]
}
}
data . Address . TXs = paginatedTXs
return address , nil
}
}
// GetExplorerNodeCount serves /nodes end-point.
// GetExplorerNodeCount serves /nodes end-point.
@ -547,7 +814,12 @@ func (s *Service) GetExplorerNodeCount(w http.ResponseWriter, r *http.Request) {
}
}
}
}
// GetExplorerShard serves /shard end-point
// GetExplorerNodeCount rpc end-point.
func ( s * ServiceAPI ) GetExplorerNodeCount ( ctx context . Context ) int {
return len ( s . Service . GetNodeIDs ( ) )
}
// GetExplorerShard serves /shard end-point.
func ( s * Service ) GetExplorerShard ( w http . ResponseWriter , r * http . Request ) {
func ( s * Service ) GetExplorerShard ( w http . ResponseWriter , r * http . Request ) {
w . Header ( ) . Set ( "Content-Type" , "application/json" )
w . Header ( ) . Set ( "Content-Type" , "application/json" )
var nodes [ ] Node
var nodes [ ] Node
@ -562,7 +834,18 @@ func (s *Service) GetExplorerShard(w http.ResponseWriter, r *http.Request) {
}
}
}
}
// NotifyService notify service
// GetExplorerShard rpc end-point.
func ( s * ServiceAPI ) GetExplorerShard ( ctx context . Context ) * Shard {
var nodes [ ] Node
for _ , nodeID := range s . Service . GetNodeIDs ( ) {
nodes = append ( nodes , Node {
ID : libp2p_peer . IDB58Encode ( nodeID ) ,
} )
}
return & Shard { Nodes : nodes }
}
// NotifyService notify service.
func ( s * Service ) NotifyService ( params map [ string ] interface { } ) {
func ( s * Service ) NotifyService ( params map [ string ] interface { } ) {
return
return
}
}
@ -574,5 +857,12 @@ func (s *Service) SetMessageChan(messageChan chan *msg_pb.Message) {
// APIs for the services.
// APIs for the services.
func ( s * Service ) APIs ( ) [ ] rpc . API {
func ( s * Service ) APIs ( ) [ ] rpc . API {
return nil
return [ ] rpc . API {
{
Namespace : "explorer" ,
Version : "1.0" ,
Service : NewServiceAPI ( s ) ,
Public : true ,
} ,
}
}
}