swapd version & shutdown (#379)

This commit is contained in:
Matt
2023-04-15 00:35:00 +02:00
committed by GitHub
parent a17f95d414
commit 8bd9281a4e
7 changed files with 188 additions and 14 deletions

View File

@@ -308,6 +308,22 @@ var (
swapdPortFlag,
},
},
{
Name: "version",
Usage: "Get the client and server versions",
Action: runGetVersions,
Flags: []cli.Flag{
swapdPortFlag,
},
},
{
Name: "shutdown",
Usage: "Shutdown swapd",
Action: runShutdown,
Flags: []cli.Flag{
swapdPortFlag,
},
},
},
}
@@ -877,6 +893,32 @@ func printOffer(o *types.Offer, index int, indent string) error {
return nil
}
func runGetVersions(ctx *cli.Context) error {
fmt.Printf("swapcli: %s\n", cliutil.GetVersion())
c := newRRPClient(ctx)
resp, err := c.Version()
if err != nil {
return err
}
fmt.Printf("swapd: %s\n", resp.SwapdVersion)
fmt.Printf("p2p version: %s\n", resp.P2PVersion)
fmt.Printf("env: %s\n", resp.Env)
fmt.Printf("swap creator address: %s\n", resp.SwapCreatorAddr)
return nil
}
func runShutdown(ctx *cli.Context) error {
c := newRRPClient(ctx)
err := c.Shutdown()
if err != nil {
return err
}
return nil
}
func providesStrToVal(providesStr string) (coins.ProvidesCoin, error) {
var provides coins.ProvidesCoin

View File

@@ -45,8 +45,7 @@ type SwapdConfig struct {
// RunSwapDaemon assembles and runs a swapd instance blocking until swapd is
// shut down. Typically, shutdown happens because a signal handler cancels the
// passed in context, but we may add a shutdown method to the RPC instance in
// the future.
// passed in context, or when the shutdown RPC method is called.
func RunSwapDaemon(ctx context.Context, conf *SwapdConfig) (err error) {
// Note: err can be modified in defer blocks, so it needs to be a named return
// value above.
@@ -158,9 +157,16 @@ func RunSwapDaemon(ctx context.Context, conf *SwapdConfig) (err error) {
})
log.Infof("starting swapd with data-dir %s", conf.EnvConf.DataDir)
err = rpcServer.Start() // blocks until server is shutdown or context is cancelled
if err != nil && !errors.Is(err, http.ErrServerClosed) {
return err
err = rpcServer.Start()
if errors.Is(err, http.ErrServerClosed) {
// Set err to nil to exit the program with exit code 0
// and to avoid a FATAL ERROR entry in log files
//
// NOTE: The ErrServerClosed error happens only when the server is told
// to shutdown or close
err = nil
}
// err can get set in defer blocks, so return err or use an empty

View File

@@ -9,6 +9,7 @@ import (
"fmt"
"math/big"
"sync"
"syscall"
"testing"
"time"
@@ -28,6 +29,7 @@ import (
"github.com/athanorlabs/atomic-swap/ethereum/block"
"github.com/athanorlabs/atomic-swap/ethereum/extethclient"
"github.com/athanorlabs/atomic-swap/monero"
"github.com/athanorlabs/atomic-swap/net"
"github.com/athanorlabs/atomic-swap/relayer"
"github.com/athanorlabs/atomic-swap/rpcclient"
"github.com/athanorlabs/atomic-swap/rpcclient/wsclient"
@@ -579,3 +581,34 @@ func TestRunSwapDaemon_CharlieIsBroke_AliceRelays(t *testing.T) {
require.NoError(t, err)
require.Equal(t, bobExpectedBal.Text('f'), coins.FmtWeiAsETH(bobBalance))
}
// Tests the version and shutdown RPC methods
func TestRunSwapDaemon_RPC_Version(t *testing.T) {
conf := createTestConf(t, tests.GetMakerTestKey(t))
protocolVersion := fmt.Sprintf("%s/%d", net.ProtocolID, conf.EthereumClient.ChainID())
timeout := time.Minute
ctx := launchDaemons(t, timeout, conf)
c := rpcclient.NewClient(ctx, fmt.Sprintf("http://127.0.0.1:%d", conf.RPCPort))
versionResp, err := c.Version()
require.NoError(t, err)
require.Equal(t, conf.EnvConf.Env, versionResp.Env)
require.NotEmpty(t, versionResp.SwapdVersion)
require.Equal(t, conf.EnvConf.SwapCreatorAddr, versionResp.SwapCreatorAddr)
require.Equal(t, protocolVersion, versionResp.P2PVersion)
}
// Tests the shutdown RPC method
func TestRunSwapDaemon_RPC_Shutdown(t *testing.T) {
conf := createTestConf(t, tests.GetMakerTestKey(t))
timeout := time.Minute
ctx := launchDaemons(t, timeout, conf)
c := rpcclient.NewClient(ctx, fmt.Sprintf("http://127.0.0.1:%d", conf.RPCPort))
err := c.Shutdown()
require.NoError(t, err)
err = c.Shutdown()
require.ErrorIs(t, err, syscall.ECONNREFUSED)
}

46
rpc/daemon.go Normal file
View File

@@ -0,0 +1,46 @@
package rpc
import (
"fmt"
"net/http"
ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/athanorlabs/atomic-swap/cliutil"
"github.com/athanorlabs/atomic-swap/common"
"github.com/athanorlabs/atomic-swap/net"
)
// DaemonService handles RPC requests for swapd version, administration and (in the future) status requests.
type DaemonService struct {
stopServer func()
pb ProtocolBackend
}
// NewDaemonService ...
func NewDaemonService(stopServer func(), pb ProtocolBackend) *DaemonService {
return &DaemonService{stopServer, pb}
}
// Shutdown swapd
func (s *DaemonService) Shutdown(_ *http.Request, _ *any, _ *any) error {
s.stopServer()
return nil
}
// VersionResponse ...
type VersionResponse struct {
SwapdVersion string `json:"swapdVersion" validate:"required"`
P2PVersion string `json:"p2pVersion" validate:"required"`
Env common.Environment `json:"env" validate:"required"`
SwapCreatorAddr ethcommon.Address `json:"swapCreatorAddress" validate:"required"`
}
// Version returns version & misc info about swapd and its dependencies
func (s *DaemonService) Version(_ *http.Request, _ *any, resp *VersionResponse) error {
resp.SwapdVersion = cliutil.GetVersion()
resp.P2PVersion = fmt.Sprintf("%s/%d", net.ProtocolID, s.pb.ETHClient().ChainID())
resp.Env = s.pb.Env()
resp.SwapCreatorAddr = s.pb.SwapCreatorAddr()
return nil
}

View File

@@ -222,3 +222,7 @@ func (*mockProtocolBackend) ClearXMRDepositAddress(types.Hash) {
func (*mockProtocolBackend) ETHClient() extethclient.EthClient {
panic("not implemented")
}
func (*mockProtocolBackend) SwapCreatorAddr() ethcommon.Address {
panic("not implemented")
}

View File

@@ -16,6 +16,7 @@ import (
"github.com/MarinX/monerorpc/wallet"
"github.com/cockroachdb/apd/v3"
ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"github.com/gorilla/rpc/v2"
@@ -60,13 +61,16 @@ func NewServer(cfg *Config) (*Server, error) {
return nil, err
}
err := rpcServer.RegisterService(NewPersonalService(cfg.Ctx, cfg.XMRMaker, cfg.ProtocolBackend), "personal")
serverCtx, serverCancel := context.WithCancel(cfg.Ctx)
err := rpcServer.RegisterService(NewPersonalService(serverCtx, cfg.XMRMaker, cfg.ProtocolBackend), "personal")
if err != nil {
serverCancel()
return nil, err
}
swapService := NewSwapService(
cfg.Ctx,
serverCtx,
cfg.ProtocolBackend.SwapManager(),
cfg.XMRTaker,
cfg.XMRMaker,
@@ -74,14 +78,16 @@ func NewServer(cfg *Config) (*Server, error) {
cfg.ProtocolBackend,
)
if err = rpcServer.RegisterService(swapService, "swap"); err != nil {
serverCancel()
return nil, err
}
wsServer := newWsServer(cfg.Ctx, cfg.ProtocolBackend.SwapManager(), ns, cfg.ProtocolBackend, cfg.XMRTaker)
wsServer := newWsServer(serverCtx, cfg.ProtocolBackend.SwapManager(), ns, cfg.ProtocolBackend, cfg.XMRTaker)
lc := net.ListenConfig{}
ln, err := lc.Listen(cfg.Ctx, "tcp", cfg.Address)
ln, err := lc.Listen(serverCtx, "tcp", cfg.Address)
if err != nil {
serverCancel()
return nil, err
}
@@ -97,15 +103,22 @@ func NewServer(cfg *Config) (*Server, error) {
ReadHeaderTimeout: time.Second,
Handler: handlers.CORS(headersOk, methodsOk, originsOk)(r),
BaseContext: func(listener net.Listener) context.Context {
return cfg.Ctx
return serverCtx
},
}
return &Server{
ctx: cfg.Ctx,
s := &Server{
ctx: serverCtx,
listener: ln,
httpServer: server,
}, nil
}
if err = rpcServer.RegisterService(NewDaemonService(serverCancel, cfg.ProtocolBackend), "daemon"); err != nil {
serverCancel()
return nil, err
}
return s, nil
}
// HttpURL returns the URL used for HTTP requests
@@ -138,7 +151,8 @@ func (s *Server) Start() error {
case <-s.ctx.Done():
// Shutdown below is passed a closed context, which means it will shut down
// immediately without servicing already connected clients.
if err := s.httpServer.Shutdown(s.ctx); err != nil {
err := s.httpServer.Shutdown(s.ctx)
if err != nil && !errors.Is(err, context.Canceled) {
log.Warnf("http server shutdown errored: %s", err)
}
// We shut down because the context was cancelled, so that's the error to return
@@ -172,6 +186,7 @@ type ProtocolBackend interface {
SetSwapTimeout(timeout time.Duration)
SwapTimeout() time.Duration
SwapManager() swap.Manager
SwapCreatorAddr() ethcommon.Address
SetXMRDepositAddress(*mcrypto.Address, types.Hash)
ClearXMRDepositAddress(types.Hash)
ETHClient() extethclient.EthClient

28
rpcclient/daemon.go Normal file
View File

@@ -0,0 +1,28 @@
package rpcclient
import (
"github.com/athanorlabs/atomic-swap/rpc"
)
// Shutdown swapd
func (c *Client) Shutdown() error {
const (
method = "daemon_shutdown"
)
if err := c.Post(method, nil, nil); err != nil {
return err
}
return nil
}
// Version returns version & misc info about swapd and its dependencies
func (c *Client) Version() (*rpc.VersionResponse, error) {
const (
method = "daemon_version"
)
resp := &rpc.VersionResponse{}
if err := c.Post(method, nil, resp); err != nil {
return nil, err
}
return resp, nil
}