mirror of
https://github.com/basicswap/basicswap.git
synced 2025-11-06 02:38:11 +01:00
Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f57888c455 | ||
|
|
e1528c9d63 | ||
|
|
48e5dcbcc9 | ||
|
|
b179667cc5 | ||
|
|
85bbccf82a | ||
|
|
20c0c372d0 | ||
|
|
80f0098a3d | ||
|
|
412770d399 | ||
|
|
1ee2db137b | ||
|
|
1c4f208d27 | ||
|
|
1601a57aed | ||
|
|
6cc54d9c61 | ||
|
|
7a3b41a11b | ||
|
|
871bdb918e | ||
|
|
cbcf90c492 | ||
|
|
2f1a9cbfae | ||
|
|
cd5af7032f | ||
|
|
18a444b071 | ||
|
|
8b09607083 | ||
|
|
fa74b9982c | ||
|
|
2c49d13aa0 | ||
|
|
3ad87df844 | ||
|
|
ede01d3fc8 | ||
|
|
a2830afc06 | ||
|
|
e03f32ea5f | ||
|
|
48e0cac5ab | ||
|
|
585bef6076 | ||
|
|
868dc27d64 | ||
|
|
1b7550ff76 | ||
|
|
91e285bf4a | ||
|
|
0580f9ebac | ||
|
|
02bd90053a | ||
|
|
0c620ea388 | ||
|
|
a2afd3f00f | ||
|
|
43048cffc0 | ||
|
|
3976b9c203 | ||
|
|
d5e35b8168 | ||
|
|
f7aadd1b9d | ||
|
|
beaff23ac3 | ||
|
|
e7a62a6a82 | ||
|
|
d2324ad097 | ||
|
|
f787bdb203 | ||
|
|
b64437db84 | ||
|
|
844db9c541 | ||
|
|
a51a895141 | ||
|
|
cddc4daf70 | ||
|
|
3ed6eca95f | ||
|
|
0edcf249aa | ||
|
|
89c60851ac | ||
|
|
d909115ea4 | ||
|
|
d47a69c7cb | ||
|
|
0c2c86070f | ||
|
|
a659eb3931 | ||
|
|
6153b76ec0 | ||
|
|
08c10bc69e | ||
|
|
8daa76f937 | ||
|
|
f4649d34b2 | ||
|
|
88c94c4acd | ||
|
|
a4683c8450 | ||
|
|
7bc9d64233 |
@@ -7,8 +7,8 @@ lint_task:
|
||||
- pip install codespell
|
||||
script:
|
||||
- flake8 --version
|
||||
- PYTHONWARNINGS="ignore" flake8 --ignore=E501,F841,W503 --exclude=basicswap/contrib,messages_pb2.py,.eggs,.tox
|
||||
- codespell --check-filenames --disable-colors --quiet-level=7 --ignore-words=tests/lint/spelling.ignore-words.txt -S .git,.eggs,.tox,gitianpubkeys,*.pyc,*basicswap/contrib,*mnemonics.py
|
||||
- PYTHONWARNINGS="ignore" flake8 --ignore=E501,F841,W503 --exclude=basicswap/contrib,basicswap/interface/contrib,messages_pb2.py,.eggs,.tox,bin/install_certifi.py
|
||||
- codespell --check-filenames --disable-colors --quiet-level=7 --ignore-words=tests/lint/spelling.ignore-words.txt -S .git,.eggs,.tox,pgp,*.pyc,*basicswap/contrib,*basicswap/interface/contrib,*mnemonics.py,bin/install_certifi.py
|
||||
|
||||
test_task:
|
||||
environment:
|
||||
@@ -24,8 +24,9 @@ test_task:
|
||||
- apt-get install -y wget python3-pip gnupg unzip protobuf-compiler automake libtool pkg-config
|
||||
- pip install tox pytest
|
||||
- python3 setup.py install
|
||||
- wget -O coincurve-anonswap.zip https://github.com/tecnovert/coincurve/archive/anonswap.zip
|
||||
- unzip coincurve-anonswap.zip
|
||||
- wget -O coincurve-anonswap.zip https://github.com/tecnovert/coincurve/archive/refs/tags/anonswap_v0.1.zip
|
||||
- unzip -d coincurve-anonswap coincurve-anonswap.zip
|
||||
- mv ./coincurve-anonswap/*/{.,}* ./coincurve-anonswap || true
|
||||
- cd coincurve-anonswap
|
||||
- python3 setup.py install --force
|
||||
bins_cache:
|
||||
|
||||
@@ -22,8 +22,9 @@ before_install:
|
||||
install:
|
||||
- travis_retry pip install tox pytest
|
||||
before_script:
|
||||
- wget -O coincurve-anonswap.zip https://github.com/tecnovert/coincurve/archive/anonswap.zip
|
||||
- unzip coincurve-anonswap.zip
|
||||
- wget -O coincurve-anonswap.zip https://github.com/tecnovert/coincurve/archive/refs/tags/anonswap_v0.1.zip
|
||||
- unzip -d coincurve-anonswap coincurve-anonswap.zip
|
||||
- mv ./coincurve-anonswap/*/{.,}* ./coincurve-anonswap || true
|
||||
- cd coincurve-anonswap
|
||||
- python3 setup.py install --force
|
||||
script:
|
||||
@@ -51,8 +52,8 @@ jobs:
|
||||
- travis_retry pip install codespell==1.15.0
|
||||
before_script:
|
||||
script:
|
||||
- PYTHONWARNINGS="ignore" flake8 --ignore=E501,F841,W503 --exclude=basicswap/contrib,messages_pb2.py,.eggs,.tox
|
||||
- codespell --check-filenames --disable-colors --quiet-level=7 --ignore-words=tests/lint/spelling.ignore-words.txt -S .git,.eggs,.tox,gitianpubkeys,*.pyc,*basicswap/contrib,*mnemonics.py
|
||||
- PYTHONWARNINGS="ignore" flake8 --ignore=E501,F841,W503 --exclude=basicswap/contrib,basicswap/interface/contrib,messages_pb2.py,.eggs,.tox,bin/install_certifi.py
|
||||
- codespell --check-filenames --disable-colors --quiet-level=7 --ignore-words=tests/lint/spelling.ignore-words.txt -S .git,.eggs,.tox,pgp,*.pyc,*basicswap/contrib,*basicswap/interface/contrib,*mnemonics.py,bin/install_certifi.py
|
||||
after_success:
|
||||
- echo "End lint"
|
||||
- stage: test
|
||||
|
||||
20
Dockerfile
20
Dockerfile
@@ -1,14 +1,24 @@
|
||||
FROM ubuntu:20.04
|
||||
FROM ubuntu:22.04
|
||||
|
||||
ENV LANG=C.UTF-8 \
|
||||
DEBIAN_FRONTEND=noninteractive \
|
||||
DATADIRS="/coindata"
|
||||
|
||||
RUN apt-get update; \
|
||||
apt-get install -y wget python3-pip gnupg unzip protobuf-compiler automake libtool pkg-config gosu tzdata;
|
||||
apt-get install -y wget python3-pip gnupg unzip make g++ autoconf automake libtool pkg-config gosu tzdata;
|
||||
|
||||
RUN wget -O coincurve-anonswap.zip https://github.com/tecnovert/coincurve/archive/anonswap.zip && \
|
||||
# Must install protoc directly as latest package is only on 3.12
|
||||
RUN wget -O protobuf_src.tar.gz https://github.com/protocolbuffers/protobuf/releases/download/v21.1/protobuf-python-4.21.1.tar.gz && \
|
||||
tar xvf protobuf_src.tar.gz && \
|
||||
cd protobuf-3.21.1 && \
|
||||
./configure --prefix=/usr && \
|
||||
make -j$(nproc) install && \
|
||||
ldconfig
|
||||
|
||||
ARG COINCURVE_VERSION=v0.1
|
||||
RUN wget -O coincurve-anonswap.zip https://github.com/tecnovert/coincurve/archive/refs/tags/anonswap_$COINCURVE_VERSION.zip && \
|
||||
unzip coincurve-anonswap.zip && \
|
||||
mv ./coincurve-anonswap_$COINCURVE_VERSION ./coincurve-anonswap && \
|
||||
cd coincurve-anonswap && \
|
||||
python3 setup.py install --force
|
||||
|
||||
@@ -24,8 +34,10 @@ RUN cd basicswap-master; \
|
||||
RUN useradd -ms /bin/bash swap_user && \
|
||||
mkdir /coindata && chown swap_user -R /coindata
|
||||
|
||||
# Expose html port
|
||||
# html port
|
||||
EXPOSE 12700
|
||||
# websocket port
|
||||
EXPOSE 11700
|
||||
|
||||
VOLUME /coindata
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
name = "basicswap"
|
||||
|
||||
__version__ = "0.0.32"
|
||||
__version__ = "0.11.36"
|
||||
|
||||
@@ -118,10 +118,12 @@ class BaseApp:
|
||||
return bytes(segwit_addr.decode(chainparams[coin_type][self.chain]['hrp'], addr)[1])
|
||||
|
||||
def callrpc(self, method, params=[], wallet=None):
|
||||
return callrpc(self.coin_clients[Coins.PART]['rpcport'], self.coin_clients[Coins.PART]['rpcauth'], method, params, wallet)
|
||||
cc = self.coin_clients[Coins.PART]
|
||||
return callrpc(cc['rpcport'], cc['rpcauth'], method, params, wallet, cc['rpchost'])
|
||||
|
||||
def callcoinrpc(self, coin, method, params=[], wallet=None):
|
||||
return callrpc(self.coin_clients[coin]['rpcport'], self.coin_clients[coin]['rpcauth'], method, params, wallet)
|
||||
cc = self.coin_clients[coin]
|
||||
return callrpc(cc['rpcport'], cc['rpcauth'], method, params, wallet, cc['rpchost'])
|
||||
|
||||
def calltx(self, cmd):
|
||||
bindir = self.coin_clients[Coins.PART]['bindir']
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2021 tecnovert
|
||||
# Copyright (c) 2021-2022 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
@@ -64,38 +64,40 @@ class SwapTypes(IntEnum):
|
||||
|
||||
|
||||
class OfferStates(IntEnum):
|
||||
OFFER_SENT = auto()
|
||||
OFFER_RECEIVED = auto()
|
||||
OFFER_ABANDONED = auto()
|
||||
OFFER_SENT = 1
|
||||
OFFER_RECEIVED = 2
|
||||
OFFER_ABANDONED = 3
|
||||
|
||||
|
||||
class BidStates(IntEnum):
|
||||
BID_SENT = auto()
|
||||
BID_RECEIVING = auto() # Partially received
|
||||
BID_RECEIVED = auto()
|
||||
BID_RECEIVING_ACC = auto() # Partially received accept message
|
||||
BID_ACCEPTED = auto() # BidAcceptMessage received/sent
|
||||
SWAP_INITIATED = auto() # Initiate txn validated
|
||||
SWAP_PARTICIPATING = auto() # Participate txn validated
|
||||
SWAP_COMPLETED = auto() # All swap txns spent
|
||||
XMR_SWAP_SCRIPT_COIN_LOCKED = auto()
|
||||
XMR_SWAP_HAVE_SCRIPT_COIN_SPEND_TX = auto()
|
||||
XMR_SWAP_NOSCRIPT_COIN_LOCKED = auto()
|
||||
XMR_SWAP_LOCK_RELEASED = auto()
|
||||
XMR_SWAP_SCRIPT_TX_REDEEMED = auto()
|
||||
XMR_SWAP_SCRIPT_TX_PREREFUND = auto() # script txo moved into pre-refund tx
|
||||
XMR_SWAP_NOSCRIPT_TX_REDEEMED = auto()
|
||||
XMR_SWAP_NOSCRIPT_TX_RECOVERED = auto()
|
||||
XMR_SWAP_FAILED_REFUNDED = auto()
|
||||
XMR_SWAP_FAILED_SWIPED = auto()
|
||||
XMR_SWAP_FAILED = auto()
|
||||
SWAP_DELAYING = auto()
|
||||
SWAP_TIMEDOUT = auto()
|
||||
BID_ABANDONED = auto() # Bid will no longer be processed
|
||||
BID_ERROR = auto() # An error occurred
|
||||
BID_STALLED_FOR_TEST = auto()
|
||||
BID_REJECTED = auto()
|
||||
BID_STATE_UNKNOWN = auto()
|
||||
BID_SENT = 1
|
||||
BID_RECEIVING = 2 # Partially received
|
||||
BID_RECEIVED = 3
|
||||
BID_RECEIVING_ACC = 4 # Partially received accept message
|
||||
BID_ACCEPTED = 5 # BidAcceptMessage received/sent
|
||||
SWAP_INITIATED = 6 # Initiate txn validated
|
||||
SWAP_PARTICIPATING = 7 # Participate txn validated
|
||||
SWAP_COMPLETED = 8 # All swap txns spent
|
||||
XMR_SWAP_SCRIPT_COIN_LOCKED = 9
|
||||
XMR_SWAP_HAVE_SCRIPT_COIN_SPEND_TX = 10
|
||||
XMR_SWAP_NOSCRIPT_COIN_LOCKED = 11
|
||||
XMR_SWAP_LOCK_RELEASED = 12
|
||||
XMR_SWAP_SCRIPT_TX_REDEEMED = 13
|
||||
XMR_SWAP_SCRIPT_TX_PREREFUND = 14 # script txo moved into pre-refund tx
|
||||
XMR_SWAP_NOSCRIPT_TX_REDEEMED = 15
|
||||
XMR_SWAP_NOSCRIPT_TX_RECOVERED = 16
|
||||
XMR_SWAP_FAILED_REFUNDED = 17
|
||||
XMR_SWAP_FAILED_SWIPED = 18
|
||||
XMR_SWAP_FAILED = 19
|
||||
SWAP_DELAYING = 20
|
||||
SWAP_TIMEDOUT = 21
|
||||
BID_ABANDONED = 22 # Bid will no longer be processed
|
||||
BID_ERROR = 23 # An error occurred
|
||||
BID_STALLED_FOR_TEST = 24
|
||||
BID_REJECTED = 25
|
||||
BID_STATE_UNKNOWN = 26
|
||||
XMR_SWAP_MSG_SCRIPT_LOCK_TX_SIGS = 27 # XmrBidLockTxSigsMessage
|
||||
XMR_SWAP_MSG_SCRIPT_LOCK_SPEND_TX = 28 # XmrBidLockSpendTxMessage
|
||||
|
||||
|
||||
class TxStates(IntEnum):
|
||||
@@ -122,7 +124,7 @@ class TxTypes(IntEnum):
|
||||
XMR_SWAP_B_LOCK = auto()
|
||||
|
||||
|
||||
class EventTypes(IntEnum):
|
||||
class ActionTypes(IntEnum):
|
||||
ACCEPT_BID = auto()
|
||||
ACCEPT_XMR_BID = auto()
|
||||
SIGN_XMR_SWAP_LOCK_TX_A = auto()
|
||||
@@ -132,6 +134,8 @@ class EventTypes(IntEnum):
|
||||
REDEEM_XMR_SWAP_LOCK_TX_A = auto() # Follower
|
||||
REDEEM_XMR_SWAP_LOCK_TX_B = auto() # Leader
|
||||
RECOVER_XMR_SWAP_LOCK_TX_B = auto()
|
||||
SEND_XMR_SWAP_LOCK_SPEND_MSG = auto()
|
||||
REDEEM_ITX = auto()
|
||||
|
||||
|
||||
class EventLogTypes(IntEnum):
|
||||
@@ -155,6 +159,9 @@ class EventLogTypes(IntEnum):
|
||||
LOCK_TX_B_SPEND_TX_PUBLISHED = auto()
|
||||
LOCK_TX_A_REFUND_TX_SEEN = auto()
|
||||
LOCK_TX_A_REFUND_SPEND_TX_SEEN = auto()
|
||||
ERROR = auto()
|
||||
AUTOMATION_CONSTRAINT = auto()
|
||||
AUTOMATION_ACCEPTING_BID = auto()
|
||||
|
||||
|
||||
class XmrSplitMsgTypes(IntEnum):
|
||||
@@ -163,12 +170,14 @@ class XmrSplitMsgTypes(IntEnum):
|
||||
|
||||
|
||||
class DebugTypes(IntEnum):
|
||||
NONE = 0
|
||||
BID_STOP_AFTER_COIN_A_LOCK = auto()
|
||||
BID_DONT_SPEND_COIN_A_LOCK_REFUND = auto()
|
||||
CREATE_INVALID_COIN_B_LOCK = auto()
|
||||
BUYER_STOP_AFTER_ITX = auto()
|
||||
MAKE_INVALID_PTX = auto()
|
||||
DONT_SPEND_ITX = auto()
|
||||
SKIP_LOCK_TX_REFUND = auto()
|
||||
|
||||
|
||||
def strOfferState(state):
|
||||
@@ -181,6 +190,13 @@ def strOfferState(state):
|
||||
return 'Unknown'
|
||||
|
||||
|
||||
class NotificationTypes(IntEnum):
|
||||
NONE = 0
|
||||
OFFER_RECEIVED = auto()
|
||||
BID_RECEIVED = auto()
|
||||
BID_ACCEPTED = auto()
|
||||
|
||||
|
||||
def strBidState(state):
|
||||
if state == BidStates.BID_SENT:
|
||||
return 'Sent'
|
||||
@@ -232,6 +248,10 @@ def strBidState(state):
|
||||
return 'Failed'
|
||||
if state == BidStates.SWAP_DELAYING:
|
||||
return 'Delaying'
|
||||
if state == BidStates.XMR_SWAP_MSG_SCRIPT_LOCK_TX_SIGS:
|
||||
return 'Exchanged script lock tx sigs msg'
|
||||
if state == BidStates.XMR_SWAP_MSG_SCRIPT_LOCK_SPEND_TX:
|
||||
return 'Exchanged script lock spend tx msg'
|
||||
return 'Unknown' + ' ' + str(state)
|
||||
|
||||
|
||||
@@ -331,6 +351,12 @@ def describeEventEntry(event_type, event_msg):
|
||||
return 'Lock tx A refund spend tx seen in chain'
|
||||
if event_type == EventLogTypes.SYSTEM_WARNING:
|
||||
return 'Warning: ' + event_msg
|
||||
if event_type == EventLogTypes.ERROR:
|
||||
return 'Error: ' + event_msg
|
||||
if event_type == EventLogTypes.AUTOMATION_CONSTRAINT:
|
||||
return 'Failed auto accepting'
|
||||
if event_type == EventLogTypes.AUTOMATION_ACCEPTING_BID:
|
||||
return 'Auto accepting'
|
||||
|
||||
|
||||
def getVoutByAddress(txjs, p2sh):
|
||||
@@ -381,6 +407,10 @@ def getLastBidState(packed_states):
|
||||
|
||||
|
||||
def isActiveBidState(state):
|
||||
if state >= BidStates.BID_ACCEPTED and state < BidStates.SWAP_COMPLETED:
|
||||
return True
|
||||
if state == BidStates.SWAP_DELAYING:
|
||||
return True
|
||||
if state == BidStates.XMR_SWAP_HAVE_SCRIPT_COIN_SPEND_TX:
|
||||
return True
|
||||
if state == BidStates.XMR_SWAP_SCRIPT_COIN_LOCKED:
|
||||
@@ -395,4 +425,8 @@ def isActiveBidState(state):
|
||||
return True
|
||||
if state == BidStates.XMR_SWAP_SCRIPT_TX_PREREFUND:
|
||||
return True
|
||||
if state == BidStates.XMR_SWAP_MSG_SCRIPT_LOCK_TX_SIGS:
|
||||
return True
|
||||
if state == BidStates.XMR_SWAP_MSG_SCRIPT_LOCK_SPEND_TX:
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -26,6 +26,9 @@ class Coins(IntEnum):
|
||||
XMR = 6
|
||||
PART_BLIND = 7
|
||||
PART_ANON = 8
|
||||
# ZANO = 9
|
||||
# NDAU = 10
|
||||
PIVX = 11
|
||||
|
||||
|
||||
chainparams = {
|
||||
@@ -116,7 +119,8 @@ chainparams = {
|
||||
'mainnet': {
|
||||
'rpcport': 9332,
|
||||
'pubkey_address': 48,
|
||||
'script_address': 50,
|
||||
'script_address': 5,
|
||||
'script_address2': 50,
|
||||
'key_prefix': 176,
|
||||
'hrp': 'ltc',
|
||||
'bip44': 2,
|
||||
@@ -126,7 +130,8 @@ chainparams = {
|
||||
'testnet': {
|
||||
'rpcport': 19332,
|
||||
'pubkey_address': 111,
|
||||
'script_address': 58,
|
||||
'script_address': 196,
|
||||
'script_address2': 58,
|
||||
'key_prefix': 239,
|
||||
'hrp': 'tltc',
|
||||
'bip44': 1,
|
||||
@@ -137,7 +142,8 @@ chainparams = {
|
||||
'regtest': {
|
||||
'rpcport': 19443,
|
||||
'pubkey_address': 111,
|
||||
'script_address': 58,
|
||||
'script_address': 196,
|
||||
'script_address2': 58,
|
||||
'key_prefix': 239,
|
||||
'hrp': 'rltc',
|
||||
'bip44': 1,
|
||||
@@ -203,10 +209,45 @@ chainparams = {
|
||||
'min_amount': 100000,
|
||||
'max_amount': 10000 * XMR_COIN,
|
||||
}
|
||||
}
|
||||
},
|
||||
Coins.PIVX: {
|
||||
'name': 'pivx',
|
||||
'ticker': 'PIVX',
|
||||
'message_magic': 'DarkNet Signed Message:\n',
|
||||
'blocks_target': 60 * 1,
|
||||
'decimal_places': 8,
|
||||
'has_csv': False,
|
||||
'has_segwit': False,
|
||||
'mainnet': {
|
||||
'rpcport': 51473,
|
||||
'pubkey_address': 30,
|
||||
'script_address': 13,
|
||||
'key_prefix': 212,
|
||||
'bip44': 119,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
},
|
||||
'testnet': {
|
||||
'rpcport': 51475,
|
||||
'pubkey_address': 139,
|
||||
'script_address': 19,
|
||||
'key_prefix': 239,
|
||||
'bip44': 1,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
'name': 'testnet4',
|
||||
},
|
||||
'regtest': {
|
||||
'rpcport': 51477,
|
||||
'pubkey_address': 139,
|
||||
'script_address': 19,
|
||||
'key_prefix': 239,
|
||||
'bip44': 1,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
ticker_map = {}
|
||||
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2019 tecnovert
|
||||
# Copyright (c) 2019-2022 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
import os
|
||||
|
||||
CONFIG_FILENAME = 'basicswap.json'
|
||||
DEFAULT_DATADIR = '~/.basicswap'
|
||||
BASICSWAP_DATADIR = os.getenv('BASICSWAP_DATADIR', '~/.basicswap')
|
||||
DEFAULT_ALLOW_CORS = False
|
||||
TEST_DATADIRS = os.path.expanduser(os.getenv('DATADIRS', '/tmp/basicswap'))
|
||||
DEFAULT_TEST_BINDIR = os.path.expanduser(os.getenv('DEFAULT_TEST_BINDIR', '~/tmp/bin'))
|
||||
@@ -36,3 +36,8 @@ NAMECOIN_TX = os.getenv('NAMECOIN_TX', 'namecoin-tx' + bin_suffix)
|
||||
XMR_BINDIR = os.path.expanduser(os.getenv('XMR_BINDIR', os.path.join(DEFAULT_TEST_BINDIR, 'monero')))
|
||||
XMRD = os.getenv('XMRD', 'monerod' + bin_suffix)
|
||||
XMR_WALLET_RPC = os.getenv('XMR_WALLET_RPC', 'monero-wallet-rpc' + bin_suffix)
|
||||
|
||||
PIVX_BINDIR = os.path.expanduser(os.getenv('PIVX_BINDIR', os.path.join(DEFAULT_TEST_BINDIR, 'pivx')))
|
||||
PIVXD = os.getenv('PIVXD', 'pivxd' + bin_suffix)
|
||||
PIVX_CLI = os.getenv('PIVX_CLI', 'pivx-cli' + bin_suffix)
|
||||
PIVX_TX = os.getenv('PIVX_TX', 'pivx-tx' + bin_suffix)
|
||||
|
||||
@@ -10,6 +10,7 @@ import hashlib
|
||||
import struct
|
||||
import unittest
|
||||
from typing import List, Dict
|
||||
from basicswap.util.crypto import ripemd160
|
||||
|
||||
from .messages import (
|
||||
CTransaction,
|
||||
@@ -25,7 +26,7 @@ MAX_SCRIPT_ELEMENT_SIZE = 520
|
||||
OPCODE_NAMES = {} # type: Dict[CScriptOp, str]
|
||||
|
||||
def hash160(s):
|
||||
return hashlib.new('ripemd160', sha256(s)).digest()
|
||||
return ripemd160(sha256(s))
|
||||
|
||||
def bn2vch(v):
|
||||
"""Convert number to bitcoin-specific little endian format."""
|
||||
|
||||
1
basicswap/contrib/websocket_server/__init__.py
Normal file
1
basicswap/contrib/websocket_server/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .websocket_server import *
|
||||
38
basicswap/contrib/websocket_server/thread.py
Normal file
38
basicswap/contrib/websocket_server/thread.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import threading
|
||||
|
||||
|
||||
class ThreadWithLoggedException(threading.Thread):
|
||||
"""
|
||||
Similar to Thread but will log exceptions to passed logger.
|
||||
|
||||
Args:
|
||||
logger: Logger instance used to log any exception in child thread
|
||||
|
||||
Exception is also reachable via <thread>.exception from the main thread.
|
||||
"""
|
||||
|
||||
DIVIDER = "*"*80
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
try:
|
||||
self.logger = kwargs.pop("logger")
|
||||
except KeyError:
|
||||
raise Exception("Missing 'logger' in kwargs")
|
||||
super().__init__(*args, **kwargs)
|
||||
self.exception = None
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
if self._target is not None:
|
||||
self._target(*self._args, **self._kwargs)
|
||||
except Exception as exception:
|
||||
thread = threading.current_thread()
|
||||
self.exception = exception
|
||||
self.logger.exception(f"{self.DIVIDER}\nException in child thread {thread}: {exception}\n{self.DIVIDER}")
|
||||
finally:
|
||||
del self._target, self._args, self._kwargs
|
||||
|
||||
|
||||
class WebsocketServerThread(ThreadWithLoggedException):
|
||||
"""Dummy wrapper to make debug messages a bit more readable"""
|
||||
pass
|
||||
495
basicswap/contrib/websocket_server/websocket_server.py
Normal file
495
basicswap/contrib/websocket_server/websocket_server.py
Normal file
@@ -0,0 +1,495 @@
|
||||
# Author: Johan Hanssen Seferidis
|
||||
# License: MIT
|
||||
|
||||
import sys
|
||||
import struct
|
||||
import ssl
|
||||
from base64 import b64encode
|
||||
from hashlib import sha1
|
||||
import logging
|
||||
from socket import error as SocketError
|
||||
import errno
|
||||
import threading
|
||||
from socketserver import ThreadingMixIn, TCPServer, StreamRequestHandler
|
||||
|
||||
from .thread import WebsocketServerThread
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logging.basicConfig()
|
||||
|
||||
'''
|
||||
+-+-+-+-+-------+-+-------------+-------------------------------+
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-------+-+-------------+-------------------------------+
|
||||
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|
||||
|I|S|S|S| (4) |A| (7) | (16/64) |
|
||||
|N|V|V|V| |S| | (if payload len==126/127) |
|
||||
| |1|2|3| |K| | |
|
||||
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|
||||
| Extended payload length continued, if payload len == 127 |
|
||||
+ - - - - - - - - - - - - - - - +-------------------------------+
|
||||
| Payload Data continued ... |
|
||||
+---------------------------------------------------------------+
|
||||
'''
|
||||
|
||||
FIN = 0x80
|
||||
OPCODE = 0x0f
|
||||
MASKED = 0x80
|
||||
PAYLOAD_LEN = 0x7f
|
||||
PAYLOAD_LEN_EXT16 = 0x7e
|
||||
PAYLOAD_LEN_EXT64 = 0x7f
|
||||
|
||||
OPCODE_CONTINUATION = 0x0
|
||||
OPCODE_TEXT = 0x1
|
||||
OPCODE_BINARY = 0x2
|
||||
OPCODE_CLOSE_CONN = 0x8
|
||||
OPCODE_PING = 0x9
|
||||
OPCODE_PONG = 0xA
|
||||
|
||||
CLOSE_STATUS_NORMAL = 1000
|
||||
DEFAULT_CLOSE_REASON = bytes('', encoding='utf-8')
|
||||
|
||||
|
||||
class API():
|
||||
|
||||
def run_forever(self, threaded=False):
|
||||
return self._run_forever(threaded)
|
||||
|
||||
def new_client(self, client, server):
|
||||
pass
|
||||
|
||||
def client_left(self, client, server):
|
||||
pass
|
||||
|
||||
def message_received(self, client, server, message):
|
||||
pass
|
||||
|
||||
def set_fn_new_client(self, fn):
|
||||
self.new_client = fn
|
||||
|
||||
def set_fn_client_left(self, fn):
|
||||
self.client_left = fn
|
||||
|
||||
def set_fn_message_received(self, fn):
|
||||
self.message_received = fn
|
||||
|
||||
def send_message(self, client, msg):
|
||||
self._unicast(client, msg)
|
||||
|
||||
def send_message_to_all(self, msg):
|
||||
self._multicast(msg)
|
||||
|
||||
def deny_new_connections(self, status=CLOSE_STATUS_NORMAL, reason=DEFAULT_CLOSE_REASON):
|
||||
self._deny_new_connections(status, reason)
|
||||
|
||||
def allow_new_connections(self):
|
||||
self._allow_new_connections()
|
||||
|
||||
def shutdown_gracefully(self, status=CLOSE_STATUS_NORMAL, reason=DEFAULT_CLOSE_REASON):
|
||||
self._shutdown_gracefully(status, reason)
|
||||
|
||||
def shutdown_abruptly(self):
|
||||
self._shutdown_abruptly()
|
||||
|
||||
def disconnect_clients_gracefully(self, status=CLOSE_STATUS_NORMAL, reason=DEFAULT_CLOSE_REASON):
|
||||
self._disconnect_clients_gracefully(status, reason)
|
||||
|
||||
def disconnect_clients_abruptly(self):
|
||||
self._disconnect_clients_abruptly()
|
||||
|
||||
|
||||
class WebsocketServer(ThreadingMixIn, TCPServer, API):
|
||||
"""
|
||||
A websocket server waiting for clients to connect.
|
||||
|
||||
Args:
|
||||
port(int): Port to bind to
|
||||
host(str): Hostname or IP to listen for connections. By default 127.0.0.1
|
||||
is being used. To accept connections from any client, you should use
|
||||
0.0.0.0.
|
||||
loglevel: Logging level from logging module to use for logging. By default
|
||||
warnings and errors are being logged.
|
||||
|
||||
Properties:
|
||||
clients(list): A list of connected clients. A client is a dictionary
|
||||
like below.
|
||||
{
|
||||
'id' : id,
|
||||
'handler' : handler,
|
||||
'address' : (addr, port)
|
||||
}
|
||||
"""
|
||||
|
||||
allow_reuse_address = True
|
||||
daemon_threads = True # comment to keep threads alive until finished
|
||||
|
||||
def __init__(self, host='127.0.0.1', port=0, loglevel=logging.WARNING, key=None, cert=None):
|
||||
logger.setLevel(loglevel)
|
||||
TCPServer.__init__(self, (host, port), WebSocketHandler)
|
||||
self.host = host
|
||||
self.port = self.socket.getsockname()[1]
|
||||
self.url = f'ws://{self.host}:{self.port}/'
|
||||
|
||||
self.key = key
|
||||
self.cert = cert
|
||||
|
||||
self.clients = []
|
||||
self.id_counter = 0
|
||||
self.thread = None
|
||||
|
||||
self._deny_clients = False
|
||||
|
||||
def _run_forever(self, threaded):
|
||||
cls_name = self.__class__.__name__
|
||||
try:
|
||||
logger.info("Listening on port %d for clients.." % self.port)
|
||||
if threaded:
|
||||
self.daemon = True
|
||||
self.thread = WebsocketServerThread(target=super().serve_forever, daemon=True, logger=logger)
|
||||
if sys.version_info[0] > 3 or (sys.version_info[0] == 3 and sys.version_info[1] >= 10):
|
||||
logger.info(f"Starting {cls_name} on thread {self.thread.name}.")
|
||||
else:
|
||||
logger.info(f"Starting {cls_name} on thread {self.thread.getName()}.")
|
||||
self.thread.start()
|
||||
else:
|
||||
self.thread = threading.current_thread()
|
||||
logger.info(f"Starting {cls_name} on main thread.")
|
||||
super().serve_forever()
|
||||
except KeyboardInterrupt:
|
||||
self.server_close()
|
||||
logger.info("Server terminated.")
|
||||
except Exception as e:
|
||||
logger.error(str(e), exc_info=True)
|
||||
sys.exit(1)
|
||||
|
||||
def _message_received_(self, handler, msg):
|
||||
self.message_received(self.handler_to_client(handler), self, msg)
|
||||
|
||||
def _ping_received_(self, handler, msg):
|
||||
handler.send_pong(msg)
|
||||
|
||||
def _pong_received_(self, handler, msg):
|
||||
pass
|
||||
|
||||
def _new_client_(self, handler):
|
||||
if self._deny_clients:
|
||||
status = self._deny_clients["status"]
|
||||
reason = self._deny_clients["reason"]
|
||||
handler.send_close(status, reason)
|
||||
self._terminate_client_handler(handler)
|
||||
return
|
||||
|
||||
self.id_counter += 1
|
||||
client = {
|
||||
'id': self.id_counter,
|
||||
'handler': handler,
|
||||
'address': handler.client_address
|
||||
}
|
||||
self.clients.append(client)
|
||||
self.new_client(client, self)
|
||||
|
||||
def _client_left_(self, handler):
|
||||
client = self.handler_to_client(handler)
|
||||
self.client_left(client, self)
|
||||
if client in self.clients:
|
||||
self.clients.remove(client)
|
||||
|
||||
def _unicast(self, receiver_client, msg):
|
||||
receiver_client['handler'].send_message(msg)
|
||||
|
||||
def _multicast(self, msg):
|
||||
for client in self.clients:
|
||||
self._unicast(client, msg)
|
||||
|
||||
def handler_to_client(self, handler):
|
||||
for client in self.clients:
|
||||
if client['handler'] == handler:
|
||||
return client
|
||||
|
||||
def _terminate_client_handler(self, handler):
|
||||
handler.keep_alive = False
|
||||
handler.finish()
|
||||
handler.connection.close()
|
||||
|
||||
def _terminate_client_handlers(self):
|
||||
"""
|
||||
Ensures request handler for each client is terminated correctly
|
||||
"""
|
||||
for client in self.clients:
|
||||
self._terminate_client_handler(client["handler"])
|
||||
|
||||
def _shutdown_gracefully(self, status=CLOSE_STATUS_NORMAL, reason=DEFAULT_CLOSE_REASON):
|
||||
"""
|
||||
Send a CLOSE handshake to all connected clients before terminating server
|
||||
"""
|
||||
self.keep_alive = False
|
||||
self._disconnect_clients_gracefully(status, reason)
|
||||
self.server_close()
|
||||
self.shutdown()
|
||||
|
||||
def _shutdown_abruptly(self):
|
||||
"""
|
||||
Terminate server without sending a CLOSE handshake
|
||||
"""
|
||||
self.keep_alive = False
|
||||
self._disconnect_clients_abruptly()
|
||||
self.server_close()
|
||||
self.shutdown()
|
||||
|
||||
def _disconnect_clients_gracefully(self, status=CLOSE_STATUS_NORMAL, reason=DEFAULT_CLOSE_REASON):
|
||||
"""
|
||||
Terminate clients gracefully without shutting down the server
|
||||
"""
|
||||
for client in self.clients:
|
||||
client["handler"].send_close(status, reason)
|
||||
self._terminate_client_handlers()
|
||||
|
||||
def _disconnect_clients_abruptly(self):
|
||||
"""
|
||||
Terminate clients abruptly (no CLOSE handshake) without shutting down the server
|
||||
"""
|
||||
self._terminate_client_handlers()
|
||||
|
||||
def _deny_new_connections(self, status, reason):
|
||||
self._deny_clients = {
|
||||
"status": status,
|
||||
"reason": reason,
|
||||
}
|
||||
|
||||
def _allow_new_connections(self):
|
||||
self._deny_clients = False
|
||||
|
||||
|
||||
class WebSocketHandler(StreamRequestHandler):
|
||||
|
||||
def __init__(self, socket, addr, server):
|
||||
self.server = server
|
||||
self.timeout = 1000 # Must set a timeout or rfile.read timesout in the tests
|
||||
assert not hasattr(self, "_send_lock"), "_send_lock already exists"
|
||||
self._send_lock = threading.Lock()
|
||||
if server.key and server.cert:
|
||||
try:
|
||||
socket = ssl.wrap_socket(socket, server_side=True, certfile=server.cert, keyfile=server.key)
|
||||
except: # Not sure which exception it throws if the key/cert isn't found
|
||||
logger.warning("SSL not available (are the paths {} and {} correct for the key and cert?)".format(server.key, server.cert))
|
||||
StreamRequestHandler.__init__(self, socket, addr, server)
|
||||
|
||||
def setup(self):
|
||||
StreamRequestHandler.setup(self)
|
||||
self.keep_alive = True
|
||||
self.handshake_done = False
|
||||
self.valid_client = False
|
||||
|
||||
def handle(self):
|
||||
while self.keep_alive:
|
||||
if not self.handshake_done:
|
||||
self.handshake()
|
||||
elif self.valid_client:
|
||||
self.read_next_message()
|
||||
|
||||
def read_bytes(self, num):
|
||||
return self.rfile.read(num)
|
||||
|
||||
def read_next_message(self):
|
||||
try:
|
||||
b1, b2 = self.read_bytes(2)
|
||||
except TimeoutError:
|
||||
return
|
||||
except SocketError as e: # to be replaced with ConnectionResetError for py3
|
||||
if e.errno == errno.ECONNRESET:
|
||||
logger.info("Client closed connection.")
|
||||
self.keep_alive = 0
|
||||
return
|
||||
b1, b2 = 0, 0
|
||||
except ValueError as e:
|
||||
b1, b2 = 0, 0
|
||||
|
||||
fin = b1 & FIN
|
||||
opcode = b1 & OPCODE
|
||||
masked = b2 & MASKED
|
||||
payload_length = b2 & PAYLOAD_LEN
|
||||
|
||||
if opcode == OPCODE_CLOSE_CONN:
|
||||
logger.info("Client asked to close connection.")
|
||||
self.keep_alive = 0
|
||||
return
|
||||
if not masked:
|
||||
logger.warning("Client must always be masked.")
|
||||
self.keep_alive = 0
|
||||
return
|
||||
if opcode == OPCODE_CONTINUATION:
|
||||
logger.warning("Continuation frames are not supported.")
|
||||
return
|
||||
elif opcode == OPCODE_BINARY:
|
||||
logger.warning("Binary frames are not supported.")
|
||||
return
|
||||
elif opcode == OPCODE_TEXT:
|
||||
opcode_handler = self.server._message_received_
|
||||
elif opcode == OPCODE_PING:
|
||||
opcode_handler = self.server._ping_received_
|
||||
elif opcode == OPCODE_PONG:
|
||||
opcode_handler = self.server._pong_received_
|
||||
else:
|
||||
logger.warning("Unknown opcode %#x." % opcode)
|
||||
self.keep_alive = 0
|
||||
return
|
||||
|
||||
if payload_length == 126:
|
||||
payload_length = struct.unpack(">H", self.rfile.read(2))[0]
|
||||
elif payload_length == 127:
|
||||
payload_length = struct.unpack(">Q", self.rfile.read(8))[0]
|
||||
|
||||
masks = self.read_bytes(4)
|
||||
message_bytes = bytearray()
|
||||
for message_byte in self.read_bytes(payload_length):
|
||||
message_byte ^= masks[len(message_bytes) % 4]
|
||||
message_bytes.append(message_byte)
|
||||
opcode_handler(self, message_bytes.decode('utf8'))
|
||||
|
||||
def send_message(self, message):
|
||||
self.send_text(message)
|
||||
|
||||
def send_pong(self, message):
|
||||
self.send_text(message, OPCODE_PONG)
|
||||
|
||||
def send_close(self, status=CLOSE_STATUS_NORMAL, reason=DEFAULT_CLOSE_REASON):
|
||||
"""
|
||||
Send CLOSE to client
|
||||
|
||||
Args:
|
||||
status: Status as defined in https://datatracker.ietf.org/doc/html/rfc6455#section-7.4.1
|
||||
reason: Text with reason of closing the connection
|
||||
"""
|
||||
if status < CLOSE_STATUS_NORMAL or status > 1015:
|
||||
raise Exception(f"CLOSE status must be between 1000 and 1015, got {status}")
|
||||
|
||||
header = bytearray()
|
||||
payload = struct.pack('!H', status) + reason
|
||||
payload_length = len(payload)
|
||||
assert payload_length <= 125, "We only support short closing reasons at the moment"
|
||||
|
||||
# Send CLOSE with status & reason
|
||||
header.append(FIN | OPCODE_CLOSE_CONN)
|
||||
header.append(payload_length)
|
||||
with self._send_lock:
|
||||
self.request.send(header + payload)
|
||||
|
||||
def send_text(self, message, opcode=OPCODE_TEXT):
|
||||
"""
|
||||
Important: Fragmented(=continuation) messages are not supported since
|
||||
their usage cases are limited - when we don't know the payload length.
|
||||
"""
|
||||
|
||||
# Validate message
|
||||
if isinstance(message, bytes):
|
||||
message = try_decode_UTF8(message) # this is slower but ensures we have UTF-8
|
||||
if not message:
|
||||
logger.warning("Can\'t send message, message is not valid UTF-8")
|
||||
return False
|
||||
elif not isinstance(message, str):
|
||||
logger.warning('Can\'t send message, message has to be a string or bytes. Got %s' % type(message))
|
||||
return False
|
||||
|
||||
header = bytearray()
|
||||
payload = encode_to_UTF8(message)
|
||||
payload_length = len(payload)
|
||||
|
||||
# Normal payload
|
||||
if payload_length <= 125:
|
||||
header.append(FIN | opcode)
|
||||
header.append(payload_length)
|
||||
|
||||
# Extended payload
|
||||
elif payload_length >= 126 and payload_length <= 65535:
|
||||
header.append(FIN | opcode)
|
||||
header.append(PAYLOAD_LEN_EXT16)
|
||||
header.extend(struct.pack(">H", payload_length))
|
||||
|
||||
# Huge extended payload
|
||||
elif payload_length < 18446744073709551616:
|
||||
header.append(FIN | opcode)
|
||||
header.append(PAYLOAD_LEN_EXT64)
|
||||
header.extend(struct.pack(">Q", payload_length))
|
||||
|
||||
else:
|
||||
raise Exception("Message is too big. Consider breaking it into chunks.")
|
||||
return
|
||||
|
||||
with self._send_lock:
|
||||
self.request.send(header + payload)
|
||||
|
||||
def read_http_headers(self):
|
||||
headers = {}
|
||||
# first line should be HTTP GET
|
||||
http_get = self.rfile.readline().decode().strip()
|
||||
assert http_get.upper().startswith('GET')
|
||||
# remaining should be headers
|
||||
while True:
|
||||
header = self.rfile.readline().decode().strip()
|
||||
if not header:
|
||||
break
|
||||
head, value = header.split(':', 1)
|
||||
headers[head.lower().strip()] = value.strip()
|
||||
return headers
|
||||
|
||||
def handshake(self):
|
||||
headers = self.read_http_headers()
|
||||
|
||||
try:
|
||||
assert headers['upgrade'].lower() == 'websocket'
|
||||
except AssertionError:
|
||||
self.keep_alive = False
|
||||
return
|
||||
|
||||
try:
|
||||
key = headers['sec-websocket-key']
|
||||
except KeyError:
|
||||
logger.warning("Client tried to connect but was missing a key")
|
||||
self.keep_alive = False
|
||||
return
|
||||
|
||||
response = self.make_handshake_response(key)
|
||||
with self._send_lock:
|
||||
self.handshake_done = self.request.send(response.encode())
|
||||
self.valid_client = True
|
||||
self.server._new_client_(self)
|
||||
|
||||
@classmethod
|
||||
def make_handshake_response(cls, key):
|
||||
return \
|
||||
'HTTP/1.1 101 Switching Protocols\r\n'\
|
||||
'Upgrade: websocket\r\n' \
|
||||
'Connection: Upgrade\r\n' \
|
||||
'Sec-WebSocket-Accept: %s\r\n' \
|
||||
'\r\n' % cls.calculate_response_key(key)
|
||||
|
||||
@classmethod
|
||||
def calculate_response_key(cls, key):
|
||||
GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
|
||||
hash = sha1(key.encode() + GUID.encode())
|
||||
response_key = b64encode(hash.digest()).strip()
|
||||
return response_key.decode('ASCII')
|
||||
|
||||
def finish(self):
|
||||
self.server._client_left_(self)
|
||||
|
||||
|
||||
def encode_to_UTF8(data):
|
||||
try:
|
||||
return data.encode('UTF-8')
|
||||
except UnicodeEncodeError as e:
|
||||
logger.error("Could not encode data to UTF-8 -- %s" % e)
|
||||
return False
|
||||
except Exception as e:
|
||||
raise(e)
|
||||
return False
|
||||
|
||||
|
||||
def try_decode_UTF8(data):
|
||||
try:
|
||||
return data.decode('utf-8')
|
||||
except UnicodeDecodeError:
|
||||
return False
|
||||
except Exception as e:
|
||||
raise(e)
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2019-2021 tecnovert
|
||||
# Copyright (c) 2019-2022 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
@@ -12,13 +12,26 @@ from enum import IntEnum, auto
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
|
||||
|
||||
CURRENT_DB_VERSION = 13
|
||||
CURRENT_DB_VERSION = 15
|
||||
CURRENT_DB_DATA_VERSION = 2
|
||||
Base = declarative_base()
|
||||
|
||||
|
||||
class TableTypes(IntEnum):
|
||||
class Concepts(IntEnum):
|
||||
OFFER = auto()
|
||||
BID = auto()
|
||||
NETWORK_MESSAGE = auto()
|
||||
AUTOMATION = auto()
|
||||
|
||||
|
||||
def strConcepts(state):
|
||||
if state == Concepts.OFFER:
|
||||
return 'Offer'
|
||||
if state == Concepts.BID:
|
||||
return 'Bid'
|
||||
if state == Concepts.NETWORK_MESSAGE:
|
||||
return 'Network Message'
|
||||
return 'Unknown'
|
||||
|
||||
|
||||
class DBKVInt(Base):
|
||||
@@ -236,16 +249,16 @@ class SmsgAddress(Base):
|
||||
note = sa.Column(sa.String)
|
||||
|
||||
|
||||
class EventQueue(Base):
|
||||
__tablename__ = 'eventqueue'
|
||||
class Action(Base):
|
||||
__tablename__ = 'actions'
|
||||
|
||||
event_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
action_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
active_ind = sa.Column(sa.Integer)
|
||||
created_at = sa.Column(sa.BigInteger)
|
||||
trigger_at = sa.Column(sa.BigInteger)
|
||||
linked_id = sa.Column(sa.LargeBinary)
|
||||
event_type = sa.Column(sa.Integer)
|
||||
event_data = sa.Column(sa.LargeBinary)
|
||||
action_type = sa.Column(sa.Integer)
|
||||
action_data = sa.Column(sa.LargeBinary)
|
||||
|
||||
|
||||
class EventLog(Base):
|
||||
@@ -288,7 +301,8 @@ class XmrSwap(Base):
|
||||
bid_accept_msg_id3 = sa.Column(sa.LargeBinary)
|
||||
|
||||
coin_a_lock_tx_sigs_l_msg_id = sa.Column(sa.LargeBinary) # MSG3L F -> L
|
||||
coin_a_lock_refund_spend_tx_msg_id = sa.Column(sa.LargeBinary) # MSG4F L -> F
|
||||
coin_a_lock_spend_tx_msg_id = sa.Column(sa.LargeBinary) # MSG4F L -> F
|
||||
coin_a_lock_release_msg_id = sa.Column(sa.LargeBinary) # MSG5F L -> F
|
||||
|
||||
contract_count = sa.Column(sa.Integer)
|
||||
|
||||
@@ -372,12 +386,11 @@ class Wallets(Base):
|
||||
__tablename__ = 'wallets'
|
||||
|
||||
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
active_ind = sa.Column(sa.Integer)
|
||||
coin_id = sa.Column(sa.Integer)
|
||||
wallet_name = sa.Column(sa.String)
|
||||
wallet_data = sa.Column(sa.String)
|
||||
balance_type = sa.Column(sa.Integer)
|
||||
amount = sa.Column(sa.BigInteger)
|
||||
updated_at = sa.Column(sa.BigInteger)
|
||||
created_at = sa.Column(sa.BigInteger)
|
||||
|
||||
|
||||
@@ -385,6 +398,7 @@ class KnownIdentity(Base):
|
||||
__tablename__ = 'knownidentities'
|
||||
|
||||
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
active_ind = sa.Column(sa.Integer)
|
||||
address = sa.Column(sa.String)
|
||||
label = sa.Column(sa.String)
|
||||
publickey = sa.Column(sa.LargeBinary)
|
||||
@@ -397,3 +411,64 @@ class KnownIdentity(Base):
|
||||
note = sa.Column(sa.String)
|
||||
updated_at = sa.Column(sa.BigInteger)
|
||||
created_at = sa.Column(sa.BigInteger)
|
||||
|
||||
|
||||
class AutomationStrategy(Base):
|
||||
__tablename__ = 'automationstrategies'
|
||||
|
||||
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
active_ind = sa.Column(sa.Integer)
|
||||
|
||||
label = sa.Column(sa.String)
|
||||
type_ind = sa.Column(sa.Integer)
|
||||
only_known_identities = sa.Column(sa.Integer)
|
||||
num_concurrent = sa.Column(sa.Integer)
|
||||
data = sa.Column(sa.LargeBinary)
|
||||
|
||||
note = sa.Column(sa.String)
|
||||
created_at = sa.Column(sa.BigInteger)
|
||||
|
||||
|
||||
class AutomationLink(Base):
|
||||
__tablename__ = 'automationlinks'
|
||||
# Contains per order/bid options
|
||||
|
||||
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
active_ind = sa.Column(sa.Integer)
|
||||
|
||||
linked_type = sa.Column(sa.Integer)
|
||||
linked_id = sa.Column(sa.LargeBinary)
|
||||
strategy_id = sa.Column(sa.Integer)
|
||||
|
||||
data = sa.Column(sa.LargeBinary)
|
||||
repeat_limit = sa.Column(sa.Integer)
|
||||
repeat_count = sa.Column(sa.Integer)
|
||||
|
||||
note = sa.Column(sa.String)
|
||||
created_at = sa.Column(sa.BigInteger)
|
||||
|
||||
__table_args__ = (sa.Index('linked_index', 'linked_type', 'linked_id'), )
|
||||
|
||||
|
||||
class History(Base):
|
||||
__tablename__ = 'history'
|
||||
|
||||
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
concept_type = sa.Column(sa.Integer)
|
||||
concept_id = sa.Column(sa.Integer)
|
||||
|
||||
changed_data = sa.Column(sa.LargeBinary)
|
||||
created_at = sa.Column(sa.BigInteger)
|
||||
|
||||
|
||||
class BidState(Base):
|
||||
__tablename__ = 'bidstates'
|
||||
|
||||
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
active_ind = sa.Column(sa.Integer)
|
||||
state_id = sa.Column(sa.Integer)
|
||||
label = sa.Column(sa.String)
|
||||
in_progress = sa.Column(sa.Integer)
|
||||
|
||||
note = sa.Column(sa.String)
|
||||
created_at = sa.Column(sa.BigInteger)
|
||||
|
||||
230
basicswap/db_upgrades.py
Normal file
230
basicswap/db_upgrades.py
Normal file
@@ -0,0 +1,230 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2022 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
import json
|
||||
import time
|
||||
|
||||
from sqlalchemy.orm import scoped_session
|
||||
from .db import (
|
||||
BidState,
|
||||
Concepts,
|
||||
AutomationStrategy,
|
||||
CURRENT_DB_VERSION,
|
||||
CURRENT_DB_DATA_VERSION)
|
||||
|
||||
from .basicswap_util import (
|
||||
BidStates,
|
||||
strBidState,
|
||||
isActiveBidState)
|
||||
|
||||
|
||||
def upgradeDatabaseData(self, data_version):
|
||||
if data_version >= CURRENT_DB_DATA_VERSION:
|
||||
return
|
||||
|
||||
self.log.info('Upgrading database records from version %d to %d.', data_version, CURRENT_DB_DATA_VERSION)
|
||||
with self.mxDB:
|
||||
try:
|
||||
session = scoped_session(self.session_factory)
|
||||
|
||||
now = int(time.time())
|
||||
|
||||
if data_version < 1:
|
||||
session.add(AutomationStrategy(
|
||||
active_ind=1,
|
||||
label='Accept All',
|
||||
type_ind=Concepts.OFFER,
|
||||
data=json.dumps({'exact_rate_only': True,
|
||||
'max_concurrent_bids': 5}).encode('utf-8'),
|
||||
only_known_identities=False,
|
||||
created_at=now))
|
||||
session.add(AutomationStrategy(
|
||||
active_ind=1,
|
||||
label='Accept Known',
|
||||
type_ind=Concepts.OFFER,
|
||||
data=json.dumps({'exact_rate_only': True,
|
||||
'max_concurrent_bids': 5}).encode('utf-8'),
|
||||
only_known_identities=True,
|
||||
note='Accept bids from identities with previously successful swaps only',
|
||||
created_at=now))
|
||||
|
||||
for state in BidStates:
|
||||
session.add(BidState(
|
||||
active_ind=1,
|
||||
state_id=int(state),
|
||||
in_progress=isActiveBidState(state),
|
||||
label=strBidState(state),
|
||||
created_at=now))
|
||||
|
||||
if data_version < 2:
|
||||
for state in (BidStates.XMR_SWAP_MSG_SCRIPT_LOCK_TX_SIGS, BidStates.XMR_SWAP_MSG_SCRIPT_LOCK_SPEND_TX):
|
||||
session.add(BidState(
|
||||
active_ind=1,
|
||||
state_id=int(state),
|
||||
in_progress=isActiveBidState(state),
|
||||
label=strBidState(state),
|
||||
created_at=now))
|
||||
|
||||
self.db_data_version = CURRENT_DB_DATA_VERSION
|
||||
self.setIntKVInSession('db_data_version', self.db_data_version, session)
|
||||
session.commit()
|
||||
self.log.info('Upgraded database records to version {}'.format(self.db_data_version))
|
||||
finally:
|
||||
session.close()
|
||||
session.remove()
|
||||
|
||||
|
||||
def upgradeDatabase(self, db_version):
|
||||
if db_version >= CURRENT_DB_VERSION:
|
||||
return
|
||||
|
||||
self.log.info('Upgrading database from version %d to %d.', db_version, CURRENT_DB_VERSION)
|
||||
|
||||
while True:
|
||||
session = scoped_session(self.session_factory)
|
||||
|
||||
current_version = db_version
|
||||
if current_version == 6:
|
||||
session.execute('ALTER TABLE bids ADD COLUMN security_token BLOB')
|
||||
session.execute('ALTER TABLE offers ADD COLUMN security_token BLOB')
|
||||
db_version += 1
|
||||
elif current_version == 7:
|
||||
session.execute('ALTER TABLE transactions ADD COLUMN block_hash BLOB')
|
||||
session.execute('ALTER TABLE transactions ADD COLUMN block_height INTEGER')
|
||||
session.execute('ALTER TABLE transactions ADD COLUMN block_time INTEGER')
|
||||
db_version += 1
|
||||
elif current_version == 8:
|
||||
session.execute('''
|
||||
CREATE TABLE wallets (
|
||||
record_id INTEGER NOT NULL,
|
||||
coin_id INTEGER,
|
||||
wallet_name VARCHAR,
|
||||
wallet_data VARCHAR,
|
||||
balance_type INTEGER,
|
||||
created_at BIGINT,
|
||||
PRIMARY KEY (record_id))''')
|
||||
db_version += 1
|
||||
elif current_version == 9:
|
||||
session.execute('ALTER TABLE wallets ADD COLUMN wallet_data VARCHAR')
|
||||
db_version += 1
|
||||
elif current_version == 10:
|
||||
session.execute('ALTER TABLE smsgaddresses ADD COLUMN active_ind INTEGER')
|
||||
session.execute('ALTER TABLE smsgaddresses ADD COLUMN created_at INTEGER')
|
||||
session.execute('ALTER TABLE smsgaddresses ADD COLUMN note VARCHAR')
|
||||
session.execute('ALTER TABLE smsgaddresses ADD COLUMN pubkey VARCHAR')
|
||||
session.execute('UPDATE smsgaddresses SET active_ind = 1, created_at = 1')
|
||||
|
||||
session.execute('ALTER TABLE offers ADD COLUMN addr_to VARCHAR')
|
||||
session.execute(f'UPDATE offers SET addr_to = "{self.network_addr}"')
|
||||
db_version += 1
|
||||
elif current_version == 11:
|
||||
session.execute('ALTER TABLE bids ADD COLUMN chain_a_height_start INTEGER')
|
||||
session.execute('ALTER TABLE bids ADD COLUMN chain_b_height_start INTEGER')
|
||||
session.execute('ALTER TABLE bids ADD COLUMN protocol_version INTEGER')
|
||||
session.execute('ALTER TABLE offers ADD COLUMN protocol_version INTEGER')
|
||||
session.execute('ALTER TABLE transactions ADD COLUMN tx_data BLOB')
|
||||
db_version += 1
|
||||
elif current_version == 12:
|
||||
session.execute('''
|
||||
CREATE TABLE knownidentities (
|
||||
record_id INTEGER NOT NULL,
|
||||
address VARCHAR,
|
||||
label VARCHAR,
|
||||
publickey BLOB,
|
||||
num_sent_bids_successful INTEGER,
|
||||
num_recv_bids_successful INTEGER,
|
||||
num_sent_bids_rejected INTEGER,
|
||||
num_recv_bids_rejected INTEGER,
|
||||
num_sent_bids_failed INTEGER,
|
||||
num_recv_bids_failed INTEGER,
|
||||
note VARCHAR,
|
||||
updated_at BIGINT,
|
||||
created_at BIGINT,
|
||||
PRIMARY KEY (record_id))''')
|
||||
session.execute('ALTER TABLE bids ADD COLUMN reject_code INTEGER')
|
||||
session.execute('ALTER TABLE bids ADD COLUMN rate INTEGER')
|
||||
session.execute('ALTER TABLE offers ADD COLUMN amount_negotiable INTEGER')
|
||||
session.execute('ALTER TABLE offers ADD COLUMN rate_negotiable INTEGER')
|
||||
db_version += 1
|
||||
elif current_version == 13:
|
||||
db_version += 1
|
||||
session.execute('''
|
||||
CREATE TABLE automationstrategies (
|
||||
record_id INTEGER NOT NULL,
|
||||
active_ind INTEGER,
|
||||
label VARCHAR,
|
||||
type_ind INTEGER,
|
||||
only_known_identities INTEGER,
|
||||
num_concurrent INTEGER,
|
||||
data BLOB,
|
||||
|
||||
note VARCHAR,
|
||||
created_at BIGINT,
|
||||
PRIMARY KEY (record_id))''')
|
||||
|
||||
session.execute('''
|
||||
CREATE TABLE automationlinks (
|
||||
record_id INTEGER NOT NULL,
|
||||
active_ind INTEGER,
|
||||
|
||||
linked_type INTEGER,
|
||||
linked_id BLOB,
|
||||
strategy_id INTEGER,
|
||||
|
||||
data BLOB,
|
||||
repeat_limit INTEGER,
|
||||
repeat_count INTEGER,
|
||||
|
||||
note VARCHAR,
|
||||
created_at BIGINT,
|
||||
PRIMARY KEY (record_id))''')
|
||||
|
||||
session.execute('''
|
||||
CREATE TABLE history (
|
||||
record_id INTEGER NOT NULL,
|
||||
concept_type INTEGER,
|
||||
concept_id INTEGER,
|
||||
changed_data BLOB,
|
||||
|
||||
note VARCHAR,
|
||||
created_at BIGINT,
|
||||
PRIMARY KEY (record_id))''')
|
||||
|
||||
session.execute('''
|
||||
CREATE TABLE bidstates (
|
||||
record_id INTEGER NOT NULL,
|
||||
active_ind INTEGER,
|
||||
state_id INTEGER,
|
||||
label VARCHAR,
|
||||
in_progress INTEGER,
|
||||
|
||||
note VARCHAR,
|
||||
created_at BIGINT,
|
||||
PRIMARY KEY (record_id))''')
|
||||
|
||||
session.execute('ALTER TABLE wallets ADD COLUMN active_ind INTEGER')
|
||||
session.execute('ALTER TABLE knownidentities ADD COLUMN active_ind INTEGER')
|
||||
session.execute('ALTER TABLE eventqueue RENAME TO actions')
|
||||
session.execute('ALTER TABLE actions RENAME COLUMN event_id TO action_id')
|
||||
session.execute('ALTER TABLE actions RENAME COLUMN event_type TO action_type')
|
||||
session.execute('ALTER TABLE actions RENAME COLUMN event_data TO action_data')
|
||||
elif current_version == 14:
|
||||
db_version += 1
|
||||
session.execute('ALTER TABLE xmr_swaps ADD COLUMN coin_a_lock_release_msg_id BLOB')
|
||||
session.execute('ALTER TABLE xmr_swaps RENAME COLUMN coin_a_lock_refund_spend_tx_msg_id TO coin_a_lock_spend_tx_msg_id')
|
||||
|
||||
if current_version != db_version:
|
||||
self.db_version = db_version
|
||||
self.setIntKVInSession('db_version', db_version, session)
|
||||
session.commit()
|
||||
session.close()
|
||||
session.remove()
|
||||
self.log.info('Upgraded database to version {}'.format(self.db_version))
|
||||
continue
|
||||
break
|
||||
|
||||
if db_version != CURRENT_DB_VERSION:
|
||||
raise ValueError('Unable to upgrade database.')
|
||||
@@ -75,7 +75,7 @@ class ExplorerBitAps(Explorer):
|
||||
# Can't get unspents return only if exactly one transaction exists
|
||||
data = json.loads(self.readURL(self.base_url + '/address/transactions/' + address))
|
||||
try:
|
||||
assert(data['data']['list'] == 1)
|
||||
assert data['data']['list'] == 1
|
||||
except Exception as ex:
|
||||
self.log.debug('Explorer error: {}'.format(str(ex)))
|
||||
return None
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
0
basicswap/interface/__init__.py
Normal file
0
basicswap/interface/__init__.py
Normal file
@@ -14,22 +14,22 @@ import traceback
|
||||
from io import BytesIO
|
||||
from basicswap.contrib.test_framework import segwit_addr
|
||||
|
||||
from .util import (
|
||||
from basicswap.util import (
|
||||
dumpj,
|
||||
ensure,
|
||||
make_int,
|
||||
b2h, i2b, b2i, i2h)
|
||||
from .util.ecc import (
|
||||
from basicswap.util.ecc import (
|
||||
ep,
|
||||
pointToCPK, CPKToPoint,
|
||||
getSecretInt)
|
||||
from .util.script import (
|
||||
from basicswap.util.script import (
|
||||
decodeScriptNum,
|
||||
getCompactSizeLen,
|
||||
SerialiseNumCompact,
|
||||
getWitnessElementLen,
|
||||
)
|
||||
from .util.address import (
|
||||
from basicswap.util.address import (
|
||||
toWIF,
|
||||
b58encode,
|
||||
decodeWif,
|
||||
@@ -47,7 +47,7 @@ from coincurve.ecdsaotves import (
|
||||
ecdsaotves_dec_sig,
|
||||
ecdsaotves_rec_enc_key)
|
||||
|
||||
from .contrib.test_framework.messages import (
|
||||
from basicswap.contrib.test_framework.messages import (
|
||||
COIN,
|
||||
COutPoint,
|
||||
CTransaction,
|
||||
@@ -56,7 +56,7 @@ from .contrib.test_framework.messages import (
|
||||
CTxOut,
|
||||
FromHex)
|
||||
|
||||
from .contrib.test_framework.script import (
|
||||
from basicswap.contrib.test_framework.script import (
|
||||
CScript, CScriptOp,
|
||||
OP_IF, OP_ELSE, OP_ENDIF,
|
||||
OP_0, OP_2,
|
||||
@@ -68,11 +68,11 @@ from .contrib.test_framework.script import (
|
||||
SegwitV0SignatureHash,
|
||||
hash160)
|
||||
|
||||
from .basicswap_util import (
|
||||
from basicswap.basicswap_util import (
|
||||
TxLockTypes)
|
||||
|
||||
from .chainparams import CoinInterface, Coins
|
||||
from .rpc import make_rpc_func, openrpc
|
||||
from basicswap.chainparams import CoinInterface, Coins
|
||||
from basicswap.rpc import make_rpc_func, openrpc
|
||||
|
||||
|
||||
SEQUENCE_LOCKTIME_GRANULARITY = 9 # 512 seconds
|
||||
@@ -157,7 +157,7 @@ class BTCInterface(CoinInterface):
|
||||
|
||||
@staticmethod
|
||||
def getExpectedSequence(lockType, lockVal):
|
||||
assert(lockVal >= 1), 'Bad lockVal'
|
||||
assert (lockVal >= 1), 'Bad lockVal'
|
||||
if lockType == TxLockTypes.SEQUENCE_LOCK_BLOCKS:
|
||||
return lockVal
|
||||
if lockType == TxLockTypes.SEQUENCE_LOCK_TIME:
|
||||
@@ -185,9 +185,16 @@ class BTCInterface(CoinInterface):
|
||||
self.blocks_confirmed = coin_settings['blocks_confirmed']
|
||||
self.setConfTarget(coin_settings['conf_target'])
|
||||
self._use_segwit = coin_settings['use_segwit']
|
||||
self._connection_type = coin_settings['connection_type']
|
||||
self._sc = swap_client
|
||||
self._log = self._sc.log if self._sc and self._sc.log else logging
|
||||
|
||||
def using_segwit(self):
|
||||
return self._use_segwit
|
||||
|
||||
def get_connection_type(self):
|
||||
return self._connection_type
|
||||
|
||||
def open_rpc(self, wallet=None):
|
||||
return openrpc(self._rpcport, self._rpcauth, wallet=wallet, host=self._rpc_host)
|
||||
|
||||
@@ -208,11 +215,14 @@ class BTCInterface(CoinInterface):
|
||||
rpc_conn.close()
|
||||
|
||||
def setConfTarget(self, new_conf_target):
|
||||
assert(new_conf_target >= 1 and new_conf_target < 33), 'Invalid conf_target value'
|
||||
ensure(new_conf_target >= 1 and new_conf_target < 33, 'Invalid conf_target value')
|
||||
self._conf_target = new_conf_target
|
||||
|
||||
def testDaemonRPC(self):
|
||||
self.rpc_callback('getwalletinfo', [])
|
||||
def testDaemonRPC(self, with_wallet=True):
|
||||
if with_wallet:
|
||||
self.rpc_callback('getwalletinfo', [])
|
||||
else:
|
||||
self.rpc_callback('getblockchaininfo', [])
|
||||
|
||||
def getDaemonVersion(self):
|
||||
return self.rpc_callback('getnetworkinfo')['version']
|
||||
@@ -282,12 +292,14 @@ class BTCInterface(CoinInterface):
|
||||
|
||||
def get_fee_rate(self, conf_target=2):
|
||||
try:
|
||||
return self.rpc_callback('estimatesmartfee', [conf_target])['feerate'], 'estimatesmartfee'
|
||||
fee_rate = self.rpc_callback('estimatesmartfee', [conf_target])['feerate']
|
||||
assert (fee_rate > 0.0), 'Non positive feerate'
|
||||
return fee_rate, 'estimatesmartfee'
|
||||
except Exception:
|
||||
try:
|
||||
fee_rate = self.rpc_callback('getwalletinfo')['paytxfee'], 'paytxfee'
|
||||
assert(fee_rate > 0.0), '0 feerate'
|
||||
return fee_rate
|
||||
fee_rate = self.rpc_callback('getwalletinfo')['paytxfee']
|
||||
assert (fee_rate > 0.0), 'Non positive feerate'
|
||||
return fee_rate, 'paytxfee'
|
||||
except Exception:
|
||||
return self.rpc_callback('getnetworkinfo')['relayfee'], 'relayfee'
|
||||
|
||||
@@ -308,7 +320,7 @@ class BTCInterface(CoinInterface):
|
||||
|
||||
def pkh_to_address(self, pkh):
|
||||
# pkh is hash160(pk)
|
||||
assert(len(pkh) == 20)
|
||||
assert (len(pkh) == 20)
|
||||
prefix = self.chainparams_network()['pubkey_address']
|
||||
data = bytes((prefix,)) + pkh
|
||||
checksum = hashlib.sha256(hashlib.sha256(data).digest()).digest()
|
||||
@@ -324,7 +336,7 @@ class BTCInterface(CoinInterface):
|
||||
return pubkeyToAddress(self.chainparams_network()['script_address'], script)
|
||||
|
||||
def pubkey_to_address(self, pk):
|
||||
assert(len(pk) == 33)
|
||||
assert (len(pk) == 33)
|
||||
return self.pkh_to_address(hash160(pk))
|
||||
|
||||
def getNewSecretKey(self):
|
||||
@@ -339,7 +351,7 @@ class BTCInterface(CoinInterface):
|
||||
|
||||
def verifyKey(self, k):
|
||||
i = b2i(k)
|
||||
return(i < ep.o and i > 0)
|
||||
return (i < ep.o and i > 0)
|
||||
|
||||
def verifyPubkey(self, pubkey_bytes):
|
||||
return verify_secp256k1_point(pubkey_bytes)
|
||||
@@ -626,7 +638,7 @@ class BTCInterface(CoinInterface):
|
||||
for txo in tx.vout:
|
||||
outputs_value += txo.nValue
|
||||
fee_paid = inputs_value - outputs_value
|
||||
assert(fee_paid > 0)
|
||||
assert (fee_paid > 0)
|
||||
|
||||
vsize = self.getTxVSize(tx, add_bytes, add_witness_bytes)
|
||||
fee_rate_paid = fee_paid * 1000 // vsize
|
||||
@@ -674,7 +686,7 @@ class BTCInterface(CoinInterface):
|
||||
ensure(C == Kaf, 'Bad script pubkey')
|
||||
|
||||
fee_paid = swap_value - locked_coin
|
||||
assert(fee_paid > 0)
|
||||
assert (fee_paid > 0)
|
||||
|
||||
dummy_witness_stack = self.getScriptLockTxDummyWitness(prevout_script)
|
||||
witness_bytes = self.getWitnessStackSerialisedLength(dummy_witness_stack)
|
||||
@@ -718,7 +730,7 @@ class BTCInterface(CoinInterface):
|
||||
tx_value = tx.vout[0].nValue
|
||||
|
||||
fee_paid = prevout_value - tx_value
|
||||
assert(fee_paid > 0)
|
||||
assert (fee_paid > 0)
|
||||
|
||||
dummy_witness_stack = self.getScriptLockRefundSpendTxDummyWitness(prevout_script)
|
||||
witness_bytes = self.getWitnessStackSerialisedLength(dummy_witness_stack)
|
||||
@@ -765,7 +777,7 @@ class BTCInterface(CoinInterface):
|
||||
|
||||
# The value of the lock tx output should already be verified, if the fee is as expected the difference will be the correct amount
|
||||
fee_paid = locked_coin - tx.vout[0].nValue
|
||||
assert(fee_paid > 0)
|
||||
assert (fee_paid > 0)
|
||||
|
||||
dummy_witness_stack = self.getScriptLockTxDummyWitness(lock_tx_script)
|
||||
witness_bytes = self.getWitnessStackSerialisedLength(dummy_witness_stack)
|
||||
@@ -985,7 +997,7 @@ class BTCInterface(CoinInterface):
|
||||
if not addr_info['iswatchonly']:
|
||||
ro = self.rpc_callback('importaddress', [dest_address, 'bid', False])
|
||||
self._log.info('Imported watch-only addr: {}'.format(dest_address))
|
||||
self._log.info('Rescanning chain from height: {}'.format(rescan_from))
|
||||
self._log.info('Rescanning {} chain from height: {}'.format(self.coin_name(), rescan_from))
|
||||
self.rpc_callback('rescanblockchain', [rescan_from])
|
||||
|
||||
return_txid = True if txid is None else False
|
||||
@@ -1062,7 +1074,7 @@ class BTCInterface(CoinInterface):
|
||||
message_hash = hashlib.sha256(bytes(message, 'utf-8')).digest()
|
||||
pubkey = PublicKey(K)
|
||||
rv = pubkey.verify_compact(sig, message_hash, hasher=None)
|
||||
assert(rv is True)
|
||||
assert (rv is True)
|
||||
|
||||
def verifyMessage(self, address, message, signature, message_magic=None) -> bool:
|
||||
if message_magic is None:
|
||||
@@ -1147,6 +1159,20 @@ class BTCInterface(CoinInterface):
|
||||
address = self.getNewAddress(self._use_segwit, 'create_utxo')
|
||||
return self.withdrawCoin(self.format_amount(value_sats), address, False), address
|
||||
|
||||
def createRawSignedTransaction(self, addr_to, amount):
|
||||
txn = self.rpc_callback('createrawtransaction', [[], {addr_to: self.format_amount(amount)}])
|
||||
|
||||
options = {
|
||||
'lockUnspents': True,
|
||||
'conf_target': self._conf_target,
|
||||
}
|
||||
txn_funded = self.rpc_callback('fundrawtransaction', [txn, options])['hex']
|
||||
txn_signed = self.rpc_callback('signrawtransactionwithwallet', [txn_funded])['hex']
|
||||
return txn_signed
|
||||
|
||||
def getBlockWithTxns(self, block_hash):
|
||||
return self.rpc_callback('getblock', [block_hash, 2])
|
||||
|
||||
|
||||
def testBTCInterface():
|
||||
print('testBTCInterface')
|
||||
0
basicswap/interface/contrib/__init__.py
Normal file
0
basicswap/interface/contrib/__init__.py
Normal file
180
basicswap/interface/contrib/pivx_test_framework/authproxy.py
Normal file
180
basicswap/interface/contrib/pivx_test_framework/authproxy.py
Normal file
@@ -0,0 +1,180 @@
|
||||
# Copyright (c) 2011 Jeff Garzik
|
||||
#
|
||||
# Previous copyright, from python-jsonrpc/jsonrpc/proxy.py:
|
||||
#
|
||||
# Copyright (c) 2007 Jan-Klaas Kollhof
|
||||
#
|
||||
# This file is part of jsonrpc.
|
||||
#
|
||||
# jsonrpc is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation; either version 2.1 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This software is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with this software; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
"""HTTP proxy for opening RPC connection to pivxd.
|
||||
|
||||
AuthServiceProxy has the following improvements over python-jsonrpc's
|
||||
ServiceProxy class:
|
||||
|
||||
- HTTP connections persist for the life of the AuthServiceProxy object
|
||||
(if server supports HTTP/1.1)
|
||||
- sends protocol 'version', per JSON-RPC 1.1
|
||||
- sends proper, incrementing 'id'
|
||||
- sends Basic HTTP authentication headers
|
||||
- parses all JSON numbers that look like floats as Decimal
|
||||
- uses standard Python json lib
|
||||
"""
|
||||
|
||||
import base64
|
||||
import decimal
|
||||
import http.client
|
||||
import json
|
||||
import logging
|
||||
import socket
|
||||
import time
|
||||
import urllib.parse
|
||||
|
||||
HTTP_TIMEOUT = 300
|
||||
USER_AGENT = "AuthServiceProxy/0.1"
|
||||
|
||||
log = logging.getLogger("BitcoinRPC")
|
||||
|
||||
class JSONRPCException(Exception):
|
||||
def __init__(self, rpc_error):
|
||||
try:
|
||||
errmsg = '%(message)s (%(code)i)' % rpc_error
|
||||
except (KeyError, TypeError):
|
||||
errmsg = ''
|
||||
super().__init__(errmsg)
|
||||
self.error = rpc_error
|
||||
|
||||
|
||||
def EncodeDecimal(o):
|
||||
if isinstance(o, decimal.Decimal):
|
||||
return str(o)
|
||||
raise TypeError(repr(o) + " is not JSON serializable")
|
||||
|
||||
class AuthServiceProxy():
|
||||
__id_count = 0
|
||||
|
||||
# ensure_ascii: escape unicode as \uXXXX, passed to json.dumps
|
||||
def __init__(self, service_url, service_name=None, timeout=HTTP_TIMEOUT, connection=None, ensure_ascii=True):
|
||||
self.__service_url = service_url
|
||||
self._service_name = service_name
|
||||
self.ensure_ascii = ensure_ascii # can be toggled on the fly by tests
|
||||
self.__url = urllib.parse.urlparse(service_url)
|
||||
port = 80 if self.__url.port is None else self.__url.port
|
||||
user = None if self.__url.username is None else self.__url.username.encode('utf8')
|
||||
passwd = None if self.__url.password is None else self.__url.password.encode('utf8')
|
||||
authpair = user + b':' + passwd
|
||||
self.__auth_header = b'Basic ' + base64.b64encode(authpair)
|
||||
|
||||
if connection:
|
||||
# Callables re-use the connection of the original proxy
|
||||
self.__conn = connection
|
||||
elif self.__url.scheme == 'https':
|
||||
self.__conn = http.client.HTTPSConnection(self.__url.hostname, port, timeout=timeout)
|
||||
else:
|
||||
self.__conn = http.client.HTTPConnection(self.__url.hostname, port, timeout=timeout)
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name.startswith('__') and name.endswith('__'):
|
||||
# Python internal stuff
|
||||
raise AttributeError
|
||||
if self._service_name is not None:
|
||||
name = "%s.%s" % (self._service_name, name)
|
||||
return AuthServiceProxy(self.__service_url, name, connection=self.__conn)
|
||||
|
||||
def _request(self, method, path, postdata):
|
||||
'''
|
||||
Do a HTTP request, with retry if we get disconnected (e.g. due to a timeout).
|
||||
This is a workaround for https://bugs.python.org/issue3566 which is fixed in Python 3.5.
|
||||
'''
|
||||
headers = {'Host': self.__url.hostname,
|
||||
'User-Agent': USER_AGENT,
|
||||
'Authorization': self.__auth_header,
|
||||
'Content-type': 'application/json'}
|
||||
try:
|
||||
self.__conn.request(method, path, postdata, headers)
|
||||
return self._get_response()
|
||||
except http.client.BadStatusLine as e:
|
||||
if e.line == "''": # if connection was closed, try again
|
||||
self.__conn.close()
|
||||
self.__conn.request(method, path, postdata, headers)
|
||||
return self._get_response()
|
||||
else:
|
||||
raise
|
||||
except (BrokenPipeError, ConnectionResetError):
|
||||
# Python 3.5+ raises BrokenPipeError instead of BadStatusLine when the connection was reset
|
||||
# ConnectionResetError happens on FreeBSD with Python 3.4
|
||||
self.__conn.close()
|
||||
self.__conn.request(method, path, postdata, headers)
|
||||
return self._get_response()
|
||||
|
||||
def get_request(self, *args, **argsn):
|
||||
AuthServiceProxy.__id_count += 1
|
||||
|
||||
log.debug("-%s-> %s %s" % (AuthServiceProxy.__id_count, self._service_name,
|
||||
json.dumps(args, default=EncodeDecimal, ensure_ascii=self.ensure_ascii)))
|
||||
if args and argsn:
|
||||
raise ValueError('Cannot handle both named and positional arguments')
|
||||
return {'version': '1.1',
|
||||
'method': self._service_name,
|
||||
'params': args or argsn,
|
||||
'id': AuthServiceProxy.__id_count}
|
||||
|
||||
def __call__(self, *args, **argsn):
|
||||
postdata = json.dumps(self.get_request(*args, **argsn), default=EncodeDecimal, ensure_ascii=self.ensure_ascii)
|
||||
response = self._request('POST', self.__url.path, postdata.encode('utf-8'))
|
||||
if response['error'] is not None:
|
||||
raise JSONRPCException(response['error'])
|
||||
elif 'result' not in response:
|
||||
raise JSONRPCException({
|
||||
'code': -343, 'message': 'missing JSON-RPC result'})
|
||||
else:
|
||||
return response['result']
|
||||
|
||||
def batch(self, rpc_call_list):
|
||||
postdata = json.dumps(list(rpc_call_list), default=EncodeDecimal, ensure_ascii=self.ensure_ascii)
|
||||
log.debug("--> " + postdata)
|
||||
return self._request('POST', self.__url.path, postdata.encode('utf-8'))
|
||||
|
||||
def _get_response(self):
|
||||
req_start_time = time.time()
|
||||
try:
|
||||
http_response = self.__conn.getresponse()
|
||||
except socket.timeout:
|
||||
raise JSONRPCException({
|
||||
'code': -344,
|
||||
'message': '%r RPC took longer than %f seconds. Consider '
|
||||
'using larger timeout for calls that take '
|
||||
'longer to return.' % (self._service_name,
|
||||
self.__conn.timeout)})
|
||||
if http_response is None:
|
||||
raise JSONRPCException({
|
||||
'code': -342, 'message': 'missing HTTP response from server'})
|
||||
|
||||
content_type = http_response.getheader('Content-Type')
|
||||
if content_type != 'application/json':
|
||||
raise JSONRPCException({
|
||||
'code': -342, 'message': 'non-JSON HTTP response with \'%i %s\' from server' % (http_response.status, http_response.reason)})
|
||||
|
||||
responsedata = http_response.read().decode('utf8')
|
||||
response = json.loads(responsedata, parse_float=decimal.Decimal)
|
||||
elapsed = time.time() - req_start_time
|
||||
if "error" in response and response["error"] is None:
|
||||
log.debug("<-%s- [%.6f] %s" % (response["id"], elapsed, json.dumps(response["result"], default=EncodeDecimal, ensure_ascii=self.ensure_ascii)))
|
||||
else:
|
||||
log.debug("<-- [%.6f] %s" % (elapsed, responsedata))
|
||||
return response
|
||||
|
||||
def __truediv__(self, relative_uri):
|
||||
return AuthServiceProxy("{}/{}".format(self.__service_url, relative_uri), self._service_name, connection=self.__conn)
|
||||
109
basicswap/interface/contrib/pivx_test_framework/coverage.py
Normal file
109
basicswap/interface/contrib/pivx_test_framework/coverage.py
Normal file
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-2017 The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""Utilities for doing coverage analysis on the RPC interface.
|
||||
|
||||
Provides a way to track which RPC commands are exercised during
|
||||
testing.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
|
||||
REFERENCE_FILENAME = 'rpc_interface.txt'
|
||||
|
||||
|
||||
class AuthServiceProxyWrapper():
|
||||
"""
|
||||
An object that wraps AuthServiceProxy to record specific RPC calls.
|
||||
|
||||
"""
|
||||
def __init__(self, auth_service_proxy_instance, coverage_logfile=None):
|
||||
"""
|
||||
Kwargs:
|
||||
auth_service_proxy_instance (AuthServiceProxy): the instance
|
||||
being wrapped.
|
||||
coverage_logfile (str): if specified, write each service_name
|
||||
out to a file when called.
|
||||
|
||||
"""
|
||||
self.auth_service_proxy_instance = auth_service_proxy_instance
|
||||
self.coverage_logfile = coverage_logfile
|
||||
|
||||
def __getattr__(self, name):
|
||||
return_val = getattr(self.auth_service_proxy_instance, name)
|
||||
if not isinstance(return_val, type(self.auth_service_proxy_instance)):
|
||||
# If proxy getattr returned an unwrapped value, do the same here.
|
||||
return return_val
|
||||
return AuthServiceProxyWrapper(return_val, self.coverage_logfile)
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
"""
|
||||
Delegates to AuthServiceProxy, then writes the particular RPC method
|
||||
called to a file.
|
||||
|
||||
"""
|
||||
return_val = self.auth_service_proxy_instance.__call__(*args, **kwargs)
|
||||
self._log_call()
|
||||
return return_val
|
||||
|
||||
def _log_call(self):
|
||||
rpc_method = self.auth_service_proxy_instance._service_name
|
||||
|
||||
if self.coverage_logfile:
|
||||
with open(self.coverage_logfile, 'a+', encoding='utf8') as f:
|
||||
f.write("%s\n" % rpc_method)
|
||||
|
||||
def __truediv__(self, relative_uri):
|
||||
return AuthServiceProxyWrapper(self.auth_service_proxy_instance / relative_uri,
|
||||
self.coverage_logfile)
|
||||
|
||||
def get_request(self, *args, **kwargs):
|
||||
self._log_call()
|
||||
return self.auth_service_proxy_instance.get_request(*args)
|
||||
|
||||
def get_filename(dirname, n_node):
|
||||
"""
|
||||
Get a filename unique to the test process ID and node.
|
||||
|
||||
This file will contain a list of RPC commands covered.
|
||||
"""
|
||||
pid = str(os.getpid())
|
||||
return os.path.join(
|
||||
dirname, "coverage.pid%s.node%s.txt" % (pid, str(n_node)))
|
||||
|
||||
|
||||
def write_all_rpc_commands(dirname, node):
|
||||
"""
|
||||
Write out a list of all RPC functions available in `pivx-cli` for
|
||||
coverage comparison. This will only happen once per coverage
|
||||
directory.
|
||||
|
||||
Args:
|
||||
dirname (str): temporary test dir
|
||||
node (AuthServiceProxy): client
|
||||
|
||||
Returns:
|
||||
bool. if the RPC interface file was written.
|
||||
|
||||
"""
|
||||
filename = os.path.join(dirname, REFERENCE_FILENAME)
|
||||
|
||||
if os.path.isfile(filename):
|
||||
return False
|
||||
|
||||
help_output = node.help().split('\n')
|
||||
commands = set()
|
||||
|
||||
for line in help_output:
|
||||
line = line.strip()
|
||||
|
||||
# Ignore blanks and headers
|
||||
if line and not line.startswith('='):
|
||||
commands.add("%s\n" % line.split()[0])
|
||||
|
||||
with open(filename, 'w', encoding='utf8') as f:
|
||||
f.writelines(list(commands))
|
||||
|
||||
return True
|
||||
1505
basicswap/interface/contrib/pivx_test_framework/messages.py
Executable file
1505
basicswap/interface/contrib/pivx_test_framework/messages.py
Executable file
File diff suppressed because it is too large
Load Diff
63
basicswap/interface/contrib/pivx_test_framework/siphash.py
Normal file
63
basicswap/interface/contrib/pivx_test_framework/siphash.py
Normal file
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2016-2017 The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""Specialized SipHash-2-4 implementations.
|
||||
|
||||
This implements SipHash-2-4 for 256-bit integers.
|
||||
"""
|
||||
|
||||
def rotl64(n, b):
|
||||
return n >> (64 - b) | (n & ((1 << (64 - b)) - 1)) << b
|
||||
|
||||
def siphash_round(v0, v1, v2, v3):
|
||||
v0 = (v0 + v1) & ((1 << 64) - 1)
|
||||
v1 = rotl64(v1, 13)
|
||||
v1 ^= v0
|
||||
v0 = rotl64(v0, 32)
|
||||
v2 = (v2 + v3) & ((1 << 64) - 1)
|
||||
v3 = rotl64(v3, 16)
|
||||
v3 ^= v2
|
||||
v0 = (v0 + v3) & ((1 << 64) - 1)
|
||||
v3 = rotl64(v3, 21)
|
||||
v3 ^= v0
|
||||
v2 = (v2 + v1) & ((1 << 64) - 1)
|
||||
v1 = rotl64(v1, 17)
|
||||
v1 ^= v2
|
||||
v2 = rotl64(v2, 32)
|
||||
return (v0, v1, v2, v3)
|
||||
|
||||
def siphash256(k0, k1, h):
|
||||
n0 = h & ((1 << 64) - 1)
|
||||
n1 = (h >> 64) & ((1 << 64) - 1)
|
||||
n2 = (h >> 128) & ((1 << 64) - 1)
|
||||
n3 = (h >> 192) & ((1 << 64) - 1)
|
||||
v0 = 0x736f6d6570736575 ^ k0
|
||||
v1 = 0x646f72616e646f6d ^ k1
|
||||
v2 = 0x6c7967656e657261 ^ k0
|
||||
v3 = 0x7465646279746573 ^ k1 ^ n0
|
||||
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
|
||||
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
|
||||
v0 ^= n0
|
||||
v3 ^= n1
|
||||
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
|
||||
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
|
||||
v0 ^= n1
|
||||
v3 ^= n2
|
||||
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
|
||||
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
|
||||
v0 ^= n2
|
||||
v3 ^= n3
|
||||
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
|
||||
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
|
||||
v0 ^= n3
|
||||
v3 ^= 0x2000000000000000
|
||||
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
|
||||
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
|
||||
v0 ^= 0x2000000000000000
|
||||
v2 ^= 0xFF
|
||||
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
|
||||
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
|
||||
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
|
||||
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
|
||||
return v0 ^ v1 ^ v2 ^ v3
|
||||
625
basicswap/interface/contrib/pivx_test_framework/util.py
Normal file
625
basicswap/interface/contrib/pivx_test_framework/util.py
Normal file
@@ -0,0 +1,625 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2014-2017 The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""Helpful routines for regression testing."""
|
||||
|
||||
from base64 import b64encode
|
||||
from binascii import hexlify, unhexlify
|
||||
from decimal import Decimal, ROUND_DOWN
|
||||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
from subprocess import CalledProcessError
|
||||
import time
|
||||
|
||||
from . import coverage, messages
|
||||
from .authproxy import AuthServiceProxy, JSONRPCException
|
||||
|
||||
logger = logging.getLogger("TestFramework.utils")
|
||||
|
||||
# Assert functions
|
||||
##################
|
||||
|
||||
def assert_fee_amount(fee, tx_size, fee_per_kB):
|
||||
"""Assert the fee was in range"""
|
||||
target_fee = round(tx_size * fee_per_kB / 1000, 8)
|
||||
if fee < target_fee:
|
||||
raise AssertionError("Fee of %s PIV too low! (Should be %s PIV)" % (str(fee), str(target_fee)))
|
||||
# allow the wallet's estimation to be at most 2 bytes off
|
||||
if fee > (tx_size + 20) * fee_per_kB / 1000:
|
||||
raise AssertionError("Fee of %s PIV too high! (Should be %s PIV)" % (str(fee), str(target_fee)))
|
||||
|
||||
def assert_equal(thing1, thing2, *args):
|
||||
if thing1 != thing2 or any(thing1 != arg for arg in args):
|
||||
raise AssertionError("not(%s)" % " == ".join(str(arg) for arg in (thing1, thing2) + args))
|
||||
|
||||
def assert_true(condition, message = ""):
|
||||
if not condition:
|
||||
raise AssertionError(message)
|
||||
|
||||
def assert_false(condition, message = ""):
|
||||
assert_true(not condition, message)
|
||||
|
||||
def assert_greater_than(thing1, thing2):
|
||||
if thing1 <= thing2:
|
||||
raise AssertionError("%s <= %s" % (str(thing1), str(thing2)))
|
||||
|
||||
def assert_greater_than_or_equal(thing1, thing2):
|
||||
if thing1 < thing2:
|
||||
raise AssertionError("%s < %s" % (str(thing1), str(thing2)))
|
||||
|
||||
def assert_raises(exc, fun, *args, **kwds):
|
||||
assert_raises_message(exc, None, fun, *args, **kwds)
|
||||
|
||||
def assert_raises_message(exc, message, fun, *args, **kwds):
|
||||
try:
|
||||
fun(*args, **kwds)
|
||||
except JSONRPCException:
|
||||
raise AssertionError("Use assert_raises_rpc_error() to test RPC failures")
|
||||
except exc as e:
|
||||
if message is not None and message not in e.error['message']:
|
||||
raise AssertionError("Expected substring not found:" + e.error['message'])
|
||||
except Exception as e:
|
||||
raise AssertionError("Unexpected exception raised: " + type(e).__name__)
|
||||
else:
|
||||
raise AssertionError("No exception raised")
|
||||
|
||||
def assert_raises_process_error(returncode, output, fun, *args, **kwds):
|
||||
"""Execute a process and asserts the process return code and output.
|
||||
|
||||
Calls function `fun` with arguments `args` and `kwds`. Catches a CalledProcessError
|
||||
and verifies that the return code and output are as expected. Throws AssertionError if
|
||||
no CalledProcessError was raised or if the return code and output are not as expected.
|
||||
|
||||
Args:
|
||||
returncode (int): the process return code.
|
||||
output (string): [a substring of] the process output.
|
||||
fun (function): the function to call. This should execute a process.
|
||||
args*: positional arguments for the function.
|
||||
kwds**: named arguments for the function.
|
||||
"""
|
||||
try:
|
||||
fun(*args, **kwds)
|
||||
except CalledProcessError as e:
|
||||
if returncode != e.returncode:
|
||||
raise AssertionError("Unexpected returncode %i" % e.returncode)
|
||||
if output not in e.output:
|
||||
raise AssertionError("Expected substring not found:" + e.output)
|
||||
else:
|
||||
raise AssertionError("No exception raised")
|
||||
|
||||
def assert_raises_rpc_error(code, message, fun, *args, **kwds):
|
||||
"""Run an RPC and verify that a specific JSONRPC exception code and message is raised.
|
||||
|
||||
Calls function `fun` with arguments `args` and `kwds`. Catches a JSONRPCException
|
||||
and verifies that the error code and message are as expected. Throws AssertionError if
|
||||
no JSONRPCException was raised or if the error code/message are not as expected.
|
||||
|
||||
Args:
|
||||
code (int), optional: the error code returned by the RPC call (defined
|
||||
in src/rpc/protocol.h). Set to None if checking the error code is not required.
|
||||
message (string), optional: [a substring of] the error string returned by the
|
||||
RPC call. Set to None if checking the error string is not required.
|
||||
fun (function): the function to call. This should be the name of an RPC.
|
||||
args*: positional arguments for the function.
|
||||
kwds**: named arguments for the function.
|
||||
"""
|
||||
assert try_rpc(code, message, fun, *args, **kwds), "No exception raised"
|
||||
|
||||
def try_rpc(code, message, fun, *args, **kwds):
|
||||
"""Tries to run an rpc command.
|
||||
|
||||
Test against error code and message if the rpc fails.
|
||||
Returns whether a JSONRPCException was raised."""
|
||||
try:
|
||||
fun(*args, **kwds)
|
||||
except JSONRPCException as e:
|
||||
# JSONRPCException was thrown as expected. Check the code and message values are correct.
|
||||
if (code is not None) and (code != e.error["code"]):
|
||||
raise AssertionError("Unexpected JSONRPC error code %i" % e.error["code"])
|
||||
if (message is not None) and (message not in e.error['message']):
|
||||
raise AssertionError("Expected substring (%s) not found in: %s" % (message, e.error['message']))
|
||||
return True
|
||||
except Exception as e:
|
||||
raise AssertionError("Unexpected exception raised: " + type(e).__name__)
|
||||
else:
|
||||
return False
|
||||
|
||||
def assert_is_hex_string(string):
|
||||
try:
|
||||
int(string, 16)
|
||||
except Exception as e:
|
||||
raise AssertionError(
|
||||
"Couldn't interpret %r as hexadecimal; raised: %s" % (string, e))
|
||||
|
||||
def assert_is_hash_string(string, length=64):
|
||||
if not isinstance(string, str):
|
||||
raise AssertionError("Expected a string, got type %r" % type(string))
|
||||
elif length and len(string) != length:
|
||||
raise AssertionError(
|
||||
"String of length %d expected; got %d" % (length, len(string)))
|
||||
elif not re.match('[abcdef0-9]+$', string):
|
||||
raise AssertionError(
|
||||
"String %r contains invalid characters for a hash." % string)
|
||||
|
||||
def assert_array_result(object_array, to_match, expected, should_not_find=False):
|
||||
"""
|
||||
Pass in array of JSON objects, a dictionary with key/value pairs
|
||||
to match against, and another dictionary with expected key/value
|
||||
pairs.
|
||||
If the should_not_find flag is true, to_match should not be found
|
||||
in object_array
|
||||
"""
|
||||
if should_not_find:
|
||||
assert_equal(expected, {})
|
||||
num_matched = 0
|
||||
for item in object_array:
|
||||
all_match = True
|
||||
for key, value in to_match.items():
|
||||
if item[key] != value:
|
||||
all_match = False
|
||||
if not all_match:
|
||||
continue
|
||||
elif should_not_find:
|
||||
num_matched = num_matched + 1
|
||||
for key, value in expected.items():
|
||||
if item[key] != value:
|
||||
raise AssertionError("%s : expected %s=%s" % (str(item), str(key), str(value)))
|
||||
num_matched = num_matched + 1
|
||||
if num_matched == 0 and not should_not_find:
|
||||
raise AssertionError("No objects matched %s" % (str(to_match)))
|
||||
if num_matched > 0 and should_not_find:
|
||||
raise AssertionError("Objects were found %s" % (str(to_match)))
|
||||
|
||||
# Utility functions
|
||||
###################
|
||||
|
||||
def check_json_precision():
|
||||
"""Make sure json library being used does not lose precision converting BTC values"""
|
||||
n = Decimal("20000000.00000003")
|
||||
satoshis = int(json.loads(json.dumps(float(n))) * 1.0e8)
|
||||
if satoshis != 2000000000000003:
|
||||
raise RuntimeError("JSON encode/decode loses precision")
|
||||
|
||||
def count_bytes(hex_string):
|
||||
return len(bytearray.fromhex(hex_string))
|
||||
|
||||
def bytes_to_hex_str(byte_str):
|
||||
return hexlify(byte_str).decode('ascii')
|
||||
|
||||
def hash256(byte_str):
|
||||
sha256 = hashlib.sha256()
|
||||
sha256.update(byte_str)
|
||||
sha256d = hashlib.sha256()
|
||||
sha256d.update(sha256.digest())
|
||||
return sha256d.digest()[::-1]
|
||||
|
||||
def hex_str_to_bytes(hex_str):
|
||||
return unhexlify(hex_str.encode('ascii'))
|
||||
|
||||
def str_to_b64str(string):
|
||||
return b64encode(string.encode('utf-8')).decode('ascii')
|
||||
|
||||
def satoshi_round(amount):
|
||||
return Decimal(amount).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN)
|
||||
|
||||
def wait_until(predicate,
|
||||
*,
|
||||
attempts=float('inf'),
|
||||
timeout=float('inf'),
|
||||
lock=None,
|
||||
sendpings=None,
|
||||
mocktime=None):
|
||||
|
||||
if attempts == float('inf') and timeout == float('inf'):
|
||||
timeout = 60
|
||||
attempt = 0
|
||||
timeout += time.time()
|
||||
|
||||
while attempt < attempts and time.time() < timeout:
|
||||
if lock:
|
||||
with lock:
|
||||
if predicate():
|
||||
return
|
||||
else:
|
||||
if predicate():
|
||||
return
|
||||
attempt += 1
|
||||
time.sleep(0.5)
|
||||
if sendpings is not None:
|
||||
sendpings()
|
||||
if mocktime is not None:
|
||||
mocktime(1)
|
||||
|
||||
# Print the cause of the timeout
|
||||
assert_greater_than(attempts, attempt)
|
||||
assert_greater_than(timeout, time.time())
|
||||
raise RuntimeError('Unreachable')
|
||||
|
||||
# RPC/P2P connection constants and functions
|
||||
############################################
|
||||
|
||||
# The maximum number of nodes a single test can spawn
|
||||
MAX_NODES = 8
|
||||
# Don't assign rpc or p2p ports lower than this
|
||||
PORT_MIN = 11000
|
||||
# The number of ports to "reserve" for p2p and rpc, each
|
||||
PORT_RANGE = 5000
|
||||
|
||||
class PortSeed:
|
||||
# Must be initialized with a unique integer for each process
|
||||
n = None
|
||||
|
||||
def get_rpc_proxy(url, node_number, timeout=None, coveragedir=None):
|
||||
"""
|
||||
Args:
|
||||
url (str): URL of the RPC server to call
|
||||
node_number (int): the node number (or id) that this calls to
|
||||
|
||||
Kwargs:
|
||||
timeout (int): HTTP timeout in seconds
|
||||
|
||||
Returns:
|
||||
AuthServiceProxy. convenience object for making RPC calls.
|
||||
|
||||
"""
|
||||
proxy_kwargs = {}
|
||||
if timeout is not None:
|
||||
proxy_kwargs['timeout'] = timeout
|
||||
|
||||
proxy = AuthServiceProxy(url, **proxy_kwargs)
|
||||
proxy.url = url # store URL on proxy for info
|
||||
|
||||
coverage_logfile = coverage.get_filename(
|
||||
coveragedir, node_number) if coveragedir else None
|
||||
|
||||
return coverage.AuthServiceProxyWrapper(proxy, coverage_logfile)
|
||||
|
||||
def p2p_port(n):
|
||||
assert(n <= MAX_NODES)
|
||||
return PORT_MIN + n + (MAX_NODES * PortSeed.n) % (PORT_RANGE - 1 - MAX_NODES)
|
||||
|
||||
def rpc_port(n):
|
||||
return PORT_MIN + PORT_RANGE + n + (MAX_NODES * PortSeed.n) % (PORT_RANGE - 1 - MAX_NODES)
|
||||
|
||||
def rpc_url(datadir, i, rpchost=None):
|
||||
rpc_u, rpc_p = get_auth_cookie(datadir)
|
||||
host = '127.0.0.1'
|
||||
port = rpc_port(i)
|
||||
if rpchost:
|
||||
parts = rpchost.split(':')
|
||||
if len(parts) == 2:
|
||||
host, port = parts
|
||||
else:
|
||||
host = rpchost
|
||||
return "http://%s:%s@%s:%d" % (rpc_u, rpc_p, host, int(port))
|
||||
|
||||
# Node functions
|
||||
################
|
||||
|
||||
def initialize_datadir(dirname, n):
|
||||
datadir = get_datadir_path(dirname, n)
|
||||
if not os.path.isdir(datadir):
|
||||
os.makedirs(datadir)
|
||||
with open(os.path.join(datadir, "pivx.conf"), 'w', encoding='utf8') as f:
|
||||
f.write("regtest=1\n")
|
||||
f.write("[regtest]\n")
|
||||
f.write("port=" + str(p2p_port(n)) + "\n")
|
||||
f.write("rpcport=" + str(rpc_port(n)) + "\n")
|
||||
f.write("server=1\n")
|
||||
f.write("keypool=1\n")
|
||||
f.write("discover=0\n")
|
||||
f.write("listenonion=0\n")
|
||||
f.write("spendzeroconfchange=1\n")
|
||||
f.write("printtoconsole=0\n")
|
||||
f.write("natpmp=0\n")
|
||||
return datadir
|
||||
|
||||
def get_datadir_path(dirname, n):
|
||||
return os.path.join(dirname, "node" + str(n))
|
||||
|
||||
def append_config(dirname, n, options):
|
||||
datadir = get_datadir_path(dirname, n)
|
||||
with open(os.path.join(datadir, "pivx.conf"), 'a', encoding='utf8') as f:
|
||||
for option in options:
|
||||
f.write(option + "\n")
|
||||
|
||||
def get_auth_cookie(datadir):
|
||||
user = None
|
||||
password = None
|
||||
if os.path.isfile(os.path.join(datadir, "pivx.conf")):
|
||||
with open(os.path.join(datadir, "pivx.conf"), 'r', encoding='utf8') as f:
|
||||
for line in f:
|
||||
if line.startswith("rpcuser="):
|
||||
assert user is None # Ensure that there is only one rpcuser line
|
||||
user = line.split("=")[1].strip("\n")
|
||||
if line.startswith("rpcpassword="):
|
||||
assert password is None # Ensure that there is only one rpcpassword line
|
||||
password = line.split("=")[1].strip("\n")
|
||||
if os.path.isfile(os.path.join(datadir, "regtest", ".cookie")):
|
||||
with open(os.path.join(datadir, "regtest", ".cookie"), 'r', encoding="utf8") as f:
|
||||
userpass = f.read()
|
||||
split_userpass = userpass.split(':')
|
||||
user = split_userpass[0]
|
||||
password = split_userpass[1]
|
||||
if user is None or password is None:
|
||||
raise ValueError("No RPC credentials")
|
||||
return user, password
|
||||
|
||||
# If a cookie file exists in the given datadir, delete it.
|
||||
def delete_cookie_file(datadir):
|
||||
if os.path.isfile(os.path.join(datadir, "regtest", ".cookie")):
|
||||
logger.debug("Deleting leftover cookie file")
|
||||
os.remove(os.path.join(datadir, "regtest", ".cookie"))
|
||||
|
||||
def get_bip9_status(node, key):
|
||||
info = node.getblockchaininfo()
|
||||
return info['bip9_softforks'][key]
|
||||
|
||||
def set_node_times(nodes, t):
|
||||
for node in nodes:
|
||||
node.setmocktime(t)
|
||||
|
||||
def disconnect_nodes(from_connection, node_num):
|
||||
for addr in [peer['addr'] for peer in from_connection.getpeerinfo() if "testnode%d" % node_num in peer['subver']]:
|
||||
try:
|
||||
from_connection.disconnectnode(addr)
|
||||
except JSONRPCException as e:
|
||||
# If this node is disconnected between calculating the peer id
|
||||
# and issuing the disconnect, don't worry about it.
|
||||
# This avoids a race condition if we're mass-disconnecting peers.
|
||||
if e.error['code'] != -29: # RPC_CLIENT_NODE_NOT_CONNECTED
|
||||
raise
|
||||
|
||||
# wait to disconnect
|
||||
wait_until(lambda: [peer['addr'] for peer in from_connection.getpeerinfo() if "testnode%d" % node_num in peer['subver']] == [], timeout=5)
|
||||
|
||||
def connect_nodes(from_connection, node_num):
|
||||
ip_port = "127.0.0.1:" + str(p2p_port(node_num))
|
||||
from_connection.addnode(ip_port, "onetry")
|
||||
# poll until version handshake complete to avoid race conditions
|
||||
# with transaction relaying
|
||||
wait_until(lambda: all(peer['version'] != 0 for peer in from_connection.getpeerinfo()))
|
||||
|
||||
def connect_nodes_clique(nodes):
|
||||
l = len(nodes)
|
||||
for a in range(l):
|
||||
for b in range(a, l):
|
||||
connect_nodes(nodes[a], b)
|
||||
connect_nodes(nodes[b], a)
|
||||
|
||||
# Transaction/Block functions
|
||||
#############################
|
||||
|
||||
def find_output(node, txid, amount):
|
||||
"""
|
||||
Return index to output of txid with value amount
|
||||
Raises exception if there is none.
|
||||
"""
|
||||
txdata = node.getrawtransaction(txid, 1)
|
||||
for i in range(len(txdata["vout"])):
|
||||
if txdata["vout"][i]["value"] == amount:
|
||||
return i
|
||||
raise RuntimeError("find_output txid %s : %s not found" % (txid, str(amount)))
|
||||
|
||||
def gather_inputs(from_node, amount_needed, confirmations_required=1):
|
||||
"""
|
||||
Return a random set of unspent txouts that are enough to pay amount_needed
|
||||
"""
|
||||
assert(confirmations_required >= 0)
|
||||
utxo = from_node.listunspent(confirmations_required)
|
||||
random.shuffle(utxo)
|
||||
inputs = []
|
||||
total_in = Decimal("0.00000000")
|
||||
while total_in < amount_needed and len(utxo) > 0:
|
||||
t = utxo.pop()
|
||||
total_in += t["amount"]
|
||||
inputs.append({"txid": t["txid"], "vout": t["vout"], "address": t["address"]})
|
||||
if total_in < amount_needed:
|
||||
raise RuntimeError("Insufficient funds: need %d, have %d" % (amount_needed, total_in))
|
||||
return (total_in, inputs)
|
||||
|
||||
def make_change(from_node, amount_in, amount_out, fee):
|
||||
"""
|
||||
Create change output(s), return them
|
||||
"""
|
||||
outputs = {}
|
||||
amount = amount_out + fee
|
||||
change = amount_in - amount
|
||||
if change > amount * 2:
|
||||
# Create an extra change output to break up big inputs
|
||||
change_address = from_node.getnewaddress()
|
||||
# Split change in two, being careful of rounding:
|
||||
outputs[change_address] = Decimal(change / 2).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN)
|
||||
change = amount_in - amount - outputs[change_address]
|
||||
if change > 0:
|
||||
outputs[from_node.getnewaddress()] = change
|
||||
return outputs
|
||||
|
||||
def random_transaction(nodes, amount, min_fee, fee_increment, fee_variants):
|
||||
"""
|
||||
Create a random transaction.
|
||||
Returns (txid, hex-encoded-transaction-data, fee)
|
||||
"""
|
||||
from_node = random.choice(nodes)
|
||||
to_node = random.choice(nodes)
|
||||
fee = min_fee + fee_increment * random.randint(0, fee_variants)
|
||||
|
||||
(total_in, inputs) = gather_inputs(from_node, amount + fee)
|
||||
outputs = make_change(from_node, total_in, amount, fee)
|
||||
outputs[to_node.getnewaddress()] = float(amount)
|
||||
|
||||
rawtx = from_node.createrawtransaction(inputs, outputs)
|
||||
signresult = from_node.signrawtransaction(rawtx)
|
||||
txid = from_node.sendrawtransaction(signresult["hex"], True)
|
||||
|
||||
return (txid, signresult["hex"], fee)
|
||||
|
||||
# Helper to create at least "count" utxos
|
||||
# Pass in a fee that is sufficient for relay and mining new transactions.
|
||||
def create_confirmed_utxos(fee, node, count):
|
||||
to_generate = int(0.5 * count) + 101
|
||||
while to_generate > 0:
|
||||
node.generate(min(25, to_generate))
|
||||
to_generate -= 25
|
||||
utxos = node.listunspent()
|
||||
iterations = count - len(utxos)
|
||||
addr1 = node.getnewaddress()
|
||||
addr2 = node.getnewaddress()
|
||||
if iterations <= 0:
|
||||
return utxos
|
||||
for i in range(iterations):
|
||||
t = utxos.pop()
|
||||
inputs = []
|
||||
inputs.append({"txid": t["txid"], "vout": t["vout"]})
|
||||
outputs = {}
|
||||
send_value = t['amount'] - fee
|
||||
outputs[addr1] = float(satoshi_round(send_value / 2))
|
||||
outputs[addr2] = float(satoshi_round(send_value / 2))
|
||||
raw_tx = node.createrawtransaction(inputs, outputs)
|
||||
signed_tx = node.signrawtransaction(raw_tx)["hex"]
|
||||
node.sendrawtransaction(signed_tx)
|
||||
|
||||
while (node.getmempoolinfo()['size'] > 0):
|
||||
node.generate(1)
|
||||
|
||||
utxos = node.listunspent()
|
||||
assert(len(utxos) >= count)
|
||||
return utxos
|
||||
|
||||
# Create large OP_RETURN txouts that can be appended to a transaction
|
||||
# to make it large (helper for constructing large transactions).
|
||||
def gen_return_txouts():
|
||||
# Some pre-processing to create a bunch of OP_RETURN txouts to insert into transactions we create
|
||||
# So we have big transactions (and therefore can't fit very many into each block)
|
||||
# create one script_pubkey
|
||||
script_pubkey = "6a4d0200" # OP_RETURN OP_PUSH2 512 bytes
|
||||
for i in range(512):
|
||||
script_pubkey = script_pubkey + "01"
|
||||
# concatenate 128 txouts of above script_pubkey which we'll insert before the txout for change
|
||||
txouts = "81"
|
||||
for k in range(128):
|
||||
# add txout value
|
||||
txouts = txouts + "0000000000000000"
|
||||
# add length of script_pubkey
|
||||
txouts = txouts + "fd0402"
|
||||
# add script_pubkey
|
||||
txouts = txouts + script_pubkey
|
||||
return txouts
|
||||
|
||||
def create_tx(node, coinbase, to_address, amount):
|
||||
inputs = [{"txid": coinbase, "vout": 0}]
|
||||
outputs = {to_address: amount}
|
||||
rawtx = node.createrawtransaction(inputs, outputs)
|
||||
signresult = node.signrawtransaction(rawtx)
|
||||
assert_equal(signresult["complete"], True)
|
||||
return signresult["hex"]
|
||||
|
||||
# Create a spend of each passed-in utxo, splicing in "txouts" to each raw
|
||||
# transaction to make it large. See gen_return_txouts() above.
|
||||
def create_lots_of_big_transactions(node, txouts, utxos, num, fee):
|
||||
addr = node.getnewaddress()
|
||||
txids = []
|
||||
for _ in range(num):
|
||||
t = utxos.pop()
|
||||
inputs = [{"txid": t["txid"], "vout": t["vout"]}]
|
||||
outputs = {}
|
||||
change = t['amount'] - fee
|
||||
outputs[addr] = float(satoshi_round(change))
|
||||
rawtx = node.createrawtransaction(inputs, outputs)
|
||||
newtx = rawtx[0:92]
|
||||
newtx = newtx + txouts
|
||||
newtx = newtx + rawtx[94:]
|
||||
signresult = node.signrawtransaction(newtx, None, None, "NONE")
|
||||
txid = node.sendrawtransaction(signresult["hex"], True)
|
||||
txids.append(txid)
|
||||
return txids
|
||||
|
||||
def mine_large_block(node, utxos=None):
|
||||
# generate a 66k transaction,
|
||||
# and 14 of them is close to the 1MB block limit
|
||||
num = 14
|
||||
txouts = gen_return_txouts()
|
||||
utxos = utxos if utxos is not None else []
|
||||
if len(utxos) < num:
|
||||
utxos.clear()
|
||||
utxos.extend(node.listunspent())
|
||||
fee = 100 * node.getnetworkinfo()["relayfee"]
|
||||
create_lots_of_big_transactions(node, txouts, utxos, num, fee=fee)
|
||||
node.generate(1)
|
||||
|
||||
def find_vout_for_address(node, txid, addr):
|
||||
"""
|
||||
Locate the vout index of the given transaction sending to the
|
||||
given address. Raises runtime error exception if not found.
|
||||
"""
|
||||
tx = node.getrawtransaction(txid, True)
|
||||
for i in range(len(tx["vout"])):
|
||||
if any([addr == a for a in tx["vout"][i]["scriptPubKey"]["addresses"]]):
|
||||
return i
|
||||
raise RuntimeError("Vout not found for address: txid=%s, addr=%s" % (txid, addr))
|
||||
|
||||
# PIVX specific utils
|
||||
DEFAULT_FEE = 0.01
|
||||
SPORK_ACTIVATION_TIME = 1563253447
|
||||
SPORK_DEACTIVATION_TIME = 4070908800
|
||||
|
||||
def DecimalAmt(x):
|
||||
"""Return Decimal from float for equality checks against rpc outputs"""
|
||||
return Decimal("{:0.8f}".format(x))
|
||||
|
||||
# Find a coinstake/coinbase address on the node, filtering by the number of UTXOs it has.
|
||||
# If no filter is provided, returns the coinstake/coinbase address on the node containing
|
||||
# the greatest number of spendable UTXOs.
|
||||
# The default cached chain has one address per coinbase output.
|
||||
def get_coinstake_address(node, expected_utxos=None):
|
||||
addrs = [utxo['address'] for utxo in node.listunspent() if utxo['generated']]
|
||||
assert(len(set(addrs)) > 0)
|
||||
|
||||
if expected_utxos is None:
|
||||
addrs = [(addrs.count(a), a) for a in set(addrs)]
|
||||
return sorted(addrs, reverse=True)[0][1]
|
||||
|
||||
addrs = [a for a in set(addrs) if addrs.count(a) == expected_utxos]
|
||||
assert(len(addrs) > 0)
|
||||
return addrs[0]
|
||||
|
||||
# Deterministic masternodes
|
||||
def is_coin_locked_by(node, outpoint):
|
||||
return outpoint.to_json() in node.listlockunspent()
|
||||
|
||||
def get_collateral_vout(json_tx):
|
||||
funding_txidn = -1
|
||||
for o in json_tx["vout"]:
|
||||
if o["value"] == Decimal('100'):
|
||||
funding_txidn = o["n"]
|
||||
break
|
||||
assert_greater_than(funding_txidn, -1)
|
||||
return funding_txidn
|
||||
|
||||
# owner and voting keys are created from controller node.
|
||||
# operator keys are created, if operator_keys is None.
|
||||
def create_new_dmn(idx, controller, payout_addr, operator_keys):
|
||||
port = p2p_port(idx) if idx <= MAX_NODES else p2p_port(MAX_NODES) + (idx - MAX_NODES)
|
||||
ipport = "127.0.0.1:" + str(port)
|
||||
owner_addr = controller.getnewaddress("mnowner-%d" % idx)
|
||||
voting_addr = controller.getnewaddress("mnvoting-%d" % idx)
|
||||
if operator_keys is None:
|
||||
bls_keypair = controller.generateblskeypair()
|
||||
operator_pk = bls_keypair["public"]
|
||||
operator_sk = bls_keypair["secret"]
|
||||
else:
|
||||
operator_pk = operator_keys[0]
|
||||
operator_sk = operator_keys[1]
|
||||
return messages.Masternode(idx, owner_addr, operator_pk, voting_addr, ipport, payout_addr, operator_sk)
|
||||
|
||||
def spend_mn_collateral(spender, dmn):
|
||||
inputs = [dmn.collateral.to_json()]
|
||||
outputs = {spender.getnewaddress(): Decimal('99.99')}
|
||||
sig_res = spender.signrawtransaction(spender.createrawtransaction(inputs, outputs))
|
||||
assert_equal(sig_res['complete'], True)
|
||||
return spender.sendrawtransaction(sig_res['hex'])
|
||||
@@ -5,8 +5,8 @@
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from .interface_btc import BTCInterface
|
||||
from .chainparams import Coins
|
||||
from .btc import BTCInterface
|
||||
from basicswap.chainparams import Coins
|
||||
|
||||
|
||||
class LTCInterface(BTCInterface):
|
||||
42
basicswap/interface/nmc.py
Normal file
42
basicswap/interface/nmc.py
Normal file
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2020-2022 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from .btc import BTCInterface
|
||||
from basicswap.chainparams import Coins
|
||||
from basicswap.util import (
|
||||
make_int,
|
||||
)
|
||||
|
||||
|
||||
class NMCInterface(BTCInterface):
|
||||
@staticmethod
|
||||
def coin_type():
|
||||
return Coins.NMC
|
||||
|
||||
def getLockTxHeight(self, txid, dest_address, bid_amount, rescan_from, find_index=False):
|
||||
self._log.debug('[rm] scantxoutset start') # scantxoutset is slow
|
||||
ro = self.rpc_callback('scantxoutset', ['start', ['addr({})'.format(dest_address)]]) # TODO: Use combo(address) where possible
|
||||
self._log.debug('[rm] scantxoutset end')
|
||||
return_txid = True if txid is None else False
|
||||
for o in ro['unspents']:
|
||||
if txid and o['txid'] != txid.hex():
|
||||
continue
|
||||
# Verify amount
|
||||
if make_int(o['amount']) != int(bid_amount):
|
||||
self._log.warning('Found output to lock tx address of incorrect value: %s, %s', str(o['amount']), o['txid'])
|
||||
continue
|
||||
|
||||
rv = {
|
||||
'depth': 0,
|
||||
'height': o['height']}
|
||||
if o['height'] > 0:
|
||||
rv['depth'] = ro['height'] - o['height']
|
||||
if find_index:
|
||||
rv['index'] = o['vout']
|
||||
if return_txid:
|
||||
rv['txid'] = o['txid']
|
||||
return rv
|
||||
@@ -8,30 +8,30 @@
|
||||
import hashlib
|
||||
from enum import IntEnum
|
||||
|
||||
from .contrib.test_framework.messages import (
|
||||
from basicswap.contrib.test_framework.messages import (
|
||||
CTxOutPart,
|
||||
)
|
||||
from .contrib.test_framework.script import (
|
||||
from basicswap.contrib.test_framework.script import (
|
||||
CScript,
|
||||
OP_0,
|
||||
OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG
|
||||
)
|
||||
from .util import (
|
||||
from basicswap.util import (
|
||||
i2b,
|
||||
ensure,
|
||||
make_int,
|
||||
TemporaryError,
|
||||
)
|
||||
from .util.script import (
|
||||
from basicswap.util.script import (
|
||||
getP2WSH,
|
||||
getCompactSizeLen,
|
||||
getWitnessElementLen,
|
||||
)
|
||||
from .util.address import (
|
||||
from basicswap.util.address import (
|
||||
toWIF,
|
||||
encodeStealthAddress)
|
||||
from .chainparams import Coins, chainparams
|
||||
from .interface_btc import BTCInterface
|
||||
from basicswap.chainparams import Coins, chainparams
|
||||
from .btc import BTCInterface
|
||||
|
||||
|
||||
class BalanceTypes(IntEnum):
|
||||
@@ -65,9 +65,13 @@ class PARTInterface(BTCInterface):
|
||||
def txoType():
|
||||
return CTxOutPart
|
||||
|
||||
def setDefaults(self) -> None:
|
||||
super().setDefaults()
|
||||
self._anon_tx_ring_size = 8 # TODO: Make option
|
||||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
super().__init__(coin_settings, network, swap_client)
|
||||
self.setAnonTxRingSize(int(coin_settings.get('anon_tx_ring_size', 12)))
|
||||
|
||||
def setAnonTxRingSize(self, value):
|
||||
ensure(value >= 3 and value < 33, 'Invalid anon_tx_ring_size value')
|
||||
self._anon_tx_ring_size = value
|
||||
|
||||
def knownWalletSeed(self):
|
||||
# TODO: Double check
|
||||
@@ -168,7 +172,7 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
# Nonce is derived from vkbv, ephemeral_key isn't used
|
||||
ephemeral_key = i2b(self.getNewSecretKey())
|
||||
ephemeral_pubkey = self.getPubkey(ephemeral_key)
|
||||
assert(len(ephemeral_pubkey) == 33)
|
||||
assert (len(ephemeral_pubkey) == 33)
|
||||
nonce = self.getScriptLockTxNonce(vkbv)
|
||||
p2wsh_addr = self.encode_p2wsh(getP2WSH(script))
|
||||
inputs = []
|
||||
@@ -188,7 +192,7 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
|
||||
tx_obj = self.rpc_callback('decoderawtransaction', [tx_hex])
|
||||
|
||||
assert(len(tx_obj['vout']) == 1)
|
||||
assert (len(tx_obj['vout']) == 1)
|
||||
txo = tx_obj['vout'][0]
|
||||
blinded_info = self.rpc_callback('rewindrangeproof', [txo['rangeproof'], txo['valueCommitment'], nonce.hex()])
|
||||
|
||||
@@ -203,11 +207,11 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
|
||||
def createScriptLockRefundTx(self, tx_lock_bytes, script_lock, Kal, Kaf, lock1_value, csv_val, tx_fee_rate, vkbv):
|
||||
lock_tx_obj = self.rpc_callback('decoderawtransaction', [tx_lock_bytes.hex()])
|
||||
assert(self.getTxid(tx_lock_bytes).hex() == lock_tx_obj['txid'])
|
||||
assert (self.getTxid(tx_lock_bytes).hex() == lock_tx_obj['txid'])
|
||||
# Nonce is derived from vkbv, ephemeral_key isn't used
|
||||
ephemeral_key = i2b(self.getNewSecretKey())
|
||||
ephemeral_pubkey = self.getPubkey(ephemeral_key)
|
||||
assert(len(ephemeral_pubkey) == 33)
|
||||
assert (len(ephemeral_pubkey) == 33)
|
||||
nonce = self.getScriptLockTxNonce(vkbv)
|
||||
output_nonce = self.getScriptLockRefundTxNonce(vkbv)
|
||||
|
||||
@@ -663,7 +667,7 @@ class PARTInterfaceAnon(PARTInterface):
|
||||
wif_scan_key = toWIF(wif_prefix, kbv)
|
||||
self.rpc_callback('importstealthaddress', [wif_scan_key, Kbs.hex()])
|
||||
self._log.info('Imported watch-only sx_addr: {}'.format(sx_addr))
|
||||
self._log.info('Rescanning chain from height: {}'.format(restore_height))
|
||||
self._log.info('Rescanning {} chain from height: {}'.format(self.coin_name(), restore_height))
|
||||
self.rpc_callback('rescanblockchain', [restore_height])
|
||||
|
||||
params = [{'include_watchonly': True, 'search': sx_addr}]
|
||||
@@ -671,7 +675,7 @@ class PARTInterfaceAnon(PARTInterface):
|
||||
|
||||
if len(txns) == 1:
|
||||
tx = txns[0]
|
||||
assert(tx['outputs'][0]['stealth_address'] == sx_addr) # Should not be possible
|
||||
assert (tx['outputs'][0]['stealth_address'] == sx_addr) # Should not be possible
|
||||
ensure(tx['outputs'][0]['type'] == 'anon', 'Output is not anon')
|
||||
|
||||
if make_int(tx['outputs'][0]['amount']) == cb_swap_value:
|
||||
@@ -696,7 +700,7 @@ class PARTInterfaceAnon(PARTInterface):
|
||||
wif_spend_key = toWIF(wif_prefix, kbs)
|
||||
self.rpc_callback('importstealthaddress', [wif_scan_key, wif_spend_key])
|
||||
self._log.info('Imported spend key for sx_addr: {}'.format(sx_addr))
|
||||
self._log.info('Rescanning chain from height: {}'.format(restore_height))
|
||||
self._log.info('Rescanning {} chain from height: {}'.format(self.coin_name(), restore_height))
|
||||
self.rpc_callback('rescanblockchain', [restore_height])
|
||||
|
||||
autxos = self.rpc_callback('listunspentanon', [1, 9999999, [sx_addr]])
|
||||
@@ -708,6 +712,7 @@ class PARTInterfaceAnon(PARTInterface):
|
||||
|
||||
utxo = autxos[0]
|
||||
utxo_sats = make_int(utxo['amount'])
|
||||
|
||||
if spend_actual_balance and utxo_sats != cb_swap_value:
|
||||
self._log.warning('Spending actual balance {}, not swap value {}.'.format(utxo_sats, cb_swap_value))
|
||||
cb_swap_value = utxo_sats
|
||||
@@ -5,8 +5,8 @@
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from .interface_btc import BTCInterface
|
||||
from .contrib.test_framework.messages import (
|
||||
from .btc import BTCInterface
|
||||
from basicswap.contrib.test_framework.messages import (
|
||||
CTxOut)
|
||||
|
||||
|
||||
56
basicswap/interface/pivx.py
Normal file
56
basicswap/interface/pivx.py
Normal file
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2020 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from .btc import BTCInterface
|
||||
from basicswap.chainparams import Coins
|
||||
from .contrib.pivx_test_framework.messages import (
|
||||
CBlock,
|
||||
ToHex,
|
||||
FromHex)
|
||||
|
||||
|
||||
class PIVXInterface(BTCInterface):
|
||||
@staticmethod
|
||||
def coin_type():
|
||||
return Coins.PIVX
|
||||
|
||||
def createRawSignedTransaction(self, addr_to, amount):
|
||||
txn = self.rpc_callback('createrawtransaction', [[], {addr_to: self.format_amount(amount)}])
|
||||
|
||||
fee_rate, fee_src = self.get_fee_rate(self._conf_target)
|
||||
self._log.debug(f'Fee rate: {fee_rate}, source: {fee_src}, block target: {self._conf_target}')
|
||||
|
||||
options = {
|
||||
'lockUnspents': True,
|
||||
'feeRate': fee_rate,
|
||||
}
|
||||
txn_funded = self.rpc_callback('fundrawtransaction', [txn, options])['hex']
|
||||
txn_signed = self.rpc_callback('signrawtransaction', [txn_funded])['hex']
|
||||
return txn_signed
|
||||
|
||||
def getBlockWithTxns(self, block_hash):
|
||||
# TODO: Bypass decoderawtransaction and getblockheader
|
||||
block = self.rpc_callback('getblock', [block_hash, False])
|
||||
block_header = self.rpc_callback('getblockheader', [block_hash])
|
||||
decoded_block = CBlock()
|
||||
decoded_block = FromHex(decoded_block, block)
|
||||
|
||||
tx_rv = []
|
||||
for tx in decoded_block.vtx:
|
||||
tx_dec = self.rpc_callback('decoderawtransaction', [ToHex(tx)])
|
||||
tx_rv.append(tx_dec)
|
||||
|
||||
block_rv = {
|
||||
'hash': block_hash,
|
||||
'tx': tx_rv,
|
||||
'confirmations': block_header['confirmations'],
|
||||
'height': block_header['height'],
|
||||
'version': block_header['version'],
|
||||
'merkleroot': block_header['merkleroot'],
|
||||
}
|
||||
|
||||
return block_rv
|
||||
@@ -1,41 +1,41 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2020-2021 tecnovert
|
||||
# Copyright (c) 2020-2022 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
import time
|
||||
import json
|
||||
import logging
|
||||
|
||||
import basicswap.contrib.ed25519_fast as edf
|
||||
import basicswap.ed25519_fast_util as edu
|
||||
import basicswap.util_xmr as xmr_util
|
||||
from coincurve.ed25519 import (
|
||||
ed25519_add,
|
||||
ed25519_get_pubkey,
|
||||
ed25519_scalar_add,
|
||||
ed25519_add)
|
||||
)
|
||||
from coincurve.keys import PrivateKey
|
||||
from coincurve.dleag import (
|
||||
verify_ed25519_point,
|
||||
dleag_proof_len,
|
||||
dleag_prove,
|
||||
dleag_verify,
|
||||
dleag_prove)
|
||||
dleag_proof_len,
|
||||
verify_ed25519_point,
|
||||
)
|
||||
|
||||
from .util import (
|
||||
ensure,
|
||||
from basicswap.util import (
|
||||
dumpj,
|
||||
ensure,
|
||||
make_int,
|
||||
TemporaryError)
|
||||
from .rpc_xmr import (
|
||||
from basicswap.rpc_xmr import (
|
||||
make_xmr_rpc_func,
|
||||
make_xmr_rpc2_func,
|
||||
make_xmr_wallet_rpc_func)
|
||||
from .util import (
|
||||
from basicswap.util import (
|
||||
b2i, b2h)
|
||||
from .chainparams import CoinInterface, Coins
|
||||
|
||||
XMR_COIN = 10 ** 12
|
||||
from basicswap.chainparams import XMR_COIN, CoinInterface, Coins
|
||||
|
||||
|
||||
class XMRInterface(CoinInterface):
|
||||
@@ -67,7 +67,7 @@ class XMRInterface(CoinInterface):
|
||||
super().__init__(network)
|
||||
self.rpc_cb = make_xmr_rpc_func(coin_settings['rpcport'], host=coin_settings.get('rpchost', '127.0.0.1'))
|
||||
self.rpc_cb2 = make_xmr_rpc2_func(coin_settings['rpcport'], host=coin_settings.get('rpchost', '127.0.0.1')) # non-json endpoint
|
||||
self.rpc_wallet_cb = make_xmr_wallet_rpc_func(coin_settings['walletrpcport'], coin_settings['walletrpcauth'])
|
||||
self.rpc_wallet_cb = make_xmr_wallet_rpc_func(coin_settings['walletrpcport'], coin_settings['walletrpcauth'], host=coin_settings.get('walletrpchost', '127.0.0.1'))
|
||||
|
||||
self.blocks_confirmed = coin_settings['blocks_confirmed']
|
||||
self._restore_height = coin_settings.get('restore_height', 0)
|
||||
@@ -110,29 +110,41 @@ class XMRInterface(CoinInterface):
|
||||
with self._mx_wallet:
|
||||
self.rpc_wallet_cb('open_wallet', {'filename': self._wallet_filename})
|
||||
|
||||
def testDaemonRPC(self):
|
||||
def testDaemonRPC(self, with_wallet=True):
|
||||
self.rpc_wallet_cb('get_languages')
|
||||
|
||||
def getDaemonVersion(self):
|
||||
return self.rpc_wallet_cb('get_version')['version']
|
||||
|
||||
def getBlockchainInfo(self):
|
||||
rv = {}
|
||||
get_height = self.rpc_cb2('get_height', timeout=30)
|
||||
rv = {
|
||||
'blocks': get_height['height'],
|
||||
'verificationprogress': 0.0,
|
||||
}
|
||||
|
||||
# get_block_count returns "Internal error" if bootstrap-daemon is active
|
||||
# rv['blocks'] = self.rpc_cb('get_block_count')['count']
|
||||
rv['blocks'] = self.rpc_cb2('get_height', timeout=30)['height']
|
||||
try:
|
||||
# get_block_count.block_count is how many blocks are in the longest chain known to the node.
|
||||
# get_block_count returns "Internal error" if bootstrap-daemon is active
|
||||
if get_height['untrusted'] is True:
|
||||
rv['bootstrapping'] = True
|
||||
get_info = self.rpc_cb2('get_info', timeout=30)
|
||||
if 'height_without_bootstrap' in get_info:
|
||||
rv['blocks'] = get_info['height_without_bootstrap']
|
||||
|
||||
# sync_info = self.rpc_cb('sync_info', timeout=30)
|
||||
# rv['verificationprogress'] = 0.0 if 'spans' in sync_info else 1.0
|
||||
rv['verificationprogress'] = 0.0
|
||||
rv['known_block_count'] = get_info['height']
|
||||
if rv['known_block_count'] > rv['blocks']:
|
||||
rv['verificationprogress'] = rv['blocks'] / rv['known_block_count']
|
||||
else:
|
||||
rv['known_block_count'] = self.rpc_cb('get_block_count', timeout=30)['count']
|
||||
rv['verificationprogress'] = rv['blocks'] / rv['known_block_count']
|
||||
except Exception as e:
|
||||
self._log.warning('XMR get_block_count failed with: %s', str(e))
|
||||
rv['verificationprogress'] = 0.0
|
||||
|
||||
return rv
|
||||
|
||||
def getChainHeight(self):
|
||||
# get_block_count returns "Internal error" if bootstrap-daemon is active
|
||||
# return self.rpc_cb('get_info')['height']
|
||||
# return self.rpc_cb('get_block_count')['count']
|
||||
return self.rpc_cb2('get_height', timeout=30)['height']
|
||||
|
||||
def getWalletInfo(self):
|
||||
@@ -155,7 +167,6 @@ class XMRInterface(CoinInterface):
|
||||
|
||||
def getNewAddress(self, placeholder):
|
||||
with self._mx_wallet:
|
||||
self._log.warning('TODO - subaddress?')
|
||||
self.rpc_wallet_cb('open_wallet', {'filename': self._wallet_filename})
|
||||
return self.rpc_wallet_cb('create_address', {'account_index': 0})['address']
|
||||
|
||||
@@ -188,7 +199,7 @@ class XMRInterface(CoinInterface):
|
||||
|
||||
def verifyKey(self, k):
|
||||
i = b2i(k)
|
||||
return(i < edf.l and i > 8)
|
||||
return (i < edf.l and i > 8)
|
||||
|
||||
def verifyPubkey(self, pubkey_bytes):
|
||||
# Calls ed25519_decode_check_point() in secp256k1
|
||||
@@ -217,7 +228,7 @@ class XMRInterface(CoinInterface):
|
||||
def encodeSharedAddress(self, Kbv, Kbs):
|
||||
return xmr_util.encode_address(Kbv, Kbs)
|
||||
|
||||
def publishBLockTx(self, Kbv, Kbs, output_amount, feerate):
|
||||
def publishBLockTx(self, Kbv, Kbs, output_amount, feerate, delay_for=10):
|
||||
with self._mx_wallet:
|
||||
self.rpc_wallet_cb('open_wallet', {'filename': self._wallet_filename})
|
||||
|
||||
@@ -230,14 +241,17 @@ class XMRInterface(CoinInterface):
|
||||
self._log.info('publishBLockTx %s to address_b58 %s', rv['tx_hash'], shared_addr)
|
||||
tx_hash = bytes.fromhex(rv['tx_hash'])
|
||||
|
||||
# Debug
|
||||
for i in range(10):
|
||||
params = {'out': True, 'pending': True, 'failed': True, 'pool': True, }
|
||||
rv = self.rpc_wallet_cb('get_transfers', params)
|
||||
self._log.info('[rm] get_transfers {}'.format(dumpj(rv)))
|
||||
if 'pending' not in rv:
|
||||
break
|
||||
time.sleep(1)
|
||||
if self._sc.debug:
|
||||
i = 0
|
||||
while not self._sc.delay_event.is_set():
|
||||
params = {'out': True, 'pending': True, 'failed': True, 'pool': True, }
|
||||
rv = self.rpc_wallet_cb('get_transfers', params)
|
||||
self._log.debug('get_transfers {}'.format(dumpj(rv)))
|
||||
if 'pending' not in rv:
|
||||
break
|
||||
if i >= delay_for:
|
||||
break
|
||||
self._sc.delay_event.wait(1.0)
|
||||
|
||||
return tx_hash
|
||||
|
||||
@@ -333,7 +347,6 @@ class XMRInterface(CoinInterface):
|
||||
return True
|
||||
|
||||
# TODO: Is it necessary to check the address?
|
||||
|
||||
'''
|
||||
rv = self.rpc_wallet_cb('get_balance')
|
||||
print('get_balance', rv)
|
||||
@@ -346,7 +359,9 @@ class XMRInterface(CoinInterface):
|
||||
|
||||
if i >= num_tries:
|
||||
raise ValueError('Balance not confirming on node')
|
||||
time.sleep(1)
|
||||
self._sc.delay_event.wait(1.0)
|
||||
if self._sc.delay_event.is_set():
|
||||
raise ValueError('Stopped')
|
||||
|
||||
return False
|
||||
|
||||
@@ -431,7 +446,7 @@ class XMRInterface(CoinInterface):
|
||||
params['priority'] = self._fee_priority
|
||||
|
||||
rv = self.rpc_wallet_cb('sweep_all', params)
|
||||
print('sweep_all', rv)
|
||||
self._log.debug('sweep_all {}'.format(json.dumps(rv)))
|
||||
|
||||
return bytes.fromhex(rv['tx_hash_list'][0])
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2020-2021 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from .interface_btc import BTCInterface
|
||||
from .chainparams import Coins
|
||||
|
||||
|
||||
class NMCInterface(BTCInterface):
|
||||
@staticmethod
|
||||
def coin_type():
|
||||
return Coins.NMC
|
||||
|
||||
def getLockTxHeight(self, txid, dest_address, bid_amount, rescan_from, find_index=False):
|
||||
raise ValueError('TODO: Use scantxoutset')
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2020-2021 tecnovert
|
||||
# Copyright (c) 2020-2022 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
@@ -16,6 +16,7 @@ from .basicswap_util import (
|
||||
)
|
||||
from .chainparams import (
|
||||
Coins,
|
||||
chainparams,
|
||||
)
|
||||
from .ui.util import (
|
||||
PAGE_LIMIT,
|
||||
@@ -27,7 +28,9 @@ from .ui.util import (
|
||||
get_data_entry_or,
|
||||
have_data_entry,
|
||||
tickerToCoinId,
|
||||
listOldBidStates,
|
||||
)
|
||||
from .ui.page_offers import postNewOffer
|
||||
from .protocols.xmr_swap_1 import recoverNoScriptTxnWithKey, getChainBSplitKey
|
||||
|
||||
|
||||
@@ -59,7 +62,29 @@ def withdraw_coin(swap_client, coin_type, post_string, is_json):
|
||||
return {'txid': txid_hex}
|
||||
|
||||
|
||||
def js_coins(self, url_split, post_string, is_json):
|
||||
swap_client = self.server.swap_client
|
||||
|
||||
coins = []
|
||||
for coin in Coins:
|
||||
cc = swap_client.coin_clients[coin]
|
||||
entry = {
|
||||
'id': int(coin),
|
||||
'ticker': chainparams[cc['coin']]['ticker'],
|
||||
'name': cc['name'].capitalize(),
|
||||
'active': False if cc['connection_type'] == 'none' else True,
|
||||
}
|
||||
if coin == Coins.PART_ANON:
|
||||
entry['variant'] = 'Anon'
|
||||
elif coin == Coins.PART_BLIND:
|
||||
entry['variant'] = 'Blind'
|
||||
coins.append(entry)
|
||||
|
||||
return bytes(json.dumps(coins), 'UTF-8')
|
||||
|
||||
|
||||
def js_wallets(self, url_split, post_string, is_json):
|
||||
swap_client = self.server.swap_client
|
||||
if len(url_split) > 3:
|
||||
ticker_str = url_split[3]
|
||||
coin_type = tickerToCoinId(ticker_str)
|
||||
@@ -67,13 +92,19 @@ def js_wallets(self, url_split, post_string, is_json):
|
||||
if len(url_split) > 4:
|
||||
cmd = url_split[4]
|
||||
if cmd == 'withdraw':
|
||||
return bytes(json.dumps(withdraw_coin(self.server.swap_client, coin_type, post_string, is_json)), 'UTF-8')
|
||||
return bytes(json.dumps(withdraw_coin(swap_client, coin_type, post_string, is_json)), 'UTF-8')
|
||||
if cmd == 'nextdepositaddr':
|
||||
return bytes(json.dumps(swap_client.cacheNewAddressForCoin(coin_type)), 'UTF-8')
|
||||
raise ValueError('Unknown command')
|
||||
return bytes(json.dumps(self.server.swap_client.getWalletInfo(coin_type)), 'UTF-8')
|
||||
return bytes(json.dumps(self.server.swap_client.getWalletsInfo()), 'UTF-8')
|
||||
|
||||
rv = swap_client.getWalletInfo(coin_type)
|
||||
rv.update(swap_client.getBlockchainInfo(coin_type))
|
||||
return bytes(json.dumps(rv), 'UTF-8')
|
||||
return bytes(json.dumps(self.server.swap_client.getWalletsInfo({'ticker_key': True})), 'UTF-8')
|
||||
|
||||
|
||||
def js_offers(self, url_split, post_string, is_json, sent=False):
|
||||
swap_client = self.server.swap_client
|
||||
offer_id = None
|
||||
if len(url_split) > 3:
|
||||
if url_split[3] == 'new':
|
||||
@@ -84,7 +115,7 @@ def js_offers(self, url_split, post_string, is_json, sent=False):
|
||||
form_data['is_json'] = True
|
||||
else:
|
||||
form_data = urllib.parse.parse_qs(post_string)
|
||||
offer_id = self.postNewOffer(form_data)
|
||||
offer_id = postNewOffer(swap_client, form_data)
|
||||
rv = {'offer_id': offer_id.hex()}
|
||||
return bytes(json.dumps(rv), 'UTF-8')
|
||||
offer_id = bytes.fromhex(url_split[3])
|
||||
@@ -112,18 +143,18 @@ def js_offers(self, url_split, post_string, is_json, sent=False):
|
||||
|
||||
if have_data_entry(post_data, 'sort_by'):
|
||||
sort_by = get_data_entry(post_data, 'sort_by')
|
||||
assert(sort_by in ['created_at', 'rate']), 'Invalid sort by'
|
||||
assert (sort_by in ['created_at', 'rate']), 'Invalid sort by'
|
||||
filters['sort_by'] = sort_by
|
||||
if have_data_entry(post_data, 'sort_dir'):
|
||||
sort_dir = get_data_entry(post_data, 'sort_dir')
|
||||
assert(sort_dir in ['asc', 'desc']), 'Invalid sort dir'
|
||||
assert (sort_dir in ['asc', 'desc']), 'Invalid sort dir'
|
||||
filters['sort_dir'] = sort_dir
|
||||
|
||||
if b'offset' in post_data:
|
||||
filters['offset'] = int(get_data_entry(post_data, 'offset'))
|
||||
if b'limit' in post_data:
|
||||
filters['limit'] = int(get_data_entry(post_data, 'limit'))
|
||||
assert(filters['limit'] > 0 and filters['limit'] <= PAGE_LIMIT), 'Invalid limit'
|
||||
assert (filters['limit'] > 0 and filters['limit'] <= PAGE_LIMIT), 'Invalid limit'
|
||||
|
||||
offers = self.server.swap_client.listOffers(sent, filters)
|
||||
rv = []
|
||||
@@ -163,10 +194,10 @@ def js_bids(self, url_split, post_string, is_json):
|
||||
post_data = urllib.parse.parse_qs(post_string)
|
||||
|
||||
offer_id = bytes.fromhex(get_data_entry(post_data, 'offer_id'))
|
||||
assert(len(offer_id) == 28)
|
||||
assert (len(offer_id) == 28)
|
||||
|
||||
offer = swap_client.getOffer(offer_id)
|
||||
assert(offer), 'Offer not found.'
|
||||
assert (offer), 'Offer not found.'
|
||||
|
||||
ci_from = swap_client.ci(offer.coin_from)
|
||||
ci_to = swap_client.ci(offer.coin_to)
|
||||
@@ -205,7 +236,7 @@ def js_bids(self, url_split, post_string, is_json):
|
||||
return bytes(json.dumps(rv), 'UTF-8')
|
||||
|
||||
bid_id = bytes.fromhex(url_split[3])
|
||||
assert(len(bid_id) == 28)
|
||||
assert (len(bid_id) == 28)
|
||||
|
||||
if post_string != '':
|
||||
if is_json:
|
||||
@@ -219,7 +250,7 @@ def js_bids(self, url_split, post_string, is_json):
|
||||
swap_client.setBidDebugInd(bid_id, int(get_data_entry(post_data, 'debugind')))
|
||||
|
||||
bid, xmr_swap, offer, xmr_offer, events = swap_client.getXmrBidAndOffer(bid_id)
|
||||
assert(bid), 'Unknown bid ID'
|
||||
assert (bid), 'Unknown bid ID'
|
||||
|
||||
if post_string != '':
|
||||
if have_data_entry(post_data, 'chainbkeysplit'):
|
||||
@@ -228,6 +259,10 @@ def js_bids(self, url_split, post_string, is_json):
|
||||
remote_key = get_data_entry(post_data, 'remote_key')
|
||||
return bytes(json.dumps({'txid': recoverNoScriptTxnWithKey(swap_client, bid_id, remote_key).hex()}), 'UTF-8')
|
||||
|
||||
if len(url_split) > 4 and url_split[4] == 'states':
|
||||
old_states = listOldBidStates(bid)
|
||||
return bytes(json.dumps(old_states), 'UTF-8')
|
||||
|
||||
edit_bid = False
|
||||
show_txns = False
|
||||
data = describeBid(swap_client, bid, xmr_swap, offer, xmr_offer, events, edit_bid, show_txns, for_api=True)
|
||||
@@ -255,7 +290,7 @@ def js_network(self, url_split, post_string, is_json):
|
||||
|
||||
def js_revokeoffer(self, url_split, post_string, is_json):
|
||||
offer_id = bytes.fromhex(url_split[3])
|
||||
assert(len(offer_id) == 28)
|
||||
assert (len(offer_id) == 28)
|
||||
self.server.swap_client.revokeOffer(offer_id)
|
||||
return bytes(json.dumps({'revoked_offer': offer_id.hex()}), 'UTF-8')
|
||||
|
||||
@@ -304,6 +339,15 @@ def js_rates(self, url_split, post_string, is_json):
|
||||
return bytes(json.dumps(sc.lookupRates(coin_from, coin_to)), 'UTF-8')
|
||||
|
||||
|
||||
def js_rates_list(self, url_split, query_string, is_json):
|
||||
get_data = urllib.parse.parse_qs(query_string)
|
||||
|
||||
sc = self.server.swap_client
|
||||
coin_from = getCoinType(get_data['from'][0])
|
||||
coin_to = getCoinType(get_data['to'][0])
|
||||
return bytes(json.dumps(sc.lookupRates(coin_from, coin_to, True)), 'UTF-8')
|
||||
|
||||
|
||||
def js_rate(self, url_split, post_string, is_json):
|
||||
if post_string == '':
|
||||
raise ValueError('No post data')
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: messages.proto
|
||||
"""Generated protocol buffer code."""
|
||||
from google.protobuf.internal import builder as _builder
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import message as _message
|
||||
from google.protobuf import reflection as _reflection
|
||||
from google.protobuf import descriptor_pool as _descriptor_pool
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
@@ -13,872 +13,35 @@ _sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor.FileDescriptor(
|
||||
name='messages.proto',
|
||||
package='basicswap',
|
||||
syntax='proto3',
|
||||
serialized_options=None,
|
||||
create_key=_descriptor._internal_create_key,
|
||||
serialized_pb=b'\n\x0emessages.proto\x12\tbasicswap\"\xa6\x04\n\x0cOfferMessage\x12\x11\n\tcoin_from\x18\x01 \x01(\r\x12\x0f\n\x07\x63oin_to\x18\x02 \x01(\r\x12\x13\n\x0b\x61mount_from\x18\x03 \x01(\x04\x12\x0c\n\x04rate\x18\x04 \x01(\x04\x12\x16\n\x0emin_bid_amount\x18\x05 \x01(\x04\x12\x12\n\ntime_valid\x18\x06 \x01(\x04\x12\x33\n\tlock_type\x18\x07 \x01(\x0e\x32 .basicswap.OfferMessage.LockType\x12\x12\n\nlock_value\x18\x08 \x01(\r\x12\x11\n\tswap_type\x18\t \x01(\r\x12\x15\n\rproof_address\x18\n \x01(\t\x12\x17\n\x0fproof_signature\x18\x0b \x01(\t\x12\x15\n\rpkhash_seller\x18\x0c \x01(\x0c\x12\x13\n\x0bsecret_hash\x18\r \x01(\x0c\x12\x15\n\rfee_rate_from\x18\x0e \x01(\x04\x12\x13\n\x0b\x66\x65\x65_rate_to\x18\x0f \x01(\x04\x12\x18\n\x10protocol_version\x18\x10 \x01(\r\x12\x19\n\x11\x61mount_negotiable\x18\x11 \x01(\x08\x12\x17\n\x0frate_negotiable\x18\x12 \x01(\x08\"q\n\x08LockType\x12\x0b\n\x07NOT_SET\x10\x00\x12\x18\n\x14SEQUENCE_LOCK_BLOCKS\x10\x01\x12\x16\n\x12SEQUENCE_LOCK_TIME\x10\x02\x12\x13\n\x0f\x41\x42S_LOCK_BLOCKS\x10\x03\x12\x11\n\rABS_LOCK_TIME\x10\x04\"\xb4\x01\n\nBidMessage\x12\x14\n\x0coffer_msg_id\x18\x01 \x01(\x0c\x12\x12\n\ntime_valid\x18\x02 \x01(\x04\x12\x0e\n\x06\x61mount\x18\x03 \x01(\x04\x12\x0c\n\x04rate\x18\x04 \x01(\x04\x12\x14\n\x0cpkhash_buyer\x18\x05 \x01(\x0c\x12\x15\n\rproof_address\x18\x06 \x01(\t\x12\x17\n\x0fproof_signature\x18\x07 \x01(\t\x12\x18\n\x10protocol_version\x18\x08 \x01(\r\"V\n\x10\x42idAcceptMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12\x15\n\rinitiate_txid\x18\x02 \x01(\x0c\x12\x17\n\x0f\x63ontract_script\x18\x03 \x01(\x0c\"=\n\x12OfferRevokeMessage\x12\x14\n\x0coffer_msg_id\x18\x01 \x01(\x0c\x12\x11\n\tsignature\x18\x02 \x01(\x0c\";\n\x10\x42idRejectMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12\x13\n\x0breject_code\x18\x02 \x01(\r\"\xb2\x01\n\rXmrBidMessage\x12\x14\n\x0coffer_msg_id\x18\x01 \x01(\x0c\x12\x12\n\ntime_valid\x18\x02 \x01(\x04\x12\x0e\n\x06\x61mount\x18\x03 \x01(\x04\x12\x0c\n\x04rate\x18\x04 \x01(\x04\x12\x0c\n\x04pkaf\x18\x05 \x01(\x0c\x12\x0c\n\x04kbvf\x18\x06 \x01(\x0c\x12\x12\n\nkbsf_dleag\x18\x07 \x01(\x0c\x12\x0f\n\x07\x64\x65st_af\x18\x08 \x01(\x0c\x12\x18\n\x10protocol_version\x18\t \x01(\r\"T\n\x0fXmrSplitMessage\x12\x0e\n\x06msg_id\x18\x01 \x01(\x0c\x12\x10\n\x08msg_type\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\r\n\x05\x64leag\x18\x04 \x01(\x0c\"\x80\x02\n\x13XmrBidAcceptMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12\x0c\n\x04pkal\x18\x03 \x01(\x0c\x12\x0c\n\x04kbvl\x18\x04 \x01(\x0c\x12\x12\n\nkbsl_dleag\x18\x05 \x01(\x0c\x12\x11\n\ta_lock_tx\x18\x06 \x01(\x0c\x12\x18\n\x10\x61_lock_tx_script\x18\x07 \x01(\x0c\x12\x18\n\x10\x61_lock_refund_tx\x18\x08 \x01(\x0c\x12\x1f\n\x17\x61_lock_refund_tx_script\x18\t \x01(\x0c\x12\x1e\n\x16\x61_lock_refund_spend_tx\x18\n \x01(\x0c\x12\x1d\n\x15\x61l_lock_refund_tx_sig\x18\x0b \x01(\x0c\"r\n\x17XmrBidLockTxSigsMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12$\n\x1c\x61\x66_lock_refund_spend_tx_esig\x18\x02 \x01(\x0c\x12\x1d\n\x15\x61\x66_lock_refund_tx_sig\x18\x03 \x01(\x0c\"X\n\x18XmrBidLockSpendTxMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12\x17\n\x0f\x61_lock_spend_tx\x18\x02 \x01(\x0c\x12\x0f\n\x07kal_sig\x18\x03 \x01(\x0c\"M\n\x18XmrBidLockReleaseMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12\x1d\n\x15\x61l_lock_spend_tx_esig\x18\x02 \x01(\x0c\x62\x06proto3'
|
||||
)
|
||||
|
||||
|
||||
|
||||
_OFFERMESSAGE_LOCKTYPE = _descriptor.EnumDescriptor(
|
||||
name='LockType',
|
||||
full_name='basicswap.OfferMessage.LockType',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
create_key=_descriptor._internal_create_key,
|
||||
values=[
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='NOT_SET', index=0, number=0,
|
||||
serialized_options=None,
|
||||
type=None,
|
||||
create_key=_descriptor._internal_create_key),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='SEQUENCE_LOCK_BLOCKS', index=1, number=1,
|
||||
serialized_options=None,
|
||||
type=None,
|
||||
create_key=_descriptor._internal_create_key),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='SEQUENCE_LOCK_TIME', index=2, number=2,
|
||||
serialized_options=None,
|
||||
type=None,
|
||||
create_key=_descriptor._internal_create_key),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='ABS_LOCK_BLOCKS', index=3, number=3,
|
||||
serialized_options=None,
|
||||
type=None,
|
||||
create_key=_descriptor._internal_create_key),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='ABS_LOCK_TIME', index=4, number=4,
|
||||
serialized_options=None,
|
||||
type=None,
|
||||
create_key=_descriptor._internal_create_key),
|
||||
],
|
||||
containing_type=None,
|
||||
serialized_options=None,
|
||||
serialized_start=467,
|
||||
serialized_end=580,
|
||||
)
|
||||
_sym_db.RegisterEnumDescriptor(_OFFERMESSAGE_LOCKTYPE)
|
||||
|
||||
|
||||
_OFFERMESSAGE = _descriptor.Descriptor(
|
||||
name='OfferMessage',
|
||||
full_name='basicswap.OfferMessage',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
create_key=_descriptor._internal_create_key,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='coin_from', full_name='basicswap.OfferMessage.coin_from', index=0,
|
||||
number=1, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='coin_to', full_name='basicswap.OfferMessage.coin_to', index=1,
|
||||
number=2, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='amount_from', full_name='basicswap.OfferMessage.amount_from', index=2,
|
||||
number=3, type=4, cpp_type=4, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='rate', full_name='basicswap.OfferMessage.rate', index=3,
|
||||
number=4, type=4, cpp_type=4, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='min_bid_amount', full_name='basicswap.OfferMessage.min_bid_amount', index=4,
|
||||
number=5, type=4, cpp_type=4, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='time_valid', full_name='basicswap.OfferMessage.time_valid', index=5,
|
||||
number=6, type=4, cpp_type=4, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='lock_type', full_name='basicswap.OfferMessage.lock_type', index=6,
|
||||
number=7, type=14, cpp_type=8, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='lock_value', full_name='basicswap.OfferMessage.lock_value', index=7,
|
||||
number=8, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='swap_type', full_name='basicswap.OfferMessage.swap_type', index=8,
|
||||
number=9, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='proof_address', full_name='basicswap.OfferMessage.proof_address', index=9,
|
||||
number=10, type=9, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"".decode('utf-8'),
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='proof_signature', full_name='basicswap.OfferMessage.proof_signature', index=10,
|
||||
number=11, type=9, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"".decode('utf-8'),
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='pkhash_seller', full_name='basicswap.OfferMessage.pkhash_seller', index=11,
|
||||
number=12, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='secret_hash', full_name='basicswap.OfferMessage.secret_hash', index=12,
|
||||
number=13, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='fee_rate_from', full_name='basicswap.OfferMessage.fee_rate_from', index=13,
|
||||
number=14, type=4, cpp_type=4, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='fee_rate_to', full_name='basicswap.OfferMessage.fee_rate_to', index=14,
|
||||
number=15, type=4, cpp_type=4, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='protocol_version', full_name='basicswap.OfferMessage.protocol_version', index=15,
|
||||
number=16, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='amount_negotiable', full_name='basicswap.OfferMessage.amount_negotiable', index=16,
|
||||
number=17, type=8, cpp_type=7, label=1,
|
||||
has_default_value=False, default_value=False,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='rate_negotiable', full_name='basicswap.OfferMessage.rate_negotiable', index=17,
|
||||
number=18, type=8, cpp_type=7, label=1,
|
||||
has_default_value=False, default_value=False,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
_OFFERMESSAGE_LOCKTYPE,
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=30,
|
||||
serialized_end=580,
|
||||
)
|
||||
|
||||
|
||||
_BIDMESSAGE = _descriptor.Descriptor(
|
||||
name='BidMessage',
|
||||
full_name='basicswap.BidMessage',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
create_key=_descriptor._internal_create_key,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='offer_msg_id', full_name='basicswap.BidMessage.offer_msg_id', index=0,
|
||||
number=1, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='time_valid', full_name='basicswap.BidMessage.time_valid', index=1,
|
||||
number=2, type=4, cpp_type=4, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='amount', full_name='basicswap.BidMessage.amount', index=2,
|
||||
number=3, type=4, cpp_type=4, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='rate', full_name='basicswap.BidMessage.rate', index=3,
|
||||
number=4, type=4, cpp_type=4, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='pkhash_buyer', full_name='basicswap.BidMessage.pkhash_buyer', index=4,
|
||||
number=5, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='proof_address', full_name='basicswap.BidMessage.proof_address', index=5,
|
||||
number=6, type=9, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"".decode('utf-8'),
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='proof_signature', full_name='basicswap.BidMessage.proof_signature', index=6,
|
||||
number=7, type=9, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"".decode('utf-8'),
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='protocol_version', full_name='basicswap.BidMessage.protocol_version', index=7,
|
||||
number=8, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=583,
|
||||
serialized_end=763,
|
||||
)
|
||||
|
||||
|
||||
_BIDACCEPTMESSAGE = _descriptor.Descriptor(
|
||||
name='BidAcceptMessage',
|
||||
full_name='basicswap.BidAcceptMessage',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
create_key=_descriptor._internal_create_key,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='bid_msg_id', full_name='basicswap.BidAcceptMessage.bid_msg_id', index=0,
|
||||
number=1, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='initiate_txid', full_name='basicswap.BidAcceptMessage.initiate_txid', index=1,
|
||||
number=2, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='contract_script', full_name='basicswap.BidAcceptMessage.contract_script', index=2,
|
||||
number=3, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=765,
|
||||
serialized_end=851,
|
||||
)
|
||||
|
||||
|
||||
_OFFERREVOKEMESSAGE = _descriptor.Descriptor(
|
||||
name='OfferRevokeMessage',
|
||||
full_name='basicswap.OfferRevokeMessage',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
create_key=_descriptor._internal_create_key,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='offer_msg_id', full_name='basicswap.OfferRevokeMessage.offer_msg_id', index=0,
|
||||
number=1, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='signature', full_name='basicswap.OfferRevokeMessage.signature', index=1,
|
||||
number=2, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=853,
|
||||
serialized_end=914,
|
||||
)
|
||||
|
||||
|
||||
_BIDREJECTMESSAGE = _descriptor.Descriptor(
|
||||
name='BidRejectMessage',
|
||||
full_name='basicswap.BidRejectMessage',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
create_key=_descriptor._internal_create_key,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='bid_msg_id', full_name='basicswap.BidRejectMessage.bid_msg_id', index=0,
|
||||
number=1, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='reject_code', full_name='basicswap.BidRejectMessage.reject_code', index=1,
|
||||
number=2, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=916,
|
||||
serialized_end=975,
|
||||
)
|
||||
|
||||
|
||||
_XMRBIDMESSAGE = _descriptor.Descriptor(
|
||||
name='XmrBidMessage',
|
||||
full_name='basicswap.XmrBidMessage',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
create_key=_descriptor._internal_create_key,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='offer_msg_id', full_name='basicswap.XmrBidMessage.offer_msg_id', index=0,
|
||||
number=1, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='time_valid', full_name='basicswap.XmrBidMessage.time_valid', index=1,
|
||||
number=2, type=4, cpp_type=4, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='amount', full_name='basicswap.XmrBidMessage.amount', index=2,
|
||||
number=3, type=4, cpp_type=4, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='rate', full_name='basicswap.XmrBidMessage.rate', index=3,
|
||||
number=4, type=4, cpp_type=4, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='pkaf', full_name='basicswap.XmrBidMessage.pkaf', index=4,
|
||||
number=5, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='kbvf', full_name='basicswap.XmrBidMessage.kbvf', index=5,
|
||||
number=6, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='kbsf_dleag', full_name='basicswap.XmrBidMessage.kbsf_dleag', index=6,
|
||||
number=7, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='dest_af', full_name='basicswap.XmrBidMessage.dest_af', index=7,
|
||||
number=8, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='protocol_version', full_name='basicswap.XmrBidMessage.protocol_version', index=8,
|
||||
number=9, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=978,
|
||||
serialized_end=1156,
|
||||
)
|
||||
|
||||
|
||||
_XMRSPLITMESSAGE = _descriptor.Descriptor(
|
||||
name='XmrSplitMessage',
|
||||
full_name='basicswap.XmrSplitMessage',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
create_key=_descriptor._internal_create_key,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='msg_id', full_name='basicswap.XmrSplitMessage.msg_id', index=0,
|
||||
number=1, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='msg_type', full_name='basicswap.XmrSplitMessage.msg_type', index=1,
|
||||
number=2, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='sequence', full_name='basicswap.XmrSplitMessage.sequence', index=2,
|
||||
number=3, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='dleag', full_name='basicswap.XmrSplitMessage.dleag', index=3,
|
||||
number=4, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=1158,
|
||||
serialized_end=1242,
|
||||
)
|
||||
|
||||
|
||||
_XMRBIDACCEPTMESSAGE = _descriptor.Descriptor(
|
||||
name='XmrBidAcceptMessage',
|
||||
full_name='basicswap.XmrBidAcceptMessage',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
create_key=_descriptor._internal_create_key,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='bid_msg_id', full_name='basicswap.XmrBidAcceptMessage.bid_msg_id', index=0,
|
||||
number=1, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='pkal', full_name='basicswap.XmrBidAcceptMessage.pkal', index=1,
|
||||
number=3, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='kbvl', full_name='basicswap.XmrBidAcceptMessage.kbvl', index=2,
|
||||
number=4, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='kbsl_dleag', full_name='basicswap.XmrBidAcceptMessage.kbsl_dleag', index=3,
|
||||
number=5, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='a_lock_tx', full_name='basicswap.XmrBidAcceptMessage.a_lock_tx', index=4,
|
||||
number=6, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='a_lock_tx_script', full_name='basicswap.XmrBidAcceptMessage.a_lock_tx_script', index=5,
|
||||
number=7, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='a_lock_refund_tx', full_name='basicswap.XmrBidAcceptMessage.a_lock_refund_tx', index=6,
|
||||
number=8, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='a_lock_refund_tx_script', full_name='basicswap.XmrBidAcceptMessage.a_lock_refund_tx_script', index=7,
|
||||
number=9, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='a_lock_refund_spend_tx', full_name='basicswap.XmrBidAcceptMessage.a_lock_refund_spend_tx', index=8,
|
||||
number=10, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='al_lock_refund_tx_sig', full_name='basicswap.XmrBidAcceptMessage.al_lock_refund_tx_sig', index=9,
|
||||
number=11, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=1245,
|
||||
serialized_end=1501,
|
||||
)
|
||||
|
||||
|
||||
_XMRBIDLOCKTXSIGSMESSAGE = _descriptor.Descriptor(
|
||||
name='XmrBidLockTxSigsMessage',
|
||||
full_name='basicswap.XmrBidLockTxSigsMessage',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
create_key=_descriptor._internal_create_key,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='bid_msg_id', full_name='basicswap.XmrBidLockTxSigsMessage.bid_msg_id', index=0,
|
||||
number=1, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='af_lock_refund_spend_tx_esig', full_name='basicswap.XmrBidLockTxSigsMessage.af_lock_refund_spend_tx_esig', index=1,
|
||||
number=2, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='af_lock_refund_tx_sig', full_name='basicswap.XmrBidLockTxSigsMessage.af_lock_refund_tx_sig', index=2,
|
||||
number=3, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=1503,
|
||||
serialized_end=1617,
|
||||
)
|
||||
|
||||
|
||||
_XMRBIDLOCKSPENDTXMESSAGE = _descriptor.Descriptor(
|
||||
name='XmrBidLockSpendTxMessage',
|
||||
full_name='basicswap.XmrBidLockSpendTxMessage',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
create_key=_descriptor._internal_create_key,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='bid_msg_id', full_name='basicswap.XmrBidLockSpendTxMessage.bid_msg_id', index=0,
|
||||
number=1, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='a_lock_spend_tx', full_name='basicswap.XmrBidLockSpendTxMessage.a_lock_spend_tx', index=1,
|
||||
number=2, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='kal_sig', full_name='basicswap.XmrBidLockSpendTxMessage.kal_sig', index=2,
|
||||
number=3, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=1619,
|
||||
serialized_end=1707,
|
||||
)
|
||||
|
||||
|
||||
_XMRBIDLOCKRELEASEMESSAGE = _descriptor.Descriptor(
|
||||
name='XmrBidLockReleaseMessage',
|
||||
full_name='basicswap.XmrBidLockReleaseMessage',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
create_key=_descriptor._internal_create_key,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='bid_msg_id', full_name='basicswap.XmrBidLockReleaseMessage.bid_msg_id', index=0,
|
||||
number=1, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='al_lock_spend_tx_esig', full_name='basicswap.XmrBidLockReleaseMessage.al_lock_spend_tx_esig', index=1,
|
||||
number=2, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=1709,
|
||||
serialized_end=1786,
|
||||
)
|
||||
|
||||
_OFFERMESSAGE.fields_by_name['lock_type'].enum_type = _OFFERMESSAGE_LOCKTYPE
|
||||
_OFFERMESSAGE_LOCKTYPE.containing_type = _OFFERMESSAGE
|
||||
DESCRIPTOR.message_types_by_name['OfferMessage'] = _OFFERMESSAGE
|
||||
DESCRIPTOR.message_types_by_name['BidMessage'] = _BIDMESSAGE
|
||||
DESCRIPTOR.message_types_by_name['BidAcceptMessage'] = _BIDACCEPTMESSAGE
|
||||
DESCRIPTOR.message_types_by_name['OfferRevokeMessage'] = _OFFERREVOKEMESSAGE
|
||||
DESCRIPTOR.message_types_by_name['BidRejectMessage'] = _BIDREJECTMESSAGE
|
||||
DESCRIPTOR.message_types_by_name['XmrBidMessage'] = _XMRBIDMESSAGE
|
||||
DESCRIPTOR.message_types_by_name['XmrSplitMessage'] = _XMRSPLITMESSAGE
|
||||
DESCRIPTOR.message_types_by_name['XmrBidAcceptMessage'] = _XMRBIDACCEPTMESSAGE
|
||||
DESCRIPTOR.message_types_by_name['XmrBidLockTxSigsMessage'] = _XMRBIDLOCKTXSIGSMESSAGE
|
||||
DESCRIPTOR.message_types_by_name['XmrBidLockSpendTxMessage'] = _XMRBIDLOCKSPENDTXMESSAGE
|
||||
DESCRIPTOR.message_types_by_name['XmrBidLockReleaseMessage'] = _XMRBIDLOCKRELEASEMESSAGE
|
||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
||||
|
||||
OfferMessage = _reflection.GeneratedProtocolMessageType('OfferMessage', (_message.Message,), {
|
||||
'DESCRIPTOR' : _OFFERMESSAGE,
|
||||
'__module__' : 'messages_pb2'
|
||||
# @@protoc_insertion_point(class_scope:basicswap.OfferMessage)
|
||||
})
|
||||
_sym_db.RegisterMessage(OfferMessage)
|
||||
|
||||
BidMessage = _reflection.GeneratedProtocolMessageType('BidMessage', (_message.Message,), {
|
||||
'DESCRIPTOR' : _BIDMESSAGE,
|
||||
'__module__' : 'messages_pb2'
|
||||
# @@protoc_insertion_point(class_scope:basicswap.BidMessage)
|
||||
})
|
||||
_sym_db.RegisterMessage(BidMessage)
|
||||
|
||||
BidAcceptMessage = _reflection.GeneratedProtocolMessageType('BidAcceptMessage', (_message.Message,), {
|
||||
'DESCRIPTOR' : _BIDACCEPTMESSAGE,
|
||||
'__module__' : 'messages_pb2'
|
||||
# @@protoc_insertion_point(class_scope:basicswap.BidAcceptMessage)
|
||||
})
|
||||
_sym_db.RegisterMessage(BidAcceptMessage)
|
||||
|
||||
OfferRevokeMessage = _reflection.GeneratedProtocolMessageType('OfferRevokeMessage', (_message.Message,), {
|
||||
'DESCRIPTOR' : _OFFERREVOKEMESSAGE,
|
||||
'__module__' : 'messages_pb2'
|
||||
# @@protoc_insertion_point(class_scope:basicswap.OfferRevokeMessage)
|
||||
})
|
||||
_sym_db.RegisterMessage(OfferRevokeMessage)
|
||||
|
||||
BidRejectMessage = _reflection.GeneratedProtocolMessageType('BidRejectMessage', (_message.Message,), {
|
||||
'DESCRIPTOR' : _BIDREJECTMESSAGE,
|
||||
'__module__' : 'messages_pb2'
|
||||
# @@protoc_insertion_point(class_scope:basicswap.BidRejectMessage)
|
||||
})
|
||||
_sym_db.RegisterMessage(BidRejectMessage)
|
||||
|
||||
XmrBidMessage = _reflection.GeneratedProtocolMessageType('XmrBidMessage', (_message.Message,), {
|
||||
'DESCRIPTOR' : _XMRBIDMESSAGE,
|
||||
'__module__' : 'messages_pb2'
|
||||
# @@protoc_insertion_point(class_scope:basicswap.XmrBidMessage)
|
||||
})
|
||||
_sym_db.RegisterMessage(XmrBidMessage)
|
||||
|
||||
XmrSplitMessage = _reflection.GeneratedProtocolMessageType('XmrSplitMessage', (_message.Message,), {
|
||||
'DESCRIPTOR' : _XMRSPLITMESSAGE,
|
||||
'__module__' : 'messages_pb2'
|
||||
# @@protoc_insertion_point(class_scope:basicswap.XmrSplitMessage)
|
||||
})
|
||||
_sym_db.RegisterMessage(XmrSplitMessage)
|
||||
|
||||
XmrBidAcceptMessage = _reflection.GeneratedProtocolMessageType('XmrBidAcceptMessage', (_message.Message,), {
|
||||
'DESCRIPTOR' : _XMRBIDACCEPTMESSAGE,
|
||||
'__module__' : 'messages_pb2'
|
||||
# @@protoc_insertion_point(class_scope:basicswap.XmrBidAcceptMessage)
|
||||
})
|
||||
_sym_db.RegisterMessage(XmrBidAcceptMessage)
|
||||
|
||||
XmrBidLockTxSigsMessage = _reflection.GeneratedProtocolMessageType('XmrBidLockTxSigsMessage', (_message.Message,), {
|
||||
'DESCRIPTOR' : _XMRBIDLOCKTXSIGSMESSAGE,
|
||||
'__module__' : 'messages_pb2'
|
||||
# @@protoc_insertion_point(class_scope:basicswap.XmrBidLockTxSigsMessage)
|
||||
})
|
||||
_sym_db.RegisterMessage(XmrBidLockTxSigsMessage)
|
||||
|
||||
XmrBidLockSpendTxMessage = _reflection.GeneratedProtocolMessageType('XmrBidLockSpendTxMessage', (_message.Message,), {
|
||||
'DESCRIPTOR' : _XMRBIDLOCKSPENDTXMESSAGE,
|
||||
'__module__' : 'messages_pb2'
|
||||
# @@protoc_insertion_point(class_scope:basicswap.XmrBidLockSpendTxMessage)
|
||||
})
|
||||
_sym_db.RegisterMessage(XmrBidLockSpendTxMessage)
|
||||
|
||||
XmrBidLockReleaseMessage = _reflection.GeneratedProtocolMessageType('XmrBidLockReleaseMessage', (_message.Message,), {
|
||||
'DESCRIPTOR' : _XMRBIDLOCKRELEASEMESSAGE,
|
||||
'__module__' : 'messages_pb2'
|
||||
# @@protoc_insertion_point(class_scope:basicswap.XmrBidLockReleaseMessage)
|
||||
})
|
||||
_sym_db.RegisterMessage(XmrBidLockReleaseMessage)
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0emessages.proto\x12\tbasicswap\"\xa6\x04\n\x0cOfferMessage\x12\x11\n\tcoin_from\x18\x01 \x01(\r\x12\x0f\n\x07\x63oin_to\x18\x02 \x01(\r\x12\x13\n\x0b\x61mount_from\x18\x03 \x01(\x04\x12\x0c\n\x04rate\x18\x04 \x01(\x04\x12\x16\n\x0emin_bid_amount\x18\x05 \x01(\x04\x12\x12\n\ntime_valid\x18\x06 \x01(\x04\x12\x33\n\tlock_type\x18\x07 \x01(\x0e\x32 .basicswap.OfferMessage.LockType\x12\x12\n\nlock_value\x18\x08 \x01(\r\x12\x11\n\tswap_type\x18\t \x01(\r\x12\x15\n\rproof_address\x18\n \x01(\t\x12\x17\n\x0fproof_signature\x18\x0b \x01(\t\x12\x15\n\rpkhash_seller\x18\x0c \x01(\x0c\x12\x13\n\x0bsecret_hash\x18\r \x01(\x0c\x12\x15\n\rfee_rate_from\x18\x0e \x01(\x04\x12\x13\n\x0b\x66\x65\x65_rate_to\x18\x0f \x01(\x04\x12\x18\n\x10protocol_version\x18\x10 \x01(\r\x12\x19\n\x11\x61mount_negotiable\x18\x11 \x01(\x08\x12\x17\n\x0frate_negotiable\x18\x12 \x01(\x08\"q\n\x08LockType\x12\x0b\n\x07NOT_SET\x10\x00\x12\x18\n\x14SEQUENCE_LOCK_BLOCKS\x10\x01\x12\x16\n\x12SEQUENCE_LOCK_TIME\x10\x02\x12\x13\n\x0f\x41\x42S_LOCK_BLOCKS\x10\x03\x12\x11\n\rABS_LOCK_TIME\x10\x04\"\xb4\x01\n\nBidMessage\x12\x14\n\x0coffer_msg_id\x18\x01 \x01(\x0c\x12\x12\n\ntime_valid\x18\x02 \x01(\x04\x12\x0e\n\x06\x61mount\x18\x03 \x01(\x04\x12\x0c\n\x04rate\x18\x04 \x01(\x04\x12\x14\n\x0cpkhash_buyer\x18\x05 \x01(\x0c\x12\x15\n\rproof_address\x18\x06 \x01(\t\x12\x17\n\x0fproof_signature\x18\x07 \x01(\t\x12\x18\n\x10protocol_version\x18\x08 \x01(\r\"V\n\x10\x42idAcceptMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12\x15\n\rinitiate_txid\x18\x02 \x01(\x0c\x12\x17\n\x0f\x63ontract_script\x18\x03 \x01(\x0c\"=\n\x12OfferRevokeMessage\x12\x14\n\x0coffer_msg_id\x18\x01 \x01(\x0c\x12\x11\n\tsignature\x18\x02 \x01(\x0c\";\n\x10\x42idRejectMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12\x13\n\x0breject_code\x18\x02 \x01(\r\"\xb2\x01\n\rXmrBidMessage\x12\x14\n\x0coffer_msg_id\x18\x01 \x01(\x0c\x12\x12\n\ntime_valid\x18\x02 \x01(\x04\x12\x0e\n\x06\x61mount\x18\x03 \x01(\x04\x12\x0c\n\x04rate\x18\x04 \x01(\x04\x12\x0c\n\x04pkaf\x18\x05 \x01(\x0c\x12\x0c\n\x04kbvf\x18\x06 \x01(\x0c\x12\x12\n\nkbsf_dleag\x18\x07 \x01(\x0c\x12\x0f\n\x07\x64\x65st_af\x18\x08 \x01(\x0c\x12\x18\n\x10protocol_version\x18\t \x01(\r\"T\n\x0fXmrSplitMessage\x12\x0e\n\x06msg_id\x18\x01 \x01(\x0c\x12\x10\n\x08msg_type\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\r\n\x05\x64leag\x18\x04 \x01(\x0c\"\x80\x02\n\x13XmrBidAcceptMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12\x0c\n\x04pkal\x18\x03 \x01(\x0c\x12\x0c\n\x04kbvl\x18\x04 \x01(\x0c\x12\x12\n\nkbsl_dleag\x18\x05 \x01(\x0c\x12\x11\n\ta_lock_tx\x18\x06 \x01(\x0c\x12\x18\n\x10\x61_lock_tx_script\x18\x07 \x01(\x0c\x12\x18\n\x10\x61_lock_refund_tx\x18\x08 \x01(\x0c\x12\x1f\n\x17\x61_lock_refund_tx_script\x18\t \x01(\x0c\x12\x1e\n\x16\x61_lock_refund_spend_tx\x18\n \x01(\x0c\x12\x1d\n\x15\x61l_lock_refund_tx_sig\x18\x0b \x01(\x0c\"r\n\x17XmrBidLockTxSigsMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12$\n\x1c\x61\x66_lock_refund_spend_tx_esig\x18\x02 \x01(\x0c\x12\x1d\n\x15\x61\x66_lock_refund_tx_sig\x18\x03 \x01(\x0c\"X\n\x18XmrBidLockSpendTxMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12\x17\n\x0f\x61_lock_spend_tx\x18\x02 \x01(\x0c\x12\x0f\n\x07kal_sig\x18\x03 \x01(\x0c\"M\n\x18XmrBidLockReleaseMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12\x1d\n\x15\x61l_lock_spend_tx_esig\x18\x02 \x01(\x0c\x62\x06proto3')
|
||||
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'messages_pb2', globals())
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
|
||||
DESCRIPTOR._options = None
|
||||
_OFFERMESSAGE._serialized_start=30
|
||||
_OFFERMESSAGE._serialized_end=580
|
||||
_OFFERMESSAGE_LOCKTYPE._serialized_start=467
|
||||
_OFFERMESSAGE_LOCKTYPE._serialized_end=580
|
||||
_BIDMESSAGE._serialized_start=583
|
||||
_BIDMESSAGE._serialized_end=763
|
||||
_BIDACCEPTMESSAGE._serialized_start=765
|
||||
_BIDACCEPTMESSAGE._serialized_end=851
|
||||
_OFFERREVOKEMESSAGE._serialized_start=853
|
||||
_OFFERREVOKEMESSAGE._serialized_end=914
|
||||
_BIDREJECTMESSAGE._serialized_start=916
|
||||
_BIDREJECTMESSAGE._serialized_end=975
|
||||
_XMRBIDMESSAGE._serialized_start=978
|
||||
_XMRBIDMESSAGE._serialized_end=1156
|
||||
_XMRSPLITMESSAGE._serialized_start=1158
|
||||
_XMRSPLITMESSAGE._serialized_end=1242
|
||||
_XMRBIDACCEPTMESSAGE._serialized_start=1245
|
||||
_XMRBIDACCEPTMESSAGE._serialized_end=1501
|
||||
_XMRBIDLOCKTXSIGSMESSAGE._serialized_start=1503
|
||||
_XMRBIDLOCKTXSIGSMESSAGE._serialized_end=1617
|
||||
_XMRBIDLOCKSPENDTXMESSAGE._serialized_start=1619
|
||||
_XMRBIDLOCKSPENDTXMESSAGE._serialized_end=1707
|
||||
_XMRBIDLOCKRELEASEMESSAGE._serialized_start=1709
|
||||
_XMRBIDLOCKRELEASEMESSAGE._serialized_end=1786
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2020 tecnovert
|
||||
# Copyright (c) 2020-2022 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
@@ -11,7 +11,6 @@ from basicswap.script import (
|
||||
OpCodes,
|
||||
)
|
||||
|
||||
|
||||
INITIATE_TX_TIMEOUT = 40 * 60 # TODO: make variable per coin
|
||||
|
||||
|
||||
@@ -48,3 +47,14 @@ def buildContractScript(lock_val, secret_hash, pkh_redeem, pkh_refund, op_lock=O
|
||||
|
||||
def extractScriptSecretHash(script):
|
||||
return script[7:39]
|
||||
|
||||
|
||||
def redeemITx(self, bid_id, session):
|
||||
bid, offer = self.getBidAndOffer(bid_id, session)
|
||||
ci_from = self.ci(offer.coin_from)
|
||||
|
||||
txn = self.createRedeemTxn(ci_from.coin_type(), bid, for_txn_type='initiate')
|
||||
txid = ci_from.publishTx(bytes.fromhex(txn))
|
||||
|
||||
bid.initiate_tx.spend_txid = bytes.fromhex(txid)
|
||||
self.log.debug('Submitted initiate redeem txn %s to %s chain for bid %s', txid, ci_from.coin_name(), bid_id.hex())
|
||||
|
||||
@@ -46,12 +46,18 @@ def recoverNoScriptTxnWithKey(self, bid_id, encoded_key):
|
||||
ci_to = self.ci(offer.coin_to)
|
||||
|
||||
for_ed25519 = True if Coins(offer.coin_to) == Coins.XMR else False
|
||||
|
||||
try:
|
||||
decoded_key_half = ci_to.decodeKey(encoded_key)
|
||||
except Exception as e:
|
||||
raise ValueError('Failed to decode provided key-half: ', str(e))
|
||||
|
||||
if bid.was_sent:
|
||||
kbsl = ci_to.decodeKey(encoded_key)
|
||||
kbsl = decoded_key_half
|
||||
kbsf = self.getPathKey(offer.coin_from, offer.coin_to, bid.created_at, xmr_swap.contract_count, KeyTypes.KBSF, for_ed25519)
|
||||
else:
|
||||
kbsl = self.getPathKey(offer.coin_from, offer.coin_to, bid.created_at, xmr_swap.contract_count, KeyTypes.KBSL, for_ed25519)
|
||||
kbsf = ci_to.decodeKey(encoded_key)
|
||||
kbsf = decoded_key_half
|
||||
ensure(ci_to.verifyKey(kbsl), 'Invalid kbsl')
|
||||
ensure(ci_to.verifyKey(kbsf), 'Invalid kbsf')
|
||||
vkbs = ci_to.sumKeys(kbsl, kbsf)
|
||||
|
||||
@@ -20,10 +20,13 @@ from xmlrpc.client import (
|
||||
from .util import jsonDecimal
|
||||
|
||||
|
||||
def waitForRPC(rpc_func, wallet=None, max_tries=7):
|
||||
def waitForRPC(rpc_func, expect_wallet=True, max_tries=7):
|
||||
for i in range(max_tries + 1):
|
||||
try:
|
||||
rpc_func('getwalletinfo')
|
||||
if expect_wallet:
|
||||
rpc_func('getwalletinfo')
|
||||
else:
|
||||
rpc_func('getblockchaininfo')
|
||||
return
|
||||
except Exception as ex:
|
||||
if i < max_tries:
|
||||
|
||||
34
basicswap/static/css/simple/style.css
Normal file
34
basicswap/static/css/simple/style.css
Normal file
@@ -0,0 +1,34 @@
|
||||
|
||||
.padded_row td
|
||||
{
|
||||
padding-top:1.5em;
|
||||
}
|
||||
|
||||
.bold
|
||||
{
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
.monospace
|
||||
{
|
||||
font-family:monospace;
|
||||
}
|
||||
|
||||
.floatright
|
||||
{
|
||||
position:fixed;
|
||||
top:10px;
|
||||
right:18px;
|
||||
margin: 0;
|
||||
width:calc(33.33% - 25px);
|
||||
}
|
||||
|
||||
.error
|
||||
{
|
||||
background:#ff5b5b;
|
||||
}
|
||||
|
||||
.error_msg
|
||||
{
|
||||
color:red;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 715 B After Width: | Height: | Size: 715 B |
27
basicswap/static/js/new_offer.js
Normal file
27
basicswap/static/js/new_offer.js
Normal file
@@ -0,0 +1,27 @@
|
||||
|
||||
window.addEventListener('DOMContentLoaded', (event) => {
|
||||
let err_msgs = document.querySelectorAll('p.error_msg');
|
||||
for (let i=0; i < err_msgs.length; i++) {
|
||||
err_msg = err_msgs[i].innerText
|
||||
if (err_msg.indexOf('coin_to') >= 0 || err_msg.indexOf('Coin To') >= 0) {
|
||||
e = document.getElementById('coin_to');
|
||||
e.classList.add('error');
|
||||
}
|
||||
if (err_msg.indexOf('Coin From') >= 0) {
|
||||
e = document.getElementById('coin_from');
|
||||
e.classList.add('error');
|
||||
}
|
||||
if (err_msg.indexOf('Amount From') >= 0) {
|
||||
e = document.getElementById('amt_from');
|
||||
e.classList.add('error');
|
||||
}
|
||||
if (err_msg.indexOf('Amount To') >= 0) {
|
||||
e = document.getElementById('amt_to');
|
||||
e.classList.add('error');
|
||||
}
|
||||
if (err_msg.indexOf('Minimum Bid Amount') >= 0) {
|
||||
e = document.getElementById('amt_bid_min');
|
||||
e.classList.add('error');
|
||||
}
|
||||
}
|
||||
});
|
||||
343
basicswap/static/sequence_diagrams/bidder.alt.xu.min.svg
Normal file
343
basicswap/static/sequence_diagrams/bidder.alt.xu.min.svg
Normal file
@@ -0,0 +1,343 @@
|
||||
<svg version="1.1" id="mscgenjsreplaceme" class="mscgenjsreplaceme" xmlns="http://www.w3.org/2000/svg" width="1176" height="1664.36" style="font-family:Helvetica,sans-serif;font-size:12px;font-weight:400;font-style:normal;text-decoration:none;background-color:#fff;stroke:#000;stroke-width:2">
|
||||
<defs>
|
||||
<marker orient="auto" id="mscgenjsreplacemecallback-#0000FF" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path d="m1 1 8 2-8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:#00f"/>
|
||||
</marker>
|
||||
<marker orient="auto" id="mscgenjsreplacemecallback-l-#0000FF" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path d="M17 1 9 3l8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:#00f"/>
|
||||
</marker>
|
||||
<marker orient="auto" id="mscgenjsreplacemecallback-#008800" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path d="m1 1 8 2-8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:#080"/>
|
||||
</marker>
|
||||
<marker orient="auto" id="mscgenjsreplacemecallback-l-#008800" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path d="M17 1 9 3l8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:#080"/>
|
||||
</marker>
|
||||
<marker orient="auto" id="mscgenjsreplacemecallback-#FF0000" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path d="m1 1 8 2-8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:red"/>
|
||||
</marker>
|
||||
<marker orient="auto" id="mscgenjsreplacemecallback-l-#FF0000" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path d="M17 1 9 3l8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:red"/>
|
||||
</marker>
|
||||
<marker orient="auto" id="mscgenjsreplacememethod-#0000FF" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path class="arrow-style" stroke="#00F" fill="#00F" d="m1 1 8 2-8 2z"/>
|
||||
</marker>
|
||||
<marker orient="auto" id="mscgenjsreplacememethod-l-#0000FF" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path class="arrow-style" stroke="#00F" fill="#00F" d="M17 1 9 3l8 2z"/>
|
||||
</marker>
|
||||
<marker orient="auto" id="mscgenjsreplacememethod-#FF0000" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path class="arrow-style" stroke="red" fill="red" d="m1 1 8 2-8 2z"/>
|
||||
</marker>
|
||||
<marker orient="auto" id="mscgenjsreplacememethod-l-#FF0000" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path class="arrow-style" stroke="red" fill="red" d="M17 1 9 3l8 2z"/>
|
||||
</marker>
|
||||
<style>
|
||||
.mscgenjsreplaceme path,.mscgenjsreplaceme rect{fill:none}.mscgenjsreplaceme .label-text-background{fill:#fff;stroke:#fff;stroke-width:0}.mscgenjsreplaceme .return{stroke-dasharray:5,3}.mscgenjsreplaceme text{color:inherit;stroke:none;text-anchor:middle}.mscgenjsreplaceme text.anchor-start{text-anchor:start}.mscgenjsreplaceme .arrow-marker{overflow:visible}.mscgenjsreplaceme .arrow-style{stroke-width:1}.mscgenjsreplaceme .arcrow{stroke-linecap:butt}.mscgenjsreplaceme .box,.mscgenjsreplaceme .entity{fill:#fff;stroke-linejoin:round}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="mscgenjsreplaceme_body" transform="translate(48 3)">
|
||||
<path class="bglayer" style="fill:#fff;stroke:#fff;stroke-width:0" d="M-48-3h1176v1664.36H-48z" id="mscgenjsreplaceme_background"/>
|
||||
<g id="mscgenjsreplaceme_arcspans">
|
||||
<path class="box inline_expression alt" d="M-38 859.12h964v780.24H-38z"/>
|
||||
<path class="box inline_expression alt" d="M-34 1206.18h956v195.06H-34z"/>
|
||||
</g>
|
||||
<g id="mscgenjsreplaceme_lifelines">
|
||||
<path class="arcrow" style="stroke:transparent" d="M60 38v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 38v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 38v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 38v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 38v38M1020 38v38M60 76v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 76v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 76v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 76v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 76v38M1020 76v38M60 114v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 114v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 114v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 114v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 114v38M1020 114v38M60 152v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 152v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 152v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 152v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 152v38M1020 152v38M60 190v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 190v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 190v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 190v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 190v38M1020 190v38M60 228v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 228v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 228v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 228v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 228v38M1020 228v38M60 266v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 266v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 266v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 266v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 266v38M1020 266v38M60 304v54"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 304v54"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 304v54"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 304v54"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 304v54M1020 304v54M60 358v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 358v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 358v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 358v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 358v38M1020 358v38M60 396v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 396v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 396v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 396v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 396v38M1020 396v38M60 434v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 434v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 434v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 434v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 434v38M1020 434v38M60 472v43.06"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 472v43.06"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 472v43.06"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 472v43.06"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 472v43.06M1020 472v43.06M60 515.06v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 515.06v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 515.06v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 515.06v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 515.06v38M1020 515.06v38M60 553.06v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 553.06v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 553.06v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 553.06v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 553.06v38M1020 553.06v38M60 591.06v54"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 591.06v54"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 591.06v54"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 591.06v54"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 591.06v54M1020 591.06v54M60 645.06v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 645.06v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 645.06v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 645.06v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 645.06v38M1020 645.06v38M60 683.06v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 683.06v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 683.06v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 683.06v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 683.06v38M1020 683.06v38M60 721.06v43.06"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 721.06v43.06"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 721.06v43.06"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 721.06v43.06"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 721.06v43.06M1020 721.06v43.06M60 764.12v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 764.12v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 764.12v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 764.12v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 764.12v38M1020 764.12v38M60 802.12v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 802.12v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 802.12v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 802.12v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 802.12v38M1020 802.12v38M60 840.12v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 840.12v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 840.12v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 840.12v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 840.12v38M1020 840.12v38M60 878.12v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 878.12v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 878.12v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 878.12v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 878.12v38M1020 878.12v38M60 916.12v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 916.12v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 916.12v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 916.12v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 916.12v38M1020 916.12v38M60 954.12v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 954.12v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 954.12v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 954.12v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 954.12v38M1020 954.12v38M60 992.12v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 992.12v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 992.12v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 992.12v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 992.12v38M1020 992.12v38M60 1030.12v43.06"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 1030.12v43.06"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 1030.12v43.06"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 1030.12v43.06"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 1030.12v43.06M1020 1030.12v43.06M60 1073.18v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 1073.18v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 1073.18v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 1073.18v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 1073.18v38M1020 1073.18v38M60 1111.18v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 1111.18v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 1111.18v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 1111.18v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 1111.18v38M1020 1111.18v38M60 1149.18v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 1149.18v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 1149.18v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 1149.18v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 1149.18v38M1020 1149.18v38M60 1187.18v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 1187.18v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 1187.18v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 1187.18v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 1187.18v38M1020 1187.18v38M60 1225.18v43.06"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 1225.18v43.06"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 1225.18v43.06"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 1225.18v43.06"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 1225.18v43.06M1020 1225.18v43.06M60 1268.24v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 1268.24v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 1268.24v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 1268.24v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 1268.24v38M1020 1268.24v38M60 1306.24v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 1306.24v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 1306.24v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 1306.24v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 1306.24v38M1020 1306.24v38M60 1344.24v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 1344.24v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 1344.24v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 1344.24v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 1344.24v38M1020 1344.24v38M60 1382.24v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 1382.24v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 1382.24v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 1382.24v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 1382.24v38M1020 1382.24v38M60 1420.24v43.06"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 1420.24v43.06"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 1420.24v43.06"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 1420.24v43.06"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 1420.24v43.06M1020 1420.24v43.06M60 1463.3v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 1463.3v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 1463.3v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 1463.3v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 1463.3v38M1020 1463.3v38M60 1501.3v43.06"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 1501.3v43.06"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 1501.3v43.06"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 1501.3v43.06"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 1501.3v43.06M1020 1501.3v43.06M60 1544.36v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 1544.36v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 1544.36v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 1544.36v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 1544.36v38M1020 1544.36v38M60 1582.36v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 1582.36v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 1582.36v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 1582.36v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 1582.36v38M1020 1582.36v38M60 1620.36v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M252 1620.36v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M444 1620.36v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M636 1620.36v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M828 1620.36v38M1020 1620.36v38"/>
|
||||
</g>
|
||||
<g id="mscgenjsreplaceme_sequence">
|
||||
<path class="entity" style="stroke:transparent" d="M0 0h120v38H0z"/>
|
||||
<text x="60" y="22.75" class="entity-text"><tspan> </tspan></text>
|
||||
<path class="entity" style="fill:#cfc;stroke:#080" d="M192 0h120v38H192z"/>
|
||||
<text x="252" y="22.75" class="entity-text"><tspan>Network</tspan></text>
|
||||
<path class="entity" style="fill:#fcc;stroke:red" d="M384 0h120v38H384z"/>
|
||||
<text x="444" y="22.75" class="entity-text"><tspan>Offerer</tspan></text>
|
||||
<path class="entity" style="fill:#ccf;stroke:#00f" d="M576 0h120v38H576z"/>
|
||||
<text x="636" y="22.75" class="entity-text"><tspan>Bidder</tspan></text>
|
||||
<path class="entity" style="stroke:transparent" d="M768 0h120v38H768z"/>
|
||||
<text x="828" y="22.75" class="entity-text"><tspan> </tspan></text>
|
||||
<path class="entity" style="stroke:transparent" d="M960 0h120v38H960z"/>
|
||||
<text x="1020" y="22.75" class="entity-text"><tspan> </tspan></text>
|
||||
<path class="arc directional callback" style="stroke:red" marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M444 95H252"/>
|
||||
<path class="label-text-background" d="M316.08 79.25h63.84v14h-63.84z"/>
|
||||
<text x="348" y="90.25" class="directional-text callback-text"><tspan>Sends Offer</tspan></text>
|
||||
<path class="arc directional return" style="stroke:#080" marker-end="url(#mscgenjsreplacemecallback-#008800)" d="M252 133h384"/>
|
||||
<path class="label-text-background" d="M408.75 117.25h70.5v14h-70.5z"/>
|
||||
<text x="444" y="128.25" class="directional-text return-text"><tspan>Detects Offer</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:#00f" marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M636 171H444"/>
|
||||
<path class="label-text-background" d="M512.64 155.25h54.72v14h-54.72z"/>
|
||||
<text x="540" y="166.25" class="directional-text callback-text"><tspan>Sends Bid</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:red" marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M444 285H252"/>
|
||||
<path class="label-text-background" d="M303.3 269.25h89.41v14H303.3z"/>
|
||||
<text x="348" y="280.25" class="directional-text callback-text"><tspan>Sends Initiate Tx</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:red" marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M444 331h192"/>
|
||||
<path class="label-text-background" d="M494.3 315.25h92.08v14H494.3z"/>
|
||||
<text x="540" y="326.25" class="directional-text callback-text"><tspan>Sends BidAccept</tspan></text>
|
||||
<path class="arc directional return" style="stroke:#080" marker-end="url(#mscgenjsreplacemecallback-#008800)" d="M252 415h384"/>
|
||||
<path class="label-text-background" d="M395.97 399.25h96.06v14h-96.06z"/>
|
||||
<text x="444" y="410.25" class="directional-text return-text"><tspan>Detects Initiate Tx</tspan></text>
|
||||
<path d="M636 485.93c96 .1 96 22.8 0 22.8" class="arc directional method" style="stroke:#00f" marker-end="url(#mscgenjsreplacememethod-#0000FF)"/>
|
||||
<path class="label-text-background" d="M639 469.67h118.92v14.02H639z"/>
|
||||
<text x="639" y="480.68" class="directional-text method-text anchor-start"><tspan>Wait for ITX to confirm</tspan></text>
|
||||
<path d="M444 485.93c96 .1 96 22.8 0 22.8" class="arc directional method" style="stroke:red" marker-end="url(#mscgenjsreplacememethod-#FF0000)"/>
|
||||
<path class="label-text-background" d="M447 469.67h118.92v14.02H447z"/>
|
||||
<text x="447" y="480.68" class="directional-text method-text anchor-start"><tspan>Wait for ITX to confirm</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:#00f" marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M636 618.06H252"/>
|
||||
<path class="label-text-background" d="M388.64 602.3h110.72v14.02H388.64z"/>
|
||||
<text x="444" y="613.31" class="directional-text callback-text"><tspan>Sends Participate Tx</tspan></text>
|
||||
<path class="arc directional return" style="stroke:#080" marker-end="url(#mscgenjsreplacemecallback-#008800)" d="M252 702.06h192"/>
|
||||
<path class="label-text-background" d="M289.31 686.3h117.38v14.02H289.31z"/>
|
||||
<text x="348" y="697.31" class="directional-text return-text"><tspan>Detects Participate Tx</tspan></text>
|
||||
<path d="M636 734.99c96 .1 96 22.8 0 22.8" class="arc directional method" style="stroke:#00f" marker-end="url(#mscgenjsreplacememethod-#0000FF)"/>
|
||||
<path class="label-text-background" d="M639 718.73h123.59v14.02H639z"/>
|
||||
<text x="639" y="729.74" class="directional-text method-text anchor-start"><tspan>Wait for PTX to confirm</tspan></text>
|
||||
<path d="M444 734.99c96 .1 96 22.8 0 22.8" class="arc directional method" style="stroke:red" marker-end="url(#mscgenjsreplacememethod-#FF0000)"/>
|
||||
<path class="label-text-background" d="M447 718.73h123.59v14.02H447z"/>
|
||||
<text x="447" y="729.74" class="directional-text method-text anchor-start"><tspan>Wait for PTX to confirm</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:red" marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M444 897.12H252"/>
|
||||
<path class="label-text-background" d="M268.28 881.36h159.44v14.02H268.28z"/>
|
||||
<text x="348" y="892.37" class="directional-text callback-text"><tspan>Sends Participate Redeem Tx</tspan></text>
|
||||
<path class="arc directional return" style="stroke:#080" marker-end="url(#mscgenjsreplacemecallback-#008800)" d="M252 935.12h384"/>
|
||||
<path class="label-text-background" d="M360.95 919.36h166.09v14.02H360.95z"/>
|
||||
<text x="444" y="930.37" class="directional-text return-text"><tspan>Detects Participate Redeem Tx</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:#00f" marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M636 1011.12H252"/>
|
||||
<path class="label-text-background" d="M374.95 995.36h138.09v14.02H374.95z"/>
|
||||
<text x="444" y="1006.37" class="directional-text callback-text"><tspan>Sends Initiate Redeem Tx</tspan></text>
|
||||
<path d="M636 1044.05c96 .1 96 22.8 0 22.8" class="arc directional method" style="stroke:#00f" marker-end="url(#mscgenjsreplacememethod-#0000FF)"/>
|
||||
<path class="label-text-background" d="M639 1027.8h167.61v14.02H639z"/>
|
||||
<text x="639" y="1038.8" class="directional-text method-text anchor-start"><tspan>Wait for ITX Redeem to confirm</tspan></text>
|
||||
<path class="inline_expression_divider" style="stroke-dasharray:10,5" d="M-38 1168.18h964"/>
|
||||
<path class="label-text-background" d="M422.98 1160.92h42.03v14.02h-42.03z"/>
|
||||
<text x="444" y="1171.93" class="empty-text comment-row-text"><tspan>fail path</tspan></text>
|
||||
<path d="M444 1239.11c96 .1 96 22.8 0 22.8" class="arc directional method" style="stroke:red" marker-end="url(#mscgenjsreplacememethod-#FF0000)"/>
|
||||
<path class="label-text-background" d="M447 1222.86h159.94v14.02H447z"/>
|
||||
<text x="447" y="1233.86" class="directional-text method-text anchor-start"><tspan>Wait for ITX locktime to expire</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:red" marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M444 1287.24H252"/>
|
||||
<path class="label-text-background" d="M309.31 1271.48h77.38v14.02h-77.38z"/>
|
||||
<text x="348" y="1282.49" class="directional-text callback-text"><tspan>ITX Refund Tx</tspan></text>
|
||||
<path class="arc directional return" style="stroke:#080" marker-end="url(#mscgenjsreplacemecallback-#008800)" d="M252 1325.24h384"/>
|
||||
<path class="label-text-background" d="M368.97 1309.48h150.06v14.02H368.97z"/>
|
||||
<text x="444" y="1320.49" class="directional-text return-text"><tspan>Detects Initiate Tx refund Tx</tspan></text>
|
||||
<path d="M636 1434.17c96 .1 96 22.8 0 22.8" class="arc directional method" style="stroke:#00f" marker-end="url(#mscgenjsreplacememethod-#0000FF)"/>
|
||||
<path class="label-text-background" d="M639 1417.91h164.59v14.02H639z"/>
|
||||
<text x="639" y="1428.92" class="directional-text method-text anchor-start"><tspan>Wait for PTX locktime to expire</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:#00f" marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M636 1482.3H252"/>
|
||||
<path class="label-text-background" d="M402.98 1466.55h82.03v14.02h-82.03z"/>
|
||||
<text x="444" y="1477.55" class="directional-text callback-text"><tspan>PTX Refund Tx</tspan></text>
|
||||
<path d="M636 1515.23c96 .1 96 22.8 0 22.8" class="arc directional method" style="stroke:#00f" marker-end="url(#mscgenjsreplacememethod-#0000FF)"/>
|
||||
<path class="label-text-background" d="M639 1498.97h165.63v14.02H639z"/>
|
||||
<text x="639" y="1509.98" class="directional-text method-text anchor-start"><tspan>Wait for PTX Refund to confirm</tspan></text>
|
||||
</g>
|
||||
<g id="mscgenjsreplaceme_notes">
|
||||
<path d="m546 209 3-17h174l3 17-3 17H549z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="636" y="212.75" class="box-text abox-text"><tspan>Bid Sent</tspan></text>
|
||||
<path class="box" style="stroke:red" d="M354 230h180v34H354z"/>
|
||||
<text x="444" y="250.75" class="box-text"><tspan>User accepts bid</tspan></text>
|
||||
<path d="M738 268h363v9h9m-9-9 9 9v25H738v-34z" class="box note" style="fill:#ffc"/>
|
||||
<text x="924" y="280.75" class="box-text note-text"><tspan>Offerer generates secret_value and sends Hash(secret_value) to</tspan></text>
|
||||
<text x="924" y="296.75" class="box-text note-text"><tspan>the Bidder</tspan></text>
|
||||
<path d="M738 306h363v9h9m-9-9 9 9v41H738v-50z" class="box note" style="fill:#ffc"/>
|
||||
<text x="924" y="318.75" class="box-text note-text"><tspan>ITX can be spent by knowledge of the</tspan></text>
|
||||
<text x="924" y="334.75" class="box-text note-text"><tspan>secret_value and the bidder_redeem_key or after a timeout</tspan></text>
|
||||
<text x="924" y="350.75" class="box-text note-text"><tspan>by the offerer_refund_key</tspan></text>
|
||||
<path d="m546 377 3-17h174l3 17-3 17H549z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="636" y="380.75" class="box-text abox-text"><tspan>Bid Accepted</tspan></text>
|
||||
<path d="m546 453 3-17h174l3 17-3 17H549z" class="box abox" style="fill:#4bdbf1;stroke:#00f"/>
|
||||
<text x="636" y="456.75" class="box-text abox-text"><tspan>ITX Sent</tspan></text>
|
||||
<path d="m546 534.06 3-17h174l3 17-3 17H549z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="636" y="537.81" class="box-text abox-text"><tspan>Bid Initiated</tspan></text>
|
||||
<path d="m546 572.06 3-17h174l3 17-3 17H549z" class="box abox" style="fill:#4bdbf1;stroke:#00f"/>
|
||||
<text x="636" y="575.81" class="box-text abox-text"><tspan>ITX Confirmed</tspan></text>
|
||||
<path d="M738 593.05h363v9h9m-9-9 9 9v41.02H738v-50.02z" class="box note" style="fill:#ffc"/>
|
||||
<text x="924" y="605.81" class="box-text note-text"><tspan>PTX can be spent by knowledge of the</tspan></text>
|
||||
<text x="924" y="621.81" class="box-text note-text"><tspan>secret_value and the offerer_redeem_key or after a timeout</tspan></text>
|
||||
<text x="924" y="637.81" class="box-text note-text"><tspan>by the bidder_refund_key</tspan></text>
|
||||
<path d="m546 664.06 3-17h174l3 17-3 17H549z" class="box abox" style="fill:#f1db4b;stroke:#00f"/>
|
||||
<text x="636" y="667.81" class="box-text abox-text"><tspan>PTX Sent</tspan></text>
|
||||
<path d="m546 783.12 3-17h174l3 17-3 17H549z" class="box abox" style="fill:#f1db4b;stroke:#00f"/>
|
||||
<text x="636" y="786.87" class="box-text abox-text"><tspan>PTX Confirmed</tspan></text>
|
||||
<path d="m546 821.12 3-17h174l3 17-3 17H549z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="636" y="824.87" class="box-text abox-text"><tspan>Bid Participating</tspan></text>
|
||||
<path d="M-37 859.12h98.39v11.02l-7 7H-37" class="box inline_expression_label"/>
|
||||
<text x="-35" y="872.37" class="inline_expression-text alt-text anchor-start"><tspan>alt: success path</tspan></text>
|
||||
<path d="M738 880.12h363v9h9m-9-9 9 9v25H738v-34z" class="box note" style="fill:#ffc"/>
|
||||
<text x="924" y="900.87" class="box-text note-text"><tspan>Reveals secret_value</tspan></text>
|
||||
<path d="m546 973.12 3-17h174l3 17-3 17H549z" class="box abox" style="fill:#f1db4b;stroke:#00f"/>
|
||||
<text x="636" y="976.87" class="box-text abox-text"><tspan>PTX Redeemed</tspan></text>
|
||||
<path d="m546 1092.18 3-17h174l3 17-3 17H549z" class="box abox" style="fill:#4bdbf1;stroke:#00f"/>
|
||||
<text x="636" y="1095.93" class="box-text abox-text"><tspan>ITX Redeemed</tspan></text>
|
||||
<path d="m546 1130.18 3-17h174l3 17-3 17H549z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="636" y="1133.93" class="box-text abox-text"><tspan>Bid Completed</tspan></text>
|
||||
<path d="M-33 1206.18h152.83v11.02l-7 7H-33" class="box inline_expression_label"/>
|
||||
<text x="-31" y="1219.43" class="inline_expression-text alt-text anchor-start"><tspan>alt: offerer may reclaim ITX</tspan></text>
|
||||
<path d="m546 1363.24 3-17h174l3 17-3 17H549z" class="box abox" style="fill:#4bdbf1;stroke:#00f"/>
|
||||
<text x="636" y="1366.99" class="box-text abox-text"><tspan>ITX Refunded</tspan></text>
|
||||
<path d="m546 1563.36 3-17h174l3 17-3 17H549z" class="box abox" style="fill:#f1db4b;stroke:#00f"/>
|
||||
<text x="636" y="1567.11" class="box-text abox-text"><tspan>PTX Refunded</tspan></text>
|
||||
<path d="m546 1601.36 3-17h174l3 17-3 17H549z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="636" y="1605.11" class="box-text abox-text"><tspan>Bid Completed</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 32 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 28 KiB |
414
basicswap/static/sequence_diagrams/xmr.bidder.alt.xu.min.svg
Normal file
414
basicswap/static/sequence_diagrams/xmr.bidder.alt.xu.min.svg
Normal file
@@ -0,0 +1,414 @@
|
||||
<svg version="1.1" id="mscgenjsreplaceme" class="mscgenjsreplaceme" xmlns="http://www.w3.org/2000/svg" width="1272" height="2063.3" style="font-family:Helvetica,sans-serif;font-size:12px;font-weight:400;font-style:normal;text-decoration:none;background-color:#fff;stroke:#000;stroke-width:2">
|
||||
<defs>
|
||||
<marker orient="auto" id="mscgenjsreplacemecallback-#0000FF" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path d="m1 1 8 2-8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:#00f"/>
|
||||
</marker>
|
||||
<marker orient="auto" id="mscgenjsreplacemecallback-l-#0000FF" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path d="M17 1 9 3l8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:#00f"/>
|
||||
</marker>
|
||||
<marker orient="auto" id="mscgenjsreplacemecallback-#008800" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path d="m1 1 8 2-8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:#080"/>
|
||||
</marker>
|
||||
<marker orient="auto" id="mscgenjsreplacemecallback-l-#008800" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path d="M17 1 9 3l8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:#080"/>
|
||||
</marker>
|
||||
<marker orient="auto" id="mscgenjsreplacemecallback-#FF0000" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path d="m1 1 8 2-8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:red"/>
|
||||
</marker>
|
||||
<marker orient="auto" id="mscgenjsreplacemecallback-l-#FF0000" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path d="M17 1 9 3l8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:red"/>
|
||||
</marker>
|
||||
<marker orient="auto" id="mscgenjsreplacememethod-#0000FF" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path class="arrow-style" stroke="#00F" fill="#00F" d="m1 1 8 2-8 2z"/>
|
||||
</marker>
|
||||
<marker orient="auto" id="mscgenjsreplacememethod-l-#0000FF" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path class="arrow-style" stroke="#00F" fill="#00F" d="M17 1 9 3l8 2z"/>
|
||||
</marker>
|
||||
<marker orient="auto" id="mscgenjsreplacememethod-#FF0000" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path class="arrow-style" stroke="red" fill="red" d="m1 1 8 2-8 2z"/>
|
||||
</marker>
|
||||
<marker orient="auto" id="mscgenjsreplacememethod-l-#FF0000" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path class="arrow-style" stroke="red" fill="red" d="M17 1 9 3l8 2z"/>
|
||||
</marker>
|
||||
<style>
|
||||
.mscgenjsreplaceme path,.mscgenjsreplaceme rect{fill:none}.mscgenjsreplaceme .label-text-background{fill:#fff;stroke:#fff;stroke-width:0}.mscgenjsreplaceme .return{stroke-dasharray:5,3}.mscgenjsreplaceme .inline_expression_divider{stroke-dasharray:10,5}.mscgenjsreplaceme text{color:inherit;stroke:none;text-anchor:middle}.mscgenjsreplaceme text.anchor-start{text-anchor:start}.mscgenjsreplaceme .arrow-marker{overflow:visible}.mscgenjsreplaceme .arrow-style{stroke-width:1}.mscgenjsreplaceme .arcrow{stroke-linecap:butt}.mscgenjsreplaceme .box,.mscgenjsreplaceme .entity{fill:#fff;stroke-linejoin:round}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="mscgenjsreplaceme_body" transform="translate(51 3)">
|
||||
<path class="bglayer" style="fill:#fff;stroke:#fff;stroke-width:0" d="M-51-3h1272v2063.3H-51z" id="mscgenjsreplaceme_background"/>
|
||||
<g id="mscgenjsreplaceme_arcspans">
|
||||
<path class="box inline_expression alt" d="M-41 869.12h1044V2038.3H-41z"/>
|
||||
<path class="box inline_expression alt" d="M-37 1410.18H999v590.12H-37z"/>
|
||||
</g>
|
||||
<g id="mscgenjsreplaceme_lifelines">
|
||||
<path class="arcrow" style="stroke:transparent" d="M65 38v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 38v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 38v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 38v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 38v38M1105 38v38M65 76v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 76v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 76v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 76v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 76v38M1105 76v38M65 114v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 114v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 114v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 114v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 114v38M1105 114v38M65 152v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 152v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 152v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 152v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 152v38M1105 152v38M65 190v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 190v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 190v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 190v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 190v38M1105 190v38M65 228v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 228v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 228v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 228v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 228v38M1105 228v38M65 266v54"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 266v54"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 266v54"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 266v54"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 266v54M1105 266v54M65 320v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 320v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 320v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 320v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 320v38M1105 320v38M65 358v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 358v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 358v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 358v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 358v38M1105 358v38M65 396v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 396v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 396v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 396v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 396v38M1105 396v38M65 434v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 434v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 434v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 434v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 434v38M1105 434v38M65 472v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 472v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 472v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 472v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 472v38M1105 472v38M65 510v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 510v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 510v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 510v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 510v38M1105 510v38M65 548v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 548v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 548v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 548v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 548v38M1105 548v38M65 586v75.06"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 586v75.06"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 586v75.06"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 586v75.06"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 586v75.06M1105 586v75.06M65 661.06v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 661.06v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 661.06v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 661.06v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 661.06v38M1105 661.06v38M65 699.06v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 699.06v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 699.06v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 699.06v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 699.06v38M1105 699.06v38M65 737.06v75.06"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 737.06v75.06"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 737.06v75.06"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 737.06v75.06"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 737.06v75.06M1105 737.06v75.06M65 812.12v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 812.12v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 812.12v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 812.12v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 812.12v38M1105 812.12v38M65 850.12v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 850.12v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 850.12v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 850.12v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 850.12v38M1105 850.12v38M65 888.12v86"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 888.12v86"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 888.12v86"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 888.12v86"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 888.12v86M1105 888.12v86M65 974.12v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 974.12v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 974.12v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 974.12v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 974.12v38M1105 974.12v38M65 1012.12v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1012.12v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1012.12v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1012.12v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1012.12v38M1105 1012.12v38M65 1050.12v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1050.12v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1050.12v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1050.12v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1050.12v38M1105 1050.12v38M65 1088.12v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1088.12v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1088.12v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1088.12v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1088.12v38M1105 1088.12v38M65 1126.12v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1126.12v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1126.12v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1126.12v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1126.12v38M1105 1126.12v38M65 1164.12v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1164.12v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1164.12v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1164.12v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1164.12v38M1105 1164.12v38M65 1202.12v75.06"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1202.12v75.06"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1202.12v75.06"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1202.12v75.06"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1202.12v75.06M1105 1202.12v75.06M65 1277.18v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1277.18v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1277.18v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1277.18v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1277.18v38M1105 1277.18v38M65 1315.18v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1315.18v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1315.18v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1315.18v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1315.18v38M1105 1315.18v38M65 1353.18v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1353.18v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1353.18v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1353.18v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1353.18v38M1105 1353.18v38M65 1391.18v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1391.18v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1391.18v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1391.18v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1391.18v38M1105 1391.18v38M65 1429.18v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1429.18v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1429.18v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1429.18v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1429.18v38M1105 1429.18v38M65 1467.18v59.06"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1467.18v59.06"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1467.18v59.06"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1467.18v59.06"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1467.18v59.06M1105 1467.18v59.06M65 1526.24v54"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1526.24v54"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1526.24v54"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1526.24v54"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1526.24v54M1105 1526.24v54M65 1580.24v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1580.24v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1580.24v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1580.24v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1580.24v38M1105 1580.24v38M65 1618.24v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1618.24v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1618.24v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1618.24v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1618.24v38M1105 1618.24v38M65 1656.24v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1656.24v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1656.24v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1656.24v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1656.24v38M1105 1656.24v38M65 1694.24v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1694.24v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1694.24v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1694.24v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1694.24v38M1105 1694.24v38M65 1732.24v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1732.24v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1732.24v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1732.24v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1732.24v38M1105 1732.24v38M65 1770.24v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1770.24v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1770.24v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1770.24v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1770.24v38M1105 1770.24v38M65 1808.24v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1808.24v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1808.24v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1808.24v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1808.24v38M1105 1808.24v38M65 1846.24v59.06"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1846.24v59.06"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1846.24v59.06"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1846.24v59.06"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1846.24v59.06M1105 1846.24v59.06M65 1905.3v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1905.3v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1905.3v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1905.3v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1905.3v38M1105 1905.3v38M65 1943.3v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1943.3v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1943.3v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1943.3v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1943.3v38M1105 1943.3v38M65 1981.3v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1981.3v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1981.3v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1981.3v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1981.3v38M1105 1981.3v38M65 2019.3v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 2019.3v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 2019.3v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 2019.3v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 2019.3v38M1105 2019.3v38"/>
|
||||
</g>
|
||||
<g id="mscgenjsreplaceme_sequence">
|
||||
<path class="entity" style="stroke:transparent" d="M0 0h130v38H0z"/>
|
||||
<text x="65" y="22.75" class="entity-text"><tspan> </tspan></text>
|
||||
<path class="entity" style="fill:#cfc;stroke:#080" d="M208 0h130v38H208z"/>
|
||||
<text x="273" y="22.75" class="entity-text"><tspan>Network</tspan></text>
|
||||
<path class="entity" style="fill:#fcc;stroke:red" d="M416 0h130v38H416z"/>
|
||||
<text x="481" y="22.75" class="entity-text"><tspan>Offerer</tspan></text>
|
||||
<path class="entity" style="fill:#ccf;stroke:#00f" d="M624 0h130v38H624z"/>
|
||||
<text x="689" y="22.75" class="entity-text"><tspan>Bidder</tspan></text>
|
||||
<path class="entity" style="stroke:transparent" d="M832 0h130v38H832z"/>
|
||||
<text x="897" y="22.75" class="entity-text"><tspan> </tspan></text>
|
||||
<path class="entity" style="stroke:transparent" d="M1040 0h130v38h-130z"/>
|
||||
<text x="1105" y="22.75" class="entity-text"><tspan> </tspan></text>
|
||||
<path class="arc directional callback" style="stroke:red" marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 95H273"/>
|
||||
<path class="label-text-background" d="M345.08 79.25h63.84v14h-63.84z"/>
|
||||
<text x="377" y="90.25" class="directional-text callback-text"><tspan>Sends Offer</tspan></text>
|
||||
<path class="arc directional return" style="stroke:#080" marker-end="url(#mscgenjsreplacemecallback-#008800)" d="M273 133h416"/>
|
||||
<path class="label-text-background" d="M445.75 117.25h70.5v14h-70.5z"/>
|
||||
<text x="481" y="128.25" class="directional-text return-text"><tspan>Detects Offer</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:#00f" marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 171H481"/>
|
||||
<path class="label-text-background" d="M557.64 155.25h54.72v14h-54.72z"/>
|
||||
<text x="585" y="166.25" class="directional-text callback-text"><tspan>Sends Bid</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:red" marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 293h208"/>
|
||||
<path class="label-text-background" d="M513.28 277.25h143.77v14H513.28z"/>
|
||||
<text x="585" y="288.25" class="directional-text callback-text"><tspan>Sends BidAccept message</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:#00f" marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 415H481"/>
|
||||
<path class="label-text-background" d="M491.28 399.25h187.77v14H491.28z"/>
|
||||
<text x="585" y="410.25" class="directional-text callback-text"><tspan>Sends XmrBidLockTxSigsMessage</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:red" marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 491h208"/>
|
||||
<path class="label-text-background" d="M485.61 475.25h199.11v14H485.61z"/>
|
||||
<text x="585" y="486.25" class="directional-text callback-text"><tspan>Sends XmrBidLockSpendTxMessage</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:red" marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 529H273"/>
|
||||
<path class="label-text-background" d="M311.64 513.25h130.72v14H311.64z"/>
|
||||
<text x="377" y="524.25" class="directional-text callback-text"><tspan>Sends script-coin-lock-tx</tspan></text>
|
||||
<path d="M689 615.93c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:#00f" marker-end="url(#mscgenjsreplacememethod-#0000FF)"/>
|
||||
<path class="label-text-background" d="M692 567.67h40.91v14.02H692z"/>
|
||||
<text x="692" y="578.68" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path class="label-text-background" d="M692 583.67h107.02v14.02H692z"/>
|
||||
<text x="692" y="594.68" class="directional-text method-text anchor-start"><tspan>script-coin-lock-tx to</tspan></text>
|
||||
<path class="label-text-background" d="M692 599.67h39.34v14.02H692z"/>
|
||||
<text x="692" y="610.68" class="directional-text method-text anchor-start"><tspan>confirm</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:#00f" marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 718.06H273"/>
|
||||
<path class="label-text-background" d="M408.97 702.3h144.06v14.02H408.97z"/>
|
||||
<text x="481" y="713.31" class="directional-text callback-text"><tspan>Sends noscript-coin-lock-tx</tspan></text>
|
||||
<path d="M689 766.99c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:#00f" marker-end="url(#mscgenjsreplacememethod-#0000FF)"/>
|
||||
<path class="label-text-background" d="M692 718.73h40.91v14.02H692z"/>
|
||||
<text x="692" y="729.74" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path class="label-text-background" d="M692 734.73h120.38v14.02H692z"/>
|
||||
<text x="692" y="745.74" class="directional-text method-text anchor-start"><tspan>noscript-coin-lock-tx to</tspan></text>
|
||||
<path class="label-text-background" d="M692 750.73h39.34v14.02H692z"/>
|
||||
<text x="692" y="761.74" class="directional-text method-text anchor-start"><tspan>confirm</tspan></text>
|
||||
<path d="M481 766.99c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:red" marker-end="url(#mscgenjsreplacememethod-#FF0000)"/>
|
||||
<path class="label-text-background" d="M484 718.73h40.91v14.02H484z"/>
|
||||
<text x="484" y="729.74" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path class="label-text-background" d="M484 734.73h120.38v14.02H484z"/>
|
||||
<text x="484" y="745.74" class="directional-text method-text anchor-start"><tspan>noscript-coin-lock-tx to</tspan></text>
|
||||
<path class="label-text-background" d="M484 750.73h39.34v14.02H484z"/>
|
||||
<text x="484" y="761.74" class="directional-text method-text anchor-start"><tspan>confirm</tspan></text>
|
||||
<path class="arc directional method" style="stroke:red" marker-end="url(#mscgenjsreplacememethod-#FF0000)" d="M481 931.12h208"/>
|
||||
<path class="label-text-background" d="M519.64 915.36h130.72v14.02H519.64z"/>
|
||||
<text x="585" y="926.37" class="directional-text method-text"><tspan>Sends script-coin-lock-tx</tspan></text>
|
||||
<path class="label-text-background" d="M539.3 933.36h91.73v14.02H539.3z"/>
|
||||
<text x="585" y="944.37" class="directional-text method-text"><tspan>release message</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:#00f" marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 1031.12H273"/>
|
||||
<path class="label-text-background" d="M397.3 1015.36h167.41v14.02H397.3z"/>
|
||||
<text x="481" y="1026.37" class="directional-text callback-text"><tspan>Sends script-coin-lock-spend-tx</tspan></text>
|
||||
<path class="inline_expression_divider" d="M-41 1145.12h1044"/>
|
||||
<path class="label-text-background" d="M459.98 1137.86h42.03v14.02h-42.03z"/>
|
||||
<text x="481" y="1148.87" class="empty-text comment-row-text"><tspan>fail path</tspan></text>
|
||||
<path d="M689 1232.05c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:#00f" marker-end="url(#mscgenjsreplacememethod-#0000FF)"/>
|
||||
<path class="label-text-background" d="M692 1183.8h40.91v14.02H692z"/>
|
||||
<text x="692" y="1194.8" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path class="label-text-background" d="M692 1199.8h131.69v14.02H692z"/>
|
||||
<text x="692" y="1210.8" class="directional-text method-text anchor-start"><tspan>script-coin-lock-tx lock to</tspan></text>
|
||||
<path class="label-text-background" d="M692 1215.8h33.02v14.02H692z"/>
|
||||
<text x="692" y="1226.8" class="directional-text method-text anchor-start"><tspan>expire</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:red" marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 1296.18H273"/>
|
||||
<path class="label-text-background" d="M359.98 1280.42h34.03v14.02h-34.03z"/>
|
||||
<text x="377" y="1291.43" class="directional-text callback-text"><tspan>Sends</tspan></text>
|
||||
<path class="label-text-background" d="M300.64 1298.42h152.72v14.02H300.64z"/>
|
||||
<text x="377" y="1309.43" class="directional-text callback-text"><tspan>script-coin-lock-pre-refund-tx</tspan></text>
|
||||
<path class="arc directional return" style="stroke:#080" marker-end="url(#mscgenjsreplacemecallback-#008800)" d="M273 1334.18h208"/>
|
||||
<path class="label-text-background" d="M300.64 1318.42h152.72v14.02H300.64z"/>
|
||||
<text x="377" y="1329.43" class="directional-text return-text"><tspan>script-coin-lock-pre-refund-tx</tspan></text>
|
||||
<path d="M481 1489.11c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:red" marker-end="url(#mscgenjsreplacememethod-#FF0000)"/>
|
||||
<path class="label-text-background" d="M484 1456.86h40.91v14.02H484z"/>
|
||||
<text x="484" y="1467.86" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path class="label-text-background" d="M484 1472.86h124.06v14.02H484z"/>
|
||||
<text x="484" y="1483.86" class="directional-text method-text anchor-start"><tspan>pre-refund tx to confirm</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:red" marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 1553.24H273"/>
|
||||
<path class="label-text-background" d="M359.98 1537.48h34.03v14.02h-34.03z"/>
|
||||
<text x="377" y="1548.49" class="directional-text callback-text"><tspan>Sends</tspan></text>
|
||||
<path class="label-text-background" d="M282.3 1555.48h189.41v14.02H282.3z"/>
|
||||
<text x="377" y="1566.49" class="directional-text callback-text"><tspan>script-coin-lock-pre-refund-spend-tx</tspan></text>
|
||||
<path class="arc directional return" style="stroke:#080" marker-end="url(#mscgenjsreplacemecallback-#008800)" d="M273 1637.24h416"/>
|
||||
<path class="label-text-background" d="M364.28 1621.48h233.44v14.02H364.28z"/>
|
||||
<text x="481" y="1632.49" class="directional-text return-text"><tspan>Detects script-coin-lock-pre-refund-spend-tx</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:#00f" marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 1675.24H273"/>
|
||||
<path class="label-text-background" d="M382.97 1659.48h196.06v14.02H382.97z"/>
|
||||
<text x="481" y="1670.49" class="directional-text callback-text"><tspan>Sends scriptless-coin-lock-recover-tx</tspan></text>
|
||||
<path class="inline_expression_divider" d="M-37 1789.24H999"/>
|
||||
<path class="label-text-background" d="M396.95 1781.98h168.09V1796H396.95z"/>
|
||||
<text x="481" y="1792.99" class="empty-text comment-row-text"><tspan>bidder swipes script coin lock tx</tspan></text>
|
||||
<path d="M689 1868.17c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:#00f" marker-end="url(#mscgenjsreplacememethod-#0000FF)"/>
|
||||
<path class="label-text-background" d="M692 1835.91h40.91v14.02H692z"/>
|
||||
<text x="692" y="1846.92" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path class="label-text-background" d="M692 1851.91h142.41v14.02H692z"/>
|
||||
<text x="692" y="1862.92" class="directional-text method-text anchor-start"><tspan>pre-refund tx lock to expire</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:#00f" marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 1924.3H273"/>
|
||||
<path class="label-text-background" d="M368.63 1908.55h224.75v14.02H368.63z"/>
|
||||
<text x="481" y="1919.55" class="directional-text callback-text"><tspan>Sends script-coin-lock-pre-refund-swipe-tx</tspan></text>
|
||||
</g>
|
||||
<g id="mscgenjsreplaceme_notes">
|
||||
<path d="m591 209 3-17h190l3 17-3 17H594z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="212.75" class="box-text abox-text"><tspan>Bid Sent</tspan></text>
|
||||
<path class="box" style="stroke:red" d="M383 230h196v34H383z"/>
|
||||
<text x="481" y="250.75" class="box-text"><tspan>User accepts bid</tspan></text>
|
||||
<path d="M799 268h395v9h9m-9-9 9 9v41H799v-50z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="280.75" class="box-text note-text"><tspan>The BidAccept message contains the pubkeys the offerer will use and</tspan></text>
|
||||
<text x="1001" y="296.75" class="box-text note-text"><tspan>a DLEAG proof one key will work across both chains of the swapping</tspan></text>
|
||||
<text x="1001" y="312.75" class="box-text note-text"><tspan>coins</tspan></text>
|
||||
<path d="m591 339 3-17h190l3 17-3 17H594z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="342.75" class="box-text abox-text"><tspan>Bid Receiving accept</tspan></text>
|
||||
<path d="m591 377 3-17h190l3 17-3 17H594z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="380.75" class="box-text abox-text"><tspan>Bid Accepted</tspan></text>
|
||||
<path d="M799 398h395v9h9m-9-9 9 9v25H799v-34z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="410.75" class="box-text note-text"><tspan>The XmrBidLockTxSigsMessage contains the bidder's signatures for the</tspan></text>
|
||||
<text x="1001" y="426.75" class="box-text note-text"><tspan>script-coin-lock-refund and script-coin-lock-refund-spend txns.</tspan></text>
|
||||
<path d="m591 453 3-17h190l3 17-3 17H594z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="448.75" class="box-text abox-text"><tspan>Exchanged script lock tx sigs</tspan></text>
|
||||
<text x="689" y="464.75" class="box-text abox-text"><tspan>msg</tspan></text>
|
||||
<path d="M799 474h395v9h9m-9-9 9 9v25H799v-34z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="486.75" class="box-text note-text"><tspan>The XmrBidLockSpendTxMessage contains the script-coin-lock-tx and</tspan></text>
|
||||
<text x="1001" y="502.75" class="box-text note-text"><tspan>the offerer's signature for it.</tspan></text>
|
||||
<path d="m591 529 3-17h190l3 17-3 17H594z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="532.75" class="box-text abox-text"><tspan>Bid Script coin spend tx valid</tspan></text>
|
||||
<path d="m591 567 3-17h190l3 17-3 17H594z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="562.75" class="box-text abox-text"><tspan>Exchanged script lock spend tx</tspan></text>
|
||||
<text x="689" y="578.75" class="box-text abox-text"><tspan>msg</tspan></text>
|
||||
<path d="m591 680.06 3-17h190l3 17-3 17H594z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="683.81" class="box-text abox-text"><tspan>Bid Script coin locked</tspan></text>
|
||||
<path d="m591 831.12 3-17h190l3 17-3 17H594z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="834.87" class="box-text abox-text"><tspan>Bid Scriptless coin locked</tspan></text>
|
||||
<path d="M-40 869.12h98.39v11.02l-7 7H-40" class="box inline_expression_label"/>
|
||||
<text x="-38" y="882.37" class="inline_expression-text alt-text anchor-start"><tspan>alt: success path</tspan></text>
|
||||
<path d="M799 890.11h395v9h9m-9-9 9 9v73.02H799v-82.02z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="902.87" class="box-text note-text"><tspan>The XmrBidLockReleaseMessage contains the offerer's OTVES for it. </tspan></text>
|
||||
<text x="1001" y="918.87" class="box-text note-text"><tspan> The bidder decodes the offerer's signature</tspan></text>
|
||||
<text x="1001" y="934.87" class="box-text note-text"><tspan>from the OTVES. When the offerer has the</tspan></text>
|
||||
<text x="1001" y="950.87" class="box-text note-text"><tspan>plaintext signature, they can decode the bidder's noscript-coin-lock-tx</tspan></text>
|
||||
<text x="1001" y="966.87" class="box-text note-text"><tspan>signature.</tspan></text>
|
||||
<path d="m591 993.12 3-17h190l3 17-3 17H594z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="996.87" class="box-text abox-text"><tspan>Script coin lock released</tspan></text>
|
||||
<path d="m591 1069.12 3-17h190l3 17-3 17H594z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="1072.87" class="box-text abox-text"><tspan>Script tx redeemed</tspan></text>
|
||||
<path d="m591 1107.12 3-17h190l3 17-3 17H594z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="1110.87" class="box-text abox-text"><tspan>Bid Completed</tspan></text>
|
||||
<path d="M799 1279.18h395v9h9m-9-9 9 9v25H799v-34z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="1299.93" class="box-text note-text"><tspan>tx can be sent by either party.</tspan></text>
|
||||
<path d="m591 1372.18 3-17.01h190l3 17.01-3 17.01H594z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="1367.93" class="box-text abox-text"><tspan>Bid Script pre-refund tx in</tspan></text>
|
||||
<text x="689" y="1383.93" class="box-text abox-text"><tspan>chain</tspan></text>
|
||||
<path d="M-36 1410.18h200.86v11.02l-7 7H-36" class="box inline_expression_label"/>
|
||||
<text x="-34" y="1423.43" class="inline_expression-text alt-text anchor-start"><tspan>alt: offerer refunds script coin lock tx</tspan></text>
|
||||
<path d="M799 1528.23h395v9h9m-9-9 9 9v41.02H799v-50.02z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="1540.99" class="box-text note-text"><tspan>Refunds the script lock tx, with the offerer's cleartext signature</tspan></text>
|
||||
<text x="1001" y="1556.99" class="box-text note-text"><tspan>the bidder can refund the noscript lock tx. </tspan></text>
|
||||
<text x="1001" y="1572.99" class="box-text note-text"><tspan>Once the lock expires the pre-refund tx can be spent by the bidder.</tspan></text>
|
||||
<path d="m383 1599.24 3-17h190l3 17-3 17H386z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="1602.99" class="box-text abox-text"><tspan>Bid Failed, refunded</tspan></text>
|
||||
<path d="M799 1620.24h395v9h9m-9-9 9 9v25H799v-34z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="1640.99" class="box-text note-text"><tspan>Bidder recovers the offerer's scriptless chain key-shard.</tspan></text>
|
||||
<path d="m591 1713.24 3-17h190l3 17-3 17H594z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="1716.99" class="box-text abox-text"><tspan>Bid Scriptless tx recovered</tspan></text>
|
||||
<path d="m591 1751.24 3-17h190l3 17-3 17H594z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="1754.99" class="box-text abox-text"><tspan>Bid Failed, refunded</tspan></text>
|
||||
<path d="m591 1962.3 3-17h190l3 17-3 17H594z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="1966.05" class="box-text abox-text"><tspan>Bid Failed, swiped</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 39 KiB |
388
basicswap/static/sequence_diagrams/xmr.offerer.alt.xu.min.svg
Normal file
388
basicswap/static/sequence_diagrams/xmr.offerer.alt.xu.min.svg
Normal file
@@ -0,0 +1,388 @@
|
||||
<svg version="1.1" id="mscgenjsreplaceme" class="mscgenjsreplaceme" xmlns="http://www.w3.org/2000/svg" width="1264" height="1933.3" style="font-family:Helvetica,sans-serif;font-size:12px;font-weight:400;font-style:normal;text-decoration:none;background-color:#fff;stroke:#000;stroke-width:2">
|
||||
<defs>
|
||||
<marker orient="auto" id="mscgenjsreplacemecallback-#0000FF" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path d="m1 1 8 2-8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:#00f"/>
|
||||
</marker>
|
||||
<marker orient="auto" id="mscgenjsreplacemecallback-l-#0000FF" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path d="M17 1 9 3l8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:#00f"/>
|
||||
</marker>
|
||||
<marker orient="auto" id="mscgenjsreplacemecallback-#008800" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path d="m1 1 8 2-8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:#080"/>
|
||||
</marker>
|
||||
<marker orient="auto" id="mscgenjsreplacemecallback-l-#008800" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path d="M17 1 9 3l8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:#080"/>
|
||||
</marker>
|
||||
<marker orient="auto" id="mscgenjsreplacemecallback-#FF0000" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path d="m1 1 8 2-8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:red"/>
|
||||
</marker>
|
||||
<marker orient="auto" id="mscgenjsreplacemecallback-l-#FF0000" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path d="M17 1 9 3l8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:red"/>
|
||||
</marker>
|
||||
<marker orient="auto" id="mscgenjsreplacememethod-#0000FF" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path class="arrow-style" stroke="#00F" fill="#00F" d="m1 1 8 2-8 2z"/>
|
||||
</marker>
|
||||
<marker orient="auto" id="mscgenjsreplacememethod-l-#0000FF" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path class="arrow-style" stroke="#00F" fill="#00F" d="M17 1 9 3l8 2z"/>
|
||||
</marker>
|
||||
<marker orient="auto" id="mscgenjsreplacememethod-#FF0000" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path class="arrow-style" stroke="red" fill="red" d="m1 1 8 2-8 2z"/>
|
||||
</marker>
|
||||
<marker orient="auto" id="mscgenjsreplacememethod-l-#FF0000" class="arrow-marker" viewBox="0 0 10 10" refX="9" refY="3" markerUnits="strokeWidth" markerWidth="10" markerHeight="10">
|
||||
<path class="arrow-style" stroke="red" fill="red" d="M17 1 9 3l8 2z"/>
|
||||
</marker>
|
||||
<style>
|
||||
.mscgenjsreplaceme path,.mscgenjsreplaceme rect{fill:none}.mscgenjsreplaceme .label-text-background{fill:#fff;stroke:#fff;stroke-width:0}.mscgenjsreplaceme .return{stroke-dasharray:5,3}.mscgenjsreplaceme text{color:inherit;stroke:none;text-anchor:middle}.mscgenjsreplaceme text.anchor-start{text-anchor:start}.mscgenjsreplaceme .arrow-marker{overflow:visible}.mscgenjsreplaceme .arrow-style{stroke-width:1}.mscgenjsreplaceme .arcrow{stroke-linecap:butt}.mscgenjsreplaceme .box,.mscgenjsreplaceme .entity{fill:#fff;stroke-linejoin:round}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="mscgenjsreplaceme_body" transform="translate(47 3)">
|
||||
<path class="bglayer" style="fill:#fff;stroke:#fff;stroke-width:0" d="M-47-3h1264v1933.3H-47z" id="mscgenjsreplaceme_background"/>
|
||||
<path class="box inline_expression alt" d="M-39 778.06h1040V1908.3H-39z" id="mscgenjsreplaceme_arcspans"/>
|
||||
<g id="mscgenjsreplaceme_lifelines">
|
||||
<path class="arcrow" style="stroke:transparent" d="M65 38v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 38v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 38v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 38v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 38v38M1105 38v38M65 76v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 76v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 76v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 76v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 76v38M1105 76v38M65 114v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 114v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 114v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 114v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 114v38M1105 114v38M65 152v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 152v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 152v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 152v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 152v38M1105 152v38M65 190v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 190v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 190v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 190v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 190v38M1105 190v38M65 228v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 228v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 228v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 228v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 228v38M1105 228v38M65 266v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 266v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 266v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 266v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 266v38M1105 266v38M65 304v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 304v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 304v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 304v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 304v38M1105 304v38M65 342v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 342v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 342v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 342v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 342v38M1105 342v38M65 380v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 380v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 380v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 380v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 380v38M1105 380v38M65 418v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 418v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 418v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 418v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 418v38M1105 418v38M65 456v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 456v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 456v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 456v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 456v38M1105 456v38M65 494v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 494v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 494v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 494v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 494v38M1105 494v38M65 532v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 532v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 532v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 532v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 532v38M1105 532v38M65 570v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 570v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 570v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 570v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 570v38M1105 570v38M65 608v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 608v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 608v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 608v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 608v38M1105 608v38M65 646v75.06"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 646v75.06"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 646v75.06"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 646v75.06"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 646v75.06M1105 646v75.06M65 721.06v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 721.06v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 721.06v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 721.06v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 721.06v38M1105 721.06v38M65 759.06v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 759.06v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 759.06v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 759.06v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 759.06v38M1105 759.06v38M65 797.06v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 797.06v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 797.06v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 797.06v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 797.06v38M1105 797.06v38M65 835.06v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 835.06v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 835.06v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 835.06v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 835.06v38M1105 835.06v38M65 873.06v75.06"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 873.06v75.06"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 873.06v75.06"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 873.06v75.06"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 873.06v75.06M1105 873.06v75.06M65 948.12v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 948.12v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 948.12v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 948.12v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 948.12v38M1105 948.12v38M65 986.12v86"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 986.12v86"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 986.12v86"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 986.12v86"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 986.12v86M1105 986.12v86M65 1072.12v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1072.12v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1072.12v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1072.12v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1072.12v38M1105 1072.12v38M65 1110.12v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1110.12v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1110.12v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1110.12v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1110.12v38M1105 1110.12v38M65 1148.12v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1148.12v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1148.12v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1148.12v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1148.12v38M1105 1148.12v38M65 1186.12v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1186.12v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1186.12v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1186.12v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1186.12v38M1105 1186.12v38M65 1224.12v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1224.12v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1224.12v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1224.12v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1224.12v38M1105 1224.12v38M65 1262.12v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1262.12v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1262.12v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1262.12v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1262.12v38M1105 1262.12v38M65 1300.12v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1300.12v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1300.12v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1300.12v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1300.12v38M1105 1300.12v38M65 1338.12v75.06"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1338.12v75.06"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1338.12v75.06"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1338.12v75.06"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1338.12v75.06M1105 1338.12v75.06M65 1413.18v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1413.18v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1413.18v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1413.18v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1413.18v38M1105 1413.18v38M65 1451.18v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1451.18v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1451.18v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1451.18v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1451.18v38M1105 1451.18v38M65 1489.18v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1489.18v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1489.18v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1489.18v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1489.18v38M1105 1489.18v38M65 1527.18v75.06"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1527.18v75.06"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1527.18v75.06"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1527.18v75.06"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1527.18v75.06M1105 1527.18v75.06M65 1602.24v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1602.24v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1602.24v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1602.24v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1602.24v38M1105 1602.24v38M65 1640.24v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1640.24v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1640.24v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1640.24v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1640.24v38M1105 1640.24v38M65 1678.24v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1678.24v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1678.24v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1678.24v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1678.24v38M1105 1678.24v38M65 1716.24v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1716.24v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1716.24v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1716.24v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1716.24v38M1105 1716.24v38M65 1754.24v59.06"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1754.24v59.06"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1754.24v59.06"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1754.24v59.06"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1754.24v59.06M1105 1754.24v59.06M65 1813.3v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1813.3v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1813.3v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1813.3v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1813.3v38M1105 1813.3v38M65 1851.3v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1851.3v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1851.3v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1851.3v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1851.3v38M1105 1851.3v38M65 1889.3v38"/>
|
||||
<path class="arcrow" style="stroke:#080" d="M273 1889.3v38"/>
|
||||
<path class="arcrow" style="stroke:red" d="M481 1889.3v38"/>
|
||||
<path class="arcrow" style="stroke:#00f" d="M689 1889.3v38"/>
|
||||
<path class="arcrow" style="stroke:transparent" d="M897 1889.3v38M1105 1889.3v38"/>
|
||||
</g>
|
||||
<g id="mscgenjsreplaceme_sequence">
|
||||
<path class="entity" style="stroke:transparent" d="M0 0h130v38H0z"/>
|
||||
<text x="65" y="22.75" class="entity-text"><tspan> </tspan></text>
|
||||
<path class="entity" style="fill:#cfc;stroke:#080" d="M208 0h130v38H208z"/>
|
||||
<text x="273" y="22.75" class="entity-text"><tspan>Network</tspan></text>
|
||||
<path class="entity" style="fill:#fcc;stroke:red" d="M416 0h130v38H416z"/>
|
||||
<text x="481" y="22.75" class="entity-text"><tspan>Offerer</tspan></text>
|
||||
<path class="entity" style="fill:#ccf;stroke:#00f" d="M624 0h130v38H624z"/>
|
||||
<text x="689" y="22.75" class="entity-text"><tspan>Bidder</tspan></text>
|
||||
<path class="entity" style="stroke:transparent" d="M832 0h130v38H832z"/>
|
||||
<text x="897" y="22.75" class="entity-text"><tspan> </tspan></text>
|
||||
<path class="entity" style="stroke:transparent" d="M1040 0h130v38h-130z"/>
|
||||
<text x="1105" y="22.75" class="entity-text"><tspan> </tspan></text>
|
||||
<path class="arc directional callback" style="stroke:red" marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 95H273"/>
|
||||
<path class="label-text-background" d="M345.08 79.25h63.84v14h-63.84z"/>
|
||||
<text x="377" y="90.25" class="directional-text callback-text"><tspan>Sends Offer</tspan></text>
|
||||
<path class="arc directional return" style="stroke:#080" marker-end="url(#mscgenjsreplacemecallback-#008800)" d="M273 133h416"/>
|
||||
<path class="label-text-background" d="M445.75 117.25h70.5v14h-70.5z"/>
|
||||
<text x="481" y="128.25" class="directional-text return-text"><tspan>Detects Offer</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:#00f" marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 171H481"/>
|
||||
<path class="label-text-background" d="M557.64 155.25h54.72v14h-54.72z"/>
|
||||
<text x="585" y="166.25" class="directional-text callback-text"><tspan>Sends Bid</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:red" marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 323h208"/>
|
||||
<path class="label-text-background" d="M513.28 307.25h143.77v14H513.28z"/>
|
||||
<text x="585" y="318.25" class="directional-text callback-text"><tspan>Sends BidAccept message</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:#00f" marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 399H481"/>
|
||||
<path class="label-text-background" d="M491.28 383.25h187.77v14H491.28z"/>
|
||||
<text x="585" y="394.25" class="directional-text callback-text"><tspan>Sends XmrBidLockTxSigsMessage</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:red" marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 475H273"/>
|
||||
<path class="label-text-background" d="M311.64 459.25h130.72v14H311.64z"/>
|
||||
<text x="377" y="470.25" class="directional-text callback-text"><tspan>Sends script-coin-lock-tx</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:red" marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 551h208"/>
|
||||
<path class="label-text-background" d="M485.61 535.25h199.11v14H485.61z"/>
|
||||
<text x="585" y="546.25" class="directional-text callback-text"><tspan>Sends XmrBidLockSpendTxMessage</tspan></text>
|
||||
<path d="M689 675.93c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:#00f" marker-end="url(#mscgenjsreplacememethod-#0000FF)"/>
|
||||
<path class="label-text-background" d="M692 627.67h40.91v14.02H692z"/>
|
||||
<text x="692" y="638.68" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path class="label-text-background" d="M692 643.67h107.02v14.02H692z"/>
|
||||
<text x="692" y="654.68" class="directional-text method-text anchor-start"><tspan>script-coin-lock-tx to</tspan></text>
|
||||
<path class="label-text-background" d="M692 659.67h39.34v14.02H692z"/>
|
||||
<text x="692" y="670.68" class="directional-text method-text anchor-start"><tspan>confirm</tspan></text>
|
||||
<path d="M481 675.93c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:red" marker-end="url(#mscgenjsreplacememethod-#FF0000)"/>
|
||||
<path class="label-text-background" d="M484 627.67h40.91v14.02H484z"/>
|
||||
<text x="484" y="638.68" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path class="label-text-background" d="M484 643.67h107.02v14.02H484z"/>
|
||||
<text x="484" y="654.68" class="directional-text method-text anchor-start"><tspan>script-coin-lock-tx to</tspan></text>
|
||||
<path class="label-text-background" d="M484 659.67h39.34v14.02H484z"/>
|
||||
<text x="484" y="670.68" class="directional-text method-text anchor-start"><tspan>confirm</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:#00f" marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 816.06H273"/>
|
||||
<path class="label-text-background" d="M408.97 800.3h144.06v14.02H408.97z"/>
|
||||
<text x="481" y="811.31" class="directional-text callback-text"><tspan>Sends noscript-coin-lock-tx</tspan></text>
|
||||
<path d="M481 902.99c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:red" marker-end="url(#mscgenjsreplacememethod-#FF0000)"/>
|
||||
<path class="label-text-background" d="M484 854.73h40.91v14.02H484z"/>
|
||||
<text x="484" y="865.74" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path class="label-text-background" d="M484 870.73h120.38v14.02H484z"/>
|
||||
<text x="484" y="881.74" class="directional-text method-text anchor-start"><tspan>noscript-coin-lock-tx to</tspan></text>
|
||||
<path class="label-text-background" d="M484 886.73h39.34v14.02H484z"/>
|
||||
<text x="484" y="897.74" class="directional-text method-text anchor-start"><tspan>confirm</tspan></text>
|
||||
<path class="arc directional method" style="stroke:red" marker-end="url(#mscgenjsreplacememethod-#FF0000)" d="M481 1029.12h208"/>
|
||||
<path class="label-text-background" d="M519.64 1013.36h130.72v14.02H519.64z"/>
|
||||
<text x="585" y="1024.37" class="directional-text method-text"><tspan>Sends script-coin-lock-tx</tspan></text>
|
||||
<path class="label-text-background" d="M539.3 1031.36h91.73v14.02H539.3z"/>
|
||||
<text x="585" y="1042.37" class="directional-text method-text"><tspan>release message</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:#00f" marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 1129.12H273"/>
|
||||
<path class="label-text-background" d="M397.3 1113.36h167.41v14.02H397.3z"/>
|
||||
<text x="481" y="1124.37" class="directional-text callback-text"><tspan>Sends script-coin-lock-spend-tx</tspan></text>
|
||||
<path class="arc directional return" style="stroke:#080" marker-end="url(#mscgenjsreplacemecallback-#008800)" d="M273 1167.12h208"/>
|
||||
<path class="label-text-background" d="M289.97 1151.36h174.06v14.02H289.97z"/>
|
||||
<text x="377" y="1162.37" class="directional-text return-text"><tspan>Detects script-coin-lock-spend-tx</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:red" marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 1243.12H273"/>
|
||||
<path class="label-text-background" d="M286.63 1227.36h180.75v14.02H286.63z"/>
|
||||
<text x="377" y="1238.37" class="directional-text callback-text"><tspan>Sends noscript-coin-lock-spend-tx</tspan></text>
|
||||
<path d="M481 1368.05c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:red" marker-end="url(#mscgenjsreplacememethod-#FF0000)"/>
|
||||
<path class="label-text-background" d="M484 1319.8h40.91v14.02H484z"/>
|
||||
<text x="484" y="1330.8" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path class="label-text-background" d="M484 1335.8h143.39v14.02H484z"/>
|
||||
<text x="484" y="1346.8" class="directional-text method-text anchor-start"><tspan>noscript-coin-lock-spend-tx</tspan></text>
|
||||
<path class="label-text-background" d="M484 1351.8h52.69v14.02H484z"/>
|
||||
<text x="484" y="1362.8" class="directional-text method-text anchor-start"><tspan>to confirm</tspan></text>
|
||||
<path class="inline_expression_divider" style="stroke-dasharray:10,5" d="M-39 1470.18h1040"/>
|
||||
<path class="label-text-background" d="M459.98 1462.92h42.03v14.02h-42.03z"/>
|
||||
<text x="481" y="1473.93" class="empty-text comment-row-text"><tspan>fail path</tspan></text>
|
||||
<path d="M481 1557.11c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:red" marker-end="url(#mscgenjsreplacememethod-#FF0000)"/>
|
||||
<path class="label-text-background" d="M484 1508.86h40.91v14.02H484z"/>
|
||||
<text x="484" y="1519.86" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path class="label-text-background" d="M484 1524.86h93.36v14.02H484z"/>
|
||||
<text x="484" y="1535.86" class="directional-text method-text anchor-start"><tspan>script-coin-lock-tx</tspan></text>
|
||||
<path class="label-text-background" d="M484 1540.86h93.7v14.02H484z"/>
|
||||
<text x="484" y="1551.86" class="directional-text method-text anchor-start"><tspan>locktime to expire</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:red" marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 1621.24H273"/>
|
||||
<path class="label-text-background" d="M359.98 1605.48h34.03v14.02h-34.03z"/>
|
||||
<text x="377" y="1616.49" class="directional-text callback-text"><tspan>Sends</tspan></text>
|
||||
<path class="label-text-background" d="M300.64 1623.48h152.72v14.02H300.64z"/>
|
||||
<text x="377" y="1634.49" class="directional-text callback-text"><tspan>script-coin-lock-pre-refund-tx</tspan></text>
|
||||
<path class="arc directional return" style="stroke:#080" marker-end="url(#mscgenjsreplacemecallback-#008800)" d="M273 1659.24h208"/>
|
||||
<path class="label-text-background" d="M300.64 1643.48h152.72v14.02H300.64z"/>
|
||||
<text x="377" y="1654.49" class="directional-text return-text"><tspan>script-coin-lock-pre-refund-tx</tspan></text>
|
||||
<path d="M481 1776.17c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:red" marker-end="url(#mscgenjsreplacememethod-#FF0000)"/>
|
||||
<path class="label-text-background" d="M484 1743.91h40.91v14.02H484z"/>
|
||||
<text x="484" y="1754.92" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path class="label-text-background" d="M484 1759.91h124.06v14.02H484z"/>
|
||||
<text x="484" y="1770.92" class="directional-text method-text anchor-start"><tspan>pre-refund tx to confirm</tspan></text>
|
||||
<path class="arc directional callback" style="stroke:red" marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 1832.3H273"/>
|
||||
<path class="label-text-background" d="M359.98 1816.55h34.03v14.02h-34.03z"/>
|
||||
<text x="377" y="1827.55" class="directional-text callback-text"><tspan>Sends</tspan></text>
|
||||
<path class="label-text-background" d="M282.3 1834.55h189.41v14.02H282.3z"/>
|
||||
<text x="377" y="1845.55" class="directional-text callback-text"><tspan>script-coin-lock-pre-refund-spend-tx</tspan></text>
|
||||
</g>
|
||||
<g id="mscgenjsreplaceme_notes">
|
||||
<path d="m381 209 3-17h194l3 17-3 17H384z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="212.75" class="box-text abox-text"><tspan>Bid Receiving</tspan></text>
|
||||
<path d="m381 247 3-17h194l3 17-3 17H384z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="250.75" class="box-text abox-text"><tspan>Bid Received</tspan></text>
|
||||
<path class="box" style="stroke:red" d="M381 268h200v34H381z"/>
|
||||
<text x="481" y="288.75" class="box-text"><tspan>User accepts bid</tspan></text>
|
||||
<path d="M797 306h399v9h9m-9-9 9 9v25H797v-34z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="318.75" class="box-text note-text"><tspan>The BidAccept message contains the pubkeys the offerer will use and a</tspan></text>
|
||||
<text x="1001" y="334.75" class="box-text note-text"><tspan>DLEAG proof one key will work across both chains of the swapping coins</tspan></text>
|
||||
<path d="m381 361 3-17h194l3 17-3 17H384z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="364.75" class="box-text abox-text"><tspan>Bid Accepted</tspan></text>
|
||||
<path d="M797 382h399v9h9m-9-9 9 9v25H797v-34z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="394.75" class="box-text note-text"><tspan>The XmrBidLockTxSigsMessage contains the bidder's signatures for the</tspan></text>
|
||||
<text x="1001" y="410.75" class="box-text note-text"><tspan>script-coin-lock-refund and script-coin-lock-refund-spend txns.</tspan></text>
|
||||
<path d="m381 437 3-17h194l3 17-3 17H384z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="432.75" class="box-text abox-text"><tspan>Exchanged script lock tx sigs</tspan></text>
|
||||
<text x="481" y="448.75" class="box-text abox-text"><tspan>msg</tspan></text>
|
||||
<path d="m381 513 3-17h194l3 17-3 17H384z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="516.75" class="box-text abox-text"><tspan>Bid Script coin spend tx valid</tspan></text>
|
||||
<path d="M797 534h399v9h9m-9-9 9 9v25H797v-34z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="546.75" class="box-text note-text"><tspan>The XmrBidLockSpendTxMessage contains the script-coin-lock-tx and the</tspan></text>
|
||||
<text x="1001" y="562.75" class="box-text note-text"><tspan>offerer's signature for it.</tspan></text>
|
||||
<path d="m381 589 3-17h194l3 17-3 17H384z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="584.75" class="box-text abox-text"><tspan>Exchanged script lock spend tx</tspan></text>
|
||||
<text x="481" y="600.75" class="box-text abox-text"><tspan>msg</tspan></text>
|
||||
<path d="m381 740.06 3-17h194l3 17-3 17H384z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="743.81" class="box-text abox-text"><tspan>Bid Script coin locked</tspan></text>
|
||||
<path d="M-38 778.06h98.39v11.02l-7 7H-38" class="box inline_expression_label"/>
|
||||
<text x="-36" y="791.31" class="inline_expression-text alt-text anchor-start"><tspan>alt: success path</tspan></text>
|
||||
<path d="m381 967.12 3-17h194l3 17-3 17H384z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="970.87" class="box-text abox-text"><tspan>Bid Scriptless coin locked</tspan></text>
|
||||
<path d="M797 988.11h399v9h9m-9-9 9 9v73.02H797v-82.02z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="1000.87" class="box-text note-text"><tspan>The XmrBidLockReleaseMessage contains the offerer's OTVES for the</tspan></text>
|
||||
<text x="1001" y="1016.87" class="box-text note-text"><tspan>script-coin-lock-tx. The bidder decodes the</tspan></text>
|
||||
<text x="1001" y="1032.87" class="box-text note-text"><tspan>offerer's signature from the OTVES. When the</tspan></text>
|
||||
<text x="1001" y="1048.87" class="box-text note-text"><tspan>offerer has the plaintext signature, they can decode the bidder's key</tspan></text>
|
||||
<text x="1001" y="1064.87" class="box-text note-text"><tspan>for the noscript-lock-tx.</tspan></text>
|
||||
<path d="m381 1091.12 3-17h194l3 17-3 17H384z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="1094.87" class="box-text abox-text"><tspan>Bid Script coin lock released</tspan></text>
|
||||
<path d="m381 1205.12 3-17h194l3 17-3 17H384z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="1208.87" class="box-text abox-text"><tspan>Bid Script tx redeemed</tspan></text>
|
||||
<path d="M797 1188.11h399v9h9m-9-9 9 9v25.02H797v-34.02z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="1200.87" class="box-text note-text"><tspan>The offerer extracts the bidder's plaintext signature and derives the</tspan></text>
|
||||
<text x="1001" y="1216.87" class="box-text note-text"><tspan>bidder's noscript-lock-tx keyhalf.</tspan></text>
|
||||
<path d="m381 1281.12 3-17h194l3 17-3 17H384z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="1284.87" class="box-text abox-text"><tspan>Bid Scriptless tx redeemed</tspan></text>
|
||||
<path d="m381 1432.18 3-17h194l3 17-3 17H384z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="1435.93" class="box-text abox-text"><tspan>Bid Completed</tspan></text>
|
||||
<path d="M797 1604.24h399v9h9m-9-9 9 9v25H797v-34z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="1624.99" class="box-text note-text"><tspan>tx can be sent by either party.</tspan></text>
|
||||
<path d="m381 1697.24 3-17.01h194l3 17.01-3 17.01H384z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="1692.99" class="box-text abox-text"><tspan>Bid Script pre-refund tx in</tspan></text>
|
||||
<text x="481" y="1708.99" class="box-text abox-text"><tspan>chain</tspan></text>
|
||||
<path d="M797 1815.29h399v9h9m-9-9 9 9v25.02H797v-34.02z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="1828.05" class="box-text note-text"><tspan>Refunds the script lock tx, with the offerer's cleartext signature</tspan></text>
|
||||
<text x="1001" y="1844.05" class="box-text note-text"><tspan>the bidder can refund the noscript lock tx.</tspan></text>
|
||||
<path d="m381 1870.3 3-17h194l3 17-3 17H384z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="1874.05" class="box-text abox-text"><tspan>Bid Failed, refunded</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 36 KiB |
@@ -1,15 +0,0 @@
|
||||
|
||||
.padded_row td
|
||||
{
|
||||
padding-top:1.5em;
|
||||
}
|
||||
|
||||
.bold
|
||||
{
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
.monospace
|
||||
{
|
||||
font-family:monospace;
|
||||
}
|
||||
38
basicswap/templates/automation_strategies.html
Normal file
38
basicswap/templates/automation_strategies.html
Normal file
@@ -0,0 +1,38 @@
|
||||
{% include 'header.html' %}
|
||||
|
||||
<h3>Automation Strategies</h3>
|
||||
{% for m in messages %}
|
||||
<p>{{ m }}</p>
|
||||
{% endfor %}
|
||||
|
||||
<form method="post">
|
||||
<table>
|
||||
|
||||
<tr><td>Sort By</td><td>
|
||||
<select name="sort_by">
|
||||
<option value="created_at"{% if filters.sort_by=='created_at' %} selected{% endif %}>Created At</option>
|
||||
</select>
|
||||
<select name="sort_dir">
|
||||
<option value="asc"{% if filters.sort_dir=='asc' %} selected{% endif %}>Ascending</option>
|
||||
<option value="desc"{% if filters.sort_dir=='desc' %} selected{% endif %}>Descending</option>
|
||||
</select>
|
||||
</td></tr>
|
||||
|
||||
<tr><td><input type="submit" name='applyfilters' value="Apply Filters"></td><td><input type="submit" name='clearfilters' value="Clear Filters"></td></tr>
|
||||
<tr><td><input type="submit" name='pageback' value="Page Back"></td><td>Page: {{ filters.page_no }}</td><td><input type="submit" name='pageforwards' value="Page Forwards"></td></tr>
|
||||
</table>
|
||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||
<input type="hidden" name="pageno" value="{{ filters.page_no }}">
|
||||
</form>
|
||||
|
||||
<p><a href="/newautomationstrategy">Create New Strategy</a></p>
|
||||
|
||||
<table>
|
||||
<tr><th>Name</th><th>Type</th></tr>
|
||||
{% for s in strategies %}
|
||||
<tr><td><a class="monospace" href=/automationstrategy/{{ s[0] }}>{{ s[1] }}</a></td><td>{{ s[2] }}</td></tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
<p><a href="/">home</a></p>
|
||||
</body></html>
|
||||
27
basicswap/templates/automation_strategy.html
Normal file
27
basicswap/templates/automation_strategy.html
Normal file
@@ -0,0 +1,27 @@
|
||||
{% include 'header.html' %}
|
||||
|
||||
<h3>Automation Strategy {{ strategy_id }}</h3>
|
||||
|
||||
{% for m in messages %}
|
||||
<p>{{ m }}</p>
|
||||
{% endfor %}
|
||||
|
||||
<table>
|
||||
<tr><td>Label</td><td>{{ strategy.label }}</td></tr>
|
||||
<tr><td>Type</td><td>{{ strategy.type }}</td></tr>
|
||||
<tr><td>Only known identities</td><td>{{ strategy.only_known_identities }}</td></tr>
|
||||
|
||||
<tr><td>Data</td><td>
|
||||
<textarea class="monospace" rows="10" cols="150" readonly>
|
||||
{{ strategy.data }}
|
||||
</textarea>
|
||||
<tr><td>Notes</td><td>
|
||||
<textarea rows="10" cols="150" readonly>
|
||||
{{ strategy.note }}
|
||||
</textarea>
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
|
||||
<p><a href="/">home</a></p>
|
||||
</body></html>
|
||||
11
basicswap/templates/automation_strategy_new.html
Normal file
11
basicswap/templates/automation_strategy_new.html
Normal file
@@ -0,0 +1,11 @@
|
||||
{% include 'header.html' %}
|
||||
|
||||
<h3>New Automation Strategy</h3>
|
||||
{% for m in messages %}
|
||||
<p>{{ m }}</p>
|
||||
{% endfor %}
|
||||
|
||||
<p>TODO</p>
|
||||
|
||||
<p><a href="/">home</a></p>
|
||||
</body></html>
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
<h3>Bid {{ bid_id }}</h3>
|
||||
{% if refresh %}
|
||||
<p>Page Refresh: {{ refresh }} seconds</p>
|
||||
<p><a href=/bid/{{ bid_id }}>Page Refresh: {{ refresh }} second</a></p>
|
||||
{% else %}
|
||||
<p><a href=/bid/{{ bid_id }}>refresh</a></p>
|
||||
{% endif %}
|
||||
|
||||
{% for m in messages %}
|
||||
@@ -75,9 +77,29 @@
|
||||
{% endif %}
|
||||
<input name="edit_bid" type="submit" value="Edit Bid">
|
||||
{% endif %}
|
||||
<br/>
|
||||
{% if data.show_bidder_seq_diagram %}
|
||||
<input name="hide_bidder_seq_diagram" type="submit" value="Hide Bidder Sequence Diagram">
|
||||
{% else %}
|
||||
<input name="show_bidder_seq_diagram" type="submit" value="Show Bidder Sequence Diagram">
|
||||
{% endif %}
|
||||
{% if data.show_offerer_seq_diagram %}
|
||||
<input name="hide_offerer_seq_diagram" type="submit" value="Hide Offerer Sequence Diagram">
|
||||
{% else %}
|
||||
<input name="show_offerer_seq_diagram" type="submit" value="Show Offerer Sequence Diagram">
|
||||
{% endif %}
|
||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||
</form>
|
||||
|
||||
{% if data.show_bidder_seq_diagram %}
|
||||
<img src="/static/sequence_diagrams/bidder.alt.xu.min.svg" />
|
||||
{% endif %}
|
||||
|
||||
{% if data.show_offerer_seq_diagram %}
|
||||
<img src="/static/sequence_diagrams/offerer.alt.xu.min.svg" />
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
||||
<h4>Old States</h4>
|
||||
<table>
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
<h3>Bid {{ bid_id }}</h3>
|
||||
{% if refresh %}
|
||||
<p>Page Refresh: {{ refresh }} seconds</p>
|
||||
<p><a href=/bid/{{ bid_id }}>Page Refresh: {{ refresh }} second</a></p>
|
||||
{% else %}
|
||||
<p><a href=/bid/{{ bid_id }}>refresh</a></p>
|
||||
{% endif %}
|
||||
|
||||
{% for m in messages %}
|
||||
@@ -72,6 +74,17 @@
|
||||
{% endif %}
|
||||
<input name="edit_bid" type="submit" value="Edit Bid">
|
||||
{% endif %}
|
||||
<br/>
|
||||
{% if data.show_bidder_seq_diagram %}
|
||||
<input name="hide_bidder_seq_diagram" type="submit" value="Hide Bidder Sequence Diagram">
|
||||
{% else %}
|
||||
<input name="show_bidder_seq_diagram" type="submit" value="Show Bidder Sequence Diagram">
|
||||
{% endif %}
|
||||
{% if data.show_offerer_seq_diagram %}
|
||||
<input name="hide_offerer_seq_diagram" type="submit" value="Hide Offerer Sequence Diagram">
|
||||
{% else %}
|
||||
<input name="show_offerer_seq_diagram" type="submit" value="Show Offerer Sequence Diagram">
|
||||
{% endif %}
|
||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||
|
||||
{% if data.show_txns %}
|
||||
@@ -117,6 +130,14 @@
|
||||
{% endif %}
|
||||
</form>
|
||||
|
||||
{% if data.show_bidder_seq_diagram %}
|
||||
<img src="/static/sequence_diagrams/xmr.bidder.alt.xu.min.svg" />
|
||||
{% endif %}
|
||||
|
||||
{% if data.show_offerer_seq_diagram %}
|
||||
<img src="/static/sequence_diagrams/xmr.offerer.alt.xu.min.svg" />
|
||||
{% endif %}
|
||||
|
||||
{% if data.chain_a_lock_tx_inputs %}
|
||||
<h5>Chain A Lock TX Inputs:</h5>
|
||||
<table>
|
||||
|
||||
@@ -20,6 +20,19 @@
|
||||
<option value="desc"{% if filters.sort_dir=='desc' %} selected{% endif %}>Descending</option>
|
||||
</select>
|
||||
</td></tr>
|
||||
<tr><td>State</td><td>
|
||||
<select name="state">
|
||||
<option value="-1"{% if filters.bid_state_ind==-1 %} selected{% endif %}>-- Any --</option>
|
||||
{% for s in data.bid_states %}
|
||||
<option value="{{ s[0] }}"{% if filters.bid_state_ind==s[0] %} selected{% endif %}>{{ s[1] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<tr><td>Include Expired</td><td>
|
||||
<select name="with_expired">
|
||||
<option value="true"{% if filters.with_expired==true %} selected{% endif %}>Include</option>
|
||||
<option value="false"{% if filters.with_expired==false %} selected{% endif %}>Exclude</option>
|
||||
</select>
|
||||
</td></tr>
|
||||
|
||||
<tr><td><input type="submit" name='applyfilters' value="Apply Filters"></td><td><input type="submit" name='clearfilters' value="Clear Filters"></td></tr>
|
||||
<tr><td><input type="submit" name='pageback' value="Page Back"></td><td>Page: {{ filters.page_no }}</td><td><input type="submit" name='pageforwards' value="Page Forwards"></td></tr>
|
||||
|
||||
@@ -4,11 +4,42 @@
|
||||
{% if refresh %}
|
||||
<meta http-equiv="refresh" content="{{ refresh }}">
|
||||
{% endif %}
|
||||
<link type="text/css" media="all" href="/static/style.css" rel="stylesheet">
|
||||
<link rel=icon sizes="32x32" type="image/png" href="/static/favicon-32.png">
|
||||
<link type="text/css" media="all" href="/static/css/simple/style.css" rel="stylesheet">
|
||||
<link rel=icon sizes="32x32" type="image/png" href="/static/images/favicon-32.png">
|
||||
<title>{{ title }}</title>
|
||||
</head>
|
||||
<body>
|
||||
{% if h2 %}
|
||||
<h2>{{ h2 }}</h2>
|
||||
{% endif %}
|
||||
|
||||
{% if ws_url %}
|
||||
<script>
|
||||
var ws = new WebSocket("{{ ws_url }}"),
|
||||
floating_div = document.createElement('div');
|
||||
floating_div.classList.add('floatright');
|
||||
messages = document.createElement('ul');
|
||||
messages.setAttribute('id', 'ul_updates');
|
||||
ws.onmessage = function (event) {
|
||||
let json = JSON.parse(event.data);
|
||||
|
||||
let event_message = 'Unknown event';
|
||||
if (json['event'] == 'new_offer') {
|
||||
event_message = '<a href=/offer/' + json['offer_id'] + '>New offer</a>';
|
||||
} else
|
||||
if (json['event'] == 'new_bid') {
|
||||
event_message = '<a href=/bid/' + json['bid_id'] + '>New bid</a> on offer <a href=/offer/' + json['offer_id'] + '>' + json['offer_id'] + '</a>';
|
||||
} else
|
||||
if (json['event'] == 'bid_accepted') {
|
||||
event_message = '<a href=/bid/' + json['bid_id'] + '>Bid accepted</a>';
|
||||
}
|
||||
|
||||
let messages = document.getElementById('ul_updates'),
|
||||
message = document.createElement('li');
|
||||
message.innerHTML = event_message;
|
||||
messages.appendChild(message);
|
||||
};
|
||||
floating_div.appendChild(messages);
|
||||
document.body.appendChild(floating_div);
|
||||
</script>
|
||||
{% endif %}
|
||||
|
||||
@@ -16,9 +16,11 @@ Version: {{ version }}
|
||||
<a href="/active">Swaps in Progress: {{ summary.num_swapping }}</a><br/>
|
||||
<a href="/offers">Network Offers: {{ summary.num_network_offers }}</a><br/>
|
||||
<a href="/sentoffers">Sent Offers: {{ summary.num_sent_offers }}</a><br/>
|
||||
<a href="/availablebids">Available Bids: {{ summary.num_available_bids }}</a><br/>
|
||||
<a href="/bids">Received Bids: {{ summary.num_recv_bids }}</a><br/>
|
||||
<a href="/sentbids">Sent Bids: {{ summary.num_sent_bids }}</a><br/>
|
||||
<a href="/watched">Watched Outputs: {{ summary.num_watched_outputs }}</a><br/>
|
||||
<a href="/automation">Automation Strategies</a><br/>
|
||||
{% if use_tor_proxy %} <a href="/tor">TOR Information</a><br/> {% endif %}
|
||||
</p>
|
||||
|
||||
|
||||
@@ -2,12 +2,17 @@
|
||||
|
||||
<h3>Offer {{ offer_id }}</h3>
|
||||
{% if refresh %}
|
||||
<p>Page Refresh: {{ refresh }} seconds</p>
|
||||
<p><a href=/offer/{{ offer_id }}>Page Refresh: {{ refresh }} second</a></p>
|
||||
{% else %}
|
||||
<p><a href=/offer/{{ offer_id }}>refresh</a></p>
|
||||
{% endif %}
|
||||
|
||||
{% for m in messages %}
|
||||
<p>{{ m }}</p>
|
||||
{% endfor %}
|
||||
{% for m in err_messages %}
|
||||
<p class="error_msg">Error: {{ m }}</p>
|
||||
{% endfor %}
|
||||
|
||||
{% if sent_bid_id %}
|
||||
<p><a href="/bid/{{ sent_bid_id }}">Sent Bid {{ sent_bid_id }}</a></p>
|
||||
@@ -19,9 +24,11 @@
|
||||
<tr><td>Coin To</td><td>{{ data.coin_to }}</td></tr>
|
||||
<tr><td>Amount From</td><td>{{ data.amt_from }} {{ data.tla_from }}</td></tr>
|
||||
<tr><td>Amount To</td><td>{{ data.amt_to }} {{ data.tla_to }}</td></tr>
|
||||
<tr><td>Minimum Bid Amount</td><td>{{ data.amt_bid_min }} {{ data.tla_from }}</td></tr>
|
||||
<tr><td>Rate</td><td>{{ data.rate }}</td></tr>
|
||||
<tr><td>Amount Variable</td><td>{{ data.amount_negotiable }}</td></tr>
|
||||
<tr><td>Rate Variable</td><td>{{ data.rate_negotiable }}</td></tr>
|
||||
<tr><td title="Total coin-from value of completed bids, that this node is involved in">Amount Swapped</td><td>{{ data.amt_swapped }} {{ data.tla_from }}</td></tr>
|
||||
<tr><td title="If bids can be sent with a different amount">Amount Variable</td><td>{{ data.amount_negotiable }}</td></tr>
|
||||
<tr><td title="If bids can be sent with a different amount">Rate Variable</td><td>{{ data.rate_negotiable }}</td></tr>
|
||||
<tr><td>Script Lock Type</td><td>{{ data.lock_type }}</td></tr>
|
||||
<tr><td>Script Lock Value</td><td>{{ data.lock_value }} {{ data.lock_value_hr }}</td></tr>
|
||||
{% if data.addr_to == "Public" %}
|
||||
@@ -35,7 +42,14 @@
|
||||
<tr><td>Sent</td><td>{{ data.sent }}</td></tr>
|
||||
<tr><td>Revoked</td><td>{{ data.was_revoked }}</td></tr>
|
||||
{% if data.sent == 'True' %}
|
||||
<tr><td>Auto Accept Bids</td><td>{{ data.auto_accept }}</td></tr>
|
||||
<tr><td>Auto Accept Strategy</td>
|
||||
<td>
|
||||
{% if data.automation_strat_id == -1 %}
|
||||
None
|
||||
{% else %}
|
||||
<a class="monospace" href="/automationstrategy/{{ data.automation_strat_id }}">{{ data.automation_strat_label }}</a>
|
||||
{% endif %}
|
||||
</td></tr>
|
||||
{% endif %}
|
||||
|
||||
{% if data.xmr_type == true %}
|
||||
@@ -159,12 +173,12 @@ function updateBidParams(value_changed) {
|
||||
|
||||
let amt_from = '';
|
||||
let rate = '';
|
||||
if (amt_var) {
|
||||
if (amt_var == 'True') {
|
||||
amt_from = document.getElementById('bid_amount').value;
|
||||
} else {
|
||||
amt_from = document.getElementById('amount_from').value;
|
||||
}
|
||||
if (rate_var) {
|
||||
if (rate_var == 'True') {
|
||||
rate = document.getElementById('bid_rate').value;
|
||||
} else {
|
||||
rate = document.getElementById('offer_rate').value;
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
{% for m in messages %}
|
||||
<p>{{ m }}</p>
|
||||
{% endfor %}
|
||||
{% for m in err_messages %}
|
||||
<p class="error_msg">Error: {{ m }}</p>
|
||||
{% endfor %}
|
||||
|
||||
<form method="post">
|
||||
|
||||
@@ -59,9 +62,10 @@
|
||||
<option value="100"{% if data.fee_to_extra==100 %} selected{% endif %}>100%</option>
|
||||
</select></td></tr>
|
||||
{% endif %}
|
||||
</td><td>Rate</td><td><input type="text" id="rate" name="rate" value="{{ data.rate }}" readonly></td></tr>
|
||||
<tr><td>Amount Variable</td><td colspan=3><input type="checkbox" id="amt_var" name="amt_var_" value="av" {% if data.amt_var==true %} checked="true"{% endif %} disabled></td></tr>
|
||||
<tr><td>Rate Variable</td><td colspan=3><input type="checkbox" id="rate_var" name="rate_var_" value="rv" {% if data.rate_var==true %} checked="true"{% endif %} disabled></td></tr>
|
||||
<tr><td>Minimum Bid Amount</td><td><input type="text" id="amt_bid_min" name="amt_bid_min" value="{{ data.amt_bid_min }}" title="Bids with an amount below the minimum bid value will be discarded" readonly></td></tr>
|
||||
<tr><td>Rate</td><td><input type="text" id="rate" name="rate" value="{{ data.rate }}" readonly></td></tr>
|
||||
<tr><td>Amount Variable</td><td colspan=3><input type="checkbox" id="amt_var" name="amt_var_" value="av" {% if data.amt_var==true %} checked=checked{% endif %} disabled></td></tr>
|
||||
<tr><td>Rate Variable</td><td colspan=3><input type="checkbox" id="rate_var" name="rate_var_" value="rv" {% if data.rate_var==true %} checked=checked{% endif %} disabled></td></tr>
|
||||
|
||||
<tr class="padded_row"><td>Offer valid (hrs)</td><td><input type="number" name="validhrs" min="1" max="48" value="{{ data.validhrs }}" readonly></td></tr>
|
||||
{% if data.debug_ui == true %}
|
||||
@@ -69,7 +73,13 @@
|
||||
{% else %}
|
||||
<tr><td>Contract locked (hrs)</td><td><input type="number" name="lockhrs" min="1" max="64" value="{{ data.lockhrs }}" readonly></td>{% if data.swap_style != 'xmr' %}<td colspan=2>Participate txn will be locked for half the time.</td>{% endif %}</tr>
|
||||
{% endif %}
|
||||
<tr><td>Auto Accept Bids</td><td colspan=3><input type="checkbox" id="autoaccept" name="autoaccept_" value="aa" {% if data.autoaccept==true %} checked="true"{% endif %} disabled></td></tr>
|
||||
<tr><td>Auto Accept Strategy</td><td colspan=3>
|
||||
<select name="automation_strat_id_" disabled><option value="-1"{% if data.automation_strat_id==-1 %} selected{% endif %}>-- None --</option>
|
||||
{% for a in automation_strategies %}
|
||||
<option value="{{ a[0] }}"{% if data.automation_strat_id==a[0] %} selected{% endif %}>{{ a[1] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
<input name="submit_offer" type="submit" value="Confirm Offer">
|
||||
@@ -81,8 +91,8 @@
|
||||
<input type="hidden" name="fee_from_extra" value="{{ data.fee_from_extra }}">
|
||||
<input type="hidden" name="coin_to" value="{{ data.coin_to }}">
|
||||
<input type="hidden" name="fee_to_extra" value="{{ data.fee_to_extra }}">
|
||||
{% if data.autoaccept==true %}
|
||||
<input type="hidden" name="autoaccept" value="aa">
|
||||
{% if data.automation_strat_id != -1 %}
|
||||
<input type="hidden" name="automation_strat_id" value="{{ data.automation_strat_id }}">
|
||||
{% endif %}
|
||||
{% if data.amt_var==true %}
|
||||
<input type="hidden" name="amt_var" value="av">
|
||||
@@ -91,5 +101,6 @@
|
||||
<input type="hidden" name="rate_var" value="rv">
|
||||
{% endif %}
|
||||
</form>
|
||||
<script src="static/js/new_offer.js"></script>
|
||||
|
||||
</body></html>
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
{% for m in messages %}
|
||||
<p>{{ m }}</p>
|
||||
{% endfor %}
|
||||
{% for m in err_messages %}
|
||||
<p class="error_msg">Error: {{ m }}</p>
|
||||
{% endfor %}
|
||||
|
||||
<form method="post">
|
||||
|
||||
@@ -36,14 +39,16 @@
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td><td>Amount To</td><td><input type="text" id="amt_to" name="amt_to" value="{{ data.amt_to }}" onchange="set_rate('amt_to');"></td><td>The amount you will receive.</td></tr>
|
||||
</td><td>Rate</td><td><input type="text" id="rate" name="rate" value="{{ data.rate }}" onchange="set_rate('rate');"></td><td>Lock Rate: <input type="checkbox" id="rate_lock" name="rate_lock" value="rl" checked="true"></td></tr>
|
||||
<tr><td>Minimum Bid Amount</td><td><input type="text" id="amt_bid_min" name="amt_bid_min" value="{{ data.amt_bid_min }}" title="Bids with an amount below the minimum bid value will be discarded"></td></tr>
|
||||
<tr><td>Rate</td><td><input type="text" id="rate" name="rate" value="{{ data.rate }}" onchange="set_rate('rate');"></td><td>Lock Rate: <input type="checkbox" id="rate_lock" name="rate_lock" value="rl" checked=checked></td></tr>
|
||||
|
||||
<tr><td>Amount Variable</td><td><input type="checkbox" id="amt_var" name="amt_var" value="av" {% if data.amt_var==true %} checked="true"{% endif %}></td></tr>
|
||||
<tr><td>Rate Variable</td><td><input type="checkbox" id="rate_var" name="rate_var" value="rv" {% if data.rate_var==true %} checked="true"{% endif %}></td></tr>
|
||||
<tr><td>Amount Variable</td><td><input type="checkbox" id="amt_var" name="amt_var" value="av" {% if data.amt_var==true %} checked=checked{% endif %}></td></tr>
|
||||
<tr><td>Rate Variable</td><td><input type="checkbox" id="rate_var" name="rate_var" value="rv" {% if data.rate_var==true %} checked=checked{% endif %}></td></tr>
|
||||
</table>
|
||||
|
||||
<input name="continue" type="submit" value="Continue">
|
||||
<input name="check_rates" type="button" value="Lookup Rates" onclick='lookup_rates();'>
|
||||
<input name="show_rates_table" type="button" value="Show Rates Table" onclick='lookup_rates_table();'>
|
||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||
<input type="hidden" name="step1" value="a">
|
||||
</form>
|
||||
@@ -79,6 +84,43 @@ xhr_rate.onload = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const xhr_rates_table = new XMLHttpRequest();
|
||||
xhr_rates_table.onload = () => {
|
||||
if (xhr_rates_table.status == 200) {
|
||||
const list = JSON.parse(xhr_rates_table.response);
|
||||
|
||||
headings = ['Source', 'Coin From', 'Coin To', 'Coin From USD Rate', 'Coin To USD Rate', 'Coin From BTC Rate', 'Coin To BTC Rate', 'Relative Rate'];
|
||||
table = document.createElement('table');
|
||||
headings_row = document.createElement('tr');
|
||||
for (let i = 0; i < headings.length; i++) {
|
||||
column = document.createElement('th');
|
||||
column.textContent = headings[i];
|
||||
headings_row.appendChild(column);
|
||||
}
|
||||
table.appendChild(headings_row);
|
||||
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
data_row = document.createElement('tr');
|
||||
for (let j = 0; j < list[i].length; j++) {
|
||||
column = document.createElement('td');
|
||||
column.textContent = list[i][j];
|
||||
data_row.appendChild(column);
|
||||
}
|
||||
table.appendChild(data_row);
|
||||
}
|
||||
// Clear existing
|
||||
const display_node = document.getElementById("rates_display");
|
||||
while (display_node.lastElementChild) {
|
||||
display_node.removeChild(display_node.lastElementChild);
|
||||
}
|
||||
|
||||
heading = document.createElement('h4');
|
||||
heading.textContent = 'Rates'
|
||||
display_node.appendChild(heading);
|
||||
display_node.appendChild(table);
|
||||
}
|
||||
}
|
||||
|
||||
function lookup_rates() {
|
||||
const coin_from = document.getElementById('coin_from').value;
|
||||
const coin_to = document.getElementById('coin_to').value;
|
||||
@@ -96,6 +138,22 @@ function lookup_rates() {
|
||||
xhr_rates.send('coin_from='+coin_from+'&coin_to='+coin_to);
|
||||
}
|
||||
|
||||
function lookup_rates_table() {
|
||||
const coin_from = document.getElementById('coin_from').value;
|
||||
const coin_to = document.getElementById('coin_to').value;
|
||||
|
||||
if (coin_from == '-1' || coin_to == '-1') {
|
||||
alert('Coins from and to must be set first.');
|
||||
return;
|
||||
}
|
||||
|
||||
inner_html = '<h4>Rates</h4><p>Updating...</p>';
|
||||
document.getElementById('rates_display').innerHTML = inner_html;
|
||||
|
||||
xhr_rates_table.open('GET', '/json/rateslist?from='+coin_from+'&to='+coin_to);
|
||||
xhr_rates_table.send();
|
||||
}
|
||||
|
||||
function set_rate(value_changed) {
|
||||
const coin_from = document.getElementById('coin_from').value;
|
||||
const coin_to = document.getElementById('coin_to').value;
|
||||
@@ -131,6 +189,6 @@ function set_rate(value_changed) {
|
||||
xhr_rate.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
||||
xhr_rate.send(params);
|
||||
}
|
||||
|
||||
</script>
|
||||
<script src="static/js/new_offer.js"></script>
|
||||
</body></html>
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
{% for m in messages %}
|
||||
<p>{{ m }}</p>
|
||||
{% endfor %}
|
||||
{% for m in err_messages %}
|
||||
<p class="error_msg">Error: {{ m }}</p>
|
||||
{% endfor %}
|
||||
|
||||
<form method="post">
|
||||
|
||||
@@ -56,9 +59,10 @@
|
||||
<option value="100"{% if data.fee_to_extra==100 %} selected{% endif %}>100%</option>
|
||||
</select></td></tr>
|
||||
{% endif %}
|
||||
</td><td>Rate</td><td><input type="text" id="rate" name="rate" value="{{ data.rate }}" readonly></td></tr>
|
||||
<tr><td>Amount Variable</td><td colspan=3><input type="checkbox" id="amt_var" name="amt_var_" value="av" {% if data.amt_var==true %} checked="true"{% endif %} disabled></td></tr>
|
||||
<tr><td>Rate Variable</td><td colspan=3><input type="checkbox" id="rate_var" name="rate_var_" value="rv" {% if data.rate_var==true %} checked="true"{% endif %} disabled></td></tr>
|
||||
<tr><td>Minimum Bid Amount</td><td><input type="text" id="amt_bid_min" name="amt_bid_min" value="{{ data.amt_bid_min }}" title="Bids with an amount below the minimum bid value will be discarded" readonly></td></tr>
|
||||
<tr><td>Rate</td><td><input type="text" id="rate" name="rate" value="{{ data.rate }}" readonly></td></tr>
|
||||
<tr><td>Amount Variable</td><td colspan=3><input type="checkbox" id="amt_var" name="amt_var_" value="av" {% if data.amt_var==true %} checked=checked{% endif %} disabled></td></tr>
|
||||
<tr><td>Rate Variable</td><td colspan=3><input type="checkbox" id="rate_var" name="rate_var_" value="rv" {% if data.rate_var==true %} checked=checked{% endif %} disabled></td></tr>
|
||||
|
||||
|
||||
<tr class="padded_row"><td>Offer valid (hrs)</td><td><input type="number" name="validhrs" min="1" max="48" value="{{ data.validhrs }}"></td></tr>
|
||||
@@ -67,7 +71,13 @@
|
||||
{% else %}
|
||||
<tr><td>Contract locked (hrs)</td><td><input type="number" name="lockhrs" min="1" max="96" value="{{ data.lockhrs }}"></td>{% if data.swap_style != 'xmr' %}<td colspan=2>Participate txn will be locked for half the time.</td>{% endif %}</tr>
|
||||
{% endif %}
|
||||
<tr><td>Auto Accept Bids</td><td colspan=3><input type="checkbox" id="autoaccept" name="autoaccept" value="aa" {% if data.autoaccept==true %} checked="true"{% endif %}></td></tr>
|
||||
<tr><td>Auto Accept Strategy</td><td colspan=3>
|
||||
<select name="automation_strat_id"><option value="-1"{% if data.automation_strat_id==-1 %} selected{% endif %}>-- None --</option>
|
||||
{% for a in automation_strategies %}
|
||||
<option value="{{ a[0] }}"{% if data.automation_strat_id==a[0] %} selected{% endif %}>{{ a[1] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td></tr>
|
||||
|
||||
</table>
|
||||
|
||||
@@ -86,4 +96,5 @@
|
||||
{% endif %}
|
||||
</form>
|
||||
|
||||
<script src="static/js/new_offer.js"></script>
|
||||
</body></html>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% include 'header.html' %}
|
||||
|
||||
<h3>{{ page_type }} Offers</h3>
|
||||
<h3>Network Offers</h3>
|
||||
{% if refresh %}
|
||||
<p>Page Refresh: {{ refresh }} seconds</p>
|
||||
{% endif %}
|
||||
@@ -35,6 +35,12 @@
|
||||
<option value="desc"{% if filters.sort_dir=='desc' %} selected{% endif %}>Descending</option>
|
||||
</select>
|
||||
</td></tr>
|
||||
<tr><td>Sent From Node</td><td>
|
||||
<select name="sent_from">
|
||||
<option value="any"{% if filters.sent_from=='any' %} selected{% endif %}>Any</option>
|
||||
<option value="only"{% if filters.sent_from=='only' %} selected{% endif %}>Only</option>
|
||||
</select>
|
||||
</td></tr>
|
||||
|
||||
<tr><td><input type="submit" name='applyfilters' value="Apply Filters"></td><td><input type="submit" name='clearfilters' value="Clear Filters"></td></tr>
|
||||
<tr><td><input type="submit" name='pageback' value="Page Back"></td><td>Page: {{ filters.page_no }}</td><td><input type="submit" name='pageforwards' value="Page Forwards"></td></tr>
|
||||
@@ -45,9 +51,9 @@
|
||||
|
||||
|
||||
<table>
|
||||
<tr><th>At</th><th>Recipient</th><th>Offer ID</th><th>Coin From</th><th>Coin To</th><th>Amount From</th><th>Amount To</th><th>Rate</th></tr>
|
||||
<tr><th>At</th><th>From</th><th>Recipient</th><th>Offer ID</th><th>Coin From</th><th>Coin To</th><th>Amount From</th><th>Amount To</th><th>Rate</th><th>Amount From Swapped</th></tr>
|
||||
{% for o in offers %}
|
||||
<tr><td>{{ o[0] }}</td><td>{{ o[7] }}</td><td><a class="monospace" href=/offer/{{ o[1] }}>{{ o[1] }}</a></td><td>{{ o[2] }}</td><td>{{ o[3] }}</td><td>{{ o[4] }}</td><td>{{ o[5] }}</td><td>{{ o[6] }}</td></tr>
|
||||
<tr><td>{{ o[0] }}</td><td class="monospace">{{ o[8]|truncate(12, True) }}{% if o[9]==true %} <b>Sent</b>{% endif %}</td><td class="monospace">{{ o[7] }}</td><td><a class="monospace" href=/offer/{{ o[1] }}>{{ o[1] }}</a></td><td>{{ o[2] }}</td><td>{{ o[3] }}</td><td>{{ o[4] }}</td><td>{{ o[5] }}</td><td>{{ o[6] }}</td><td>{{ o[10] }}</td></tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
|
||||
@@ -59,6 +59,9 @@ node.xmr.to:18081<br/>
|
||||
{% else %}
|
||||
<tr><td>Blocks Confirmed Target</td><td><input type="number" name="conf_target_{{ c.name }}" min="1" max="32" value="{{ c.conf_target }}"></td></tr>
|
||||
{% endif %}
|
||||
{% if c.name == 'particl' %}
|
||||
<tr><td>Anon Tx Ring Size</td><td><input type="number" name="rct_ring_size_{{ c.name }}" min="3" max="32" value="{{ c.anon_tx_ring_size }}"></td></tr>
|
||||
{% endif %}
|
||||
<tr><td><input type="submit" name="apply_{{ c.name }}" value="Apply">
|
||||
{% if c.can_disable == true %}
|
||||
<input type="submit" name="disable_{{ c.name }}" value="Disable" onclick="return confirmPopup('Disable', '{{ c.name|capitalize }}');">
|
||||
|
||||
@@ -34,8 +34,11 @@
|
||||
{% endif %}
|
||||
|
||||
|
||||
<tr><td>Blocks:</td><td>{{ w.blocks }}</td></tr>
|
||||
<tr><td>Blocks:</td><td>{{ w.blocks }} {% if w.known_block_count %} / {{ w.known_block_count }} {% endif %}</td></tr>
|
||||
<tr><td>Synced:</td><td>{{ w.synced }}</td></tr>
|
||||
{% if w.bootstrapping %}
|
||||
<tr><td>Bootstrapping:</td><td>{{ w.bootstrapping }}</td></tr>
|
||||
{% endif %}
|
||||
<tr><td>Expected Seed:</td><td>{{ w.expected_seed }}</td>{% if w.expected_seed != true %}<td><input type="submit" name="reseed_{{ w.cid }}" value="Reseed wallet" onclick="return confirmReseed();"></td>{% endif %}</tr>
|
||||
{% if w.cid == '1' %}
|
||||
<tr><td>Stealth Address</td><td colspan=2>{{ w.stealth_address }}</td></tr>
|
||||
@@ -46,7 +49,7 @@
|
||||
{% else %}
|
||||
<tr><td><input type="submit" name="newaddr_{{ w.cid }}" value="New Deposit Address"></td><td colspan=2>{{ w.deposit_address }}</td></tr>
|
||||
{% endif %}
|
||||
<tr><td><input type="submit" name="withdraw_{{ w.cid }}" value="Withdraw" onclick="return confirmWithdrawal();"></td><td>Amount: <input type="text" name="amt_{{ w.cid }}" value="{{ w.wd_value }}"></td><td>Address: <input type="text" name="to_{{ w.cid }}" value="{{ w.wd_address }}"></td><td>Subtract fee: <input type="checkbox" name="subfee_{{ w.cid }}" {% if w.wd_subfee==true %} checked="true"{% endif %}></td></tr>
|
||||
<tr><td><input type="submit" name="withdraw_{{ w.cid }}" value="Withdraw" onclick="return confirmWithdrawal();"></td><td>Amount: <input type="text" name="amt_{{ w.cid }}" value="{{ w.wd_value }}"></td><td>Address: <input type="text" name="to_{{ w.cid }}" value="{{ w.wd_address }}"></td><td>Subtract fee: <input type="checkbox" name="subfee_{{ w.cid }}" {% if w.wd_subfee==true %} checked=checked{% endif %}></td></tr>
|
||||
{% if w.cid == '1' %}
|
||||
<tr><td>Type From, To</td><td>
|
||||
<select name="withdraw_type_from_{{ w.cid }}">
|
||||
|
||||
@@ -33,8 +33,11 @@
|
||||
{% endif %}
|
||||
|
||||
|
||||
<tr><td>Blocks:</td><td>{{ w.blocks }}</td></tr>
|
||||
<tr><td>Blocks:</td><td>{{ w.blocks }} {% if w.known_block_count %} / {{ w.known_block_count }} {% endif %}</td></tr>
|
||||
<tr><td>Synced:</td><td>{{ w.synced }}</td></tr>
|
||||
{% if w.bootstrapping %}
|
||||
<tr><td>Bootstrapping:</td><td>{{ w.bootstrapping }}</td></tr>
|
||||
{% endif %}
|
||||
<tr><td>Expected Seed:</td><td>{{ w.expected_seed }}</td></tr>
|
||||
<tr><td><a href="/wallet/{{ w.ticker }}">Manage</a></td></tr>
|
||||
</table>
|
||||
|
||||
99
basicswap/ui/page_automation.py
Normal file
99
basicswap/ui/page_automation.py
Normal file
@@ -0,0 +1,99 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2022 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from .util import (
|
||||
PAGE_LIMIT,
|
||||
get_data_entry,
|
||||
have_data_entry,
|
||||
set_pagination_filters,
|
||||
)
|
||||
from basicswap.util import (
|
||||
ensure,
|
||||
)
|
||||
from basicswap.db import (
|
||||
strConcepts,
|
||||
)
|
||||
|
||||
|
||||
def page_automation_strategies(self, url_split, post_string):
|
||||
server = self.server
|
||||
swap_client = server.swap_client
|
||||
|
||||
filters = {
|
||||
'page_no': 1,
|
||||
'limit': PAGE_LIMIT,
|
||||
'sort_by': 'created_at',
|
||||
'sort_dir': 'desc',
|
||||
}
|
||||
|
||||
messages = []
|
||||
form_data = self.checkForm(post_string, 'automationstrategies', messages)
|
||||
|
||||
if form_data and have_data_entry(form_data, 'applyfilters'):
|
||||
if have_data_entry(form_data, 'sort_by'):
|
||||
sort_by = get_data_entry(form_data, 'sort_by')
|
||||
ensure(sort_by in ['created_at', 'rate'], 'Invalid sort by')
|
||||
filters['sort_by'] = sort_by
|
||||
if have_data_entry(form_data, 'sort_dir'):
|
||||
sort_dir = get_data_entry(form_data, 'sort_dir')
|
||||
ensure(sort_dir in ['asc', 'desc'], 'Invalid sort dir')
|
||||
filters['sort_dir'] = sort_dir
|
||||
|
||||
set_pagination_filters(form_data, filters)
|
||||
|
||||
formatted_strategies = []
|
||||
for s in swap_client.listAutomationStrategies(filters):
|
||||
formatted_strategies.append((s[0], s[1], strConcepts(s[2])))
|
||||
|
||||
template = server.env.get_template('automation_strategies.html')
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
'filters': filters,
|
||||
'strategies': formatted_strategies,
|
||||
})
|
||||
|
||||
|
||||
def page_automation_strategy_new(self, url_split, post_string):
|
||||
server = self.server
|
||||
swap_client = self.server.swap_client
|
||||
|
||||
messages = []
|
||||
form_data = self.checkForm(post_string, 'automationstrategynew', messages)
|
||||
|
||||
template = server.env.get_template('automation_strategy_new.html')
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
})
|
||||
|
||||
|
||||
def page_automation_strategy(self, url_split, post_string):
|
||||
ensure(len(url_split) > 2, 'Strategy ID not specified')
|
||||
try:
|
||||
strategy_id = int(url_split[2])
|
||||
except Exception:
|
||||
raise ValueError('Bad strategy ID')
|
||||
|
||||
server = self.server
|
||||
swap_client = self.server.swap_client
|
||||
|
||||
messages = []
|
||||
|
||||
strategy = swap_client.getAutomationStrategy(strategy_id)
|
||||
|
||||
formatted_strategy = {
|
||||
'label': strategy.label,
|
||||
'type': strConcepts(strategy.type_ind),
|
||||
'only_known_identities': 'True' if strategy.only_known_identities is True else 'False',
|
||||
'data': strategy.data,
|
||||
'note': strategy.note,
|
||||
'created_at': strategy.created_at,
|
||||
}
|
||||
|
||||
template = server.env.get_template('automation_strategy.html')
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
'strategy': formatted_strategy,
|
||||
})
|
||||
562
basicswap/ui/page_offers.py
Normal file
562
basicswap/ui/page_offers.py
Normal file
@@ -0,0 +1,562 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2022 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
import traceback
|
||||
|
||||
from .util import (
|
||||
PAGE_LIMIT,
|
||||
getCoinType,
|
||||
inputAmount,
|
||||
setCoinFilter,
|
||||
get_data_entry,
|
||||
have_data_entry,
|
||||
get_data_entry_or,
|
||||
listAvailableCoins,
|
||||
set_pagination_filters,
|
||||
)
|
||||
from basicswap.db import (
|
||||
Concepts,
|
||||
)
|
||||
from basicswap.util import (
|
||||
ensure,
|
||||
format_amount,
|
||||
format_timestamp,
|
||||
)
|
||||
from basicswap.basicswap_util import (
|
||||
SwapTypes,
|
||||
DebugTypes,
|
||||
getLockName,
|
||||
strBidState,
|
||||
TxLockTypes,
|
||||
strOfferState,
|
||||
|
||||
)
|
||||
from basicswap.chainparams import (
|
||||
Coins,
|
||||
)
|
||||
|
||||
|
||||
def value_or_none(v):
|
||||
if v == -1 or v == '-1':
|
||||
return None
|
||||
return v
|
||||
|
||||
|
||||
def parseOfferFormData(swap_client, form_data, page_data, options={}):
|
||||
errors = []
|
||||
parsed_data = {}
|
||||
|
||||
if have_data_entry(form_data, 'addr_to'):
|
||||
page_data['addr_to'] = get_data_entry(form_data, 'addr_to')
|
||||
addr_to = value_or_none(page_data['addr_to'])
|
||||
if addr_to is not None:
|
||||
parsed_data['addr_to'] = addr_to
|
||||
|
||||
if have_data_entry(form_data, 'addr_from'):
|
||||
page_data['addr_from'] = get_data_entry(form_data, 'addr_from')
|
||||
parsed_data['addr_from'] = value_or_none(page_data['addr_from'])
|
||||
else:
|
||||
parsed_data['addr_from'] = None
|
||||
|
||||
try:
|
||||
page_data['coin_from'] = getCoinType(get_data_entry(form_data, 'coin_from'))
|
||||
coin_from = Coins(page_data['coin_from'])
|
||||
ci_from = swap_client.ci(coin_from)
|
||||
if coin_from != Coins.XMR:
|
||||
page_data['fee_from_conf'] = ci_from._conf_target # Set default value
|
||||
parsed_data['coin_from'] = coin_from
|
||||
except Exception:
|
||||
errors.append('Unknown Coin From')
|
||||
|
||||
try:
|
||||
page_data['coin_to'] = getCoinType(get_data_entry(form_data, 'coin_to'))
|
||||
coin_to = Coins(page_data['coin_to'])
|
||||
ci_to = swap_client.ci(coin_to)
|
||||
if coin_to != Coins.XMR:
|
||||
page_data['fee_to_conf'] = ci_to._conf_target # Set default value
|
||||
parsed_data['coin_to'] = coin_to
|
||||
except Exception:
|
||||
errors.append('Unknown Coin To')
|
||||
|
||||
if parsed_data['coin_to'] in (Coins.XMR, Coins.PART_ANON):
|
||||
page_data['swap_style'] = 'xmr'
|
||||
else:
|
||||
page_data['swap_style'] = 'atomic'
|
||||
|
||||
try:
|
||||
page_data['amt_from'] = get_data_entry(form_data, 'amt_from')
|
||||
parsed_data['amt_from'] = inputAmount(page_data['amt_from'], ci_from)
|
||||
except Exception:
|
||||
errors.append('Amount From')
|
||||
|
||||
try:
|
||||
if 'amt_bid_min' not in page_data:
|
||||
if options.get('add_min_bid_amt', False) is True:
|
||||
parsed_data['amt_bid_min'] = ci_from.chainparams_network()['min_amount']
|
||||
else:
|
||||
raise ValueError('missing')
|
||||
else:
|
||||
page_data['amt_bid_min'] = get_data_entry(form_data, 'amt_bid_min')
|
||||
parsed_data['amt_bid_min'] = inputAmount(page_data['amt_bid_min'], ci_from)
|
||||
|
||||
if parsed_data['amt_bid_min'] < 0 or parsed_data['amt_bid_min'] > parsed_data['amt_from']:
|
||||
errors.append('Minimum Bid Amount out of range')
|
||||
except Exception:
|
||||
errors.append('Minimum Bid Amount')
|
||||
|
||||
try:
|
||||
page_data['amt_to'] = get_data_entry(form_data, 'amt_to')
|
||||
parsed_data['amt_to'] = inputAmount(page_data['amt_to'], ci_to)
|
||||
except Exception:
|
||||
errors.append('Amount To')
|
||||
|
||||
if 'amt_to' in parsed_data and 'amt_from' in parsed_data:
|
||||
parsed_data['rate'] = ci_from.make_int(parsed_data['amt_to'] / parsed_data['amt_from'], r=1)
|
||||
page_data['rate'] = ci_to.format_amount(parsed_data['rate'])
|
||||
|
||||
page_data['amt_var'] = True if have_data_entry(form_data, 'amt_var') else False
|
||||
parsed_data['amt_var'] = page_data['amt_var']
|
||||
page_data['rate_var'] = True if have_data_entry(form_data, 'rate_var') else False
|
||||
parsed_data['rate_var'] = page_data['rate_var']
|
||||
|
||||
if have_data_entry(form_data, 'step1'):
|
||||
if len(errors) == 0 and have_data_entry(form_data, 'continue'):
|
||||
page_data['step2'] = True
|
||||
return parsed_data, errors
|
||||
|
||||
page_data['step2'] = True
|
||||
|
||||
if have_data_entry(form_data, 'fee_from_conf'):
|
||||
page_data['fee_from_conf'] = int(get_data_entry(form_data, 'fee_from_conf'))
|
||||
parsed_data['fee_from_conf'] = page_data['fee_from_conf']
|
||||
|
||||
if have_data_entry(form_data, 'fee_from_extra'):
|
||||
page_data['fee_from_extra'] = int(get_data_entry(form_data, 'fee_from_extra'))
|
||||
parsed_data['fee_from_extra'] = page_data['fee_from_extra']
|
||||
|
||||
if have_data_entry(form_data, 'fee_to_conf'):
|
||||
page_data['fee_to_conf'] = int(get_data_entry(form_data, 'fee_to_conf'))
|
||||
parsed_data['fee_to_conf'] = page_data['fee_to_conf']
|
||||
|
||||
if have_data_entry(form_data, 'fee_to_extra'):
|
||||
page_data['fee_to_extra'] = int(get_data_entry(form_data, 'fee_to_extra'))
|
||||
parsed_data['fee_to_extra'] = page_data['fee_to_extra']
|
||||
|
||||
if have_data_entry(form_data, 'check_offer'):
|
||||
page_data['check_offer'] = True
|
||||
if have_data_entry(form_data, 'submit_offer'):
|
||||
page_data['submit_offer'] = True
|
||||
|
||||
if have_data_entry(form_data, 'lockhrs'):
|
||||
page_data['lockhrs'] = int(get_data_entry(form_data, 'lockhrs'))
|
||||
parsed_data['lock_seconds'] = page_data['lockhrs'] * 60 * 60
|
||||
elif have_data_entry(form_data, 'lockmins'):
|
||||
page_data['lockmins'] = int(get_data_entry(form_data, 'lockmins'))
|
||||
parsed_data['lock_seconds'] = page_data['lockmins'] * 60
|
||||
elif have_data_entry(form_data, 'lockseconds'):
|
||||
parsed_data['lock_seconds'] = int(get_data_entry(form_data, 'lockseconds'))
|
||||
|
||||
if have_data_entry(form_data, 'validhrs'):
|
||||
page_data['validhrs'] = int(get_data_entry(form_data, 'validhrs'))
|
||||
parsed_data['valid_for_seconds'] = page_data['validhrs'] * 60 * 60
|
||||
elif have_data_entry(form_data, 'valid_for_seconds'):
|
||||
parsed_data['valid_for_seconds'] = int(get_data_entry(form_data, 'valid_for_seconds'))
|
||||
|
||||
page_data['automation_strat_id'] = int(get_data_entry_or(form_data, 'automation_strat_id', -1))
|
||||
parsed_data['automation_strat_id'] = page_data['automation_strat_id']
|
||||
|
||||
try:
|
||||
if len(errors) == 0 and page_data['swap_style'] == 'xmr':
|
||||
if have_data_entry(form_data, 'fee_rate_from'):
|
||||
page_data['from_fee_override'] = get_data_entry(form_data, 'fee_rate_from')
|
||||
parsed_data['from_fee_override'] = page_data['from_fee_override']
|
||||
else:
|
||||
from_fee_override, page_data['from_fee_src'] = swap_client.getFeeRateForCoin(parsed_data['coin_from'], page_data['fee_from_conf'])
|
||||
if page_data['fee_from_extra'] > 0:
|
||||
from_fee_override += from_fee_override * (float(page_data['fee_from_extra']) / 100.0)
|
||||
page_data['from_fee_override'] = ci_from.format_amount(ci_from.make_int(from_fee_override, r=1))
|
||||
parsed_data['from_fee_override'] = page_data['from_fee_override']
|
||||
|
||||
lock_spend_tx_vsize = ci_from.xmr_swap_alock_spend_tx_vsize()
|
||||
lock_spend_tx_fee = ci_from.make_int(ci_from.make_int(from_fee_override, r=1) * lock_spend_tx_vsize / 1000, r=1)
|
||||
page_data['amt_from_lock_spend_tx_fee'] = ci_from.format_amount(lock_spend_tx_fee // ci_from.COIN())
|
||||
page_data['tla_from'] = ci_from.ticker()
|
||||
|
||||
if coin_to == Coins.XMR:
|
||||
if have_data_entry(form_data, 'fee_rate_to'):
|
||||
page_data['to_fee_override'] = get_data_entry(form_data, 'fee_rate_to')
|
||||
parsed_data['to_fee_override'] = page_data['to_fee_override']
|
||||
else:
|
||||
to_fee_override, page_data['to_fee_src'] = swap_client.getFeeRateForCoin(parsed_data['coin_to'], page_data['fee_to_conf'])
|
||||
if page_data['fee_to_extra'] > 0:
|
||||
to_fee_override += to_fee_override * (float(page_data['fee_to_extra']) / 100.0)
|
||||
page_data['to_fee_override'] = ci_to.format_amount(ci_to.make_int(to_fee_override, r=1))
|
||||
parsed_data['to_fee_override'] = page_data['to_fee_override']
|
||||
except Exception as e:
|
||||
print('Error setting fee', str(e)) # Expected if missing fields
|
||||
|
||||
return parsed_data, errors
|
||||
|
||||
|
||||
def postNewOfferFromParsed(swap_client, parsed_data):
|
||||
swap_type = SwapTypes.SELLER_FIRST
|
||||
if parsed_data['coin_to'] in (Coins.XMR, Coins.PART_ANON):
|
||||
swap_type = SwapTypes.XMR_SWAP
|
||||
|
||||
if swap_client.coin_clients[parsed_data['coin_from']]['use_csv'] and swap_client.coin_clients[parsed_data['coin_to']]['use_csv']:
|
||||
lock_type = TxLockTypes.SEQUENCE_LOCK_TIME
|
||||
else:
|
||||
lock_type = TxLockTypes.ABS_LOCK_TIME
|
||||
|
||||
extra_options = {}
|
||||
|
||||
if 'fee_from_conf' in parsed_data:
|
||||
extra_options['from_fee_conf_target'] = parsed_data['fee_from_conf']
|
||||
if 'from_fee_multiplier_percent' in parsed_data:
|
||||
extra_options['from_fee_multiplier_percent'] = parsed_data['fee_from_extra']
|
||||
if 'from_fee_override' in parsed_data:
|
||||
extra_options['from_fee_override'] = parsed_data['from_fee_override']
|
||||
|
||||
if 'fee_to_conf' in parsed_data:
|
||||
extra_options['to_fee_conf_target'] = parsed_data['fee_to_conf']
|
||||
if 'to_fee_multiplier_percent' in parsed_data:
|
||||
extra_options['to_fee_multiplier_percent'] = parsed_data['fee_to_extra']
|
||||
if 'to_fee_override' in parsed_data:
|
||||
extra_options['to_fee_override'] = parsed_data['to_fee_override']
|
||||
if 'valid_for_seconds' in parsed_data:
|
||||
extra_options['valid_for_seconds'] = parsed_data['valid_for_seconds']
|
||||
|
||||
if 'addr_to' in parsed_data:
|
||||
extra_options['addr_send_to'] = parsed_data['addr_to']
|
||||
|
||||
if parsed_data.get('amt_var', False):
|
||||
extra_options['amount_negotiable'] = parsed_data['amt_var']
|
||||
if parsed_data.get('rate_var', False):
|
||||
extra_options['rate_negotiable'] = parsed_data['rate_var']
|
||||
|
||||
if parsed_data.get('rate_var', None) is not None:
|
||||
extra_options['rate_negotiable'] = parsed_data['rate_var']
|
||||
|
||||
if parsed_data.get('automation_strat_id', None) is not None:
|
||||
extra_options['automation_id'] = parsed_data['automation_strat_id']
|
||||
|
||||
offer_id = swap_client.postOffer(
|
||||
parsed_data['coin_from'],
|
||||
parsed_data['coin_to'],
|
||||
parsed_data['amt_from'],
|
||||
parsed_data['rate'],
|
||||
parsed_data['amt_bid_min'],
|
||||
swap_type,
|
||||
lock_type=lock_type,
|
||||
lock_value=parsed_data['lock_seconds'],
|
||||
addr_send_from=parsed_data['addr_from'],
|
||||
extra_options=extra_options)
|
||||
return offer_id
|
||||
|
||||
|
||||
def postNewOffer(swap_client, form_data):
|
||||
page_data = {}
|
||||
parsed_data, errors = parseOfferFormData(swap_client, form_data, page_data, options={'add_min_bid_amt': True})
|
||||
if len(errors) > 0:
|
||||
raise ValueError('Parse errors: ' + ' '.join(errors))
|
||||
return postNewOfferFromParsed(swap_client, parsed_data)
|
||||
|
||||
|
||||
def page_newoffer(self, url_split, post_string):
|
||||
server = self.server
|
||||
swap_client = server.swap_client
|
||||
|
||||
messages = []
|
||||
err_messages = []
|
||||
page_data = {
|
||||
# Set defaults
|
||||
'addr_to': -1,
|
||||
'fee_from_conf': 2,
|
||||
'fee_to_conf': 2,
|
||||
'validhrs': 1,
|
||||
'lockhrs': 32,
|
||||
'lockmins': 30, # used in debug mode
|
||||
'debug_ui': swap_client.debug_ui,
|
||||
'automation_strat_id': -1,
|
||||
'amt_bid_min': format_amount(1000, 8),
|
||||
}
|
||||
form_data = self.checkForm(post_string, 'newoffer', err_messages)
|
||||
|
||||
if form_data:
|
||||
try:
|
||||
parsed_data, errors = parseOfferFormData(swap_client, form_data, page_data)
|
||||
for e in errors:
|
||||
err_messages.append(str(e))
|
||||
except Exception as e:
|
||||
if swap_client.debug is True:
|
||||
swap_client.log.error(traceback.format_exc())
|
||||
err_messages.append(str(e))
|
||||
|
||||
if len(err_messages) == 0 and 'submit_offer' in page_data:
|
||||
try:
|
||||
offer_id = postNewOfferFromParsed(swap_client, parsed_data)
|
||||
messages.append('<a href="/offer/' + offer_id.hex() + '">Sent Offer {}</a>'.format(offer_id.hex()))
|
||||
page_data = {}
|
||||
except Exception as e:
|
||||
if swap_client.debug is True:
|
||||
swap_client.log.error(traceback.format_exc())
|
||||
err_messages.append(str(e))
|
||||
|
||||
if len(err_messages) == 0 and 'check_offer' in page_data:
|
||||
template = server.env.get_template('offer_confirm.html')
|
||||
elif 'step2' in page_data:
|
||||
template = server.env.get_template('offer_new_2.html')
|
||||
else:
|
||||
template = server.env.get_template('offer_new_1.html')
|
||||
|
||||
if swap_client.debug_ui:
|
||||
messages.append('Debug mode active.')
|
||||
|
||||
coins_from, coins_to = listAvailableCoins(swap_client, split_from=True)
|
||||
|
||||
automation_filters = {}
|
||||
automation_filters['sort_by'] = 'label'
|
||||
automation_filters['type_ind'] = Concepts.OFFER
|
||||
automation_strategies = swap_client.listAutomationStrategies(automation_filters)
|
||||
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
'err_messages': err_messages,
|
||||
'coins_from': coins_from,
|
||||
'coins': coins_to,
|
||||
'addrs': swap_client.listSmsgAddresses('offer_send_from'),
|
||||
'addrs_to': swap_client.listSmsgAddresses('offer_send_to'),
|
||||
'data': page_data,
|
||||
'automation_strategies': automation_strategies,
|
||||
})
|
||||
|
||||
|
||||
def page_offer(self, url_split, post_string):
|
||||
ensure(len(url_split) > 2, 'Offer ID not specified')
|
||||
try:
|
||||
offer_id = bytes.fromhex(url_split[2])
|
||||
ensure(len(offer_id) == 28, 'Bad offer ID')
|
||||
except Exception:
|
||||
raise ValueError('Bad offer ID')
|
||||
server = self.server
|
||||
swap_client = server.swap_client
|
||||
offer, xmr_offer = swap_client.getXmrOffer(offer_id)
|
||||
ensure(offer, 'Unknown offer ID')
|
||||
|
||||
extend_data = { # Defaults
|
||||
'nb_validmins': 10,
|
||||
}
|
||||
messages = []
|
||||
if swap_client.debug_ui:
|
||||
messages.append('Debug mode active.')
|
||||
sent_bid_id = None
|
||||
show_bid_form = None
|
||||
form_data = self.checkForm(post_string, 'offer', messages)
|
||||
|
||||
ci_from = swap_client.ci(Coins(offer.coin_from))
|
||||
ci_to = swap_client.ci(Coins(offer.coin_to))
|
||||
debugind = -1
|
||||
|
||||
# Set defaults
|
||||
bid_amount = ci_from.format_amount(offer.amount_from)
|
||||
bid_rate = ci_to.format_amount(offer.rate)
|
||||
|
||||
if form_data:
|
||||
if b'revoke_offer' in form_data:
|
||||
try:
|
||||
swap_client.revokeOffer(offer_id)
|
||||
messages.append('Offer revoked')
|
||||
except Exception as ex:
|
||||
messages.append('Revoke offer failed: ' + str(ex))
|
||||
elif b'newbid' in form_data:
|
||||
show_bid_form = True
|
||||
elif b'sendbid' in form_data:
|
||||
try:
|
||||
addr_from = form_data[b'addr_from'][0].decode('utf-8')
|
||||
extend_data['nb_addr_from'] = addr_from
|
||||
if addr_from == '-1':
|
||||
addr_from = None
|
||||
|
||||
minutes_valid = int(form_data[b'validmins'][0].decode('utf-8'))
|
||||
extend_data['nb_validmins'] = minutes_valid
|
||||
|
||||
extra_options = {
|
||||
'valid_for_seconds': minutes_valid * 60,
|
||||
}
|
||||
if have_data_entry(form_data, 'bid_rate'):
|
||||
bid_rate = get_data_entry(form_data, 'bid_rate')
|
||||
extra_options['bid_rate'] = ci_to.make_int(bid_rate, r=1)
|
||||
|
||||
if have_data_entry(form_data, 'bid_amount'):
|
||||
bid_amount = get_data_entry(form_data, 'bid_amount')
|
||||
amount_from = inputAmount(bid_amount, ci_from)
|
||||
else:
|
||||
amount_from = offer.amount_from
|
||||
debugind = int(get_data_entry_or(form_data, 'debugind', -1))
|
||||
|
||||
sent_bid_id = swap_client.postBid(offer_id, amount_from, addr_send_from=addr_from, extra_options=extra_options).hex()
|
||||
|
||||
if debugind > -1:
|
||||
swap_client.setBidDebugInd(bytes.fromhex(sent_bid_id), debugind)
|
||||
except Exception as ex:
|
||||
if self.server.swap_client.debug is True:
|
||||
self.server.swap_client.log.error(traceback.format_exc())
|
||||
messages.append('Error: Send bid failed: ' + str(ex))
|
||||
show_bid_form = True
|
||||
|
||||
data = {
|
||||
'tla_from': ci_from.ticker(),
|
||||
'tla_to': ci_to.ticker(),
|
||||
'state': strOfferState(offer.state),
|
||||
'coin_from': ci_from.coin_name(),
|
||||
'coin_to': ci_to.coin_name(),
|
||||
'coin_from_ind': int(ci_from.coin_type()),
|
||||
'coin_to_ind': int(ci_to.coin_type()),
|
||||
'amt_from': ci_from.format_amount(offer.amount_from),
|
||||
'amt_to': ci_to.format_amount((offer.amount_from * offer.rate) // ci_from.COIN()),
|
||||
'amt_bid_min': ci_from.format_amount(offer.min_bid_amount),
|
||||
'rate': ci_to.format_amount(offer.rate),
|
||||
'lock_type': getLockName(offer.lock_type),
|
||||
'lock_value': offer.lock_value,
|
||||
'addr_from': offer.addr_from,
|
||||
'addr_to': 'Public' if offer.addr_to == swap_client.network_addr else offer.addr_to,
|
||||
'created_at': offer.created_at,
|
||||
'expired_at': offer.expire_at,
|
||||
'sent': 'True' if offer.was_sent else 'False',
|
||||
'was_revoked': 'True' if offer.active_ind == 2 else 'False',
|
||||
'show_bid_form': show_bid_form,
|
||||
'amount_negotiable': offer.amount_negotiable,
|
||||
'rate_negotiable': offer.rate_negotiable,
|
||||
'bid_amount': bid_amount,
|
||||
'bid_rate': bid_rate,
|
||||
'debug_ui': swap_client.debug_ui,
|
||||
'automation_strat_id': -1,
|
||||
}
|
||||
data.update(extend_data)
|
||||
|
||||
if offer.lock_type == TxLockTypes.SEQUENCE_LOCK_TIME or offer.lock_type == TxLockTypes.ABS_LOCK_TIME:
|
||||
if offer.lock_value > 60 * 60:
|
||||
data['lock_value_hr'] = ' ({} hours)'.format(offer.lock_value / (60 * 60))
|
||||
else:
|
||||
data['lock_value_hr'] = ' ({} minutes)'.format(offer.lock_value / 60)
|
||||
|
||||
addr_from_label, addr_to_label = swap_client.getAddressLabel([offer.addr_from, offer.addr_to])
|
||||
if len(addr_from_label) > 0:
|
||||
data['addr_from_label'] = '(' + addr_from_label + ')'
|
||||
if len(addr_to_label) > 0:
|
||||
data['addr_to_label'] = '(' + addr_to_label + ')'
|
||||
|
||||
if swap_client.debug_ui:
|
||||
data['debug_ind'] = debugind
|
||||
data['debug_options'] = [(int(t), t.name) for t in DebugTypes]
|
||||
|
||||
if xmr_offer:
|
||||
int_fee_rate_now, fee_source = ci_from.get_fee_rate()
|
||||
data['xmr_type'] = True
|
||||
data['a_fee_rate'] = ci_from.format_amount(xmr_offer.a_fee_rate)
|
||||
data['a_fee_rate_verify'] = ci_from.format_amount(int_fee_rate_now, conv_int=True)
|
||||
data['a_fee_rate_verify_src'] = fee_source
|
||||
data['a_fee_warn'] = xmr_offer.a_fee_rate < int_fee_rate_now
|
||||
|
||||
lock_spend_tx_vsize = ci_from.xmr_swap_alock_spend_tx_vsize()
|
||||
lock_spend_tx_fee = ci_from.make_int(xmr_offer.a_fee_rate * lock_spend_tx_vsize / 1000, r=1)
|
||||
data['amt_from_lock_spend_tx_fee'] = ci_from.format_amount(lock_spend_tx_fee // ci_from.COIN())
|
||||
|
||||
if offer.was_sent:
|
||||
try:
|
||||
strategy = swap_client.getLinkedStrategy(Concepts.OFFER, offer_id)
|
||||
data['automation_strat_id'] = strategy[0]
|
||||
data['automation_strat_label'] = strategy[1]
|
||||
except Exception:
|
||||
pass # None found
|
||||
|
||||
bids = swap_client.listBids(offer_id=offer_id)
|
||||
formatted_bids = []
|
||||
amt_swapped = 0
|
||||
for b in bids:
|
||||
amt_swapped += b[4]
|
||||
formatted_bids.append((b[2].hex(), ci_from.format_amount(b[4]), strBidState(b[5]), ci_to.format_amount(b[10]), b[11]))
|
||||
data['amt_swapped'] = ci_from.format_amount(amt_swapped)
|
||||
|
||||
template = server.env.get_template('offer.html')
|
||||
return self.render_template(template, {
|
||||
'offer_id': offer_id.hex(),
|
||||
'sent_bid_id': sent_bid_id,
|
||||
'messages': messages,
|
||||
'data': data,
|
||||
'bids': formatted_bids,
|
||||
'addrs': None if show_bid_form is None else swap_client.listSmsgAddresses('bid'),
|
||||
})
|
||||
|
||||
|
||||
def page_offers(self, url_split, post_string, sent=False):
|
||||
server = self.server
|
||||
swap_client = server.swap_client
|
||||
|
||||
filters = {
|
||||
'coin_from': -1,
|
||||
'coin_to': -1,
|
||||
'page_no': 1,
|
||||
'limit': PAGE_LIMIT,
|
||||
'sort_by': 'created_at',
|
||||
'sort_dir': 'desc',
|
||||
'sent_from': 'any' if sent is False else 'only',
|
||||
}
|
||||
messages = []
|
||||
form_data = self.checkForm(post_string, 'offers', messages)
|
||||
if form_data and have_data_entry(form_data, 'applyfilters'):
|
||||
filters['coin_from'] = setCoinFilter(form_data, 'coin_from')
|
||||
filters['coin_to'] = setCoinFilter(form_data, 'coin_to')
|
||||
|
||||
if have_data_entry(form_data, 'sort_by'):
|
||||
sort_by = get_data_entry(form_data, 'sort_by')
|
||||
ensure(sort_by in ['created_at', 'rate'], 'Invalid sort by')
|
||||
filters['sort_by'] = sort_by
|
||||
if have_data_entry(form_data, 'sort_dir'):
|
||||
sort_dir = get_data_entry(form_data, 'sort_dir')
|
||||
ensure(sort_dir in ['asc', 'desc'], 'Invalid sort dir')
|
||||
filters['sort_dir'] = sort_dir
|
||||
if have_data_entry(form_data, 'sent_from'):
|
||||
sent_from = get_data_entry(form_data, 'sent_from')
|
||||
ensure(sent_from in ['any', 'only'], 'Invalid sent filter')
|
||||
filters['sent_from'] = sent_from
|
||||
|
||||
set_pagination_filters(form_data, filters)
|
||||
|
||||
if filters['sent_from'] == 'only':
|
||||
sent = True
|
||||
else:
|
||||
sent = False
|
||||
offers = swap_client.listOffers(sent, filters, with_bid_info=True)
|
||||
|
||||
formatted_offers = []
|
||||
for row in offers:
|
||||
o, completed_amount = row
|
||||
ci_from = swap_client.ci(Coins(o.coin_from))
|
||||
ci_to = swap_client.ci(Coins(o.coin_to))
|
||||
formatted_offers.append((
|
||||
format_timestamp(o.created_at),
|
||||
o.offer_id.hex(),
|
||||
ci_from.coin_name(), ci_to.coin_name(),
|
||||
ci_from.format_amount(o.amount_from),
|
||||
ci_to.format_amount((o.amount_from * o.rate) // ci_from.COIN()),
|
||||
ci_to.format_amount(o.rate),
|
||||
'Public' if o.addr_to == swap_client.network_addr else o.addr_to,
|
||||
o.addr_from,
|
||||
o.was_sent,
|
||||
ci_from.format_amount(completed_amount)))
|
||||
|
||||
coins_from, coins_to = listAvailableCoins(swap_client, split_from=True)
|
||||
|
||||
template = server.env.get_template('offers.html')
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
'coins_from': coins_from,
|
||||
'coins': coins_to,
|
||||
'messages': messages,
|
||||
'filters': filters,
|
||||
'offers': formatted_offers,
|
||||
})
|
||||
@@ -4,9 +4,6 @@
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
import os
|
||||
|
||||
|
||||
def extract_data(bytes_in):
|
||||
str_in = bytes_in.decode('utf-8')
|
||||
start = str_in.find('=')
|
||||
@@ -20,7 +17,6 @@ def extract_data(bytes_in):
|
||||
|
||||
|
||||
def page_tor(self, url_split, post_string):
|
||||
template = self.server.env.get_template('tor.html')
|
||||
|
||||
swap_client = self.server.swap_client
|
||||
|
||||
@@ -37,10 +33,8 @@ def page_tor(self, url_split, post_string):
|
||||
|
||||
messages = []
|
||||
|
||||
return bytes(template.render(
|
||||
title=self.server.title,
|
||||
h2=self.server.title,
|
||||
messages=messages,
|
||||
data=page_data,
|
||||
form_id=os.urandom(8).hex(),
|
||||
), 'UTF-8')
|
||||
template = self.server.env.get_template('tor.html')
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
'data': page_data,
|
||||
})
|
||||
|
||||
306
basicswap/ui/page_wallet.py
Normal file
306
basicswap/ui/page_wallet.py
Normal file
@@ -0,0 +1,306 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2022 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
import traceback
|
||||
|
||||
from .util import (
|
||||
get_data_entry,
|
||||
have_data_entry,
|
||||
)
|
||||
from basicswap.util import (
|
||||
ensure,
|
||||
format_timestamp,
|
||||
)
|
||||
from basicswap.chainparams import (
|
||||
Coins,
|
||||
chainparams,
|
||||
getCoinIdFromTicker,
|
||||
)
|
||||
|
||||
|
||||
def format_wallet_data(ci, w):
|
||||
wf = {
|
||||
'name': ci.coin_name(),
|
||||
'version': w.get('version', '?'),
|
||||
'ticker': ci.ticker_mainnet(),
|
||||
'cid': str(int(ci.coin_type())),
|
||||
'balance': w.get('balance', '?'),
|
||||
'blocks': w.get('blocks', '?'),
|
||||
'synced': w.get('synced', '?'),
|
||||
'expected_seed': w.get('expected_seed', '?'),
|
||||
'updating': w.get('updating', '?'),
|
||||
'havedata': True,
|
||||
}
|
||||
|
||||
if w.get('bootstrapping', False) is True:
|
||||
wf['bootstrapping'] = True
|
||||
if 'known_block_count' in w:
|
||||
wf['known_block_count'] = w['known_block_count']
|
||||
|
||||
if 'balance' in w and 'unconfirmed' in w:
|
||||
wf['balance_all'] = float(w['balance']) + float(w['unconfirmed'])
|
||||
if 'lastupdated' in w:
|
||||
wf['lastupdated'] = format_timestamp(w['lastupdated'])
|
||||
if 'unconfirmed' in w and float(w['unconfirmed']) > 0.0:
|
||||
wf['unconfirmed'] = w['unconfirmed']
|
||||
|
||||
if ci.coin_type() == Coins.PART:
|
||||
wf['stealth_address'] = w.get('stealth_address', '?')
|
||||
wf['blind_balance'] = w.get('blind_balance', '?')
|
||||
if 'blind_unconfirmed' in w and float(w['blind_unconfirmed']) > 0.0:
|
||||
wf['blind_unconfirmed'] = w['blind_unconfirmed']
|
||||
wf['anon_balance'] = w.get('anon_balance', '?')
|
||||
if 'anon_pending' in w and float(w['anon_pending']) > 0.0:
|
||||
wf['anon_pending'] = w['anon_pending']
|
||||
return wf
|
||||
|
||||
|
||||
def page_wallets(self, url_split, post_string):
|
||||
server = self.server
|
||||
swap_client = server.swap_client
|
||||
|
||||
page_data = {}
|
||||
messages = []
|
||||
form_data = self.checkForm(post_string, 'wallets', messages)
|
||||
if form_data:
|
||||
for c in Coins:
|
||||
if c not in chainparams:
|
||||
continue
|
||||
cid = str(int(c))
|
||||
|
||||
if bytes('newaddr_' + cid, 'utf-8') in form_data:
|
||||
swap_client.cacheNewAddressForCoin(c)
|
||||
elif bytes('reseed_' + cid, 'utf-8') in form_data:
|
||||
try:
|
||||
swap_client.reseedWallet(c)
|
||||
messages.append('Reseed complete ' + str(c))
|
||||
except Exception as ex:
|
||||
messages.append('Reseed failed ' + str(ex))
|
||||
swap_client.updateWalletsInfo(True, c)
|
||||
elif bytes('withdraw_' + cid, 'utf-8') in form_data:
|
||||
try:
|
||||
value = form_data[bytes('amt_' + cid, 'utf-8')][0].decode('utf-8')
|
||||
page_data['wd_value_' + cid] = value
|
||||
except Exception as e:
|
||||
messages.append('Error: Missing value')
|
||||
try:
|
||||
address = form_data[bytes('to_' + cid, 'utf-8')][0].decode('utf-8')
|
||||
page_data['wd_address_' + cid] = address
|
||||
except Exception as e:
|
||||
messages.append('Error: Missing address')
|
||||
|
||||
subfee = True if bytes('subfee_' + cid, 'utf-8') in form_data else False
|
||||
page_data['wd_subfee_' + cid] = subfee
|
||||
|
||||
if c == Coins.PART:
|
||||
try:
|
||||
type_from = form_data[bytes('withdraw_type_from_' + cid, 'utf-8')][0].decode('utf-8')
|
||||
type_to = form_data[bytes('withdraw_type_to_' + cid, 'utf-8')][0].decode('utf-8')
|
||||
page_data['wd_type_from_' + cid] = type_from
|
||||
page_data['wd_type_to_' + cid] = type_to
|
||||
except Exception as e:
|
||||
messages.append('Error: Missing type')
|
||||
|
||||
if len(messages) == 0:
|
||||
ci = swap_client.ci(c)
|
||||
ticker = ci.ticker()
|
||||
if c == Coins.PART:
|
||||
try:
|
||||
txid = swap_client.withdrawParticl(type_from, type_to, value, address, subfee)
|
||||
messages.append('Withdrew {} {} ({} to {}) to address {}<br/>In txid: {}'.format(value, ticker, type_from, type_to, address, txid))
|
||||
except Exception as e:
|
||||
messages.append('Error: {}'.format(str(e)))
|
||||
else:
|
||||
try:
|
||||
txid = swap_client.withdrawCoin(c, value, address, subfee)
|
||||
messages.append('Withdrew {} {} to address {}<br/>In txid: {}'.format(value, ticker, address, txid))
|
||||
except Exception as e:
|
||||
messages.append('Error: {}'.format(str(e)))
|
||||
swap_client.updateWalletsInfo(True, c)
|
||||
|
||||
swap_client.updateWalletsInfo()
|
||||
wallets = swap_client.getCachedWalletsInfo()
|
||||
|
||||
wallets_formatted = []
|
||||
sk = sorted(wallets.keys())
|
||||
|
||||
for k in sk:
|
||||
w = wallets[k]
|
||||
if 'error' in w:
|
||||
wallets_formatted.append({
|
||||
'cid': str(int(k)),
|
||||
'error': w['error']
|
||||
})
|
||||
continue
|
||||
|
||||
if 'no_data' in w:
|
||||
wallets_formatted.append({
|
||||
'name': w['name'],
|
||||
'havedata': False,
|
||||
'updating': w['updating'],
|
||||
})
|
||||
continue
|
||||
|
||||
ci = swap_client.ci(k)
|
||||
wf = format_wallet_data(ci, w)
|
||||
|
||||
wallets_formatted.append(wf)
|
||||
|
||||
template = server.env.get_template('wallets.html')
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
'wallets': wallets_formatted,
|
||||
})
|
||||
|
||||
|
||||
def page_wallet(self, url_split, post_string):
|
||||
ensure(len(url_split) > 2, 'Wallet not specified')
|
||||
wallet_ticker = url_split[2]
|
||||
server = self.server
|
||||
swap_client = server.swap_client
|
||||
|
||||
coin_id = getCoinIdFromTicker(wallet_ticker)
|
||||
|
||||
page_data = {}
|
||||
messages = []
|
||||
form_data = self.checkForm(post_string, 'settings', messages)
|
||||
show_utxo_groups = False
|
||||
if form_data:
|
||||
cid = str(int(coin_id))
|
||||
|
||||
if bytes('newaddr_' + cid, 'utf-8') in form_data:
|
||||
swap_client.cacheNewAddressForCoin(coin_id)
|
||||
elif bytes('reseed_' + cid, 'utf-8') in form_data:
|
||||
try:
|
||||
swap_client.reseedWallet(coin_id)
|
||||
messages.append('Reseed complete ' + str(coin_id))
|
||||
except Exception as ex:
|
||||
messages.append('Reseed failed ' + str(ex))
|
||||
swap_client.updateWalletsInfo(True, coin_id)
|
||||
elif bytes('withdraw_' + cid, 'utf-8') in form_data:
|
||||
try:
|
||||
value = form_data[bytes('amt_' + cid, 'utf-8')][0].decode('utf-8')
|
||||
page_data['wd_value_' + cid] = value
|
||||
except Exception as e:
|
||||
messages.append('Error: Missing value')
|
||||
try:
|
||||
address = form_data[bytes('to_' + cid, 'utf-8')][0].decode('utf-8')
|
||||
page_data['wd_address_' + cid] = address
|
||||
except Exception as e:
|
||||
messages.append('Error: Missing address')
|
||||
|
||||
subfee = True if bytes('subfee_' + cid, 'utf-8') in form_data else False
|
||||
page_data['wd_subfee_' + cid] = subfee
|
||||
|
||||
if coin_id == Coins.PART:
|
||||
try:
|
||||
type_from = form_data[bytes('withdraw_type_from_' + cid, 'utf-8')][0].decode('utf-8')
|
||||
type_to = form_data[bytes('withdraw_type_to_' + cid, 'utf-8')][0].decode('utf-8')
|
||||
page_data['wd_type_from_' + cid] = type_from
|
||||
page_data['wd_type_to_' + cid] = type_to
|
||||
except Exception as e:
|
||||
messages.append('Error: Missing type')
|
||||
|
||||
if len(messages) == 0:
|
||||
ci = swap_client.ci(coin_id)
|
||||
ticker = ci.ticker()
|
||||
if coin_id == Coins.PART:
|
||||
try:
|
||||
txid = swap_client.withdrawParticl(type_from, type_to, value, address, subfee)
|
||||
messages.append('Withdrew {} {} ({} to {}) to address {}<br/>In txid: {}'.format(value, ticker, type_from, type_to, address, txid))
|
||||
except Exception as e:
|
||||
messages.append('Error: {}'.format(str(e)))
|
||||
else:
|
||||
try:
|
||||
txid = swap_client.withdrawCoin(coin_id, value, address, subfee)
|
||||
messages.append('Withdrew {} {} to address {}<br/>In txid: {}'.format(value, ticker, address, txid))
|
||||
except Exception as e:
|
||||
messages.append('Error: {}'.format(str(e)))
|
||||
swap_client.updateWalletsInfo(True, coin_id)
|
||||
elif have_data_entry(form_data, 'showutxogroups'):
|
||||
show_utxo_groups = True
|
||||
elif have_data_entry(form_data, 'create_utxo'):
|
||||
show_utxo_groups = True
|
||||
try:
|
||||
value = get_data_entry(form_data, 'utxo_value')
|
||||
page_data['utxo_value'] = value
|
||||
|
||||
ci = swap_client.ci(coin_id)
|
||||
|
||||
value_sats = ci.make_int(value)
|
||||
|
||||
txid, address = ci.createUTXO(value_sats)
|
||||
messages.append('Created new utxo of value {} and address {}<br/>In txid: {}'.format(value, address, txid))
|
||||
except Exception as e:
|
||||
messages.append('Error: {}'.format(str(e)))
|
||||
if swap_client.debug is True:
|
||||
swap_client.log.error(traceback.format_exc())
|
||||
|
||||
swap_client.updateWalletsInfo()
|
||||
wallets = swap_client.getCachedWalletsInfo({'coin_id': coin_id})
|
||||
for k in wallets.keys():
|
||||
w = wallets[k]
|
||||
if 'error' in w:
|
||||
wallet_data = {
|
||||
'cid': str(int(k)),
|
||||
'error': w['error']
|
||||
}
|
||||
continue
|
||||
|
||||
if 'no_data' in w:
|
||||
wallet_data = {
|
||||
'name': w['name'],
|
||||
'havedata': False,
|
||||
'updating': w['updating'],
|
||||
}
|
||||
continue
|
||||
|
||||
ci = swap_client.ci(k)
|
||||
cid = str(int(coin_id))
|
||||
|
||||
wallet_data = format_wallet_data(ci, w)
|
||||
|
||||
fee_rate, fee_src = swap_client.getFeeRateForCoin(k)
|
||||
est_fee = swap_client.estimateWithdrawFee(k, fee_rate)
|
||||
wallet_data['fee_rate'] = ci.format_amount(int(fee_rate * ci.COIN()))
|
||||
wallet_data['fee_rate_src'] = fee_src
|
||||
wallet_data['est_fee'] = 'Unknown' if est_fee is None else ci.format_amount(int(est_fee * ci.COIN()))
|
||||
wallet_data['deposit_address'] = w.get('deposit_address', 'Refresh necessary')
|
||||
|
||||
if k == Coins.XMR:
|
||||
wallet_data['main_address'] = w.get('main_address', 'Refresh necessary')
|
||||
|
||||
if 'wd_type_from_' + cid in page_data:
|
||||
wallet_data['wd_type_from'] = page_data['wd_type_from_' + cid]
|
||||
if 'wd_type_to_' + cid in page_data:
|
||||
wallet_data['wd_type_to'] = page_data['wd_type_to_' + cid]
|
||||
|
||||
if 'wd_value_' + cid in page_data:
|
||||
wallet_data['wd_value'] = page_data['wd_value_' + cid]
|
||||
if 'wd_address_' + cid in page_data:
|
||||
wallet_data['wd_address'] = page_data['wd_address_' + cid]
|
||||
if 'wd_subfee_' + cid in page_data:
|
||||
wallet_data['wd_subfee'] = page_data['wd_subfee_' + cid]
|
||||
if 'utxo_value' in page_data:
|
||||
wallet_data['utxo_value'] = page_data['utxo_value']
|
||||
|
||||
if show_utxo_groups:
|
||||
utxo_groups = ''
|
||||
|
||||
unspent_by_addr = swap_client.getUnspentsByAddr(k)
|
||||
|
||||
sorted_unspent_by_addr = sorted(unspent_by_addr.items(), key=lambda x: x[1], reverse=True)
|
||||
for kv in sorted_unspent_by_addr:
|
||||
utxo_groups += kv[0] + ' ' + ci.format_amount(kv[1]) + '\n'
|
||||
|
||||
wallet_data['show_utxo_groups'] = True
|
||||
wallet_data['utxo_groups'] = utxo_groups
|
||||
|
||||
template = server.env.get_template('wallet.html')
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
'w': wallet_data,
|
||||
})
|
||||
@@ -5,6 +5,7 @@
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
import json
|
||||
import struct
|
||||
import traceback
|
||||
from basicswap.util import (
|
||||
make_int,
|
||||
@@ -12,6 +13,7 @@ from basicswap.util import (
|
||||
)
|
||||
from basicswap.chainparams import (
|
||||
Coins,
|
||||
chainparams,
|
||||
)
|
||||
from basicswap.basicswap_util import (
|
||||
TxTypes,
|
||||
@@ -29,6 +31,7 @@ from basicswap.basicswap_util import (
|
||||
from basicswap.protocols.xmr_swap_1 import getChainBSplitKey
|
||||
|
||||
PAGE_LIMIT = 50
|
||||
invalid_coins_from = (Coins.XMR, Coins.PART_ANON)
|
||||
|
||||
|
||||
def tickerToCoinId(ticker):
|
||||
@@ -94,6 +97,17 @@ def setCoinFilter(form_data, field_name):
|
||||
raise ValueError('Unknown Coin Type {}'.format(str(field_name)))
|
||||
|
||||
|
||||
def set_pagination_filters(form_data, filters):
|
||||
if form_data and have_data_entry(form_data, 'pageback'):
|
||||
filters['page_no'] = int(form_data[b'pageno'][0]) - 1
|
||||
if filters['page_no'] < 1:
|
||||
filters['page_no'] = 1
|
||||
elif form_data and have_data_entry(form_data, 'pageforwards'):
|
||||
filters['page_no'] = int(form_data[b'pageno'][0]) + 1
|
||||
if filters['page_no'] > 1:
|
||||
filters['offset'] = (filters['page_no'] - 1) * PAGE_LIMIT
|
||||
|
||||
|
||||
def getTxIdHex(bid, tx_type, suffix):
|
||||
if tx_type == TxTypes.ITX:
|
||||
obj = bid.initiate_tx
|
||||
@@ -343,3 +357,54 @@ def describeBid(swap_client, bid, xmr_swap, offer, xmr_offer, bid_events, edit_b
|
||||
data['view_tx_desc'] = json.dumps(ci_from.describeTx(data['view_tx_hex']), indent=4)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def listOldBidStates(bid):
|
||||
old_states = []
|
||||
num_states = len(bid.states) // 12
|
||||
for i in range(num_states):
|
||||
up = struct.unpack_from('<iq', bid.states[i * 12:(i + 1) * 12])
|
||||
old_states.append((up[1], 'Bid ' + strBidState(up[0])))
|
||||
if bid.initiate_tx and bid.initiate_tx.states is not None:
|
||||
num_states = len(bid.initiate_tx.states) // 12
|
||||
for i in range(num_states):
|
||||
up = struct.unpack_from('<iq', bid.initiate_tx.states[i * 12:(i + 1) * 12])
|
||||
if up[0] != TxStates.TX_NONE:
|
||||
old_states.append((up[1], 'ITX ' + strTxState(up[0])))
|
||||
if bid.participate_tx and bid.participate_tx.states is not None:
|
||||
num_states = len(bid.participate_tx.states) // 12
|
||||
for i in range(num_states):
|
||||
up = struct.unpack_from('<iq', bid.participate_tx.states[i * 12:(i + 1) * 12])
|
||||
if up[0] != TxStates.TX_NONE:
|
||||
old_states.append((up[1], 'PTX ' + strTxState(up[0])))
|
||||
if len(old_states) > 0:
|
||||
old_states.sort(key=lambda x: x[0])
|
||||
return old_states
|
||||
|
||||
|
||||
def getCoinName(c):
|
||||
if c == Coins.PART_ANON:
|
||||
return chainparams[Coins.PART]['name'].capitalize() + 'Anon'
|
||||
if c == Coins.PART_BLIND:
|
||||
return chainparams[Coins.PART]['name'].capitalize() + 'Blind'
|
||||
return chainparams[c]['name'].capitalize()
|
||||
|
||||
|
||||
def listAvailableCoins(swap_client, with_variants=True, split_from=False):
|
||||
coins_from = []
|
||||
coins = []
|
||||
for k, v in swap_client.coin_clients.items():
|
||||
if k not in chainparams:
|
||||
continue
|
||||
if v['connection_type'] == 'rpc':
|
||||
coins.append((int(k), getCoinName(k)))
|
||||
if split_from and k not in invalid_coins_from:
|
||||
coins_from.append(coins[-1])
|
||||
if with_variants and k == Coins.PART:
|
||||
for v in (Coins.PART_ANON, Coins.PART_BLIND):
|
||||
coins.append((int(v), getCoinName(v)))
|
||||
if split_from and v not in invalid_coins_from:
|
||||
coins_from.append(coins[-1])
|
||||
if split_from:
|
||||
return coins_from, coins
|
||||
return coins
|
||||
|
||||
@@ -21,6 +21,10 @@ class TemporaryError(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
class AutomationConstraint(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
def ensure(v, err_string):
|
||||
if not v:
|
||||
raise ValueError(err_string)
|
||||
@@ -52,7 +56,7 @@ def SerialiseNum(n):
|
||||
rv = bytearray()
|
||||
neg = n < 0
|
||||
absvalue = -n if neg else n
|
||||
while(absvalue):
|
||||
while (absvalue):
|
||||
rv.append(absvalue & 0xff)
|
||||
absvalue >>= 8
|
||||
if rv[-1] & 0x80:
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import hashlib
|
||||
from basicswap.contrib.segwit_addr import bech32_decode, convertbits, bech32_encode
|
||||
|
||||
from basicswap.util.crypto import ripemd160
|
||||
|
||||
__b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
||||
|
||||
@@ -89,7 +89,7 @@ def toWIF(prefix_byte, b, compressed=True):
|
||||
|
||||
def getKeyID(bytes):
|
||||
data = hashlib.sha256(bytes).digest()
|
||||
return hashlib.new('ripemd160', data).digest()
|
||||
return ripemd160(data)
|
||||
|
||||
|
||||
def bech32Decode(hrp, addr):
|
||||
@@ -114,7 +114,7 @@ def decodeAddress(address_str):
|
||||
if b58_addr is not None:
|
||||
address = b58_addr[:-4]
|
||||
checksum = b58_addr[-4:]
|
||||
assert(hashlib.sha256(hashlib.sha256(address).digest()).digest()[:4] == checksum), 'Checksum mismatch'
|
||||
assert (hashlib.sha256(hashlib.sha256(address).digest()).digest()[:4] == checksum), 'Checksum mismatch'
|
||||
return b58_addr[:-4]
|
||||
return None
|
||||
|
||||
|
||||
13
basicswap/util/crypto.py
Normal file
13
basicswap/util/crypto.py
Normal file
@@ -0,0 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2022 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from Crypto.Hash import RIPEMD160 # pycryptodome
|
||||
|
||||
|
||||
def ripemd160(data):
|
||||
h = RIPEMD160.new()
|
||||
h.update(data)
|
||||
return h.digest()
|
||||
@@ -171,18 +171,18 @@ def testEccUtils():
|
||||
print('testEccUtils()')
|
||||
|
||||
G_enc = ToDER(G)
|
||||
assert(G_enc.hex() == '0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8')
|
||||
assert (G_enc.hex() == '0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8')
|
||||
|
||||
G_enc = pointToCPK(G)
|
||||
assert(G_enc.hex() == '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798')
|
||||
assert (G_enc.hex() == '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798')
|
||||
G_dec = CPKToPoint(G_enc)
|
||||
assert(G_dec == G)
|
||||
assert (G_dec == G)
|
||||
|
||||
G_enc = pointToCPK2(G)
|
||||
assert(G_enc.hex() == '0879be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798')
|
||||
assert (G_enc.hex() == '0879be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798')
|
||||
|
||||
H = hashToCurve(ToDER(G))
|
||||
assert(pointToCPK(H).hex() == '0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0')
|
||||
assert (pointToCPK(H).hex() == '0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0')
|
||||
|
||||
print('Passed.')
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ def rfc2440_hash_password(password, salt=None):
|
||||
|
||||
if salt is None:
|
||||
salt = secrets.token_bytes(8)
|
||||
assert(len(salt) == 8)
|
||||
assert len(salt) == 8
|
||||
|
||||
hashbytes = salt + password.encode('utf-8')
|
||||
len_hashbytes = len(hashbytes)
|
||||
|
||||
@@ -13,7 +13,7 @@ def decodeScriptNum(script_bytes, o):
|
||||
v = 0
|
||||
num_len = script_bytes[o]
|
||||
if num_len >= OpCodes.OP_1 and num_len <= OpCodes.OP_16:
|
||||
return((num_len - OpCodes.OP_1) + 1, 1)
|
||||
return ((num_len - OpCodes.OP_1) + 1, 1)
|
||||
|
||||
if num_len > 4:
|
||||
raise ValueError('Bad scriptnum length') # Max 4 bytes
|
||||
@@ -29,7 +29,7 @@ def decodeScriptNum(script_bytes, o):
|
||||
v *= -1
|
||||
else:
|
||||
v += int(b) << 8 * i
|
||||
return(v, 1 + num_len)
|
||||
return (v, 1 + num_len)
|
||||
|
||||
|
||||
def getP2SHScriptForHash(p2sh):
|
||||
|
||||
@@ -24,10 +24,6 @@ import urllib.parse
|
||||
from urllib.request import urlretrieve
|
||||
|
||||
import basicswap.config as cfg
|
||||
from basicswap.rpc import (
|
||||
callrpc_cli,
|
||||
waitForRPC,
|
||||
)
|
||||
from basicswap.base import getaddrinfo_tor
|
||||
from basicswap.basicswap import BasicSwap
|
||||
from basicswap.chainparams import Coins
|
||||
@@ -36,14 +32,41 @@ from basicswap.util.rfc2440 import rfc2440_hash_password
|
||||
from basicswap.contrib.rpcauth import generate_salt, password_to_hmac
|
||||
from bin.basicswap_run import startDaemon, startXmrWalletDaemon
|
||||
|
||||
PARTICL_VERSION = os.getenv('PARTICL_VERSION', '0.21.2.9')
|
||||
PARTICL_VERSION_TAG = os.getenv('PARTICL_VERSION_TAG', '')
|
||||
PARTICL_LINUX_EXTRA = os.getenv('PARTICL_LINUX_EXTRA', '_nousb')
|
||||
|
||||
LITECOIN_VERSION = os.getenv('LITECOIN_VERSION', '0.21.2')
|
||||
LITECOIN_VERSION_TAG = os.getenv('LITECOIN_VERSION_TAG', '')
|
||||
|
||||
BITCOIN_VERSION = os.getenv('BITCOIN_VERSION', '22.0')
|
||||
BITCOIN_VERSION_TAG = os.getenv('BITCOIN_VERSION_TAG', '')
|
||||
|
||||
MONERO_VERSION = os.getenv('MONERO_VERSION', '0.18.0.0')
|
||||
MONERO_VERSION_TAG = os.getenv('MONERO_VERSION_TAG', '')
|
||||
XMR_SITE_COMMIT = 'f093c0da2219d94e6bef5f3948ac61b4ecdcb95b' # Lock hashes.txt to monero version
|
||||
|
||||
PIVX_VERSION = os.getenv('PIVX_VERSION', '5.4.99')
|
||||
PIVX_VERSION_TAG = os.getenv('PIVX_VERSION_TAG', '_scantxoutset')
|
||||
|
||||
# version, version tag eg. "rc1", signers
|
||||
known_coins = {
|
||||
'particl': ('0.21.2.7', '', ('tecnovert',)),
|
||||
'litecoin': ('0.18.1', '', ('thrasher',)),
|
||||
'bitcoin': ('22.0', '', ('laanwj',)),
|
||||
'particl': (PARTICL_VERSION, PARTICL_VERSION_TAG, ('tecnovert',)),
|
||||
'litecoin': (LITECOIN_VERSION, LITECOIN_VERSION_TAG, ('davidburkett38',)),
|
||||
'bitcoin': (BITCOIN_VERSION, BITCOIN_VERSION_TAG, ('laanwj',)),
|
||||
'namecoin': ('0.18.0', '', ('JeremyRand',)),
|
||||
'monero': ('0.17.3.0', '', ('',)),
|
||||
'monero': (MONERO_VERSION, MONERO_VERSION_TAG, ('binaryfate',)),
|
||||
'pivx': (PIVX_VERSION, PIVX_VERSION_TAG, ('tecnovert',)),
|
||||
}
|
||||
|
||||
expected_key_ids = {
|
||||
'tecnovert': ('13F13651C9CF0D6B',),
|
||||
'thrasher': ('FE3348877809386C',),
|
||||
'laanwj': ('1E4AED62986CD25D',),
|
||||
'JeremyRand': ('2DBE339E29F6294C',),
|
||||
'binaryfate': ('F0AF4D462A0BDF92',),
|
||||
'davidburkett38': ('3620E9D387E55666',),
|
||||
'fuzzbawls': ('3BDCDA2D87A881D9',),
|
||||
}
|
||||
|
||||
if platform.system() == 'Darwin':
|
||||
@@ -61,6 +84,17 @@ logger.level = logging.DEBUG
|
||||
if not len(logger.handlers):
|
||||
logger.addHandler(logging.StreamHandler(sys.stdout))
|
||||
|
||||
UI_HTML_PORT = int(os.getenv('UI_HTML_PORT', 12700))
|
||||
UI_WS_PORT = int(os.getenv('UI_WS_PORT', 11700))
|
||||
COINS_RPCBIND_IP = os.getenv('COINS_RPCBIND_IP', '127.0.0.1')
|
||||
|
||||
PART_ZMQ_PORT = int(os.getenv('PART_ZMQ_PORT', 20792))
|
||||
PART_RPC_HOST = os.getenv('PART_RPC_HOST', '127.0.0.1')
|
||||
PART_RPC_PORT = int(os.getenv('PART_RPC_PORT', 19792))
|
||||
PART_ONION_PORT = int(os.getenv('PART_ONION_PORT', 51734))
|
||||
PART_RPC_USER = os.getenv('PART_RPC_USER', '')
|
||||
PART_RPC_PWD = os.getenv('PART_RPC_PWD', '')
|
||||
|
||||
XMR_RPC_HOST = os.getenv('XMR_RPC_HOST', '127.0.0.1')
|
||||
BASE_XMR_RPC_PORT = int(os.getenv('BASE_XMR_RPC_PORT', 29798))
|
||||
BASE_XMR_ZMQ_PORT = int(os.getenv('BASE_XMR_ZMQ_PORT', 30898))
|
||||
@@ -68,35 +102,28 @@ BASE_XMR_WALLET_PORT = int(os.getenv('BASE_XMR_WALLET_PORT', 29998))
|
||||
XMR_WALLET_RPC_HOST = os.getenv('XMR_WALLET_RPC_HOST', '127.0.0.1')
|
||||
XMR_WALLET_RPC_USER = os.getenv('XMR_WALLET_RPC_USER', 'xmr_wallet_user')
|
||||
XMR_WALLET_RPC_PWD = os.getenv('XMR_WALLET_RPC_PWD', 'xmr_wallet_pwd')
|
||||
XMR_SITE_COMMIT = 'be137696c3a1d93bfaddf619da0d05a3e6cee4f2' # Lock hashes.txt to monero version
|
||||
DEFAULT_XMR_RESTORE_HEIGHT = int(os.getenv('DEFAULT_XMR_RESTORE_HEIGHT', 2245107))
|
||||
|
||||
DEFAULT_XMR_RESTORE_HEIGHT = 2245107
|
||||
|
||||
UI_HTML_PORT = int(os.getenv('UI_HTML_PORT', 12700))
|
||||
PART_ZMQ_PORT = int(os.getenv('PART_ZMQ_PORT', 20792))
|
||||
|
||||
PART_RPC_HOST = os.getenv('PART_RPC_HOST', '127.0.0.1')
|
||||
LTC_RPC_HOST = os.getenv('LTC_RPC_HOST', '127.0.0.1')
|
||||
BTC_RPC_HOST = os.getenv('BTC_RPC_HOST', '127.0.0.1')
|
||||
NMC_RPC_HOST = os.getenv('NMC_RPC_HOST', '127.0.0.1')
|
||||
|
||||
PART_RPC_PORT = int(os.getenv('PART_RPC_PORT', 19792))
|
||||
LTC_RPC_PORT = int(os.getenv('LTC_RPC_PORT', 19795))
|
||||
BTC_RPC_PORT = int(os.getenv('BTC_RPC_PORT', 19796))
|
||||
NMC_RPC_PORT = int(os.getenv('NMC_RPC_PORT', 19798))
|
||||
|
||||
PART_ONION_PORT = int(os.getenv('PART_ONION_PORT', 51734))
|
||||
LTC_ONION_PORT = int(os.getenv('LTC_ONION_PORT', 9333)) # Still on 0.18 codebase, same port
|
||||
BTC_ONION_PORT = int(os.getenv('BTC_ONION_PORT', 8334))
|
||||
|
||||
PART_RPC_USER = os.getenv('PART_RPC_USER', '')
|
||||
PART_RPC_PWD = os.getenv('PART_RPC_PWD', '')
|
||||
BTC_RPC_USER = os.getenv('BTC_RPC_USER', '')
|
||||
BTC_RPC_PWD = os.getenv('BTC_RPC_PWD', '')
|
||||
LTC_RPC_PORT = int(os.getenv('LTC_RPC_PORT', 19895))
|
||||
LTC_ONION_PORT = int(os.getenv('LTC_ONION_PORT', 9333))
|
||||
LTC_RPC_USER = os.getenv('LTC_RPC_USER', '')
|
||||
LTC_RPC_PWD = os.getenv('LTC_RPC_PWD', '')
|
||||
|
||||
COINS_RPCBIND_IP = os.getenv('COINS_RPCBIND_IP', '127.0.0.1')
|
||||
BTC_RPC_HOST = os.getenv('BTC_RPC_HOST', '127.0.0.1')
|
||||
BTC_RPC_PORT = int(os.getenv('BTC_RPC_PORT', 19996))
|
||||
BTC_ONION_PORT = int(os.getenv('BTC_ONION_PORT', 8334))
|
||||
BTC_RPC_USER = os.getenv('BTC_RPC_USER', '')
|
||||
BTC_RPC_PWD = os.getenv('BTC_RPC_PWD', '')
|
||||
|
||||
NMC_RPC_HOST = os.getenv('NMC_RPC_HOST', '127.0.0.1')
|
||||
NMC_RPC_PORT = int(os.getenv('NMC_RPC_PORT', 19698))
|
||||
|
||||
PIVX_RPC_HOST = os.getenv('PIVX_RPC_HOST', '127.0.0.1')
|
||||
PIVX_RPC_PORT = int(os.getenv('PIVX_RPC_PORT', 51473))
|
||||
PIVX_ONION_PORT = int(os.getenv('PIVX_ONION_PORT', 51472)) # nDefaultPort
|
||||
PIVX_RPC_USER = os.getenv('PIVX_RPC_USER', '')
|
||||
PIVX_RPC_PWD = os.getenv('PIVX_RPC_PWD', '')
|
||||
|
||||
TOR_PROXY_HOST = os.getenv('TOR_PROXY_HOST', '127.0.0.1')
|
||||
TOR_PROXY_PORT = int(os.getenv('TOR_PROXY_PORT', 9050))
|
||||
@@ -105,7 +132,9 @@ TOR_DNS_PORT = int(os.getenv('TOR_DNS_PORT', 5353))
|
||||
TEST_TOR_PROXY = toBool(os.getenv('TEST_TOR_PROXY', 'true')) # Expects a known exit node
|
||||
TEST_ONION_LINK = toBool(os.getenv('TEST_ONION_LINK', 'false'))
|
||||
|
||||
extract_core_overwrite = True
|
||||
BITCOIN_FASTSYNC_URL = os.getenv('BITCOIN_FASTSYNC_URL', 'http://utxosets.blob.core.windows.net/public/')
|
||||
BITCOIN_FASTSYNC_FILE = os.getenv('BITCOIN_FASTSYNC_FILE', 'utxo-snapshot-bitcoin-mainnet-720179.tar')
|
||||
|
||||
use_tor_proxy = False
|
||||
|
||||
default_socket = socket.socket
|
||||
@@ -175,7 +204,7 @@ def testTorConnection():
|
||||
logger.info('Testing TOR connection at: ' + test_url)
|
||||
|
||||
test_response = downloadBytes(test_url).decode('utf-8')
|
||||
assert('Congratulations. This browser is configured to use Tor.' in test_response)
|
||||
assert ('Congratulations. This browser is configured to use Tor.' in test_response)
|
||||
logger.info('TOR is working.')
|
||||
|
||||
|
||||
@@ -183,13 +212,59 @@ def testOnionLink():
|
||||
test_url = 'http://jqyzxhjk6psc6ul5jnfwloamhtyh7si74b4743k2qgpskwwxrzhsxmad.onion'
|
||||
logger.info('Testing onion site: ' + test_url)
|
||||
test_response = downloadBytes(test_url).decode('utf-8')
|
||||
assert('The Tor Project\'s free software protects your privacy online.' in test_response)
|
||||
assert ('The Tor Project\'s free software protects your privacy online.' in test_response)
|
||||
logger.info('Onion links work.')
|
||||
|
||||
|
||||
def extractCore(coin, version_data, settings, bin_dir, release_path):
|
||||
def downloadPIVXParams(output_dir):
|
||||
# util/fetch-params.sh
|
||||
|
||||
if os.path.exists(output_dir):
|
||||
logger.info(f'Skipping PIVX params download, path exists: {output_dir}')
|
||||
return
|
||||
os.makedirs(output_dir)
|
||||
|
||||
source_url = 'https://download.z.cash/downloads/'
|
||||
files = {
|
||||
'sapling-spend.params': '8e48ffd23abb3a5fd9c5589204f32d9c31285a04b78096ba40a79b75677efc13',
|
||||
'sapling-output.params': '2f0ebbcbb9bb0bcffe95a397e7eba89c29eb4dde6191c339db88570e3f3fb0e4',
|
||||
}
|
||||
|
||||
try:
|
||||
setConnectionParameters()
|
||||
for k, v in files.items():
|
||||
url = urllib.parse.urljoin(source_url, k)
|
||||
path = os.path.join(output_dir, k)
|
||||
downloadFile(url, path)
|
||||
hasher = hashlib.sha256()
|
||||
with open(path, 'rb') as fp:
|
||||
hasher.update(fp.read())
|
||||
file_hash = hasher.hexdigest()
|
||||
logger.info('%s hash: %s', k, file_hash)
|
||||
assert file_hash == v
|
||||
finally:
|
||||
popConnectionParameters()
|
||||
|
||||
|
||||
def isValidSignature(result):
|
||||
if result.valid is False \
|
||||
and (result.status == 'signature valid' and result.key_status == 'signing key has expired'):
|
||||
return True
|
||||
return result.valid
|
||||
|
||||
|
||||
def ensureValidSignatureBy(result, signing_key_name):
|
||||
if not isValidSignature(result):
|
||||
raise ValueError('Signature verification failed.')
|
||||
|
||||
if result.key_id not in expected_key_ids[signing_key_name]:
|
||||
raise ValueError('Signature made by unexpected keyid: ' + result.key_id)
|
||||
|
||||
|
||||
def extractCore(coin, version_data, settings, bin_dir, release_path, extra_opts={}):
|
||||
version, version_tag, signers = version_data
|
||||
logger.info('extractCore %s v%s%s', coin, version, version_tag)
|
||||
extract_core_overwrite = extra_opts.get('extract_core_overwrite', True)
|
||||
|
||||
if coin == 'monero':
|
||||
bins = ['monerod', 'monero-wallet-rpc']
|
||||
@@ -240,7 +315,13 @@ def extractCore(coin, version_data, settings, bin_dir, release_path):
|
||||
for b in bins:
|
||||
out_path = os.path.join(bin_dir, b)
|
||||
if not os.path.exists(out_path) or extract_core_overwrite:
|
||||
with open(out_path, 'wb') as fout, ft.extractfile('{}-{}/bin/{}'.format(coin, version + version_tag, b)) as fi:
|
||||
|
||||
if coin == 'pivx':
|
||||
filename = '{}-{}/bin/{}'.format(coin, version, b)
|
||||
else:
|
||||
filename = '{}-{}/bin/{}'.format(coin, version + version_tag, b)
|
||||
|
||||
with open(out_path, 'wb') as fout, ft.extractfile(filename) as fi:
|
||||
fout.write(fi.read())
|
||||
try:
|
||||
os.chmod(out_path, stat.S_IRWXU | stat.S_IXGRP | stat.S_IXOTH)
|
||||
@@ -248,7 +329,7 @@ def extractCore(coin, version_data, settings, bin_dir, release_path):
|
||||
logging.warning('Unable to set file permissions: %s, for %s', str(e), out_path)
|
||||
|
||||
|
||||
def prepareCore(coin, version_data, settings, data_dir):
|
||||
def prepareCore(coin, version_data, settings, data_dir, extra_opts={}):
|
||||
version, version_tag, signers = version_data
|
||||
logger.info('prepareCore %s v%s%s', coin, version, version_tag)
|
||||
|
||||
@@ -256,6 +337,7 @@ def prepareCore(coin, version_data, settings, data_dir):
|
||||
if not os.path.exists(bin_dir):
|
||||
os.makedirs(bin_dir)
|
||||
|
||||
filename_extra = ''
|
||||
if 'osx' in BIN_ARCH:
|
||||
os_dir_name = 'osx-unsigned'
|
||||
os_name = 'osx'
|
||||
@@ -265,7 +347,10 @@ def prepareCore(coin, version_data, settings, data_dir):
|
||||
else:
|
||||
os_dir_name = 'linux'
|
||||
os_name = 'linux'
|
||||
if coin == 'particl':
|
||||
filename_extra = PARTICL_LINUX_EXTRA
|
||||
|
||||
signing_key_name = signers[0]
|
||||
if coin == 'monero':
|
||||
use_file_ext = 'tar.bz2' if FILE_EXT == 'tar.gz' else FILE_EXT
|
||||
release_filename = '{}-{}-{}.{}'.format(coin, version, BIN_ARCH, use_file_ext)
|
||||
@@ -284,15 +369,14 @@ def prepareCore(coin, version_data, settings, data_dir):
|
||||
downloadFile(assert_url, assert_path)
|
||||
else:
|
||||
major_version = int(version.split('.')[0])
|
||||
signing_key_name = signers[0]
|
||||
release_filename = '{}-{}-{}.{}'.format(coin, version + version_tag, BIN_ARCH, FILE_EXT)
|
||||
release_filename = '{}-{}-{}{}.{}'.format(coin, version + version_tag, BIN_ARCH, filename_extra, FILE_EXT)
|
||||
if coin == 'particl':
|
||||
release_url = 'https://github.com/particl/particl-core/releases/download/v{}/{}'.format(version + version_tag, release_filename)
|
||||
assert_filename = '{}-{}-{}-build.assert'.format(coin, os_name, version)
|
||||
assert_url = 'https://raw.githubusercontent.com/particl/gitian.sigs/master/%s-%s/%s/%s' % (version + version_tag, os_dir_name, signing_key_name, assert_filename)
|
||||
elif coin == 'litecoin':
|
||||
release_url = 'https://download.litecoin.org/litecoin-{}/{}/{}'.format(version, os_name, release_filename)
|
||||
assert_filename = '{}-{}-{}-build.assert'.format(coin, os_name, version.rsplit('.', 1)[0])
|
||||
assert_filename = '{}-core-{}-{}-build.assert'.format(coin, os_name, version.rsplit('.', 1)[0])
|
||||
assert_url = 'https://raw.githubusercontent.com/litecoin-project/gitian.sigs.ltc/master/%s-%s/%s/%s' % (version, os_dir_name, signing_key_name, assert_filename)
|
||||
elif coin == 'bitcoin':
|
||||
release_url = 'https://bitcoincore.org/bin/bitcoin-core-{}/{}'.format(version, release_filename)
|
||||
@@ -305,6 +389,11 @@ def prepareCore(coin, version_data, settings, data_dir):
|
||||
release_url = 'https://beta.namecoin.org/files/namecoin-core/namecoin-core-{}/{}'.format(version, release_filename)
|
||||
assert_filename = '{}-{}-{}-build.assert'.format(coin, os_name, version.rsplit('.', 1)[0])
|
||||
assert_url = 'https://raw.githubusercontent.com/namecoin/gitian.sigs/master/%s-%s/%s/%s' % (version, os_dir_name, signing_key_name, assert_filename)
|
||||
elif coin == 'pivx':
|
||||
release_filename = '{}-{}-{}{}.{}'.format(coin, version, BIN_ARCH, filename_extra, FILE_EXT)
|
||||
release_url = 'https://github.com/tecnovert/particl-core/releases/download/v{}/{}'.format(version + version_tag, release_filename)
|
||||
assert_filename = 'pivx-linux-6.0-build.assert'
|
||||
assert_url = 'https://raw.githubusercontent.com/tecnovert/gitian.sigs/pivx/5.4.99_scantxoutset-linux/tecnovert/{}'.format(assert_filename)
|
||||
else:
|
||||
raise ValueError('Unknown coin')
|
||||
|
||||
@@ -345,18 +434,26 @@ def prepareCore(coin, version_data, settings, data_dir):
|
||||
"""
|
||||
gpg = gnupg.GPG()
|
||||
|
||||
keysdirpath = extra_opts.get('keysdirpath', None)
|
||||
if keysdirpath is not None:
|
||||
logger.info(f'Loading PGP keys from: {keysdirpath}.')
|
||||
for path in os.scandir(keysdirpath):
|
||||
if path.is_file():
|
||||
with open(path, 'rb') as fp:
|
||||
rv = gpg.import_keys(fp.read())
|
||||
for key in rv.fingerprints:
|
||||
gpg.trust_keys(rv.fingerprints[0], 'TRUST_FULLY')
|
||||
|
||||
if coin == 'monero':
|
||||
with open(assert_path, 'rb') as fp:
|
||||
verified = gpg.verify_file(fp)
|
||||
|
||||
if verified.username is None:
|
||||
logger.warning('Signature not verified.')
|
||||
if not isValidSignature(verified) and verified.username is None:
|
||||
logger.warning('Signature made by unknown key.')
|
||||
|
||||
pubkeyurl = 'https://raw.githubusercontent.com/monero-project/monero/master/utils/gpg_keys/binaryfate.asc'
|
||||
logger.info('Importing public key from url: ' + pubkeyurl)
|
||||
rv = gpg.import_keys(downloadBytes(pubkeyurl))
|
||||
print('import_keys', rv)
|
||||
assert('F0AF4D462A0BDF92' in rv.fingerprints[0])
|
||||
gpg.trust_keys(rv.fingerprints[0], 'TRUST_FULLY')
|
||||
with open(assert_path, 'rb') as fp:
|
||||
verified = gpg.verify_file(fp)
|
||||
@@ -364,13 +461,24 @@ def prepareCore(coin, version_data, settings, data_dir):
|
||||
with open(assert_sig_path, 'rb') as fp:
|
||||
verified = gpg.verify_file(fp, assert_path)
|
||||
|
||||
if verified.username is None:
|
||||
logger.warning('Signature not verified.')
|
||||
if not isValidSignature(verified) and verified.username is None:
|
||||
logger.warning('Signature made by unknown key.')
|
||||
|
||||
pubkeyurl = 'https://raw.githubusercontent.com/tecnovert/basicswap/master/gitianpubkeys/{}_{}.pgp'.format(coin, signing_key_name)
|
||||
logger.info('Importing public key from url: ' + pubkeyurl)
|
||||
|
||||
rv = gpg.import_keys(downloadBytes(pubkeyurl))
|
||||
if coin == 'pivx':
|
||||
filename = '{}_{}.pgp'.format('particl', signing_key_name)
|
||||
else:
|
||||
filename = '{}_{}.pgp'.format(coin, signing_key_name)
|
||||
pubkeyurls = (
|
||||
'https://raw.githubusercontent.com/tecnovert/basicswap/master/pgp/keys/' + filename,
|
||||
'https://gitlab.com/particl/basicswap/-/raw/master/pgp/keys/' + filename,
|
||||
)
|
||||
for url in pubkeyurls:
|
||||
try:
|
||||
logger.info('Importing public key from url: ' + url)
|
||||
rv = gpg.import_keys(downloadBytes(url))
|
||||
break
|
||||
except Exception as e:
|
||||
logging.warning('Import from url failed: %s', str(e))
|
||||
|
||||
for key in rv.fingerprints:
|
||||
gpg.trust_keys(key, 'TRUST_FULLY')
|
||||
@@ -378,11 +486,9 @@ def prepareCore(coin, version_data, settings, data_dir):
|
||||
with open(assert_sig_path, 'rb') as fp:
|
||||
verified = gpg.verify_file(fp, assert_path)
|
||||
|
||||
if verified.valid is False \
|
||||
and not (verified.status == 'signature valid' and verified.key_status == 'signing key has expired'):
|
||||
raise ValueError('Signature verification failed.')
|
||||
ensureValidSignatureBy(verified, signing_key_name)
|
||||
|
||||
extractCore(coin, version_data, settings, bin_dir, release_path)
|
||||
extractCore(coin, version_data, settings, bin_dir, release_path, extra_opts)
|
||||
|
||||
|
||||
def writeTorSettings(fp, coin, coin_settings, tor_control_password):
|
||||
@@ -395,16 +501,17 @@ def writeTorSettings(fp, coin, coin_settings, tor_control_password):
|
||||
fp.write(f'torpassword={tor_control_password}\n')
|
||||
fp.write(f'torcontrol={TOR_PROXY_HOST}:{TOR_CONTROL_PORT}\n')
|
||||
|
||||
if coin == 'litecoin':
|
||||
fp.write(f'bind=0.0.0.0:{onionport}\n')
|
||||
else:
|
||||
if coin_settings['core_version_group'] >= 21:
|
||||
fp.write(f'bind=0.0.0.0:{onionport}=onion\n')
|
||||
else:
|
||||
fp.write(f'bind=0.0.0.0:{onionport}\n')
|
||||
|
||||
|
||||
def prepareDataDir(coin, settings, chain, particl_mnemonic, use_containers=False, tor_control_password=None):
|
||||
def prepareDataDir(coin, settings, chain, particl_mnemonic, extra_opts={}):
|
||||
core_settings = settings['chainclients'][coin]
|
||||
bin_dir = core_settings['bindir']
|
||||
data_dir = core_settings['datadir']
|
||||
tor_control_password = extra_opts.get('tor_control_password', None)
|
||||
|
||||
if not os.path.exists(data_dir):
|
||||
os.makedirs(data_dir)
|
||||
@@ -423,7 +530,11 @@ def prepareDataDir(coin, settings, chain, particl_mnemonic, use_containers=False
|
||||
fp.write('restricted-rpc=1\n')
|
||||
if chain == 'testnet':
|
||||
fp.write('testnet=1\n')
|
||||
fp.write('data-dir={}\n'.format(data_dir))
|
||||
config_datadir = data_dir
|
||||
if core_settings['manage_daemon'] is False:
|
||||
# Assume conf file is for isolated coin docker setup
|
||||
config_datadir = '/data'
|
||||
fp.write(f'data-dir={config_datadir}\n')
|
||||
fp.write('rpc-bind-port={}\n'.format(core_settings['rpcport']))
|
||||
fp.write('rpc-bind-ip={}\n'.format(COINS_RPCBIND_IP))
|
||||
fp.write('zmq-rpc-bind-port={}\n'.format(core_settings['zmqport']))
|
||||
@@ -443,21 +554,26 @@ def prepareDataDir(coin, settings, chain, particl_mnemonic, use_containers=False
|
||||
if os.path.exists(wallet_conf_path):
|
||||
exitWithError('{} exists'.format(wallet_conf_path))
|
||||
with open(wallet_conf_path, 'w') as fp:
|
||||
if use_containers:
|
||||
if extra_opts.get('use_containers', False) is True:
|
||||
fp.write('daemon-address={}:{}\n'.format(core_settings['rpchost'], core_settings['rpcport']))
|
||||
fp.write('untrusted-daemon=1\n')
|
||||
fp.write('no-dns=1\n')
|
||||
fp.write('rpc-bind-port={}\n'.format(core_settings['walletrpcport']))
|
||||
fp.write('rpc-bind-ip={}\n'.format(COINS_RPCBIND_IP))
|
||||
fp.write('wallet-dir={}\n'.format(os.path.join(data_dir, 'wallets')))
|
||||
fp.write('log-file={}\n'.format(os.path.join(data_dir, 'wallet.log')))
|
||||
fp.write('shared-ringdb-dir={}\n'.format(os.path.join(data_dir, 'shared-ringdb')))
|
||||
config_datadir = os.path.join(data_dir, 'wallets')
|
||||
if core_settings['manage_wallet_daemon'] is False:
|
||||
# Assume conf file is for isolated coin docker setup
|
||||
config_datadir = '/data'
|
||||
fp.write(f'wallet-dir={config_datadir}\n')
|
||||
fp.write('log-file={}\n'.format(os.path.join(config_datadir, 'wallet.log')))
|
||||
fp.write('shared-ringdb-dir={}\n'.format(os.path.join(config_datadir, 'shared-ringdb')))
|
||||
fp.write('rpc-login={}:{}\n'.format(core_settings['walletrpcuser'], core_settings['walletrpcpassword']))
|
||||
|
||||
if tor_control_password is not None:
|
||||
if not core_settings['manage_daemon']:
|
||||
fp.write(f'proxy={TOR_PROXY_HOST}:{TOR_PROXY_PORT}\n')
|
||||
return
|
||||
|
||||
core_conf_path = os.path.join(data_dir, coin + '.conf')
|
||||
if os.path.exists(core_conf_path):
|
||||
exitWithError('{} exists'.format(core_conf_path))
|
||||
@@ -493,10 +609,11 @@ def prepareDataDir(coin, settings, chain, particl_mnemonic, use_containers=False
|
||||
fp.write('staking=0\n')
|
||||
if PART_RPC_USER != '':
|
||||
fp.write('rpcauth={}:{}${}\n'.format(PART_RPC_USER, salt, password_to_hmac(salt, PART_RPC_PWD)))
|
||||
if particl_mnemonic == 'none':
|
||||
if particl_mnemonic == 'auto':
|
||||
fp.write('createdefaultmasterkey=1')
|
||||
elif coin == 'litecoin':
|
||||
fp.write('prune=2000\n')
|
||||
fp.write('prune=4000\n')
|
||||
fp.write('pid=litecoind.pid\n')
|
||||
if LTC_RPC_USER != '':
|
||||
fp.write('rpcauth={}:{}${}\n'.format(LTC_RPC_USER, salt, password_to_hmac(salt, LTC_RPC_PWD)))
|
||||
elif coin == 'bitcoin':
|
||||
@@ -506,13 +623,51 @@ def prepareDataDir(coin, settings, chain, particl_mnemonic, use_containers=False
|
||||
fp.write('rpcauth={}:{}${}\n'.format(BTC_RPC_USER, salt, password_to_hmac(salt, BTC_RPC_PWD)))
|
||||
elif coin == 'namecoin':
|
||||
fp.write('prune=2000\n')
|
||||
elif coin == 'pivx':
|
||||
base_dir = extra_opts.get('data_dir', data_dir)
|
||||
params_dir = os.path.join(base_dir, 'pivx-params')
|
||||
downloadPIVXParams(params_dir)
|
||||
fp.write('prune=4000\n')
|
||||
fp.write(f'paramsdir={params_dir}\n')
|
||||
if PIVX_RPC_USER != '':
|
||||
fp.write('rpcauth={}:{}${}\n'.format(PIVX_RPC_USER, salt, password_to_hmac(salt, PIVX_RPC_PWD)))
|
||||
else:
|
||||
logger.warning('Unknown coin %s', coin)
|
||||
|
||||
wallet_util = coin + '-wallet'
|
||||
if os.path.exists(os.path.join(bin_dir, wallet_util)):
|
||||
logger.info('Creating wallet.dat for {}.'.format(wallet_util.capitalize()))
|
||||
callrpc_cli(bin_dir, data_dir, chain, '-wallet=wallet.dat create', wallet_util)
|
||||
if coin == 'bitcoin' and extra_opts.get('use_btc_fastsync', False) is True:
|
||||
logger.info('Initialising BTC chain with fastsync %s', BITCOIN_FASTSYNC_FILE)
|
||||
base_dir = extra_opts['data_dir']
|
||||
|
||||
for dirname in ('blocks', 'chainstate'):
|
||||
if os.path.exists(os.path.join(data_dir, dirname)):
|
||||
raise ValueError(f'{dirname} directory already exists, not overwriting.')
|
||||
|
||||
sync_file_path = os.path.join(base_dir, BITCOIN_FASTSYNC_FILE)
|
||||
if not os.path.exists(sync_file_path):
|
||||
sync_file_url = os.path.join(BITCOIN_FASTSYNC_URL, BITCOIN_FASTSYNC_FILE)
|
||||
downloadFile(sync_file_url, sync_file_path)
|
||||
|
||||
asc_filename = BITCOIN_FASTSYNC_FILE + '.asc'
|
||||
asc_file_path = os.path.join(base_dir, asc_filename)
|
||||
if not os.path.exists(asc_file_path):
|
||||
asc_file_urls = (
|
||||
'https://raw.githubusercontent.com/tecnovert/basicswap/master/pgp/sigs/' + asc_filename,
|
||||
'https://gitlab.com/particl/basicswap/-/raw/master/pgp/sigs/' + asc_filename,
|
||||
)
|
||||
for url in asc_file_urls:
|
||||
try:
|
||||
downloadFile(url, asc_file_path)
|
||||
break
|
||||
except Exception as e:
|
||||
logging.warning('Download failed: %s', str(e))
|
||||
gpg = gnupg.GPG()
|
||||
with open(asc_file_path, 'rb') as fp:
|
||||
verified = gpg.verify_file(fp, sync_file_path)
|
||||
|
||||
ensureValidSignatureBy(verified, 'tecnovert')
|
||||
|
||||
with tarfile.open(sync_file_path) as ft:
|
||||
ft.extractall(path=data_dir)
|
||||
|
||||
|
||||
def write_torrc(data_dir, tor_control_password):
|
||||
@@ -623,20 +778,6 @@ def modify_tor_config(settings, coin, tor_control_password=None, enable=False):
|
||||
writeTorSettings(fp, coin, coin_settings, tor_control_password)
|
||||
|
||||
|
||||
def make_rpc_func(bin_dir, data_dir, chain):
|
||||
bin_dir = bin_dir
|
||||
data_dir = data_dir
|
||||
chain = chain
|
||||
|
||||
def rpc_func(cmd):
|
||||
nonlocal bin_dir
|
||||
nonlocal data_dir
|
||||
nonlocal chain
|
||||
|
||||
return callrpc_cli(bin_dir, data_dir, chain, cmd, cfg.PARTICL_CLI)
|
||||
return rpc_func
|
||||
|
||||
|
||||
def exitWithError(error_msg):
|
||||
sys.stderr.write('Error: {}, exiting.\n'.format(error_msg))
|
||||
sys.exit(1)
|
||||
@@ -655,13 +796,14 @@ def printHelp():
|
||||
logger.info('Usage: basicswap-prepare ')
|
||||
logger.info('\n--help, -h Print help.')
|
||||
logger.info('--version, -v Print version.')
|
||||
logger.info('--datadir=PATH Path to basicswap data directory, default:{}.'.format(cfg.DEFAULT_DATADIR))
|
||||
logger.info('--datadir=PATH Path to basicswap data directory, default:{}.'.format(cfg.BASICSWAP_DATADIR))
|
||||
logger.info('--bindir=PATH Path to cores directory, default:datadir/bin.')
|
||||
logger.info('--mainnet Run in mainnet mode.')
|
||||
logger.info('--testnet Run in testnet mode.')
|
||||
logger.info('--regtest Run in regtest mode.')
|
||||
logger.info('--particl_mnemonic= Recovery phrase to use for the Particl wallet, default is randomly generated,\n'
|
||||
+ ' "none" to set autogenerate account mode.')
|
||||
+ ' "auto" to create a wallet automatically - No mnemonic.'
|
||||
+ ' "none" to disable wallet initialisation.')
|
||||
logger.info('--withcoin= Prepare system to run daemon for coin.')
|
||||
logger.info('--withoutcoin= Do not prepare system to run daemon for coin.')
|
||||
logger.info('--addcoin= Add coin to existing setup.')
|
||||
@@ -670,33 +812,121 @@ def printHelp():
|
||||
logger.info('--nocores Don\'t download and extract any coin clients.')
|
||||
logger.info('--usecontainers Expect each core to run in a unique container.')
|
||||
logger.info('--portoffset=n Raise all ports by n.')
|
||||
logger.info('--htmlhost= Interface to host on, default:127.0.0.1.')
|
||||
logger.info('--htmlhost= Interface to host html server on, default:127.0.0.1.')
|
||||
logger.info('--wshost= Interface to host websocket server on, disable by setting to "none", default:127.0.0.1.')
|
||||
logger.info('--xmrrestoreheight=n Block height to restore Monero wallet from, default:{}.'.format(DEFAULT_XMR_RESTORE_HEIGHT))
|
||||
logger.info('--noextractover Prevent extracting cores if files exist. Speeds up tests')
|
||||
logger.info('--usetorproxy Use TOR proxy. Note that some download links may be inaccessible over TOR.')
|
||||
logger.info('--usetorproxy Use TOR proxy during setup. Note that some download links may be inaccessible over TOR.')
|
||||
logger.info('--enabletor Setup Basicswap instance to use TOR.')
|
||||
logger.info('--disabletor Setup Basicswap instance to not use TOR.')
|
||||
logger.info('--usebtcfastsync Initialise the BTC chain with a snapshot from btcpayserver FastSync.\n'
|
||||
+ ' See https://github.com/btcpayserver/btcpayserver-docker/blob/master/contrib/FastSync/README.md')
|
||||
logger.info('--initwalletsonly Setup coin wallets only.')
|
||||
logger.info('--keysdirpath Speed up tests by preloading all PGP keys in directory.')
|
||||
|
||||
logger.info('\n' + 'Known coins: %s', ', '.join(known_coins.keys()))
|
||||
|
||||
|
||||
def finalise_daemon(d):
|
||||
logging.info('Interrupting {}'.format(d.pid))
|
||||
d.send_signal(signal.SIGINT)
|
||||
d.wait(timeout=120)
|
||||
for fp in (d.stdout, d.stderr, d.stdin):
|
||||
if fp:
|
||||
fp.close()
|
||||
|
||||
|
||||
def initialise_wallets(particl_wallet_mnemonic, with_coins, data_dir, settings, chain, use_tor_proxy):
|
||||
daemons = []
|
||||
daemon_args = ['-noconnect', '-nodnsseed']
|
||||
if not use_tor_proxy:
|
||||
# Cannot set -bind or -whitebind together with -listen=0
|
||||
daemon_args.append('-nolisten')
|
||||
try:
|
||||
with open(os.path.join(data_dir, 'basicswap.log'), 'a') as fp:
|
||||
swap_client = BasicSwap(fp, data_dir, settings, chain)
|
||||
|
||||
start_daemons = {c for c in with_coins}
|
||||
if 'particl' not in with_coins:
|
||||
# Particl must be running to initialise a wallet in addcoin mode
|
||||
start_daemons.add('particl')
|
||||
|
||||
for coin_name in start_daemons:
|
||||
coin_settings = settings['chainclients'][coin_name]
|
||||
c = swap_client.getCoinIdFromName(coin_name)
|
||||
|
||||
if c == Coins.XMR:
|
||||
if coin_settings['manage_wallet_daemon']:
|
||||
daemons.append(startXmrWalletDaemon(coin_settings['datadir'], coin_settings['bindir'], 'monero-wallet-rpc'))
|
||||
else:
|
||||
if coin_settings['manage_daemon']:
|
||||
filename = coin_name + 'd' + ('.exe' if os.name == 'nt' else '')
|
||||
coin_args = ['-nofindpeers', '-nostaking'] if c == Coins.PART else []
|
||||
daemons.append(startDaemon(coin_settings['datadir'], coin_settings['bindir'], filename, daemon_args + coin_args))
|
||||
swap_client.setDaemonPID(c, daemons[-1].pid)
|
||||
swap_client.setCoinRunParams(c)
|
||||
swap_client.createCoinInterface(c)
|
||||
|
||||
if c in (Coins.PART, Coins.BTC, Coins.LTC, Coins.PIVX):
|
||||
swap_client.waitForDaemonRPC(c, with_wallet=False)
|
||||
# Create wallet if it doesn't exist yet
|
||||
wallets = swap_client.callcoinrpc(c, 'listwallets')
|
||||
if len(wallets) < 1:
|
||||
logger.info('Creating wallet.dat for {}.'.format(coin_name.capitalize()))
|
||||
swap_client.callcoinrpc(c, 'createwallet', ['wallet.dat'])
|
||||
|
||||
if 'particl' in with_coins:
|
||||
logger.info('Loading Particl mnemonic')
|
||||
if particl_wallet_mnemonic is None:
|
||||
particl_wallet_mnemonic = swap_client.callcoinrpc(Coins.PART, 'mnemonic', ['new'])['mnemonic']
|
||||
swap_client.callcoinrpc(Coins.PART, 'extkeyimportmaster', [particl_wallet_mnemonic])
|
||||
|
||||
for coin_name in with_coins:
|
||||
c = swap_client.getCoinIdFromName(coin_name)
|
||||
if c == Coins.PART:
|
||||
continue
|
||||
swap_client.waitForDaemonRPC(c)
|
||||
swap_client.initialiseWallet(c)
|
||||
|
||||
swap_client.finalise()
|
||||
del swap_client
|
||||
finally:
|
||||
for d in daemons:
|
||||
finalise_daemon(d)
|
||||
|
||||
if particl_wallet_mnemonic is not None:
|
||||
if particl_wallet_mnemonic:
|
||||
# Print directly to stdout for tests
|
||||
print('IMPORTANT - Save your particl wallet recovery phrase:\n{}\n'.format(particl_wallet_mnemonic))
|
||||
|
||||
|
||||
def load_config(config_path):
|
||||
if not os.path.exists(config_path):
|
||||
exitWithError('{} does not exist'.format(config_path))
|
||||
with open(config_path) as fs:
|
||||
return json.load(fs)
|
||||
|
||||
|
||||
def main():
|
||||
global extract_core_overwrite
|
||||
global use_tor_proxy
|
||||
data_dir = None
|
||||
bin_dir = None
|
||||
port_offset = None
|
||||
chain = 'mainnet'
|
||||
particl_wallet_mnemonic = None
|
||||
prepare_bin_only = False
|
||||
no_cores = False
|
||||
use_containers = False
|
||||
with_coins = {'particl', }
|
||||
add_coin = ''
|
||||
disable_coin = ''
|
||||
htmlhost = '127.0.0.1'
|
||||
wshost = '127.0.0.1'
|
||||
xmr_restore_height = DEFAULT_XMR_RESTORE_HEIGHT
|
||||
prepare_bin_only = False
|
||||
no_cores = False
|
||||
enable_tor = False
|
||||
disable_tor = False
|
||||
initwalletsonly = False
|
||||
tor_control_password = None
|
||||
extra_opts = {}
|
||||
|
||||
for v in sys.argv[1:]:
|
||||
if len(v) < 2 or v[0] != '-':
|
||||
@@ -730,10 +960,10 @@ def main():
|
||||
no_cores = True
|
||||
continue
|
||||
if name == 'usecontainers':
|
||||
use_containers = True
|
||||
extra_opts['use_containers'] = True
|
||||
continue
|
||||
if name == 'noextractover':
|
||||
extract_core_overwrite = False
|
||||
extra_opts['extract_core_overwrite'] = False
|
||||
continue
|
||||
if name == 'usetorproxy':
|
||||
use_tor_proxy = True
|
||||
@@ -744,6 +974,12 @@ def main():
|
||||
if name == 'disabletor':
|
||||
disable_tor = True
|
||||
continue
|
||||
if name == 'usebtcfastsync':
|
||||
extra_opts['use_btc_fastsync'] = True
|
||||
continue
|
||||
if name == 'initwalletsonly':
|
||||
initwalletsonly = True
|
||||
continue
|
||||
if len(s) == 2:
|
||||
if name == 'datadir':
|
||||
data_dir = os.path.expanduser(s[1].strip('"'))
|
||||
@@ -775,7 +1011,7 @@ def main():
|
||||
if s[1] not in known_coins:
|
||||
exitWithError('Unknown coin {}'.format(s[1]))
|
||||
add_coin = s[1]
|
||||
with_coins = [add_coin, ]
|
||||
with_coins = {add_coin, }
|
||||
continue
|
||||
if name == 'disablecoin':
|
||||
if s[1] not in known_coins:
|
||||
@@ -785,9 +1021,15 @@ def main():
|
||||
if name == 'htmlhost':
|
||||
htmlhost = s[1].strip('"')
|
||||
continue
|
||||
if name == 'wshost':
|
||||
wshost = s[1].strip('"')
|
||||
continue
|
||||
if name == 'xmrrestoreheight':
|
||||
xmr_restore_height = int(s[1])
|
||||
continue
|
||||
if name == 'keysdirpath':
|
||||
extra_opts['keysdirpath'] = os.path.expanduser(s[1].strip('"'))
|
||||
continue
|
||||
|
||||
exitWithError('Unknown argument {}'.format(v))
|
||||
|
||||
@@ -800,7 +1042,7 @@ def main():
|
||||
testOnionLink()
|
||||
|
||||
if data_dir is None:
|
||||
data_dir = os.path.join(os.path.expanduser(cfg.DEFAULT_DATADIR))
|
||||
data_dir = os.path.join(os.path.expanduser(cfg.BASICSWAP_DATADIR))
|
||||
if bin_dir is None:
|
||||
bin_dir = os.path.join(data_dir, 'bin')
|
||||
|
||||
@@ -828,7 +1070,7 @@ def main():
|
||||
'blocks_confirmed': 2,
|
||||
'override_feerate': 0.002,
|
||||
'conf_target': 2,
|
||||
'core_version_group': 18,
|
||||
'core_version_group': 21,
|
||||
'chain_lookups': 'local',
|
||||
},
|
||||
'litecoin': {
|
||||
@@ -842,7 +1084,7 @@ def main():
|
||||
'use_segwit': True,
|
||||
'blocks_confirmed': 2,
|
||||
'conf_target': 2,
|
||||
'core_version_group': 18,
|
||||
'core_version_group': 21,
|
||||
'chain_lookups': 'local',
|
||||
},
|
||||
'bitcoin': {
|
||||
@@ -856,7 +1098,7 @@ def main():
|
||||
'use_segwit': True,
|
||||
'blocks_confirmed': 1,
|
||||
'conf_target': 2,
|
||||
'core_version_group': 18,
|
||||
'core_version_group': 22,
|
||||
'chain_lookups': 'local',
|
||||
},
|
||||
'namecoin': {
|
||||
@@ -889,6 +1131,21 @@ def main():
|
||||
'bindir': os.path.join(bin_dir, 'monero'),
|
||||
'restore_height': xmr_restore_height,
|
||||
'blocks_confirmed': 7, # TODO: 10?
|
||||
},
|
||||
'pivx': {
|
||||
'connection_type': 'rpc' if 'pivx' in with_coins else 'none',
|
||||
'manage_daemon': True if ('pivx' in with_coins and PIVX_RPC_HOST == '127.0.0.1') else False,
|
||||
'rpchost': PIVX_RPC_HOST,
|
||||
'rpcport': PIVX_RPC_PORT + port_offset,
|
||||
'onionport': PIVX_ONION_PORT + port_offset,
|
||||
'datadir': os.getenv('PIVX_DATA_DIR', os.path.join(data_dir, 'pivx')),
|
||||
'bindir': os.path.join(bin_dir, 'pivx'),
|
||||
'use_segwit': False,
|
||||
'use_csv': False,
|
||||
'blocks_confirmed': 1,
|
||||
'conf_target': 2,
|
||||
'core_version_group': 20,
|
||||
'chain_lookups': 'local',
|
||||
}
|
||||
}
|
||||
|
||||
@@ -901,16 +1158,26 @@ def main():
|
||||
if BTC_RPC_USER != '':
|
||||
chainclients['bitcoin']['rpcuser'] = BTC_RPC_USER
|
||||
chainclients['bitcoin']['rpcpassword'] = BTC_RPC_PWD
|
||||
if PIVX_RPC_USER != '':
|
||||
chainclients['pivx']['rpcuser'] = PIVX_RPC_USER
|
||||
chainclients['pivx']['rpcpassword'] = PIVX_RPC_PWD
|
||||
|
||||
chainclients['monero']['walletsdir'] = os.getenv('XMR_WALLETS_DIR', chainclients['monero']['datadir'])
|
||||
|
||||
if initwalletsonly:
|
||||
logger.info('Initialising wallets')
|
||||
settings = load_config(config_path)
|
||||
|
||||
init_coins = settings['chainclients'].keys()
|
||||
logger.info('Active coins: %s', ', '.join(init_coins))
|
||||
initialise_wallets(particl_wallet_mnemonic, init_coins, data_dir, settings, chain, use_tor_proxy)
|
||||
|
||||
print('Done.')
|
||||
return 0
|
||||
|
||||
if enable_tor:
|
||||
logger.info('Enabling TOR')
|
||||
|
||||
if not os.path.exists(config_path):
|
||||
exitWithError('{} does not exist'.format(config_path))
|
||||
with open(config_path) as fs:
|
||||
settings = json.load(fs)
|
||||
settings = load_config(config_path)
|
||||
|
||||
tor_control_password = settings.get('tor_control_password', None)
|
||||
if tor_control_password is None:
|
||||
@@ -930,12 +1197,7 @@ def main():
|
||||
|
||||
if disable_tor:
|
||||
logger.info('Disabling TOR')
|
||||
|
||||
if not os.path.exists(config_path):
|
||||
exitWithError('{} does not exist'.format(config_path))
|
||||
with open(config_path) as fs:
|
||||
settings = json.load(fs)
|
||||
|
||||
settings = load_config(config_path)
|
||||
settings['use_tor'] = False
|
||||
for coin in settings['chainclients']:
|
||||
modify_tor_config(settings, coin, tor_control_password=None, enable=False)
|
||||
@@ -948,10 +1210,7 @@ def main():
|
||||
|
||||
if disable_coin != '':
|
||||
logger.info('Disabling coin: %s', disable_coin)
|
||||
if not os.path.exists(config_path):
|
||||
exitWithError('{} does not exist'.format(config_path))
|
||||
with open(config_path) as fs:
|
||||
settings = json.load(fs)
|
||||
settings = load_config(config_path)
|
||||
|
||||
if disable_coin not in settings['chainclients']:
|
||||
exitWithError('{} has not been prepared'.format(disable_coin))
|
||||
@@ -964,12 +1223,12 @@ def main():
|
||||
logger.info('Done.')
|
||||
return 0
|
||||
|
||||
extra_opts['data_dir'] = data_dir
|
||||
extra_opts['tor_control_password'] = tor_control_password
|
||||
|
||||
if add_coin != '':
|
||||
logger.info('Adding coin: %s', add_coin)
|
||||
if not os.path.exists(config_path):
|
||||
exitWithError('{} does not exist'.format(config_path))
|
||||
with open(config_path) as fs:
|
||||
settings = json.load(fs)
|
||||
settings = load_config(config_path)
|
||||
|
||||
if add_coin in settings['chainclients']:
|
||||
coin_settings = settings['chainclients'][add_coin]
|
||||
@@ -987,10 +1246,14 @@ def main():
|
||||
settings['use_tor_proxy'] = use_tor_proxy
|
||||
|
||||
if not no_cores:
|
||||
prepareCore(add_coin, known_coins[add_coin], settings, data_dir)
|
||||
prepareCore(add_coin, known_coins[add_coin], settings, data_dir, extra_opts)
|
||||
|
||||
if not prepare_bin_only:
|
||||
prepareDataDir(add_coin, settings, chain, particl_wallet_mnemonic, use_containers=use_containers)
|
||||
prepareDataDir(add_coin, settings, chain, particl_wallet_mnemonic, extra_opts)
|
||||
|
||||
if particl_wallet_mnemonic not in ('none', 'auto'):
|
||||
initialise_wallets(None, {add_coin, }, data_dir, settings, chain, use_tor_proxy)
|
||||
|
||||
with open(config_path, 'w') as fp:
|
||||
json.dump(settings, fp, indent=4)
|
||||
|
||||
@@ -1010,7 +1273,7 @@ def main():
|
||||
|
||||
settings = {
|
||||
'debug': True,
|
||||
'zmqhost': 'tcp://127.0.0.1',
|
||||
'zmqhost': f'tcp://{PART_RPC_HOST}',
|
||||
'zmqport': PART_ZMQ_PORT + port_offset,
|
||||
'htmlhost': htmlhost,
|
||||
'htmlport': UI_HTML_PORT + port_offset,
|
||||
@@ -1024,90 +1287,34 @@ def main():
|
||||
'check_expired_seconds': 60
|
||||
}
|
||||
|
||||
if wshost != 'none':
|
||||
settings['wshost'] = wshost
|
||||
settings['wsport'] = UI_WS_PORT + port_offset
|
||||
|
||||
if use_tor_proxy:
|
||||
tor_control_password = generate_salt(24)
|
||||
addTorSettings(settings, tor_control_password)
|
||||
|
||||
if not no_cores:
|
||||
for c in with_coins:
|
||||
prepareCore(c, known_coins[c], settings, data_dir)
|
||||
prepareCore(c, known_coins[c], settings, data_dir, extra_opts)
|
||||
|
||||
if prepare_bin_only:
|
||||
logger.info('Done.')
|
||||
return 0
|
||||
|
||||
for c in with_coins:
|
||||
prepareDataDir(c, settings, chain, particl_wallet_mnemonic, use_containers=use_containers, tor_control_password=tor_control_password)
|
||||
prepareDataDir(c, settings, chain, particl_wallet_mnemonic, extra_opts)
|
||||
|
||||
with open(config_path, 'w') as fp:
|
||||
json.dump(settings, fp, indent=4)
|
||||
|
||||
if particl_wallet_mnemonic == 'none':
|
||||
if particl_wallet_mnemonic in ('none', 'auto'):
|
||||
logger.info('Done.')
|
||||
return 0
|
||||
|
||||
logger.info('Loading Particl mnemonic')
|
||||
|
||||
particl_settings = settings['chainclients']['particl']
|
||||
partRpc = make_rpc_func(particl_settings['bindir'], particl_settings['datadir'], chain)
|
||||
|
||||
daemons = []
|
||||
daemon_args = ['-noconnect', '-nodnsseed']
|
||||
if not use_tor_proxy:
|
||||
# Cannot set -bind or -whitebind together with -listen=0
|
||||
daemon_args.append('-nolisten')
|
||||
daemons.append(startDaemon(particl_settings['datadir'], particl_settings['bindir'], cfg.PARTICLD, daemon_args + ['-nofindpeers', '-nostaking']))
|
||||
try:
|
||||
waitForRPC(partRpc)
|
||||
|
||||
if particl_wallet_mnemonic is None:
|
||||
particl_wallet_mnemonic = partRpc('mnemonic new')['mnemonic']
|
||||
partRpc('extkeyimportmaster "{}"'.format(particl_wallet_mnemonic))
|
||||
|
||||
# Initialise wallets
|
||||
with open(os.path.join(data_dir, 'basicswap.log'), 'a') as fp:
|
||||
swap_client = BasicSwap(fp, data_dir, settings, chain)
|
||||
|
||||
swap_client.setCoinConnectParams(Coins.PART)
|
||||
swap_client.setDaemonPID(Coins.PART, daemons[-1].pid)
|
||||
swap_client.setCoinRunParams(Coins.PART)
|
||||
swap_client.createCoinInterface(Coins.PART)
|
||||
|
||||
for coin_name in with_coins:
|
||||
coin_settings = settings['chainclients'][coin_name]
|
||||
c = swap_client.getCoinIdFromName(coin_name)
|
||||
if c == Coins.PART:
|
||||
continue
|
||||
|
||||
swap_client.setCoinConnectParams(c)
|
||||
|
||||
if c == Coins.XMR:
|
||||
if not coin_settings['manage_wallet_daemon']:
|
||||
continue
|
||||
daemons.append(startXmrWalletDaemon(coin_settings['datadir'], coin_settings['bindir'], 'monero-wallet-rpc'))
|
||||
else:
|
||||
if not coin_settings['manage_daemon']:
|
||||
continue
|
||||
filename = coin_name + 'd' + ('.exe' if os.name == 'nt' else '')
|
||||
daemons.append(startDaemon(coin_settings['datadir'], coin_settings['bindir'], filename, daemon_args))
|
||||
swap_client.setDaemonPID(c, daemons[-1].pid)
|
||||
swap_client.setCoinRunParams(c)
|
||||
swap_client.createCoinInterface(c)
|
||||
swap_client.waitForDaemonRPC(c)
|
||||
swap_client.initialiseWallet(c)
|
||||
swap_client.finalise()
|
||||
del swap_client
|
||||
finally:
|
||||
for d in daemons:
|
||||
logging.info('Interrupting {}'.format(d.pid))
|
||||
d.send_signal(signal.SIGINT)
|
||||
d.wait(timeout=120)
|
||||
for fp in (d.stdout, d.stderr, d.stdin):
|
||||
if fp:
|
||||
fp.close()
|
||||
|
||||
logger.info('IMPORTANT - Save your particl wallet recovery phrase:\n{}\n'.format(particl_wallet_mnemonic))
|
||||
logger.info('Done.')
|
||||
initialise_wallets(particl_wallet_mnemonic, with_coins, data_dir, settings, chain, use_tor_proxy)
|
||||
print('Done.')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -19,6 +19,7 @@ import basicswap.config as cfg
|
||||
from basicswap import __version__
|
||||
from basicswap.basicswap import BasicSwap
|
||||
from basicswap.http_server import HttpThread
|
||||
from basicswap.contrib.websocket_server import WebsocketServer
|
||||
|
||||
|
||||
logger = logging.getLogger()
|
||||
@@ -93,6 +94,25 @@ def startXmrWalletDaemon(node_dir, bin_dir, wallet_bin, opts=[]):
|
||||
return subprocess.Popen(args, stdin=subprocess.PIPE, stdout=wallet_stdout, stderr=wallet_stderr, cwd=data_dir)
|
||||
|
||||
|
||||
def ws_new_client(client, server):
|
||||
if swap_client:
|
||||
swap_client.log.debug(f'ws_new_client {client["id"]}')
|
||||
|
||||
|
||||
def ws_client_left(client, server):
|
||||
if client is None:
|
||||
return
|
||||
if swap_client:
|
||||
swap_client.log.debug(f'ws_client_left {client["id"]}')
|
||||
|
||||
|
||||
def ws_message_received(client, server, message):
|
||||
if len(message) > 200:
|
||||
message = message[:200] + '..'
|
||||
if swap_client:
|
||||
swap_client.log.debug(f'ws_message_received {client["id"]} {message}')
|
||||
|
||||
|
||||
def runClient(fp, data_dir, chain):
|
||||
global swap_client
|
||||
settings_path = os.path.join(data_dir, cfg.CONFIG_FILENAME)
|
||||
@@ -158,24 +178,45 @@ def runClient(fp, data_dir, chain):
|
||||
swap_client.start()
|
||||
|
||||
if 'htmlhost' in settings:
|
||||
swap_client.log.info('Starting server at http://%s:%d.' % (settings['htmlhost'], settings['htmlport']))
|
||||
swap_client.log.info('Starting http server at http://%s:%d.' % (settings['htmlhost'], settings['htmlport']))
|
||||
allow_cors = settings['allowcors'] if 'allowcors' in settings else cfg.DEFAULT_ALLOW_CORS
|
||||
tS1 = HttpThread(fp, settings['htmlhost'], settings['htmlport'], allow_cors, swap_client)
|
||||
threads.append(tS1)
|
||||
tS1.start()
|
||||
thread_http = HttpThread(fp, settings['htmlhost'], settings['htmlport'], allow_cors, swap_client)
|
||||
threads.append(thread_http)
|
||||
thread_http.start()
|
||||
|
||||
if 'wshost' in settings:
|
||||
ws_url = 'ws://{}:{}'.format(settings['wshost'], settings['wsport'])
|
||||
swap_client.log.info(f'Starting ws server at {ws_url}.')
|
||||
|
||||
swap_client.ws_server = WebsocketServer(host=settings['wshost'], port=settings['wsport'])
|
||||
swap_client.ws_server.set_fn_new_client(ws_new_client)
|
||||
swap_client.ws_server.set_fn_client_left(ws_client_left)
|
||||
swap_client.ws_server.set_fn_message_received(ws_message_received)
|
||||
swap_client.ws_server.run_forever(threaded=True)
|
||||
|
||||
logger.info('Exit with Ctrl + c.')
|
||||
while swap_client.is_running:
|
||||
time.sleep(0.5)
|
||||
swap_client.update()
|
||||
|
||||
except Exception as ex:
|
||||
traceback.print_exc()
|
||||
|
||||
if swap_client.ws_server:
|
||||
try:
|
||||
swap_client.log.info('Stopping websocket server.')
|
||||
swap_client.ws_server.shutdown_gracefully()
|
||||
except Exception as ex:
|
||||
traceback.print_exc()
|
||||
|
||||
swap_client.finalise()
|
||||
swap_client.log.info('Stopping HTTP threads.')
|
||||
for t in threads:
|
||||
t.stop()
|
||||
t.join()
|
||||
try:
|
||||
t.stop()
|
||||
t.join()
|
||||
except Exception as ex:
|
||||
traceback.print_exc()
|
||||
|
||||
closed_pids = []
|
||||
for d in daemons:
|
||||
@@ -216,7 +257,7 @@ def printHelp():
|
||||
logger.info('Usage: basicswap-run ')
|
||||
logger.info('\n--help, -h Print help.')
|
||||
logger.info('--version, -v Print version.')
|
||||
logger.info('--datadir=PATH Path to basicswap data directory, default:{}.'.format(cfg.DEFAULT_DATADIR))
|
||||
logger.info('--datadir=PATH Path to basicswap data directory, default:{}.'.format(cfg.BASICSWAP_DATADIR))
|
||||
logger.info('--mainnet Run in mainnet mode.')
|
||||
logger.info('--testnet Run in testnet mode.')
|
||||
logger.info('--regtest Run in regtest mode.')
|
||||
@@ -260,7 +301,7 @@ def main():
|
||||
logger.warning('Unknown argument %s', v)
|
||||
|
||||
if data_dir is None:
|
||||
data_dir = os.path.join(os.path.expanduser(cfg.DEFAULT_DATADIR))
|
||||
data_dir = os.path.join(os.path.expanduser(cfg.BASICSWAP_DATADIR))
|
||||
logger.info('Using datadir: %s', data_dir)
|
||||
logger.info('Chain: %s', chain)
|
||||
|
||||
|
||||
43
bin/install_certifi.py
Executable file
43
bin/install_certifi.py
Executable file
@@ -0,0 +1,43 @@
|
||||
# install_certifi.py
|
||||
#
|
||||
# sample script to install or update a set of default Root Certificates
|
||||
# for the ssl module. Uses the certificates provided by the certifi package:
|
||||
# https://pypi.org/project/certifi/
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import ssl
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
STAT_0o775 = ( stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR
|
||||
| stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP
|
||||
| stat.S_IROTH | stat.S_IXOTH )
|
||||
|
||||
def main():
|
||||
openssl_dir, openssl_cafile = os.path.split(
|
||||
ssl.get_default_verify_paths().openssl_cafile)
|
||||
|
||||
print(" -- pip install --upgrade certifi")
|
||||
subprocess.check_call([sys.executable,
|
||||
"-E", "-s", "-m", "pip", "install", "--upgrade", "certifi"])
|
||||
|
||||
import certifi
|
||||
|
||||
# change working directory to the default SSL directory
|
||||
os.chdir(openssl_dir)
|
||||
relpath_to_certifi_cafile = os.path.relpath(certifi.where())
|
||||
print(" -- removing any existing file or link")
|
||||
try:
|
||||
os.remove(openssl_cafile)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
print(" -- creating symlink to certifi certificate bundle")
|
||||
os.symlink(relpath_to_certifi_cafile, openssl_cafile)
|
||||
print(" -- setting permissions")
|
||||
os.chmod(openssl_cafile, STAT_0o775)
|
||||
print(" -- update complete")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
148
doc/install.md
148
doc/install.md
@@ -1,35 +1,74 @@
|
||||
|
||||
## Source code
|
||||
|
||||
$ git clone https://github.com/tecnovert/basicswap.git
|
||||
git clone https://github.com/tecnovert/basicswap.git
|
||||
|
||||
|
||||
## Run Using Docker
|
||||
|
||||
|
||||
Install dependencies:
|
||||
|
||||
apt-get curl jq
|
||||
|
||||
|
||||
Docker must be installed and started:
|
||||
|
||||
$ docker -v
|
||||
docker -v
|
||||
|
||||
Should return a line containing `Docker version`...
|
||||
|
||||
|
||||
To install docker engine on your platform see:
|
||||
|
||||
https://docs.docker.com/engine/install/#server
|
||||
|
||||
|
||||
It's recommended to setup docker to work without sudo.<br>
|
||||
Without this step you will need to preface each `docker-compose` command with `sudo`:
|
||||
|
||||
https://docs.docker.com/engine/install/linux-postinstall/
|
||||
|
||||
|
||||
#### Create the images:
|
||||
|
||||
$ cd basicswap/docker
|
||||
$ docker-compose build
|
||||
COINDATA_PATH can be set to your preferance but must be exported each time you launch Basicswap.<br>
|
||||
Consider adding COINDATA_PATH to the `.env` file in the docker directory file so it's always set.
|
||||
|
||||
export COINDATA_PATH=/var/data/coinswaps
|
||||
cd basicswap/docker
|
||||
docker-compose build
|
||||
|
||||
|
||||
#### Prepare the datadir:
|
||||
|
||||
Set XMR_RPC_HOST and BASE_XMR_RPC_PORT to a public XMR node or exclude to run a local node.
|
||||
Set xmrrestoreheight to the current xmr chain height.
|
||||
|
||||
CURRENT_XMR_HEIGHT=$(curl https://localmonero.co/blocks/api/get_stats | jq .height)
|
||||
|
||||
Adjust `--withcoins` and `--withoutcoins` as desired, eg: `--withcoins=monero,bitcoin`. By default only Particl is loaded.
|
||||
|
||||
$ export COINDATA_PATH=/var/data/coinswaps
|
||||
$ docker run --rm -e XMR_RPC_HOST="node.xmr.to" -e BASE_XMR_RPC_PORT=18081 -t --name swap_prepare -v $COINDATA_PATH:/coindata i_swapclient \
|
||||
basicswap-prepare --datadir=/coindata --withcoins=monero --htmlhost="0.0.0.0" --xmrrestoreheight=2485205
|
||||
##### FastSync
|
||||
|
||||
Append `--usebtcfastsync` to the below command to optionally initialise the Bitcoin datadir with a chain snapshot from btcpayserver FastSync.<br>
|
||||
[FastSync README.md](https://github.com/btcpayserver/btcpayserver-docker/blob/master/contrib/FastSync/README.md)
|
||||
|
||||
|
||||
Setup with a local Monero daemon (recommended):
|
||||
|
||||
export COINDATA_PATH=/var/data/coinswaps
|
||||
docker run --rm -t --name swap_prepare -v $COINDATA_PATH:/coindata i_swapclient basicswap-prepare --datadir=/coindata --withcoins=monero --htmlhost="0.0.0.0" --wshost="0.0.0.0" --xmrrestoreheight=$CURRENT_XMR_HEIGHT
|
||||
|
||||
|
||||
To instead use Monero public nodes and not run a local Monero daemon<br>(it can be difficult to find reliable public nodes):
|
||||
|
||||
Set XMR_RPC_HOST and BASE_XMR_RPC_PORT to a public XMR node.
|
||||
export COINDATA_PATH=/var/data/coinswaps
|
||||
docker run --rm -e XMR_RPC_HOST="node.xmr.to" -e BASE_XMR_RPC_PORT=18081 -t --name swap_prepare -v $COINDATA_PATH:/coindata i_swapclient basicswap-prepare --datadir=/coindata --withcoins=monero --htmlhost="0.0.0.0" --wshost="0.0.0.0" --xmrrestoreheight=$CURRENT_XMR_HEIGHT
|
||||
|
||||
|
||||
**Record the mnemonic from the output of the above command.**
|
||||
And the output of `echo $CURRENT_XMR_HEIGHT` for use if you need to later restore your wallet.
|
||||
|
||||
|
||||
#### Set the timezone (optional):
|
||||
@@ -40,8 +79,8 @@ Valid options can be listed with: `timedatectl list-timezones`
|
||||
|
||||
#### Start the container:
|
||||
|
||||
$ export COINDATA_PATH=/var/data/coinswaps
|
||||
$ docker-compose up
|
||||
export COINDATA_PATH=/var/data/coinswaps
|
||||
docker-compose up
|
||||
|
||||
Open in browser: `http://localhost:12700`
|
||||
|
||||
@@ -49,9 +88,9 @@ Open in browser: `http://localhost:12700`
|
||||
|
||||
### Add a coin
|
||||
|
||||
$ docker-compose stop
|
||||
$ export COINDATA_PATH=/var/data/coinswaps
|
||||
$ docker run --rm -t --name swap_prepare -v $COINDATA_PATH:/coindata i_swapclient basicswap-prepare --datadir=/coindata --addcoin=bitcoin
|
||||
docker-compose stop
|
||||
export COINDATA_PATH=/var/data/coinswaps
|
||||
docker run --rm -t --name swap_prepare -v $COINDATA_PATH:/coindata i_swapclient basicswap-prepare --datadir=/coindata --addcoin=bitcoin
|
||||
|
||||
You can copy an existing pruned datadir (excluding bitcoin.conf and any wallets) over to `$COINDATA_PATH/bitcoin`
|
||||
Remove any existing wallets after copying over a pruned chain or the Bitcoin daemon won't start.
|
||||
@@ -70,14 +109,14 @@ Windows key + R -> "wsl" -> Enter
|
||||
|
||||
Install Git:
|
||||
|
||||
$ sudo apt update
|
||||
$ sudo apt install git
|
||||
sudo apt update
|
||||
sudo apt install git
|
||||
|
||||
|
||||
Download the BasicSwap code:
|
||||
|
||||
$ git clone https://github.com/tecnovert/basicswap.git
|
||||
$ cd basicswap/docker/
|
||||
git clone https://github.com/tecnovert/basicswap.git
|
||||
cd basicswap/docker/
|
||||
|
||||
|
||||
It's significantly faster to set COINDATA_PATH in the linux filesystem.
|
||||
@@ -91,61 +130,78 @@ Continue from the [Run Using Docker](#run-using-docker) section.
|
||||
|
||||
### Ubuntu Setup:
|
||||
|
||||
$ apt-get install -y wget python3-pip gnupg unzip protobuf-compiler automake libtool pkg-config
|
||||
apt-get install -y wget python3-pip gnupg unzip protobuf-compiler automake libtool pkg-config curl jq
|
||||
|
||||
### OSX Setup:
|
||||
|
||||
Install Homebrew:
|
||||
Install Homebrew (See https://brew.sh/):
|
||||
|
||||
https://brew.sh/
|
||||
|
||||
Command Line Tools:
|
||||
|
||||
$ xcode-select --install
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
|
||||
Dependencies:
|
||||
|
||||
$ brew install wget unzip python git protobuf gnupg automake libtool pkg-config
|
||||
brew install wget unzip python git protobuf gnupg automake libtool pkg-config curl jq
|
||||
|
||||
Close the terminal and open a new one to update the python symlinks.
|
||||
|
||||
|
||||
### Basicswap:
|
||||
|
||||
$ export SWAP_DATADIR=/Users/$USER/coinswaps
|
||||
$ mkdir -p "$SWAP_DATADIR/venv"
|
||||
$ python3 -m venv "$SWAP_DATADIR/venv"
|
||||
$ . $SWAP_DATADIR/venv/bin/activate && python -V
|
||||
$ cd $SWAP_DATADIR
|
||||
$ wget -O coincurve-anonswap.zip https://github.com/tecnovert/coincurve/archive/anonswap.zip
|
||||
$ unzip coincurve-anonswap.zip
|
||||
$ cd $SWAP_DATADIR/coincurve-anonswap
|
||||
$ pip3 install .
|
||||
export SWAP_DATADIR=/Users/$USER/coinswaps
|
||||
mkdir -p "$SWAP_DATADIR/venv"
|
||||
python3 -m venv "$SWAP_DATADIR/venv"
|
||||
. $SWAP_DATADIR/venv/bin/activate && python -V
|
||||
cd $SWAP_DATADIR
|
||||
wget -O coincurve-anonswap.zip https://github.com/tecnovert/coincurve/archive/refs/tags/anonswap_v0.1.zip
|
||||
unzip -d coincurve-anonswap coincurve-anonswap.zip
|
||||
mv ./coincurve-anonswap/*/{.,}* ./coincurve-anonswap || true
|
||||
cd $SWAP_DATADIR/coincurve-anonswap
|
||||
pip3 install .
|
||||
|
||||
|
||||
$ cd $SWAP_DATADIR
|
||||
$ git clone https://github.com/tecnovert/basicswap.git
|
||||
$ cd $SWAP_DATADIR/basicswap
|
||||
$ protoc -I=basicswap --python_out=basicswap basicswap/messages.proto
|
||||
$ pip3 install .
|
||||
cd $SWAP_DATADIR
|
||||
git clone https://github.com/tecnovert/basicswap.git
|
||||
cd $SWAP_DATADIR/basicswap
|
||||
|
||||
If installed on OSX, you may need to install additional root ssl certificates for the ssl module.
|
||||
From https://pypi.org/project/certifi/
|
||||
|
||||
sudo python3 bin/install_certifi.py
|
||||
|
||||
|
||||
Continue installing Basicswap
|
||||
|
||||
protoc -I=basicswap --python_out=basicswap basicswap/messages.proto
|
||||
pip3 install .
|
||||
|
||||
|
||||
Prepare the datadir:
|
||||
|
||||
XMR_RPC_HOST="node.xmr.to" BASE_XMR_RPC_PORT=18081 basicswap-prepare --datadir=$SWAP_DATADIR --withcoins=monero --xmrrestoreheight=2245107
|
||||
CURRENT_XMR_HEIGHT=$(curl https://localmonero.co/blocks/api/get_stats | jq .height)
|
||||
|
||||
XMR_RPC_HOST="node.xmr.to" BASE_XMR_RPC_PORT=18081 basicswap-prepare --datadir=$SWAP_DATADIR --withcoins=monero --xmrrestoreheight=$CURRENT_XMR_HEIGHT
|
||||
|
||||
OR using a local XMR daemon:
|
||||
basicswap-prepare --datadir=$SWAP_DATADIR --withcoins=monero --xmrrestoreheight=2245107
|
||||
basicswap-prepare --datadir=$SWAP_DATADIR --withcoins=monero --xmrrestoreheight=$CURRENT_XMR_HEIGHT
|
||||
|
||||
Record the mnemonic from the output of the above command.
|
||||
|
||||
Start the app
|
||||
Start Basicswap:
|
||||
|
||||
$ basicswap-run --datadir=$SWAP_DATADIR
|
||||
basicswap-run --datadir=$SWAP_DATADIR
|
||||
|
||||
Open in browser: `http://localhost:12700`
|
||||
It may take a few minutes to start as the coin daemons are started before the http interface.
|
||||
|
||||
|
||||
Add a coin (Stop basicswap first):
|
||||
|
||||
export SWAP_DATADIR=/Users/$USER/coinswaps
|
||||
basicswap-prepare --usebtcfastsync --datadir=/$SWAP_DATADIR --addcoin=bitcoin
|
||||
|
||||
|
||||
Start after installed:
|
||||
|
||||
$ export SWAP_DATADIR=/Users/$USER/coinswaps
|
||||
$ . $SWAP_DATADIR/venv/bin/activate && python -V
|
||||
$ basicswap-run --datadir=$SWAP_DATADIR
|
||||
export SWAP_DATADIR=/Users/$USER/coinswaps
|
||||
. $SWAP_DATADIR/venv/bin/activate && python -V
|
||||
basicswap-run --datadir=$SWAP_DATADIR
|
||||
|
||||
@@ -34,7 +34,7 @@ Both coin types must support scripts.
|
||||
- submits participate tx in coin-to network
|
||||
|
||||
5. Seller redeems:
|
||||
- constructs participatescript
|
||||
- constructs participate script
|
||||
- inspects participate tx in coin-to network
|
||||
- redeems from participate tx revealing secret
|
||||
|
||||
|
||||
1
doc/protocols/sequence_diagrams/.gitignore
vendored
Normal file
1
doc/protocols/sequence_diagrams/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.svg
|
||||
66
doc/protocols/sequence_diagrams/bidder.alt.xu
Normal file
66
doc/protocols/sequence_diagrams/bidder.alt.xu
Normal file
@@ -0,0 +1,66 @@
|
||||
xu {
|
||||
hscale = "1.2";
|
||||
|
||||
CB [label=" ", linecolor="transparent"],
|
||||
N [label="Network", linecolor="#008800", textbgcolor="#CCFFCC", arclinecolor="#008800"],
|
||||
O [label="Offerer", linecolor="#FF0000", textbgcolor="#FFCCCC", arclinecolor="#FF0000"],
|
||||
B [label="Bidder", linecolor="#0000FF", textbgcolor="#CCCCFF", arclinecolor="#0000FF"],
|
||||
C [label=" ", linecolor="transparent"], C2 [label=" ", linecolor="transparent"];
|
||||
O =>> N [label="Sends Offer"];
|
||||
N >> B [label="Detects Offer"];
|
||||
B =>> O [label="Sends Bid"];
|
||||
B abox B [label="Bid Sent"];
|
||||
O box O [label="User accepts bid"];
|
||||
O =>> N [label="Sends Initiate Tx"],
|
||||
C note C2
|
||||
[label="Offerer generates secret_value and sends Hash(secret_value) to the Bidder",
|
||||
textbgcolor="#FFFFCC"];
|
||||
O =>> B [label="Sends BidAccept"],
|
||||
C note C2
|
||||
[label="ITX can be spent by
|
||||
knowledge of the secret_value and the bidder_redeem_key or
|
||||
after a timeout by the offerer_refund_key",
|
||||
textbgcolor="#FFFFCC"];
|
||||
|
||||
B abox B [label="Bid Accepted"];
|
||||
N >> B [label="Detects Initiate Tx"];
|
||||
B abox B [label="ITX Sent", textbgcolor="#4bdbf1"];
|
||||
B => B [label="Wait for ITX to confirm"], O => O [label="Wait for ITX to confirm"];
|
||||
B abox B [label="Bid Initiated"];
|
||||
B abox B [label="ITX Confirmed", textbgcolor="#4bdbf1"];
|
||||
B =>> N [label="Sends Participate Tx"],
|
||||
C note C2
|
||||
[label="PTX can be spent by
|
||||
knowledge of the secret_value and the offerer_redeem_key or
|
||||
after a timeout by the bidder_refund_key",
|
||||
textbgcolor="#FFFFCC"];
|
||||
B abox B [label="PTX Sent", textbgcolor="#f1db4b"];
|
||||
N >> O [label="Detects Participate Tx"];
|
||||
B => B [label="Wait for PTX to confirm"], O => O [label="Wait for PTX to confirm"];
|
||||
B abox B [label="PTX Confirmed", textbgcolor="#f1db4b"];
|
||||
B abox B [label="Bid Participating"];
|
||||
CB alt C [label="success path"] {
|
||||
O =>> N [label="Sends Participate Redeem Tx"],
|
||||
C note C2
|
||||
[label="Reveals secret_value",
|
||||
textbgcolor="#FFFFCC"];
|
||||
N >> B [label="Detects Participate Redeem Tx"];
|
||||
B abox B [label="PTX Redeemed", textbgcolor="#f1db4b"];
|
||||
B =>> N [label="Sends Initiate Redeem Tx"];
|
||||
B => B [label="Wait for ITX Redeem to confirm"];
|
||||
B abox B [label="ITX Redeemed", textbgcolor="#4bdbf1"];
|
||||
B abox B [label="Bid Completed"];
|
||||
--- [label="fail path"];
|
||||
CB alt C [label="offerer may reclaim ITX"] {
|
||||
O => O [label="Wait for ITX locktime to expire"];
|
||||
O =>> N [label="ITX Refund Tx"];
|
||||
N >> B [label="Detects Initiate Tx refund Tx"];
|
||||
B abox B [label="ITX Refunded", textbgcolor="#4bdbf1"];
|
||||
};
|
||||
B => B [label="Wait for PTX locktime to expire"];
|
||||
B =>> N [label="PTX Refund Tx"];
|
||||
B => B [label="Wait for PTX Refund to confirm"];
|
||||
B abox B [label="PTX Refunded", textbgcolor="#f1db4b"];
|
||||
B abox B [label="Bid Completed"];
|
||||
};
|
||||
}
|
||||
69
doc/protocols/sequence_diagrams/offerer.alt.xu
Normal file
69
doc/protocols/sequence_diagrams/offerer.alt.xu
Normal file
@@ -0,0 +1,69 @@
|
||||
xu {
|
||||
hscale = "1.2";
|
||||
|
||||
CB [label=" ", linecolor="transparent"],
|
||||
N [label="Network", linecolor="#008800", textbgcolor="#CCFFCC", arclinecolor="#008800"],
|
||||
O [label="Offerer", linecolor="#FF0000", textbgcolor="#FFCCCC", arclinecolor="#FF0000"],
|
||||
B [label="Bidder", linecolor="#0000FF", textbgcolor="#CCCCFF", arclinecolor="#0000FF"],
|
||||
C [label=" ", linecolor="transparent"], C2 [label=" ", linecolor="transparent"];
|
||||
O =>> N [label="Sends Offer"];
|
||||
N >> B [label="Detects Offer"];
|
||||
B =>> O [label="Sends Bid"];
|
||||
O abox O [label="Bid Received"];
|
||||
O box O [label="User accepts bid"];
|
||||
O =>> N [label="Sends Initiate Tx"],
|
||||
C note C2
|
||||
[label="Offerer generates secret_value and sends Hash(secret_value) to the Bidder",
|
||||
textbgcolor="#FFFFCC"];
|
||||
O =>> B [label="Sends BidAccept"],
|
||||
C note C2
|
||||
[label="ITX can be spent by
|
||||
knowledge of the secret_value and the bidder_redeem_key or
|
||||
after a timeout by the offerer_refund_key",
|
||||
textbgcolor="#FFFFCC"];
|
||||
|
||||
O abox O [label="Bid Accepted"];
|
||||
O abox O [label="ITX Sent", textbgcolor="#4bdbf1"];
|
||||
N >> B [label="Detects Initiate Tx"];
|
||||
B => B [label="Wait for ITX to confirm"], O => O [label="Wait for ITX to confirm"];
|
||||
O abox O [label="Bid Initiated"];
|
||||
O abox O [label="ITX Confirmed"];
|
||||
CB alt C [label="success path"] {
|
||||
B =>> N [label="Sends Participate Tx"],
|
||||
C note C2
|
||||
[label="PTX can be spent by
|
||||
knowledge of the secret_value and the offerer_redeem_key or
|
||||
after a timeout by the bidder_refund_key",
|
||||
textbgcolor="#FFFFCC"];
|
||||
N >> O [label="Detects Participate Tx"];
|
||||
O abox O [label="PTX Sent", textbgcolor="#f1db4b"];
|
||||
O => O [label="Wait for PTX to confirm"];
|
||||
O abox O [label="PTX Confirmed", textbgcolor="#f1db4b"];
|
||||
O abox O [label="Bid Participating"];
|
||||
O =>> N [label="Sends Participate Redeem Tx"],
|
||||
C note C2
|
||||
[label="Reveals secret_value",
|
||||
textbgcolor="#FFFFCC"];
|
||||
N >> B [label="Detects Participate Redeem Tx"];
|
||||
O abox O [label="PTX Redeemed", textbgcolor="#f1db4b"];
|
||||
CB alt C [label="success path"] {
|
||||
B =>> N [label="Sends Initiate Redeem Tx"];
|
||||
O => O [label="Wait for ITX Redeem to confirm"];
|
||||
O abox O [label="ITX Redeemed", textbgcolor="#4bdbf1"];
|
||||
O abox O [label="Bid Completed"];
|
||||
--- [label="fail path, offerer refunds ITx, bidder loses"];
|
||||
O => O [label="Wait for ITX locktime to expire"];
|
||||
O =>> N [label="ITX Refund Tx"];
|
||||
O => O [label="Wait for ITX Refund to confirm"];
|
||||
O abox O [label="ITX Refunded", textbgcolor="#4bdbf1"];
|
||||
O abox O [label="Bid Completed"];
|
||||
};
|
||||
--- [label="fail path"];
|
||||
O => O [label="Wait for ITX locktime to expire"];
|
||||
O =>> N [label="ITX Refund Tx"];
|
||||
O => O [label="Wait for ITX Refund to confirm"];
|
||||
O abox O [label="ITX Refunded", textbgcolor="#4bdbf1"];
|
||||
O abox O [label="Bid Completed"];
|
||||
};
|
||||
|
||||
}
|
||||
81
doc/protocols/sequence_diagrams/xmr.bidder.alt.xu
Normal file
81
doc/protocols/sequence_diagrams/xmr.bidder.alt.xu
Normal file
@@ -0,0 +1,81 @@
|
||||
xu {
|
||||
hscale="1.3", wordwraparcs=on;
|
||||
|
||||
CB [label=" ", linecolor="transparent"],
|
||||
N [label="Network", linecolor="#008800", textbgcolor="#CCFFCC", arclinecolor="#008800"],
|
||||
O [label="Offerer", linecolor="#FF0000", textbgcolor="#FFCCCC", arclinecolor="#FF0000"],
|
||||
B [label="Bidder", linecolor="#0000FF", textbgcolor="#CCCCFF", arclinecolor="#0000FF"],
|
||||
C [label=" ", linecolor="transparent"], C2 [label=" ", linecolor="transparent"];
|
||||
O =>> N [label="Sends Offer"];
|
||||
N >> B [label="Detects Offer"];
|
||||
B =>> O [label="Sends Bid"];
|
||||
B abox B [label="Bid Sent"];
|
||||
O box O [label="User accepts bid"];
|
||||
O =>> B [label="Sends BidAccept message"],
|
||||
C note C2
|
||||
[label="The BidAccept message contains the pubkeys the offerer will use and a DLEAG proof one key will work across both chains of the swapping coins",
|
||||
textbgcolor="#FFFFCC"];
|
||||
B abox B [label="Bid Receiving accept"];
|
||||
B abox B [label="Bid Accepted"];
|
||||
B =>> O [label="Sends XmrBidLockTxSigsMessage"],
|
||||
C note C2
|
||||
[label="The XmrBidLockTxSigsMessage contains the bidder's signatures for the script-coin-lock-refund and script-coin-lock-refund-spend txns.",
|
||||
textbgcolor="#FFFFCC"];
|
||||
B abox B [label="Exchanged script lock tx sigs msg"];
|
||||
O =>> B [label="Sends XmrBidLockSpendTxMessage"],
|
||||
C note C2
|
||||
[label="The XmrBidLockSpendTxMessage contains the script-coin-lock-tx and the offerer's signature for it.",
|
||||
textbgcolor="#FFFFCC"];
|
||||
O =>> N [label="Sends script-coin-lock-tx"],
|
||||
B abox B [label="Bid Script coin spend tx valid"];
|
||||
B abox B [label="Exchanged script lock spend tx msg"];
|
||||
B => B [label="Wait for script-coin-lock-tx to confirm"];
|
||||
B abox B [label="Bid Script coin locked"];
|
||||
# Bidder would only send noscript-coin-lock-tx if script-coin-lock-tx validates
|
||||
B =>> N [label="Sends noscript-coin-lock-tx"];
|
||||
B => B [label="Wait for noscript-coin-lock-tx to confirm"], O => O [label="Wait for noscript-coin-lock-tx to confirm"];
|
||||
B abox B [label="Bid Scriptless coin locked"];
|
||||
CB alt C [label="success path"] {
|
||||
O => B [label="Sends script-coin-lock-tx release message"],
|
||||
C note C2
|
||||
[label="The XmrBidLockReleaseMessage contains the offerer's OTVES for it.
|
||||
The bidder decodes the offerer's signature from the OTVES.
|
||||
When the offerer has the plaintext signature, they can decode the bidder's noscript-coin-lock-tx signature.",
|
||||
textbgcolor="#FFFFCC"];
|
||||
B abox B [label="Script coin lock released"];
|
||||
B =>> N [label="Sends script-coin-lock-spend-tx"];
|
||||
B abox B [label="Script tx redeemed"];
|
||||
B abox B [label="Bid Completed"];
|
||||
--- [label="fail path"];
|
||||
|||;
|
||||
B => B [label="Wait for script-coin-lock-tx lock to expire"];
|
||||
O =>> N [label="Sends script-coin-lock-pre-refund-tx"],
|
||||
C note C2
|
||||
[label="tx can be sent by either party.",
|
||||
textbgcolor="#FFFFCC"];
|
||||
N >> O [label="script-coin-lock-pre-refund-tx"];
|
||||
B abox B [label="Bid Script pre-refund tx in chain"];
|
||||
CB alt C [label="offerer refunds script coin lock tx"] {
|
||||
|||;
|
||||
O => O [label="Wait for pre-refund tx to confirm"];
|
||||
O =>> N [label="Sends script-coin-lock-pre-refund-spend-tx"],
|
||||
C note C2
|
||||
[label="Refunds the script lock tx, with the offerer's cleartext signature the bidder can refund the noscript lock tx.
|
||||
Once the lock expires the pre-refund tx can be spent by the bidder.",
|
||||
textbgcolor="#FFFFCC"];
|
||||
O abox O [label="Bid Failed, refunded"];
|
||||
N >> B [label="Detects script-coin-lock-pre-refund-spend-tx"],
|
||||
C note C2
|
||||
[label="Bidder recovers the offerer's scriptless chain key-shard.",
|
||||
textbgcolor="#FFFFCC"];
|
||||
B =>> N [label="Sends scriptless-coin-lock-recover-tx"];
|
||||
B abox B [label="Bid Scriptless tx recovered"];
|
||||
B abox B [label="Bid Failed, refunded"];
|
||||
--- [label="bidder swipes script coin lock tx"];
|
||||
|||;
|
||||
B => B [label="Wait for pre-refund tx lock to expire"];
|
||||
B =>> N [label="Sends script-coin-lock-pre-refund-swipe-tx"];
|
||||
B abox B [label="Bid Failed, swiped"];
|
||||
};
|
||||
};
|
||||
}
|
||||
76
doc/protocols/sequence_diagrams/xmr.offerer.alt.xu
Normal file
76
doc/protocols/sequence_diagrams/xmr.offerer.alt.xu
Normal file
@@ -0,0 +1,76 @@
|
||||
xu {
|
||||
hscale="1.3", wordwraparcs=on;
|
||||
|
||||
CB [label=" ", linecolor="transparent"],
|
||||
N [label="Network", linecolor="#008800", textbgcolor="#CCFFCC", arclinecolor="#008800"],
|
||||
O [label="Offerer", linecolor="#FF0000", textbgcolor="#FFCCCC", arclinecolor="#FF0000"],
|
||||
B [label="Bidder", linecolor="#0000FF", textbgcolor="#CCCCFF", arclinecolor="#0000FF"],
|
||||
C [label=" ", linecolor="transparent"], C2 [label=" ", linecolor="transparent"];
|
||||
O =>> N [label="Sends Offer"];
|
||||
N >> B [label="Detects Offer"];
|
||||
B =>> O [label="Sends Bid"];
|
||||
O abox O [label="Bid Receiving"];
|
||||
O abox O [label="Bid Received"];
|
||||
O box O [label="User accepts bid"];
|
||||
O =>> B [label="Sends BidAccept message"],
|
||||
C note C2
|
||||
[label="The BidAccept message contains the pubkeys the offerer will use and a DLEAG proof one key will work across both chains of the swapping coins",
|
||||
textbgcolor="#FFFFCC"];
|
||||
O abox O [label="Bid Accepted"];
|
||||
B =>> O [label="Sends XmrBidLockTxSigsMessage"],
|
||||
C note C2
|
||||
[label="The XmrBidLockTxSigsMessage contains the bidder's signatures for the script-coin-lock-refund and script-coin-lock-refund-spend txns.",
|
||||
textbgcolor="#FFFFCC"];
|
||||
O abox O [label="Exchanged script lock tx sigs msg"];
|
||||
O =>> N [label="Sends script-coin-lock-tx"];
|
||||
O abox O [label="Bid Script coin spend tx valid"];
|
||||
O =>> B [label="Sends XmrBidLockSpendTxMessage"],
|
||||
C note C2
|
||||
[label="The XmrBidLockSpendTxMessage contains the script-coin-lock-tx and the offerer's signature for it.",
|
||||
textbgcolor="#FFFFCC"];
|
||||
O abox O [label="Exchanged script lock spend tx msg"];
|
||||
|
||||
|||;
|
||||
B => B [label="Wait for script-coin-lock-tx to confirm"], O => O [label="Wait for script-coin-lock-tx to confirm"];
|
||||
O abox O [label="Bid Script coin locked"];
|
||||
CB alt C [label="success path"] {
|
||||
B =>> N [label="Sends noscript-coin-lock-tx"];
|
||||
|||;
|
||||
O => O [label="Wait for noscript-coin-lock-tx to confirm"];
|
||||
O abox O [label="Bid Scriptless coin locked"];
|
||||
O => B [label="Sends script-coin-lock-tx release message"],
|
||||
C note C2
|
||||
[label="The XmrBidLockReleaseMessage contains the offerer's OTVES for the script-coin-lock-tx.
|
||||
The bidder decodes the offerer's signature from the OTVES.
|
||||
When the offerer has the plaintext signature, they can decode the bidder's key for the noscript-lock-tx.",
|
||||
textbgcolor="#FFFFCC"];
|
||||
O abox O [label="Bid Script coin lock released"];
|
||||
B =>> N [label="Sends script-coin-lock-spend-tx"];
|
||||
N >> O [label="Detects script-coin-lock-spend-tx"];
|
||||
O abox O [label="Bid Script tx redeemed"],
|
||||
C note C2
|
||||
[label="The offerer extracts the bidder's plaintext signature and derives the bidder's noscript-lock-tx keyhalf.",
|
||||
textbgcolor="#FFFFCC"];
|
||||
O =>> N [label="Sends noscript-coin-lock-spend-tx"];
|
||||
O abox O [label="Bid Scriptless tx redeemed"];
|
||||
|||;
|
||||
O => O [label="Wait for noscript-coin-lock-spend-tx to confirm"];
|
||||
O abox O [label="Bid Completed"];
|
||||
--- [label="fail path"];
|
||||
|||;
|
||||
O => O [label="Wait for script-coin-lock-tx locktime to expire"];
|
||||
O =>> N [label="Sends script-coin-lock-pre-refund-tx"],
|
||||
C note C2
|
||||
[label="tx can be sent by either party.",
|
||||
textbgcolor="#FFFFCC"];
|
||||
N >> O [label="script-coin-lock-pre-refund-tx"];
|
||||
O abox O [label="Bid Script pre-refund tx in chain"];
|
||||
|||;
|
||||
O => O [label="Wait for pre-refund tx to confirm"];
|
||||
O =>> N [label="Sends script-coin-lock-pre-refund-spend-tx"],
|
||||
C note C2
|
||||
[label="Refunds the script lock tx, with the offerer's cleartext signature the bidder can refund the noscript lock tx.",
|
||||
textbgcolor="#FFFFCC"];
|
||||
O abox O [label="Bid Failed, refunded"];
|
||||
};
|
||||
}
|
||||
@@ -7,16 +7,16 @@ Update only the code:
|
||||
|
||||
basicswap]$ git pull
|
||||
$ cd docker
|
||||
$ docker-compose build
|
||||
$ export COINDATA_PATH=[PATH_TO]
|
||||
$ docker-compose build
|
||||
$ docker-compose up
|
||||
|
||||
If the dependencies have changed the container must be built with `--no-cache`:
|
||||
|
||||
basicswap]$ git pull
|
||||
$ cd docker
|
||||
$ docker-compose build --no-cache
|
||||
$ export COINDATA_PATH=[PATH_TO]
|
||||
$ docker-compose build --no-cache
|
||||
$ docker-compose up
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
HTML_PORT=127.0.0.1:12700:12700
|
||||
WS_PORT=127.0.0.1:11700:11700
|
||||
#COINDATA_PATH=/var/data/coinswaps
|
||||
TZ=UTC
|
||||
|
||||
@@ -9,6 +9,7 @@ services:
|
||||
- ${COINDATA_PATH}:/coindata
|
||||
ports:
|
||||
- "${HTML_PORT}" # Expose only to localhost, see .env
|
||||
- "${WS_PORT}" # Expose only to localhost, see .env
|
||||
environment:
|
||||
- TZ
|
||||
logging:
|
||||
|
||||
@@ -11,6 +11,7 @@ services:
|
||||
- ${COINDATA_PATH}:/coindata
|
||||
ports:
|
||||
- "${HTML_PORT}" # Expose only to localhost, see .env
|
||||
- "${WS_PORT}" # Expose only to localhost, see .env
|
||||
environment:
|
||||
- TZ
|
||||
- TOR_PROXY_HOST
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
HTML_PORT=127.0.0.1:12700:12700
|
||||
TZ=UTC
|
||||
DATA_PATH=/mnt/hdd50/docker2
|
||||
|
||||
PART_RPC_HOST=particl_core
|
||||
LTC_RPC_HOST=litecoin_core
|
||||
BTC_RPC_HOST=bitcoin_core
|
||||
|
||||
PART_RPC_USER=particl_user
|
||||
PART_RPC_PWD=particl_pwd
|
||||
BTC_RPC_USER=bitcoin_user
|
||||
BTC_RPC_PWD=bitcoin_pwd
|
||||
LTC_RPC_USER=litecoin_user
|
||||
LTC_RPC_PWD=litecoin_pwd
|
||||
|
||||
PART_DATA_DIR=/data/particl
|
||||
LTC_DATA_DIR=/data/litecoin
|
||||
BTC_DATA_DIR=/data/bitcoin
|
||||
XMR_DATA_DIR=/data/monero_daemon
|
||||
XMR_WALLETS_DIR=/data/monero_wallet
|
||||
|
||||
COINS_RPCBIND_IP=0.0.0.0
|
||||
2
docker/production/.gitignore
vendored
2
docker/production/.gitignore
vendored
@@ -1 +1,3 @@
|
||||
.env
|
||||
docker-compose-prepare.yml
|
||||
docker-compose.yml
|
||||
|
||||
@@ -5,7 +5,7 @@ FROM i_swapclient as install_stage
|
||||
RUN basicswap-prepare --preparebinonly --bindir=/coin_bin --withcoin=bitcoin --withoutcoins=particl && \
|
||||
find /coin_bin -name *.tar.gz -delete
|
||||
|
||||
FROM debian:buster-slim
|
||||
FROM debian:bullseye-slim
|
||||
COPY --from=install_stage /coin_bin .
|
||||
|
||||
ENV BITCOIN_DATA /data
|
||||
|
||||
23
docker/production/compose-fragments/0_start.yml
Normal file
23
docker/production/compose-fragments/0_start.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
version: '3.3'
|
||||
|
||||
networks:
|
||||
default:
|
||||
name: coinswap_network
|
||||
services:
|
||||
particl_core:
|
||||
image: i_particl
|
||||
build:
|
||||
context: particl
|
||||
dockerfile: Dockerfile
|
||||
container_name: particl_core
|
||||
volumes:
|
||||
- ${DATA_PATH}/particl:/data
|
||||
expose:
|
||||
- ${PART_RPC_PORT}
|
||||
- ${PART_ZMQ_PORT}
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
restart: unless-stopped
|
||||
16
docker/production/compose-fragments/1_bitcoin.yml
Normal file
16
docker/production/compose-fragments/1_bitcoin.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
bitcoin_core:
|
||||
image: i_bitcoin
|
||||
build:
|
||||
context: bitcoin
|
||||
dockerfile: Dockerfile
|
||||
container_name: bitcoin_core
|
||||
volumes:
|
||||
- ${DATA_PATH}/bitcoin:/data
|
||||
expose:
|
||||
- ${BTC_RPC_PORT}
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
restart: unless-stopped
|
||||
16
docker/production/compose-fragments/1_litecoin.yml
Normal file
16
docker/production/compose-fragments/1_litecoin.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
litecoin_core:
|
||||
image: i_litecoin
|
||||
build:
|
||||
context: litecoin
|
||||
dockerfile: Dockerfile
|
||||
container_name: litecoin_core
|
||||
volumes:
|
||||
- ${DATA_PATH}/litecoin:/data
|
||||
expose:
|
||||
- ${LTC_RPC_PORT}
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
restart: unless-stopped
|
||||
16
docker/production/compose-fragments/1_monero-wallet.yml
Normal file
16
docker/production/compose-fragments/1_monero-wallet.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
monero_wallet:
|
||||
image: i_monero_wallet
|
||||
build:
|
||||
context: monero_wallet
|
||||
dockerfile: Dockerfile
|
||||
container_name: monero_wallet
|
||||
volumes:
|
||||
- ${DATA_PATH}/monero_wallet:/data
|
||||
expose:
|
||||
- ${BASE_XMR_WALLET_PORT}
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
restart: unless-stopped
|
||||
16
docker/production/compose-fragments/2_tor.yml
Normal file
16
docker/production/compose-fragments/2_tor.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
tor:
|
||||
image: i_tor
|
||||
container_name: tor
|
||||
build:
|
||||
context: ./tor
|
||||
volumes:
|
||||
- ${DATA_PATH}/tor/data:/var/lib/tor/
|
||||
- ${DATA_PATH}/tor/torrc:/etc/tor/torrc
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "5"
|
||||
networks:
|
||||
tor_net:
|
||||
coinswap_network: 172.16.238.200
|
||||
16
docker/production/compose-fragments/8_monero-daemon.yml
Normal file
16
docker/production/compose-fragments/8_monero-daemon.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
monero_daemon:
|
||||
image: i_monero_daemon
|
||||
build:
|
||||
context: monero_daemon
|
||||
dockerfile: Dockerfile
|
||||
container_name: monero_daemon
|
||||
volumes:
|
||||
- ${DATA_PATH}/monero_daemon:/data
|
||||
expose:
|
||||
- ${BASE_XMR_RPC_PORT}
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
restart: unless-stopped
|
||||
21
docker/production/compose-fragments/8_swapclient.yml
Normal file
21
docker/production/compose-fragments/8_swapclient.yml
Normal file
@@ -0,0 +1,21 @@
|
||||
swapclient:
|
||||
image: i_swapclient
|
||||
build:
|
||||
context: swapclient
|
||||
dockerfile: Dockerfile
|
||||
container_name: swapclient
|
||||
volumes:
|
||||
- ${DATA_PATH}/swapclient:/data
|
||||
ports:
|
||||
- "${HTML_PORT}" # Expose only to localhost, see .env
|
||||
- "${WS_PORT}" # Expose only to localhost, see .env
|
||||
environment:
|
||||
- TZ
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "5"
|
||||
depends_on:
|
||||
- particl_core
|
||||
restart: unless-stopped
|
||||
45
docker/production/compose-fragments/9_swapprepare.yml
Normal file
45
docker/production/compose-fragments/9_swapprepare.yml
Normal file
@@ -0,0 +1,45 @@
|
||||
swapprepare:
|
||||
image: i_swapclient
|
||||
build:
|
||||
context: swapclient
|
||||
dockerfile: Dockerfile
|
||||
container_name: swapprepare
|
||||
volumes:
|
||||
- ${DATA_PATH}/swapclient:/data/swapclient
|
||||
- ${DATA_PATH}/monero_daemon:/data/monero_daemon
|
||||
- ${DATA_PATH}/monero_wallet:/data/monero_wallet
|
||||
- ${DATA_PATH}/particl:/data/particl
|
||||
- ${DATA_PATH}/bitcoin:/data/bitcoin
|
||||
- ${DATA_PATH}/litecoin:/data/litecoin
|
||||
environment:
|
||||
- TZ
|
||||
- UI_HTML_PORT
|
||||
- COINS_RPCBIND_IP
|
||||
- BASICSWAP_DATADIR
|
||||
- PART_DATA_DIR
|
||||
- PART_RPC_HOST
|
||||
- PART_ZMQ_PORT
|
||||
- PART_RPC_USER
|
||||
- PART_RPC_PWD
|
||||
- PART_RPC_PORT
|
||||
- BTC_DATA_DIR
|
||||
- BTC_RPC_HOST
|
||||
- BTC_RPC_PORT
|
||||
- BTC_RPC_USER
|
||||
- BTC_RPC_PWD
|
||||
- LTC_DATA_DIR
|
||||
- LTC_RPC_HOST
|
||||
- LTC_RPC_PORT
|
||||
- LTC_RPC_USER
|
||||
- LTC_RPC_PWD
|
||||
- XMR_DATA_DIR
|
||||
- XMR_RPC_HOST
|
||||
- BASE_XMR_RPC_PORT
|
||||
- BASE_XMR_ZMQ_PORT
|
||||
- XMR_WALLETS_DIR
|
||||
- XMR_WALLET_RPC_HOST
|
||||
- BASE_XMR_WALLET_PORT
|
||||
- XMR_WALLET_RPC_USER
|
||||
- XMR_WALLET_RPC_PWD
|
||||
- DEFAULT_XMR_RESTORE_HEIGHT
|
||||
restart: "no"
|
||||
@@ -1,155 +0,0 @@
|
||||
version: '3.3'
|
||||
|
||||
services:
|
||||
particl_core:
|
||||
image: i_particl
|
||||
build:
|
||||
context: particl
|
||||
dockerfile: Dockerfile
|
||||
container_name: particl_core
|
||||
volumes:
|
||||
- ${DATA_PATH}/particl:/data
|
||||
#ports:
|
||||
# - "51738:51738"
|
||||
expose:
|
||||
- 51735
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
#restart: unless-stopped
|
||||
#bitcoin_core:
|
||||
#image: i_bitcoin
|
||||
#build:
|
||||
#context: bitcoin
|
||||
#dockerfile: Dockerfile
|
||||
#container_name: bitcoin_core
|
||||
#volumes:
|
||||
#- ${DATA_PATH}/bitcoin:/data
|
||||
##ports:
|
||||
## - "8333:8333"
|
||||
#expose:
|
||||
#- 8332
|
||||
#logging:
|
||||
#driver: "json-file"
|
||||
#options:
|
||||
#max-size: "10m"
|
||||
#max-file: "3"
|
||||
#restart: unless-stopped
|
||||
litecoin_core:
|
||||
image: i_litecoin
|
||||
build:
|
||||
context: litecoin
|
||||
dockerfile: Dockerfile
|
||||
container_name: litecoin_core
|
||||
volumes:
|
||||
- ${DATA_PATH}/litecoin:/data
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
#restart: unless-stopped
|
||||
#monero_daemon:
|
||||
#image: i_monero_daemon
|
||||
#build:
|
||||
#context: monero_daemon
|
||||
#dockerfile: Dockerfile
|
||||
#container_name: monero_daemon
|
||||
#volumes:
|
||||
#- ${DATA_PATH}/monero_daemon:/data
|
||||
#ports:
|
||||
#- "18080:18080"
|
||||
#expose:
|
||||
#- 8332
|
||||
#logging:
|
||||
#driver: "json-file"
|
||||
#options:
|
||||
#max-size: "10m"
|
||||
#max-file: "3"
|
||||
#restart: unless-stopped
|
||||
#monero_wallet:
|
||||
#image: i_monero_wallet
|
||||
#build:
|
||||
#context: monero_wallet
|
||||
#dockerfile: Dockerfile
|
||||
#container_name: monero_wallet
|
||||
#volumes:
|
||||
#- ${DATA_PATH}/monero_wallet:/data
|
||||
#expose:
|
||||
#- 8332
|
||||
#logging:
|
||||
#driver: "json-file"
|
||||
#options:
|
||||
#max-size: "10m"
|
||||
#max-file: "3"
|
||||
#restart: unless-stopped
|
||||
swapclient:
|
||||
image: i_swapclient
|
||||
build:
|
||||
context: swapclient
|
||||
dockerfile: Dockerfile
|
||||
container_name: swapclient
|
||||
volumes:
|
||||
- ${DATA_PATH}/swapclient:/data
|
||||
ports:
|
||||
- "${HTML_PORT}" # Expose only to localhost, see .env
|
||||
environment:
|
||||
- TZ
|
||||
logging:
|
||||
driver: "json-file"
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "5"
|
||||
depends_on:
|
||||
- particl_core
|
||||
restart: unless-stopped
|
||||
swapprepare:
|
||||
image: i_swapclient
|
||||
build:
|
||||
context: swapclient
|
||||
dockerfile: Dockerfile
|
||||
container_name: swapprepare
|
||||
volumes:
|
||||
- ${DATA_PATH}/swapclient:/data/swapclient
|
||||
- ${DATA_PATH}/monero_daemon:/data/monero_daemon
|
||||
- ${DATA_PATH}/monero_wallet:/data/monero_wallet
|
||||
- ${DATA_PATH}/particl:/data/particl
|
||||
- ${DATA_PATH}/bitcoin:/data/bitcoin
|
||||
- ${DATA_PATH}/litecoin:/data/litecoin
|
||||
environment:
|
||||
- TZ
|
||||
- PART_RPC_HOST
|
||||
- LTC_RPC_HOST
|
||||
- BTC_RPC_HOST
|
||||
- PART_RPC_PORT
|
||||
- LTC_RPC_PORT
|
||||
- BTC_RPC_PORT
|
||||
- XMR_RPC_HOST
|
||||
- BASE_XMR_RPC_PORT
|
||||
- BASE_XMR_ZMQ_PORT
|
||||
- BASE_XMR_WALLET_PORT
|
||||
- XMR_WALLET_RPC_HOST
|
||||
- XMR_WALLET_RPC_USER
|
||||
- XMR_WALLET_RPC_PWD
|
||||
- DEFAULT_XMR_RESTORE_HEIGHT
|
||||
- UI_HTML_PORT
|
||||
- PART_ZMQ_PORT
|
||||
- PART_RPC_USER
|
||||
- PART_RPC_PWD
|
||||
- BTC_RPC_USER
|
||||
- BTC_RPC_PWD
|
||||
- LTC_RPC_USER
|
||||
- LTC_RPC_PWD
|
||||
- PART_DATA_DIR
|
||||
- LTC_DATA_DIR
|
||||
- BTC_DATA_DIR
|
||||
- XMR_DATA_DIR
|
||||
- XMR_WALLETS_DIR
|
||||
- COINS_RPCBIND_IP
|
||||
restart: "no"
|
||||
networks:
|
||||
default:
|
||||
external:
|
||||
name: coinswap_network
|
||||
@@ -1,21 +1,37 @@
|
||||
HTML_PORT=127.0.0.1:12700:12700
|
||||
WS_PORT=127.0.0.1:11700:11700
|
||||
TZ=UTC
|
||||
DATA_PATH=/var/swapdata/
|
||||
PART_RPC_HOST=particl_core
|
||||
LTC_RPC_HOST=litecoin_core
|
||||
BTC_RPC_HOST=bitcoin_core
|
||||
|
||||
DATA_PATH=/var/swapdata/
|
||||
|
||||
BASICSWAP_DATADIR=/data/swapclient
|
||||
COINS_RPCBIND_IP=0.0.0.0
|
||||
|
||||
PART_DATA_DIR=/data/particl
|
||||
PART_RPC_HOST=particl_core
|
||||
PART_RPC_PORT=19792
|
||||
PART_ZMQ_PORT=20792
|
||||
PART_RPC_USER=particl_user
|
||||
PART_RPC_PWD=particl_pwd
|
||||
BTC_RPC_USER=bitcoin_user
|
||||
BTC_RPC_PWD=bitcoin_pwd
|
||||
|
||||
LTC_DATA_DIR=/data/litecoin
|
||||
LTC_RPC_HOST=litecoin_core
|
||||
LTC_RPC_PORT=19795
|
||||
LTC_RPC_USER=litecoin_user
|
||||
LTC_RPC_PWD=litecoin_pwd
|
||||
|
||||
PART_DATA_DIR=/data/particl
|
||||
LTC_DATA_DIR=/data/litecoin
|
||||
BTC_DATA_DIR=/data/bitcoin
|
||||
XMR_DATA_DIR=/data/monero_daemon
|
||||
XMR_WALLETS_DIR=/data/monero_wallet
|
||||
BTC_RPC_HOST=bitcoin_core
|
||||
BTC_RPC_PORT=19796
|
||||
BTC_RPC_USER=bitcoin_user
|
||||
BTC_RPC_PWD=bitcoin_pwd
|
||||
|
||||
COINS_RPCBIND_IP=0.0.0.0
|
||||
XMR_DATA_DIR=/data/monero_daemon
|
||||
XMR_RPC_HOST=monero_daemon
|
||||
BASE_XMR_RPC_PORT=29798
|
||||
|
||||
XMR_WALLETS_DIR=/data/monero_wallet
|
||||
XMR_WALLET_RPC_HOST=monero_wallet
|
||||
BASE_XMR_WALLET_PORT=29998
|
||||
XMR_WALLET_RPC_USER=xmr_wallet_user
|
||||
XMR_WALLET_RPC_PWD=xmr_wallet_pwd
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user