mirror of
https://github.com/AthanorLabs/atomic-swap.git
synced 2026-01-07 21:34:05 -05:00
feat: add dockerfile (#387)
Co-authored-by: Dmitry Holodov <dimalinux@protonmail.com>
This commit is contained in:
4
Makefile
4
Makefile
@@ -10,7 +10,7 @@ lint-go:
|
||||
|
||||
.PHONY: lint-shell
|
||||
lint-shell:
|
||||
shellcheck --source-path=.:scripts scripts/*.sh
|
||||
shellcheck --source-path=.:scripts scripts/*.sh scripts/*/*.sh
|
||||
|
||||
.PHONY: lint-solidity
|
||||
lint-solidity:
|
||||
@@ -26,7 +26,7 @@ format-go:
|
||||
|
||||
.PHONY: format-shell
|
||||
format-shell:
|
||||
shfmt -w scripts/*.sh
|
||||
shfmt -w scripts/*.sh scripts/*/*.sh
|
||||
|
||||
.PHONY: format-solidity
|
||||
format-solidity:
|
||||
|
||||
@@ -76,7 +76,7 @@ func GetEthereumPrivateKey(ethPrivKeyFile string, env common.Environment, devXMR
|
||||
|
||||
fileData, err := os.ReadFile(filepath.Clean(ethPrivKeyFile))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read ethereum-privkey file: %w", err)
|
||||
return nil, fmt.Errorf("failed to read eth-privkey file: %w", err)
|
||||
}
|
||||
ethPrivKeyHex := strings.TrimSpace(string(fileData))
|
||||
privkey, err := ethcrypto.HexToECDSA(ethPrivKeyHex)
|
||||
|
||||
@@ -6,7 +6,6 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
@@ -26,7 +25,7 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
errNoEthereumPrivateKey = errors.New("must provide --ethereum-privkey file for non-development environment")
|
||||
errNoEthPrivateKey = fmt.Errorf("must provide --%s file for non-development environment", flagEthPrivKey)
|
||||
)
|
||||
|
||||
type contractAddresses struct {
|
||||
@@ -73,7 +72,7 @@ func deploySwapCreator(
|
||||
) (ethcommon.Address, *contracts.SwapCreator, error) {
|
||||
|
||||
if privkey == nil {
|
||||
return ethcommon.Address{}, nil, errNoEthereumPrivateKey
|
||||
return ethcommon.Address{}, nil, errNoEthPrivateKey
|
||||
}
|
||||
|
||||
if (forwarderAddr == ethcommon.Address{}) {
|
||||
|
||||
@@ -63,8 +63,8 @@ const (
|
||||
flagMoneroWalletPath = "wallet-file"
|
||||
flagMoneroWalletPassword = "wallet-password"
|
||||
flagMoneroWalletPort = "wallet-port"
|
||||
flagEthereumEndpoint = "ethereum-endpoint"
|
||||
flagEthereumPrivKey = "ethereum-privkey"
|
||||
flagEthEndpoint = "eth-endpoint"
|
||||
flagEthPrivKey = "eth-privkey"
|
||||
flagContractAddress = "contract-address"
|
||||
flagGasPrice = "gas-price"
|
||||
flagGasLimit = "gas-limit"
|
||||
@@ -91,9 +91,10 @@ func cliApp() *cli.App {
|
||||
Suggest: true,
|
||||
Flags: []cli.Flag{
|
||||
&cli.UintFlag{
|
||||
Name: flagRPCPort,
|
||||
Usage: "Port for the daemon RPC server to run on",
|
||||
Value: defaultRPCPort,
|
||||
Name: flagRPCPort,
|
||||
Usage: "Port for the daemon RPC server to run on",
|
||||
Value: defaultRPCPort,
|
||||
EnvVars: []string{"SWAPD_RPC_PORT"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagDataDir,
|
||||
@@ -106,25 +107,28 @@ func cliApp() *cli.App {
|
||||
Value: fmt.Sprintf("{DATA_DIR}/%s", common.DefaultLibp2pKeyFileName),
|
||||
},
|
||||
&cli.UintFlag{
|
||||
Name: flagLibp2pPort,
|
||||
Usage: "libp2p port to listen on",
|
||||
Value: defaultLibp2pPort,
|
||||
Name: flagLibp2pPort,
|
||||
Usage: "libp2p port to listen on",
|
||||
Value: defaultLibp2pPort,
|
||||
EnvVars: []string{"SWAPD_LIBP2P_PORT"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagEnv,
|
||||
Usage: "Environment to use: one of mainnet, stagenet, or dev",
|
||||
Value: "dev",
|
||||
Name: flagEnv,
|
||||
Usage: "Environment to use: one of mainnet, stagenet, or dev",
|
||||
EnvVars: []string{"SWAPD_ENV"},
|
||||
Value: "dev",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagMoneroDaemonHost,
|
||||
Usage: "monerod host",
|
||||
Value: "127.0.0.1",
|
||||
Name: flagMoneroDaemonHost,
|
||||
Usage: "monerod host",
|
||||
EnvVars: []string{"SWAPD_MONEROD_HOST"},
|
||||
},
|
||||
&cli.UintFlag{
|
||||
Name: flagMoneroDaemonPort,
|
||||
Usage: fmt.Sprintf("monerod port (--%s=stagenet changes default to %d)",
|
||||
flagEnv, common.DefaultMoneroDaemonStagenetPort),
|
||||
Value: common.DefaultMoneroDaemonMainnetPort, // at least for now, this is also the dev default
|
||||
EnvVars: []string{"SWAPD_MONEROD_PORT"},
|
||||
Value: common.DefaultMoneroDaemonMainnetPort, // at least for now, this is also the dev default
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagMoneroWalletPath,
|
||||
@@ -141,13 +145,17 @@ func cliApp() *cli.App {
|
||||
Hidden: true, // flag is for integration tests and won't be supported long term
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagEthereumEndpoint,
|
||||
Usage: "Ethereum client endpoint",
|
||||
Name: flagEthEndpoint,
|
||||
Usage: "Ethereum client endpoint",
|
||||
Aliases: []string{"ethereum-endpoint"},
|
||||
EnvVars: []string{"SWAPD_ETH_ENDPOINT"},
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagEthereumPrivKey,
|
||||
Usage: "File containing ethereum private key as hex, new key is generated if missing",
|
||||
Value: fmt.Sprintf("{DATA-DIR}/%s", common.DefaultEthKeyFileName),
|
||||
Name: flagEthPrivKey,
|
||||
Usage: "File containing ethereum private key as hex, new key is generated if missing",
|
||||
Aliases: []string{"ethereum-privkey"},
|
||||
EnvVars: []string{"SWAPD_ETH_PRIVKEY"},
|
||||
Value: fmt.Sprintf("{DATA-DIR}/%s", common.DefaultEthKeyFileName),
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagContractAddress,
|
||||
@@ -188,9 +196,10 @@ func cliApp() *cli.App {
|
||||
Usage: "Leave XMR in generated swap wallet instead of sweeping funds to primary.",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: flagLogLevel,
|
||||
Usage: "Set log level: one of [error|warn|info|debug]",
|
||||
Value: "info",
|
||||
Name: flagLogLevel,
|
||||
Usage: "Set log level: one of [error|warn|info|debug]",
|
||||
Value: "info",
|
||||
EnvVars: []string{"SWAPD_LOG_LEVEL"},
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: flagUseExternalSigner,
|
||||
@@ -414,7 +423,7 @@ func createMoneroClient(c *cli.Context, envConf *common.Config) (monero.WalletCl
|
||||
Env: envConf.Env,
|
||||
WalletFilePath: walletFilePath,
|
||||
MonerodNodes: envConf.MoneroNodes,
|
||||
MoneroWalletRPCPath: "", // look for it in "monero-bin/monero-wallet-rpc" and then the user's path
|
||||
MoneroWalletRPCPath: "", // look for it in "./monero-bin/monero-wallet-rpc" and then the user's path
|
||||
WalletPassword: c.String(flagMoneroWalletPassword),
|
||||
WalletPort: c.Uint(flagMoneroWalletPort),
|
||||
})
|
||||
@@ -424,23 +433,23 @@ func createEthClient(c *cli.Context, envConf *common.Config) (extethclient.EthCl
|
||||
env := envConf.Env
|
||||
|
||||
ethEndpoint := common.DefaultEthEndpoint
|
||||
if c.String(flagEthereumEndpoint) != "" {
|
||||
ethEndpoint = c.String(flagEthereumEndpoint)
|
||||
if c.String(flagEthEndpoint) != "" {
|
||||
ethEndpoint = c.String(flagEthEndpoint)
|
||||
}
|
||||
|
||||
var ethPrivKey *ecdsa.PrivateKey
|
||||
|
||||
useExternalSigner := c.Bool(flagUseExternalSigner)
|
||||
if useExternalSigner && c.IsSet(flagEthereumPrivKey) {
|
||||
return nil, errFlagsMutuallyExclusive(flagUseExternalSigner, flagEthereumPrivKey)
|
||||
if useExternalSigner && c.IsSet(flagEthPrivKey) {
|
||||
return nil, errFlagsMutuallyExclusive(flagUseExternalSigner, flagEthPrivKey)
|
||||
}
|
||||
|
||||
if !useExternalSigner {
|
||||
ethPrivKeyFile := envConf.EthKeyFileName()
|
||||
if c.IsSet(flagEthereumPrivKey) {
|
||||
ethPrivKeyFile = c.String(flagEthereumPrivKey)
|
||||
if c.IsSet(flagEthPrivKey) {
|
||||
ethPrivKeyFile = c.String(flagEthPrivKey)
|
||||
if ethPrivKeyFile == "" {
|
||||
return nil, errFlagValueEmpty(flagEthereumPrivKey)
|
||||
return nil, errFlagValueEmpty(flagEthPrivKey)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ file above. More information on what the individual files contain can be
|
||||
### {DATA_DIR}/eth.key
|
||||
|
||||
This is the default location of your Ethereum private key used by swaps. Alternate
|
||||
locations can be configured with `--ethereum-privkey`. If the file does not
|
||||
locations can be configured with `--eth-privkey`. If the file does not
|
||||
exist, a new random key will be created and placed in this location.
|
||||
|
||||
### {DATA_DIR}/net.key
|
||||
|
||||
@@ -54,9 +54,9 @@ cd atomic-swap
|
||||
make build
|
||||
```
|
||||
|
||||
10. Start the `swapd` daemon. Change `--ethereum-endpoint` to point to your endpoint.
|
||||
10. Start the `swapd` daemon. Change `--eth-endpoint` to point to your endpoint.
|
||||
```bash
|
||||
./swapd --env stagenet --ethereum-endpoint=<sepolia-endpoint>
|
||||
./swapd --env stagenet --eth-endpoint SEPOLIA_ENDPOINT
|
||||
```
|
||||
Note: You probably need additional flags above:
|
||||
* `--data-dir PATH`: Needed if you are launching more than one `swapd` instance
|
||||
|
||||
61
scripts/docker/Dockerfile
Normal file
61
scripts/docker/Dockerfile
Normal file
@@ -0,0 +1,61 @@
|
||||
FROM golang:1.20 as builder
|
||||
|
||||
# Download monero-wallet-rpc. We need bzip2 to unpack the tar file.
|
||||
RUN apt update && apt install -y bzip2
|
||||
RUN arch=$(uname -m | sed 's/x86_64/linux64/; s/aarch64/linuxarm8/') && \
|
||||
curl -sSL "https://downloads.getmonero.org/cli/${arch}" -o monero.tar.bz2
|
||||
RUN tar xvjf monero.tar.bz2 --no-anchored monero-wallet-rpc --strip-components=1
|
||||
|
||||
# Build the swapd and swapcli binaries. The BRANCH argument can be set to a
|
||||
# branch, release tag, "latest", or a commit hash.
|
||||
ARG VERSION=master
|
||||
RUN go install -tags=prod \
|
||||
github.com/athanorlabs/atomic-swap/cmd/swapd@"${VERSION}" \
|
||||
github.com/athanorlabs/atomic-swap/cmd/swapcli@"${VERSION}"
|
||||
RUN /go/bin/swapd --version
|
||||
|
||||
FROM debian:bullseye-slim
|
||||
RUN apt-get update && apt-get install -y ca-certificates gosu
|
||||
|
||||
# /usr/local/bin has swapd, swapcli, monero-wallet-rpc and
|
||||
# docker-entrypoint.sh.
|
||||
COPY --from=builder /go/monero-wallet-rpc /usr/local/bin/
|
||||
COPY --from=builder /go/bin/ /usr/local/bin/
|
||||
COPY ./docker-entrypoint.sh /usr/local/bin/
|
||||
|
||||
VOLUME /data
|
||||
|
||||
# USER_UID and USER_GID are defined as ARGs so that, if desired, you can
|
||||
# build the container with a UID equal to some user outside the container
|
||||
# that will own the files in /data.
|
||||
ARG USER_UID=1000
|
||||
ARG USER_GID=$USER_UID
|
||||
RUN groupadd --gid "${USER_GID}" atomic && \
|
||||
useradd --no-log-init --home-dir /atomic-swap \
|
||||
--uid "${USER_UID}" --gid "${USER_GID}" -m atomic && \
|
||||
ln -s /data /atomic-swap/.atomicswap
|
||||
|
||||
# 9900 the default p2p port. swapd also listens to swapcli on 127.0.0.1:5000,
|
||||
# which is not accessible outside the container by default. You have 2 options
|
||||
# to interact with this RPC port:
|
||||
# (1) Use swapcli inside the container::
|
||||
# $ docker exec CONTAINER_NAME_OR_ID swapcli SUBCOMMAND ...
|
||||
# (2) Run the container with --network=host so 127.0.0.1:5000 is the same
|
||||
# port inside and outside of the container.
|
||||
EXPOSE 9900/udp
|
||||
EXPOSE 9900/tcp
|
||||
|
||||
# The swapd environment (dev, stagenet, mainnet) can be convigured via the
|
||||
# SWAPD_ENV environment variable or using swapd's --env flag (which takes
|
||||
# precidence). In docker, we use the environment variable to configure file
|
||||
# permissions of the correct directory in the data volume. The suggested
|
||||
# ways of working with this are:
|
||||
# (1) Set SWAPD_ENV variable and don't use the CLI flag
|
||||
# (2) Use swapd's --env=ENVIRONMENT CLI flag, but set SWAPD_ENV to the
|
||||
# identical environment or the empty string.
|
||||
ENV SWAPD_ENV=stagenet
|
||||
ENV SWAPD_ETH_ENDPOINT=https://rpc.sepolia.org/
|
||||
ENV SWAPD_LOG_LEVEL=info
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
|
||||
CMD ["swapd"]
|
||||
19
scripts/docker/build-docker-image.sh
Executable file
19
scripts/docker/build-docker-image.sh
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
IMAGE_NAME=atomic-swap
|
||||
|
||||
# VERSION can be "latest", a release tag, a hash or a branch name that does not contain slashes.
|
||||
# It must exist on github, local changes are not visible inside the container.
|
||||
VERSION=master
|
||||
|
||||
# Run docker build from the directory of this script
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
docker build \
|
||||
--build-arg "VERSION=${VERSION}" \
|
||||
--build-arg "USER_UID=$(id -u)" \
|
||||
--build-arg "USER_GID=$(id -g)" \
|
||||
. -t "${IMAGE_NAME}:${VERSION}"
|
||||
|
||||
echo "built ${IMAGE_NAME}:${VERSION}"
|
||||
43
scripts/docker/docker-entrypoint.sh
Executable file
43
scripts/docker/docker-entrypoint.sh
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
cmd="$(basename "${1}")"
|
||||
|
||||
#
|
||||
# If we are running swapd and SWAPD_ENV is set, so this script
|
||||
# knows where swapd will be writing data, we ensure that the
|
||||
# atomic user that runs swapd has access the directories where
|
||||
# the data is written.
|
||||
#
|
||||
if [[ "${cmd}" == 'swapd' ]] && [[ -n "${SWAPD_ENV}" ]]; then
|
||||
|
||||
if ! [[ "${SWAPD_ENV}" =~ ^dev|stagenet|mainnet$ ]]; then
|
||||
echo "invalid SWAPD_ENV value"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${*}:1}" =~ '--data-dir' ]]; then
|
||||
echo "Setting --data-dir is not recommended for dockerized swapd."
|
||||
echo "If required, unset SWAPD_ENV or override the entrypoint."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
data_dir="/data/${SWAPD_ENV}"
|
||||
|
||||
# create the directory if it does not exist
|
||||
if [[ ! -d "${data_dir}" ]]; then
|
||||
mkdir --mode=700 "${data_dir}"
|
||||
fi
|
||||
|
||||
# ensure the files are owned by the atomic user
|
||||
chown -R atomic.atomic "${data_dir}"
|
||||
fi
|
||||
|
||||
# Run swapd and swapcli commands as the atomic user for reduced
|
||||
# privileges.
|
||||
if [[ "${cmd}" == 'swapd' || "${cmd}" == 'swacli' ]]; then
|
||||
exec gosu atomic "$@"
|
||||
fi
|
||||
|
||||
exec "$@"
|
||||
26
scripts/docker/example-docker-run.sh
Executable file
26
scripts/docker/example-docker-run.sh
Executable file
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env bash
|
||||
set -ex
|
||||
|
||||
CONTAINER_NAME=atomic-stagenet
|
||||
IMAGE_NAME=atomic-swap
|
||||
TAG=master
|
||||
|
||||
# Setting NETWORK to "host" allows you to run swapcli commands on the local
|
||||
# host. You can also use "bridge", which requires all swapcli commands to
|
||||
# be run from inside the container.
|
||||
NETWORK=host
|
||||
|
||||
# Note: We mount one directory above what swapd considers its "data-dir".
|
||||
DATA_MOUNT_DIR="${HOME}/.atomicswap/docker"
|
||||
|
||||
# Pre-create the mounted directory, or docker will create it with root
|
||||
# as the owner.
|
||||
mkdir -p "${DATA_MOUNT_DIR}"
|
||||
|
||||
docker run --rm -v "${DATA_MOUNT_DIR}:/data" \
|
||||
--env SWAPD_ENV=stagenet \
|
||||
--env SWAPD_ETH_ENDPOINT="https://rpc.sepolia.org/" \
|
||||
--env SWAPD_LOG_LEVEL=debug \
|
||||
--network="${NETWORK}" \
|
||||
--name="${CONTAINER_NAME}" \
|
||||
"${IMAGE_NAME}:${TAG}"
|
||||
@@ -21,7 +21,7 @@ log_level=info # change to "debug" for more logs
|
||||
|
||||
./bin/swapd --env stagenet \
|
||||
"--log-level=${log_level}" \
|
||||
"--ethereum-endpoint=${ETHEREUM_ENDPOINT}" \
|
||||
"--eth-endpoint=${ETHEREUM_ENDPOINT}" \
|
||||
&>swapd.log &
|
||||
|
||||
echo "swapd start with logs in swapd.log"
|
||||
|
||||
Reference in New Issue
Block a user