mirror of
https://github.com/AthanorLabs/atomic-swap.git
synced 2026-01-10 06:38:04 -05:00
CLI cleanups (#185)
* Switches to current v2 version of urfave CLI * Fixes incorrect port values in the help * Provides defaults directly to urfave for clear `--help` messaging * `--base-path` flag renamed to `--data-dir`. This matches `monerod`'s' `--data-dir` and almost matches the `geth` flag `--datadir` * Bootnodes and contract address are provided for stagenet testing * Bootnodes can be provided individually by repeating the `--bootnodes`/`--bn` flag, or as a comma separated block (original behavior), or even a combination of both. (Makes adding/removing bootnodes a lot easier.) * More precise error messaging when XMR maker/taker values are off * Fixes bug in the peer finder where XMR was hardcoded as the provides coin * `cleanup-test-processes.sh` displays which processes are being killed
This commit is contained in:
2
Makefile
2
Makefile
@@ -45,7 +45,7 @@ test-integration: init
|
||||
|
||||
.PHONY: install
|
||||
install: init
|
||||
cd cmd/ && go install
|
||||
cd cmd/ && go install ./...
|
||||
|
||||
.PHONY: build
|
||||
build: init
|
||||
|
||||
@@ -5,10 +5,9 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
errNoMultiaddr = errors.New("must provide peer's multiaddress with --multiaddr")
|
||||
errNoMinAmount = errors.New("must provide non-zero --min-amount")
|
||||
errNoMaxAmount = errors.New("must provide non-zero --max-amount")
|
||||
errNoExchangeRate = errors.New("must provide non-zero --exchange-rate")
|
||||
errNoOfferID = errors.New("must provide --offer-id")
|
||||
errNoProvidesAmount = errors.New("must provide --provides-amount")
|
||||
errNoProvidesAmount = errors.New("must provide non-zero --provides-amount")
|
||||
errNoDuration = errors.New("must provide non-zero --duration")
|
||||
)
|
||||
|
||||
@@ -6,30 +6,28 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/athanorlabs/atomic-swap/common/types"
|
||||
"github.com/athanorlabs/atomic-swap/rpcclient"
|
||||
"github.com/athanorlabs/atomic-swap/rpcclient/wsclient"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
||||
logging "github.com/ipfs/go-log"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultSwapdAddress = "http://localhost:5001"
|
||||
defaultSwapdAddress = "http://127.0.0.1:5001"
|
||||
defaultDiscoverSearchTimeSecs = 12
|
||||
)
|
||||
|
||||
var log = logging.Logger("cmd")
|
||||
|
||||
var (
|
||||
app = &cli.App{
|
||||
Name: "swapcli",
|
||||
Usage: "Client for swapd",
|
||||
Commands: []cli.Command{
|
||||
Commands: []*cli.Command{
|
||||
{
|
||||
Name: "addresses",
|
||||
Aliases: []string{"a"},
|
||||
Usage: "list our daemon's libp2p listening addresses",
|
||||
Usage: "List our daemon's libp2p listening addresses",
|
||||
Action: runAddresses,
|
||||
Flags: []cli.Flag{
|
||||
daemonAddrFlag,
|
||||
@@ -38,16 +36,19 @@ var (
|
||||
{
|
||||
Name: "discover",
|
||||
Aliases: []string{"d"},
|
||||
Usage: "discover peers who provide a certain coin",
|
||||
Usage: "Discover peers who provide a certain coin",
|
||||
Action: runDiscover,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "provides",
|
||||
Usage: "coin to find providers for: one of [ETH, XMR]",
|
||||
Name: "provides",
|
||||
Usage: fmt.Sprintf("Coin to find providers for: one of [%s, %s]",
|
||||
types.ProvidesXMR, types.ProvidesETH),
|
||||
Value: string(types.ProvidesXMR),
|
||||
},
|
||||
&cli.UintFlag{
|
||||
Name: "search-time",
|
||||
Usage: "duration of time to search for, in seconds",
|
||||
Usage: "Duration of time to search for, in seconds",
|
||||
Value: defaultDiscoverSearchTimeSecs,
|
||||
},
|
||||
daemonAddrFlag,
|
||||
},
|
||||
@@ -55,12 +56,13 @@ var (
|
||||
{
|
||||
Name: "query",
|
||||
Aliases: []string{"q"},
|
||||
Usage: "query a peer for details on what they provide",
|
||||
Usage: "Query a peer for details on what they provide",
|
||||
Action: runQuery,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "multiaddr",
|
||||
Usage: "peer's multiaddress, as provided by discover",
|
||||
Name: "multiaddr",
|
||||
Usage: "Peer's multiaddress, as provided by discover",
|
||||
Required: true,
|
||||
},
|
||||
daemonAddrFlag,
|
||||
},
|
||||
@@ -72,12 +74,15 @@ var (
|
||||
Action: runQueryAll,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "provides",
|
||||
Usage: "coin to find providers for: one of [ETH, XMR]",
|
||||
Name: "provides",
|
||||
Usage: fmt.Sprintf("Coin to find providers for: one of [%s, %s]",
|
||||
types.ProvidesXMR, types.ProvidesETH),
|
||||
Value: string(types.ProvidesXMR),
|
||||
},
|
||||
&cli.UintFlag{
|
||||
Name: "search-time",
|
||||
Usage: "duration of time to search for, in seconds",
|
||||
Usage: "Duration of time to search for, in seconds",
|
||||
Value: defaultDiscoverSearchTimeSecs,
|
||||
},
|
||||
daemonAddrFlag,
|
||||
},
|
||||
@@ -85,24 +90,27 @@ var (
|
||||
{
|
||||
Name: "make",
|
||||
Aliases: []string{"m"},
|
||||
Usage: "mke a swap offer; currently monero holders must be the makers",
|
||||
Usage: "Make a swap offer; currently monero holders must be the makers",
|
||||
Action: runMake,
|
||||
Flags: []cli.Flag{
|
||||
&cli.Float64Flag{
|
||||
Name: "min-amount",
|
||||
Usage: "minimum amount to be swapped, in XMR",
|
||||
Name: "min-amount",
|
||||
Usage: "Minimum amount to be swapped, in XMR",
|
||||
Required: true,
|
||||
},
|
||||
&cli.Float64Flag{
|
||||
Name: "max-amount",
|
||||
Usage: "maximum amount to be swapped, in XMR",
|
||||
Name: "max-amount",
|
||||
Usage: "Maximum amount to be swapped, in XMR",
|
||||
Required: true,
|
||||
},
|
||||
&cli.Float64Flag{
|
||||
Name: "exchange-rate",
|
||||
Usage: "desired exchange rate of XMR:ETH, eg. --exchange-rate=0.1 means 10XMR = 1ETH",
|
||||
Name: "exchange-rate",
|
||||
Usage: "Desired exchange rate of XMR:ETH, eg. --exchange-rate=0.1 means 10XMR = 1ETH",
|
||||
Required: true,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "subscribe",
|
||||
Usage: "subscribe to push notifications about the swap's status",
|
||||
Usage: "Subscribe to push notifications about the swap's status",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "eth-asset",
|
||||
@@ -114,73 +122,79 @@ var (
|
||||
{
|
||||
Name: "take",
|
||||
Aliases: []string{"t"},
|
||||
Usage: "initiate a swap by taking an offer; currently only eth holders can be the takers",
|
||||
Usage: "Initiate a swap by taking an offer; currently only eth holders can be the takers",
|
||||
Action: runTake,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "multiaddr",
|
||||
Usage: "peer's multiaddress, as provided by discover",
|
||||
Name: "multiaddr",
|
||||
Usage: "Peer's multiaddress, as provided by discover",
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "offer-id",
|
||||
Usage: "ID of the offer being taken",
|
||||
Name: "offer-id",
|
||||
Usage: "ID of the offer being taken",
|
||||
Required: true,
|
||||
},
|
||||
&cli.Float64Flag{
|
||||
Name: "provides-amount",
|
||||
Usage: "amount of coin to send in the swap",
|
||||
Name: "provides-amount",
|
||||
Usage: "Amount of coin to send in the swap",
|
||||
Required: true,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "subscribe",
|
||||
Usage: "subscribe to push notifications about the swap's status",
|
||||
Usage: "Subscribe to push notifications about the swap's status",
|
||||
},
|
||||
daemonAddrFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "get-past-swap-ids",
|
||||
Usage: "get past swap IDs",
|
||||
Usage: "Get past swap IDs",
|
||||
Action: runGetPastSwapIDs,
|
||||
Flags: []cli.Flag{daemonAddrFlag},
|
||||
},
|
||||
{
|
||||
Name: "get-ongoing-swap",
|
||||
Usage: "get information about ongoing swap, if there is one",
|
||||
Usage: "Get information about ongoing swap, if there is one",
|
||||
Action: runGetOngoingSwap,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "offer-id",
|
||||
Usage: "ID of swap to retrieve info for",
|
||||
Name: "offer-id",
|
||||
Usage: "ID of swap to retrieve info for",
|
||||
Required: true,
|
||||
},
|
||||
daemonAddrFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "get-past-swap",
|
||||
Usage: "get information about a past swap with the given ID",
|
||||
Usage: "Get information about a past swap with the given ID",
|
||||
Action: runGetPastSwap,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "offer-id",
|
||||
Usage: "ID of swap to retrieve info for",
|
||||
Name: "offer-id",
|
||||
Usage: "ID of swap to retrieve info for",
|
||||
Required: true,
|
||||
},
|
||||
daemonAddrFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "refund",
|
||||
Usage: "if we are the ETH provider for an ongoing swap, refund it if possible.",
|
||||
Usage: "If we are the ETH provider for an ongoing swap, refund it if possible.",
|
||||
Action: runRefund,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "offer-id",
|
||||
Usage: "ID of swap to retrieve info for",
|
||||
Name: "offer-id",
|
||||
Usage: "ID of swap to retrieve info for",
|
||||
Required: true,
|
||||
},
|
||||
daemonAddrFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "cancel",
|
||||
Usage: "cancel a ongoing swap if possible.",
|
||||
Usage: "Cancel a ongoing swap if possible.",
|
||||
Action: runCancel,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
@@ -192,36 +206,38 @@ var (
|
||||
},
|
||||
{
|
||||
Name: "clear-offers",
|
||||
Usage: "clear current offers. if no offer IDs are provided, clears all current offers.",
|
||||
Usage: "Clear current offers. If no offer IDs are provided, clears all current offers.",
|
||||
Action: runClearOffers,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "offer-ids",
|
||||
Usage: "a comma-separated list of offer IDs to delete",
|
||||
Usage: "A comma-separated list of offer IDs to delete",
|
||||
},
|
||||
daemonAddrFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "get-stage",
|
||||
Usage: "get the stage of a current swap.",
|
||||
Usage: "Get the stage of a current swap.",
|
||||
Action: runGetStage,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "offer-id",
|
||||
Usage: "ID of swap to retrieve info for",
|
||||
Name: "offer-id",
|
||||
Usage: "ID of swap to retrieve info for",
|
||||
Required: true,
|
||||
},
|
||||
daemonAddrFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "set-swap-timeout",
|
||||
Usage: "set the duration between swap initiation and t0 and t0 and t1, in seconds",
|
||||
Usage: "Set the duration between swap initiation and t0 and t0 and t1, in seconds",
|
||||
Action: runSetSwapTimeout,
|
||||
Flags: []cli.Flag{
|
||||
&cli.UintFlag{
|
||||
Name: "duration",
|
||||
Usage: "duration of timeout, in seconds",
|
||||
Name: "duration",
|
||||
Usage: "Duration of timeout, in seconds",
|
||||
Required: true,
|
||||
},
|
||||
daemonAddrFlag,
|
||||
},
|
||||
@@ -232,13 +248,14 @@ var (
|
||||
|
||||
daemonAddrFlag = &cli.StringFlag{
|
||||
Name: "daemon-addr",
|
||||
Usage: "address of swap daemon; default http://localhost:5001",
|
||||
Usage: "Address of swap daemon",
|
||||
Value: defaultSwapdAddress,
|
||||
}
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Error(err)
|
||||
_, _ = fmt.Fprintf(os.Stderr, "%s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -265,15 +282,7 @@ func runDiscover(ctx *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if provides == "" {
|
||||
provides = types.ProvidesXMR
|
||||
}
|
||||
|
||||
endpoint := ctx.String("daemon-addr")
|
||||
if endpoint == "" {
|
||||
endpoint = defaultSwapdAddress
|
||||
}
|
||||
|
||||
searchTime := ctx.Uint("search-time")
|
||||
|
||||
c := rpcclient.NewClient(endpoint)
|
||||
@@ -291,14 +300,7 @@ func runDiscover(ctx *cli.Context) error {
|
||||
|
||||
func runQuery(ctx *cli.Context) error {
|
||||
maddr := ctx.String("multiaddr")
|
||||
if maddr == "" {
|
||||
return errNoMultiaddr
|
||||
}
|
||||
|
||||
endpoint := ctx.String("daemon-addr")
|
||||
if endpoint == "" {
|
||||
endpoint = defaultSwapdAddress
|
||||
}
|
||||
|
||||
c := rpcclient.NewClient(endpoint)
|
||||
res, err := c.Query(maddr)
|
||||
@@ -318,10 +320,6 @@ func runQueryAll(ctx *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if provides == "" {
|
||||
provides = types.ProvidesXMR
|
||||
}
|
||||
|
||||
endpoint := ctx.String("daemon-addr")
|
||||
if endpoint == "" {
|
||||
endpoint = defaultSwapdAddress
|
||||
@@ -364,9 +362,6 @@ func runMake(ctx *cli.Context) error {
|
||||
}
|
||||
|
||||
endpoint := ctx.String("daemon-addr")
|
||||
if endpoint == "" {
|
||||
endpoint = defaultSwapdAddress
|
||||
}
|
||||
|
||||
ethAssetStr := ctx.String("eth-asset")
|
||||
ethAsset := types.EthAssetETH
|
||||
@@ -404,29 +399,23 @@ func runMake(ctx *cli.Context) error {
|
||||
}
|
||||
|
||||
fmt.Printf("Published offer with ID %s\n", id)
|
||||
addrs, err := c.Addresses()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("On addresses: %v\n", addrs)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runTake(ctx *cli.Context) error {
|
||||
maddr := ctx.String("multiaddr")
|
||||
if maddr == "" {
|
||||
return errNoMultiaddr
|
||||
}
|
||||
|
||||
offerID := ctx.String("offer-id")
|
||||
if offerID == "" {
|
||||
return errNoOfferID
|
||||
}
|
||||
|
||||
providesAmount := ctx.Float64("provides-amount")
|
||||
if providesAmount == 0 {
|
||||
return errNoProvidesAmount
|
||||
}
|
||||
|
||||
endpoint := ctx.String("daemon-addr")
|
||||
if endpoint == "" {
|
||||
endpoint = defaultSwapdAddress
|
||||
}
|
||||
|
||||
if ctx.Bool("subscribe") {
|
||||
c, err := wsclient.NewWsClient(context.Background(), endpoint)
|
||||
@@ -479,14 +468,7 @@ func runGetPastSwapIDs(ctx *cli.Context) error {
|
||||
|
||||
func runGetOngoingSwap(ctx *cli.Context) error {
|
||||
endpoint := ctx.String("daemon-addr")
|
||||
if endpoint == "" {
|
||||
endpoint = defaultSwapdAddress
|
||||
}
|
||||
|
||||
offerID := ctx.String("offer-id")
|
||||
if offerID == "" {
|
||||
return errNoOfferID
|
||||
}
|
||||
|
||||
c := rpcclient.NewClient(endpoint)
|
||||
info, err := c.GetOngoingSwap(offerID)
|
||||
@@ -506,14 +488,7 @@ func runGetOngoingSwap(ctx *cli.Context) error {
|
||||
|
||||
func runGetPastSwap(ctx *cli.Context) error {
|
||||
endpoint := ctx.String("daemon-addr")
|
||||
if endpoint == "" {
|
||||
endpoint = defaultSwapdAddress
|
||||
}
|
||||
|
||||
offerID := ctx.String("offer-id")
|
||||
if offerID == "" {
|
||||
return errNoOfferID
|
||||
}
|
||||
|
||||
c := rpcclient.NewClient(endpoint)
|
||||
info, err := c.GetPastSwap(offerID)
|
||||
@@ -533,14 +508,7 @@ func runGetPastSwap(ctx *cli.Context) error {
|
||||
|
||||
func runRefund(ctx *cli.Context) error {
|
||||
endpoint := ctx.String("daemon-addr")
|
||||
if endpoint == "" {
|
||||
endpoint = defaultSwapdAddress
|
||||
}
|
||||
|
||||
offerID := ctx.String("offer-id")
|
||||
if offerID == "" {
|
||||
return errNoOfferID
|
||||
}
|
||||
|
||||
c := rpcclient.NewClient(endpoint)
|
||||
resp, err := c.Refund(offerID)
|
||||
@@ -554,14 +522,7 @@ func runRefund(ctx *cli.Context) error {
|
||||
|
||||
func runCancel(ctx *cli.Context) error {
|
||||
endpoint := ctx.String("daemon-addr")
|
||||
if endpoint == "" {
|
||||
endpoint = defaultSwapdAddress
|
||||
}
|
||||
|
||||
offerID := ctx.String("offer-id")
|
||||
if offerID == "" {
|
||||
return errNoOfferID
|
||||
}
|
||||
|
||||
c := rpcclient.NewClient(endpoint)
|
||||
resp, err := c.Cancel(offerID)
|
||||
@@ -575,10 +536,6 @@ func runCancel(ctx *cli.Context) error {
|
||||
|
||||
func runClearOffers(ctx *cli.Context) error {
|
||||
endpoint := ctx.String("daemon-addr")
|
||||
if endpoint == "" {
|
||||
endpoint = defaultSwapdAddress
|
||||
}
|
||||
|
||||
c := rpcclient.NewClient(endpoint)
|
||||
|
||||
ids := ctx.String("offer-ids")
|
||||
@@ -603,14 +560,7 @@ func runClearOffers(ctx *cli.Context) error {
|
||||
|
||||
func runGetStage(ctx *cli.Context) error {
|
||||
endpoint := ctx.String("daemon-addr")
|
||||
if endpoint == "" {
|
||||
endpoint = defaultSwapdAddress
|
||||
}
|
||||
|
||||
offerID := ctx.String("offer-id")
|
||||
if offerID == "" {
|
||||
return errNoOfferID
|
||||
}
|
||||
|
||||
c := rpcclient.NewClient(endpoint)
|
||||
resp, err := c.GetStage(offerID)
|
||||
@@ -624,11 +574,10 @@ func runGetStage(ctx *cli.Context) error {
|
||||
|
||||
func runSetSwapTimeout(ctx *cli.Context) error {
|
||||
duration := ctx.Uint("duration")
|
||||
|
||||
endpoint := ctx.String("daemon-addr")
|
||||
if endpoint == "" {
|
||||
endpoint = defaultSwapdAddress
|
||||
if duration == 0 {
|
||||
return errNoDuration
|
||||
}
|
||||
endpoint := ctx.String("daemon-addr")
|
||||
|
||||
c := rpcclient.NewClient(endpoint)
|
||||
err := c.SetSwapTimeout(uint64(duration))
|
||||
@@ -636,6 +585,6 @@ func runSetSwapTimeout(ctx *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Set timeout duration to %ds", duration)
|
||||
fmt.Printf("Set timeout duration to %ds\n", duration)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -24,9 +24,15 @@ var (
|
||||
errInvalidSwapContract = errors.New("given contract address does not contain correct code")
|
||||
)
|
||||
|
||||
func getOrDeploySwapFactory(ctx context.Context, address ethcommon.Address, env common.Environment,
|
||||
basePath string, chainID *big.Int, privkey *ecdsa.PrivateKey,
|
||||
ec *ethclient.Client) (*swapfactory.SwapFactory, ethcommon.Address, error) {
|
||||
func getOrDeploySwapFactory(
|
||||
ctx context.Context,
|
||||
address ethcommon.Address,
|
||||
env common.Environment,
|
||||
dataDir string,
|
||||
chainID *big.Int,
|
||||
privkey *ecdsa.PrivateKey,
|
||||
ec *ethclient.Client,
|
||||
) (*swapfactory.SwapFactory, ethcommon.Address, error) {
|
||||
var (
|
||||
sf *swapfactory.SwapFactory
|
||||
)
|
||||
@@ -51,7 +57,7 @@ func getOrDeploySwapFactory(ctx context.Context, address ethcommon.Address, env
|
||||
log.Infof("deployed SwapFactory.sol: address=%s tx hash=%s", address, tx.Hash())
|
||||
|
||||
// store the contract address on disk
|
||||
fp := path.Join(basePath, "contractaddress")
|
||||
fp := path.Join(dataDir, "contractaddress")
|
||||
if err = pcommon.WriteContractAddressToFile(fp, address.String()); err != nil {
|
||||
return nil, ethcommon.Address{}, fmt.Errorf("failed to write contract address to file: %w", err)
|
||||
}
|
||||
|
||||
@@ -3,16 +3,15 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
ethcommon "github.com/ethereum/go-ethereum/common"
|
||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/urfave/cli"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/athanorlabs/atomic-swap/cmd/utils"
|
||||
"github.com/athanorlabs/atomic-swap/common"
|
||||
@@ -50,15 +49,18 @@ const (
|
||||
var (
|
||||
log = logging.Logger("cmd")
|
||||
|
||||
// default dev basepaths
|
||||
defaultXMRMakerBasepath = os.TempDir() + "/xmrmaker"
|
||||
defaultXMRTakerBasepath = os.TempDir() + "/xmrtaker"
|
||||
// Default dev base paths. If SWAP_TEST_DATA_DIR is not defined, it is
|
||||
// still safe, there just won't be an intermediate directory and tests
|
||||
// could fail from stale data.
|
||||
testDataDir = os.Getenv("SWAP_TEST_DATA_DIR")
|
||||
defaultXMRMakerDataDir = path.Join(os.TempDir(), testDataDir, "xmrmaker")
|
||||
defaultXMRTakerDataDir = path.Join(os.TempDir(), testDataDir, "xmrtaker")
|
||||
)
|
||||
|
||||
const (
|
||||
flagRPCPort = "rpc-port"
|
||||
flagWSPort = "ws-port"
|
||||
flagBasepath = "basepath"
|
||||
flagDataDir = "data-dir"
|
||||
flagLibp2pKey = "libp2p-key"
|
||||
flagLibp2pPort = "libp2p-port"
|
||||
flagBootnodes = "bootnodes"
|
||||
@@ -92,35 +94,41 @@ var (
|
||||
Flags: []cli.Flag{
|
||||
&cli.UintFlag{
|
||||
Name: flagRPCPort,
|
||||
Usage: "port for the daemon RPC server to run on; default 5001",
|
||||
Usage: "Port for the daemon RPC server to run on",
|
||||
Value: defaultRPCPort,
|
||||
},
|
||||
&cli.UintFlag{
|
||||
Name: flagWSPort,
|
||||
Usage: "port for the daemon RPC websockets server to run on; default 8080",
|
||||
Usage: "Port for the daemon RPC websockets server to run on",
|
||||
Value: defaultWSPort,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagBasepath,
|
||||
Usage: "path to store swap artefacts",
|
||||
Name: flagDataDir,
|
||||
Usage: "Path to store swap artifacts", //nolint:misspell
|
||||
Value: "{HOME}/.atomicswap/{ENV}", // For --help only, actual default replaces variables
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagLibp2pKey,
|
||||
Usage: "libp2p private key",
|
||||
Value: defaultLibp2pKey,
|
||||
},
|
||||
&cli.UintFlag{
|
||||
Name: flagLibp2pPort,
|
||||
Usage: "libp2p port to listen on",
|
||||
Value: defaultLibp2pPort,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagWalletFile,
|
||||
Usage: "filename of wallet file containing XMR to be swapped; required if running as XMR provider",
|
||||
Usage: "Filename of wallet file containing XMR to be swapped; required if running as XMR provider",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagWalletPassword,
|
||||
Usage: "password of wallet file containing XMR to be swapped",
|
||||
Usage: "Password of wallet file containing XMR to be swapped",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagEnv,
|
||||
Usage: "environment to use: one of mainnet, stagenet, or dev",
|
||||
Usage: "Environment to use: one of mainnet, stagenet, or dev",
|
||||
Value: "dev",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagMoneroWalletEndpoint,
|
||||
@@ -132,55 +140,57 @@ var (
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagEthereumEndpoint,
|
||||
Usage: "ethereum client endpoint",
|
||||
Usage: "Ethereum client endpoint",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagEthereumPrivKey,
|
||||
Usage: "file containing a private key hex string",
|
||||
Usage: "File containing a private key as hex string",
|
||||
},
|
||||
&cli.UintFlag{
|
||||
Name: flagEthereumChainID,
|
||||
Usage: "ethereum chain ID; eg. mainnet=1, ropsten=3, rinkeby=4, goerli=5, ganache=1337",
|
||||
Usage: "Ethereum chain ID; eg. mainnet=1, goerli=5, ganache=1337",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagContractAddress,
|
||||
Usage: "address of instance of SwapFactory.sol already deployed on-chain; required if running on mainnet",
|
||||
Usage: "Address of instance of SwapFactory.sol already deployed on-chain; required if running on mainnet",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagBootnodes,
|
||||
Usage: "comma-separated string of libp2p bootnodes",
|
||||
&cli.StringSliceFlag{
|
||||
Name: flagBootnodes,
|
||||
Aliases: []string{"bn"},
|
||||
Usage: "libp2p bootnode, comma separated if passing multiple to a single flag",
|
||||
},
|
||||
&cli.UintFlag{
|
||||
Name: flagGasPrice,
|
||||
Usage: "ethereum gas price to use for transactions (in gwei). if not set, the gas price is set via oracle.",
|
||||
Usage: "Ethereum gas price to use for transactions (in gwei). If not set, the gas price is set via oracle.",
|
||||
},
|
||||
&cli.UintFlag{
|
||||
Name: flagGasLimit,
|
||||
Usage: "ethereum gas limit to use for transactions. if not set, the gas limit is estimated for each transaction.",
|
||||
Usage: "Ethereum gas limit to use for transactions. If not set, the gas limit is estimated for each transaction.",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flagDevXMRTaker,
|
||||
Usage: "run in development mode and use ETH provider default values",
|
||||
Usage: "Run in development mode and use ETH provider default values",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flagDevXMRMaker,
|
||||
Usage: "run in development mode and use XMR provider default values",
|
||||
Usage: "Run in development mode and use XMR provider default values",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flagDeploy,
|
||||
Usage: "deploy an instance of the swap contract; defaults to false",
|
||||
Usage: "Deploy an instance of the swap contract",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flagTransferBack,
|
||||
Usage: "when receiving XMR in a swap, transfer it back to the original wallet.",
|
||||
Usage: "When receiving XMR in a swap, transfer it back to the original wallet.",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagLog,
|
||||
Usage: "set log level: one of [error|warn|info|debug]",
|
||||
Usage: "Set log level: one of [error|warn|info|debug]",
|
||||
Value: "info",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flagUseExternalSigner,
|
||||
Usage: "use external signer, for usage with the swap UI",
|
||||
Usage: "Use external signer, for usage with the swap UI",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -216,14 +226,10 @@ func setLogLevels(c *cli.Context) error {
|
||||
)
|
||||
|
||||
level := c.String(flagLog)
|
||||
if level == "" {
|
||||
level = levelInfo
|
||||
}
|
||||
|
||||
switch level {
|
||||
case levelError, levelWarn, levelInfo, levelDebug:
|
||||
default:
|
||||
return errors.New("invalid log level")
|
||||
return fmt.Errorf("invalid log level %q", level)
|
||||
}
|
||||
|
||||
_ = logging.SetLogLevel("xmrtaker", level)
|
||||
@@ -258,86 +264,94 @@ func runDaemon(c *cli.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *daemon) make(c *cli.Context) error {
|
||||
env, cfg, err := utils.GetEnvironment(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
devXMRTaker := c.Bool(flagDevXMRTaker)
|
||||
devXMRMaker := c.Bool(flagDevXMRMaker)
|
||||
|
||||
chainID := int64(c.Uint(flagEthereumChainID))
|
||||
if chainID == 0 {
|
||||
chainID = cfg.EthereumChainID
|
||||
}
|
||||
|
||||
var bootnodes []string
|
||||
if c.String(flagBootnodes) != "" {
|
||||
bootnodes = strings.Split(c.String(flagBootnodes), ",")
|
||||
}
|
||||
|
||||
k := c.String(flagLibp2pKey)
|
||||
p := uint16(c.Uint(flagLibp2pPort))
|
||||
var (
|
||||
libp2pKey string
|
||||
libp2pPort uint16
|
||||
rpcPort uint16
|
||||
)
|
||||
|
||||
switch {
|
||||
case k != "":
|
||||
libp2pKey = k
|
||||
case devXMRTaker:
|
||||
libp2pKey = defaultXMRTakerLibp2pKey
|
||||
case devXMRMaker:
|
||||
libp2pKey = defaultXMRMakerLibp2pKey
|
||||
default:
|
||||
libp2pKey = defaultLibp2pKey
|
||||
}
|
||||
|
||||
switch {
|
||||
case p != 0:
|
||||
libp2pPort = p
|
||||
case devXMRTaker:
|
||||
libp2pPort = defaultXMRTakerLibp2pPort
|
||||
case devXMRMaker:
|
||||
libp2pPort = defaultXMRMakerLibp2pPort
|
||||
default:
|
||||
libp2pPort = defaultLibp2pPort
|
||||
}
|
||||
|
||||
// basepath is already set in default case
|
||||
basepath := c.String(flagBasepath)
|
||||
switch {
|
||||
case basepath != "":
|
||||
cfg.Basepath = basepath
|
||||
case devXMRTaker:
|
||||
cfg.Basepath = defaultXMRTakerBasepath
|
||||
case devXMRMaker:
|
||||
cfg.Basepath = defaultXMRMakerBasepath
|
||||
}
|
||||
|
||||
exists, err := common.Exists(cfg.Basepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !exists {
|
||||
err = common.MakeDir(cfg.Basepath)
|
||||
if err != nil {
|
||||
return err
|
||||
// expandBootnodes expands the boot nodes passed on the command line that
|
||||
// can be specified individually with multiple flags, but can also contain
|
||||
// multiple boot nodes passed to single flag separated by commas.
|
||||
func expandBootnodes(nodesCLI []string) []string {
|
||||
var nodes []string
|
||||
for _, n := range nodesCLI {
|
||||
splitNodes := strings.Split(n, ",")
|
||||
for _, ns := range splitNodes {
|
||||
nodes = append(nodes, strings.TrimSpace(ns))
|
||||
}
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
|
||||
func (d *daemon) make(c *cli.Context) error {
|
||||
env, cfg, err := utils.GetEnvironment(c.String(flagEnv))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
devXMRMaker := c.Bool(flagDevXMRMaker)
|
||||
devXMRTaker := c.Bool(flagDevXMRTaker)
|
||||
if devXMRMaker && devXMRTaker {
|
||||
return errFlagsMutuallyExclusive(flagDevXMRMaker, flagDevXMRTaker)
|
||||
}
|
||||
|
||||
// By default, the chain ID is derived from the `flagEnv` value, but it can be overridden if
|
||||
// `flagEthereumChainID` is passed:
|
||||
if c.Uint(flagEthereumChainID) != 0 {
|
||||
cfg.EthereumChainID = int64(c.Uint(flagEthereumChainID))
|
||||
}
|
||||
|
||||
if len(c.StringSlice(flagBootnodes)) > 0 {
|
||||
cfg.Bootnodes = expandBootnodes(c.StringSlice(flagBootnodes))
|
||||
}
|
||||
|
||||
//
|
||||
// Note: Overrides for devXMRTaker/devXMRMaker use "IsSet" instead of checking the value so that
|
||||
// the devXMRTaker/devXMRMaker configurations take precedence over normal default values,
|
||||
// but will not override values explicitly set by the end user.
|
||||
//
|
||||
|
||||
libp2pKey := c.String(flagLibp2pKey)
|
||||
if !c.IsSet(flagLibp2pKey) {
|
||||
switch {
|
||||
case devXMRTaker:
|
||||
libp2pKey = defaultXMRTakerLibp2pKey
|
||||
case devXMRMaker:
|
||||
libp2pKey = defaultXMRMakerLibp2pKey
|
||||
}
|
||||
}
|
||||
|
||||
libp2pPort := uint16(c.Uint(flagLibp2pPort))
|
||||
if !c.IsSet(flagLibp2pPort) {
|
||||
switch {
|
||||
case devXMRTaker:
|
||||
libp2pPort = defaultXMRTakerLibp2pPort
|
||||
case devXMRMaker:
|
||||
libp2pPort = defaultXMRMakerLibp2pPort
|
||||
}
|
||||
}
|
||||
|
||||
// cfg.DataDir was already defaulted from the `flagEnv` value and `flagDataDir` does
|
||||
// not directly set a default value.
|
||||
if c.IsSet(flagDataDir) {
|
||||
cfg.DataDir = c.String(flagDataDir) // override the value derived from `flagEnv`
|
||||
} else {
|
||||
// Override in dev scenarios if the value was not explicitly set
|
||||
switch {
|
||||
case devXMRTaker:
|
||||
cfg.DataDir = defaultXMRTakerDataDir
|
||||
case devXMRMaker:
|
||||
cfg.DataDir = defaultXMRMakerDataDir
|
||||
}
|
||||
}
|
||||
|
||||
if err = common.MakeDir(cfg.DataDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
netCfg := &net.Config{
|
||||
Ctx: d.ctx,
|
||||
Environment: env,
|
||||
Basepath: cfg.Basepath,
|
||||
ChainID: chainID,
|
||||
DataDir: cfg.DataDir,
|
||||
EthChainID: cfg.EthereumChainID,
|
||||
Port: libp2pPort,
|
||||
KeyFile: libp2pKey,
|
||||
Bootnodes: bootnodes,
|
||||
Bootnodes: cfg.Bootnodes,
|
||||
}
|
||||
|
||||
host, err := net.NewHost(netCfg)
|
||||
@@ -346,7 +360,7 @@ func (d *daemon) make(c *cli.Context) error {
|
||||
}
|
||||
|
||||
sm := swap.NewManager()
|
||||
backend, err := newBackend(d.ctx, c, env, cfg, chainID, devXMRMaker, sm, host)
|
||||
backend, err := newBackend(d.ctx, c, env, cfg, devXMRMaker, devXMRTaker, sm, host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -364,27 +378,24 @@ func (d *daemon) make(c *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
p = uint16(c.Uint(flagRPCPort))
|
||||
switch {
|
||||
case p != 0:
|
||||
rpcPort = p
|
||||
case devXMRTaker:
|
||||
rpcPort = defaultXMRTakerRPCPort
|
||||
case devXMRMaker:
|
||||
rpcPort = defaultXMRMakerRPCPort
|
||||
default:
|
||||
rpcPort = defaultRPCPort
|
||||
rpcPort := uint16(c.Uint(flagRPCPort))
|
||||
if !c.IsSet(flagRPCPort) {
|
||||
switch {
|
||||
case devXMRTaker:
|
||||
rpcPort = defaultXMRTakerRPCPort
|
||||
case devXMRMaker:
|
||||
rpcPort = defaultXMRMakerRPCPort
|
||||
}
|
||||
}
|
||||
|
||||
wsPort := uint16(c.Uint(flagWSPort))
|
||||
switch {
|
||||
case wsPort != 0:
|
||||
case devXMRTaker:
|
||||
wsPort = defaultXMRTakerWSPort
|
||||
case devXMRMaker:
|
||||
wsPort = defaultXMRMakerWSPort
|
||||
default:
|
||||
wsPort = defaultWSPort
|
||||
if !c.IsSet(flagWSPort) {
|
||||
switch {
|
||||
case devXMRTaker:
|
||||
wsPort = defaultXMRTakerWSPort
|
||||
case devXMRMaker:
|
||||
wsPort = defaultXMRMakerWSPort
|
||||
}
|
||||
}
|
||||
|
||||
rpcCfg := &rpc.Config{
|
||||
@@ -414,24 +425,46 @@ func (d *daemon) make(c *cli.Context) error {
|
||||
}
|
||||
}()
|
||||
|
||||
log.Infof("started swapd with basepath %s",
|
||||
cfg.Basepath,
|
||||
)
|
||||
log.Infof("started swapd with data-dir %s", cfg.DataDir)
|
||||
return nil
|
||||
}
|
||||
|
||||
func newBackend(ctx context.Context, c *cli.Context, env common.Environment, cfg common.Config,
|
||||
chainID int64, devXMRMaker bool, sm swap.Manager, net net.Host) (backend.Backend, error) {
|
||||
func errFlagsMutuallyExclusive(flag1, flag2 string) error {
|
||||
return fmt.Errorf("flags %q and %q are mutually exclusive", flag1, flag2)
|
||||
}
|
||||
|
||||
func errFlagRequired(flag string) error {
|
||||
return fmt.Errorf("required flag %q not specified", flag)
|
||||
}
|
||||
|
||||
func newBackend(
|
||||
ctx context.Context,
|
||||
c *cli.Context,
|
||||
env common.Environment,
|
||||
cfg common.Config,
|
||||
devXMRMaker bool,
|
||||
devXMRTaker bool,
|
||||
sm swap.Manager,
|
||||
net net.Host,
|
||||
) (backend.Backend, error) {
|
||||
var (
|
||||
moneroEndpoint, daemonEndpoint, ethEndpoint string
|
||||
moneroEndpoint string
|
||||
daemonEndpoint string
|
||||
ethEndpoint string
|
||||
ethPrivKey *ecdsa.PrivateKey
|
||||
)
|
||||
|
||||
if c.String(flagMoneroWalletEndpoint) != "" {
|
||||
switch {
|
||||
// flagMoneroWalletEndpoint doesn't have a default, so we don't have to use c.IsSet when
|
||||
// doing the devXMRMaker/devXMRTaker overrides. We'll also be eliminating this flag soon.
|
||||
case c.String(flagMoneroWalletEndpoint) != "":
|
||||
moneroEndpoint = c.String(flagMoneroWalletEndpoint)
|
||||
} else if devXMRMaker {
|
||||
case devXMRMaker:
|
||||
moneroEndpoint = common.DefaultXMRMakerMoneroEndpoint
|
||||
} else {
|
||||
case devXMRTaker:
|
||||
moneroEndpoint = common.DefaultXMRTakerMoneroEndpoint
|
||||
default:
|
||||
return nil, errFlagRequired(flagMoneroWalletEndpoint)
|
||||
}
|
||||
|
||||
if c.String(flagEthereumEndpoint) != "" {
|
||||
@@ -440,15 +473,15 @@ func newBackend(ctx context.Context, c *cli.Context, env common.Environment, cfg
|
||||
ethEndpoint = common.DefaultEthEndpoint
|
||||
}
|
||||
|
||||
ethPrivKey, err := utils.GetEthereumPrivateKey(c, env, devXMRMaker, c.Bool(flagUseExternalSigner))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
useExternalSigner := c.Bool(flagUseExternalSigner)
|
||||
ethPrivKeyFile := c.String(flagEthereumPrivKey)
|
||||
if useExternalSigner && ethPrivKeyFile != "" {
|
||||
return nil, errFlagsMutuallyExclusive(flagUseExternalSigner, flagEthereumPrivKey)
|
||||
}
|
||||
|
||||
var pk *ecdsa.PrivateKey
|
||||
if ethPrivKey != "" {
|
||||
pk, err = ethcrypto.HexToECDSA(ethPrivKey)
|
||||
if err != nil {
|
||||
if !useExternalSigner {
|
||||
var err error
|
||||
if ethPrivKey, err = utils.GetEthereumPrivateKey(ethPrivKeyFile, env, devXMRMaker, devXMRTaker); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -465,12 +498,11 @@ func newBackend(ctx context.Context, c *cli.Context, env common.Environment, cfg
|
||||
gasPrice = big.NewInt(int64(c.Uint(flagGasPrice)))
|
||||
}
|
||||
|
||||
var contractAddr ethcommon.Address
|
||||
contractAddrStr := c.String(flagContractAddress)
|
||||
if contractAddrStr == "" {
|
||||
contractAddr = ethcommon.Address{}
|
||||
} else {
|
||||
contractAddr = ethcommon.HexToAddress(contractAddrStr)
|
||||
if contractAddrStr != "" {
|
||||
// We check the contract code at the address later, so we don't need
|
||||
// to tightly validate the address here.
|
||||
cfg.ContractAddress = ethcommon.HexToAddress(contractAddrStr)
|
||||
}
|
||||
|
||||
ec, err := ethclient.Dial(ethEndpoint)
|
||||
@@ -480,11 +512,16 @@ func newBackend(ctx context.Context, c *cli.Context, env common.Environment, cfg
|
||||
|
||||
deploy := c.Bool(flagDeploy)
|
||||
if deploy {
|
||||
contractAddr = ethcommon.Address{}
|
||||
if c.IsSet(flagContractAddress) {
|
||||
return nil, errFlagsMutuallyExclusive(flagDeploy, flagContractAddress)
|
||||
}
|
||||
// Zero out any default contract address in the config, so we deploy
|
||||
cfg.ContractAddress = ethcommon.Address{}
|
||||
}
|
||||
|
||||
contract, contractAddr, err := getOrDeploySwapFactory(ctx, contractAddr, env, cfg.Basepath,
|
||||
big.NewInt(chainID), pk, ec)
|
||||
chainID := big.NewInt(cfg.EthereumChainID)
|
||||
contract, contractAddr, err :=
|
||||
getOrDeploySwapFactory(ctx, cfg.ContractAddress, env, cfg.DataDir, chainID, ethPrivKey, ec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -494,9 +531,9 @@ func newBackend(ctx context.Context, c *cli.Context, env common.Environment, cfg
|
||||
MoneroWalletEndpoint: moneroEndpoint,
|
||||
MoneroDaemonEndpoint: daemonEndpoint,
|
||||
EthereumClient: ec,
|
||||
EthereumPrivateKey: pk,
|
||||
EthereumPrivateKey: ethPrivKey,
|
||||
Environment: env,
|
||||
ChainID: big.NewInt(chainID),
|
||||
ChainID: chainID,
|
||||
GasPrice: gasPrice,
|
||||
GasLimit: uint64(c.Uint(flagGasLimit)),
|
||||
SwapManager: sm,
|
||||
@@ -527,7 +564,7 @@ func getProtocolInstances(c *cli.Context, cfg common.Config,
|
||||
|
||||
xmrtakerCfg := &xmrtaker.Config{
|
||||
Backend: b,
|
||||
Basepath: cfg.Basepath,
|
||||
DataDir: cfg.DataDir,
|
||||
MoneroWalletFile: walletFile,
|
||||
MoneroWalletPassword: walletPassword,
|
||||
TransferBack: c.Bool(flagTransferBack),
|
||||
@@ -540,7 +577,7 @@ func getProtocolInstances(c *cli.Context, cfg common.Config,
|
||||
|
||||
xmrmakerCfg := &xmrmaker.Config{
|
||||
Backend: b,
|
||||
Basepath: cfg.Basepath,
|
||||
DataDir: cfg.DataDir,
|
||||
WalletFile: walletFile,
|
||||
WalletPassword: walletPassword,
|
||||
}
|
||||
|
||||
@@ -4,69 +4,57 @@ import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/urfave/cli"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func newTestContext(t *testing.T, description string, flags []string, values []interface{}) *cli.Context {
|
||||
require.Equal(t, len(flags), len(values))
|
||||
|
||||
func newTestContext(t *testing.T, description string, flags map[string]any) *cli.Context {
|
||||
set := flag.NewFlagSet(description, 0)
|
||||
for i := range values {
|
||||
switch v := values[i].(type) {
|
||||
for flag, value := range flags {
|
||||
switch v := value.(type) {
|
||||
case bool:
|
||||
set.Bool(flags[i], v, "")
|
||||
set.Bool(flag, v, "")
|
||||
case string:
|
||||
set.String(flags[i], v, "")
|
||||
set.String(flag, v, "")
|
||||
case uint:
|
||||
set.Uint(flags[i], v, "")
|
||||
set.Uint(flag, v, "")
|
||||
case int64:
|
||||
set.Int64(flags[i], v, "")
|
||||
set.Int64(flag, v, "")
|
||||
case []string:
|
||||
set.Var(&cli.StringSlice{}, flags[i], "")
|
||||
set.Var(&cli.StringSlice{}, flag, "")
|
||||
default:
|
||||
t.Fatalf("unexpected cli value type: %T", values[i])
|
||||
t.Fatalf("unexpected cli value type: %T", value)
|
||||
}
|
||||
}
|
||||
|
||||
ctx := cli.NewContext(app, set, nil)
|
||||
var (
|
||||
err error
|
||||
i int
|
||||
)
|
||||
|
||||
for i = range values {
|
||||
switch v := values[i].(type) {
|
||||
case bool:
|
||||
err = ctx.Set(flags[i], strconv.FormatBool(v))
|
||||
case string:
|
||||
err = ctx.Set(flags[i], values[i].(string))
|
||||
case uint:
|
||||
err = ctx.Set(flags[i], strconv.Itoa(int(values[i].(uint))))
|
||||
case int64:
|
||||
err = ctx.Set(flags[i], strconv.Itoa(int(values[i].(int64))))
|
||||
for flag, value := range flags {
|
||||
switch v := value.(type) {
|
||||
case bool, uint, int64, string:
|
||||
require.NoError(t, ctx.Set(flag, fmt.Sprintf("%v", v)))
|
||||
case []string:
|
||||
for _, str := range values[i].([]string) {
|
||||
err = ctx.Set(flags[i], str)
|
||||
require.NoError(t, err)
|
||||
for _, str := range v {
|
||||
require.NoError(t, ctx.Set(flag, str))
|
||||
}
|
||||
default:
|
||||
t.Fatalf("unexpected cli value type: %T", values[i])
|
||||
t.Fatalf("unexpected cli value type: %T", value)
|
||||
}
|
||||
}
|
||||
|
||||
require.NoError(t, err, fmt.Sprintf("failed to set cli flag: %T, err: %s", flags[i], err))
|
||||
return ctx
|
||||
}
|
||||
|
||||
func TestDaemon_DevXMRTaker(t *testing.T) {
|
||||
c := newTestContext(t,
|
||||
"test --dev-xmrtaker",
|
||||
[]string{flagDevXMRTaker, flagBasepath},
|
||||
[]interface{}{true, t.TempDir()},
|
||||
map[string]any{
|
||||
flagEnv: "dev",
|
||||
flagDevXMRTaker: true,
|
||||
flagDataDir: t.TempDir(),
|
||||
},
|
||||
)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
@@ -84,8 +72,12 @@ func TestDaemon_DevXMRTaker(t *testing.T) {
|
||||
func TestDaemon_DevXMRMaker(t *testing.T) {
|
||||
c := newTestContext(t,
|
||||
"test --dev-xmrmaker",
|
||||
[]string{flagDevXMRMaker, flagDeploy, flagBasepath},
|
||||
[]interface{}{true, true, t.TempDir()},
|
||||
map[string]any{
|
||||
flagEnv: "dev",
|
||||
flagDevXMRMaker: true,
|
||||
flagDeploy: true,
|
||||
flagDataDir: t.TempDir(),
|
||||
},
|
||||
)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
@@ -99,3 +91,23 @@ func TestDaemon_DevXMRMaker(t *testing.T) {
|
||||
err := d.make(c)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func Test_expandBootnodes(t *testing.T) {
|
||||
cliNodes := []string{
|
||||
" node1, node2 ,node3,node4 ",
|
||||
"node5",
|
||||
"\tnode6\n",
|
||||
"node7,node8",
|
||||
}
|
||||
expected := []string{
|
||||
"node1",
|
||||
"node2",
|
||||
"node3",
|
||||
"node4",
|
||||
"node5",
|
||||
"node6",
|
||||
"node7",
|
||||
"node8",
|
||||
}
|
||||
require.EqualValues(t, expected, expandBootnodes(cliNodes))
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
ethcommon "github.com/ethereum/go-ethereum/common"
|
||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/urfave/cli"
|
||||
logging "github.com/ipfs/go-log"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/athanorlabs/atomic-swap/cmd/utils"
|
||||
"github.com/athanorlabs/atomic-swap/common"
|
||||
@@ -21,15 +21,13 @@ import (
|
||||
"github.com/athanorlabs/atomic-swap/protocol/xmrtaker"
|
||||
recovery "github.com/athanorlabs/atomic-swap/recover"
|
||||
"github.com/athanorlabs/atomic-swap/swapfactory"
|
||||
|
||||
logging "github.com/ipfs/go-log"
|
||||
)
|
||||
|
||||
const (
|
||||
flagEnv = "env"
|
||||
flagMoneroWalletEndpoint = "monero-endpoint"
|
||||
flagEthereumEndpoint = "ethereum-endpoint"
|
||||
flagEthereumPrivateKey = "ethereum-privkey"
|
||||
flagEthereumPrivKey = "ethereum-privkey"
|
||||
flagEthereumChainID = "ethereum-chain-id"
|
||||
flagGasPrice = "gas-price"
|
||||
flagGasLimit = "gas-limit"
|
||||
@@ -56,7 +54,8 @@ var (
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: flagEnv,
|
||||
Usage: "environment to use: one of mainnet, stagenet, or dev",
|
||||
Usage: "Environment to use: one of mainnet, stagenet, or dev",
|
||||
Value: "dev",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagMoneroWalletEndpoint,
|
||||
@@ -64,35 +63,35 @@ var (
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagEthereumEndpoint,
|
||||
Usage: "ethereum client endpoint",
|
||||
Usage: "Ethereum client endpoint",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagEthereumPrivateKey,
|
||||
Usage: "file containing a private key hex string",
|
||||
Name: flagEthereumPrivKey,
|
||||
Usage: "File containing a private key hex string",
|
||||
},
|
||||
&cli.UintFlag{
|
||||
Name: flagEthereumChainID,
|
||||
Usage: "ethereum chain ID; eg. mainnet=1, ropsten=3, rinkeby=4, goerli=5, ganache=1337",
|
||||
Usage: "Ethereum chain ID; eg. mainnet=1, goerli=5, ganache=1337",
|
||||
},
|
||||
&cli.UintFlag{
|
||||
Name: flagGasPrice,
|
||||
Usage: "ethereum gas price to use for transactions (in gwei). if not set, the gas price is set via oracle.",
|
||||
Usage: "Ethereum gas price to use for transactions (in gwei). If not set, the gas price is set via oracle.",
|
||||
},
|
||||
&cli.UintFlag{
|
||||
Name: flagGasLimit,
|
||||
Usage: "ethereum gas limit to use for transactions. if not set, the gas limit is estimated for each transaction.",
|
||||
Usage: "Ethereum gas limit to use for transactions. if not set, the gas limit is estimated for each transaction.",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagInfoFile,
|
||||
Usage: "path to swap infofile",
|
||||
Usage: "Path to swap infofile",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flagXMRMaker,
|
||||
Usage: "true if recovering as an xmr-maker",
|
||||
Usage: "Use when recovering as an xmr-maker",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flagXMRTaker,
|
||||
Usage: "true if recovering as an xmr-taker",
|
||||
Usage: "Use when recovering as an xmr-taker",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -108,8 +107,8 @@ func main() {
|
||||
// Recoverer is implemented by a backend which is able to recover swap funds
|
||||
type Recoverer interface {
|
||||
WalletFromSharedSecret(secret *mcrypto.PrivateKeyInfo) (mcrypto.Address, error)
|
||||
RecoverFromXMRMakerSecretAndContract(b backend.Backend, basepath string, xmrmakerSecret, contractAddr string, swapID [32]byte, swap swapfactory.SwapFactorySwap) (*xmrmaker.RecoveryResult, error) //nolint:lll
|
||||
RecoverFromXMRTakerSecretAndContract(b backend.Backend, basepath string, xmrtakerSecret string, swapID [32]byte, swap swapfactory.SwapFactorySwap) (*xmrtaker.RecoveryResult, error) //nolint:lll
|
||||
RecoverFromXMRMakerSecretAndContract(b backend.Backend, dataDir string, xmrmakerSecret, contractAddr string, swapID [32]byte, swap swapfactory.SwapFactorySwap) (*xmrmaker.RecoveryResult, error) //nolint:lll
|
||||
RecoverFromXMRTakerSecretAndContract(b backend.Backend, dataDir string, xmrtakerSecret string, swapID [32]byte, swap swapfactory.SwapFactorySwap) (*xmrtaker.RecoveryResult, error) //nolint:lll
|
||||
}
|
||||
|
||||
type instance struct {
|
||||
@@ -126,11 +125,12 @@ func runRecover(c *cli.Context) error {
|
||||
func (inst *instance) recover(c *cli.Context) error {
|
||||
xmrmaker := c.Bool(flagXMRMaker)
|
||||
xmrtaker := c.Bool(flagXMRTaker)
|
||||
if !xmrmaker && !xmrtaker {
|
||||
// Either maker or taker must be specified, but not both, so their values must be opposite
|
||||
if xmrmaker == xmrtaker {
|
||||
return errMustSpecifyXMRMakerOrTaker
|
||||
}
|
||||
|
||||
env, cfg, err := utils.GetEnvironment(c)
|
||||
env, cfg, err := utils.GetEnvironment(c.String(flagEnv))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -173,10 +173,10 @@ func (inst *instance) recover(c *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
basepath := filepath.Dir(filepath.Clean(infofilePath))
|
||||
dataDir := filepath.Dir(filepath.Clean(infofilePath))
|
||||
|
||||
if xmrmaker {
|
||||
res, err := r.RecoverFromXMRMakerSecretAndContract(b, basepath, infofile.PrivateKeyInfo.PrivateSpendKey,
|
||||
res, err := r.RecoverFromXMRMakerSecretAndContract(b, dataDir, infofile.PrivateKeyInfo.PrivateSpendKey,
|
||||
contractAddr, infofile.ContractSwapID, infofile.ContractSwap)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -194,7 +194,7 @@ func (inst *instance) recover(c *cli.Context) error {
|
||||
}
|
||||
|
||||
if xmrtaker {
|
||||
res, err := r.RecoverFromXMRTakerSecretAndContract(b, basepath, infofile.PrivateKeyInfo.PrivateSpendKey,
|
||||
res, err := r.RecoverFromXMRTakerSecretAndContract(b, dataDir, infofile.PrivateKeyInfo.PrivateSpendKey,
|
||||
infofile.ContractSwapID, infofile.ContractSwap)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -245,9 +245,8 @@ func createBackend(ctx context.Context, c *cli.Context, env common.Environment,
|
||||
moneroEndpoint, ethEndpoint string
|
||||
)
|
||||
|
||||
chainID := int64(c.Uint(flagEthereumChainID))
|
||||
if chainID == 0 {
|
||||
chainID = cfg.EthereumChainID
|
||||
if c.IsSet(flagEthereumChainID) {
|
||||
cfg.EthereumChainID = int64(c.Uint(flagEthereumChainID))
|
||||
}
|
||||
|
||||
if c.String(flagMoneroWalletEndpoint) != "" {
|
||||
@@ -263,7 +262,10 @@ func createBackend(ctx context.Context, c *cli.Context, env common.Environment,
|
||||
}
|
||||
|
||||
// TODO: add --external-signer option to allow front-end integration (#124)
|
||||
ethPrivKey, err := utils.GetEthereumPrivateKey(c, env, false, false)
|
||||
ethPrivKeyFile := c.String(flagEthereumPrivKey)
|
||||
devXMRMaker := false // Not directly supported, but you can put the Ganache key in a file
|
||||
devXMRTaker := false
|
||||
ethPrivKey, err := utils.GetEthereumPrivateKey(ethPrivKeyFile, env, devXMRMaker, devXMRTaker)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -274,11 +276,6 @@ func createBackend(ctx context.Context, c *cli.Context, env common.Environment,
|
||||
gasPrice = big.NewInt(int64(c.Uint(flagGasPrice)))
|
||||
}
|
||||
|
||||
pk, err := ethcrypto.HexToECDSA(ethPrivKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ec, err := ethclient.Dial(ethEndpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -293,9 +290,9 @@ func createBackend(ctx context.Context, c *cli.Context, env common.Environment,
|
||||
Ctx: ctx,
|
||||
MoneroWalletEndpoint: moneroEndpoint,
|
||||
EthereumClient: ec,
|
||||
EthereumPrivateKey: pk,
|
||||
EthereumPrivateKey: ethPrivKey,
|
||||
Environment: env,
|
||||
ChainID: big.NewInt(chainID),
|
||||
ChainID: big.NewInt(cfg.EthereumChainID),
|
||||
GasPrice: gasPrice,
|
||||
GasLimit: uint64(c.Uint(flagGasLimit)),
|
||||
SwapContract: contract,
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/athanorlabs/atomic-swap/common"
|
||||
@@ -19,57 +18,43 @@ import (
|
||||
"github.com/athanorlabs/atomic-swap/tests"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/urfave/cli"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func newTestContext(t *testing.T, description string, flags []string, values []interface{}) *cli.Context {
|
||||
require.Equal(t, len(flags), len(values))
|
||||
|
||||
func newTestContext(t *testing.T, description string, flags map[string]any) *cli.Context {
|
||||
set := flag.NewFlagSet(description, 0)
|
||||
for i := range values {
|
||||
switch v := values[i].(type) {
|
||||
for flag, value := range flags {
|
||||
switch v := value.(type) {
|
||||
case bool:
|
||||
set.Bool(flags[i], v, "")
|
||||
set.Bool(flag, v, "")
|
||||
case string:
|
||||
set.String(flags[i], v, "")
|
||||
set.String(flag, v, "")
|
||||
case uint:
|
||||
set.Uint(flags[i], v, "")
|
||||
set.Uint(flag, v, "")
|
||||
case int64:
|
||||
set.Int64(flags[i], v, "")
|
||||
set.Int64(flag, v, "")
|
||||
case []string:
|
||||
set.Var(&cli.StringSlice{}, flags[i], "")
|
||||
set.Var(&cli.StringSlice{}, flag, "")
|
||||
default:
|
||||
t.Fatalf("unexpected cli value type: %T", values[i])
|
||||
t.Fatalf("unexpected cli value type: %T", value)
|
||||
}
|
||||
}
|
||||
|
||||
ctx := cli.NewContext(app, set, nil)
|
||||
var (
|
||||
err error
|
||||
i int
|
||||
)
|
||||
|
||||
for i = range values {
|
||||
switch v := values[i].(type) {
|
||||
case bool:
|
||||
err = ctx.Set(flags[i], strconv.FormatBool(v))
|
||||
case string:
|
||||
err = ctx.Set(flags[i], values[i].(string))
|
||||
case uint:
|
||||
err = ctx.Set(flags[i], strconv.Itoa(int(values[i].(uint))))
|
||||
case int64:
|
||||
err = ctx.Set(flags[i], strconv.Itoa(int(values[i].(int64))))
|
||||
for flag, value := range flags {
|
||||
switch v := value.(type) {
|
||||
case bool, uint, int64, string:
|
||||
require.NoError(t, ctx.Set(flag, fmt.Sprintf("%v", v)))
|
||||
case []string:
|
||||
for _, str := range values[i].([]string) {
|
||||
err = ctx.Set(flags[i], str)
|
||||
require.NoError(t, err)
|
||||
for _, str := range v {
|
||||
require.NoError(t, ctx.Set(flag, str))
|
||||
}
|
||||
default:
|
||||
t.Fatalf("unexpected cli value type: %T", values[i])
|
||||
t.Fatalf("unexpected cli value type: %T", value)
|
||||
}
|
||||
}
|
||||
|
||||
require.NoError(t, err, fmt.Sprintf("failed to set cli flag: %T, err: %s", flags[i], err))
|
||||
return ctx
|
||||
}
|
||||
|
||||
@@ -122,11 +107,18 @@ func createInfoFile(t *testing.T, kpA, kpB *mcrypto.PrivateKeyPair, contractAddr
|
||||
bz, err := json.MarshalIndent(infofile, "", "\t")
|
||||
require.NoError(t, err)
|
||||
filepath := path.Join(t.TempDir(), "test-infofile.txt")
|
||||
err = os.WriteFile(filepath, bz, os.ModePerm)
|
||||
err = os.WriteFile(filepath, bz, 0600)
|
||||
require.NoError(t, err)
|
||||
return filepath
|
||||
}
|
||||
|
||||
func createEthPrivKeyFile(t *testing.T, ethKeyHex string) string {
|
||||
fileName := path.Join(t.TempDir(), "eth.key")
|
||||
err := os.WriteFile(fileName, []byte(ethKeyHex), 0600)
|
||||
require.NoError(t, err)
|
||||
return fileName
|
||||
}
|
||||
|
||||
func TestRecover_sharedSwapSecret(t *testing.T) {
|
||||
kpA, err := mcrypto.GenerateKeys()
|
||||
require.NoError(t, err)
|
||||
@@ -137,11 +129,11 @@ func TestRecover_sharedSwapSecret(t *testing.T) {
|
||||
|
||||
c := newTestContext(t,
|
||||
"test --xmrtaker with shared swap secret",
|
||||
[]string{flagXMRTaker, flagInfoFile, flagMoneroWalletEndpoint},
|
||||
[]interface{}{
|
||||
true,
|
||||
infoFilePath,
|
||||
tests.CreateWalletRPCService(t),
|
||||
map[string]any{
|
||||
flagEnv: "dev",
|
||||
flagXMRTaker: true,
|
||||
flagInfoFile: infoFilePath,
|
||||
flagMoneroWalletEndpoint: tests.CreateWalletRPCService(t),
|
||||
},
|
||||
)
|
||||
|
||||
@@ -157,10 +149,12 @@ func TestRecover_withXMRMakerSecretAndContract(t *testing.T) {
|
||||
|
||||
c := newTestContext(t,
|
||||
"test --xmrmaker with contract address and secret",
|
||||
[]string{flagXMRMaker, flagInfoFile},
|
||||
[]interface{}{
|
||||
true,
|
||||
infoFilePath,
|
||||
map[string]any{
|
||||
flagEnv: "dev",
|
||||
flagXMRMaker: true,
|
||||
flagInfoFile: infoFilePath,
|
||||
flagEthereumPrivKey: createEthPrivKeyFile(t, common.DefaultPrivKeyXMRMaker),
|
||||
flagMoneroWalletEndpoint: tests.CreateWalletRPCService(t),
|
||||
},
|
||||
)
|
||||
|
||||
@@ -179,10 +173,11 @@ func TestRecover_withXMRTakerSecretAndContract(t *testing.T) {
|
||||
|
||||
c := newTestContext(t,
|
||||
"test --xmrtaker with contract address and secret",
|
||||
[]string{flagXMRTaker, flagInfoFile},
|
||||
[]interface{}{
|
||||
true,
|
||||
infoFilePath,
|
||||
map[string]any{
|
||||
flagEnv: "dev",
|
||||
flagXMRTaker: true,
|
||||
flagInfoFile: infoFilePath,
|
||||
flagEthereumPrivKey: createEthPrivKeyFile(t, common.DefaultPrivKeyXMRTaker),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/athanorlabs/atomic-swap/common"
|
||||
"github.com/athanorlabs/atomic-swap/common/types"
|
||||
|
||||
@@ -1,63 +1,52 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
logging "github.com/ipfs/go-log"
|
||||
"github.com/urfave/cli"
|
||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
|
||||
"github.com/athanorlabs/atomic-swap/common"
|
||||
)
|
||||
|
||||
const (
|
||||
// TODO: just move all the flags to here, or their own package? there's a lot of duplicate ones
|
||||
flagEthereumPrivKey = "ethereum-privkey"
|
||||
flagEnv = "env"
|
||||
)
|
||||
|
||||
var log = logging.Logger("cmd")
|
||||
|
||||
var defaultEnvironment = common.Development
|
||||
|
||||
var (
|
||||
errNoEthereumPrivateKey = errors.New("must provide --ethereum-privkey file for non-development environment")
|
||||
errInvalidEnv = errors.New("--env must be one of mainnet, stagenet, or dev")
|
||||
)
|
||||
|
||||
// GetEthereumPrivateKey returns an ethereum private key hex string given the CLI options.
|
||||
func GetEthereumPrivateKey(c *cli.Context, env common.Environment, devXMRMaker,
|
||||
useExternal bool) (ethPrivKeyHex string, err error) {
|
||||
if c.String(flagEthereumPrivKey) != "" {
|
||||
ethPrivKeyFile := c.String(flagEthereumPrivKey)
|
||||
key, err := os.ReadFile(filepath.Clean(ethPrivKeyFile))
|
||||
// GetEthereumPrivateKey returns an ethereum private key for the given the CLI options.
|
||||
func GetEthereumPrivateKey(ethPrivKeyFile string, env common.Environment, devXMRMaker, devXMRTaker bool) (
|
||||
key *ecdsa.PrivateKey,
|
||||
err error,
|
||||
) {
|
||||
if ethPrivKeyFile != "" {
|
||||
fileData, err := os.ReadFile(filepath.Clean(ethPrivKeyFile))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read ethereum-privkey file: %w", err)
|
||||
}
|
||||
ethPrivKeyHex = strings.TrimSpace(string(key))
|
||||
} else {
|
||||
if env != common.Development || useExternal {
|
||||
log.Warnf("%s", errNoEthereumPrivateKey)
|
||||
return "", nil
|
||||
return nil, fmt.Errorf("failed to read ethereum-privkey file: %w", err)
|
||||
}
|
||||
ethPrivKeyHex := strings.TrimSpace(string(fileData))
|
||||
return ethcrypto.HexToECDSA(ethPrivKeyHex)
|
||||
}
|
||||
|
||||
log.Warn("no ethereum private key file provided, using ganache deterministic key")
|
||||
if devXMRMaker {
|
||||
ethPrivKeyHex = common.DefaultPrivKeyXMRMaker
|
||||
} else {
|
||||
ethPrivKeyHex = common.DefaultPrivKeyXMRTaker
|
||||
if env == common.Development {
|
||||
switch {
|
||||
case devXMRMaker:
|
||||
return ethcrypto.HexToECDSA(common.DefaultPrivKeyXMRMaker)
|
||||
case devXMRTaker:
|
||||
return ethcrypto.HexToECDSA(common.DefaultPrivKeyXMRTaker)
|
||||
}
|
||||
}
|
||||
|
||||
return ethPrivKeyHex, nil
|
||||
return nil, errNoEthereumPrivateKey
|
||||
}
|
||||
|
||||
// GetEnvironment returns a common.Environment from the CLI options.
|
||||
func GetEnvironment(c *cli.Context) (env common.Environment, cfg common.Config, err error) {
|
||||
switch c.String(flagEnv) {
|
||||
func GetEnvironment(envStr string) (env common.Environment, cfg common.Config, err error) {
|
||||
switch envStr {
|
||||
case "mainnet":
|
||||
env = common.Mainnet
|
||||
cfg = common.MainnetConfig
|
||||
@@ -67,9 +56,6 @@ func GetEnvironment(c *cli.Context) (env common.Environment, cfg common.Config,
|
||||
case "dev":
|
||||
env = common.Development
|
||||
cfg = common.DevelopmentConfig
|
||||
case "":
|
||||
env = defaultEnvironment
|
||||
cfg = common.DevelopmentConfig
|
||||
default:
|
||||
return 0, common.Config{}, errInvalidEnv
|
||||
}
|
||||
|
||||
77
cmd/utils/utils_test.go
Normal file
77
cmd/utils/utils_test.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
ethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/athanorlabs/atomic-swap/common"
|
||||
)
|
||||
|
||||
func TestGetEthereumPrivateKey_devXMRMaker(t *testing.T) {
|
||||
devXMRMaker := true
|
||||
devXMRTaker := false
|
||||
key, err := GetEthereumPrivateKey("", common.Development, devXMRMaker, devXMRTaker)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, common.DefaultPrivKeyXMRMaker, hex.EncodeToString(ethcrypto.FromECDSA(key)))
|
||||
}
|
||||
|
||||
func TestGetEthereumPrivateKey_devXMRTaker(t *testing.T) {
|
||||
devXMRMaker := false
|
||||
devXMRTaker := true
|
||||
key, err := GetEthereumPrivateKey("", common.Development, devXMRMaker, devXMRTaker)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, common.DefaultPrivKeyXMRTaker, hex.EncodeToString(ethcrypto.FromECDSA(key)))
|
||||
}
|
||||
|
||||
func TestGetEthereumPrivateKey_devXMRMaker_nonDevEnv(t *testing.T) {
|
||||
devXMRMaker := true
|
||||
devXMRTaker := false
|
||||
_, err := GetEthereumPrivateKey("", common.Stagenet, devXMRMaker, devXMRTaker)
|
||||
require.ErrorIs(t, err, errNoEthereumPrivateKey)
|
||||
}
|
||||
|
||||
func TestGetEthereumPrivateKey_fromFile(t *testing.T) {
|
||||
keyHex := "87c546d6cb8ec705bea47e2ab40f42a768b1e5900686b0cecc68c0e8b74cd789"
|
||||
fileData := []byte(fmt.Sprintf(" %s\n", keyHex)) // add whitespace that we should ignore
|
||||
keyFile := path.Join(t.TempDir(), "eth.key")
|
||||
require.NoError(t, os.WriteFile(keyFile, fileData, 0600))
|
||||
key, err := GetEthereumPrivateKey(keyFile, common.Mainnet, false, false)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, keyHex, hex.EncodeToString(ethcrypto.FromECDSA(key)))
|
||||
}
|
||||
|
||||
func TestGetEthereumPrivateKey_fromFileFail(t *testing.T) {
|
||||
keyHex := "87c546d6cb8ec705bea47e2ab40f42a768b1e5900686b0cecc68c0e8b74cd789"
|
||||
keyBytes, err := hex.DecodeString(keyHex)
|
||||
require.NoError(t, err)
|
||||
keyFile := path.Join(t.TempDir(), "eth.key")
|
||||
require.NoError(t, os.WriteFile(keyFile, keyBytes, 0600)) // key is binary instead of hex
|
||||
_, err = GetEthereumPrivateKey(keyFile, common.Mainnet, false, false)
|
||||
require.ErrorContains(t, err, "invalid hex character")
|
||||
}
|
||||
|
||||
func TestGetEnvironment(t *testing.T) {
|
||||
expected := map[string]common.Environment{
|
||||
"mainnet": common.Mainnet,
|
||||
"stagenet": common.Stagenet,
|
||||
"dev": common.Development,
|
||||
}
|
||||
|
||||
for cliVal, expectedResult := range expected {
|
||||
env, cfg, err := GetEnvironment(cliVal)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedResult, env)
|
||||
require.NotEmpty(t, cfg.DataDir)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetEnvironment_fail(t *testing.T) {
|
||||
_, _, err := GetEnvironment("goerli")
|
||||
require.ErrorIs(t, err, errInvalidEnv)
|
||||
}
|
||||
@@ -3,35 +3,49 @@ package common
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
ethcommon "github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
var homeDir, _ = os.UserHomeDir()
|
||||
|
||||
// Config contains constants that are defaults for various environments
|
||||
type Config struct {
|
||||
Basepath string
|
||||
DataDir string
|
||||
MoneroDaemonEndpoint string
|
||||
EthereumChainID int64
|
||||
Bootnodes []string // TODO: when it's ready for users to test, add some bootnodes (#153)
|
||||
ContractAddress ethcommon.Address
|
||||
Bootnodes []string
|
||||
}
|
||||
|
||||
// MainnetConfig is the mainnet ethereum and monero configuration
|
||||
var MainnetConfig = Config{
|
||||
Basepath: fmt.Sprintf("%s/.atomicswap/mainnet", homeDir),
|
||||
DataDir: fmt.Sprintf("%s/.atomicswap/mainnet", homeDir),
|
||||
MoneroDaemonEndpoint: "http://127.0.0.1:18081/json_rpc",
|
||||
EthereumChainID: MainnetChainID,
|
||||
}
|
||||
|
||||
// StagenetConfig is the monero stagenet and ethereum ropsten configuration
|
||||
// StagenetConfig is the monero stagenet and ethereum Gorli configuration
|
||||
var StagenetConfig = Config{
|
||||
Basepath: fmt.Sprintf("%s/.atomicswap/stagenet", homeDir),
|
||||
DataDir: fmt.Sprintf("%s/.atomicswap/stagenet", homeDir),
|
||||
MoneroDaemonEndpoint: "http://127.0.0.1:38081/json_rpc",
|
||||
EthereumChainID: RopstenChainID,
|
||||
EthereumChainID: GorliChainID,
|
||||
ContractAddress: ethcommon.HexToAddress("0x64e902cD8A29bBAefb9D4e2e3A24d8250C606ee7"),
|
||||
Bootnodes: []string{
|
||||
"/ip4/134.122.115.208/tcp/9900/p2p/12D3KooWDqCzbjexHEa8Rut7bzxHFpRMZyDRW1L6TGkL1KY24JH5",
|
||||
"/ip4/143.198.123.27/tcp/9900/p2p/12D3KooWSc4yFkPWBFmPToTMbhChH3FAgGH96DNzSg5fio1pQYoN",
|
||||
"/ip4/67.207.89.83/tcp/9900/p2p/12D3KooWLbfkLZZvvn8Lxs1KDU3u7gyvBk88ZNtJBbugytBr5RCG",
|
||||
"/ip4/134.122.115.208/tcp/9900/p2p/12D3KooWDqCzbjexHEa8Rut7bzxHFpRMZyDRW1L6TGkL1KY24JH5",
|
||||
"/ip4/164.92.103.160/tcp/9900/p2p/12D3KooWAZtRECEv7zN69zU1e7sPrHbMgfqFUn7QTLh1pKGiMuaM",
|
||||
"/ip4/164.92.103.159/tcp/9900/p2p/12D3KooWSNQF1eNyapxC2zA3jJExgLX7jWhEyw8B3k7zMW5ZRvQz",
|
||||
"/ip4/164.92.123.10/tcp/9900/p2p/12D3KooWG8z9fXVTB72XL8hQbahpfEjutREL9vbBQ4FzqtDKzTBu",
|
||||
"/ip4/161.35.110.210/tcp/9900/p2p/12D3KooWS8iKxqsGTiL3Yc1VaAfg99U5km1AE7bWYQiuavXj3Yz6",
|
||||
},
|
||||
}
|
||||
|
||||
// DevelopmentConfig is the monero and ethereum development environment configuration
|
||||
var DevelopmentConfig = Config{
|
||||
Basepath: fmt.Sprintf("%s/.atomicswap/dev", homeDir),
|
||||
DataDir: fmt.Sprintf("%s/.atomicswap/dev", homeDir),
|
||||
MoneroDaemonEndpoint: "http://127.0.0.1:18081/json_rpc",
|
||||
EthereumChainID: GanacheChainID,
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package common
|
||||
|
||||
const (
|
||||
MainnetChainID = 1 //nolint
|
||||
RopstenChainID = 3
|
||||
GorliChainID = 5
|
||||
GanacheChainID = 1337
|
||||
|
||||
DefaultXMRTakerMoneroEndpoint = "http://127.0.0.1:18084/json_rpc"
|
||||
|
||||
@@ -32,13 +32,10 @@ func GetTopic(sig string) ethcommon.Hash {
|
||||
return b
|
||||
}
|
||||
|
||||
// MakeDir makes a directory
|
||||
// MakeDir creates a directory, including leading directories, if they don't already exist.
|
||||
// File permissions of created directories are only granted to the current user.
|
||||
func MakeDir(dir string) error {
|
||||
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return os.MkdirAll(dir, 0700)
|
||||
}
|
||||
|
||||
// Exists returns whether the given file or directory exists
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
ethcommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -23,3 +26,12 @@ func TestGetTopic(t *testing.T) {
|
||||
refundedTopic := ethcommon.HexToHash("0x007c875846b687732a7579c19bb1dade66cd14e9f4f809565e2b2b5e76c72b4f")
|
||||
require.Equal(t, GetTopic(RefundedEventSignature), refundedTopic)
|
||||
}
|
||||
|
||||
func TestMakeDir(t *testing.T) {
|
||||
path := path.Join(t.TempDir(), "mainnet")
|
||||
require.NoError(t, MakeDir(path))
|
||||
assert.NoError(t, MakeDir(path)) // No error if the dir already exists
|
||||
fileStats, err := os.Stat(path)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "drwx------", fileStats.Mode().String()) // only user has access
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ make build
|
||||
|
||||
10. Copy `goerli.key` into this directory. If you are using an Infura Goerli endpoint, copy-paste your API key into the field below following the `--ethereum-endpoint` flag. Otherwise, change `--ethereum-endpoint` to point to your endpoint. Finally, start the `swapd` atomic swap daemon process:
|
||||
```bash
|
||||
./swapd --env stagenet --ethereum-privkey=goerli.key --monero-endpoint=http://localhost:18083/json_rpc --wallet-file=stagenet-wallet --ethereum-endpoint=https://goerli.infura.io/v3/<your-api-key> --ethereum-chain-id=5 --contract-address=0x2125320230096B33b55f6d7905Fef61A3a0906a0 --bootnodes /ip4/134.122.115.208/tcp/9900/p2p/12D3KooWDqCzbjexHEa8Rut7bzxHFpRMZyDRW1L6TGkL1KY24JH5,/ip4/143.198.123.27/tcp/9900/p2p/12D3KooWSc4yFkPWBFmPToTMbhChH3FAgGH96DNzSg5fio1pQYoN,/ip4/67.207.89.83/tcp/9900/p2p/12D3KooWLbfkLZZvvn8Lxs1KDU3u7gyvBk88ZNtJBbugytBr5RCG,/ip4/134.122.115.208/tcp/9900/p2p/12D3KooWDqCzbjexHEa8Rut7bzxHFpRMZyDRW1L6TGkL1KY24JH5,/ip4/164.92.103.160/tcp/9900/p2p/12D3KooWAZtRECEv7zN69zU1e7sPrHbMgfqFUn7QTLh1pKGiMuaM,/ip4/164.92.103.159/tcp/9900/p2p/12D3KooWSNQF1eNyapxC2zA3jJExgLX7jWhEyw8B3k7zMW5ZRvQz,/ip4/164.92.123.10/tcp/9900/p2p/12D3KooWG8z9fXVTB72XL8hQbahpfEjutREL9vbBQ4FzqtDKzTBu,/ip4/161.35.110.210/tcp/9900/p2p/12D3KooWS8iKxqsGTiL3Yc1VaAfg99U5km1AE7bWYQiuavXj3Yz6,/ip4/206.189.47.220/tcp/9900/p2p/12D3KooWGVzz2d2LSceVFFdqTYqmQXTqc5eWziw7PLRahCWGJhKB --rpc-port=5001
|
||||
./swapd --env stagenet --ethereum-privkey=goerli.key --monero-endpoint=http://localhost:18083/json_rpc --wallet-file=stagenet-wallet --ethereum-endpoint=https://goerli.infura.io/v3/<your-api-key> --rpc-port=5001
|
||||
```
|
||||
|
||||
> Note: please also see the [RPC documentation](./rpc.md) for complete documentation on available RPC calls and their parameters.
|
||||
|
||||
3
go.mod
3
go.mod
@@ -23,7 +23,7 @@ require (
|
||||
github.com/multiformats/go-multiaddr v0.6.0
|
||||
github.com/noot/cgo-dleq v0.0.0-20220726051627-d0716fb55684
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/urfave/cli v1.22.9
|
||||
github.com/urfave/cli/v2 v2.10.2
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
|
||||
)
|
||||
|
||||
@@ -139,6 +139,7 @@ require (
|
||||
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||
github.com/tklauser/numcpus v0.5.0 // indirect
|
||||
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
go.uber.org/atomic v1.10.0 // indirect
|
||||
|
||||
4
go.sum
4
go.sum
@@ -741,9 +741,8 @@ github.com/tklauser/numcpus v0.5.0/go.mod h1:OGzpTxpcIMNGYQdit2BYL1pvk/dSOaJWjKo
|
||||
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef h1:wHSqTBrZW24CsNJDfeh9Ex6Pm0Rcpc7qrgKBiL44vF4=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli v1.22.9 h1:cv3/KhXGBGjEXLC4bH0sLuJ9BewaAbpk5oyMOveu4pw=
|
||||
github.com/urfave/cli v1.22.9/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli/v2 v2.10.2 h1:x3p8awjp/2arX+Nl/G2040AZpOCHS/eMJJ1/a+mye4Y=
|
||||
github.com/urfave/cli/v2 v2.10.2/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo=
|
||||
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
|
||||
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
|
||||
github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a h1:G++j5e0OC488te356JvdhaM8YS6nMsjLAYF7JxCv07w=
|
||||
@@ -754,6 +753,7 @@ github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1
|
||||
github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
|
||||
@@ -122,7 +122,7 @@ func (d *discovery) discover(provides types.ProvidesCoin,
|
||||
searchTime.Seconds(),
|
||||
)
|
||||
|
||||
peerCh, err := d.rd.FindPeers(d.ctx, string("XMR"))
|
||||
peerCh, err := d.rd.FindPeers(d.ctx, string(provides))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
10
net/host.go
10
net/host.go
@@ -71,8 +71,8 @@ type host struct {
|
||||
type Config struct {
|
||||
Ctx context.Context
|
||||
Environment common.Environment
|
||||
Basepath string
|
||||
ChainID int64
|
||||
DataDir string
|
||||
EthChainID int64
|
||||
Port uint16
|
||||
KeyFile string
|
||||
Bootnodes []string
|
||||
@@ -111,7 +111,7 @@ func NewHost(cfg *Config) (*host, error) {
|
||||
}
|
||||
}
|
||||
|
||||
ds, err := badger.NewDatastore(path.Join(cfg.Basepath, "libp2p-datastore"), &badger.DefaultOptions)
|
||||
ds, err := badger.NewDatastore(path.Join(cfg.DataDir, "libp2p-datastore"), &badger.DefaultOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -165,7 +165,7 @@ func NewHost(cfg *Config) (*host, error) {
|
||||
hst := &host{
|
||||
ctx: ourCtx,
|
||||
cancel: cancel,
|
||||
protocolID: fmt.Sprintf("%s/%s/%d", protocolID, cfg.Environment, cfg.ChainID),
|
||||
protocolID: fmt.Sprintf("%s/%s/%d", protocolID, cfg.Environment, cfg.EthChainID),
|
||||
h: h,
|
||||
handler: cfg.Handler,
|
||||
ds: ds,
|
||||
@@ -218,7 +218,7 @@ func (h *host) logPeers() {
|
||||
}
|
||||
}
|
||||
|
||||
// close closes host services and the libp2p host (host services first)
|
||||
// Stop closes host services and the libp2p host (host services first)
|
||||
func (h *host) Stop() error {
|
||||
h.cancel()
|
||||
|
||||
|
||||
@@ -62,8 +62,8 @@ func newHost(t *testing.T, port uint16) *host {
|
||||
cfg := &Config{
|
||||
Ctx: context.Background(),
|
||||
Environment: common.Development,
|
||||
Basepath: t.TempDir(),
|
||||
ChainID: common.GanacheChainID,
|
||||
DataDir: t.TempDir(),
|
||||
EthChainID: common.GanacheChainID,
|
||||
Port: port,
|
||||
KeyFile: path.Join(t.TempDir(), fmt.Sprintf("node-%d.key", port)),
|
||||
Bootnodes: []string{},
|
||||
|
||||
@@ -11,15 +11,15 @@ import (
|
||||
)
|
||||
|
||||
// GetSwapInfoFilepath returns an info file path with the current timestamp.
|
||||
func GetSwapInfoFilepath(basePath string) string {
|
||||
func GetSwapInfoFilepath(dataDir string) string {
|
||||
t := time.Now().Format(common.TimeFmtNSecs)
|
||||
return path.Join(basePath, t)
|
||||
return path.Join(dataDir, t)
|
||||
}
|
||||
|
||||
// GetSwapRecoveryFilepath returns an info file path with the current timestamp.
|
||||
func GetSwapRecoveryFilepath(basePath string) string {
|
||||
func GetSwapRecoveryFilepath(dataDir string) string {
|
||||
t := time.Now().Format(common.TimeFmtNSecs)
|
||||
return path.Join(basePath, fmt.Sprintf("recovery-%s.txt", t))
|
||||
return path.Join(dataDir, fmt.Sprintf("recovery-%s.txt", t))
|
||||
}
|
||||
|
||||
// ConvertContractSwapToMsg converts a swapfactory.SwapFactorySwap to a *message.ContractSwap
|
||||
|
||||
@@ -17,8 +17,9 @@ func (b *Instance) MakeOffer(o *types.Offer) (*types.OfferExtra, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if common.MoneroAmount(balance.UnlockedBalance) < common.MoneroToPiconero(o.MaximumAmount) {
|
||||
return nil, errUnlockedBalanceTooLow
|
||||
unlockedBalance := common.MoneroAmount(balance.UnlockedBalance)
|
||||
if unlockedBalance < common.MoneroToPiconero(o.MaximumAmount) {
|
||||
return nil, errUnlockedBalanceTooLow{unlockedBalance.AsMonero(), o.MaximumAmount}
|
||||
}
|
||||
|
||||
extra := b.offerManager.AddOffer(o)
|
||||
|
||||
@@ -3,6 +3,7 @@ package xmrmaker
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -28,11 +29,55 @@ var (
|
||||
|
||||
// protocol initiation errors
|
||||
errProtocolAlreadyInProgress = errors.New("protocol already in progress")
|
||||
errBalanceTooLow = errors.New("balance lower than amount to be provided")
|
||||
errNoOfferWithID = errors.New("failed to find offer with given ID")
|
||||
errOfferIDNotSet = errors.New("offer ID was not set")
|
||||
errAmountProvidedTooLow = errors.New("amount provided by taker is too low for offer")
|
||||
errAmountProvidedTooHigh = errors.New("amount provided by taker is too high for offer")
|
||||
errUnlockedBalanceTooLow = errors.New("unlocked balance is less than maximum offer amount")
|
||||
errSwapCompleted = errors.New("swap is already completed")
|
||||
)
|
||||
|
||||
type errBalanceTooLow struct {
|
||||
unlockedBalance float64
|
||||
providedAmount float64
|
||||
}
|
||||
|
||||
func (e errBalanceTooLow) Error() string {
|
||||
return fmt.Sprintf("balance of %s XMR is below provided %s XMR",
|
||||
strconv.FormatFloat(e.unlockedBalance, 'f', -1, 64),
|
||||
strconv.FormatFloat(e.providedAmount, 'f', -1, 64),
|
||||
)
|
||||
}
|
||||
|
||||
type errAmountProvidedTooLow struct {
|
||||
providedAmount float64
|
||||
minAmount float64
|
||||
}
|
||||
|
||||
func (e errAmountProvidedTooLow) Error() string {
|
||||
return fmt.Sprintf("%s XMR provided by taker is under offer minimum of %s XMR",
|
||||
strconv.FormatFloat(e.providedAmount, 'f', -1, 64),
|
||||
strconv.FormatFloat(e.minAmount, 'f', -1, 64),
|
||||
)
|
||||
}
|
||||
|
||||
type errAmountProvidedTooHigh struct {
|
||||
providedAmount float64
|
||||
maxAmount float64
|
||||
}
|
||||
|
||||
func (e errAmountProvidedTooHigh) Error() string {
|
||||
return fmt.Sprintf("%s XMR provided by taker is over offer maximum of %s XMR",
|
||||
strconv.FormatFloat(e.providedAmount, 'f', -1, 64),
|
||||
strconv.FormatFloat(e.maxAmount, 'f', -1, 64),
|
||||
)
|
||||
}
|
||||
|
||||
type errUnlockedBalanceTooLow struct {
|
||||
minAmount float64
|
||||
unlockedBalance float64
|
||||
}
|
||||
|
||||
func (e errUnlockedBalanceTooLow) Error() string {
|
||||
return fmt.Sprintf("balance %s XMR is too low for maximum offer amount of %s XMR",
|
||||
strconv.FormatFloat(e.minAmount, 'f', -1, 64),
|
||||
strconv.FormatFloat(e.unlockedBalance, 'f', -1, 64),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ var (
|
||||
// Instance implements the functionality that will be needed by a user who owns XMR
|
||||
// and wishes to swap for ETH.
|
||||
type Instance struct {
|
||||
backend backend.Backend
|
||||
basepath string
|
||||
backend backend.Backend
|
||||
dataDir string
|
||||
|
||||
walletFile, walletPassword string
|
||||
|
||||
@@ -32,7 +32,7 @@ type Instance struct {
|
||||
// Config contains the configuration values for a new XMRMaker instance.
|
||||
type Config struct {
|
||||
Backend backend.Backend
|
||||
Basepath string
|
||||
DataDir string
|
||||
WalletFile, WalletPassword string
|
||||
ExternalSender bool
|
||||
}
|
||||
@@ -50,10 +50,10 @@ func NewInstance(cfg *Config) (*Instance, error) {
|
||||
|
||||
return &Instance{
|
||||
backend: cfg.Backend,
|
||||
basepath: cfg.Basepath,
|
||||
dataDir: cfg.DataDir,
|
||||
walletFile: cfg.WalletFile,
|
||||
walletPassword: cfg.WalletPassword,
|
||||
offerManager: offers.NewManager(cfg.Basepath),
|
||||
offerManager: offers.NewManager(cfg.DataDir),
|
||||
swapStates: make(map[types.Hash]*swapState),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -34,7 +34,10 @@ func (b *Instance) initiate(
|
||||
|
||||
// check user's balance and that they actually have what they will provide
|
||||
if balance.UnlockedBalance <= uint64(providesAmount) {
|
||||
return nil, errBalanceTooLow
|
||||
return nil, errBalanceTooLow{
|
||||
unlockedBalance: common.MoneroAmount(balance.UnlockedBalance).AsMonero(),
|
||||
providedAmount: providesAmount.AsMonero(),
|
||||
}
|
||||
}
|
||||
|
||||
s, err := newSwapState(b.backend, offer, b.offerManager, offerExtra.StatusCh,
|
||||
@@ -86,11 +89,11 @@ func (b *Instance) HandleInitiateMessage(msg *net.SendKeysMessage) (net.SwapStat
|
||||
providedAmount := offer.ExchangeRate.ToXMR(msg.ProvidedAmount)
|
||||
|
||||
if providedAmount < offer.MinimumAmount {
|
||||
return nil, nil, errAmountProvidedTooLow
|
||||
return nil, nil, errAmountProvidedTooLow{providedAmount, offer.MinimumAmount}
|
||||
}
|
||||
|
||||
if providedAmount > offer.MaximumAmount {
|
||||
return nil, nil, errAmountProvidedTooHigh
|
||||
return nil, nil, errAmountProvidedTooHigh{providedAmount, offer.MaximumAmount}
|
||||
}
|
||||
|
||||
providedPicoXMR := common.MoneroToPiconero(providedAmount)
|
||||
|
||||
@@ -11,9 +11,9 @@ const statusChSize = 6 // the max number of stages a swap can potentially go thr
|
||||
|
||||
// Manager synchronises access to the offers map.
|
||||
type Manager struct {
|
||||
mu sync.Mutex // synchronises access to the offers map
|
||||
offers map[types.Hash]*offerWithExtra
|
||||
basePath string
|
||||
mu sync.Mutex // synchronises access to the offers map
|
||||
offers map[types.Hash]*offerWithExtra
|
||||
dataDir string
|
||||
}
|
||||
|
||||
type offerWithExtra struct {
|
||||
@@ -21,12 +21,12 @@ type offerWithExtra struct {
|
||||
extra *types.OfferExtra
|
||||
}
|
||||
|
||||
// NewManager creates a new offers manager. The passed in basePath is the directory where the
|
||||
// NewManager creates a new offers manager. The passed in dataDir is the directory where the
|
||||
// recovery file is for each individual swap is stored.
|
||||
func NewManager(basePath string) *Manager {
|
||||
func NewManager(dataDir string) *Manager {
|
||||
return &Manager{
|
||||
offers: make(map[types.Hash]*offerWithExtra),
|
||||
basePath: basePath,
|
||||
offers: make(map[types.Hash]*offerWithExtra),
|
||||
dataDir: dataDir,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ func (m *Manager) AddOffer(o *types.Offer) *types.OfferExtra {
|
||||
|
||||
extra := &types.OfferExtra{
|
||||
StatusCh: make(chan types.Status, statusChSize),
|
||||
InfoFile: pcommon.GetSwapInfoFilepath(m.basePath),
|
||||
InfoFile: pcommon.GetSwapInfoFilepath(m.dataDir),
|
||||
}
|
||||
|
||||
m.offers[id] = &offerWithExtra{
|
||||
|
||||
@@ -4,13 +4,14 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
ethcommon "github.com/ethereum/go-ethereum/common"
|
||||
|
||||
"github.com/athanorlabs/atomic-swap/common/types"
|
||||
mcrypto "github.com/athanorlabs/atomic-swap/crypto/monero"
|
||||
"github.com/athanorlabs/atomic-swap/dleq"
|
||||
pcommon "github.com/athanorlabs/atomic-swap/protocol"
|
||||
"github.com/athanorlabs/atomic-swap/protocol/backend"
|
||||
"github.com/athanorlabs/atomic-swap/swapfactory"
|
||||
ethcommon "github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
type recoveryState struct {
|
||||
@@ -19,7 +20,7 @@ type recoveryState struct {
|
||||
|
||||
// NewRecoveryState returns a new *xmrmaker.recoveryState,
|
||||
// which has methods to either claim ether or reclaim monero from an initiated swap.
|
||||
func NewRecoveryState(b backend.Backend, basePath string, secret *mcrypto.PrivateSpendKey,
|
||||
func NewRecoveryState(b backend.Backend, dataDir string, secret *mcrypto.PrivateSpendKey,
|
||||
contractAddr ethcommon.Address,
|
||||
contractSwapID [32]byte, contractSwap swapfactory.SwapFactorySwap) (*recoveryState, error) {
|
||||
kp, err := secret.AsPrivateKeyPair()
|
||||
@@ -49,7 +50,7 @@ func NewRecoveryState(b backend.Backend, basePath string, secret *mcrypto.Privat
|
||||
dleqProof: dleq.NewProofWithSecret(sc),
|
||||
contractSwapID: contractSwapID,
|
||||
contractSwap: contractSwap,
|
||||
infoFile: pcommon.GetSwapRecoveryFilepath(basePath),
|
||||
infoFile: pcommon.GetSwapRecoveryFilepath(dataDir),
|
||||
}
|
||||
|
||||
if err := s.setContract(contractAddr); err != nil {
|
||||
|
||||
@@ -23,8 +23,8 @@ func newTestRecoveryState(t *testing.T, timeout time.Duration) *recoveryState {
|
||||
|
||||
newSwap(t, s, [32]byte{}, sr, big.NewInt(1), timeout)
|
||||
|
||||
basePath := path.Join(t.TempDir(), "test-infofile")
|
||||
rs, err := NewRecoveryState(inst.backend, basePath, s.privkeys.SpendKey(), s.ContractAddr(),
|
||||
dataDir := path.Join(t.TempDir(), "test-infofile")
|
||||
rs, err := NewRecoveryState(inst.backend, dataDir, s.privkeys.SpendKey(), s.ContractAddr(),
|
||||
s.contractSwapID, s.contractSwap)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ func newTestXMRMaker(t *testing.T) *Instance {
|
||||
|
||||
cfg := &Config{
|
||||
Backend: b,
|
||||
Basepath: path.Join(t.TempDir(), "xmrmaker"),
|
||||
DataDir: path.Join(t.TempDir(), "xmrmaker"),
|
||||
WalletFile: testWallet,
|
||||
WalletPassword: "",
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ var (
|
||||
// Instance implements the functionality that will be used by a user who owns ETH
|
||||
// and wishes to swap for XMR.
|
||||
type Instance struct {
|
||||
backend backend.Backend
|
||||
basepath string
|
||||
backend backend.Backend
|
||||
dataDir string
|
||||
|
||||
walletFile, walletPassword string
|
||||
transferBack bool // transfer xmr back to original account
|
||||
@@ -42,7 +42,7 @@ type Instance struct {
|
||||
// Config contains the configuration values for a new XMRTaker instance.
|
||||
type Config struct {
|
||||
Backend backend.Backend
|
||||
Basepath string
|
||||
DataDir string
|
||||
MoneroWalletFile, MoneroWalletPassword string
|
||||
TransferBack bool
|
||||
ExternalSender bool
|
||||
@@ -74,7 +74,7 @@ func NewInstance(cfg *Config) (*Instance, error) {
|
||||
|
||||
return &Instance{
|
||||
backend: cfg.Backend,
|
||||
basepath: cfg.Basepath,
|
||||
dataDir: cfg.DataDir,
|
||||
walletFile: cfg.MoneroWalletFile,
|
||||
walletPassword: cfg.MoneroWalletPassword,
|
||||
swapStates: make(map[types.Hash]*swapState),
|
||||
|
||||
@@ -45,8 +45,16 @@ func (a *Instance) initiate(providesAmount common.EtherAmount, receivedAmount co
|
||||
return nil, errBalanceTooLow
|
||||
}
|
||||
|
||||
s, err := newSwapState(a.backend, offerID, pcommon.GetSwapInfoFilepath(a.basepath), a.transferBack,
|
||||
providesAmount, receivedAmount, exchangeRate, ethAsset)
|
||||
s, err := newSwapState(
|
||||
a.backend,
|
||||
offerID,
|
||||
pcommon.GetSwapInfoFilepath(a.dataDir),
|
||||
a.transferBack,
|
||||
providesAmount,
|
||||
receivedAmount,
|
||||
exchangeRate,
|
||||
ethAsset,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ import (
|
||||
func newTestXMRTaker(t *testing.T) *Instance {
|
||||
b := newBackend(t)
|
||||
cfg := &Config{
|
||||
Backend: b,
|
||||
Basepath: path.Join(t.TempDir(), "xmrtaker"),
|
||||
Backend: b,
|
||||
DataDir: path.Join(t.TempDir(), "xmrtaker"),
|
||||
}
|
||||
|
||||
xmrtaker, err := NewInstance(cfg)
|
||||
|
||||
@@ -27,7 +27,7 @@ type recoveryState struct {
|
||||
|
||||
// NewRecoveryState returns a new *xmrmaker.recoveryState,
|
||||
// which has methods to either claim ether or reclaim monero from an initiated swap.
|
||||
func NewRecoveryState(b backend.Backend, basePath string, secret *mcrypto.PrivateSpendKey,
|
||||
func NewRecoveryState(b backend.Backend, dataDir string, secret *mcrypto.PrivateSpendKey,
|
||||
contractSwapID [32]byte, contractSwap swapfactory.SwapFactorySwap) (*recoveryState, error) {
|
||||
kp, err := secret.AsPrivateKeyPair()
|
||||
if err != nil {
|
||||
@@ -56,7 +56,7 @@ func NewRecoveryState(b backend.Backend, basePath string, secret *mcrypto.Privat
|
||||
dleqProof: dleq.NewProofWithSecret(sc),
|
||||
contractSwapID: contractSwapID,
|
||||
contractSwap: contractSwap,
|
||||
infoFile: pcommon.GetSwapRecoveryFilepath(basePath),
|
||||
infoFile: pcommon.GetSwapRecoveryFilepath(dataDir),
|
||||
claimedCh: make(chan struct{}),
|
||||
info: pswap.NewEmptyInfo(),
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ func newTestRecoveryState(t *testing.T, timeout time.Duration) *recoveryState {
|
||||
_, err = s.lockETH(common.NewEtherAmount(1))
|
||||
require.NoError(t, err)
|
||||
|
||||
basePath := path.Join(t.TempDir(), "test-infoFile")
|
||||
rs, err := NewRecoveryState(s, basePath, s.privkeys.SpendKey(), s.contractSwapID, s.contractSwap)
|
||||
dataDir := path.Join(t.TempDir(), "test-infoFile")
|
||||
rs, err := NewRecoveryState(s, dataDir, s.privkeys.SpendKey(), s.contractSwapID, s.contractSwap)
|
||||
require.NoError(t, err)
|
||||
return rs
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ func (r *recoverer) WalletFromSharedSecret(pk *mcrypto.PrivateKeyInfo) (mcrypto.
|
||||
}
|
||||
|
||||
// RecoverFromXMRMakerSecretAndContract recovers funds by either claiming ether or reclaiming locked monero.
|
||||
func (r *recoverer) RecoverFromXMRMakerSecretAndContract(b backend.Backend, basePath string,
|
||||
func (r *recoverer) RecoverFromXMRMakerSecretAndContract(b backend.Backend, dataDir string,
|
||||
xmrmakerSecret, contractAddr string, swapID [32]byte,
|
||||
swap swapfactory.SwapFactorySwap) (*xmrmaker.RecoveryResult, error) {
|
||||
bs, err := hex.DecodeString(xmrmakerSecret)
|
||||
@@ -103,7 +103,7 @@ func (r *recoverer) RecoverFromXMRMakerSecretAndContract(b backend.Backend, base
|
||||
}
|
||||
|
||||
addr := ethcommon.HexToAddress(contractAddr)
|
||||
rs, err := xmrmaker.NewRecoveryState(b, basePath, bk, addr, swapID, swap)
|
||||
rs, err := xmrmaker.NewRecoveryState(b, dataDir, bk, addr, swapID, swap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -112,7 +112,7 @@ func (r *recoverer) RecoverFromXMRMakerSecretAndContract(b backend.Backend, base
|
||||
}
|
||||
|
||||
// RecoverFromXMRTakerSecretAndContract recovers funds by either claiming locked monero or refunding ether.
|
||||
func (r *recoverer) RecoverFromXMRTakerSecretAndContract(b backend.Backend, basePath string,
|
||||
func (r *recoverer) RecoverFromXMRTakerSecretAndContract(b backend.Backend, dataDir string,
|
||||
xmrtakerSecret string, swapID [32]byte, swap swapfactory.SwapFactorySwap) (*xmrtaker.RecoveryResult, error) {
|
||||
as, err := hex.DecodeString(xmrtakerSecret)
|
||||
if err != nil {
|
||||
@@ -124,7 +124,7 @@ func (r *recoverer) RecoverFromXMRTakerSecretAndContract(b backend.Backend, base
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rs, err := xmrtaker.NewRecoveryState(b, basePath, ak, swapID, swap)
|
||||
rs, err := xmrtaker.NewRecoveryState(b, dataDir, ak, swapID, swap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -140,8 +140,8 @@ func TestRecoverer_RecoverFromXMRMakerSecretAndContract_Claim(t *testing.T) {
|
||||
b := newBackend(t, addr, contract, tests.GetMakerTestKey(t))
|
||||
|
||||
r := newRecoverer(t)
|
||||
basePath := path.Join(t.TempDir(), "test-infofile")
|
||||
res, err := r.RecoverFromXMRMakerSecretAndContract(b, basePath, keys.PrivateKeyPair.SpendKey().Hex(),
|
||||
dataDir := path.Join(t.TempDir(), "test-infofile")
|
||||
res, err := r.RecoverFromXMRMakerSecretAndContract(b, dataDir, keys.PrivateKeyPair.SpendKey().Hex(),
|
||||
addr.String(), swapID, swap)
|
||||
require.NoError(t, err)
|
||||
require.True(t, res.Claimed)
|
||||
@@ -156,8 +156,8 @@ func TestRecoverer_RecoverFromXMRMakerSecretAndContract_Claim_afterTimeout(t *te
|
||||
b := newBackend(t, addr, contract, tests.GetMakerTestKey(t))
|
||||
|
||||
r := newRecoverer(t)
|
||||
basePath := path.Join(t.TempDir(), "test-infofile")
|
||||
res, err := r.RecoverFromXMRMakerSecretAndContract(b, basePath, keys.PrivateKeyPair.SpendKey().Hex(),
|
||||
dataDir := path.Join(t.TempDir(), "test-infofile")
|
||||
res, err := r.RecoverFromXMRMakerSecretAndContract(b, dataDir, keys.PrivateKeyPair.SpendKey().Hex(),
|
||||
addr.String(), swapID, swap)
|
||||
require.NoError(t, err)
|
||||
require.True(t, res.Claimed)
|
||||
@@ -172,8 +172,8 @@ func TestRecoverer_RecoverFromXMRTakerSecretAndContract_Refund(t *testing.T) {
|
||||
b := newBackend(t, addr, contract, tests.GetTakerTestKey(t))
|
||||
|
||||
r := newRecoverer(t)
|
||||
basePath := path.Join(t.TempDir(), "test-infofile")
|
||||
res, err := r.RecoverFromXMRTakerSecretAndContract(b, basePath, keys.PrivateKeyPair.SpendKey().Hex(),
|
||||
dataDir := path.Join(t.TempDir(), "test-infofile")
|
||||
res, err := r.RecoverFromXMRTakerSecretAndContract(b, dataDir, keys.PrivateKeyPair.SpendKey().Hex(),
|
||||
swapID, swap)
|
||||
require.NoError(t, err)
|
||||
require.True(t, res.Refunded)
|
||||
|
||||
@@ -2,13 +2,10 @@
|
||||
# This script can be handy to kill test processes launched manually
|
||||
# for debugging or still hanging around for other reasons.
|
||||
|
||||
pkill --uid "${UID}" --full '/monerod .* --regtest '
|
||||
pkill --uid "${UID}" --full '/ganache.* --deterministic '
|
||||
pkill --echo --uid "${UID}" --full '/monerod .* --regtest '
|
||||
pkill --echo --uid "${UID}" --full '/ganache.* --deterministic '
|
||||
|
||||
# If you have monero-wallet-rpc or swapd processes owned by the current user
|
||||
# that you don't want to kill, don't use this script!
|
||||
pkill --uid "${UID}" --full '/monero-wallet-rpc '
|
||||
pkill --uid "${UID}" --full '/swapd '
|
||||
|
||||
# These directories MUST be removed every time you start a fresh monerod instance
|
||||
rm -rf alice-test-keys bob-test-keys
|
||||
pkill --echo --uid "${UID}" --full '/monero-wallet-rpc '
|
||||
pkill --echo --uid "${UID}" --full '/swapd '
|
||||
|
||||
@@ -35,4 +35,4 @@ fi
|
||||
--abi ethereum/abi/IERC20Metadata.abi \
|
||||
--pkg swapfactory \
|
||||
--type IERC20 \
|
||||
--out swapfactory/ierc20.go
|
||||
--out swapfactory/ierc20.go
|
||||
|
||||
@@ -10,6 +10,10 @@ start-monerod-regtest
|
||||
start-ganache
|
||||
start-alice-wallet
|
||||
start-bob-wallet
|
||||
start-charlie-wallet
|
||||
|
||||
CHARLIE_ETH_KEY="${SWAP_TEST_DATA_DIR}/charlie-eth.key"
|
||||
echo "87c546d6cb8ec705bea47e2ab40f42a768b1e5900686b0cecc68c0e8b74cd789" >"${CHARLIE_ETH_KEY}"
|
||||
|
||||
# wait for wallets to start
|
||||
sleep 5
|
||||
@@ -40,6 +44,8 @@ start-swapd bob \
|
||||
--deploy
|
||||
|
||||
start-swapd charlie \
|
||||
--monero-endpoint "http://127.0.0.1:${CHARLIE_WALLET_PORT}/json_rpc" \
|
||||
--ethereum-privkey "${CHARLIE_ETH_KEY}" \
|
||||
--libp2p-port 9955 \
|
||||
--rpc-port 5003 \
|
||||
--ws-port 8083 \
|
||||
@@ -51,15 +57,26 @@ sleep 3 # Time for Bob and Charlie's swapd to be fully up
|
||||
# run tests
|
||||
echo "running integration tests..."
|
||||
TESTS=integration go test ./tests -v -count=1
|
||||
OK=$?
|
||||
OK="${?}"
|
||||
|
||||
# If we failed, make a copy of the log files that won't get deleted
|
||||
if [[ "${OK}" -ne 0 ]]; then
|
||||
mkdir -p "${SWAP_TEST_DATA_DIR}/saved-logs"
|
||||
cp "${SWAP_TEST_DATA_DIR}/"*.log "${SWAP_TEST_DATA_DIR}/saved-logs/"
|
||||
echo "Logs saved to ${SWAP_TEST_DATA_DIR}/saved-logs/"
|
||||
fi
|
||||
|
||||
stop-swapd alice
|
||||
stop-swapd bob
|
||||
stop-swapd charlie
|
||||
stop-alice-wallet
|
||||
stop-bob-wallet
|
||||
stop-charlie-wallet
|
||||
stop-monerod-regtest
|
||||
stop-ganache
|
||||
remove-test-data-dir
|
||||
rm -f "${CHARLIE_ETH_KEY}"
|
||||
if [[ "${OK}" -eq 0 ]]; then
|
||||
remove-test-data-dir
|
||||
fi
|
||||
|
||||
exit $OK
|
||||
exit "${OK}"
|
||||
|
||||
@@ -21,8 +21,11 @@
|
||||
|
||||
MONEROD_PORT=18081
|
||||
GANACHE_PORT=8545
|
||||
|
||||
BOB_WALLET_PORT=18083
|
||||
ALICE_WALLET_PORT=18084
|
||||
CHARLIE_WALLET_PORT=18085
|
||||
|
||||
PROJECT_ROOT="$(dirname "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")")"
|
||||
MONERO_BIN_DIR="${PROJECT_ROOT}/monero-bin"
|
||||
|
||||
@@ -80,6 +83,7 @@ stop-program() {
|
||||
echo "ERROR: failed to kill ${name}"
|
||||
return 1
|
||||
fi
|
||||
sleep 2 # let program flush data and exit so we delete all files below
|
||||
# Remove the PID file, log file and any data subdirectory
|
||||
rm -rf "${SWAP_TEST_DATA_DIR:?}/${name}"{.pid,.log,}
|
||||
}
|
||||
@@ -202,3 +206,11 @@ start-bob-wallet() {
|
||||
stop-bob-wallet() {
|
||||
stop-program bob-monero-wallet-rpc
|
||||
}
|
||||
|
||||
start-charlie-wallet() {
|
||||
start-monero-wallet-rpc charlie "${CHARLIE_WALLET_PORT}"
|
||||
}
|
||||
|
||||
stop-charlie-wallet() {
|
||||
stop-program charlie-monero-wallet-rpc
|
||||
}
|
||||
|
||||
@@ -67,7 +67,10 @@ func generateBlocks(num uint64) {
|
||||
}
|
||||
|
||||
fmt.Println("> Generating blocks for test setup...")
|
||||
_ = d.GenerateBlocks(xmrmakerAddr.Address, num)
|
||||
err = d.GenerateBlocks(xmrmakerAddr.Address, num)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = c.Refresh()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -96,6 +99,9 @@ func generateBlocksAsync() {
|
||||
}
|
||||
|
||||
func TestXMRTaker_Discover(t *testing.T) {
|
||||
if os.Getenv(generateBlocksEnv) != falseStr {
|
||||
generateBlocks(64)
|
||||
}
|
||||
bc := rpcclient.NewClient(defaultXMRMakerDaemonEndpoint)
|
||||
_, err := bc.MakeOffer(xmrmakerProvideAmount, xmrmakerProvideAmount, exchangeRate, types.EthAssetETH)
|
||||
require.NoError(t, err)
|
||||
@@ -119,6 +125,9 @@ func TestXMRMaker_Discover(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestXMRTaker_Query(t *testing.T) {
|
||||
if os.Getenv(generateBlocksEnv) != falseStr {
|
||||
generateBlocks(64)
|
||||
}
|
||||
bc := rpcclient.NewClient(defaultXMRMakerDaemonEndpoint)
|
||||
offerID, err := bc.MakeOffer(xmrmakerProvideAmount, xmrmakerProvideAmount, exchangeRate, types.EthAssetETH)
|
||||
require.NoError(t, err)
|
||||
@@ -150,6 +159,10 @@ func TestXMRTaker_Query(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSuccess_OneSwap(t *testing.T) {
|
||||
if os.Getenv(generateBlocksEnv) != falseStr {
|
||||
generateBlocks(64)
|
||||
}
|
||||
|
||||
const testTimeout = time.Second * 75
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
Reference in New Issue
Block a user