mirror of
https://github.com/AthanorLabs/atomic-swap.git
synced 2026-01-09 14:18:03 -05:00
swapd version & shutdown (#379)
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
46
rpc/daemon.go
Normal 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
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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
28
rpcclient/daemon.go
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user