gecko/api/ipcs/server.go

142 lines
3.7 KiB
Go

// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package ipcs
import (
"fmt"
"net/http"
"nanomsg.org/go/mangos/v2/protocol/pub"
_ "nanomsg.org/go/mangos/v2/transport/ipc" // registers the IPC transport
"github.com/gorilla/rpc/v2"
"github.com/ava-labs/gecko/api"
"github.com/ava-labs/gecko/chains"
"github.com/ava-labs/gecko/snow/engine/common"
"github.com/ava-labs/gecko/snow/triggers"
"github.com/ava-labs/gecko/utils/json"
"github.com/ava-labs/gecko/utils/logging"
"github.com/ava-labs/gecko/utils/wrappers"
)
const baseURL = "ipc:///tmp/"
// IPCs maintains the IPCs
type IPCs struct {
log logging.Logger
chainManager chains.Manager
httpServer *api.Server
events *triggers.EventDispatcher
chains map[[32]byte]*ChainIPC
}
// NewService returns a new IPCs API service
func NewService(log logging.Logger, chainManager chains.Manager, events *triggers.EventDispatcher, httpServer *api.Server) *common.HTTPHandler {
newServer := rpc.NewServer()
codec := json.NewCodec()
newServer.RegisterCodec(codec, "application/json")
newServer.RegisterCodec(codec, "application/json;charset=UTF-8")
newServer.RegisterService(&IPCs{
log: log,
chainManager: chainManager,
httpServer: httpServer,
events: events,
chains: map[[32]byte]*ChainIPC{},
}, "ipcs")
return &common.HTTPHandler{Handler: newServer}
}
// PublishBlockchainArgs are the arguments for calling PublishBlockchain
type PublishBlockchainArgs struct {
BlockchainID string `json:"blockchainID"`
}
// PublishBlockchainReply are the results from calling PublishBlockchain
type PublishBlockchainReply struct {
URL string `json:"url"`
}
// PublishBlockchain publishes the finalized accepted transactions from the blockchainID over the IPC
func (ipc *IPCs) PublishBlockchain(r *http.Request, args *PublishBlockchainArgs, reply *PublishBlockchainReply) error {
chainID, err := ipc.chainManager.Lookup(args.BlockchainID)
if err != nil {
ipc.log.Error("unknown blockchainID: %s", err)
return err
}
chainIDKey := chainID.Key()
chainIDStr := chainID.String()
url := baseURL + chainIDStr + ".ipc"
reply.URL = url
if _, ok := ipc.chains[chainIDKey]; ok {
ipc.log.Info("returning existing blockchainID %s", chainIDStr)
return nil
}
sock, err := pub.NewSocket()
if err != nil {
ipc.log.Error("can't get new pub socket: %s", err)
return err
}
if err = sock.Listen(url); err != nil {
ipc.log.Error("can't listen on pub socket: %s", err)
sock.Close()
return err
}
chainIPC := &ChainIPC{
log: ipc.log,
socket: sock,
}
if err := ipc.events.RegisterChain(chainID, "ipc", chainIPC); err != nil {
ipc.log.Error("couldn't register event: %s", err)
sock.Close()
return err
}
ipc.chains[chainIDKey] = chainIPC
return nil
}
// UnpublishBlockchainArgs are the arguments for calling UnpublishBlockchain
type UnpublishBlockchainArgs struct {
BlockchainID string `json:"blockchainID"`
}
// UnpublishBlockchainReply are the results from calling UnpublishBlockchain
type UnpublishBlockchainReply struct {
Success bool `json:"success"`
}
// UnpublishBlockchain closes publishing of a blockchainID
func (ipc *IPCs) UnpublishBlockchain(r *http.Request, args *UnpublishBlockchainArgs, reply *UnpublishBlockchainReply) error {
chainID, err := ipc.chainManager.Lookup(args.BlockchainID)
if err != nil {
ipc.log.Error("unknown blockchainID %s: %s", args.BlockchainID, err)
return err
}
chainIDKey := chainID.Key()
chain, ok := ipc.chains[chainIDKey]
if !ok {
return fmt.Errorf("blockchainID not publishing: %s", chainID)
}
errs := wrappers.Errs{}
errs.Add(
chain.Stop(),
ipc.events.DeregisterChain(chainID, "ipc"),
)
delete(ipc.chains, chainIDKey)
reply.Success = true
return errs.Err
}