Compare commits
256 Commits
firo
...
mweb_uncon
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6e7d36e84 | ||
|
|
6e4feb33d7 | ||
|
|
1f810fab6b | ||
|
|
7bf5f7ddfa | ||
|
|
d7da532111 | ||
|
|
fa35102794 | ||
|
|
da8c3e0237 | ||
|
|
ec29c9889c | ||
|
|
14298d022a | ||
|
|
5ceaab57d1 | ||
|
|
a5c3534c19 | ||
|
|
3b6f72c084 | ||
|
|
7c0ea36e37 | ||
|
|
14577f7741 | ||
|
|
dbf3f8f034 | ||
|
|
b85d234a0b | ||
|
|
1bfb271b87 | ||
|
|
f9bc5d46af | ||
|
|
fab89a42f3 | ||
|
|
3e14a784f3 | ||
|
|
e4f196411a | ||
|
|
8318961f0b | ||
|
|
7c9504e0cd | ||
|
|
98f3a52daa | ||
|
|
3276f9abd4 | ||
|
|
041ab18288 | ||
|
|
d57366c0b2 | ||
|
|
1ec1764012 | ||
|
|
28fc4817c0 | ||
|
|
2d1bd87b41 | ||
|
|
9ee6669179 | ||
|
|
30a5ea1652 | ||
|
|
53ceae718b | ||
|
|
1af9f64020 | ||
|
|
cfd2151c1a | ||
|
|
237d12afa0 | ||
|
|
a0cdd8cec9 | ||
|
|
1754650e75 | ||
|
|
3b8b512003 | ||
|
|
81649dcf9b | ||
|
|
f5d4b8dc0d | ||
|
|
bcfd63037a | ||
|
|
ddf3734f9d | ||
|
|
8c07ee5108 | ||
|
|
6ad5880ba4 | ||
|
|
0ff0a13a67 | ||
|
|
ff7e8fe0aa | ||
|
|
1068694990 | ||
|
|
66d1abd888 | ||
|
|
671e626551 | ||
|
|
192aff221e | ||
|
|
03fdf44220 | ||
|
|
38fa498b0b | ||
|
|
7547587d4e | ||
|
|
fd0772f893 | ||
|
|
0a12625290 | ||
|
|
d6ed5ba24c | ||
|
|
fb48797298 | ||
|
|
5bec1c31da | ||
|
|
3d3fcbde0b | ||
|
|
61845a7a84 | ||
|
|
65c93eaee6 | ||
|
|
6a26f72bca | ||
|
|
0a9db22828 | ||
|
|
08f0156b75 | ||
|
|
15bf9b2187 | ||
|
|
258b730c41 | ||
|
|
b409fe9f0e | ||
|
|
01bb3870b6 | ||
|
|
9efb244952 | ||
|
|
0be5a4fca7 | ||
|
|
a4c79fb7aa | ||
|
|
5b6f447692 | ||
|
|
28baa597cc | ||
|
|
d4a6ad7d6f | ||
|
|
ce578f8025 | ||
|
|
c387bfec71 | ||
|
|
d668a2f342 | ||
|
|
8e17ee5939 | ||
|
|
3b55d17a26 | ||
|
|
fd0bf9ed73 | ||
|
|
e6c7c4d9bb | ||
|
|
7053d7ee4b | ||
|
|
22cd3cf9f1 | ||
|
|
05e6edd5df | ||
|
|
9a1b7db2dc | ||
|
|
2a2f1ca3b6 | ||
|
|
db0e85d37c | ||
|
|
ebcd7738e5 | ||
|
|
4d8d421de6 | ||
|
|
23330c20bc | ||
|
|
22e005728a | ||
|
|
f2b69e5498 | ||
|
|
d617ab1d6b | ||
|
|
e7ae290eb5 | ||
|
|
20b405a944 | ||
|
|
c01b4a3d70 | ||
|
|
7caac8a8eb | ||
|
|
a17129999c | ||
|
|
5775ac5931 | ||
|
|
0b963bffde | ||
|
|
45e49848b1 | ||
|
|
5c23983c8e | ||
|
|
68ff57ebdc | ||
|
|
7fd60b3e82 | ||
|
|
7735c9733a | ||
|
|
af876fa166 | ||
|
|
8f4b962285 | ||
|
|
a13a5d4bf6 | ||
|
|
547e50acb6 | ||
|
|
55ded71686 | ||
|
|
c3b33c502e | ||
|
|
67624a252b | ||
|
|
d4f6286980 | ||
|
|
0432fae5b5 | ||
|
|
9888c4ebe1 | ||
|
|
7bc5fc78ba | ||
|
|
705ac2c6fc | ||
|
|
303499fc6f | ||
|
|
724f9348d5 | ||
|
|
4464ca1746 | ||
|
|
08d3f05c1c | ||
|
|
be46d8a7bd | ||
|
|
f6fb11f452 | ||
|
|
00bebfa371 | ||
|
|
eb44dc9626 | ||
|
|
1859eccf0e | ||
|
|
416b003da9 | ||
|
|
88fd8ac23e | ||
|
|
04d4d89e96 | ||
|
|
57554d4fec | ||
|
|
cbb3d0ac02 | ||
|
|
1dcd750fea | ||
|
|
75c5f6a905 | ||
|
|
9645e87961 | ||
|
|
2b2b98505b | ||
|
|
1093eaed3b | ||
|
|
788efeef5b | ||
|
|
f02b62b6ea | ||
|
|
8885a58a2e | ||
|
|
0041fb4a3c | ||
|
|
81e7825b51 | ||
|
|
5c0e70c5b2 | ||
|
|
5309ff1b50 | ||
|
|
e67ca94dfb | ||
|
|
5536c82ded | ||
|
|
5b6c8a23a9 | ||
|
|
87937ec0ac | ||
|
|
c5ab11d015 | ||
|
|
3a97c0c7bb | ||
|
|
90aaa46918 | ||
|
|
697d88d5f2 | ||
|
|
6e94c4a05d | ||
|
|
409c75851f | ||
|
|
22576c0316 | ||
|
|
b5a4df9908 | ||
|
|
ca3bfe858c | ||
|
|
37952fd296 | ||
|
|
159d3e2c33 | ||
|
|
89e0080173 | ||
|
|
484d811fe7 | ||
|
|
ea8cc70696 | ||
|
|
97506850c4 | ||
|
|
724e7f0ffc | ||
|
|
f33629f2a5 | ||
|
|
d8de9a6aa7 | ||
|
|
114e8e4d2b | ||
|
|
0a2133f43f | ||
|
|
06065958b7 | ||
|
|
09cc523ac3 | ||
|
|
5b0c1e9b51 | ||
|
|
577849f01c | ||
|
|
6ccfd93997 | ||
|
|
b6046fdbf3 | ||
|
|
c2d6cdafdd | ||
|
|
c322c9ae0c | ||
|
|
c13606ab54 | ||
|
|
c5bd58afc2 | ||
|
|
2922b171a6 | ||
|
|
3234e3fba3 | ||
|
|
ac16fc07a4 | ||
|
|
3241616d68 | ||
|
|
dc0bd147b8 | ||
|
|
9117e2b723 | ||
|
|
c9e04332fc | ||
|
|
a2fa2ff9de | ||
|
|
83a077e5b0 | ||
|
|
90ce6b3e04 | ||
|
|
1a136cd219 | ||
|
|
553af1a3e8 | ||
|
|
9729dcf526 | ||
|
|
ef71ca7ef4 | ||
|
|
149616a59f | ||
|
|
9677c48f39 | ||
|
|
ac10c9db76 | ||
|
|
c4321b7740 | ||
|
|
2a3d89b112 | ||
|
|
fb5e8ff8b1 | ||
|
|
ac07727da7 | ||
|
|
09ce086790 | ||
|
|
80a78b4070 | ||
|
|
5ce178e673 | ||
|
|
ef6653a8db | ||
|
|
3f71dffe5a | ||
|
|
2a9e423eaa | ||
|
|
6860279faa | ||
|
|
80df3b1a34 | ||
|
|
efcee68663 | ||
|
|
31aaacf4e1 | ||
|
|
7101a5d1ee | ||
|
|
ef51719e62 | ||
|
|
0e1cb6d03d | ||
|
|
1d0a3fbc12 | ||
|
|
55f6095ec2 | ||
|
|
7d43512845 | ||
|
|
c90fa6f2c6 | ||
|
|
23e89882a4 | ||
|
|
a250daca8b | ||
|
|
789cd0f6ab | ||
|
|
2a35148a4b | ||
|
|
9370eff4a6 | ||
|
|
b5b43a8bf3 | ||
|
|
7bc411eb98 | ||
|
|
47c1237f6d | ||
|
|
d15cf3dd6f | ||
|
|
ba5339d8bd | ||
|
|
6e5c54b447 | ||
|
|
391f6ffe80 | ||
|
|
c5f31f0d1e | ||
|
|
0f7df9e5f1 | ||
|
|
aabf865873 | ||
|
|
1bcce46a7c | ||
|
|
cb6e773848 | ||
|
|
640d22bdc5 | ||
|
|
e5749a397f | ||
|
|
a520ebf23f | ||
|
|
b43d58afbf | ||
|
|
59adf3368b | ||
|
|
d2de181550 | ||
|
|
0bd75fae0e | ||
|
|
a6162718c8 | ||
|
|
850f2c7b17 | ||
|
|
2ba2ff5791 | ||
|
|
5d4db2c1df | ||
|
|
8022ada01f | ||
|
|
d08e85e73e | ||
|
|
8ec6d55963 | ||
|
|
54e434e1c9 | ||
|
|
bbe7556d18 | ||
|
|
093a36c8d2 | ||
|
|
fc31615a97 | ||
|
|
020a65db8a | ||
|
|
d78ff8573c | ||
|
|
09394c58a6 | ||
|
|
eb8aa18224 | ||
|
|
e525878aef |
@@ -24,7 +24,7 @@ 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/refs/tags/anonswap_v0.1.zip
|
||||
- wget -O coincurve-anonswap.zip https://github.com/tecnovert/coincurve/archive/refs/tags/anonswap_v0.2.zip
|
||||
- unzip -d coincurve-anonswap coincurve-anonswap.zip
|
||||
- mv ./coincurve-anonswap/*/{.,}* ./coincurve-anonswap || true
|
||||
- cd coincurve-anonswap
|
||||
@@ -47,3 +47,4 @@ test_task:
|
||||
- pytest tests/basicswap/test_other.py
|
||||
- pytest tests/basicswap/test_run.py
|
||||
- pytest tests/basicswap/test_reload.py
|
||||
- pytest tests/basicswap/test_btc_xmr.py -k 'test_01_a or test_01_b or test_02_a or test_02_b'
|
||||
|
||||
BIN
.github-readme/basicswap_header.jpg
Normal file
|
After Width: | Height: | Size: 230 KiB |
4
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
old/
|
||||
build/
|
||||
*.pyc
|
||||
__pycache__
|
||||
/dist/
|
||||
@@ -8,3 +9,6 @@ __pycache__
|
||||
.tox
|
||||
.eggs
|
||||
*~
|
||||
|
||||
# geckodriver.log
|
||||
*.log
|
||||
|
||||
@@ -22,7 +22,7 @@ before_install:
|
||||
install:
|
||||
- travis_retry pip install tox pytest
|
||||
before_script:
|
||||
- wget -O coincurve-anonswap.zip https://github.com/tecnovert/coincurve/archive/refs/tags/anonswap_v0.1.zip
|
||||
- wget -O coincurve-anonswap.zip https://github.com/tecnovert/coincurve/archive/refs/tags/anonswap_v0.2.zip
|
||||
- unzip -d coincurve-anonswap coincurve-anonswap.zip
|
||||
- mv ./coincurve-anonswap/*/{.,}* ./coincurve-anonswap || true
|
||||
- cd coincurve-anonswap
|
||||
|
||||
@@ -15,7 +15,7 @@ RUN wget -O protobuf_src.tar.gz https://github.com/protocolbuffers/protobuf/rele
|
||||
make -j$(nproc) install && \
|
||||
ldconfig
|
||||
|
||||
ARG COINCURVE_VERSION=v0.1
|
||||
ARG COINCURVE_VERSION=v0.2
|
||||
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 && \
|
||||
|
||||
2
LICENSE
@@ -1,5 +1,5 @@
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2019 tecnovert
|
||||
Copyright (c) 2019-2024 tecnovert
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
135
README.md
@@ -1,20 +1,131 @@
|
||||
# BasicSwap DEX (BSX)
|
||||
|
||||
# Simple Atomic Swap Network - Proof of Concept
|
||||

|
||||
|
||||
## Overview
|
||||
**[Official Website](https://basicswapdex.com)** | **[News](https://particl.news)** | **[Tutorials](https://academy.particl.io)** | **[Chat]( https://matrix.to/#/#basicswap:matrix.org )**
|
||||
|
||||
Simple atomic swap experiment, doesn't have many interesting features yet.
|
||||
Not ready for real world use.
|
||||
Table of Contents
|
||||
|
||||
Uses Particl secure messaging and Decred style atomic swaps.
|
||||
* [About](#about)
|
||||
* [Features](#features)
|
||||
* [Available Assets](#available-assets)
|
||||
* [Participate](#participate)
|
||||
* [Tutorials](#tutorials)
|
||||
* [License](#license)
|
||||
|
||||
The Particl node is used to hold the keys and sign for the swap transactions.
|
||||
Other nodes can be run in pruned mode.
|
||||
A node must be run for each coin type traded.
|
||||
In the future it should be possible to use data from explorers instead of running a node.
|
||||
## About
|
||||
|
||||
## Currently a work in progress
|
||||
The BasicSwap DEX is a privacy-first and decentralized exchange which features cross-chain atomic swaps and a distributed order book.
|
||||
|
||||
Not ready for real-world use.
|
||||
[BasicSwap](https://academy.particl.io/en/latest/glossary.html#term-BasicSwap) is a cross-chain and privacy-centric DEX (decentralized exchange) that lets you trade cryptocurrencies with no third party involvement. Its distributed order book lets you make or take orders at no cost and trade within a free and open environment without central points of failure.
|
||||
|
||||
Discuss development and help with testing in the matrix channel [#basicswap:matrix.org](https://riot.im/app/#/room/#basicswap:matrix.org)
|
||||
This DEX protocol was built in direct response to the increasingly invasive demands and data mining practices of today’s cryptocurrency exchanges. It strives to bring more decentralized and more private cryptocurrency trading conditions for all.
|
||||
|
||||
BasicSwap is still in beta. This means that, while it already offers most of the vital trading features you’d expect to see on centralized exchanges, it is still in heavy development, and many more features will come about in the near future.
|
||||
|
||||
Check out our [roadmap](https://basicswapdex.com/roadmap) to get a better idea of what we've got planned for it!
|
||||
|
||||
## Features
|
||||
|
||||
* **True cross-chain support** — Swap cryptocurrencies that live on entirely different blockchain environments, like Bitcoin and Monero.
|
||||
* **Distributed order book** — Make or take limit orders on a completely distributed order book system.
|
||||
* **No third-party or middleman** — Trade crypto with no intermediaries whatsoever.
|
||||
* **No trading fees** — Only pay the typical cryptocurrency network fee.
|
||||
* **Privacy from the ground up** — Every component of BasicSwap is built with a privacy-first commitment.
|
||||
* **Full Monero support** — Swap Monero with a variety of other cryptocurrencies like Bitcoin or Particl. No wrapped assets or trickery involved.
|
||||
* **User-friendly interface** — Enjoy all these features within a user-friendly and intuitive interface that handles all the complicated parts for you.
|
||||
|
||||
BasicSwap is still in beta. This means that, while it already offers most of the vital trading features you’d expect to see on centralized exchanges, it is still in heavy development, and many more features will come about in the near future.
|
||||
|
||||
## Available Assets
|
||||
|
||||
BasicSwap is compatible with the following digital assets.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><strong>Coin Name</strong>
|
||||
</td>
|
||||
<td><strong>Ticker</strong>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Bitcoin
|
||||
</td>
|
||||
<td>BTC
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Monero
|
||||
</td>
|
||||
<td>XMR
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Dash
|
||||
</td>
|
||||
<td>DASH
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Litecoin
|
||||
</td>
|
||||
<td>LTC
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Firo
|
||||
</td>
|
||||
<td>FIRO
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>PIVX
|
||||
</td>
|
||||
<td>PIVX
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Particl
|
||||
</td>
|
||||
<td>PART
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
We plan on adding many other cryptocurrencies moving forward, including ETH and its ERC-20 tokens. However, due to the true cross-chain nature of the BasicSwap DEX protocol, each integration has to be done on a case-by-case basis.
|
||||
|
||||
If you’d like to add a cryptocurrency to BasicSwap, either [apply for a listing using our listing application form](https://forms.gle/9DsHoHTJVqSiMNHW9), or try coding the integration yourself by referencing how other cryptocurrencies have been added. Follow [this link](https://academy.particl.io/en/latest/basicswap-guides/basicswapguides_apply.html) for more information on how to integrate a coin yourself.
|
||||
|
||||
# Participate
|
||||
|
||||
### Chats
|
||||
|
||||
* **For developers** The chat [#particl-dev:matrix.org](https://app.element.io/#/room/#particl-dev:matrix.org) using [Element](https://element.io) (formerly Riot).
|
||||
* **For community** The community chat [https://discord.me/particl](https://discord.me/particl) [](https://discord.me/particl).
|
||||
|
||||
[](http://twitter.com/BasicSwapDEX)
|
||||
[](http://reddit.com/r/particl)
|
||||
|
||||
### Documentation, installation
|
||||
|
||||
For non-developers curious to explore a new world of commerce, binaries can be downloaded and installed. It is the easiest way to get started. Following the guides on [Particl Academy](https://academy.particl.io), a reference book in straightforward language, is recommended.
|
||||
|
||||
* [Download BasicSwapDEX](https://github.com/tecnovert/basicswap/tree/master/doc)
|
||||
|
||||
#### Community chat support
|
||||
|
||||
* [Discord](https://discord.me/particl) navigate to the #support channel
|
||||
|
||||
* [Telegram](https://t.me/particlhelp)
|
||||
|
||||
* [Element](https://app.element.io/#/room/#particlhelp:matrix.org)
|
||||
|
||||
# Tutorials
|
||||
|
||||
You can find a wide variety of tutorials and step-by-step guides about BasicSwap on the [Particl Academy](https://academy.particl.io) or on Particl’s Youtube channel.
|
||||
|
||||
If you encounter an issue or try to accomplish something not mentioned in any of the tutorials included in the links above, please join the community chat support channels; you’ll be sure to find help and support from our awesome community and open-source team there!
|
||||
|
||||
# License
|
||||
|
||||
BasicSwap is released under MIT software license.
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
name = "basicswap"
|
||||
|
||||
__version__ = "0.11.45"
|
||||
__version__ = "0.12.7"
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2019-2022 tecnovert
|
||||
# Copyright (c) 2019-2024 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
import os
|
||||
import time
|
||||
import shlex
|
||||
import socks
|
||||
import random
|
||||
import socket
|
||||
import urllib
|
||||
import logging
|
||||
@@ -14,7 +16,7 @@ import threading
|
||||
import traceback
|
||||
import subprocess
|
||||
|
||||
import basicswap.config as cfg
|
||||
from sockshandler import SocksiPyHandler
|
||||
|
||||
from .rpc import (
|
||||
callrpc,
|
||||
@@ -36,8 +38,8 @@ class BaseApp:
|
||||
def __init__(self, fp, data_dir, settings, chain, log_name='BasicSwap'):
|
||||
self.log_name = log_name
|
||||
self.fp = fp
|
||||
self.is_running = True
|
||||
self.fail_code = 0
|
||||
self.mock_time_offset = 0
|
||||
|
||||
self.data_dir = data_dir
|
||||
self.chain = chain
|
||||
@@ -47,6 +49,8 @@ class BaseApp:
|
||||
self.mxDB = threading.RLock()
|
||||
self.debug = self.settings.get('debug', False)
|
||||
self.delay_event = threading.Event()
|
||||
self.chainstate_delay_event = threading.Event()
|
||||
|
||||
self._network = None
|
||||
self.prepareLogging()
|
||||
self.log.info('Network: {}'.format(self.chain))
|
||||
@@ -63,7 +67,7 @@ class BaseApp:
|
||||
def stopRunning(self, with_code=0):
|
||||
self.fail_code = with_code
|
||||
with self.mxDB:
|
||||
self.is_running = False
|
||||
self.chainstate_delay_event.set()
|
||||
self.delay_event.set()
|
||||
|
||||
def prepareLogging(self):
|
||||
@@ -73,10 +77,10 @@ class BaseApp:
|
||||
# Remove any existing handlers
|
||||
self.log.handlers = []
|
||||
|
||||
formatter = logging.Formatter('%(asctime)s %(levelname)s : %(message)s')
|
||||
formatter = logging.Formatter('%(asctime)s %(levelname)s : %(message)s', '%Y-%m-%d %H:%M:%S')
|
||||
stream_stdout = logging.StreamHandler()
|
||||
if self.log_name != 'BasicSwap':
|
||||
stream_stdout.setFormatter(logging.Formatter('%(asctime)s %(name)s %(levelname)s : %(message)s'))
|
||||
stream_stdout.setFormatter(logging.Formatter('%(asctime)s %(name)s %(levelname)s : %(message)s', '%Y-%m-%d %H:%M:%S'))
|
||||
else:
|
||||
stream_stdout.setFormatter(formatter)
|
||||
stream_fp = logging.StreamHandler(self.fp)
|
||||
@@ -92,7 +96,7 @@ class BaseApp:
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
def setDaemonPID(self, name, pid):
|
||||
def setDaemonPID(self, name, pid) -> None:
|
||||
if isinstance(name, Coins):
|
||||
self.coin_clients[name]['pid'] = pid
|
||||
return
|
||||
@@ -100,12 +104,12 @@ class BaseApp:
|
||||
if v['name'] == name:
|
||||
v['pid'] = pid
|
||||
|
||||
def getChainDatadirPath(self, coin):
|
||||
def getChainDatadirPath(self, coin) -> str:
|
||||
datadir = self.coin_clients[coin]['datadir']
|
||||
testnet_name = '' if self.chain == 'mainnet' else chainparams[coin][self.chain].get('name', self.chain)
|
||||
return os.path.join(datadir, testnet_name)
|
||||
|
||||
def getCoinIdFromName(self, coin_name):
|
||||
def getCoinIdFromName(self, coin_name: str):
|
||||
for c, params in chainparams.items():
|
||||
if coin_name.lower() == params['name'].lower():
|
||||
return c
|
||||
@@ -119,18 +123,6 @@ class BaseApp:
|
||||
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']
|
||||
args = [os.path.join(bindir, cfg.PARTICL_TX), ]
|
||||
if self.chain != 'mainnet':
|
||||
args.append('-' + self.chain)
|
||||
args += shlex.split(cmd)
|
||||
p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
out = p.communicate()
|
||||
if len(out[1]) > 0:
|
||||
raise ValueError('TX error ' + str(out[1]))
|
||||
return out[0].decode('utf-8').strip()
|
||||
|
||||
def callcoincli(self, coin_type, params, wallet=None, timeout=None):
|
||||
bindir = self.coin_clients[coin_type]['bindir']
|
||||
datadir = self.coin_clients[coin_type]['datadir']
|
||||
@@ -146,7 +138,7 @@ class BaseApp:
|
||||
raise ValueError('CLI error ' + str(out[1]))
|
||||
return out[0].decode('utf-8').strip()
|
||||
|
||||
def is_transient_error(self, ex):
|
||||
def is_transient_error(self, ex) -> bool:
|
||||
if isinstance(ex, TemporaryError):
|
||||
return True
|
||||
str_error = str(ex).lower()
|
||||
@@ -164,13 +156,22 @@ class BaseApp:
|
||||
|
||||
socket.setdefaulttimeout(timeout)
|
||||
|
||||
def popConnectionParameters(self):
|
||||
def popConnectionParameters(self) -> None:
|
||||
if self.use_tor_proxy:
|
||||
socket.socket = self.default_socket
|
||||
socket.getaddrinfo = self.default_socket_getaddrinfo
|
||||
socket.setdefaulttimeout(self.default_socket_timeout)
|
||||
|
||||
def logException(self, message):
|
||||
def readURL(self, url: str, timeout: int = 120, headers=None) -> bytes:
|
||||
open_handler = None
|
||||
if self.use_tor_proxy:
|
||||
open_handler = SocksiPyHandler(socks.PROXY_TYPE_SOCKS5, self.tor_proxy_host, self.tor_proxy_port)
|
||||
opener = urllib.request.build_opener(open_handler) if self.use_tor_proxy else urllib.request.build_opener()
|
||||
opener.addheaders = [('User-agent', 'Mozilla/5.0')]
|
||||
request = urllib.request.Request(url, headers=headers)
|
||||
return opener.open(request, timeout=timeout).read()
|
||||
|
||||
def logException(self, message) -> None:
|
||||
self.log.error(message)
|
||||
if self.debug:
|
||||
self.log.error(traceback.format_exc())
|
||||
@@ -191,3 +192,35 @@ class BaseApp:
|
||||
except Exception as e:
|
||||
self.log.error(f'torControl {e}')
|
||||
return
|
||||
|
||||
def getTime(self) -> int:
|
||||
return int(time.time()) + self.mock_time_offset
|
||||
|
||||
def setMockTimeOffset(self, new_offset: int) -> None:
|
||||
self.log.warning(f'Setting mocktime to {new_offset}')
|
||||
self.mock_time_offset = new_offset
|
||||
|
||||
def get_int_setting(self, name: str, default_v: int, min_v: int, max_v) -> int:
|
||||
value: int = self.settings.get(name, default_v)
|
||||
if value < min_v:
|
||||
self.log.warning(f'Setting {name} to {min_v}')
|
||||
value = min_v
|
||||
if value > max_v:
|
||||
self.log.warning(f'Setting {name} to {max_v}')
|
||||
value = max_v
|
||||
return value
|
||||
|
||||
def get_delay_event_seconds(self):
|
||||
if self.min_delay_event == self.max_delay_event:
|
||||
return self.min_delay_event
|
||||
return random.randrange(self.min_delay_event, self.max_delay_event)
|
||||
|
||||
def get_short_delay_event_seconds(self):
|
||||
if self.min_delay_event_short == self.max_delay_event_short:
|
||||
return self.min_delay_event_short
|
||||
return random.randrange(self.min_delay_event_short, self.max_delay_event_short)
|
||||
|
||||
def get_delay_retry_seconds(self):
|
||||
if self.min_delay_retry == self.max_delay_retry:
|
||||
return self.min_delay_retry
|
||||
return random.randrange(self.min_delay_retry, self.max_delay_retry)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2021-2022 tecnovert
|
||||
# Copyright (c) 2021-2023 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
@@ -47,6 +47,9 @@ class MessageTypes(IntEnum):
|
||||
XMR_BID_LOCK_RELEASE_LF = auto()
|
||||
OFFER_REVOKE = auto()
|
||||
|
||||
ADS_BID_LF = auto()
|
||||
ADS_BID_ACCEPT_FL = auto()
|
||||
|
||||
|
||||
class AddressTypes(IntEnum):
|
||||
OFFER = auto()
|
||||
@@ -98,6 +101,8 @@ class BidStates(IntEnum):
|
||||
BID_STATE_UNKNOWN = 26
|
||||
XMR_SWAP_MSG_SCRIPT_LOCK_TX_SIGS = 27 # XmrBidLockTxSigsMessage
|
||||
XMR_SWAP_MSG_SCRIPT_LOCK_SPEND_TX = 28 # XmrBidLockSpendTxMessage
|
||||
BID_REQUEST_SENT = 29
|
||||
BID_REQUEST_ACCEPTED = 30
|
||||
|
||||
|
||||
class TxStates(IntEnum):
|
||||
@@ -106,6 +111,8 @@ class TxStates(IntEnum):
|
||||
TX_CONFIRMED = auto()
|
||||
TX_REDEEMED = auto()
|
||||
TX_REFUNDED = auto()
|
||||
TX_IN_MEMPOOL = auto()
|
||||
TX_IN_CHAIN = auto()
|
||||
|
||||
|
||||
class TxTypes(IntEnum):
|
||||
@@ -122,6 +129,10 @@ class TxTypes(IntEnum):
|
||||
XMR_SWAP_A_LOCK_REFUND_SPEND = auto()
|
||||
XMR_SWAP_A_LOCK_REFUND_SWIPE = auto()
|
||||
XMR_SWAP_B_LOCK = auto()
|
||||
XMR_SWAP_B_LOCK_SPEND = auto()
|
||||
XMR_SWAP_B_LOCK_REFUND = auto()
|
||||
|
||||
ITX_PRE_FUNDED = auto()
|
||||
|
||||
|
||||
class ActionTypes(IntEnum):
|
||||
@@ -136,6 +147,7 @@ class ActionTypes(IntEnum):
|
||||
RECOVER_XMR_SWAP_LOCK_TX_B = auto()
|
||||
SEND_XMR_SWAP_LOCK_SPEND_MSG = auto()
|
||||
REDEEM_ITX = auto()
|
||||
ACCEPT_AS_REV_BID = auto()
|
||||
|
||||
|
||||
class EventLogTypes(IntEnum):
|
||||
@@ -168,6 +180,7 @@ class EventLogTypes(IntEnum):
|
||||
PTX_PUBLISHED = auto()
|
||||
PTX_REDEEM_PUBLISHED = auto()
|
||||
PTX_REFUND_PUBLISHED = auto()
|
||||
LOCK_TX_B_IN_MEMPOOL = auto()
|
||||
|
||||
|
||||
class XmrSplitMsgTypes(IntEnum):
|
||||
@@ -184,6 +197,48 @@ class DebugTypes(IntEnum):
|
||||
MAKE_INVALID_PTX = auto()
|
||||
DONT_SPEND_ITX = auto()
|
||||
SKIP_LOCK_TX_REFUND = auto()
|
||||
SEND_LOCKED_XMR = auto()
|
||||
B_LOCK_TX_MISSED_SEND = auto()
|
||||
DUPLICATE_ACTIONS = auto()
|
||||
|
||||
|
||||
class NotificationTypes(IntEnum):
|
||||
NONE = 0
|
||||
OFFER_RECEIVED = auto()
|
||||
BID_RECEIVED = auto()
|
||||
BID_ACCEPTED = auto()
|
||||
|
||||
|
||||
class AutomationOverrideOptions(IntEnum):
|
||||
DEFAULT = 0
|
||||
ALWAYS_ACCEPT = 1
|
||||
NEVER_ACCEPT = auto()
|
||||
|
||||
|
||||
def strAutomationOverrideOption(option):
|
||||
if option == AutomationOverrideOptions.DEFAULT:
|
||||
return 'Default'
|
||||
if option == AutomationOverrideOptions.ALWAYS_ACCEPT:
|
||||
return 'Always Accept'
|
||||
if option == AutomationOverrideOptions.NEVER_ACCEPT:
|
||||
return 'Never Accept'
|
||||
return 'Unknown'
|
||||
|
||||
|
||||
class VisibilityOverrideOptions(IntEnum):
|
||||
DEFAULT = 0
|
||||
HIDE = 1
|
||||
BLOCK = auto()
|
||||
|
||||
|
||||
def strVisibilityOverrideOption(option):
|
||||
if option == VisibilityOverrideOptions.DEFAULT:
|
||||
return 'Default'
|
||||
if option == VisibilityOverrideOptions.HIDE:
|
||||
return 'Hide'
|
||||
if option == VisibilityOverrideOptions.BLOCK:
|
||||
return 'Block'
|
||||
return 'Unknown'
|
||||
|
||||
|
||||
def strOfferState(state):
|
||||
@@ -196,13 +251,6 @@ 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'
|
||||
@@ -258,6 +306,13 @@ def strBidState(state):
|
||||
return 'Exchanged script lock tx sigs msg'
|
||||
if state == BidStates.XMR_SWAP_MSG_SCRIPT_LOCK_SPEND_TX:
|
||||
return 'Exchanged script lock spend tx msg'
|
||||
if state == BidStates.BID_REQUEST_SENT:
|
||||
return 'Request sent'
|
||||
if state == BidStates.BID_REQUEST_ACCEPTED:
|
||||
return 'Request accepted'
|
||||
if state == BidStates.BID_STATE_UNKNOWN:
|
||||
return 'Unknown bid state'
|
||||
|
||||
return 'Unknown' + ' ' + str(state)
|
||||
|
||||
|
||||
@@ -272,6 +327,10 @@ def strTxState(state):
|
||||
return 'Redeemed'
|
||||
if state == TxStates.TX_REFUNDED:
|
||||
return 'Refunded'
|
||||
if state == TxStates.TX_IN_MEMPOOL:
|
||||
return 'In Mempool'
|
||||
if state == TxStates.TX_IN_CHAIN:
|
||||
return 'In Chain'
|
||||
return 'Unknown'
|
||||
|
||||
|
||||
@@ -288,6 +347,8 @@ def strTxType(tx_type):
|
||||
return 'Chain A Lock Refund Swipe Tx'
|
||||
if tx_type == TxTypes.XMR_SWAP_B_LOCK:
|
||||
return 'Chain B Lock Tx'
|
||||
if tx_type == TxTypes.ITX_PRE_FUNDED:
|
||||
return 'Funded mock initiate tx'
|
||||
return 'Unknown'
|
||||
|
||||
|
||||
@@ -315,8 +376,6 @@ def getLockName(lock_type):
|
||||
|
||||
|
||||
def describeEventEntry(event_type, event_msg):
|
||||
if event_type == EventLogTypes.FAILED_TX_B_LOCK_PUBLISH:
|
||||
return 'Failed to publish lock tx B'
|
||||
if event_type == EventLogTypes.FAILED_TX_B_LOCK_PUBLISH:
|
||||
return 'Failed to publish lock tx B'
|
||||
if event_type == EventLogTypes.LOCK_TX_A_PUBLISHED:
|
||||
@@ -333,6 +392,8 @@ def describeEventEntry(event_type, event_msg):
|
||||
return 'Lock tx B seen in chain'
|
||||
if event_type == EventLogTypes.LOCK_TX_B_CONFIRMED:
|
||||
return 'Lock tx B confirmed in chain'
|
||||
if event_type == EventLogTypes.LOCK_TX_B_IN_MEMPOOL:
|
||||
return 'Lock tx B seen in mempool'
|
||||
if event_type == EventLogTypes.DEBUG_TWEAK_APPLIED:
|
||||
return 'Debug tweak applied ' + event_msg
|
||||
if event_type == EventLogTypes.FAILED_TX_B_REFUND:
|
||||
@@ -380,6 +441,8 @@ def describeEventEntry(event_type, event_msg):
|
||||
def getVoutByAddress(txjs, p2sh):
|
||||
for o in txjs['vout']:
|
||||
try:
|
||||
if 'address' in o['scriptPubKey'] and o['scriptPubKey']['address'] == p2sh:
|
||||
return o['n']
|
||||
if p2sh in o['scriptPubKey']['addresses']:
|
||||
return o['n']
|
||||
except Exception:
|
||||
@@ -387,14 +450,14 @@ def getVoutByAddress(txjs, p2sh):
|
||||
raise ValueError('Address output not found in txn')
|
||||
|
||||
|
||||
def getVoutByP2WSH(txjs, p2wsh_hex):
|
||||
def getVoutByScriptPubKey(txjs, scriptPubKey_hex: str) -> int:
|
||||
for o in txjs['vout']:
|
||||
try:
|
||||
if p2wsh_hex == o['scriptPubKey']['hex']:
|
||||
if scriptPubKey_hex == o['scriptPubKey']['hex']:
|
||||
return o['n']
|
||||
except Exception:
|
||||
pass
|
||||
raise ValueError('P2WSH output not found in txn')
|
||||
raise ValueError('scriptPubKey output not found in txn')
|
||||
|
||||
|
||||
def replaceAddrPrefix(addr, coin_type, chain_name, addr_type='pubkey_address'):
|
||||
@@ -424,27 +487,62 @@ def getLastBidState(packed_states):
|
||||
return BidStates.BID_STATE_UNKNOWN
|
||||
|
||||
|
||||
def strSwapType(swap_type):
|
||||
if swap_type == SwapTypes.SELLER_FIRST:
|
||||
return 'seller_first'
|
||||
if swap_type == SwapTypes.XMR_SWAP:
|
||||
return 'xmr_swap'
|
||||
return None
|
||||
|
||||
|
||||
def strSwapDesc(swap_type):
|
||||
if swap_type == SwapTypes.SELLER_FIRST:
|
||||
return 'Secret Hash'
|
||||
if swap_type == SwapTypes.XMR_SWAP:
|
||||
return 'Adaptor Sig'
|
||||
return None
|
||||
|
||||
|
||||
inactive_states = [BidStates.SWAP_COMPLETED, BidStates.BID_ERROR, BidStates.BID_REJECTED, BidStates.SWAP_TIMEDOUT, BidStates.BID_ABANDONED]
|
||||
|
||||
|
||||
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:
|
||||
return True
|
||||
if state == BidStates.XMR_SWAP_NOSCRIPT_COIN_LOCKED:
|
||||
return True
|
||||
if state == BidStates.XMR_SWAP_LOCK_RELEASED:
|
||||
return True
|
||||
if state == BidStates.XMR_SWAP_NOSCRIPT_TX_REDEEMED:
|
||||
return True
|
||||
if state == BidStates.XMR_SWAP_SCRIPT_TX_REDEEMED:
|
||||
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
|
||||
return state in (
|
||||
BidStates.SWAP_DELAYING,
|
||||
BidStates.XMR_SWAP_HAVE_SCRIPT_COIN_SPEND_TX,
|
||||
BidStates.XMR_SWAP_SCRIPT_COIN_LOCKED,
|
||||
BidStates.XMR_SWAP_NOSCRIPT_COIN_LOCKED,
|
||||
BidStates.XMR_SWAP_LOCK_RELEASED,
|
||||
BidStates.XMR_SWAP_NOSCRIPT_TX_REDEEMED,
|
||||
BidStates.XMR_SWAP_SCRIPT_TX_REDEEMED,
|
||||
BidStates.XMR_SWAP_SCRIPT_TX_PREREFUND,
|
||||
BidStates.XMR_SWAP_MSG_SCRIPT_LOCK_TX_SIGS,
|
||||
BidStates.XMR_SWAP_MSG_SCRIPT_LOCK_SPEND_TX,
|
||||
BidStates.XMR_SWAP_FAILED,
|
||||
BidStates.BID_REQUEST_ACCEPTED,
|
||||
)
|
||||
|
||||
|
||||
def isErrorBidState(state):
|
||||
return state in (
|
||||
BidStates.BID_STALLED_FOR_TEST,
|
||||
BidStates.BID_ERROR,
|
||||
)
|
||||
|
||||
|
||||
def isFailingBidState(state):
|
||||
return state in (
|
||||
BidStates.BID_STALLED_FOR_TEST,
|
||||
BidStates.BID_ERROR,
|
||||
BidStates.XMR_SWAP_SCRIPT_TX_PREREFUND,
|
||||
BidStates.XMR_SWAP_NOSCRIPT_TX_RECOVERED,
|
||||
BidStates.XMR_SWAP_FAILED_REFUNDED,
|
||||
BidStates.XMR_SWAP_FAILED_SWIPED,
|
||||
BidStates.XMR_SWAP_FAILED,
|
||||
)
|
||||
|
||||
|
||||
def isFinalBidState(state):
|
||||
return state in inactive_states
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2019-2022 tecnovert
|
||||
# Copyright (c) 2019-2023 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
@@ -31,6 +31,8 @@ class Coins(IntEnum):
|
||||
PIVX = 11
|
||||
DASH = 12
|
||||
FIRO = 13
|
||||
NAV = 14
|
||||
LTC_MWEB = 15
|
||||
|
||||
|
||||
chainparams = {
|
||||
@@ -218,6 +220,7 @@ chainparams = {
|
||||
'message_magic': 'DarkNet Signed Message:\n',
|
||||
'blocks_target': 60 * 1,
|
||||
'decimal_places': 8,
|
||||
'has_cltv': True,
|
||||
'has_csv': False,
|
||||
'has_segwit': False,
|
||||
'use_ticker_as_name': True,
|
||||
@@ -295,8 +298,9 @@ chainparams = {
|
||||
'message_magic': 'Zcoin Signed Message:\n',
|
||||
'blocks_target': 60 * 10,
|
||||
'decimal_places': 8,
|
||||
'has_csv': True,
|
||||
'has_segwit': True,
|
||||
'has_cltv': False,
|
||||
'has_csv': False,
|
||||
'has_segwit': False,
|
||||
'mainnet': {
|
||||
'rpcport': 8888,
|
||||
'pubkey_address': 82,
|
||||
@@ -321,7 +325,46 @@ chainparams = {
|
||||
'rpcport': 28888,
|
||||
'pubkey_address': 65,
|
||||
'script_address': 178,
|
||||
'key_prefix': 185,
|
||||
'key_prefix': 239,
|
||||
'hrp': '',
|
||||
'bip44': 1,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
}
|
||||
},
|
||||
Coins.NAV: {
|
||||
'name': 'navcoin',
|
||||
'ticker': 'NAV',
|
||||
'message_magic': 'Navcoin Signed Message:\n',
|
||||
'blocks_target': 30,
|
||||
'decimal_places': 8,
|
||||
'has_csv': True,
|
||||
'has_segwit': True,
|
||||
'mainnet': {
|
||||
'rpcport': 44444,
|
||||
'pubkey_address': 53,
|
||||
'script_address': 85,
|
||||
'key_prefix': 150,
|
||||
'hrp': '',
|
||||
'bip44': 130,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
},
|
||||
'testnet': {
|
||||
'rpcport': 44445,
|
||||
'pubkey_address': 111,
|
||||
'script_address': 196,
|
||||
'key_prefix': 239,
|
||||
'hrp': '',
|
||||
'bip44': 1,
|
||||
'min_amount': 1000,
|
||||
'max_amount': 100000 * COIN,
|
||||
},
|
||||
'regtest': {
|
||||
'rpcport': 44446,
|
||||
'pubkey_address': 111,
|
||||
'script_address': 196,
|
||||
'key_prefix': 239,
|
||||
'hrp': '',
|
||||
'bip44': 1,
|
||||
'min_amount': 1000,
|
||||
@@ -353,20 +396,20 @@ class CoinInterface:
|
||||
self._unknown_wallet_seed = True
|
||||
self._restore_height = None
|
||||
|
||||
def make_int(self, amount_in, r=0):
|
||||
def make_int(self, amount_in: int, r: int = 0) -> int:
|
||||
return make_int(amount_in, self.exp(), r=r)
|
||||
|
||||
def format_amount(self, amount_in, conv_int=False, r=0):
|
||||
amount_int = make_int(amount_in, self.exp(), r=r) if conv_int else amount_in
|
||||
return format_amount(amount_int, self.exp())
|
||||
|
||||
def coin_name(self):
|
||||
def coin_name(self) -> str:
|
||||
coin_chainparams = chainparams[self.coin_type()]
|
||||
if coin_chainparams.get('use_ticker_as_name', False):
|
||||
return coin_chainparams['ticker']
|
||||
return coin_chainparams['name'].capitalize()
|
||||
|
||||
def ticker(self):
|
||||
def ticker(self) -> str:
|
||||
ticker = chainparams[self.coin_type()]['ticker']
|
||||
if self._network == 'testnet':
|
||||
ticker = 't' + ticker
|
||||
@@ -374,23 +417,29 @@ class CoinInterface:
|
||||
ticker = 'rt' + ticker
|
||||
return ticker
|
||||
|
||||
def ticker_mainnet(self):
|
||||
def getExchangeTicker(self, exchange_name: str) -> str:
|
||||
return chainparams[self.coin_type()]['ticker']
|
||||
|
||||
def getExchangeName(self, exchange_name: str) -> str:
|
||||
return chainparams[self.coin_type()]['name']
|
||||
|
||||
def ticker_mainnet(self) -> str:
|
||||
ticker = chainparams[self.coin_type()]['ticker']
|
||||
return ticker
|
||||
|
||||
def min_amount(self):
|
||||
def min_amount(self) -> int:
|
||||
return chainparams[self.coin_type()][self._network]['min_amount']
|
||||
|
||||
def max_amount(self):
|
||||
def max_amount(self) -> int:
|
||||
return chainparams[self.coin_type()][self._network]['max_amount']
|
||||
|
||||
def setWalletSeedWarning(self, value):
|
||||
def setWalletSeedWarning(self, value: bool) -> None:
|
||||
self._unknown_wallet_seed = value
|
||||
|
||||
def setWalletRestoreHeight(self, value):
|
||||
def setWalletRestoreHeight(self, value: int) -> None:
|
||||
self._restore_height = value
|
||||
|
||||
def knownWalletSeed(self):
|
||||
def knownWalletSeed(self) -> bool:
|
||||
return not self._unknown_wallet_seed
|
||||
|
||||
def chainparams(self):
|
||||
@@ -399,16 +448,25 @@ class CoinInterface:
|
||||
def chainparams_network(self):
|
||||
return chainparams[self.coin_type()][self._network]
|
||||
|
||||
def is_transient_error(self, ex):
|
||||
def has_segwit(self) -> bool:
|
||||
return chainparams[self.coin_type()].get('has_segwit', True)
|
||||
|
||||
def is_transient_error(self, ex) -> bool:
|
||||
if isinstance(ex, TemporaryError):
|
||||
return True
|
||||
str_error = str(ex).lower()
|
||||
str_error: str = str(ex).lower()
|
||||
if 'not enough unlocked money' in str_error:
|
||||
return True
|
||||
if 'no unlocked balance' in str_error:
|
||||
return True
|
||||
if 'transaction was rejected by daemon' in str_error:
|
||||
return True
|
||||
if 'invalid unlocked_balance' in str_error:
|
||||
return True
|
||||
if 'daemon is busy' in str_error:
|
||||
return True
|
||||
if 'timed out' in str_error:
|
||||
return True
|
||||
if 'request-sent' in str_error:
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2019-2022 tecnovert
|
||||
# Copyright (c) 2019-2023 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
@@ -10,7 +10,7 @@ CONFIG_FILENAME = 'basicswap.json'
|
||||
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'))
|
||||
DEFAULT_TEST_BINDIR = os.path.expanduser(os.getenv('DEFAULT_TEST_BINDIR', '~/.basicswap/bin'))
|
||||
|
||||
bin_suffix = ('.exe' if os.name == 'nt' else '')
|
||||
PARTICL_BINDIR = os.path.expanduser(os.getenv('PARTICL_BINDIR', os.path.join(DEFAULT_TEST_BINDIR, 'particl')))
|
||||
@@ -36,18 +36,3 @@ 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)
|
||||
|
||||
DASH_BINDIR = os.path.expanduser(os.getenv('DASH_BINDIR', os.path.join(DEFAULT_TEST_BINDIR, 'dash')))
|
||||
DASHD = os.getenv('DASHD', 'dashd' + bin_suffix)
|
||||
DASH_CLI = os.getenv('DASH_CLI', 'dash-cli' + bin_suffix)
|
||||
DASH_TX = os.getenv('DASH_TX', 'dash-tx' + bin_suffix)
|
||||
|
||||
FIRO_BINDIR = os.path.expanduser(os.getenv('FIRO_BINDIR', os.path.join(DEFAULT_TEST_BINDIR, 'firo')))
|
||||
FIROD = os.getenv('FIROD', 'firod' + bin_suffix)
|
||||
FIRO_CLI = os.getenv('FIRO_CLI', 'firo-cli' + bin_suffix)
|
||||
FIRO_TX = os.getenv('FIRO_TX', 'firo-tx' + bin_suffix)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2019-2022 tecnovert
|
||||
# Copyright (c) 2019-2023 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
@@ -12,8 +12,8 @@ from enum import IntEnum, auto
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
|
||||
|
||||
CURRENT_DB_VERSION = 16
|
||||
CURRENT_DB_DATA_VERSION = 2
|
||||
CURRENT_DB_VERSION = 22
|
||||
CURRENT_DB_DATA_VERSION = 4
|
||||
Base = declarative_base()
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ class Offer(Base):
|
||||
|
||||
proof_address = sa.Column(sa.String)
|
||||
proof_signature = sa.Column(sa.LargeBinary)
|
||||
proof_utxos = sa.Column(sa.LargeBinary)
|
||||
pkhash_seller = sa.Column(sa.LargeBinary)
|
||||
secret_hash = sa.Column(sa.LargeBinary)
|
||||
|
||||
@@ -74,7 +75,7 @@ class Offer(Base):
|
||||
addr_to = sa.Column(sa.String)
|
||||
created_at = sa.Column(sa.BigInteger)
|
||||
expire_at = sa.Column(sa.BigInteger)
|
||||
was_sent = sa.Column(sa.Boolean)
|
||||
was_sent = sa.Column(sa.Boolean) # Sent by node
|
||||
|
||||
from_feerate = sa.Column(sa.BigInteger)
|
||||
to_feerate = sa.Column(sa.BigInteger)
|
||||
@@ -86,6 +87,7 @@ class Offer(Base):
|
||||
auto_accept_bids = sa.Column(sa.Boolean)
|
||||
withdraw_to_addr = sa.Column(sa.String) # Address to spend lock tx to - address from wallet if empty TODO
|
||||
security_token = sa.Column(sa.LargeBinary)
|
||||
bid_reversed = sa.Column(sa.Boolean)
|
||||
|
||||
state = sa.Column(sa.Integer)
|
||||
states = sa.Column(sa.LargeBinary) # Packed states and times
|
||||
@@ -107,13 +109,14 @@ class Bid(Base):
|
||||
active_ind = sa.Column(sa.Integer)
|
||||
|
||||
protocol_version = sa.Column(sa.Integer)
|
||||
was_sent = sa.Column(sa.Boolean)
|
||||
was_sent = sa.Column(sa.Boolean) # Sent by node
|
||||
was_received = sa.Column(sa.Boolean)
|
||||
contract_count = sa.Column(sa.Integer)
|
||||
created_at = sa.Column(sa.BigInteger)
|
||||
expire_at = sa.Column(sa.BigInteger)
|
||||
bid_addr = sa.Column(sa.String)
|
||||
proof_address = sa.Column(sa.String)
|
||||
proof_utxos = sa.Column(sa.LargeBinary)
|
||||
withdraw_to_addr = sa.Column(sa.String) # Address to spend lock tx to - address from wallet if empty TODO
|
||||
|
||||
recovered_secret = sa.Column(sa.LargeBinary)
|
||||
@@ -123,7 +126,6 @@ class Bid(Base):
|
||||
amount = sa.Column(sa.BigInteger)
|
||||
rate = sa.Column(sa.BigInteger)
|
||||
|
||||
accept_msg_id = sa.Column(sa.LargeBinary)
|
||||
pkhash_seller = sa.Column(sa.LargeBinary)
|
||||
|
||||
initiate_txn_redeem = sa.Column(sa.LargeBinary)
|
||||
@@ -217,10 +219,25 @@ class SwapTx(Base):
|
||||
states = sa.Column(sa.LargeBinary) # Packed states and times
|
||||
|
||||
def setState(self, new_state):
|
||||
if self.state == new_state:
|
||||
return
|
||||
self.state = new_state
|
||||
self.states = (self.states if self.states is not None else bytes()) + struct.pack('<iq', new_state, int(time.time()))
|
||||
|
||||
|
||||
class PrefundedTx(Base):
|
||||
__tablename__ = 'prefunded_transactions'
|
||||
|
||||
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
active_ind = sa.Column(sa.Integer)
|
||||
created_at = sa.Column(sa.BigInteger)
|
||||
linked_type = sa.Column(sa.Integer)
|
||||
linked_id = sa.Column(sa.LargeBinary)
|
||||
tx_type = sa.Column(sa.Integer) # TxTypes
|
||||
tx_data = sa.Column(sa.LargeBinary)
|
||||
used_by = sa.Column(sa.LargeBinary)
|
||||
|
||||
|
||||
class PooledAddress(Base):
|
||||
__tablename__ = 'addresspool'
|
||||
|
||||
@@ -277,12 +294,13 @@ class EventLog(Base):
|
||||
|
||||
class XmrOffer(Base):
|
||||
__tablename__ = 'xmr_offers'
|
||||
# TODO: Merge to Offer
|
||||
|
||||
swap_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
offer_id = sa.Column(sa.LargeBinary, sa.ForeignKey('offers.offer_id'))
|
||||
|
||||
a_fee_rate = sa.Column(sa.BigInteger)
|
||||
b_fee_rate = sa.Column(sa.BigInteger)
|
||||
a_fee_rate = sa.Column(sa.BigInteger) # Chain a fee rate
|
||||
b_fee_rate = sa.Column(sa.BigInteger) # Chain b fee rate
|
||||
|
||||
lock_time_1 = sa.Column(sa.Integer) # Delay before the chain a lock refund tx can be mined
|
||||
lock_time_2 = sa.Column(sa.Integer) # Delay before the follower can spend from the chain a lock refund tx
|
||||
@@ -293,16 +311,6 @@ class XmrSwap(Base):
|
||||
|
||||
swap_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
bid_id = sa.Column(sa.LargeBinary, sa.ForeignKey('bids.bid_id'))
|
||||
bid_msg_id2 = sa.Column(sa.LargeBinary)
|
||||
bid_msg_id3 = sa.Column(sa.LargeBinary)
|
||||
|
||||
bid_accept_msg_id = sa.Column(sa.LargeBinary)
|
||||
bid_accept_msg_id2 = sa.Column(sa.LargeBinary)
|
||||
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_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)
|
||||
|
||||
@@ -363,6 +371,8 @@ class XmrSplitData(Base):
|
||||
__tablename__ = 'xmr_split_data'
|
||||
|
||||
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
addr_from = sa.Column(sa.String)
|
||||
addr_to = sa.Column(sa.String)
|
||||
bid_id = sa.Column(sa.LargeBinary)
|
||||
msg_type = sa.Column(sa.Integer)
|
||||
msg_sequence = sa.Column(sa.Integer)
|
||||
@@ -408,6 +418,9 @@ class KnownIdentity(Base):
|
||||
num_recv_bids_rejected = sa.Column(sa.Integer)
|
||||
num_sent_bids_failed = sa.Column(sa.Integer)
|
||||
num_recv_bids_failed = sa.Column(sa.Integer)
|
||||
automation_override = sa.Column(sa.Integer) # AutomationOverrideOptions
|
||||
visibility_override = sa.Column(sa.Integer) # VisibilityOverrideOptions
|
||||
data = sa.Column(sa.LargeBinary)
|
||||
note = sa.Column(sa.String)
|
||||
updated_at = sa.Column(sa.BigInteger)
|
||||
created_at = sa.Column(sa.BigInteger)
|
||||
@@ -469,6 +482,9 @@ class BidState(Base):
|
||||
state_id = sa.Column(sa.Integer)
|
||||
label = sa.Column(sa.String)
|
||||
in_progress = sa.Column(sa.Integer)
|
||||
in_error = sa.Column(sa.Integer)
|
||||
swap_failed = sa.Column(sa.Integer)
|
||||
swap_ended = sa.Column(sa.Integer)
|
||||
|
||||
note = sa.Column(sa.String)
|
||||
created_at = sa.Column(sa.BigInteger)
|
||||
@@ -482,3 +498,19 @@ class Notification(Base):
|
||||
created_at = sa.Column(sa.BigInteger)
|
||||
event_type = sa.Column(sa.Integer)
|
||||
event_data = sa.Column(sa.LargeBinary)
|
||||
|
||||
|
||||
class MessageLink(Base):
|
||||
__tablename__ = 'message_links'
|
||||
|
||||
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||
active_ind = sa.Column(sa.Integer)
|
||||
created_at = sa.Column(sa.BigInteger)
|
||||
|
||||
linked_type = sa.Column(sa.Integer)
|
||||
linked_id = sa.Column(sa.LargeBinary)
|
||||
# linked_row_id = sa.Column(sa.Integer) # TODO: Find a way to use table rowids
|
||||
|
||||
msg_type = sa.Column(sa.Integer)
|
||||
msg_sequence = sa.Column(sa.Integer)
|
||||
msg_id = sa.Column(sa.LargeBinary)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2022 tecnovert
|
||||
# Copyright (c) 2022-2023 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
@@ -18,7 +18,11 @@ from .db import (
|
||||
from .basicswap_util import (
|
||||
BidStates,
|
||||
strBidState,
|
||||
isActiveBidState)
|
||||
isActiveBidState,
|
||||
isErrorBidState,
|
||||
isFailingBidState,
|
||||
isFinalBidState,
|
||||
)
|
||||
|
||||
|
||||
def upgradeDatabaseData(self, data_version):
|
||||
@@ -56,10 +60,13 @@ def upgradeDatabaseData(self, data_version):
|
||||
active_ind=1,
|
||||
state_id=int(state),
|
||||
in_progress=isActiveBidState(state),
|
||||
in_error=isErrorBidState(state),
|
||||
swap_failed=isFailingBidState(state),
|
||||
swap_ended=isFinalBidState(state),
|
||||
label=strBidState(state),
|
||||
created_at=now))
|
||||
|
||||
if data_version < 2:
|
||||
if data_version > 0 and 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,
|
||||
@@ -67,6 +74,23 @@ def upgradeDatabaseData(self, data_version):
|
||||
in_progress=isActiveBidState(state),
|
||||
label=strBidState(state),
|
||||
created_at=now))
|
||||
if data_version > 0 and data_version < 3:
|
||||
for state in BidStates:
|
||||
in_error = isErrorBidState(state)
|
||||
swap_failed = isFailingBidState(state)
|
||||
swap_ended = isFinalBidState(state)
|
||||
session.execute('UPDATE bidstates SET in_error = :in_error, swap_failed = :swap_failed, swap_ended = :swap_ended WHERE state_id = :state_id', {'in_error': in_error, 'swap_failed': swap_failed, 'swap_ended': swap_ended, 'state_id': int(state)})
|
||||
if data_version > 0 and data_version < 4:
|
||||
for state in (BidStates.BID_REQUEST_SENT, BidStates.BID_REQUEST_ACCEPTED):
|
||||
session.add(BidState(
|
||||
active_ind=1,
|
||||
state_id=int(state),
|
||||
in_progress=isActiveBidState(state),
|
||||
in_error=isErrorBidState(state),
|
||||
swap_failed=isFailingBidState(state),
|
||||
swap_ended=isFinalBidState(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)
|
||||
@@ -225,6 +249,54 @@ def upgradeDatabase(self, db_version):
|
||||
event_data BLOB,
|
||||
created_at BIGINT,
|
||||
PRIMARY KEY (record_id))''')
|
||||
elif current_version == 16:
|
||||
db_version += 1
|
||||
session.execute('''
|
||||
CREATE TABLE prefunded_transactions (
|
||||
record_id INTEGER NOT NULL,
|
||||
active_ind INTEGER,
|
||||
created_at BIGINT,
|
||||
linked_type INTEGER,
|
||||
linked_id BLOB,
|
||||
tx_type INTEGER,
|
||||
tx_data BLOB,
|
||||
used_by BLOB,
|
||||
PRIMARY KEY (record_id))''')
|
||||
elif current_version == 17:
|
||||
db_version += 1
|
||||
session.execute('ALTER TABLE knownidentities ADD COLUMN automation_override INTEGER')
|
||||
session.execute('ALTER TABLE knownidentities ADD COLUMN visibility_override INTEGER')
|
||||
session.execute('ALTER TABLE knownidentities ADD COLUMN data BLOB')
|
||||
session.execute('UPDATE knownidentities SET active_ind = 1')
|
||||
elif current_version == 18:
|
||||
db_version += 1
|
||||
session.execute('ALTER TABLE xmr_split_data ADD COLUMN addr_from STRING')
|
||||
session.execute('ALTER TABLE xmr_split_data ADD COLUMN addr_to STRING')
|
||||
elif current_version == 19:
|
||||
db_version += 1
|
||||
session.execute('ALTER TABLE bidstates ADD COLUMN in_error INTEGER')
|
||||
session.execute('ALTER TABLE bidstates ADD COLUMN swap_failed INTEGER')
|
||||
session.execute('ALTER TABLE bidstates ADD COLUMN swap_ended INTEGER')
|
||||
elif current_version == 20:
|
||||
db_version += 1
|
||||
session.execute('''
|
||||
CREATE TABLE message_links (
|
||||
record_id INTEGER NOT NULL,
|
||||
active_ind INTEGER,
|
||||
created_at BIGINT,
|
||||
|
||||
linked_type INTEGER,
|
||||
linked_id BLOB,
|
||||
|
||||
msg_type INTEGER,
|
||||
msg_sequence INTEGER,
|
||||
msg_id BLOB,
|
||||
PRIMARY KEY (record_id))''')
|
||||
session.execute('ALTER TABLE offers ADD COLUMN bid_reversed INTEGER')
|
||||
elif current_version == 21:
|
||||
db_version += 1
|
||||
session.execute('ALTER TABLE offers ADD COLUMN proof_utxos BLOB')
|
||||
session.execute('ALTER TABLE bids ADD COLUMN proof_utxos BLOB')
|
||||
|
||||
if current_version != db_version:
|
||||
self.db_version = db_version
|
||||
|
||||
56
basicswap/db_util.py
Normal file
@@ -0,0 +1,56 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2023 The BSX Developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from .db import (
|
||||
Concepts,
|
||||
)
|
||||
|
||||
|
||||
def remove_expired_data(self, time_offset: int = 0):
|
||||
now: int = self.getTime()
|
||||
try:
|
||||
session = self.openSession()
|
||||
|
||||
active_bids_insert = self.activeBidsQueryStr(now, '', 'b2')
|
||||
query_str = f'''
|
||||
SELECT o.offer_id FROM offers o
|
||||
WHERE o.expire_at <= :expired_at AND 0 = (SELECT COUNT(*) FROM bids b2 WHERE b2.offer_id = o.offer_id AND {active_bids_insert})
|
||||
'''
|
||||
num_offers = 0
|
||||
num_bids = 0
|
||||
offer_rows = session.execute(query_str, {'expired_at': now - time_offset})
|
||||
for offer_row in offer_rows:
|
||||
num_offers += 1
|
||||
bid_rows = session.execute('SELECT bids.bid_id FROM bids WHERE bids.offer_id = :offer_id', {'offer_id': offer_row[0]})
|
||||
for bid_row in bid_rows:
|
||||
num_bids += 1
|
||||
session.execute('DELETE FROM transactions WHERE transactions.bid_id = :bid_id', {'bid_id': bid_row[0]})
|
||||
session.execute('DELETE FROM eventlog WHERE eventlog.linked_type = :type_ind AND eventlog.linked_id = :bid_id', {'type_ind': int(Concepts.BID), 'bid_id': bid_row[0]})
|
||||
session.execute('DELETE FROM automationlinks WHERE automationlinks.linked_type = :type_ind AND automationlinks.linked_id = :bid_id', {'type_ind': int(Concepts.BID), 'bid_id': bid_row[0]})
|
||||
session.execute('DELETE FROM prefunded_transactions WHERE prefunded_transactions.linked_type = :type_ind AND prefunded_transactions.linked_id = :bid_id', {'type_ind': int(Concepts.BID), 'bid_id': bid_row[0]})
|
||||
session.execute('DELETE FROM history WHERE history.concept_type = :type_ind AND history.concept_id = :bid_id', {'type_ind': int(Concepts.BID), 'bid_id': bid_row[0]})
|
||||
session.execute('DELETE FROM xmr_swaps WHERE xmr_swaps.bid_id = :bid_id', {'bid_id': bid_row[0]})
|
||||
session.execute('DELETE FROM actions WHERE actions.linked_id = :bid_id', {'bid_id': bid_row[0]})
|
||||
session.execute('DELETE FROM addresspool WHERE addresspool.bid_id = :bid_id', {'bid_id': bid_row[0]})
|
||||
session.execute('DELETE FROM xmr_split_data WHERE xmr_split_data.bid_id = :bid_id', {'bid_id': bid_row[0]})
|
||||
session.execute('DELETE FROM bids WHERE bids.bid_id = :bid_id', {'bid_id': bid_row[0]})
|
||||
session.execute('DELETE FROM message_links WHERE linked_type = :type_ind AND linked_id = :linked_id', {'type_ind': int(Concepts.BID), 'linked_id': bid_row[0]})
|
||||
|
||||
session.execute('DELETE FROM eventlog WHERE eventlog.linked_type = :type_ind AND eventlog.linked_id = :offer_id', {'type_ind': int(Concepts.OFFER), 'offer_id': offer_row[0]})
|
||||
session.execute('DELETE FROM automationlinks WHERE automationlinks.linked_type = :type_ind AND automationlinks.linked_id = :offer_id', {'type_ind': int(Concepts.OFFER), 'offer_id': offer_row[0]})
|
||||
session.execute('DELETE FROM prefunded_transactions WHERE prefunded_transactions.linked_type = :type_ind AND prefunded_transactions.linked_id = :offer_id', {'type_ind': int(Concepts.OFFER), 'offer_id': offer_row[0]})
|
||||
session.execute('DELETE FROM history WHERE history.concept_type = :type_ind AND history.concept_id = :offer_id', {'type_ind': int(Concepts.OFFER), 'offer_id': offer_row[0]})
|
||||
session.execute('DELETE FROM xmr_offers WHERE xmr_offers.offer_id = :offer_id', {'offer_id': offer_row[0]})
|
||||
session.execute('DELETE FROM sentoffers WHERE sentoffers.offer_id = :offer_id', {'offer_id': offer_row[0]})
|
||||
session.execute('DELETE FROM actions WHERE actions.linked_id = :offer_id', {'offer_id': offer_row[0]})
|
||||
session.execute('DELETE FROM offers WHERE offers.offer_id = :offer_id', {'offer_id': offer_row[0]})
|
||||
session.execute('DELETE FROM message_links WHERE linked_type = :type_ind AND linked_id = :offer_id', {'type_ind': int(Concepts.OFFER), 'offer_id': offer_row[0]})
|
||||
|
||||
if num_offers > 0 or num_bids > 0:
|
||||
self.log.info('Removed data for {} expired offer{} and {} bid{}.'.format(num_offers, 's' if num_offers != 1 else '', num_bids, 's' if num_bids != 1 else ''))
|
||||
|
||||
finally:
|
||||
self.closeSession(session)
|
||||
@@ -1,11 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2019-2022 tecnovert
|
||||
# Copyright (c) 2019-2023 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
import json
|
||||
import urllib.request
|
||||
|
||||
|
||||
class Explorer():
|
||||
@@ -17,12 +16,7 @@ class Explorer():
|
||||
|
||||
def readURL(self, url):
|
||||
self.log.debug('Explorer url: {}'.format(url))
|
||||
try:
|
||||
self.swapclient.setConnectionParameters()
|
||||
req = urllib.request.Request(url)
|
||||
return urllib.request.urlopen(req).read()
|
||||
finally:
|
||||
self.swapclient.popConnectionParameters()
|
||||
return self.swapclient.readURL(url)
|
||||
|
||||
|
||||
class ExplorerInsight(Explorer):
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2019-2022 tecnovert
|
||||
# Copyright (c) 2019-2024 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
import os
|
||||
import json
|
||||
import shlex
|
||||
import traceback
|
||||
import threading
|
||||
import http.client
|
||||
@@ -16,7 +17,8 @@ from jinja2 import Environment, PackageLoader
|
||||
from . import __version__
|
||||
from .util import (
|
||||
dumpj,
|
||||
ensure,
|
||||
toBool,
|
||||
LockedCoinError,
|
||||
format_timestamp,
|
||||
)
|
||||
from .chainparams import (
|
||||
@@ -24,9 +26,8 @@ from .chainparams import (
|
||||
chainparams,
|
||||
)
|
||||
from .basicswap_util import (
|
||||
strBidState,
|
||||
strTxState,
|
||||
strAddressType,
|
||||
strBidState,
|
||||
)
|
||||
|
||||
from .js_server import (
|
||||
@@ -36,7 +37,7 @@ from .js_server import (
|
||||
from .ui.util import (
|
||||
getCoinName,
|
||||
get_data_entry,
|
||||
have_data_entry,
|
||||
get_data_entry_or,
|
||||
listAvailableCoins,
|
||||
)
|
||||
from .ui.page_automation import (
|
||||
@@ -49,22 +50,16 @@ from .ui.page_bids import page_bids, page_bid
|
||||
from .ui.page_offers import page_offers, page_offer, page_newoffer
|
||||
from .ui.page_tor import page_tor, get_tor_established_state
|
||||
from .ui.page_wallet import page_wallets, page_wallet
|
||||
|
||||
from .ui.page_settings import page_settings
|
||||
from .ui.page_encryption import page_changepassword, page_unlock, page_lock
|
||||
from .ui.page_identity import page_identity
|
||||
from .ui.page_smsgaddresses import page_smsgaddresses
|
||||
from .ui.page_debug import page_debug
|
||||
|
||||
env = Environment(loader=PackageLoader('basicswap', 'templates'))
|
||||
env.filters['formatts'] = format_timestamp
|
||||
|
||||
|
||||
def validateTextInput(text, name, messages, max_length=None):
|
||||
if max_length is not None and len(text) > max_length:
|
||||
messages.append(f'Error: {name} is too long')
|
||||
return False
|
||||
if len(text) > 0 and all(c.isalnum() or c.isspace() for c in text) is False:
|
||||
messages.append(f'Error: {name} must consist of only letters and digits')
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def extractDomain(url):
|
||||
return url.split('://', 1)[1].split('/', 1)[0]
|
||||
|
||||
@@ -88,6 +83,31 @@ def listExplorerActions(swap_client):
|
||||
return actions
|
||||
|
||||
|
||||
def parse_cmd(cmd: str, type_map: str):
|
||||
params = shlex.split(cmd)
|
||||
if len(params) < 1:
|
||||
return '', []
|
||||
method = params[0]
|
||||
typed_params = []
|
||||
params = params[1:]
|
||||
|
||||
for i, param in enumerate(params):
|
||||
if i >= len(type_map):
|
||||
type_ind = 's'
|
||||
else:
|
||||
type_ind = type_map[i]
|
||||
if type_ind == 'i':
|
||||
typed_params.append(int(param))
|
||||
elif type_ind == 'b':
|
||||
typed_params.append(toBool(param))
|
||||
elif type_ind == 'j':
|
||||
typed_params.append(json.loads(param))
|
||||
else:
|
||||
typed_params.append(param)
|
||||
|
||||
return method, typed_params
|
||||
|
||||
|
||||
class HttpHandler(BaseHTTPRequestHandler):
|
||||
|
||||
def log_error(self, format, *args):
|
||||
@@ -111,7 +131,7 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||
self.server.last_form_id[name] = form_id
|
||||
return form_data
|
||||
|
||||
def render_template(self, template, args_dict, status_code=200):
|
||||
def render_template(self, template, args_dict, status_code=200, version=__version__):
|
||||
swap_client = self.server.swap_client
|
||||
if swap_client.ws_server:
|
||||
args_dict['ws_url'] = swap_client.ws_server.url
|
||||
@@ -131,7 +151,6 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||
if swap_client._show_notifications:
|
||||
args_dict['notifications'] = swap_client.getNotifications()
|
||||
|
||||
# TODO: Remove _withids
|
||||
if 'messages' in args_dict:
|
||||
messages_with_ids = []
|
||||
for msg in args_dict['messages']:
|
||||
@@ -145,9 +164,19 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||
self.server.msg_id_counter += 1
|
||||
args_dict['err_messages'] = err_messages_with_ids
|
||||
|
||||
shutdown_token = os.urandom(8).hex()
|
||||
self.server.session_tokens['shutdown'] = shutdown_token
|
||||
args_dict['shutdown_token'] = shutdown_token
|
||||
|
||||
encrypted, locked = swap_client.getLockedState()
|
||||
args_dict['encrypted'] = encrypted
|
||||
args_dict['locked'] = locked
|
||||
|
||||
if self.server.msg_id_counter >= 0x7FFFFFFF:
|
||||
self.server.msg_id_counter = 0
|
||||
|
||||
args_dict['version'] = version
|
||||
|
||||
self.putHeaders(status_code, 'text/html')
|
||||
return bytes(template.render(
|
||||
title=self.server.title,
|
||||
@@ -185,6 +214,7 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||
|
||||
def page_explorers(self, url_split, post_string):
|
||||
swap_client = self.server.swap_client
|
||||
swap_client.checkSystemStatus()
|
||||
summary = swap_client.getSummary()
|
||||
|
||||
result = None
|
||||
@@ -231,45 +261,64 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||
|
||||
def page_rpc(self, url_split, post_string):
|
||||
swap_client = self.server.swap_client
|
||||
swap_client.checkSystemStatus()
|
||||
summary = swap_client.getSummary()
|
||||
|
||||
result = None
|
||||
coin_type = -1
|
||||
coin_id = -1
|
||||
call_type = 'cli'
|
||||
type_map = ''
|
||||
messages = []
|
||||
err_messages = []
|
||||
form_data = self.checkForm(post_string, 'rpc', err_messages)
|
||||
if form_data:
|
||||
try:
|
||||
coin_id = int(form_data[b'coin_type'][0])
|
||||
if coin_id in (-2, -3, -4):
|
||||
coin_type = Coins(Coins.XMR)
|
||||
else:
|
||||
coin_type = Coins(coin_id)
|
||||
except Exception:
|
||||
raise ValueError('Unknown Coin Type')
|
||||
call_type = get_data_entry_or(form_data, 'call_type', 'cli')
|
||||
type_map = get_data_entry_or(form_data, 'type_map', '')
|
||||
try:
|
||||
coin_id = int(get_data_entry(form_data, 'coin_type'))
|
||||
if coin_id in (-2, -3, -4):
|
||||
coin_type = Coins(Coins.XMR)
|
||||
elif coin_id in (-5,):
|
||||
coin_type = Coins(Coins.LTC)
|
||||
else:
|
||||
coin_type = Coins(coin_id)
|
||||
except Exception:
|
||||
raise ValueError('Unknown Coin Type')
|
||||
|
||||
cmd = form_data[b'cmd'][0].decode('utf-8')
|
||||
|
||||
try:
|
||||
try:
|
||||
cmd = get_data_entry(form_data, 'cmd')
|
||||
except Exception:
|
||||
raise ValueError('Invalid command')
|
||||
if coin_type == Coins.XMR:
|
||||
ci = swap_client.ci(coin_type)
|
||||
arr = cmd.split(None, 1)
|
||||
method = arr[0]
|
||||
params = json.loads(arr[1]) if len(arr) > 1 else []
|
||||
if coin_id == -4:
|
||||
rv = ci.rpc_wallet_cb(method, params)
|
||||
rv = ci.rpc_wallet(method, params)
|
||||
elif coin_id == -3:
|
||||
rv = ci.rpc_cb(method, params)
|
||||
rv = ci.rpc(method, params)
|
||||
elif coin_id == -2:
|
||||
if params == []:
|
||||
params = None
|
||||
rv = ci.rpc_cb2(method, params)
|
||||
rv = ci.rpc2(method, params)
|
||||
else:
|
||||
raise ValueError('Unknown XMR RPC variant')
|
||||
result = json.dumps(rv, indent=4)
|
||||
else:
|
||||
result = cmd + '\n' + swap_client.callcoincli(coin_type, cmd)
|
||||
if call_type == 'http':
|
||||
method, params = parse_cmd(cmd, type_map)
|
||||
if coin_id == -5:
|
||||
rv = swap_client.ci(coin_type).rpc_wallet_mweb(method, params)
|
||||
else:
|
||||
rv = swap_client.ci(coin_type).rpc_wallet(method, params)
|
||||
if not isinstance(rv, str):
|
||||
rv = json.dumps(rv, indent=4)
|
||||
result = cmd + '\n' + rv
|
||||
else:
|
||||
result = cmd + '\n' + swap_client.callcoincli(coin_type, cmd)
|
||||
except Exception as ex:
|
||||
result = str(ex)
|
||||
if self.server.swap_client.debug is True:
|
||||
@@ -278,47 +327,30 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||
template = env.get_template('rpc.html')
|
||||
|
||||
coins = listAvailableCoins(swap_client, with_variants=False)
|
||||
with_xmr: bool = any(c[0] == Coins.XMR for c in coins)
|
||||
coins = [c for c in coins if c[0] != Coins.XMR]
|
||||
coins.append((-2, 'Monero'))
|
||||
coins.append((-3, 'Monero JSON'))
|
||||
coins.append((-4, 'Monero Wallet'))
|
||||
|
||||
if any(c[0] == Coins.LTC for c in coins):
|
||||
coins.append((-5, 'Litecoin MWEB Wallet'))
|
||||
if with_xmr:
|
||||
coins.append((-2, 'Monero'))
|
||||
coins.append((-3, 'Monero JSON'))
|
||||
coins.append((-4, 'Monero Wallet'))
|
||||
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
'err_messages': err_messages,
|
||||
'coins': coins,
|
||||
'coin_type': coin_id,
|
||||
'call_type': call_type,
|
||||
'result': result,
|
||||
'messages': messages,
|
||||
'summary': summary,
|
||||
})
|
||||
|
||||
def page_debug(self, url_split, post_string):
|
||||
swap_client = self.server.swap_client
|
||||
summary = swap_client.getSummary()
|
||||
|
||||
result = None
|
||||
messages = []
|
||||
err_messages = []
|
||||
form_data = self.checkForm(post_string, 'wallets', err_messages)
|
||||
if form_data:
|
||||
if have_data_entry(form_data, 'reinit_xmr'):
|
||||
try:
|
||||
swap_client.initialiseWallet(Coins.XMR)
|
||||
messages.append('Done.')
|
||||
except Exception as a:
|
||||
err_messages.append('Failed.')
|
||||
|
||||
template = env.get_template('debug.html')
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
'err_messages': err_messages,
|
||||
'result': result,
|
||||
'summary': summary,
|
||||
})
|
||||
|
||||
def page_active(self, url_split, post_string):
|
||||
swap_client = self.server.swap_client
|
||||
swap_client.checkSystemStatus()
|
||||
active_swaps = swap_client.listSwapsInProgress()
|
||||
summary = swap_client.getSummary()
|
||||
|
||||
@@ -329,89 +361,9 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||
'summary': summary,
|
||||
})
|
||||
|
||||
def page_settings(self, url_split, post_string):
|
||||
swap_client = self.server.swap_client
|
||||
summary = swap_client.getSummary()
|
||||
|
||||
messages = []
|
||||
err_messages = []
|
||||
form_data = self.checkForm(post_string, 'settings', err_messages)
|
||||
if form_data:
|
||||
for name, c in swap_client.settings['chainclients'].items():
|
||||
if have_data_entry(form_data, 'apply_' + name):
|
||||
data = {'lookups': get_data_entry(form_data, 'lookups_' + name)}
|
||||
if name == 'monero':
|
||||
data['fee_priority'] = int(get_data_entry(form_data, 'fee_priority_' + name))
|
||||
data['manage_daemon'] = True if get_data_entry(form_data, 'managedaemon_' + name) == 'true' else False
|
||||
data['rpchost'] = get_data_entry(form_data, 'rpchost_' + name)
|
||||
data['rpcport'] = int(get_data_entry(form_data, 'rpcport_' + name))
|
||||
data['remotedaemonurls'] = get_data_entry(form_data, 'remotedaemonurls_' + name)
|
||||
data['automatically_select_daemon'] = True if get_data_entry(form_data, 'autosetdaemon_' + name) == 'true' else False
|
||||
else:
|
||||
data['conf_target'] = int(get_data_entry(form_data, 'conf_target_' + name))
|
||||
if name == 'particl':
|
||||
data['anon_tx_ring_size'] = int(get_data_entry(form_data, 'rct_ring_size_' + name))
|
||||
|
||||
settings_changed, suggest_reboot = swap_client.editSettings(name, data)
|
||||
if settings_changed is True:
|
||||
messages.append('Settings applied.')
|
||||
if suggest_reboot is True:
|
||||
messages.append('Please restart BasicSwap.')
|
||||
elif have_data_entry(form_data, 'enable_' + name):
|
||||
swap_client.enableCoin(name)
|
||||
display_name = getCoinName(swap_client.getCoinIdFromName(name))
|
||||
messages.append(display_name + ' enabled, shutting down.')
|
||||
swap_client.stopRunning()
|
||||
elif have_data_entry(form_data, 'disable_' + name):
|
||||
swap_client.disableCoin(name)
|
||||
display_name = getCoinName(swap_client.getCoinIdFromName(name))
|
||||
messages.append(display_name + ' disabled, shutting down.')
|
||||
swap_client.stopRunning()
|
||||
chains_formatted = []
|
||||
|
||||
sorted_names = sorted(swap_client.settings['chainclients'].keys())
|
||||
for name in sorted_names:
|
||||
c = swap_client.settings['chainclients'][name]
|
||||
try:
|
||||
display_name = getCoinName(swap_client.getCoinIdFromName(name))
|
||||
except Exception:
|
||||
display_name = name
|
||||
chains_formatted.append({
|
||||
'name': name,
|
||||
'display_name': display_name,
|
||||
'lookups': c.get('chain_lookups', 'local'),
|
||||
'manage_daemon': c.get('manage_daemon', 'Unknown'),
|
||||
'connection_type': c.get('connection_type', 'Unknown'),
|
||||
})
|
||||
if name == 'monero':
|
||||
chains_formatted[-1]['fee_priority'] = c.get('fee_priority', 0)
|
||||
chains_formatted[-1]['manage_wallet_daemon'] = c.get('manage_wallet_daemon', 'Unknown')
|
||||
chains_formatted[-1]['rpchost'] = c.get('rpchost', 'localhost')
|
||||
chains_formatted[-1]['rpcport'] = int(c.get('rpcport', 18081))
|
||||
chains_formatted[-1]['remotedaemonurls'] = '\n'.join(c.get('remote_daemon_urls', []))
|
||||
chains_formatted[-1]['autosetdaemon'] = c.get('automatically_select_daemon', False)
|
||||
else:
|
||||
chains_formatted[-1]['conf_target'] = c.get('conf_target', 2)
|
||||
|
||||
if name == 'particl':
|
||||
chains_formatted[-1]['anon_tx_ring_size'] = c.get('anon_tx_ring_size', 12)
|
||||
else:
|
||||
if c.get('connection_type', 'Unknown') == 'none':
|
||||
if 'connection_type_prev' in c:
|
||||
chains_formatted[-1]['can_reenable'] = True
|
||||
else:
|
||||
chains_formatted[-1]['can_disable'] = True
|
||||
|
||||
template = env.get_template('settings.html')
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
'err_messages': err_messages,
|
||||
'chains': chains_formatted,
|
||||
'summary': summary,
|
||||
})
|
||||
|
||||
def page_watched(self, url_split, post_string):
|
||||
swap_client = self.server.swap_client
|
||||
swap_client.checkSystemStatus()
|
||||
watched_outputs, last_scanned = swap_client.listWatchedOutputs()
|
||||
summary = swap_client.getSummary()
|
||||
|
||||
@@ -423,125 +375,6 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||
'summary': summary,
|
||||
})
|
||||
|
||||
def page_smsgaddresses(self, url_split, post_string):
|
||||
swap_client = self.server.swap_client
|
||||
summary = swap_client.getSummary()
|
||||
|
||||
page_data = {}
|
||||
messages = []
|
||||
err_messages = []
|
||||
smsgaddresses = []
|
||||
|
||||
listaddresses = True
|
||||
form_data = self.checkForm(post_string, 'smsgaddresses', err_messages)
|
||||
if form_data:
|
||||
edit_address_id = None
|
||||
for key in form_data:
|
||||
if key.startswith(b'editaddr_'):
|
||||
edit_address_id = int(key.split(b'_')[1])
|
||||
break
|
||||
if edit_address_id is not None:
|
||||
listaddresses = False
|
||||
page_data['edit_address'] = edit_address_id
|
||||
page_data['addr_data'] = swap_client.listAllSMSGAddresses(addr_id=edit_address_id)[0]
|
||||
elif b'saveaddr' in form_data:
|
||||
edit_address_id = int(form_data[b'edit_address_id'][0].decode('utf-8'))
|
||||
edit_addr = form_data[b'edit_address'][0].decode('utf-8')
|
||||
active_ind = int(form_data[b'active_ind'][0].decode('utf-8'))
|
||||
ensure(active_ind in (0, 1), 'Invalid sort by')
|
||||
addressnote = '' if b'addressnote' not in form_data else form_data[b'addressnote'][0].decode('utf-8')
|
||||
if not validateTextInput(addressnote, 'Address note', messages, max_length=30):
|
||||
listaddresses = False
|
||||
page_data['edit_address'] = edit_address_id
|
||||
else:
|
||||
swap_client.editSMSGAddress(edit_addr, active_ind=active_ind, addressnote=addressnote)
|
||||
messages.append(f'Edited address {edit_addr}')
|
||||
elif b'shownewaddr' in form_data:
|
||||
listaddresses = False
|
||||
page_data['new_address'] = True
|
||||
elif b'showaddaddr' in form_data:
|
||||
listaddresses = False
|
||||
page_data['new_send_address'] = True
|
||||
elif b'createnewaddr' in form_data:
|
||||
addressnote = '' if b'addressnote' not in form_data else form_data[b'addressnote'][0].decode('utf-8')
|
||||
if not validateTextInput(addressnote, 'Address note', messages, max_length=30):
|
||||
listaddresses = False
|
||||
page_data['new_address'] = True
|
||||
else:
|
||||
new_addr, pubkey = swap_client.newSMSGAddress(addressnote=addressnote)
|
||||
messages.append(f'Created address {new_addr}, pubkey {pubkey}')
|
||||
elif b'createnewsendaddr' in form_data:
|
||||
pubkey_hex = form_data[b'addresspubkey'][0].decode('utf-8')
|
||||
addressnote = '' if b'addressnote' not in form_data else form_data[b'addressnote'][0].decode('utf-8')
|
||||
if not validateTextInput(addressnote, 'Address note', messages, max_length=30) or \
|
||||
not validateTextInput(pubkey_hex, 'Pubkey', messages, max_length=66):
|
||||
listaddresses = False
|
||||
page_data['new_send_address'] = True
|
||||
else:
|
||||
new_addr = swap_client.addSMSGAddress(pubkey_hex, addressnote=addressnote)
|
||||
messages.append(f'Added address {new_addr}')
|
||||
|
||||
if listaddresses is True:
|
||||
smsgaddresses = swap_client.listAllSMSGAddresses()
|
||||
network_addr = swap_client.network_addr
|
||||
|
||||
for addr in smsgaddresses:
|
||||
addr['type'] = strAddressType(addr['type'])
|
||||
|
||||
template = env.get_template('smsgaddresses.html')
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
'err_messages': err_messages,
|
||||
'data': page_data,
|
||||
'smsgaddresses': smsgaddresses,
|
||||
'network_addr': network_addr,
|
||||
'summary': summary,
|
||||
})
|
||||
|
||||
def page_identity(self, url_split, post_string):
|
||||
ensure(len(url_split) > 2, 'Address not specified')
|
||||
identity_address = url_split[2]
|
||||
swap_client = self.server.swap_client
|
||||
summary = swap_client.getSummary()
|
||||
|
||||
page_data = {'identity_address': identity_address}
|
||||
messages = []
|
||||
err_messages = []
|
||||
form_data = self.checkForm(post_string, 'identity', err_messages)
|
||||
if form_data:
|
||||
if have_data_entry(form_data, 'edit'):
|
||||
page_data['show_edit_form'] = True
|
||||
if have_data_entry(form_data, 'apply'):
|
||||
new_label = get_data_entry(form_data, 'label')
|
||||
|
||||
try:
|
||||
swap_client.updateIdentity(identity_address, new_label)
|
||||
messages.append('Updated')
|
||||
except Exception as e:
|
||||
err_messages.append(str(e))
|
||||
|
||||
try:
|
||||
identity = swap_client.getIdentity(identity_address)
|
||||
if identity is None:
|
||||
raise ValueError('Unknown address')
|
||||
page_data['label'] = identity.label
|
||||
page_data['num_sent_bids_successful'] = identity.num_sent_bids_successful
|
||||
page_data['num_recv_bids_successful'] = identity.num_recv_bids_successful
|
||||
page_data['num_sent_bids_rejected'] = identity.num_sent_bids_rejected
|
||||
page_data['num_recv_bids_rejected'] = identity.num_recv_bids_rejected
|
||||
page_data['num_sent_bids_failed'] = identity.num_sent_bids_failed
|
||||
page_data['num_recv_bids_failed'] = identity.num_recv_bids_failed
|
||||
except Exception as e:
|
||||
messages.append(e)
|
||||
|
||||
template = env.get_template('identity.html')
|
||||
return self.render_template(template, {
|
||||
'messages': messages,
|
||||
'err_messages': err_messages,
|
||||
'data': page_data,
|
||||
'summary': summary,
|
||||
})
|
||||
|
||||
def page_shutdown(self, url_split, post_string):
|
||||
swap_client = self.server.swap_client
|
||||
|
||||
@@ -557,18 +390,13 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||
|
||||
def page_index(self, url_split):
|
||||
swap_client = self.server.swap_client
|
||||
swap_client.checkSystemStatus()
|
||||
summary = swap_client.getSummary()
|
||||
|
||||
shutdown_token = os.urandom(8).hex()
|
||||
self.server.session_tokens['shutdown'] = shutdown_token
|
||||
|
||||
template = env.get_template('index.html')
|
||||
return self.render_template(template, {
|
||||
'refresh': 30,
|
||||
'version': __version__,
|
||||
'summary': summary,
|
||||
'use_tor_proxy': swap_client.use_tor_proxy,
|
||||
'shutdown_token': shutdown_token
|
||||
'use_tor_proxy': swap_client.use_tor_proxy
|
||||
})
|
||||
|
||||
def page_404(self, url_split):
|
||||
@@ -587,6 +415,7 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||
self.end_headers()
|
||||
|
||||
def handle_http(self, status_code, path, post_string='', is_json=False):
|
||||
swap_client = self.server.swap_client
|
||||
parsed = parse.urlparse(self.path)
|
||||
url_split = parsed.path.split('/')
|
||||
if post_string == '' and len(parsed.query) > 0:
|
||||
@@ -597,14 +426,13 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||
func = js_url_to_function(url_split)
|
||||
return func(self, url_split, post_string, is_json)
|
||||
except Exception as ex:
|
||||
if self.server.swap_client.debug is True:
|
||||
self.server.swap_client.log.error(traceback.format_exc())
|
||||
if swap_client.debug is True:
|
||||
swap_client.log.error(traceback.format_exc())
|
||||
return js_error(self, str(ex))
|
||||
|
||||
if len(url_split) > 1 and url_split[1] == 'static':
|
||||
try:
|
||||
static_path = os.path.join(os.path.dirname(__file__), 'static')
|
||||
|
||||
if len(url_split) > 3 and url_split[2] == 'sequence_diagrams':
|
||||
with open(os.path.join(static_path, 'sequence_diagrams', url_split[3]), 'rb') as fp:
|
||||
self.putHeaders(status_code, 'image/svg+xml')
|
||||
@@ -639,13 +467,14 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||
except FileNotFoundError:
|
||||
return self.page_404(url_split)
|
||||
except Exception as ex:
|
||||
if self.server.swap_client.debug is True:
|
||||
self.server.swap_client.log.error(traceback.format_exc())
|
||||
if swap_client.debug is True:
|
||||
swap_client.log.error(traceback.format_exc())
|
||||
return self.page_error(str(ex))
|
||||
|
||||
try:
|
||||
if len(url_split) > 1:
|
||||
page = url_split[1]
|
||||
|
||||
if page == 'active':
|
||||
return self.page_active(url_split, post_string)
|
||||
if page == 'wallets':
|
||||
@@ -653,7 +482,7 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||
if page == 'wallet':
|
||||
return page_wallet(self, url_split, post_string)
|
||||
if page == 'settings':
|
||||
return self.page_settings(url_split, post_string)
|
||||
return page_settings(self, url_split, post_string)
|
||||
if page == 'error':
|
||||
return self.page_error(url_split, post_string)
|
||||
if page == 'info':
|
||||
@@ -661,7 +490,7 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||
if page == 'rpc':
|
||||
return self.page_rpc(url_split, post_string)
|
||||
if page == 'debug':
|
||||
return self.page_debug(url_split, post_string)
|
||||
return page_debug(self, url_split, post_string)
|
||||
if page == 'explorers':
|
||||
return self.page_explorers(url_split, post_string)
|
||||
if page == 'offer':
|
||||
@@ -683,9 +512,9 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||
if page == 'watched':
|
||||
return self.page_watched(url_split, post_string)
|
||||
if page == 'smsgaddresses':
|
||||
return self.page_smsgaddresses(url_split, post_string)
|
||||
return page_smsgaddresses(self, url_split, post_string)
|
||||
if page == 'identity':
|
||||
return self.page_identity(url_split, post_string)
|
||||
return page_identity(self, url_split, post_string)
|
||||
if page == 'tor':
|
||||
return page_tor(self, url_split, post_string)
|
||||
if page == 'automation':
|
||||
@@ -696,12 +525,20 @@ class HttpHandler(BaseHTTPRequestHandler):
|
||||
return page_automation_strategy_new(self, url_split, post_string)
|
||||
if page == 'shutdown':
|
||||
return self.page_shutdown(url_split, post_string)
|
||||
if page == 'changepassword':
|
||||
return page_changepassword(self, url_split, post_string)
|
||||
if page == 'unlock':
|
||||
return page_unlock(self, url_split, post_string)
|
||||
if page == 'lock':
|
||||
return page_lock(self, url_split, post_string)
|
||||
if page != '':
|
||||
return self.page_404(url_split)
|
||||
return self.page_index(url_split)
|
||||
except LockedCoinError:
|
||||
return page_unlock(self, url_split, post_string)
|
||||
except Exception as ex:
|
||||
if self.server.swap_client.debug is True:
|
||||
self.server.swap_client.log.error(traceback.format_exc())
|
||||
if swap_client.debug is True:
|
||||
swap_client.log.error(traceback.format_exc())
|
||||
return self.page_error(str(ex))
|
||||
|
||||
def do_GET(self):
|
||||
@@ -756,11 +593,8 @@ class HttpThread(threading.Thread, HTTPServer):
|
||||
data = response.read()
|
||||
conn.close()
|
||||
|
||||
def stopped(self):
|
||||
return self.stop_event.is_set()
|
||||
|
||||
def serve_forever(self):
|
||||
while not self.stopped():
|
||||
while not self.stop_event.is_set():
|
||||
self.handle_request()
|
||||
self.socket.close()
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2023 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from enum import IntEnum
|
||||
|
||||
|
||||
class Curves(IntEnum):
|
||||
secp256k1 = 1
|
||||
ed25519 = 2
|
||||
|
||||
191
basicswap/interface/contrib/firo_test_framework/authproxy.py
Normal file
@@ -0,0 +1,191 @@
|
||||
|
||||
"""
|
||||
Copyright (c) 2011 Jeff Garzik
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
"""
|
||||
|
||||
try:
|
||||
import http.client as httplib
|
||||
except ImportError:
|
||||
import httplib
|
||||
import base64
|
||||
import decimal
|
||||
import json
|
||||
import logging
|
||||
import socket
|
||||
try:
|
||||
import urllib.parse as urlparse
|
||||
except ImportError:
|
||||
import urlparse
|
||||
|
||||
USER_AGENT = "AuthServiceProxy/0.1"
|
||||
|
||||
HTTP_TIMEOUT = 30
|
||||
|
||||
log = logging.getLogger("BitcoinRPC")
|
||||
|
||||
class JSONRPCException(Exception):
|
||||
def __init__(self, rpc_error):
|
||||
try:
|
||||
errmsg = '%(message)s (%(code)i)' % rpc_error
|
||||
except (KeyError, TypeError):
|
||||
errmsg = ''
|
||||
Exception.__init__(self, 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(object):
|
||||
__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 = urlparse.urlparse(service_url)
|
||||
if self.__url.port is None:
|
||||
port = 80
|
||||
else:
|
||||
port = self.__url.port
|
||||
(user, passwd) = (self.__url.username, self.__url.password)
|
||||
try:
|
||||
user = user.encode('utf8')
|
||||
except AttributeError:
|
||||
pass
|
||||
try:
|
||||
passwd = passwd.encode('utf8')
|
||||
except AttributeError:
|
||||
pass
|
||||
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 = httplib.HTTPSConnection(self.__url.hostname, port,
|
||||
timeout=timeout)
|
||||
else:
|
||||
self.__conn = httplib.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 httplib.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 __call__(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')
|
||||
postdata = json.dumps({'version': '1.1',
|
||||
'method': self._service_name,
|
||||
'params': args or argsn,
|
||||
'id': AuthServiceProxy.__id_count}, 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):
|
||||
try:
|
||||
http_response = self.__conn.getresponse()
|
||||
except socket.timeout as e:
|
||||
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)
|
||||
if "error" in response and response["error"] is None:
|
||||
log.debug("<-%s- %s"%(response["id"], json.dumps(response["result"], default=EncodeDecimal, ensure_ascii=self.ensure_ascii)))
|
||||
else:
|
||||
log.debug("<-- "+responsedata)
|
||||
return response
|
||||
101
basicswap/interface/contrib/firo_test_framework/bignum.py
Normal file
@@ -0,0 +1,101 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# bignum.py
|
||||
#
|
||||
# This file is copied from python-bitcoinlib.
|
||||
#
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
#
|
||||
|
||||
"""Bignum routines"""
|
||||
|
||||
|
||||
import struct
|
||||
|
||||
|
||||
# generic big endian MPI format
|
||||
|
||||
def bn_bytes(v, have_ext=False):
|
||||
ext = 0
|
||||
if have_ext:
|
||||
ext = 1
|
||||
return ((v.bit_length()+7)//8) + ext
|
||||
|
||||
def bn2bin(v):
|
||||
s = bytearray()
|
||||
i = bn_bytes(v)
|
||||
while i > 0:
|
||||
s.append((v >> ((i-1) * 8)) & 0xff)
|
||||
i -= 1
|
||||
return s
|
||||
|
||||
def bin2bn(s):
|
||||
l = 0
|
||||
for ch in s:
|
||||
l = (l << 8) | ch
|
||||
return l
|
||||
|
||||
def bn2mpi(v):
|
||||
have_ext = False
|
||||
if v.bit_length() > 0:
|
||||
have_ext = (v.bit_length() & 0x07) == 0
|
||||
|
||||
neg = False
|
||||
if v < 0:
|
||||
neg = True
|
||||
v = -v
|
||||
|
||||
s = struct.pack(b">I", bn_bytes(v, have_ext))
|
||||
ext = bytearray()
|
||||
if have_ext:
|
||||
ext.append(0)
|
||||
v_bin = bn2bin(v)
|
||||
if neg:
|
||||
if have_ext:
|
||||
ext[0] |= 0x80
|
||||
else:
|
||||
v_bin[0] |= 0x80
|
||||
return s + ext + v_bin
|
||||
|
||||
def mpi2bn(s):
|
||||
if len(s) < 4:
|
||||
return None
|
||||
s_size = bytes(s[:4])
|
||||
v_len = struct.unpack(b">I", s_size)[0]
|
||||
if len(s) != (v_len + 4):
|
||||
return None
|
||||
if v_len == 0:
|
||||
return 0
|
||||
|
||||
v_str = bytearray(s[4:])
|
||||
neg = False
|
||||
i = v_str[0]
|
||||
if i & 0x80:
|
||||
neg = True
|
||||
i &= ~0x80
|
||||
v_str[0] = i
|
||||
|
||||
v = bin2bn(v_str)
|
||||
|
||||
if neg:
|
||||
return -v
|
||||
return v
|
||||
|
||||
# bitcoin-specific little endian format, with implicit size
|
||||
def mpi2vch(s):
|
||||
r = s[4:] # strip size
|
||||
r = r[::-1] # reverse string, converting BE->LE
|
||||
return r
|
||||
|
||||
def bn2vch(v):
|
||||
return bytes(mpi2vch(bn2mpi(v)))
|
||||
|
||||
def vch2mpi(s):
|
||||
r = struct.pack(b">I", len(s)) # size
|
||||
r += s[::-1] # reverse string, converting LE->BE
|
||||
return r
|
||||
|
||||
def vch2bn(s):
|
||||
return mpi2bn(vch2mpi(s))
|
||||
|
||||
106
basicswap/interface/contrib/firo_test_framework/coverage.py
Normal file
@@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-2016 The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
"""
|
||||
This module contains utilities for doing coverage analysis on the RPC
|
||||
interface.
|
||||
|
||||
It provides a way to track which RPC commands are exercised during
|
||||
testing.
|
||||
|
||||
"""
|
||||
import os
|
||||
|
||||
|
||||
REFERENCE_FILENAME = 'rpc_interface.txt'
|
||||
|
||||
|
||||
class AuthServiceProxyWrapper(object):
|
||||
"""
|
||||
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, *args, **kwargs):
|
||||
return_val = self.auth_service_proxy_instance.__getattr__(
|
||||
*args, **kwargs)
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
return return_val
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
return self.auth_service_proxy_instance.url
|
||||
|
||||
|
||||
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 `bitcoin-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
|
||||
1904
basicswap/interface/contrib/firo_test_framework/mininode.py
Normal file
943
basicswap/interface/contrib/firo_test_framework/script.py
Normal file
@@ -0,0 +1,943 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-2016 The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#
|
||||
# script.py
|
||||
#
|
||||
# This file is modified from python-bitcoinlib.
|
||||
#
|
||||
|
||||
"""Scripts
|
||||
|
||||
Functionality to build scripts, as well as SignatureHash().
|
||||
"""
|
||||
|
||||
|
||||
from .mininode import CTransaction, CTxOut, sha256, hash256, uint256_from_str, ser_uint256, ser_string
|
||||
from binascii import hexlify
|
||||
import hashlib
|
||||
|
||||
import sys
|
||||
bchr = chr
|
||||
bord = ord
|
||||
if sys.version > '3':
|
||||
long = int
|
||||
bchr = lambda x: bytes([x])
|
||||
bord = lambda x: x
|
||||
|
||||
import struct
|
||||
|
||||
from .bignum import bn2vch
|
||||
|
||||
MAX_SCRIPT_SIZE = 10000
|
||||
MAX_SCRIPT_ELEMENT_SIZE = 520
|
||||
MAX_SCRIPT_OPCODES = 201
|
||||
|
||||
OPCODE_NAMES = {}
|
||||
|
||||
_opcode_instances = []
|
||||
class CScriptOp(int):
|
||||
"""A single script opcode"""
|
||||
__slots__ = []
|
||||
|
||||
@staticmethod
|
||||
def encode_op_pushdata(d):
|
||||
"""Encode a PUSHDATA op, returning bytes"""
|
||||
if len(d) < 0x4c:
|
||||
return b'' + bchr(len(d)) + d # OP_PUSHDATA
|
||||
elif len(d) <= 0xff:
|
||||
return b'\x4c' + bchr(len(d)) + d # OP_PUSHDATA1
|
||||
elif len(d) <= 0xffff:
|
||||
return b'\x4d' + struct.pack(b'<H', len(d)) + d # OP_PUSHDATA2
|
||||
elif len(d) <= 0xffffffff:
|
||||
return b'\x4e' + struct.pack(b'<I', len(d)) + d # OP_PUSHDATA4
|
||||
else:
|
||||
raise ValueError("Data too long to encode in a PUSHDATA op")
|
||||
|
||||
@staticmethod
|
||||
def encode_op_n(n):
|
||||
"""Encode a small integer op, returning an opcode"""
|
||||
if not (0 <= n <= 16):
|
||||
raise ValueError('Integer must be in range 0 <= n <= 16, got %d' % n)
|
||||
|
||||
if n == 0:
|
||||
return OP_0
|
||||
else:
|
||||
return CScriptOp(OP_1 + n-1)
|
||||
|
||||
def decode_op_n(self):
|
||||
"""Decode a small integer opcode, returning an integer"""
|
||||
if self == OP_0:
|
||||
return 0
|
||||
|
||||
if not (self == OP_0 or OP_1 <= self <= OP_16):
|
||||
raise ValueError('op %r is not an OP_N' % self)
|
||||
|
||||
return int(self - OP_1+1)
|
||||
|
||||
def is_small_int(self):
|
||||
"""Return true if the op pushes a small integer to the stack"""
|
||||
if 0x51 <= self <= 0x60 or self == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def __str__(self):
|
||||
return repr(self)
|
||||
|
||||
def __repr__(self):
|
||||
if self in OPCODE_NAMES:
|
||||
return OPCODE_NAMES[self]
|
||||
else:
|
||||
return 'CScriptOp(0x%x)' % self
|
||||
|
||||
def __new__(cls, n):
|
||||
try:
|
||||
return _opcode_instances[n]
|
||||
except IndexError:
|
||||
assert len(_opcode_instances) == n
|
||||
_opcode_instances.append(super(CScriptOp, cls).__new__(cls, n))
|
||||
return _opcode_instances[n]
|
||||
|
||||
# Populate opcode instance table
|
||||
for n in range(0xff+1):
|
||||
CScriptOp(n)
|
||||
|
||||
|
||||
# push value
|
||||
OP_0 = CScriptOp(0x00)
|
||||
OP_FALSE = OP_0
|
||||
OP_PUSHDATA1 = CScriptOp(0x4c)
|
||||
OP_PUSHDATA2 = CScriptOp(0x4d)
|
||||
OP_PUSHDATA4 = CScriptOp(0x4e)
|
||||
OP_1NEGATE = CScriptOp(0x4f)
|
||||
OP_RESERVED = CScriptOp(0x50)
|
||||
OP_1 = CScriptOp(0x51)
|
||||
OP_TRUE=OP_1
|
||||
OP_2 = CScriptOp(0x52)
|
||||
OP_3 = CScriptOp(0x53)
|
||||
OP_4 = CScriptOp(0x54)
|
||||
OP_5 = CScriptOp(0x55)
|
||||
OP_6 = CScriptOp(0x56)
|
||||
OP_7 = CScriptOp(0x57)
|
||||
OP_8 = CScriptOp(0x58)
|
||||
OP_9 = CScriptOp(0x59)
|
||||
OP_10 = CScriptOp(0x5a)
|
||||
OP_11 = CScriptOp(0x5b)
|
||||
OP_12 = CScriptOp(0x5c)
|
||||
OP_13 = CScriptOp(0x5d)
|
||||
OP_14 = CScriptOp(0x5e)
|
||||
OP_15 = CScriptOp(0x5f)
|
||||
OP_16 = CScriptOp(0x60)
|
||||
|
||||
# control
|
||||
OP_NOP = CScriptOp(0x61)
|
||||
OP_VER = CScriptOp(0x62)
|
||||
OP_IF = CScriptOp(0x63)
|
||||
OP_NOTIF = CScriptOp(0x64)
|
||||
OP_VERIF = CScriptOp(0x65)
|
||||
OP_VERNOTIF = CScriptOp(0x66)
|
||||
OP_ELSE = CScriptOp(0x67)
|
||||
OP_ENDIF = CScriptOp(0x68)
|
||||
OP_VERIFY = CScriptOp(0x69)
|
||||
OP_RETURN = CScriptOp(0x6a)
|
||||
|
||||
# stack ops
|
||||
OP_TOALTSTACK = CScriptOp(0x6b)
|
||||
OP_FROMALTSTACK = CScriptOp(0x6c)
|
||||
OP_2DROP = CScriptOp(0x6d)
|
||||
OP_2DUP = CScriptOp(0x6e)
|
||||
OP_3DUP = CScriptOp(0x6f)
|
||||
OP_2OVER = CScriptOp(0x70)
|
||||
OP_2ROT = CScriptOp(0x71)
|
||||
OP_2SWAP = CScriptOp(0x72)
|
||||
OP_IFDUP = CScriptOp(0x73)
|
||||
OP_DEPTH = CScriptOp(0x74)
|
||||
OP_DROP = CScriptOp(0x75)
|
||||
OP_DUP = CScriptOp(0x76)
|
||||
OP_NIP = CScriptOp(0x77)
|
||||
OP_OVER = CScriptOp(0x78)
|
||||
OP_PICK = CScriptOp(0x79)
|
||||
OP_ROLL = CScriptOp(0x7a)
|
||||
OP_ROT = CScriptOp(0x7b)
|
||||
OP_SWAP = CScriptOp(0x7c)
|
||||
OP_TUCK = CScriptOp(0x7d)
|
||||
|
||||
# splice ops
|
||||
OP_CAT = CScriptOp(0x7e)
|
||||
OP_SUBSTR = CScriptOp(0x7f)
|
||||
OP_LEFT = CScriptOp(0x80)
|
||||
OP_RIGHT = CScriptOp(0x81)
|
||||
OP_SIZE = CScriptOp(0x82)
|
||||
|
||||
# bit logic
|
||||
OP_INVERT = CScriptOp(0x83)
|
||||
OP_AND = CScriptOp(0x84)
|
||||
OP_OR = CScriptOp(0x85)
|
||||
OP_XOR = CScriptOp(0x86)
|
||||
OP_EQUAL = CScriptOp(0x87)
|
||||
OP_EQUALVERIFY = CScriptOp(0x88)
|
||||
OP_RESERVED1 = CScriptOp(0x89)
|
||||
OP_RESERVED2 = CScriptOp(0x8a)
|
||||
|
||||
# numeric
|
||||
OP_1ADD = CScriptOp(0x8b)
|
||||
OP_1SUB = CScriptOp(0x8c)
|
||||
OP_2MUL = CScriptOp(0x8d)
|
||||
OP_2DIV = CScriptOp(0x8e)
|
||||
OP_NEGATE = CScriptOp(0x8f)
|
||||
OP_ABS = CScriptOp(0x90)
|
||||
OP_NOT = CScriptOp(0x91)
|
||||
OP_0NOTEQUAL = CScriptOp(0x92)
|
||||
|
||||
OP_ADD = CScriptOp(0x93)
|
||||
OP_SUB = CScriptOp(0x94)
|
||||
OP_MUL = CScriptOp(0x95)
|
||||
OP_DIV = CScriptOp(0x96)
|
||||
OP_MOD = CScriptOp(0x97)
|
||||
OP_LSHIFT = CScriptOp(0x98)
|
||||
OP_RSHIFT = CScriptOp(0x99)
|
||||
|
||||
OP_BOOLAND = CScriptOp(0x9a)
|
||||
OP_BOOLOR = CScriptOp(0x9b)
|
||||
OP_NUMEQUAL = CScriptOp(0x9c)
|
||||
OP_NUMEQUALVERIFY = CScriptOp(0x9d)
|
||||
OP_NUMNOTEQUAL = CScriptOp(0x9e)
|
||||
OP_LESSTHAN = CScriptOp(0x9f)
|
||||
OP_GREATERTHAN = CScriptOp(0xa0)
|
||||
OP_LESSTHANOREQUAL = CScriptOp(0xa1)
|
||||
OP_GREATERTHANOREQUAL = CScriptOp(0xa2)
|
||||
OP_MIN = CScriptOp(0xa3)
|
||||
OP_MAX = CScriptOp(0xa4)
|
||||
|
||||
OP_WITHIN = CScriptOp(0xa5)
|
||||
|
||||
# crypto
|
||||
OP_RIPEMD160 = CScriptOp(0xa6)
|
||||
OP_SHA1 = CScriptOp(0xa7)
|
||||
OP_SHA256 = CScriptOp(0xa8)
|
||||
OP_HASH160 = CScriptOp(0xa9)
|
||||
OP_HASH256 = CScriptOp(0xaa)
|
||||
OP_CODESEPARATOR = CScriptOp(0xab)
|
||||
OP_CHECKSIG = CScriptOp(0xac)
|
||||
OP_CHECKSIGVERIFY = CScriptOp(0xad)
|
||||
OP_CHECKMULTISIG = CScriptOp(0xae)
|
||||
OP_CHECKMULTISIGVERIFY = CScriptOp(0xaf)
|
||||
|
||||
# expansion
|
||||
OP_NOP1 = CScriptOp(0xb0)
|
||||
OP_CHECKLOCKTIMEVERIFY = CScriptOp(0xb1)
|
||||
OP_CHECKSEQUENCEVERIFY = CScriptOp(0xb2)
|
||||
OP_NOP4 = CScriptOp(0xb3)
|
||||
OP_NOP5 = CScriptOp(0xb4)
|
||||
OP_NOP6 = CScriptOp(0xb5)
|
||||
OP_NOP7 = CScriptOp(0xb6)
|
||||
OP_NOP8 = CScriptOp(0xb7)
|
||||
OP_NOP9 = CScriptOp(0xb8)
|
||||
OP_NOP10 = CScriptOp(0xb9)
|
||||
|
||||
# template matching params
|
||||
OP_SMALLINTEGER = CScriptOp(0xfa)
|
||||
OP_PUBKEYS = CScriptOp(0xfb)
|
||||
OP_PUBKEYHASH = CScriptOp(0xfd)
|
||||
OP_PUBKEY = CScriptOp(0xfe)
|
||||
|
||||
OP_INVALIDOPCODE = CScriptOp(0xff)
|
||||
|
||||
VALID_OPCODES = {
|
||||
OP_1NEGATE,
|
||||
OP_RESERVED,
|
||||
OP_1,
|
||||
OP_2,
|
||||
OP_3,
|
||||
OP_4,
|
||||
OP_5,
|
||||
OP_6,
|
||||
OP_7,
|
||||
OP_8,
|
||||
OP_9,
|
||||
OP_10,
|
||||
OP_11,
|
||||
OP_12,
|
||||
OP_13,
|
||||
OP_14,
|
||||
OP_15,
|
||||
OP_16,
|
||||
|
||||
OP_NOP,
|
||||
OP_VER,
|
||||
OP_IF,
|
||||
OP_NOTIF,
|
||||
OP_VERIF,
|
||||
OP_VERNOTIF,
|
||||
OP_ELSE,
|
||||
OP_ENDIF,
|
||||
OP_VERIFY,
|
||||
OP_RETURN,
|
||||
|
||||
OP_TOALTSTACK,
|
||||
OP_FROMALTSTACK,
|
||||
OP_2DROP,
|
||||
OP_2DUP,
|
||||
OP_3DUP,
|
||||
OP_2OVER,
|
||||
OP_2ROT,
|
||||
OP_2SWAP,
|
||||
OP_IFDUP,
|
||||
OP_DEPTH,
|
||||
OP_DROP,
|
||||
OP_DUP,
|
||||
OP_NIP,
|
||||
OP_OVER,
|
||||
OP_PICK,
|
||||
OP_ROLL,
|
||||
OP_ROT,
|
||||
OP_SWAP,
|
||||
OP_TUCK,
|
||||
|
||||
OP_CAT,
|
||||
OP_SUBSTR,
|
||||
OP_LEFT,
|
||||
OP_RIGHT,
|
||||
OP_SIZE,
|
||||
|
||||
OP_INVERT,
|
||||
OP_AND,
|
||||
OP_OR,
|
||||
OP_XOR,
|
||||
OP_EQUAL,
|
||||
OP_EQUALVERIFY,
|
||||
OP_RESERVED1,
|
||||
OP_RESERVED2,
|
||||
|
||||
OP_1ADD,
|
||||
OP_1SUB,
|
||||
OP_2MUL,
|
||||
OP_2DIV,
|
||||
OP_NEGATE,
|
||||
OP_ABS,
|
||||
OP_NOT,
|
||||
OP_0NOTEQUAL,
|
||||
|
||||
OP_ADD,
|
||||
OP_SUB,
|
||||
OP_MUL,
|
||||
OP_DIV,
|
||||
OP_MOD,
|
||||
OP_LSHIFT,
|
||||
OP_RSHIFT,
|
||||
|
||||
OP_BOOLAND,
|
||||
OP_BOOLOR,
|
||||
OP_NUMEQUAL,
|
||||
OP_NUMEQUALVERIFY,
|
||||
OP_NUMNOTEQUAL,
|
||||
OP_LESSTHAN,
|
||||
OP_GREATERTHAN,
|
||||
OP_LESSTHANOREQUAL,
|
||||
OP_GREATERTHANOREQUAL,
|
||||
OP_MIN,
|
||||
OP_MAX,
|
||||
|
||||
OP_WITHIN,
|
||||
|
||||
OP_RIPEMD160,
|
||||
OP_SHA1,
|
||||
OP_SHA256,
|
||||
OP_HASH160,
|
||||
OP_HASH256,
|
||||
OP_CODESEPARATOR,
|
||||
OP_CHECKSIG,
|
||||
OP_CHECKSIGVERIFY,
|
||||
OP_CHECKMULTISIG,
|
||||
OP_CHECKMULTISIGVERIFY,
|
||||
|
||||
OP_NOP1,
|
||||
OP_CHECKLOCKTIMEVERIFY,
|
||||
OP_CHECKSEQUENCEVERIFY,
|
||||
OP_NOP4,
|
||||
OP_NOP5,
|
||||
OP_NOP6,
|
||||
OP_NOP7,
|
||||
OP_NOP8,
|
||||
OP_NOP9,
|
||||
OP_NOP10,
|
||||
|
||||
OP_SMALLINTEGER,
|
||||
OP_PUBKEYS,
|
||||
OP_PUBKEYHASH,
|
||||
OP_PUBKEY,
|
||||
}
|
||||
|
||||
OPCODE_NAMES.update({
|
||||
OP_0 : 'OP_0',
|
||||
OP_PUSHDATA1 : 'OP_PUSHDATA1',
|
||||
OP_PUSHDATA2 : 'OP_PUSHDATA2',
|
||||
OP_PUSHDATA4 : 'OP_PUSHDATA4',
|
||||
OP_1NEGATE : 'OP_1NEGATE',
|
||||
OP_RESERVED : 'OP_RESERVED',
|
||||
OP_1 : 'OP_1',
|
||||
OP_2 : 'OP_2',
|
||||
OP_3 : 'OP_3',
|
||||
OP_4 : 'OP_4',
|
||||
OP_5 : 'OP_5',
|
||||
OP_6 : 'OP_6',
|
||||
OP_7 : 'OP_7',
|
||||
OP_8 : 'OP_8',
|
||||
OP_9 : 'OP_9',
|
||||
OP_10 : 'OP_10',
|
||||
OP_11 : 'OP_11',
|
||||
OP_12 : 'OP_12',
|
||||
OP_13 : 'OP_13',
|
||||
OP_14 : 'OP_14',
|
||||
OP_15 : 'OP_15',
|
||||
OP_16 : 'OP_16',
|
||||
OP_NOP : 'OP_NOP',
|
||||
OP_VER : 'OP_VER',
|
||||
OP_IF : 'OP_IF',
|
||||
OP_NOTIF : 'OP_NOTIF',
|
||||
OP_VERIF : 'OP_VERIF',
|
||||
OP_VERNOTIF : 'OP_VERNOTIF',
|
||||
OP_ELSE : 'OP_ELSE',
|
||||
OP_ENDIF : 'OP_ENDIF',
|
||||
OP_VERIFY : 'OP_VERIFY',
|
||||
OP_RETURN : 'OP_RETURN',
|
||||
OP_TOALTSTACK : 'OP_TOALTSTACK',
|
||||
OP_FROMALTSTACK : 'OP_FROMALTSTACK',
|
||||
OP_2DROP : 'OP_2DROP',
|
||||
OP_2DUP : 'OP_2DUP',
|
||||
OP_3DUP : 'OP_3DUP',
|
||||
OP_2OVER : 'OP_2OVER',
|
||||
OP_2ROT : 'OP_2ROT',
|
||||
OP_2SWAP : 'OP_2SWAP',
|
||||
OP_IFDUP : 'OP_IFDUP',
|
||||
OP_DEPTH : 'OP_DEPTH',
|
||||
OP_DROP : 'OP_DROP',
|
||||
OP_DUP : 'OP_DUP',
|
||||
OP_NIP : 'OP_NIP',
|
||||
OP_OVER : 'OP_OVER',
|
||||
OP_PICK : 'OP_PICK',
|
||||
OP_ROLL : 'OP_ROLL',
|
||||
OP_ROT : 'OP_ROT',
|
||||
OP_SWAP : 'OP_SWAP',
|
||||
OP_TUCK : 'OP_TUCK',
|
||||
OP_CAT : 'OP_CAT',
|
||||
OP_SUBSTR : 'OP_SUBSTR',
|
||||
OP_LEFT : 'OP_LEFT',
|
||||
OP_RIGHT : 'OP_RIGHT',
|
||||
OP_SIZE : 'OP_SIZE',
|
||||
OP_INVERT : 'OP_INVERT',
|
||||
OP_AND : 'OP_AND',
|
||||
OP_OR : 'OP_OR',
|
||||
OP_XOR : 'OP_XOR',
|
||||
OP_EQUAL : 'OP_EQUAL',
|
||||
OP_EQUALVERIFY : 'OP_EQUALVERIFY',
|
||||
OP_RESERVED1 : 'OP_RESERVED1',
|
||||
OP_RESERVED2 : 'OP_RESERVED2',
|
||||
OP_1ADD : 'OP_1ADD',
|
||||
OP_1SUB : 'OP_1SUB',
|
||||
OP_2MUL : 'OP_2MUL',
|
||||
OP_2DIV : 'OP_2DIV',
|
||||
OP_NEGATE : 'OP_NEGATE',
|
||||
OP_ABS : 'OP_ABS',
|
||||
OP_NOT : 'OP_NOT',
|
||||
OP_0NOTEQUAL : 'OP_0NOTEQUAL',
|
||||
OP_ADD : 'OP_ADD',
|
||||
OP_SUB : 'OP_SUB',
|
||||
OP_MUL : 'OP_MUL',
|
||||
OP_DIV : 'OP_DIV',
|
||||
OP_MOD : 'OP_MOD',
|
||||
OP_LSHIFT : 'OP_LSHIFT',
|
||||
OP_RSHIFT : 'OP_RSHIFT',
|
||||
OP_BOOLAND : 'OP_BOOLAND',
|
||||
OP_BOOLOR : 'OP_BOOLOR',
|
||||
OP_NUMEQUAL : 'OP_NUMEQUAL',
|
||||
OP_NUMEQUALVERIFY : 'OP_NUMEQUALVERIFY',
|
||||
OP_NUMNOTEQUAL : 'OP_NUMNOTEQUAL',
|
||||
OP_LESSTHAN : 'OP_LESSTHAN',
|
||||
OP_GREATERTHAN : 'OP_GREATERTHAN',
|
||||
OP_LESSTHANOREQUAL : 'OP_LESSTHANOREQUAL',
|
||||
OP_GREATERTHANOREQUAL : 'OP_GREATERTHANOREQUAL',
|
||||
OP_MIN : 'OP_MIN',
|
||||
OP_MAX : 'OP_MAX',
|
||||
OP_WITHIN : 'OP_WITHIN',
|
||||
OP_RIPEMD160 : 'OP_RIPEMD160',
|
||||
OP_SHA1 : 'OP_SHA1',
|
||||
OP_SHA256 : 'OP_SHA256',
|
||||
OP_HASH160 : 'OP_HASH160',
|
||||
OP_HASH256 : 'OP_HASH256',
|
||||
OP_CODESEPARATOR : 'OP_CODESEPARATOR',
|
||||
OP_CHECKSIG : 'OP_CHECKSIG',
|
||||
OP_CHECKSIGVERIFY : 'OP_CHECKSIGVERIFY',
|
||||
OP_CHECKMULTISIG : 'OP_CHECKMULTISIG',
|
||||
OP_CHECKMULTISIGVERIFY : 'OP_CHECKMULTISIGVERIFY',
|
||||
OP_NOP1 : 'OP_NOP1',
|
||||
OP_CHECKLOCKTIMEVERIFY : 'OP_CHECKLOCKTIMEVERIFY',
|
||||
OP_CHECKSEQUENCEVERIFY : 'OP_CHECKSEQUENCEVERIFY',
|
||||
OP_NOP4 : 'OP_NOP4',
|
||||
OP_NOP5 : 'OP_NOP5',
|
||||
OP_NOP6 : 'OP_NOP6',
|
||||
OP_NOP7 : 'OP_NOP7',
|
||||
OP_NOP8 : 'OP_NOP8',
|
||||
OP_NOP9 : 'OP_NOP9',
|
||||
OP_NOP10 : 'OP_NOP10',
|
||||
OP_SMALLINTEGER : 'OP_SMALLINTEGER',
|
||||
OP_PUBKEYS : 'OP_PUBKEYS',
|
||||
OP_PUBKEYHASH : 'OP_PUBKEYHASH',
|
||||
OP_PUBKEY : 'OP_PUBKEY',
|
||||
OP_INVALIDOPCODE : 'OP_INVALIDOPCODE',
|
||||
})
|
||||
|
||||
OPCODES_BY_NAME = {
|
||||
'OP_0' : OP_0,
|
||||
'OP_PUSHDATA1' : OP_PUSHDATA1,
|
||||
'OP_PUSHDATA2' : OP_PUSHDATA2,
|
||||
'OP_PUSHDATA4' : OP_PUSHDATA4,
|
||||
'OP_1NEGATE' : OP_1NEGATE,
|
||||
'OP_RESERVED' : OP_RESERVED,
|
||||
'OP_1' : OP_1,
|
||||
'OP_2' : OP_2,
|
||||
'OP_3' : OP_3,
|
||||
'OP_4' : OP_4,
|
||||
'OP_5' : OP_5,
|
||||
'OP_6' : OP_6,
|
||||
'OP_7' : OP_7,
|
||||
'OP_8' : OP_8,
|
||||
'OP_9' : OP_9,
|
||||
'OP_10' : OP_10,
|
||||
'OP_11' : OP_11,
|
||||
'OP_12' : OP_12,
|
||||
'OP_13' : OP_13,
|
||||
'OP_14' : OP_14,
|
||||
'OP_15' : OP_15,
|
||||
'OP_16' : OP_16,
|
||||
'OP_NOP' : OP_NOP,
|
||||
'OP_VER' : OP_VER,
|
||||
'OP_IF' : OP_IF,
|
||||
'OP_NOTIF' : OP_NOTIF,
|
||||
'OP_VERIF' : OP_VERIF,
|
||||
'OP_VERNOTIF' : OP_VERNOTIF,
|
||||
'OP_ELSE' : OP_ELSE,
|
||||
'OP_ENDIF' : OP_ENDIF,
|
||||
'OP_VERIFY' : OP_VERIFY,
|
||||
'OP_RETURN' : OP_RETURN,
|
||||
'OP_TOALTSTACK' : OP_TOALTSTACK,
|
||||
'OP_FROMALTSTACK' : OP_FROMALTSTACK,
|
||||
'OP_2DROP' : OP_2DROP,
|
||||
'OP_2DUP' : OP_2DUP,
|
||||
'OP_3DUP' : OP_3DUP,
|
||||
'OP_2OVER' : OP_2OVER,
|
||||
'OP_2ROT' : OP_2ROT,
|
||||
'OP_2SWAP' : OP_2SWAP,
|
||||
'OP_IFDUP' : OP_IFDUP,
|
||||
'OP_DEPTH' : OP_DEPTH,
|
||||
'OP_DROP' : OP_DROP,
|
||||
'OP_DUP' : OP_DUP,
|
||||
'OP_NIP' : OP_NIP,
|
||||
'OP_OVER' : OP_OVER,
|
||||
'OP_PICK' : OP_PICK,
|
||||
'OP_ROLL' : OP_ROLL,
|
||||
'OP_ROT' : OP_ROT,
|
||||
'OP_SWAP' : OP_SWAP,
|
||||
'OP_TUCK' : OP_TUCK,
|
||||
'OP_CAT' : OP_CAT,
|
||||
'OP_SUBSTR' : OP_SUBSTR,
|
||||
'OP_LEFT' : OP_LEFT,
|
||||
'OP_RIGHT' : OP_RIGHT,
|
||||
'OP_SIZE' : OP_SIZE,
|
||||
'OP_INVERT' : OP_INVERT,
|
||||
'OP_AND' : OP_AND,
|
||||
'OP_OR' : OP_OR,
|
||||
'OP_XOR' : OP_XOR,
|
||||
'OP_EQUAL' : OP_EQUAL,
|
||||
'OP_EQUALVERIFY' : OP_EQUALVERIFY,
|
||||
'OP_RESERVED1' : OP_RESERVED1,
|
||||
'OP_RESERVED2' : OP_RESERVED2,
|
||||
'OP_1ADD' : OP_1ADD,
|
||||
'OP_1SUB' : OP_1SUB,
|
||||
'OP_2MUL' : OP_2MUL,
|
||||
'OP_2DIV' : OP_2DIV,
|
||||
'OP_NEGATE' : OP_NEGATE,
|
||||
'OP_ABS' : OP_ABS,
|
||||
'OP_NOT' : OP_NOT,
|
||||
'OP_0NOTEQUAL' : OP_0NOTEQUAL,
|
||||
'OP_ADD' : OP_ADD,
|
||||
'OP_SUB' : OP_SUB,
|
||||
'OP_MUL' : OP_MUL,
|
||||
'OP_DIV' : OP_DIV,
|
||||
'OP_MOD' : OP_MOD,
|
||||
'OP_LSHIFT' : OP_LSHIFT,
|
||||
'OP_RSHIFT' : OP_RSHIFT,
|
||||
'OP_BOOLAND' : OP_BOOLAND,
|
||||
'OP_BOOLOR' : OP_BOOLOR,
|
||||
'OP_NUMEQUAL' : OP_NUMEQUAL,
|
||||
'OP_NUMEQUALVERIFY' : OP_NUMEQUALVERIFY,
|
||||
'OP_NUMNOTEQUAL' : OP_NUMNOTEQUAL,
|
||||
'OP_LESSTHAN' : OP_LESSTHAN,
|
||||
'OP_GREATERTHAN' : OP_GREATERTHAN,
|
||||
'OP_LESSTHANOREQUAL' : OP_LESSTHANOREQUAL,
|
||||
'OP_GREATERTHANOREQUAL' : OP_GREATERTHANOREQUAL,
|
||||
'OP_MIN' : OP_MIN,
|
||||
'OP_MAX' : OP_MAX,
|
||||
'OP_WITHIN' : OP_WITHIN,
|
||||
'OP_RIPEMD160' : OP_RIPEMD160,
|
||||
'OP_SHA1' : OP_SHA1,
|
||||
'OP_SHA256' : OP_SHA256,
|
||||
'OP_HASH160' : OP_HASH160,
|
||||
'OP_HASH256' : OP_HASH256,
|
||||
'OP_CODESEPARATOR' : OP_CODESEPARATOR,
|
||||
'OP_CHECKSIG' : OP_CHECKSIG,
|
||||
'OP_CHECKSIGVERIFY' : OP_CHECKSIGVERIFY,
|
||||
'OP_CHECKMULTISIG' : OP_CHECKMULTISIG,
|
||||
'OP_CHECKMULTISIGVERIFY' : OP_CHECKMULTISIGVERIFY,
|
||||
'OP_NOP1' : OP_NOP1,
|
||||
'OP_CHECKLOCKTIMEVERIFY' : OP_CHECKLOCKTIMEVERIFY,
|
||||
'OP_CHECKSEQUENCEVERIFY' : OP_CHECKSEQUENCEVERIFY,
|
||||
'OP_NOP4' : OP_NOP4,
|
||||
'OP_NOP5' : OP_NOP5,
|
||||
'OP_NOP6' : OP_NOP6,
|
||||
'OP_NOP7' : OP_NOP7,
|
||||
'OP_NOP8' : OP_NOP8,
|
||||
'OP_NOP9' : OP_NOP9,
|
||||
'OP_NOP10' : OP_NOP10,
|
||||
'OP_SMALLINTEGER' : OP_SMALLINTEGER,
|
||||
'OP_PUBKEYS' : OP_PUBKEYS,
|
||||
'OP_PUBKEYHASH' : OP_PUBKEYHASH,
|
||||
'OP_PUBKEY' : OP_PUBKEY,
|
||||
}
|
||||
|
||||
class CScriptInvalidError(Exception):
|
||||
"""Base class for CScript exceptions"""
|
||||
pass
|
||||
|
||||
class CScriptTruncatedPushDataError(CScriptInvalidError):
|
||||
"""Invalid pushdata due to truncation"""
|
||||
def __init__(self, msg, data):
|
||||
self.data = data
|
||||
super(CScriptTruncatedPushDataError, self).__init__(msg)
|
||||
|
||||
# This is used, eg, for blockchain heights in coinbase scripts (bip34)
|
||||
class CScriptNum(object):
|
||||
def __init__(self, d=0):
|
||||
self.value = d
|
||||
|
||||
@staticmethod
|
||||
def encode(obj):
|
||||
r = bytearray(0)
|
||||
if obj.value == 0:
|
||||
return bytes(r)
|
||||
neg = obj.value < 0
|
||||
absvalue = -obj.value if neg else obj.value
|
||||
while (absvalue):
|
||||
r.append(absvalue & 0xff)
|
||||
absvalue >>= 8
|
||||
if r[-1] & 0x80:
|
||||
r.append(0x80 if neg else 0)
|
||||
elif neg:
|
||||
r[-1] |= 0x80
|
||||
return bytes(bchr(len(r)) + r)
|
||||
|
||||
|
||||
class CScript(bytes):
|
||||
"""Serialized script
|
||||
|
||||
A bytes subclass, so you can use this directly whenever bytes are accepted.
|
||||
Note that this means that indexing does *not* work - you'll get an index by
|
||||
byte rather than opcode. This format was chosen for efficiency so that the
|
||||
general case would not require creating a lot of little CScriptOP objects.
|
||||
|
||||
iter(script) however does iterate by opcode.
|
||||
"""
|
||||
@classmethod
|
||||
def __coerce_instance(cls, other):
|
||||
# Coerce other into bytes
|
||||
if isinstance(other, CScriptOp):
|
||||
other = bchr(other)
|
||||
elif isinstance(other, CScriptNum):
|
||||
if (other.value == 0):
|
||||
other = bchr(CScriptOp(OP_0))
|
||||
else:
|
||||
other = CScriptNum.encode(other)
|
||||
elif isinstance(other, int):
|
||||
if 0 <= other <= 16:
|
||||
other = bytes(bchr(CScriptOp.encode_op_n(other)))
|
||||
elif other == -1:
|
||||
other = bytes(bchr(OP_1NEGATE))
|
||||
else:
|
||||
other = CScriptOp.encode_op_pushdata(bn2vch(other))
|
||||
elif isinstance(other, (bytes, bytearray)):
|
||||
other = CScriptOp.encode_op_pushdata(other)
|
||||
return other
|
||||
|
||||
def __add__(self, other):
|
||||
# Do the coercion outside of the try block so that errors in it are
|
||||
# noticed.
|
||||
other = self.__coerce_instance(other)
|
||||
|
||||
try:
|
||||
# bytes.__add__ always returns bytes instances unfortunately
|
||||
return CScript(super(CScript, self).__add__(other))
|
||||
except TypeError:
|
||||
raise TypeError('Can not add a %r instance to a CScript' % other.__class__)
|
||||
|
||||
def join(self, iterable):
|
||||
# join makes no sense for a CScript()
|
||||
raise NotImplementedError
|
||||
|
||||
def __new__(cls, value=b''):
|
||||
if isinstance(value, bytes) or isinstance(value, bytearray):
|
||||
return super(CScript, cls).__new__(cls, value)
|
||||
else:
|
||||
def coerce_iterable(iterable):
|
||||
for instance in iterable:
|
||||
yield cls.__coerce_instance(instance)
|
||||
# Annoyingly on both python2 and python3 bytes.join() always
|
||||
# returns a bytes instance even when subclassed.
|
||||
return super(CScript, cls).__new__(cls, b''.join(coerce_iterable(value)))
|
||||
|
||||
def raw_iter(self):
|
||||
"""Raw iteration
|
||||
|
||||
Yields tuples of (opcode, data, sop_idx) so that the different possible
|
||||
PUSHDATA encodings can be accurately distinguished, as well as
|
||||
determining the exact opcode byte indexes. (sop_idx)
|
||||
"""
|
||||
i = 0
|
||||
while i < len(self):
|
||||
sop_idx = i
|
||||
opcode = bord(self[i])
|
||||
i += 1
|
||||
|
||||
if opcode > OP_PUSHDATA4:
|
||||
yield (opcode, None, sop_idx)
|
||||
else:
|
||||
datasize = None
|
||||
pushdata_type = None
|
||||
if opcode < OP_PUSHDATA1:
|
||||
pushdata_type = 'PUSHDATA(%d)' % opcode
|
||||
datasize = opcode
|
||||
|
||||
elif opcode == OP_PUSHDATA1:
|
||||
pushdata_type = 'PUSHDATA1'
|
||||
if i >= len(self):
|
||||
raise CScriptInvalidError('PUSHDATA1: missing data length')
|
||||
datasize = bord(self[i])
|
||||
i += 1
|
||||
|
||||
elif opcode == OP_PUSHDATA2:
|
||||
pushdata_type = 'PUSHDATA2'
|
||||
if i + 1 >= len(self):
|
||||
raise CScriptInvalidError('PUSHDATA2: missing data length')
|
||||
datasize = bord(self[i]) + (bord(self[i+1]) << 8)
|
||||
i += 2
|
||||
|
||||
elif opcode == OP_PUSHDATA4:
|
||||
pushdata_type = 'PUSHDATA4'
|
||||
if i + 3 >= len(self):
|
||||
raise CScriptInvalidError('PUSHDATA4: missing data length')
|
||||
datasize = bord(self[i]) + (bord(self[i+1]) << 8) + (bord(self[i+2]) << 16) + (bord(self[i+3]) << 24)
|
||||
i += 4
|
||||
|
||||
else:
|
||||
assert False # shouldn't happen
|
||||
|
||||
|
||||
data = bytes(self[i:i+datasize])
|
||||
|
||||
# Check for truncation
|
||||
if len(data) < datasize:
|
||||
raise CScriptTruncatedPushDataError('%s: truncated data' % pushdata_type, data)
|
||||
|
||||
i += datasize
|
||||
|
||||
yield (opcode, data, sop_idx)
|
||||
|
||||
def __iter__(self):
|
||||
"""'Cooked' iteration
|
||||
|
||||
Returns either a CScriptOP instance, an integer, or bytes, as
|
||||
appropriate.
|
||||
|
||||
See raw_iter() if you need to distinguish the different possible
|
||||
PUSHDATA encodings.
|
||||
"""
|
||||
for (opcode, data, sop_idx) in self.raw_iter():
|
||||
if data is not None:
|
||||
yield data
|
||||
else:
|
||||
opcode = CScriptOp(opcode)
|
||||
|
||||
if opcode.is_small_int():
|
||||
yield opcode.decode_op_n()
|
||||
else:
|
||||
yield CScriptOp(opcode)
|
||||
|
||||
def __repr__(self):
|
||||
# For Python3 compatibility add b before strings so testcases don't
|
||||
# need to change
|
||||
def _repr(o):
|
||||
if isinstance(o, bytes):
|
||||
return b"x('%s')" % hexlify(o).decode('ascii')
|
||||
else:
|
||||
return repr(o)
|
||||
|
||||
ops = []
|
||||
i = iter(self)
|
||||
while True:
|
||||
op = None
|
||||
try:
|
||||
op = _repr(next(i))
|
||||
except CScriptTruncatedPushDataError as err:
|
||||
op = '%s...<ERROR: %s>' % (_repr(err.data), err)
|
||||
break
|
||||
except CScriptInvalidError as err:
|
||||
op = '<ERROR: %s>' % err
|
||||
break
|
||||
except StopIteration:
|
||||
break
|
||||
finally:
|
||||
if op is not None:
|
||||
ops.append(op)
|
||||
|
||||
return "CScript([%s])" % ', '.join(ops)
|
||||
|
||||
def GetSigOpCount(self, fAccurate):
|
||||
"""Get the SigOp count.
|
||||
|
||||
fAccurate - Accurately count CHECKMULTISIG, see BIP16 for details.
|
||||
|
||||
Note that this is consensus-critical.
|
||||
"""
|
||||
n = 0
|
||||
lastOpcode = OP_INVALIDOPCODE
|
||||
for (opcode, data, sop_idx) in self.raw_iter():
|
||||
if opcode in (OP_CHECKSIG, OP_CHECKSIGVERIFY):
|
||||
n += 1
|
||||
elif opcode in (OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY):
|
||||
if fAccurate and (OP_1 <= lastOpcode <= OP_16):
|
||||
n += opcode.decode_op_n()
|
||||
else:
|
||||
n += 20
|
||||
lastOpcode = opcode
|
||||
return n
|
||||
|
||||
|
||||
SIGHASH_ALL = 1
|
||||
SIGHASH_NONE = 2
|
||||
SIGHASH_SINGLE = 3
|
||||
SIGHASH_ANYONECANPAY = 0x80
|
||||
|
||||
def FindAndDelete(script, sig):
|
||||
"""Consensus critical, see FindAndDelete() in Satoshi codebase"""
|
||||
r = b''
|
||||
last_sop_idx = sop_idx = 0
|
||||
skip = True
|
||||
for (opcode, data, sop_idx) in script.raw_iter():
|
||||
if not skip:
|
||||
r += script[last_sop_idx:sop_idx]
|
||||
last_sop_idx = sop_idx
|
||||
if script[sop_idx:sop_idx + len(sig)] == sig:
|
||||
skip = True
|
||||
else:
|
||||
skip = False
|
||||
if not skip:
|
||||
r += script[last_sop_idx:]
|
||||
return CScript(r)
|
||||
|
||||
|
||||
def SignatureHash(script, txTo, inIdx, hashtype):
|
||||
"""Consensus-correct SignatureHash
|
||||
|
||||
Returns (hash, err) to precisely match the consensus-critical behavior of
|
||||
the SIGHASH_SINGLE bug. (inIdx is *not* checked for validity)
|
||||
"""
|
||||
HASH_ONE = b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
|
||||
if inIdx >= len(txTo.vin):
|
||||
return (HASH_ONE, "inIdx %d out of range (%d)" % (inIdx, len(txTo.vin)))
|
||||
txtmp = CTransaction(txTo)
|
||||
|
||||
for txin in txtmp.vin:
|
||||
txin.scriptSig = b''
|
||||
txtmp.vin[inIdx].scriptSig = FindAndDelete(script, CScript([OP_CODESEPARATOR]))
|
||||
|
||||
if (hashtype & 0x1f) == SIGHASH_NONE:
|
||||
txtmp.vout = []
|
||||
|
||||
for i in range(len(txtmp.vin)):
|
||||
if i != inIdx:
|
||||
txtmp.vin[i].nSequence = 0
|
||||
|
||||
elif (hashtype & 0x1f) == SIGHASH_SINGLE:
|
||||
outIdx = inIdx
|
||||
if outIdx >= len(txtmp.vout):
|
||||
return (HASH_ONE, "outIdx %d out of range (%d)" % (outIdx, len(txtmp.vout)))
|
||||
|
||||
tmp = txtmp.vout[outIdx]
|
||||
txtmp.vout = []
|
||||
for i in range(outIdx):
|
||||
txtmp.vout.append(CTxOut(-1))
|
||||
txtmp.vout.append(tmp)
|
||||
|
||||
for i in range(len(txtmp.vin)):
|
||||
if i != inIdx:
|
||||
txtmp.vin[i].nSequence = 0
|
||||
|
||||
if hashtype & SIGHASH_ANYONECANPAY:
|
||||
tmp = txtmp.vin[inIdx]
|
||||
txtmp.vin = []
|
||||
txtmp.vin.append(tmp)
|
||||
|
||||
s = txtmp.serialize()
|
||||
s += struct.pack(b"<I", hashtype)
|
||||
|
||||
hash = hash256(s)
|
||||
|
||||
return (hash, None)
|
||||
|
||||
# TODO: Allow cached hashPrevouts/hashSequence/hashOutputs to be provided.
|
||||
# Performance optimization probably not necessary for python tests, however.
|
||||
# Note that this corresponds to sigversion == 1 in EvalScript, which is used
|
||||
# for version 0 witnesses.
|
||||
def SegwitVersion1SignatureHash(script, txTo, inIdx, hashtype, amount):
|
||||
|
||||
hashPrevouts = 0
|
||||
hashSequence = 0
|
||||
hashOutputs = 0
|
||||
|
||||
if not (hashtype & SIGHASH_ANYONECANPAY):
|
||||
serialize_prevouts = bytes()
|
||||
for i in txTo.vin:
|
||||
serialize_prevouts += i.prevout.serialize()
|
||||
hashPrevouts = uint256_from_str(hash256(serialize_prevouts))
|
||||
|
||||
if (not (hashtype & SIGHASH_ANYONECANPAY) and (hashtype & 0x1f) != SIGHASH_SINGLE and (hashtype & 0x1f) != SIGHASH_NONE):
|
||||
serialize_sequence = bytes()
|
||||
for i in txTo.vin:
|
||||
serialize_sequence += struct.pack("<I", i.nSequence)
|
||||
hashSequence = uint256_from_str(hash256(serialize_sequence))
|
||||
|
||||
if ((hashtype & 0x1f) != SIGHASH_SINGLE and (hashtype & 0x1f) != SIGHASH_NONE):
|
||||
serialize_outputs = bytes()
|
||||
for o in txTo.vout:
|
||||
serialize_outputs += o.serialize()
|
||||
hashOutputs = uint256_from_str(hash256(serialize_outputs))
|
||||
elif ((hashtype & 0x1f) == SIGHASH_SINGLE and inIdx < len(txTo.vout)):
|
||||
serialize_outputs = txTo.vout[inIdx].serialize()
|
||||
hashOutputs = uint256_from_str(hash256(serialize_outputs))
|
||||
|
||||
ss = bytes()
|
||||
ss += struct.pack("<i", txTo.nVersion)
|
||||
ss += ser_uint256(hashPrevouts)
|
||||
ss += ser_uint256(hashSequence)
|
||||
ss += txTo.vin[inIdx].prevout.serialize()
|
||||
ss += ser_string(script)
|
||||
ss += struct.pack("<q", amount)
|
||||
ss += struct.pack("<I", txTo.vin[inIdx].nSequence)
|
||||
ss += ser_uint256(hashOutputs)
|
||||
ss += struct.pack("<i", txTo.nLockTime)
|
||||
ss += struct.pack("<I", hashtype)
|
||||
|
||||
return hash256(ss)
|
||||
64
basicswap/interface/contrib/firo_test_framework/siphash.py
Normal file
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2016 The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#
|
||||
# siphash.py - 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
|
||||
841
basicswap/interface/contrib/firo_test_framework/util.py
Normal file
@@ -0,0 +1,841 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2014-2016 The Bitcoin Core developers
|
||||
# Copyright (c) 2014-2017 The Dash 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
|
||||
#
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from binascii import hexlify, unhexlify
|
||||
from base64 import b64encode
|
||||
from decimal import Decimal, ROUND_DOWN
|
||||
import json
|
||||
import http.client
|
||||
import random
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import time
|
||||
import re
|
||||
import errno
|
||||
import logging
|
||||
|
||||
from . import coverage
|
||||
from .authproxy import AuthServiceProxy, JSONRPCException
|
||||
|
||||
COVERAGE_DIR = None
|
||||
|
||||
logger = logging.getLogger("TestFramework.utils")
|
||||
# The maximum number of nodes a single test can spawn
|
||||
MAX_NODES = 15
|
||||
# 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
|
||||
|
||||
BITCOIND_PROC_WAIT_TIMEOUT = 60
|
||||
|
||||
|
||||
class PortSeed:
|
||||
# Must be initialized with a unique integer for each process
|
||||
n = None
|
||||
|
||||
#Set Mocktime default to OFF.
|
||||
#MOCKTIME is only needed for scripts that use the
|
||||
#cached version of the blockchain. If the cached
|
||||
#version of the blockchain is used without MOCKTIME
|
||||
#then the mempools will not sync due to IBD.
|
||||
MOCKTIME = 0
|
||||
|
||||
def enable_mocktime():
|
||||
#For backwared compatibility of the python scripts
|
||||
#with previous versions of the cache, set MOCKTIME
|
||||
#to Jan 1, 2014 + (201 * 10 * 60)
|
||||
global MOCKTIME
|
||||
MOCKTIME = 1414776313 + (201 * 10 * 60)
|
||||
|
||||
def set_mocktime(t):
|
||||
global MOCKTIME
|
||||
MOCKTIME = t
|
||||
|
||||
def disable_mocktime():
|
||||
global MOCKTIME
|
||||
MOCKTIME = 0
|
||||
|
||||
def get_mocktime():
|
||||
return MOCKTIME
|
||||
|
||||
def enable_coverage(dirname):
|
||||
"""Maintain a log of which RPC calls are made during testing."""
|
||||
global COVERAGE_DIR
|
||||
COVERAGE_DIR = dirname
|
||||
|
||||
|
||||
def get_rpc_proxy(url, node_number, timeout=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(
|
||||
COVERAGE_DIR, node_number) if COVERAGE_DIR else None
|
||||
|
||||
return coverage.AuthServiceProxyWrapper(proxy, coverage_logfile)
|
||||
|
||||
def get_evoznsync_status(node):
|
||||
result = node.evoznsync("status")
|
||||
return result['IsSynced']
|
||||
|
||||
def wait_to_sync(node, fast_znsync=False):
|
||||
tm = 0
|
||||
synced = False
|
||||
while tm < 30:
|
||||
synced = get_evoznsync_status(node)
|
||||
if synced:
|
||||
return
|
||||
time.sleep(0.2)
|
||||
if fast_znsync:
|
||||
# skip mnsync states
|
||||
node.evoznsync("next")
|
||||
tm += 0.2
|
||||
assert(synced)
|
||||
|
||||
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 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 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 sync_blocks(rpc_connections, *, wait=1, timeout=60):
|
||||
"""
|
||||
Wait until everybody has the same tip.
|
||||
|
||||
sync_blocks needs to be called with an rpc_connections set that has least
|
||||
one node already synced to the latest, stable tip, otherwise there's a
|
||||
chance it might return before all nodes are stably synced.
|
||||
"""
|
||||
# Use getblockcount() instead of waitforblockheight() to determine the
|
||||
# initial max height because the two RPCs look at different internal global
|
||||
# variables (chainActive vs latestBlock) and the former gets updated
|
||||
# earlier.
|
||||
maxheight = max(x.getblockcount() for x in rpc_connections)
|
||||
start_time = cur_time = time.time()
|
||||
while cur_time <= start_time + timeout:
|
||||
tips = [r.waitforblockheight(maxheight, int(wait * 1000)) for r in rpc_connections]
|
||||
if all(t["height"] == maxheight for t in tips):
|
||||
if all(t["hash"] == tips[0]["hash"] for t in tips):
|
||||
return
|
||||
raise AssertionError("Block sync failed, mismatched block hashes:{}".format(
|
||||
"".join("\n {!r}".format(tip) for tip in tips)))
|
||||
|
||||
time.sleep(wait)
|
||||
cur_time = time.time()
|
||||
raise AssertionError("Block sync to height {} timed out:{}".format(
|
||||
maxheight, "".join("\n {!r}".format(tip) for tip in tips)))
|
||||
|
||||
def sync_znodes(rpc_connections, *, timeout=60):
|
||||
"""
|
||||
Waits until every node has their znsync status is synced.
|
||||
"""
|
||||
start_time = cur_time = time.time()
|
||||
while cur_time <= start_time + timeout:
|
||||
statuses = [r.znsync("status") for r in rpc_connections]
|
||||
if all(stat["IsSynced"] == True for stat in statuses):
|
||||
return
|
||||
cur_time = time.time()
|
||||
raise AssertionError("Znode sync failed.")
|
||||
|
||||
def sync_chain(rpc_connections, *, wait=1, timeout=60):
|
||||
"""
|
||||
Wait until everybody has the same best block
|
||||
"""
|
||||
while timeout > 0:
|
||||
best_hash = [x.getbestblockhash() for x in rpc_connections]
|
||||
if best_hash == [best_hash[0]]*len(best_hash):
|
||||
return
|
||||
time.sleep(wait)
|
||||
timeout -= wait
|
||||
raise AssertionError("Chain sync failed: Best block hashes don't match")
|
||||
|
||||
def sync_mempools(rpc_connections, *, wait=1, timeout=60):
|
||||
"""
|
||||
Wait until everybody has the same transactions in their memory
|
||||
pools
|
||||
"""
|
||||
while timeout > 0:
|
||||
pool = set(rpc_connections[0].getrawmempool())
|
||||
num_match = 1
|
||||
for i in range(1, len(rpc_connections)):
|
||||
if set(rpc_connections[i].getrawmempool()) == pool:
|
||||
num_match = num_match+1
|
||||
if num_match == len(rpc_connections):
|
||||
return
|
||||
time.sleep(wait)
|
||||
timeout -= wait
|
||||
raise AssertionError("Mempool sync failed")
|
||||
|
||||
def sync_znodes(rpc_connections, fast_mnsync=False):
|
||||
for node in rpc_connections:
|
||||
wait_to_sync(node, fast_mnsync)
|
||||
|
||||
bitcoind_processes = {}
|
||||
|
||||
def initialize_datadir(dirname, n):
|
||||
datadir = os.path.join(dirname, "node"+str(n))
|
||||
if not os.path.isdir(datadir):
|
||||
os.makedirs(datadir)
|
||||
rpc_u, rpc_p = rpc_auth_pair(n)
|
||||
with open(os.path.join(datadir, "firo.conf"), 'w', encoding='utf8') as f:
|
||||
f.write("regtest=1\n")
|
||||
f.write("rpcuser=" + rpc_u + "\n")
|
||||
f.write("rpcpassword=" + rpc_p + "\n")
|
||||
f.write("port="+str(p2p_port(n))+"\n")
|
||||
f.write("rpcport="+str(rpc_port(n))+"\n")
|
||||
f.write("listenonion=0\n")
|
||||
return datadir
|
||||
|
||||
def rpc_auth_pair(n):
|
||||
return 'rpcuser💻' + str(n), 'rpcpass🔑' + str(n)
|
||||
|
||||
def rpc_url(i, rpchost=None):
|
||||
rpc_u, rpc_p = rpc_auth_pair(i)
|
||||
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))
|
||||
|
||||
def wait_for_bitcoind_start(process, url, i):
|
||||
'''
|
||||
Wait for firod to start. This means that RPC is accessible and fully initialized.
|
||||
Raise an exception if firod exits during initialization.
|
||||
'''
|
||||
while True:
|
||||
if process.poll() is not None:
|
||||
raise Exception('firod exited with status %i during initialization' % process.returncode)
|
||||
try:
|
||||
rpc = get_rpc_proxy(url, i)
|
||||
blocks = rpc.getblockcount()
|
||||
break # break out of loop on success
|
||||
except IOError as e:
|
||||
if e.errno != errno.ECONNREFUSED: # Port not yet open?
|
||||
raise # unknown IO error
|
||||
except JSONRPCException as e: # Initialization phase
|
||||
if e.error['code'] != -28: # RPC in warmup?
|
||||
raise # unknown JSON RPC exception
|
||||
time.sleep(0.25)
|
||||
|
||||
def initialize_chain(test_dir, num_nodes, cachedir):
|
||||
"""
|
||||
Create a cache of a 200-block-long chain (with wallet) for MAX_NODES
|
||||
Afterward, create num_nodes copies from the cache
|
||||
"""
|
||||
|
||||
assert num_nodes <= MAX_NODES
|
||||
create_cache = False
|
||||
for i in range(MAX_NODES):
|
||||
if not os.path.isdir(os.path.join(cachedir, 'node'+str(i))):
|
||||
create_cache = True
|
||||
break
|
||||
|
||||
if create_cache:
|
||||
|
||||
#find and delete old cache directories if any exist
|
||||
for i in range(MAX_NODES):
|
||||
if os.path.isdir(os.path.join(cachedir,"node"+str(i))):
|
||||
shutil.rmtree(os.path.join(cachedir,"node"+str(i)))
|
||||
|
||||
# Create cache directories, run bitcoinds:
|
||||
for i in range(MAX_NODES):
|
||||
datadir=initialize_datadir(cachedir, i)
|
||||
args = [ os.getenv("FIROD", "firod"), "-server", "-keypool=1", "-datadir="+datadir, "-discover=0" ]
|
||||
if i > 0:
|
||||
args.append("-connect=127.0.0.1:"+str(p2p_port(0)))
|
||||
bitcoind_processes[i] = subprocess.Popen(args)
|
||||
if os.getenv("PYTHON_DEBUG", ""):
|
||||
print("initialize_chain: bitcoind started, waiting for RPC to come up")
|
||||
wait_for_bitcoind_start(bitcoind_processes[i], rpc_url(i), i)
|
||||
if os.getenv("PYTHON_DEBUG", ""):
|
||||
print("initialize_chain: RPC successfully started")
|
||||
|
||||
rpcs = []
|
||||
for i in range(MAX_NODES):
|
||||
try:
|
||||
rpcs.append(get_rpc_proxy(rpc_url(i), i))
|
||||
except:
|
||||
sys.stderr.write("Error connecting to "+url+"\n")
|
||||
sys.exit(1)
|
||||
|
||||
# Create a 200-block-long chain; each of the 4 first nodes
|
||||
# gets 25 mature blocks and 25 immature.
|
||||
# Note: To preserve compatibility with older versions of
|
||||
# initialize_chain, only 4 nodes will generate coins.
|
||||
#
|
||||
# blocks are created with timestamps 10 minutes apart
|
||||
# starting from 2010 minutes in the past
|
||||
enable_mocktime()
|
||||
block_time = get_mocktime() - (201 * 10 * 60)
|
||||
for i in range(2):
|
||||
for peer in range(4):
|
||||
for j in range(25):
|
||||
set_node_times(rpcs, block_time)
|
||||
rpcs[peer].generate(1)
|
||||
block_time += 10*60
|
||||
# Must sync before next peer starts generating blocks
|
||||
sync_blocks(rpcs)
|
||||
|
||||
# Shut them down, and clean up cache directories:
|
||||
stop_nodes(rpcs)
|
||||
disable_mocktime()
|
||||
for i in range(MAX_NODES):
|
||||
try:
|
||||
os.remove(log_filename(cachedir, i, "debug.log"))
|
||||
os.remove(log_filename(cachedir, i, "db.log"))
|
||||
os.remove(log_filename(cachedir, i, "peers.dat"))
|
||||
os.remove(log_filename(cachedir, i, "fee_estimates.dat"))
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
for i in range(num_nodes):
|
||||
from_dir = os.path.join(cachedir, "node"+str(i))
|
||||
to_dir = os.path.join(test_dir, "node"+str(i))
|
||||
if from_dir != to_dir:
|
||||
shutil.copytree(from_dir, to_dir)
|
||||
initialize_datadir(test_dir, i) # Overwrite port/rpcport in bitcoin.conf
|
||||
|
||||
def initialize_chain_clean(test_dir, num_nodes):
|
||||
"""
|
||||
Create an empty blockchain and num_nodes wallets.
|
||||
Useful if a test case wants complete control over initialization.
|
||||
"""
|
||||
for i in range(num_nodes):
|
||||
datadir=initialize_datadir(test_dir, i)
|
||||
|
||||
|
||||
def _rpchost_to_args(rpchost):
|
||||
'''Convert optional IP:port spec to rpcconnect/rpcport args'''
|
||||
if rpchost is None:
|
||||
return []
|
||||
|
||||
match = re.match('(\[[0-9a-fA-f:]+\]|[^:]+)(?::([0-9]+))?$', rpchost)
|
||||
if not match:
|
||||
raise ValueError('Invalid RPC host spec ' + rpchost)
|
||||
|
||||
rpcconnect = match.group(1)
|
||||
rpcport = match.group(2)
|
||||
|
||||
if rpcconnect.startswith('['): # remove IPv6 [...] wrapping
|
||||
rpcconnect = rpcconnect[1:-1]
|
||||
|
||||
rv = ['-rpcconnect=' + rpcconnect]
|
||||
if rpcport:
|
||||
rv += ['-rpcport=' + rpcport]
|
||||
return rv
|
||||
|
||||
def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=None, redirect_stderr=False, stderr=None):
|
||||
"""
|
||||
Start a bitcoind and return RPC connection to it
|
||||
"""
|
||||
datadir = os.path.join(dirname, "node"+str(i))
|
||||
if binary is None:
|
||||
binary = os.getenv("FIROD", "firod")
|
||||
args = [ binary, "-datadir="+datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-dandelion=0", "-usemnemonic=0", "-mocktime="+str(get_mocktime()) ]
|
||||
#Useful args for debugging
|
||||
# "screen", "--",
|
||||
# "gdb", "-x", "/tmp/gdb_run", "--args",
|
||||
|
||||
# Don't try auto backups (they fail a lot when running tests)
|
||||
args += [ "-createwalletbackups=0" ]
|
||||
if extra_args is not None: args.extend(extra_args)
|
||||
# Allow to redirect stderr to stdout in case we expect some non-critical warnings/errors printed to stderr
|
||||
# Otherwise the whole test would be considered to be failed in such cases
|
||||
if redirect_stderr:
|
||||
stderr = sys.stdout
|
||||
bitcoind_processes[i] = subprocess.Popen(args, stderr=stderr)
|
||||
logger.debug("start_node: firod started, waiting for RPC to come up")
|
||||
url = rpc_url(i, rpchost)
|
||||
wait_for_bitcoind_start(bitcoind_processes[i], url, i)
|
||||
logger.debug("start_node: RPC successfully started")
|
||||
proxy = get_rpc_proxy(url, i, timeout=timewait)
|
||||
|
||||
if COVERAGE_DIR:
|
||||
coverage.write_all_rpc_commands(COVERAGE_DIR, proxy)
|
||||
|
||||
return proxy
|
||||
|
||||
def start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, timewait=None, binary=None):
|
||||
"""
|
||||
Start multiple bitcoinds, return RPC connections to them
|
||||
"""
|
||||
if extra_args is None: extra_args = [ None for _ in range(num_nodes) ]
|
||||
if binary is None: binary = [ None for _ in range(num_nodes) ]
|
||||
rpcs = []
|
||||
try:
|
||||
for i in range(num_nodes):
|
||||
rpcs.append(start_node(i, dirname, extra_args[i], rpchost, timewait=timewait, binary=binary[i]))
|
||||
except: # If one node failed to start, stop the others
|
||||
stop_nodes(rpcs)
|
||||
raise
|
||||
return rpcs
|
||||
|
||||
def copy_datadir(from_node, to_node, dirname):
|
||||
from_datadir = os.path.join(dirname, "node"+str(from_node), "regtest")
|
||||
to_datadir = os.path.join(dirname, "node"+str(to_node), "regtest")
|
||||
|
||||
dirs = ["blocks", "chainstate", "evodb", "llmq"]
|
||||
for d in dirs:
|
||||
try:
|
||||
src = os.path.join(from_datadir, d)
|
||||
dst = os.path.join(to_datadir, d)
|
||||
shutil.copytree(src, dst)
|
||||
except:
|
||||
pass
|
||||
def log_filename(dirname, n_node, logname):
|
||||
return os.path.join(dirname, "node"+str(n_node), "regtest", logname)
|
||||
|
||||
def wait_node(i):
|
||||
return_code = bitcoind_processes[i].wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT)
|
||||
assert_equal(return_code, 0)
|
||||
del bitcoind_processes[i]
|
||||
|
||||
def stop_node(node, i, wait=True):
|
||||
logger.debug("Stopping node %d" % i)
|
||||
try:
|
||||
node.stop()
|
||||
except http.client.CannotSendRequest as e:
|
||||
logger.exception("Unable to stop node")
|
||||
if wait:
|
||||
wait_node(i)
|
||||
|
||||
def stop_nodes(nodes, fast=True):
|
||||
for i, node in enumerate(nodes):
|
||||
stop_node(node, i, not fast)
|
||||
if fast:
|
||||
for i, node in enumerate(nodes):
|
||||
wait_node(i)
|
||||
assert not bitcoind_processes.values() # All connections must be gone now
|
||||
|
||||
def set_node_times(nodes, t):
|
||||
for node in nodes:
|
||||
node.setmocktime(t)
|
||||
|
||||
def connect_nodes(from_connection, node_num):
|
||||
# NOTE: In next line p2p_port(0) was replaced by rpc_port(0).
|
||||
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
|
||||
while any(peer['version'] == 0 for peer in from_connection.getpeerinfo()):
|
||||
time.sleep(0.1)
|
||||
|
||||
def connect_nodes_bi(nodes, a, b):
|
||||
connect_nodes(nodes[a], b)
|
||||
connect_nodes(nodes[b], a)
|
||||
|
||||
def isolate_node(node, timeout=5):
|
||||
node.setnetworkactive(False)
|
||||
st = time.time()
|
||||
while time.time() < st + timeout:
|
||||
if node.getconnectioncount() == 0:
|
||||
return
|
||||
time.sleep(0.5)
|
||||
raise AssertionError("disconnect_node timed out")
|
||||
|
||||
def reconnect_isolated_node(node, node_num):
|
||||
node.setnetworkactive(True)
|
||||
connect_nodes(node, node_num)
|
||||
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 send_zeropri_transaction(from_node, to_node, amount, fee):
|
||||
"""
|
||||
Create&broadcast a zero-priority transaction.
|
||||
Returns (txid, hex-encoded-txdata)
|
||||
Ensures transaction is zero-priority by first creating a send-to-self,
|
||||
then using its output
|
||||
"""
|
||||
|
||||
# Create a send-to-self with confirmed inputs:
|
||||
self_address = from_node.getnewaddress()
|
||||
(total_in, inputs) = gather_inputs(from_node, amount+fee*2)
|
||||
outputs = make_change(from_node, total_in, amount+fee, fee)
|
||||
outputs[self_address] = float(amount+fee)
|
||||
|
||||
self_rawtx = from_node.createrawtransaction(inputs, outputs)
|
||||
self_signresult = from_node.signrawtransaction(self_rawtx)
|
||||
self_txid = from_node.sendrawtransaction(self_signresult["hex"], True)
|
||||
|
||||
vout = find_output(from_node, self_txid, amount+fee)
|
||||
# Now immediately spend the output to create a 1-input, 1-output
|
||||
# zero-priority transaction:
|
||||
inputs = [ { "txid" : self_txid, "vout" : vout } ]
|
||||
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"])
|
||||
|
||||
def random_zeropri_transaction(nodes, amount, min_fee, fee_increment, fee_variants):
|
||||
"""
|
||||
Create a random zero-priority 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)
|
||||
(txid, txhex) = send_zeropri_transaction(from_node, to_node, amount, fee)
|
||||
return (txid, txhex, fee)
|
||||
|
||||
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)
|
||||
|
||||
def assert_fee_amount(fee, tx_size, fee_per_kB):
|
||||
"""Assert the fee was in range"""
|
||||
target_fee = tx_size * fee_per_kB / 1000
|
||||
if fee < target_fee:
|
||||
raise AssertionError("Fee of %s BTC too low! (Should be %s BTC)"%(str(fee), str(target_fee)))
|
||||
# allow the wallet's estimation to be at most 2 bytes off
|
||||
if fee > (tx_size + 2) * fee_per_kB / 1000:
|
||||
raise AssertionError("Fee of %s BTC too high! (Should be %s BTC)"%(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_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 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_jsonrpc(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 returned 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.
|
||||
"""
|
||||
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 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_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 == True:
|
||||
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 == True:
|
||||
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 should_not_find != True:
|
||||
raise AssertionError("No objects matched %s"%(str(to_match)))
|
||||
if num_matched > 0 and should_not_find == True:
|
||||
raise AssertionError("Objects were found %s"%(str(to_match)))
|
||||
|
||||
def satoshi_round(amount):
|
||||
return Decimal(amount).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN)
|
||||
|
||||
# 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):
|
||||
node.generate(int(0.5*count)+101)
|
||||
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] = satoshi_round(send_value/2)
|
||||
outputs[addr2] = satoshi_round(send_value/2)
|
||||
raw_tx = node.createrawtransaction(inputs, outputs)
|
||||
signed_tx = node.signrawtransaction(raw_tx)["hex"]
|
||||
txid = 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"]
|
||||
|
||||
def create_tx_multi_input(node, inputs, outputs):
|
||||
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] = 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 get_bip9_status(node, key):
|
||||
info = node.getblockchaininfo()
|
||||
return info['bip9_softforks'][key]
|
||||
|
||||
def dumpprivkey_otac(node, address):
|
||||
import re
|
||||
error_text = ''
|
||||
try:
|
||||
return node.dumpprivkey(address)
|
||||
except JSONRPCException as e:
|
||||
error_text = e.error
|
||||
else:
|
||||
raise
|
||||
|
||||
otac_match = re.search("Your one time authorization code is: ([a-zA-Z0-9]+)", error_text['message'])
|
||||
if not otac_match:
|
||||
raise JSONRPCException(error_text)
|
||||
return node.dumpprivkey(address, otac_match.groups()[0])
|
||||
|
||||
def get_znsync_status(node):
|
||||
result = node.znsync("status")
|
||||
return result['IsSynced']
|
||||
|
||||
def wait_to_sync_znodes(node, fast_znsync=False):
|
||||
while True:
|
||||
synced = get_znsync_status(node)
|
||||
if synced:
|
||||
break
|
||||
time.sleep(0.2)
|
||||
if fast_znsync:
|
||||
# skip mnsync states
|
||||
node.znsync("next")
|
||||
|
||||
def get_full_balance(node):
|
||||
wallet_info = node.getwalletinfo()
|
||||
return wallet_info["balance"] + wallet_info["immature_balance"] + wallet_info["unconfirmed_balance"]
|
||||
0
basicswap/interface/contrib/nav_test_framework/__init__.py
Executable file
175
basicswap/interface/contrib/nav_test_framework/authproxy.py
Executable file
@@ -0,0 +1,175 @@
|
||||
|
||||
"""
|
||||
Copyright 2011 Jeff Garzik
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
"""
|
||||
|
||||
try:
|
||||
import http.client as httplib
|
||||
except ImportError:
|
||||
import httplib
|
||||
import base64
|
||||
import decimal
|
||||
import json
|
||||
import logging
|
||||
try:
|
||||
import urllib.parse as urlparse
|
||||
except ImportError:
|
||||
import urlparse
|
||||
|
||||
USER_AGENT = "AuthServiceProxy/0.1"
|
||||
|
||||
HTTP_TIMEOUT = 30
|
||||
|
||||
log = logging.getLogger("NavcoinRPC")
|
||||
|
||||
class JSONRPCException(Exception):
|
||||
def __init__(self, rpc_error):
|
||||
Exception.__init__(self)
|
||||
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(object):
|
||||
__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 = urlparse.urlparse(service_url)
|
||||
if self.__url.port is None:
|
||||
port = 80
|
||||
else:
|
||||
port = self.__url.port
|
||||
(user, passwd) = (self.__url.username, self.__url.password)
|
||||
try:
|
||||
user = user.encode('utf8')
|
||||
except AttributeError:
|
||||
pass
|
||||
try:
|
||||
passwd = passwd.encode('utf8')
|
||||
except AttributeError:
|
||||
pass
|
||||
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 = httplib.HTTPSConnection(self.__url.hostname, port,
|
||||
timeout=timeout)
|
||||
else:
|
||||
self.__conn = httplib.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 httplib.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:
|
||||
# Python 3.5+ raises this instead of BadStatusLine when the connection was reset
|
||||
self.__conn.close()
|
||||
self.__conn.request(method, path, postdata, headers)
|
||||
return self._get_response()
|
||||
|
||||
def __call__(self, *args):
|
||||
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)))
|
||||
postdata = json.dumps({'version': '1.1',
|
||||
'method': self._service_name,
|
||||
'params': args,
|
||||
'id': AuthServiceProxy.__id_count}, 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):
|
||||
http_response = self.__conn.getresponse()
|
||||
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)
|
||||
if "error" in response and response["error"] is None:
|
||||
log.debug("<-%s- %s"%(response["id"], json.dumps(response["result"], default=EncodeDecimal, ensure_ascii=self.ensure_ascii)))
|
||||
else:
|
||||
log.debug("<-- "+responsedata)
|
||||
return response
|
||||
101
basicswap/interface/contrib/nav_test_framework/bignum.py
Executable file
@@ -0,0 +1,101 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# bignum.py
|
||||
#
|
||||
# This file is copied from python-navcoinlib.
|
||||
#
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
#
|
||||
|
||||
"""Bignum routines"""
|
||||
|
||||
|
||||
import struct
|
||||
|
||||
|
||||
# generic big endian MPI format
|
||||
|
||||
def bn_bytes(v, have_ext=False):
|
||||
ext = 0
|
||||
if have_ext:
|
||||
ext = 1
|
||||
return ((v.bit_length()+7)//8) + ext
|
||||
|
||||
def bn2bin(v):
|
||||
s = bytearray()
|
||||
i = bn_bytes(v)
|
||||
while i > 0:
|
||||
s.append((v >> ((i-1) * 8)) & 0xff)
|
||||
i -= 1
|
||||
return s
|
||||
|
||||
def bin2bn(s):
|
||||
l = 0
|
||||
for ch in s:
|
||||
l = (l << 8) | ch
|
||||
return l
|
||||
|
||||
def bn2mpi(v):
|
||||
have_ext = False
|
||||
if v.bit_length() > 0:
|
||||
have_ext = (v.bit_length() & 0x07) == 0
|
||||
|
||||
neg = False
|
||||
if v < 0:
|
||||
neg = True
|
||||
v = -v
|
||||
|
||||
s = struct.pack(b">I", bn_bytes(v, have_ext))
|
||||
ext = bytearray()
|
||||
if have_ext:
|
||||
ext.append(0)
|
||||
v_bin = bn2bin(v)
|
||||
if neg:
|
||||
if have_ext:
|
||||
ext[0] |= 0x80
|
||||
else:
|
||||
v_bin[0] |= 0x80
|
||||
return s + ext + v_bin
|
||||
|
||||
def mpi2bn(s):
|
||||
if len(s) < 4:
|
||||
return None
|
||||
s_size = bytes(s[:4])
|
||||
v_len = struct.unpack(b">I", s_size)[0]
|
||||
if len(s) != (v_len + 4):
|
||||
return None
|
||||
if v_len == 0:
|
||||
return 0
|
||||
|
||||
v_str = bytearray(s[4:])
|
||||
neg = False
|
||||
i = v_str[0]
|
||||
if i & 0x80:
|
||||
neg = True
|
||||
i &= ~0x80
|
||||
v_str[0] = i
|
||||
|
||||
v = bin2bn(v_str)
|
||||
|
||||
if neg:
|
||||
return -v
|
||||
return v
|
||||
|
||||
# navcoin-specific little endian format, with implicit size
|
||||
def mpi2vch(s):
|
||||
r = s[4:] # strip size
|
||||
r = r[::-1] # reverse string, converting BE->LE
|
||||
return r
|
||||
|
||||
def bn2vch(v):
|
||||
return bytes(mpi2vch(bn2mpi(v)))
|
||||
|
||||
def vch2mpi(s):
|
||||
r = struct.pack(b">I", len(s)) # size
|
||||
r += s[::-1] # reverse string, converting LE->BE
|
||||
return r
|
||||
|
||||
def vch2bn(s):
|
||||
return mpi2bn(vch2mpi(s))
|
||||
|
||||
106
basicswap/interface/contrib/nav_test_framework/coverage.py
Executable file
@@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-2016 The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
"""
|
||||
This module contains utilities for doing coverage analysis on the RPC
|
||||
interface.
|
||||
|
||||
It provides a way to track which RPC commands are exercised during
|
||||
testing.
|
||||
|
||||
"""
|
||||
import os
|
||||
|
||||
|
||||
REFERENCE_FILENAME = 'rpc_interface.txt'
|
||||
|
||||
|
||||
class AuthServiceProxyWrapper(object):
|
||||
"""
|
||||
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, *args, **kwargs):
|
||||
return_val = self.auth_service_proxy_instance.__getattr__(
|
||||
*args, **kwargs)
|
||||
|
||||
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)
|
||||
rpc_method = self.auth_service_proxy_instance._service_name
|
||||
|
||||
if self.coverage_logfile:
|
||||
with open(self.coverage_logfile, 'a+') as f:
|
||||
f.write("%s\n" % rpc_method)
|
||||
|
||||
return return_val
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
return self.auth_service_proxy_instance.url
|
||||
|
||||
|
||||
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 `navcoin-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') as f:
|
||||
f.writelines(list(commands))
|
||||
|
||||
return True
|
||||
1495
basicswap/interface/contrib/nav_test_framework/mininode.py
Executable file
943
basicswap/interface/contrib/nav_test_framework/script.py
Executable file
@@ -0,0 +1,943 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-2016 The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#
|
||||
# script.py
|
||||
#
|
||||
# This file is modified from python-navcoinlib.
|
||||
#
|
||||
|
||||
"""Scripts
|
||||
|
||||
Functionality to build scripts, as well as SignatureHash().
|
||||
"""
|
||||
|
||||
|
||||
from .mininode import CTransaction, CTxOut, sha256, hash256, uint256_from_str, ser_uint256, ser_string
|
||||
from binascii import hexlify
|
||||
import hashlib
|
||||
|
||||
import sys
|
||||
bchr = chr
|
||||
bord = ord
|
||||
if sys.version > '3':
|
||||
long = int
|
||||
bchr = lambda x: bytes([x])
|
||||
bord = lambda x: x
|
||||
|
||||
import struct
|
||||
|
||||
from .bignum import bn2vch
|
||||
|
||||
MAX_SCRIPT_SIZE = 10000
|
||||
MAX_SCRIPT_ELEMENT_SIZE = 520
|
||||
MAX_SCRIPT_OPCODES = 201
|
||||
|
||||
OPCODE_NAMES = {}
|
||||
|
||||
_opcode_instances = []
|
||||
class CScriptOp(int):
|
||||
"""A single script opcode"""
|
||||
__slots__ = []
|
||||
|
||||
@staticmethod
|
||||
def encode_op_pushdata(d):
|
||||
"""Encode a PUSHDATA op, returning bytes"""
|
||||
if len(d) < 0x4c:
|
||||
return b'' + bchr(len(d)) + d # OP_PUSHDATA
|
||||
elif len(d) <= 0xff:
|
||||
return b'\x4c' + bchr(len(d)) + d # OP_PUSHDATA1
|
||||
elif len(d) <= 0xffff:
|
||||
return b'\x4d' + struct.pack(b'<H', len(d)) + d # OP_PUSHDATA2
|
||||
elif len(d) <= 0xffffffff:
|
||||
return b'\x4e' + struct.pack(b'<I', len(d)) + d # OP_PUSHDATA4
|
||||
else:
|
||||
raise ValueError("Data too long to encode in a PUSHDATA op")
|
||||
|
||||
@staticmethod
|
||||
def encode_op_n(n):
|
||||
"""Encode a small integer op, returning an opcode"""
|
||||
if not (0 <= n <= 16):
|
||||
raise ValueError('Integer must be in range 0 <= n <= 16, got %d' % n)
|
||||
|
||||
if n == 0:
|
||||
return OP_0
|
||||
else:
|
||||
return CScriptOp(OP_1 + n-1)
|
||||
|
||||
def decode_op_n(self):
|
||||
"""Decode a small integer opcode, returning an integer"""
|
||||
if self == OP_0:
|
||||
return 0
|
||||
|
||||
if not (self == OP_0 or OP_1 <= self <= OP_16):
|
||||
raise ValueError('op %r is not an OP_N' % self)
|
||||
|
||||
return int(self - OP_1+1)
|
||||
|
||||
def is_small_int(self):
|
||||
"""Return true if the op pushes a small integer to the stack"""
|
||||
if 0x51 <= self <= 0x60 or self == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def __str__(self):
|
||||
return repr(self)
|
||||
|
||||
def __repr__(self):
|
||||
if self in OPCODE_NAMES:
|
||||
return OPCODE_NAMES[self]
|
||||
else:
|
||||
return 'CScriptOp(0x%x)' % self
|
||||
|
||||
def __new__(cls, n):
|
||||
try:
|
||||
return _opcode_instances[n]
|
||||
except IndexError:
|
||||
assert len(_opcode_instances) == n
|
||||
_opcode_instances.append(super(CScriptOp, cls).__new__(cls, n))
|
||||
return _opcode_instances[n]
|
||||
|
||||
# Populate opcode instance table
|
||||
for n in range(0xff+1):
|
||||
CScriptOp(n)
|
||||
|
||||
|
||||
# push value
|
||||
OP_0 = CScriptOp(0x00)
|
||||
OP_FALSE = OP_0
|
||||
OP_PUSHDATA1 = CScriptOp(0x4c)
|
||||
OP_PUSHDATA2 = CScriptOp(0x4d)
|
||||
OP_PUSHDATA4 = CScriptOp(0x4e)
|
||||
OP_1NEGATE = CScriptOp(0x4f)
|
||||
OP_RESERVED = CScriptOp(0x50)
|
||||
OP_1 = CScriptOp(0x51)
|
||||
OP_TRUE=OP_1
|
||||
OP_2 = CScriptOp(0x52)
|
||||
OP_3 = CScriptOp(0x53)
|
||||
OP_4 = CScriptOp(0x54)
|
||||
OP_5 = CScriptOp(0x55)
|
||||
OP_6 = CScriptOp(0x56)
|
||||
OP_7 = CScriptOp(0x57)
|
||||
OP_8 = CScriptOp(0x58)
|
||||
OP_9 = CScriptOp(0x59)
|
||||
OP_10 = CScriptOp(0x5a)
|
||||
OP_11 = CScriptOp(0x5b)
|
||||
OP_12 = CScriptOp(0x5c)
|
||||
OP_13 = CScriptOp(0x5d)
|
||||
OP_14 = CScriptOp(0x5e)
|
||||
OP_15 = CScriptOp(0x5f)
|
||||
OP_16 = CScriptOp(0x60)
|
||||
|
||||
# control
|
||||
OP_NOP = CScriptOp(0x61)
|
||||
OP_VER = CScriptOp(0x62)
|
||||
OP_IF = CScriptOp(0x63)
|
||||
OP_NOTIF = CScriptOp(0x64)
|
||||
OP_VERIF = CScriptOp(0x65)
|
||||
OP_VERNOTIF = CScriptOp(0x66)
|
||||
OP_ELSE = CScriptOp(0x67)
|
||||
OP_ENDIF = CScriptOp(0x68)
|
||||
OP_VERIFY = CScriptOp(0x69)
|
||||
OP_RETURN = CScriptOp(0x6a)
|
||||
|
||||
# stack ops
|
||||
OP_TOALTSTACK = CScriptOp(0x6b)
|
||||
OP_FROMALTSTACK = CScriptOp(0x6c)
|
||||
OP_2DROP = CScriptOp(0x6d)
|
||||
OP_2DUP = CScriptOp(0x6e)
|
||||
OP_3DUP = CScriptOp(0x6f)
|
||||
OP_2OVER = CScriptOp(0x70)
|
||||
OP_2ROT = CScriptOp(0x71)
|
||||
OP_2SWAP = CScriptOp(0x72)
|
||||
OP_IFDUP = CScriptOp(0x73)
|
||||
OP_DEPTH = CScriptOp(0x74)
|
||||
OP_DROP = CScriptOp(0x75)
|
||||
OP_DUP = CScriptOp(0x76)
|
||||
OP_NIP = CScriptOp(0x77)
|
||||
OP_OVER = CScriptOp(0x78)
|
||||
OP_PICK = CScriptOp(0x79)
|
||||
OP_ROLL = CScriptOp(0x7a)
|
||||
OP_ROT = CScriptOp(0x7b)
|
||||
OP_SWAP = CScriptOp(0x7c)
|
||||
OP_TUCK = CScriptOp(0x7d)
|
||||
|
||||
# splice ops
|
||||
OP_CAT = CScriptOp(0x7e)
|
||||
OP_SUBSTR = CScriptOp(0x7f)
|
||||
OP_LEFT = CScriptOp(0x80)
|
||||
OP_RIGHT = CScriptOp(0x81)
|
||||
OP_SIZE = CScriptOp(0x82)
|
||||
|
||||
# bit logic
|
||||
OP_INVERT = CScriptOp(0x83)
|
||||
OP_AND = CScriptOp(0x84)
|
||||
OP_OR = CScriptOp(0x85)
|
||||
OP_XOR = CScriptOp(0x86)
|
||||
OP_EQUAL = CScriptOp(0x87)
|
||||
OP_EQUALVERIFY = CScriptOp(0x88)
|
||||
OP_RESERVED1 = CScriptOp(0x89)
|
||||
OP_RESERVED2 = CScriptOp(0x8a)
|
||||
|
||||
# numeric
|
||||
OP_1ADD = CScriptOp(0x8b)
|
||||
OP_1SUB = CScriptOp(0x8c)
|
||||
OP_2MUL = CScriptOp(0x8d)
|
||||
OP_2DIV = CScriptOp(0x8e)
|
||||
OP_NEGATE = CScriptOp(0x8f)
|
||||
OP_ABS = CScriptOp(0x90)
|
||||
OP_NOT = CScriptOp(0x91)
|
||||
OP_0NOTEQUAL = CScriptOp(0x92)
|
||||
|
||||
OP_ADD = CScriptOp(0x93)
|
||||
OP_SUB = CScriptOp(0x94)
|
||||
OP_MUL = CScriptOp(0x95)
|
||||
OP_DIV = CScriptOp(0x96)
|
||||
OP_MOD = CScriptOp(0x97)
|
||||
OP_LSHIFT = CScriptOp(0x98)
|
||||
OP_RSHIFT = CScriptOp(0x99)
|
||||
|
||||
OP_BOOLAND = CScriptOp(0x9a)
|
||||
OP_BOOLOR = CScriptOp(0x9b)
|
||||
OP_NUMEQUAL = CScriptOp(0x9c)
|
||||
OP_NUMEQUALVERIFY = CScriptOp(0x9d)
|
||||
OP_NUMNOTEQUAL = CScriptOp(0x9e)
|
||||
OP_LESSTHAN = CScriptOp(0x9f)
|
||||
OP_GREATERTHAN = CScriptOp(0xa0)
|
||||
OP_LESSTHANOREQUAL = CScriptOp(0xa1)
|
||||
OP_GREATERTHANOREQUAL = CScriptOp(0xa2)
|
||||
OP_MIN = CScriptOp(0xa3)
|
||||
OP_MAX = CScriptOp(0xa4)
|
||||
|
||||
OP_WITHIN = CScriptOp(0xa5)
|
||||
|
||||
# crypto
|
||||
OP_RIPEMD160 = CScriptOp(0xa6)
|
||||
OP_SHA1 = CScriptOp(0xa7)
|
||||
OP_SHA256 = CScriptOp(0xa8)
|
||||
OP_HASH160 = CScriptOp(0xa9)
|
||||
OP_HASH256 = CScriptOp(0xaa)
|
||||
OP_CODESEPARATOR = CScriptOp(0xab)
|
||||
OP_CHECKSIG = CScriptOp(0xac)
|
||||
OP_CHECKSIGVERIFY = CScriptOp(0xad)
|
||||
OP_CHECKMULTISIG = CScriptOp(0xae)
|
||||
OP_CHECKMULTISIGVERIFY = CScriptOp(0xaf)
|
||||
|
||||
# expansion
|
||||
OP_NOP1 = CScriptOp(0xb0)
|
||||
OP_CHECKLOCKTIMEVERIFY = CScriptOp(0xb1)
|
||||
OP_CHECKSEQUENCEVERIFY = CScriptOp(0xb2)
|
||||
OP_NOP4 = CScriptOp(0xb3)
|
||||
OP_NOP5 = CScriptOp(0xb4)
|
||||
OP_NOP6 = CScriptOp(0xb5)
|
||||
OP_NOP7 = CScriptOp(0xb6)
|
||||
OP_NOP8 = CScriptOp(0xb7)
|
||||
OP_NOP9 = CScriptOp(0xb8)
|
||||
OP_NOP10 = CScriptOp(0xb9)
|
||||
|
||||
# template matching params
|
||||
OP_SMALLINTEGER = CScriptOp(0xfa)
|
||||
OP_PUBKEYS = CScriptOp(0xfb)
|
||||
OP_PUBKEYHASH = CScriptOp(0xfd)
|
||||
OP_PUBKEY = CScriptOp(0xfe)
|
||||
|
||||
OP_INVALIDOPCODE = CScriptOp(0xff)
|
||||
|
||||
VALID_OPCODES = {
|
||||
OP_1NEGATE,
|
||||
OP_RESERVED,
|
||||
OP_1,
|
||||
OP_2,
|
||||
OP_3,
|
||||
OP_4,
|
||||
OP_5,
|
||||
OP_6,
|
||||
OP_7,
|
||||
OP_8,
|
||||
OP_9,
|
||||
OP_10,
|
||||
OP_11,
|
||||
OP_12,
|
||||
OP_13,
|
||||
OP_14,
|
||||
OP_15,
|
||||
OP_16,
|
||||
|
||||
OP_NOP,
|
||||
OP_VER,
|
||||
OP_IF,
|
||||
OP_NOTIF,
|
||||
OP_VERIF,
|
||||
OP_VERNOTIF,
|
||||
OP_ELSE,
|
||||
OP_ENDIF,
|
||||
OP_VERIFY,
|
||||
OP_RETURN,
|
||||
|
||||
OP_TOALTSTACK,
|
||||
OP_FROMALTSTACK,
|
||||
OP_2DROP,
|
||||
OP_2DUP,
|
||||
OP_3DUP,
|
||||
OP_2OVER,
|
||||
OP_2ROT,
|
||||
OP_2SWAP,
|
||||
OP_IFDUP,
|
||||
OP_DEPTH,
|
||||
OP_DROP,
|
||||
OP_DUP,
|
||||
OP_NIP,
|
||||
OP_OVER,
|
||||
OP_PICK,
|
||||
OP_ROLL,
|
||||
OP_ROT,
|
||||
OP_SWAP,
|
||||
OP_TUCK,
|
||||
|
||||
OP_CAT,
|
||||
OP_SUBSTR,
|
||||
OP_LEFT,
|
||||
OP_RIGHT,
|
||||
OP_SIZE,
|
||||
|
||||
OP_INVERT,
|
||||
OP_AND,
|
||||
OP_OR,
|
||||
OP_XOR,
|
||||
OP_EQUAL,
|
||||
OP_EQUALVERIFY,
|
||||
OP_RESERVED1,
|
||||
OP_RESERVED2,
|
||||
|
||||
OP_1ADD,
|
||||
OP_1SUB,
|
||||
OP_2MUL,
|
||||
OP_2DIV,
|
||||
OP_NEGATE,
|
||||
OP_ABS,
|
||||
OP_NOT,
|
||||
OP_0NOTEQUAL,
|
||||
|
||||
OP_ADD,
|
||||
OP_SUB,
|
||||
OP_MUL,
|
||||
OP_DIV,
|
||||
OP_MOD,
|
||||
OP_LSHIFT,
|
||||
OP_RSHIFT,
|
||||
|
||||
OP_BOOLAND,
|
||||
OP_BOOLOR,
|
||||
OP_NUMEQUAL,
|
||||
OP_NUMEQUALVERIFY,
|
||||
OP_NUMNOTEQUAL,
|
||||
OP_LESSTHAN,
|
||||
OP_GREATERTHAN,
|
||||
OP_LESSTHANOREQUAL,
|
||||
OP_GREATERTHANOREQUAL,
|
||||
OP_MIN,
|
||||
OP_MAX,
|
||||
|
||||
OP_WITHIN,
|
||||
|
||||
OP_RIPEMD160,
|
||||
OP_SHA1,
|
||||
OP_SHA256,
|
||||
OP_HASH160,
|
||||
OP_HASH256,
|
||||
OP_CODESEPARATOR,
|
||||
OP_CHECKSIG,
|
||||
OP_CHECKSIGVERIFY,
|
||||
OP_CHECKMULTISIG,
|
||||
OP_CHECKMULTISIGVERIFY,
|
||||
|
||||
OP_NOP1,
|
||||
OP_CHECKLOCKTIMEVERIFY,
|
||||
OP_CHECKSEQUENCEVERIFY,
|
||||
OP_NOP4,
|
||||
OP_NOP5,
|
||||
OP_NOP6,
|
||||
OP_NOP7,
|
||||
OP_NOP8,
|
||||
OP_NOP9,
|
||||
OP_NOP10,
|
||||
|
||||
OP_SMALLINTEGER,
|
||||
OP_PUBKEYS,
|
||||
OP_PUBKEYHASH,
|
||||
OP_PUBKEY,
|
||||
}
|
||||
|
||||
OPCODE_NAMES.update({
|
||||
OP_0 : 'OP_0',
|
||||
OP_PUSHDATA1 : 'OP_PUSHDATA1',
|
||||
OP_PUSHDATA2 : 'OP_PUSHDATA2',
|
||||
OP_PUSHDATA4 : 'OP_PUSHDATA4',
|
||||
OP_1NEGATE : 'OP_1NEGATE',
|
||||
OP_RESERVED : 'OP_RESERVED',
|
||||
OP_1 : 'OP_1',
|
||||
OP_2 : 'OP_2',
|
||||
OP_3 : 'OP_3',
|
||||
OP_4 : 'OP_4',
|
||||
OP_5 : 'OP_5',
|
||||
OP_6 : 'OP_6',
|
||||
OP_7 : 'OP_7',
|
||||
OP_8 : 'OP_8',
|
||||
OP_9 : 'OP_9',
|
||||
OP_10 : 'OP_10',
|
||||
OP_11 : 'OP_11',
|
||||
OP_12 : 'OP_12',
|
||||
OP_13 : 'OP_13',
|
||||
OP_14 : 'OP_14',
|
||||
OP_15 : 'OP_15',
|
||||
OP_16 : 'OP_16',
|
||||
OP_NOP : 'OP_NOP',
|
||||
OP_VER : 'OP_VER',
|
||||
OP_IF : 'OP_IF',
|
||||
OP_NOTIF : 'OP_NOTIF',
|
||||
OP_VERIF : 'OP_VERIF',
|
||||
OP_VERNOTIF : 'OP_VERNOTIF',
|
||||
OP_ELSE : 'OP_ELSE',
|
||||
OP_ENDIF : 'OP_ENDIF',
|
||||
OP_VERIFY : 'OP_VERIFY',
|
||||
OP_RETURN : 'OP_RETURN',
|
||||
OP_TOALTSTACK : 'OP_TOALTSTACK',
|
||||
OP_FROMALTSTACK : 'OP_FROMALTSTACK',
|
||||
OP_2DROP : 'OP_2DROP',
|
||||
OP_2DUP : 'OP_2DUP',
|
||||
OP_3DUP : 'OP_3DUP',
|
||||
OP_2OVER : 'OP_2OVER',
|
||||
OP_2ROT : 'OP_2ROT',
|
||||
OP_2SWAP : 'OP_2SWAP',
|
||||
OP_IFDUP : 'OP_IFDUP',
|
||||
OP_DEPTH : 'OP_DEPTH',
|
||||
OP_DROP : 'OP_DROP',
|
||||
OP_DUP : 'OP_DUP',
|
||||
OP_NIP : 'OP_NIP',
|
||||
OP_OVER : 'OP_OVER',
|
||||
OP_PICK : 'OP_PICK',
|
||||
OP_ROLL : 'OP_ROLL',
|
||||
OP_ROT : 'OP_ROT',
|
||||
OP_SWAP : 'OP_SWAP',
|
||||
OP_TUCK : 'OP_TUCK',
|
||||
OP_CAT : 'OP_CAT',
|
||||
OP_SUBSTR : 'OP_SUBSTR',
|
||||
OP_LEFT : 'OP_LEFT',
|
||||
OP_RIGHT : 'OP_RIGHT',
|
||||
OP_SIZE : 'OP_SIZE',
|
||||
OP_INVERT : 'OP_INVERT',
|
||||
OP_AND : 'OP_AND',
|
||||
OP_OR : 'OP_OR',
|
||||
OP_XOR : 'OP_XOR',
|
||||
OP_EQUAL : 'OP_EQUAL',
|
||||
OP_EQUALVERIFY : 'OP_EQUALVERIFY',
|
||||
OP_RESERVED1 : 'OP_RESERVED1',
|
||||
OP_RESERVED2 : 'OP_RESERVED2',
|
||||
OP_1ADD : 'OP_1ADD',
|
||||
OP_1SUB : 'OP_1SUB',
|
||||
OP_2MUL : 'OP_2MUL',
|
||||
OP_2DIV : 'OP_2DIV',
|
||||
OP_NEGATE : 'OP_NEGATE',
|
||||
OP_ABS : 'OP_ABS',
|
||||
OP_NOT : 'OP_NOT',
|
||||
OP_0NOTEQUAL : 'OP_0NOTEQUAL',
|
||||
OP_ADD : 'OP_ADD',
|
||||
OP_SUB : 'OP_SUB',
|
||||
OP_MUL : 'OP_MUL',
|
||||
OP_DIV : 'OP_DIV',
|
||||
OP_MOD : 'OP_MOD',
|
||||
OP_LSHIFT : 'OP_LSHIFT',
|
||||
OP_RSHIFT : 'OP_RSHIFT',
|
||||
OP_BOOLAND : 'OP_BOOLAND',
|
||||
OP_BOOLOR : 'OP_BOOLOR',
|
||||
OP_NUMEQUAL : 'OP_NUMEQUAL',
|
||||
OP_NUMEQUALVERIFY : 'OP_NUMEQUALVERIFY',
|
||||
OP_NUMNOTEQUAL : 'OP_NUMNOTEQUAL',
|
||||
OP_LESSTHAN : 'OP_LESSTHAN',
|
||||
OP_GREATERTHAN : 'OP_GREATERTHAN',
|
||||
OP_LESSTHANOREQUAL : 'OP_LESSTHANOREQUAL',
|
||||
OP_GREATERTHANOREQUAL : 'OP_GREATERTHANOREQUAL',
|
||||
OP_MIN : 'OP_MIN',
|
||||
OP_MAX : 'OP_MAX',
|
||||
OP_WITHIN : 'OP_WITHIN',
|
||||
OP_RIPEMD160 : 'OP_RIPEMD160',
|
||||
OP_SHA1 : 'OP_SHA1',
|
||||
OP_SHA256 : 'OP_SHA256',
|
||||
OP_HASH160 : 'OP_HASH160',
|
||||
OP_HASH256 : 'OP_HASH256',
|
||||
OP_CODESEPARATOR : 'OP_CODESEPARATOR',
|
||||
OP_CHECKSIG : 'OP_CHECKSIG',
|
||||
OP_CHECKSIGVERIFY : 'OP_CHECKSIGVERIFY',
|
||||
OP_CHECKMULTISIG : 'OP_CHECKMULTISIG',
|
||||
OP_CHECKMULTISIGVERIFY : 'OP_CHECKMULTISIGVERIFY',
|
||||
OP_NOP1 : 'OP_NOP1',
|
||||
OP_CHECKLOCKTIMEVERIFY : 'OP_CHECKLOCKTIMEVERIFY',
|
||||
OP_CHECKSEQUENCEVERIFY : 'OP_CHECKSEQUENCEVERIFY',
|
||||
OP_NOP4 : 'OP_NOP4',
|
||||
OP_NOP5 : 'OP_NOP5',
|
||||
OP_NOP6 : 'OP_NOP6',
|
||||
OP_NOP7 : 'OP_NOP7',
|
||||
OP_NOP8 : 'OP_NOP8',
|
||||
OP_NOP9 : 'OP_NOP9',
|
||||
OP_NOP10 : 'OP_NOP10',
|
||||
OP_SMALLINTEGER : 'OP_SMALLINTEGER',
|
||||
OP_PUBKEYS : 'OP_PUBKEYS',
|
||||
OP_PUBKEYHASH : 'OP_PUBKEYHASH',
|
||||
OP_PUBKEY : 'OP_PUBKEY',
|
||||
OP_INVALIDOPCODE : 'OP_INVALIDOPCODE',
|
||||
})
|
||||
|
||||
OPCODES_BY_NAME = {
|
||||
'OP_0' : OP_0,
|
||||
'OP_PUSHDATA1' : OP_PUSHDATA1,
|
||||
'OP_PUSHDATA2' : OP_PUSHDATA2,
|
||||
'OP_PUSHDATA4' : OP_PUSHDATA4,
|
||||
'OP_1NEGATE' : OP_1NEGATE,
|
||||
'OP_RESERVED' : OP_RESERVED,
|
||||
'OP_1' : OP_1,
|
||||
'OP_2' : OP_2,
|
||||
'OP_3' : OP_3,
|
||||
'OP_4' : OP_4,
|
||||
'OP_5' : OP_5,
|
||||
'OP_6' : OP_6,
|
||||
'OP_7' : OP_7,
|
||||
'OP_8' : OP_8,
|
||||
'OP_9' : OP_9,
|
||||
'OP_10' : OP_10,
|
||||
'OP_11' : OP_11,
|
||||
'OP_12' : OP_12,
|
||||
'OP_13' : OP_13,
|
||||
'OP_14' : OP_14,
|
||||
'OP_15' : OP_15,
|
||||
'OP_16' : OP_16,
|
||||
'OP_NOP' : OP_NOP,
|
||||
'OP_VER' : OP_VER,
|
||||
'OP_IF' : OP_IF,
|
||||
'OP_NOTIF' : OP_NOTIF,
|
||||
'OP_VERIF' : OP_VERIF,
|
||||
'OP_VERNOTIF' : OP_VERNOTIF,
|
||||
'OP_ELSE' : OP_ELSE,
|
||||
'OP_ENDIF' : OP_ENDIF,
|
||||
'OP_VERIFY' : OP_VERIFY,
|
||||
'OP_RETURN' : OP_RETURN,
|
||||
'OP_TOALTSTACK' : OP_TOALTSTACK,
|
||||
'OP_FROMALTSTACK' : OP_FROMALTSTACK,
|
||||
'OP_2DROP' : OP_2DROP,
|
||||
'OP_2DUP' : OP_2DUP,
|
||||
'OP_3DUP' : OP_3DUP,
|
||||
'OP_2OVER' : OP_2OVER,
|
||||
'OP_2ROT' : OP_2ROT,
|
||||
'OP_2SWAP' : OP_2SWAP,
|
||||
'OP_IFDUP' : OP_IFDUP,
|
||||
'OP_DEPTH' : OP_DEPTH,
|
||||
'OP_DROP' : OP_DROP,
|
||||
'OP_DUP' : OP_DUP,
|
||||
'OP_NIP' : OP_NIP,
|
||||
'OP_OVER' : OP_OVER,
|
||||
'OP_PICK' : OP_PICK,
|
||||
'OP_ROLL' : OP_ROLL,
|
||||
'OP_ROT' : OP_ROT,
|
||||
'OP_SWAP' : OP_SWAP,
|
||||
'OP_TUCK' : OP_TUCK,
|
||||
'OP_CAT' : OP_CAT,
|
||||
'OP_SUBSTR' : OP_SUBSTR,
|
||||
'OP_LEFT' : OP_LEFT,
|
||||
'OP_RIGHT' : OP_RIGHT,
|
||||
'OP_SIZE' : OP_SIZE,
|
||||
'OP_INVERT' : OP_INVERT,
|
||||
'OP_AND' : OP_AND,
|
||||
'OP_OR' : OP_OR,
|
||||
'OP_XOR' : OP_XOR,
|
||||
'OP_EQUAL' : OP_EQUAL,
|
||||
'OP_EQUALVERIFY' : OP_EQUALVERIFY,
|
||||
'OP_RESERVED1' : OP_RESERVED1,
|
||||
'OP_RESERVED2' : OP_RESERVED2,
|
||||
'OP_1ADD' : OP_1ADD,
|
||||
'OP_1SUB' : OP_1SUB,
|
||||
'OP_2MUL' : OP_2MUL,
|
||||
'OP_2DIV' : OP_2DIV,
|
||||
'OP_NEGATE' : OP_NEGATE,
|
||||
'OP_ABS' : OP_ABS,
|
||||
'OP_NOT' : OP_NOT,
|
||||
'OP_0NOTEQUAL' : OP_0NOTEQUAL,
|
||||
'OP_ADD' : OP_ADD,
|
||||
'OP_SUB' : OP_SUB,
|
||||
'OP_MUL' : OP_MUL,
|
||||
'OP_DIV' : OP_DIV,
|
||||
'OP_MOD' : OP_MOD,
|
||||
'OP_LSHIFT' : OP_LSHIFT,
|
||||
'OP_RSHIFT' : OP_RSHIFT,
|
||||
'OP_BOOLAND' : OP_BOOLAND,
|
||||
'OP_BOOLOR' : OP_BOOLOR,
|
||||
'OP_NUMEQUAL' : OP_NUMEQUAL,
|
||||
'OP_NUMEQUALVERIFY' : OP_NUMEQUALVERIFY,
|
||||
'OP_NUMNOTEQUAL' : OP_NUMNOTEQUAL,
|
||||
'OP_LESSTHAN' : OP_LESSTHAN,
|
||||
'OP_GREATERTHAN' : OP_GREATERTHAN,
|
||||
'OP_LESSTHANOREQUAL' : OP_LESSTHANOREQUAL,
|
||||
'OP_GREATERTHANOREQUAL' : OP_GREATERTHANOREQUAL,
|
||||
'OP_MIN' : OP_MIN,
|
||||
'OP_MAX' : OP_MAX,
|
||||
'OP_WITHIN' : OP_WITHIN,
|
||||
'OP_RIPEMD160' : OP_RIPEMD160,
|
||||
'OP_SHA1' : OP_SHA1,
|
||||
'OP_SHA256' : OP_SHA256,
|
||||
'OP_HASH160' : OP_HASH160,
|
||||
'OP_HASH256' : OP_HASH256,
|
||||
'OP_CODESEPARATOR' : OP_CODESEPARATOR,
|
||||
'OP_CHECKSIG' : OP_CHECKSIG,
|
||||
'OP_CHECKSIGVERIFY' : OP_CHECKSIGVERIFY,
|
||||
'OP_CHECKMULTISIG' : OP_CHECKMULTISIG,
|
||||
'OP_CHECKMULTISIGVERIFY' : OP_CHECKMULTISIGVERIFY,
|
||||
'OP_NOP1' : OP_NOP1,
|
||||
'OP_CHECKLOCKTIMEVERIFY' : OP_CHECKLOCKTIMEVERIFY,
|
||||
'OP_CHECKSEQUENCEVERIFY' : OP_CHECKSEQUENCEVERIFY,
|
||||
'OP_NOP4' : OP_NOP4,
|
||||
'OP_NOP5' : OP_NOP5,
|
||||
'OP_NOP6' : OP_NOP6,
|
||||
'OP_NOP7' : OP_NOP7,
|
||||
'OP_NOP8' : OP_NOP8,
|
||||
'OP_NOP9' : OP_NOP9,
|
||||
'OP_NOP10' : OP_NOP10,
|
||||
'OP_SMALLINTEGER' : OP_SMALLINTEGER,
|
||||
'OP_PUBKEYS' : OP_PUBKEYS,
|
||||
'OP_PUBKEYHASH' : OP_PUBKEYHASH,
|
||||
'OP_PUBKEY' : OP_PUBKEY,
|
||||
}
|
||||
|
||||
class CScriptInvalidError(Exception):
|
||||
"""Base class for CScript exceptions"""
|
||||
pass
|
||||
|
||||
class CScriptTruncatedPushDataError(CScriptInvalidError):
|
||||
"""Invalid pushdata due to truncation"""
|
||||
def __init__(self, msg, data):
|
||||
self.data = data
|
||||
super(CScriptTruncatedPushDataError, self).__init__(msg)
|
||||
|
||||
# This is used, eg, for blockchain heights in coinbase scripts (bip34)
|
||||
class CScriptNum(object):
|
||||
def __init__(self, d=0):
|
||||
self.value = d
|
||||
|
||||
@staticmethod
|
||||
def encode(obj):
|
||||
r = bytearray(0)
|
||||
if obj.value == 0:
|
||||
return bytes(r)
|
||||
neg = obj.value < 0
|
||||
absvalue = -obj.value if neg else obj.value
|
||||
while (absvalue):
|
||||
r.append(absvalue & 0xff)
|
||||
absvalue >>= 8
|
||||
if r[-1] & 0x80:
|
||||
r.append(0x80 if neg else 0)
|
||||
elif neg:
|
||||
r[-1] |= 0x80
|
||||
return bytes(bchr(len(r)) + r)
|
||||
|
||||
|
||||
class CScript(bytes):
|
||||
"""Serialized script
|
||||
|
||||
A bytes subclass, so you can use this directly whenever bytes are accepted.
|
||||
Note that this means that indexing does *not* work - you'll get an index by
|
||||
byte rather than opcode. This format was chosen for efficiency so that the
|
||||
general case would not require creating a lot of little CScriptOP objects.
|
||||
|
||||
iter(script) however does iterate by opcode.
|
||||
"""
|
||||
@classmethod
|
||||
def __coerce_instance(cls, other):
|
||||
# Coerce other into bytes
|
||||
if isinstance(other, CScriptOp):
|
||||
other = bchr(other)
|
||||
elif isinstance(other, CScriptNum):
|
||||
if (other.value == 0):
|
||||
other = bchr(CScriptOp(OP_0))
|
||||
else:
|
||||
other = CScriptNum.encode(other)
|
||||
elif isinstance(other, int):
|
||||
if 0 <= other <= 16:
|
||||
other = bytes(bchr(CScriptOp.encode_op_n(other)))
|
||||
elif other == -1:
|
||||
other = bytes(bchr(OP_1NEGATE))
|
||||
else:
|
||||
other = CScriptOp.encode_op_pushdata(bn2vch(other))
|
||||
elif isinstance(other, (bytes, bytearray)):
|
||||
other = CScriptOp.encode_op_pushdata(other)
|
||||
return other
|
||||
|
||||
def __add__(self, other):
|
||||
# Do the coercion outside of the try block so that errors in it are
|
||||
# noticed.
|
||||
other = self.__coerce_instance(other)
|
||||
|
||||
try:
|
||||
# bytes.__add__ always returns bytes instances unfortunately
|
||||
return CScript(super(CScript, self).__add__(other))
|
||||
except TypeError:
|
||||
raise TypeError('Can not add a %r instance to a CScript' % other.__class__)
|
||||
|
||||
def join(self, iterable):
|
||||
# join makes no sense for a CScript()
|
||||
raise NotImplementedError
|
||||
|
||||
def __new__(cls, value=b''):
|
||||
if isinstance(value, bytes) or isinstance(value, bytearray):
|
||||
return super(CScript, cls).__new__(cls, value)
|
||||
else:
|
||||
def coerce_iterable(iterable):
|
||||
for instance in iterable:
|
||||
yield cls.__coerce_instance(instance)
|
||||
# Annoyingly on both python2 and python3 bytes.join() always
|
||||
# returns a bytes instance even when subclassed.
|
||||
return super(CScript, cls).__new__(cls, b''.join(coerce_iterable(value)))
|
||||
|
||||
def raw_iter(self):
|
||||
"""Raw iteration
|
||||
|
||||
Yields tuples of (opcode, data, sop_idx) so that the different possible
|
||||
PUSHDATA encodings can be accurately distinguished, as well as
|
||||
determining the exact opcode byte indexes. (sop_idx)
|
||||
"""
|
||||
i = 0
|
||||
while i < len(self):
|
||||
sop_idx = i
|
||||
opcode = bord(self[i])
|
||||
i += 1
|
||||
|
||||
if opcode > OP_PUSHDATA4:
|
||||
yield (opcode, None, sop_idx)
|
||||
else:
|
||||
datasize = None
|
||||
pushdata_type = None
|
||||
if opcode < OP_PUSHDATA1:
|
||||
pushdata_type = 'PUSHDATA(%d)' % opcode
|
||||
datasize = opcode
|
||||
|
||||
elif opcode == OP_PUSHDATA1:
|
||||
pushdata_type = 'PUSHDATA1'
|
||||
if i >= len(self):
|
||||
raise CScriptInvalidError('PUSHDATA1: missing data length')
|
||||
datasize = bord(self[i])
|
||||
i += 1
|
||||
|
||||
elif opcode == OP_PUSHDATA2:
|
||||
pushdata_type = 'PUSHDATA2'
|
||||
if i + 1 >= len(self):
|
||||
raise CScriptInvalidError('PUSHDATA2: missing data length')
|
||||
datasize = bord(self[i]) + (bord(self[i+1]) << 8)
|
||||
i += 2
|
||||
|
||||
elif opcode == OP_PUSHDATA4:
|
||||
pushdata_type = 'PUSHDATA4'
|
||||
if i + 3 >= len(self):
|
||||
raise CScriptInvalidError('PUSHDATA4: missing data length')
|
||||
datasize = bord(self[i]) + (bord(self[i+1]) << 8) + (bord(self[i+2]) << 16) + (bord(self[i+3]) << 24)
|
||||
i += 4
|
||||
|
||||
else:
|
||||
assert False # shouldn't happen
|
||||
|
||||
|
||||
data = bytes(self[i:i+datasize])
|
||||
|
||||
# Check for truncation
|
||||
if len(data) < datasize:
|
||||
raise CScriptTruncatedPushDataError('%s: truncated data' % pushdata_type, data)
|
||||
|
||||
i += datasize
|
||||
|
||||
yield (opcode, data, sop_idx)
|
||||
|
||||
def __iter__(self):
|
||||
"""'Cooked' iteration
|
||||
|
||||
Returns either a CScriptOP instance, an integer, or bytes, as
|
||||
appropriate.
|
||||
|
||||
See raw_iter() if you need to distinguish the different possible
|
||||
PUSHDATA encodings.
|
||||
"""
|
||||
for (opcode, data, sop_idx) in self.raw_iter():
|
||||
if data is not None:
|
||||
yield data
|
||||
else:
|
||||
opcode = CScriptOp(opcode)
|
||||
|
||||
if opcode.is_small_int():
|
||||
yield opcode.decode_op_n()
|
||||
else:
|
||||
yield CScriptOp(opcode)
|
||||
|
||||
def __repr__(self):
|
||||
# For Python3 compatibility add b before strings so testcases don't
|
||||
# need to change
|
||||
def _repr(o):
|
||||
if isinstance(o, bytes):
|
||||
return b"x('%s')" % hexlify(o).decode('ascii')
|
||||
else:
|
||||
return repr(o)
|
||||
|
||||
ops = []
|
||||
i = iter(self)
|
||||
while True:
|
||||
op = None
|
||||
try:
|
||||
op = _repr(next(i))
|
||||
except CScriptTruncatedPushDataError as err:
|
||||
op = '%s...<ERROR: %s>' % (_repr(err.data), err)
|
||||
break
|
||||
except CScriptInvalidError as err:
|
||||
op = '<ERROR: %s>' % err
|
||||
break
|
||||
except StopIteration:
|
||||
break
|
||||
finally:
|
||||
if op is not None:
|
||||
ops.append(op)
|
||||
|
||||
return "CScript([%s])" % ', '.join(ops)
|
||||
|
||||
def GetSigOpCount(self, fAccurate):
|
||||
"""Get the SigOp count.
|
||||
|
||||
fAccurate - Accurately count CHECKMULTISIG, see BIP16 for details.
|
||||
|
||||
Note that this is consensus-critical.
|
||||
"""
|
||||
n = 0
|
||||
lastOpcode = OP_INVALIDOPCODE
|
||||
for (opcode, data, sop_idx) in self.raw_iter():
|
||||
if opcode in (OP_CHECKSIG, OP_CHECKSIGVERIFY):
|
||||
n += 1
|
||||
elif opcode in (OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY):
|
||||
if fAccurate and (OP_1 <= lastOpcode <= OP_16):
|
||||
n += opcode.decode_op_n()
|
||||
else:
|
||||
n += 20
|
||||
lastOpcode = opcode
|
||||
return n
|
||||
|
||||
|
||||
SIGHASH_ALL = 1
|
||||
SIGHASH_NONE = 2
|
||||
SIGHASH_SINGLE = 3
|
||||
SIGHASH_ANYONECANPAY = 0x80
|
||||
|
||||
def FindAndDelete(script, sig):
|
||||
"""Consensus critical, see FindAndDelete() in Satoshi codebase"""
|
||||
r = b''
|
||||
last_sop_idx = sop_idx = 0
|
||||
skip = True
|
||||
for (opcode, data, sop_idx) in script.raw_iter():
|
||||
if not skip:
|
||||
r += script[last_sop_idx:sop_idx]
|
||||
last_sop_idx = sop_idx
|
||||
if script[sop_idx:sop_idx + len(sig)] == sig:
|
||||
skip = True
|
||||
else:
|
||||
skip = False
|
||||
if not skip:
|
||||
r += script[last_sop_idx:]
|
||||
return CScript(r)
|
||||
|
||||
|
||||
def SignatureHash(script, txTo, inIdx, hashtype):
|
||||
"""Consensus-correct SignatureHash
|
||||
|
||||
Returns (hash, err) to precisely match the consensus-critical behavior of
|
||||
the SIGHASH_SINGLE bug. (inIdx is *not* checked for validity)
|
||||
"""
|
||||
HASH_ONE = b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
|
||||
if inIdx >= len(txTo.vin):
|
||||
return (HASH_ONE, "inIdx %d out of range (%d)" % (inIdx, len(txTo.vin)))
|
||||
txtmp = CTransaction(txTo)
|
||||
|
||||
for txin in txtmp.vin:
|
||||
txin.scriptSig = b''
|
||||
txtmp.vin[inIdx].scriptSig = FindAndDelete(script, CScript([OP_CODESEPARATOR]))
|
||||
|
||||
if (hashtype & 0x1f) == SIGHASH_NONE:
|
||||
txtmp.vout = []
|
||||
|
||||
for i in range(len(txtmp.vin)):
|
||||
if i != inIdx:
|
||||
txtmp.vin[i].nSequence = 0
|
||||
|
||||
elif (hashtype & 0x1f) == SIGHASH_SINGLE:
|
||||
outIdx = inIdx
|
||||
if outIdx >= len(txtmp.vout):
|
||||
return (HASH_ONE, "outIdx %d out of range (%d)" % (outIdx, len(txtmp.vout)))
|
||||
|
||||
tmp = txtmp.vout[outIdx]
|
||||
txtmp.vout = []
|
||||
for i in range(outIdx):
|
||||
txtmp.vout.append(CTxOut())
|
||||
txtmp.vout.append(tmp)
|
||||
|
||||
for i in range(len(txtmp.vin)):
|
||||
if i != inIdx:
|
||||
txtmp.vin[i].nSequence = 0
|
||||
|
||||
if hashtype & SIGHASH_ANYONECANPAY:
|
||||
tmp = txtmp.vin[inIdx]
|
||||
txtmp.vin = []
|
||||
txtmp.vin.append(tmp)
|
||||
|
||||
s = txtmp.serialize()
|
||||
s += struct.pack(b"<I", hashtype)
|
||||
|
||||
hash = hash256(s)
|
||||
|
||||
return (hash, None)
|
||||
|
||||
# TODO: Allow cached hashPrevouts/hashSequence/hashOutputs to be provided.
|
||||
# Performance optimization probably not necessary for python tests, however.
|
||||
# Note that this corresponds to sigversion == 1 in EvalScript, which is used
|
||||
# for version 0 witnesses.
|
||||
def SegwitVersion1SignatureHash(script, txTo, inIdx, hashtype, amount):
|
||||
|
||||
hashPrevouts = 0
|
||||
hashSequence = 0
|
||||
hashOutputs = 0
|
||||
|
||||
if not (hashtype & SIGHASH_ANYONECANPAY):
|
||||
serialize_prevouts = bytes()
|
||||
for i in txTo.vin:
|
||||
serialize_prevouts += i.prevout.serialize()
|
||||
hashPrevouts = uint256_from_str(hash256(serialize_prevouts))
|
||||
|
||||
if (not (hashtype & SIGHASH_ANYONECANPAY) and (hashtype & 0x1f) != SIGHASH_SINGLE and (hashtype & 0x1f) != SIGHASH_NONE):
|
||||
serialize_sequence = bytes()
|
||||
for i in txTo.vin:
|
||||
serialize_sequence += struct.pack("<I", i.nSequence)
|
||||
hashSequence = uint256_from_str(hash256(serialize_sequence))
|
||||
|
||||
if ((hashtype & 0x1f) != SIGHASH_SINGLE and (hashtype & 0x1f) != SIGHASH_NONE):
|
||||
serialize_outputs = bytes()
|
||||
for o in txTo.vout:
|
||||
serialize_outputs += o.serialize()
|
||||
hashOutputs = uint256_from_str(hash256(serialize_outputs))
|
||||
elif ((hashtype & 0x1f) == SIGHASH_SINGLE and inIdx < len(txTo.vout)):
|
||||
serialize_outputs = txTo.vout[inIdx].serialize()
|
||||
hashOutputs = uint256_from_str(hash256(serialize_outputs))
|
||||
|
||||
ss = bytes()
|
||||
ss += struct.pack("<i", txTo.nVersion)
|
||||
ss += ser_uint256(hashPrevouts)
|
||||
ss += ser_uint256(hashSequence)
|
||||
ss += txTo.vin[inIdx].prevout.serialize()
|
||||
ss += ser_string(script)
|
||||
ss += struct.pack("<q", amount)
|
||||
ss += struct.pack("<I", txTo.vin[inIdx].nSequence)
|
||||
ss += ser_uint256(hashOutputs)
|
||||
ss += struct.pack("<i", txTo.nLockTime)
|
||||
ss += struct.pack("<I", hashtype)
|
||||
|
||||
return hash256(ss)
|
||||
63
basicswap/interface/contrib/nav_test_framework/siphash.py
Normal file
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2016-2018 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
|
||||
700
basicswap/interface/contrib/nav_test_framework/util.py
Executable file
@@ -0,0 +1,700 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2014-2016 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
|
||||
#
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from binascii import hexlify, unhexlify
|
||||
from base64 import b64encode
|
||||
from decimal import Decimal, ROUND_DOWN
|
||||
import json
|
||||
import http.client
|
||||
import random
|
||||
import shutil
|
||||
import subprocess
|
||||
import time
|
||||
import re
|
||||
import errno
|
||||
|
||||
from . import coverage
|
||||
from .authproxy import AuthServiceProxy, JSONRPCException
|
||||
|
||||
COVERAGE_DIR = None
|
||||
|
||||
# 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
|
||||
|
||||
NAVCOIND_PROC_WAIT_TIMEOUT = 60
|
||||
|
||||
|
||||
class PortSeed:
|
||||
# Must be initialized with a unique integer for each process
|
||||
n = None
|
||||
|
||||
#Set Mocktime default to OFF.
|
||||
#MOCKTIME is only needed for scripts that use the
|
||||
#cached version of the blockchain. If the cached
|
||||
#version of the blockchain is used without MOCKTIME
|
||||
#then the mempools will not sync due to IBD.
|
||||
MOCKTIME = 0
|
||||
|
||||
def enable_mocktime():
|
||||
#For backwared compatibility of the python scripts
|
||||
#with previous versions of the cache, set MOCKTIME
|
||||
#to Jan 1, 2014 + (201 * 10 * 60)
|
||||
global MOCKTIME
|
||||
MOCKTIME = 1388534400 + (201 * 10 * 60)
|
||||
|
||||
def disable_mocktime():
|
||||
global MOCKTIME
|
||||
MOCKTIME = 0
|
||||
|
||||
def get_mocktime():
|
||||
return MOCKTIME
|
||||
|
||||
def enable_coverage(dirname):
|
||||
"""Maintain a log of which RPC calls are made during testing."""
|
||||
global COVERAGE_DIR
|
||||
COVERAGE_DIR = dirname
|
||||
|
||||
def get_rpc_proxy(url, node_number, timeout=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(
|
||||
COVERAGE_DIR, node_number) if COVERAGE_DIR 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 check_json_precision():
|
||||
"""Make sure json library being used does not lose precision converting NAV 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 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 sync_blocks(rpc_connections, wait=1, timeout=60):
|
||||
"""
|
||||
Wait until everybody has the same tip
|
||||
"""
|
||||
while timeout > 0:
|
||||
tips = [ x.getbestblockhash() for x in rpc_connections ]
|
||||
if tips == [ tips[0] ]*len(tips):
|
||||
#if all x.getblockhash() in tips are the same return True
|
||||
return True
|
||||
time.sleep(wait)
|
||||
timeout -= wait
|
||||
raise AssertionError("Block sync failed")
|
||||
|
||||
def sync_mempools(rpc_connections, wait=1, timeout=60):
|
||||
"""
|
||||
Wait until everybody has the same transactions in their memory
|
||||
pools
|
||||
"""
|
||||
while timeout > 0:
|
||||
pool = set(rpc_connections[0].getrawmempool())
|
||||
num_match = 1
|
||||
for i in range(1, len(rpc_connections)):
|
||||
if set(rpc_connections[i].getrawmempool()) == pool:
|
||||
num_match = num_match+1
|
||||
if num_match == len(rpc_connections):
|
||||
return True
|
||||
time.sleep(wait)
|
||||
timeout -= wait
|
||||
raise AssertionError("Mempool sync failed")
|
||||
|
||||
navcoind_processes = {}
|
||||
|
||||
def initialize_datadir(dirname, n):
|
||||
datadir = os.path.join(dirname, "node"+str(n))
|
||||
if not os.path.isdir(datadir):
|
||||
os.makedirs(datadir)
|
||||
rpc_u, rpc_p = rpc_auth_pair(n)
|
||||
with open(os.path.join(datadir, "navcoin.conf"), 'w') as f:
|
||||
f.write("devnet=1\n")
|
||||
f.write("rpcuser=" + rpc_u + "\n")
|
||||
f.write("rpcpassword=" + rpc_p + "\n")
|
||||
f.write("port="+str(p2p_port(n))+"\n")
|
||||
f.write("rpcport="+str(rpc_port(n))+"\n")
|
||||
f.write("listenonion=0\n")
|
||||
f.write("dandelion=0\n")
|
||||
f.write("ntpminmeasures=-1\n")
|
||||
f.write("torserver=0\n")
|
||||
f.write("suppressblsctwarning=1\n")
|
||||
return datadir
|
||||
|
||||
def rpc_auth_pair(n):
|
||||
return 'rpcuser💻' + str(n), 'rpcpass🔑' + str(n)
|
||||
|
||||
def rpc_url(i, rpchost=None):
|
||||
rpc_u, rpc_p = rpc_auth_pair(i)
|
||||
return "http://%s:%s@%s:%d" % (rpc_u, rpc_p, rpchost or '127.0.0.1', rpc_port(i))
|
||||
|
||||
def wait_for_navcoind_start(process, url, i):
|
||||
'''
|
||||
Wait for navcoind to start. This means that RPC is accessible and fully initialized.
|
||||
Raise an exception if navcoind exits during initialization.
|
||||
'''
|
||||
polls_interval = 1.0 / 4
|
||||
runtime = 60
|
||||
while runtime > 0:
|
||||
if process.poll() is not None:
|
||||
raise Exception('navcoind exited with status %i during initialization' % process.returncode)
|
||||
try:
|
||||
# print('Checking RPC')
|
||||
rpc = get_rpc_proxy(url, i)
|
||||
blocks = rpc.getblockcount()
|
||||
# print('RPC replied with blocks: %i' % blocks)
|
||||
return # break out of loop on success
|
||||
except IOError as e:
|
||||
if e.errno != errno.ECONNREFUSED: # Port not yet open?
|
||||
raise # unknown IO error
|
||||
# else:
|
||||
# print('Waiting for port')
|
||||
except JSONRPCException as e: # Initialization phase
|
||||
if e.error['code'] != -28: # RPC in warmup?
|
||||
raise # unkown JSON RPC exception
|
||||
# else:
|
||||
# print('RPC in warmup')
|
||||
time.sleep(polls_interval)
|
||||
runtime -= polls_interval
|
||||
raise Exception('navcoind RPC timeout')
|
||||
|
||||
def initialize_chain(test_dir, num_nodes):
|
||||
"""
|
||||
Create a cache of a 200-block-long chain (with wallet) for MAX_NODES
|
||||
Afterward, create num_nodes copies from the cache
|
||||
"""
|
||||
|
||||
assert num_nodes <= MAX_NODES
|
||||
create_cache = False
|
||||
for i in range(MAX_NODES):
|
||||
if not os.path.isdir(os.path.join('cache', 'node'+str(i))):
|
||||
create_cache = True
|
||||
break
|
||||
|
||||
if create_cache:
|
||||
|
||||
#find and delete old cache directories if any exist
|
||||
for i in range(MAX_NODES):
|
||||
if os.path.isdir(os.path.join("cache","node"+str(i))):
|
||||
shutil.rmtree(os.path.join("cache","node"+str(i)))
|
||||
|
||||
# Create cache directories, run navcoinds:
|
||||
for i in range(MAX_NODES):
|
||||
datadir=initialize_datadir("cache", i)
|
||||
args = [ os.getenv("NAVCOIND", "navcoind"), "-server", "-keypool=1", "-datadir="+datadir, "-discover=0" ]
|
||||
if i > 0:
|
||||
args.append("-connect=127.0.0.1:"+str(p2p_port(0)))
|
||||
navcoind_processes[i] = subprocess.Popen(args)
|
||||
if os.getenv("PYTHON_DEBUG", ""):
|
||||
print("initialize_chain: navcoind started, waiting for RPC to come up")
|
||||
wait_for_navcoind_start(navcoind_processes[i], rpc_url(i), i)
|
||||
if os.getenv("PYTHON_DEBUG", ""):
|
||||
print("initialize_chain: RPC succesfully started")
|
||||
|
||||
rpcs = []
|
||||
for i in range(MAX_NODES):
|
||||
try:
|
||||
rpcs.append(get_rpc_proxy(rpc_url(i), i))
|
||||
except:
|
||||
sys.stderr.write("Error connecting to "+url+"\n")
|
||||
sys.exit(1)
|
||||
|
||||
# Create a 200-block-long chain; each of the 4 first nodes
|
||||
# gets 25 mature blocks and 25 immature.
|
||||
# Note: To preserve compatibility with older versions of
|
||||
# initialize_chain, only 4 nodes will generate coins.
|
||||
#
|
||||
# blocks are created with timestamps 10 minutes apart
|
||||
# starting from 2010 minutes in the past
|
||||
enable_mocktime()
|
||||
block_time = get_mocktime() - (201 * 10 * 60)
|
||||
for i in range(2):
|
||||
for peer in range(4):
|
||||
for j in range(25):
|
||||
set_node_times(rpcs, block_time)
|
||||
slow_gen(rpcs[peer], 1)
|
||||
block_time += 10*60
|
||||
# Must sync before next peer starts generating blocks
|
||||
sync_blocks(rpcs)
|
||||
|
||||
# Shut them down, and clean up cache directories:
|
||||
stop_nodes(rpcs)
|
||||
wait_navcoinds()
|
||||
disable_mocktime()
|
||||
for i in range(MAX_NODES):
|
||||
os.remove(log_filename("cache", i, "debug.log"))
|
||||
os.remove(log_filename("cache", i, "db.log"))
|
||||
os.remove(log_filename("cache", i, "peers.dat"))
|
||||
os.remove(log_filename("cache", i, "fee_estimates.dat"))
|
||||
|
||||
for i in range(num_nodes):
|
||||
from_dir = os.path.join("cache", "node"+str(i))
|
||||
to_dir = os.path.join(test_dir, "node"+str(i))
|
||||
shutil.copytree(from_dir, to_dir)
|
||||
initialize_datadir(test_dir, i) # Overwrite port/rpcport in navcoin.conf
|
||||
|
||||
def initialize_chain_clean(test_dir, num_nodes):
|
||||
"""
|
||||
Create an empty blockchain and num_nodes wallets.
|
||||
Useful if a test case wants complete control over initialization.
|
||||
"""
|
||||
for i in range(num_nodes):
|
||||
datadir=initialize_datadir(test_dir, i)
|
||||
|
||||
|
||||
def _rpchost_to_args(rpchost):
|
||||
'''Convert optional IP:port spec to rpcconnect/rpcport args'''
|
||||
if rpchost is None:
|
||||
return []
|
||||
|
||||
match = re.match('(\[[0-9a-fA-f:]+\]|[^:]+)(?::([0-9]+))?$', rpchost)
|
||||
if not match:
|
||||
raise ValueError('Invalid RPC host spec ' + rpchost)
|
||||
|
||||
rpcconnect = match.group(1)
|
||||
rpcport = match.group(2)
|
||||
|
||||
if rpcconnect.startswith('['): # remove IPv6 [...] wrapping
|
||||
rpcconnect = rpcconnect[1:-1]
|
||||
|
||||
rv = ['-rpcconnect=' + rpcconnect]
|
||||
if rpcport:
|
||||
rv += ['-rpcport=' + rpcport]
|
||||
return rv
|
||||
|
||||
def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=None):
|
||||
"""
|
||||
Start a navcoind and return RPC connection to it
|
||||
"""
|
||||
datadir = os.path.join(dirname, "node"+str(i))
|
||||
if binary is None:
|
||||
binary = os.getenv("NAVCOIND", "navcoind")
|
||||
args = [ binary, "-datadir="+datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-mocktime="+str(get_mocktime()) ]
|
||||
if extra_args is not None: args.extend(extra_args)
|
||||
navcoind_processes[i] = subprocess.Popen(args)
|
||||
if os.getenv("PYTHON_DEBUG", ""):
|
||||
print("start_node: navcoind started, waiting for RPC to come up")
|
||||
url = rpc_url(i, rpchost)
|
||||
wait_for_navcoind_start(navcoind_processes[i], url, i)
|
||||
if os.getenv("PYTHON_DEBUG", ""):
|
||||
print("start_node: RPC succesfully started")
|
||||
proxy = get_rpc_proxy(url, i, timeout=timewait)
|
||||
|
||||
if COVERAGE_DIR:
|
||||
coverage.write_all_rpc_commands(COVERAGE_DIR, proxy)
|
||||
|
||||
return proxy
|
||||
|
||||
def start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, binary=None):
|
||||
"""
|
||||
Start multiple navcoinds, return RPC connections to them
|
||||
"""
|
||||
if extra_args is None: extra_args = [ None for _ in range(num_nodes) ]
|
||||
if binary is None: binary = [ None for _ in range(num_nodes) ]
|
||||
rpcs = []
|
||||
try:
|
||||
for i in range(num_nodes):
|
||||
rpcs.append(start_node(i, dirname, extra_args[i], rpchost, binary=binary[i]))
|
||||
except: # If one node failed to start, stop the others
|
||||
stop_nodes(rpcs)
|
||||
raise
|
||||
return rpcs
|
||||
|
||||
def log_filename(dirname, n_node, logname):
|
||||
return os.path.join(dirname, "node"+str(n_node), "devnet", logname)
|
||||
|
||||
def stop_node(node, i):
|
||||
try:
|
||||
node.stop()
|
||||
except http.client.CannotSendRequest as e:
|
||||
print("WARN: Unable to stop node: " + repr(e))
|
||||
navcoind_processes[i].wait(timeout=NAVCOIND_PROC_WAIT_TIMEOUT)
|
||||
del navcoind_processes[i]
|
||||
|
||||
def stop_nodes(nodes):
|
||||
for node in nodes:
|
||||
try:
|
||||
node.stop()
|
||||
except http.client.CannotSendRequest as e:
|
||||
print("WARN: Unable to stop node: " + repr(e))
|
||||
del nodes[:] # Emptying array closes connections as a side effect
|
||||
|
||||
def set_node_times(nodes, t):
|
||||
for node in nodes:
|
||||
node.setmocktime(t)
|
||||
|
||||
def wait_navcoinds():
|
||||
# Wait for all navcoinds to cleanly exit
|
||||
for navcoind in navcoind_processes.values():
|
||||
navcoind.wait(timeout=NAVCOIND_PROC_WAIT_TIMEOUT)
|
||||
navcoind_processes.clear()
|
||||
|
||||
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
|
||||
while any(peer['version'] == 0 for peer in from_connection.getpeerinfo()):
|
||||
time.sleep(0.1)
|
||||
|
||||
def connect_nodes_bi(nodes, a, b):
|
||||
connect_nodes(nodes[a], b)
|
||||
connect_nodes(nodes[b], a)
|
||||
|
||||
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 send_zeropri_transaction(from_node, to_node, amount, fee):
|
||||
"""
|
||||
Create&broadcast a zero-priority transaction.
|
||||
Returns (txid, hex-encoded-txdata)
|
||||
Ensures transaction is zero-priority by first creating a send-to-self,
|
||||
then using its output
|
||||
"""
|
||||
|
||||
# Create a send-to-self with confirmed inputs:
|
||||
self_address = from_node.getnewaddress()
|
||||
(total_in, inputs) = gather_inputs(from_node, amount+fee*2)
|
||||
outputs = make_change(from_node, total_in, amount+fee, fee)
|
||||
outputs[self_address] = float(amount+fee)
|
||||
|
||||
self_rawtx = from_node.createrawtransaction(inputs, outputs)
|
||||
self_signresult = from_node.signrawtransaction(self_rawtx)
|
||||
self_txid = from_node.sendrawtransaction(self_signresult["hex"], True)
|
||||
|
||||
vout = find_output(from_node, self_txid, amount+fee)
|
||||
# Now immediately spend the output to create a 1-input, 1-output
|
||||
# zero-priority transaction:
|
||||
inputs = [ { "txid" : self_txid, "vout" : vout } ]
|
||||
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"])
|
||||
|
||||
def random_zeropri_transaction(nodes, amount, min_fee, fee_increment, fee_variants):
|
||||
"""
|
||||
Create a random zero-priority 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)
|
||||
(txid, txhex) = send_zeropri_transaction(from_node, to_node, amount, fee)
|
||||
return (txid, txhex, fee)
|
||||
|
||||
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)
|
||||
|
||||
def assert_fee_amount(fee, tx_size, fee_per_kB):
|
||||
"""Assert the fee was in range"""
|
||||
target_fee = tx_size * fee_per_kB / 1000
|
||||
if fee < target_fee:
|
||||
raise AssertionError("Fee of %s NAV too low! (Should be %s NAV)"%(str(fee), str(target_fee)))
|
||||
# allow the wallet's estimation to be at most 2 bytes off
|
||||
if fee > (tx_size + 2) * fee_per_kB / 1000:
|
||||
raise AssertionError("Fee of %s NAV too high! (Should be %s NAV)"%(str(fee), str(target_fee)))
|
||||
|
||||
def assert_equal(thing1, thing2):
|
||||
if thing1 != thing2:
|
||||
raise AssertionError("%s != %s"%(str(thing1),str(thing2)))
|
||||
|
||||
def assert_greater_than(thing1, thing2):
|
||||
if thing1 <= thing2:
|
||||
raise AssertionError("%s <= %s"%(str(thing1),str(thing2)))
|
||||
|
||||
def assert_raises(exc, fun, *args, **kwds):
|
||||
try:
|
||||
fun(*args, **kwds)
|
||||
except exc:
|
||||
pass
|
||||
except Exception as e:
|
||||
raise AssertionError("Unexpected exception raised: "+type(e).__name__)
|
||||
else:
|
||||
raise AssertionError("No exception raised")
|
||||
|
||||
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 == True:
|
||||
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 == True:
|
||||
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 should_not_find != True:
|
||||
raise AssertionError("No objects matched %s"%(str(to_match)))
|
||||
if num_matched > 0 and should_not_find == True:
|
||||
raise AssertionError("Objects were found %s"%(str(to_match)))
|
||||
|
||||
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 not found:" + e.error['message'])
|
||||
return True
|
||||
except Exception as e:
|
||||
raise AssertionError("Unexpected exception raised: " + type(e).__name__)
|
||||
else:
|
||||
return False
|
||||
|
||||
def satoshi_round(amount):
|
||||
return Decimal(amount).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN)
|
||||
|
||||
# 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):
|
||||
node.generate(int(0.5*count)+101)
|
||||
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] = satoshi_round(send_value/2)
|
||||
outputs[addr2] = satoshi_round(send_value/2)
|
||||
raw_tx = node.createrawtransaction(inputs, outputs)
|
||||
signed_tx = node.signrawtransaction(raw_tx)["hex"]
|
||||
txid = 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, fee):
|
||||
addr = node.getnewaddress()
|
||||
txids = []
|
||||
for i in range(len(utxos)):
|
||||
t = utxos.pop()
|
||||
inputs = []
|
||||
inputs.append({ "txid" : t["txid"], "vout" : t["vout"]})
|
||||
outputs = {}
|
||||
send_value = t['amount'] - fee
|
||||
outputs[addr] = satoshi_round(send_value)
|
||||
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 get_bip9_status(node, key):
|
||||
info = node.getblockchaininfo()
|
||||
return info['bip9_softforks'][key]
|
||||
|
||||
|
||||
def slow_gen(node, count, sleep = 0.1):
|
||||
total = count
|
||||
blocks = []
|
||||
while total > 0:
|
||||
now = min(total, 10)
|
||||
blocks.extend(node.generate(now))
|
||||
total -= now
|
||||
time.sleep(sleep)
|
||||
return blocks
|
||||
@@ -51,9 +51,6 @@ MSG_TYPE_MASK = 0xffffffff >> 2
|
||||
def sha256(s):
|
||||
return hashlib.new('sha256', s).digest()
|
||||
|
||||
def ripemd160(s):
|
||||
return hashlib.new('ripemd160', s).digest()
|
||||
|
||||
def hash256(s):
|
||||
return sha256(sha256(s))
|
||||
|
||||
@@ -184,11 +181,18 @@ def ser_string_vector(l):
|
||||
return r
|
||||
|
||||
|
||||
# Deserialize from bytes
|
||||
def FromBytes(obj, tx_bytes):
|
||||
obj.deserialize(BytesIO(tx_bytes))
|
||||
return obj
|
||||
|
||||
|
||||
# Deserialize from a hex string representation (eg from RPC)
|
||||
def FromHex(obj, hex_string):
|
||||
obj.deserialize(BytesIO(hex_str_to_bytes(hex_string)))
|
||||
return obj
|
||||
|
||||
|
||||
# Convert a binary-serializable object to hex (eg for submission via RPC)
|
||||
def ToHex(obj):
|
||||
return bytes_to_hex_str(obj.serialize())
|
||||
|
||||
@@ -7,7 +7,12 @@
|
||||
|
||||
from .btc import BTCInterface
|
||||
from basicswap.chainparams import Coins
|
||||
from basicswap.util.address import decodeAddress
|
||||
from mnemonic import Mnemonic
|
||||
from basicswap.contrib.test_framework.script import (
|
||||
CScript,
|
||||
OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG
|
||||
)
|
||||
|
||||
|
||||
class DASHInterface(BTCInterface):
|
||||
@@ -15,20 +20,75 @@ class DASHInterface(BTCInterface):
|
||||
def coin_type():
|
||||
return Coins.DASH
|
||||
|
||||
def initialiseWallet(self, key):
|
||||
words = Mnemonic('english').to_mnemonic(key)
|
||||
self.rpc_callback('upgradetohd', [words, ])
|
||||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
super().__init__(coin_settings, network, swap_client)
|
||||
self._wallet_passphrase = ''
|
||||
self._have_checked_seed = False
|
||||
|
||||
def checkExpectedSeed(self, key_hash):
|
||||
def seedToMnemonic(self, key: bytes) -> str:
|
||||
return Mnemonic('english').to_mnemonic(key)
|
||||
|
||||
def initialiseWallet(self, key: bytes):
|
||||
words = self.seedToMnemonic(key)
|
||||
|
||||
mnemonic_passphrase = ''
|
||||
self.rpc_wallet('upgradetohd', [words, mnemonic_passphrase, self._wallet_passphrase])
|
||||
self._have_checked_seed = False
|
||||
if self._wallet_passphrase != '':
|
||||
self.unlockWallet(self._wallet_passphrase)
|
||||
|
||||
def decodeAddress(self, address: str) -> bytes:
|
||||
return decodeAddress(address)[1:]
|
||||
|
||||
def checkExpectedSeed(self, key_hash: str):
|
||||
try:
|
||||
rv = self.rpc_callback('dumphdinfo')
|
||||
rv = self.rpc_wallet('dumphdinfo')
|
||||
entropy = Mnemonic('english').to_entropy(rv['mnemonic'].split(' '))
|
||||
entropy_hash = self.getAddressHashFromKey(entropy)[::-1].hex()
|
||||
self._have_checked_seed = True
|
||||
return entropy_hash == key_hash
|
||||
except Exception as e:
|
||||
self._log.warning('checkExpectedSeed failed: {}'.format(str(e)))
|
||||
return False
|
||||
|
||||
def withdrawCoin(self, value, addr_to, subfee):
|
||||
params = [addr_to, value, '', '', subfee]
|
||||
return self.rpc_callback('sendtoaddress', params)
|
||||
params = [addr_to, value, '', '', subfee, False, False, self._conf_target]
|
||||
return self.rpc_wallet('sendtoaddress', params)
|
||||
|
||||
def getSpendableBalance(self) -> int:
|
||||
return self.make_int(self.rpc_wallet('getwalletinfo')['balance'])
|
||||
|
||||
def getScriptForPubkeyHash(self, pkh: bytes) -> bytearray:
|
||||
# Return P2PKH
|
||||
return CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG])
|
||||
|
||||
def getBLockSpendTxFee(self, tx, fee_rate: int) -> int:
|
||||
add_bytes = 107
|
||||
size = len(tx.serialize_with_witness()) + add_bytes
|
||||
pay_fee = round(fee_rate * size / 1000)
|
||||
self._log.info(f'BLockSpendTx fee_rate, size, fee: {fee_rate}, {size}, {pay_fee}.')
|
||||
return pay_fee
|
||||
|
||||
def findTxnByHash(self, txid_hex: str):
|
||||
# Only works for wallet txns
|
||||
try:
|
||||
rv = self.rpc_wallet('gettransaction', [txid_hex])
|
||||
except Exception as ex:
|
||||
self._log.debug('findTxnByHash getrawtransaction failed: {}'.format(txid_hex))
|
||||
return None
|
||||
if 'confirmations' in rv and rv['confirmations'] >= self.blocks_confirmed:
|
||||
block_height = self.getBlockHeader(rv['blockhash'])['height']
|
||||
return {'txid': txid_hex, 'amount': 0, 'height': block_height}
|
||||
|
||||
return None
|
||||
|
||||
def unlockWallet(self, password: str):
|
||||
super().unlockWallet(password)
|
||||
# Store password for initialiseWallet
|
||||
self._wallet_passphrase = password
|
||||
if not self._have_checked_seed:
|
||||
self._sc.checkWalletSeed(self.coin_type())
|
||||
|
||||
def lockWallet(self):
|
||||
super().lockWallet()
|
||||
self._wallet_passphrase = ''
|
||||
|
||||
@@ -1,28 +1,33 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2022 tecnovert
|
||||
# Copyright (c) 2022-2023 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
import random
|
||||
import hashlib
|
||||
from .btc import BTCInterface, find_vout_for_address_from_txobj
|
||||
from basicswap.chainparams import Coins
|
||||
|
||||
from basicswap.util.address import (
|
||||
decodeAddress,
|
||||
from .btc import BTCInterface, find_vout_for_address_from_txobj
|
||||
from basicswap.util import (
|
||||
i2b,
|
||||
ensure,
|
||||
)
|
||||
from basicswap.contrib.test_framework.script import (
|
||||
from basicswap.rpc import make_rpc_func
|
||||
from basicswap.util.crypto import hash160
|
||||
from basicswap.util.address import decodeAddress
|
||||
from basicswap.chainparams import Coins
|
||||
from basicswap.interface.contrib.firo_test_framework.script import (
|
||||
CScript,
|
||||
OP_0,
|
||||
OP_DUP,
|
||||
OP_EQUAL,
|
||||
OP_HASH160,
|
||||
OP_CHECKSIG,
|
||||
OP_EQUALVERIFY,
|
||||
hash160,
|
||||
)
|
||||
from basicswap.contrib.test_framework.messages import (
|
||||
from basicswap.interface.contrib.firo_test_framework.mininode import (
|
||||
CBlock,
|
||||
FromHex,
|
||||
CTransaction,
|
||||
)
|
||||
|
||||
@@ -32,14 +37,25 @@ class FIROInterface(BTCInterface):
|
||||
def coin_type():
|
||||
return Coins.FIRO
|
||||
|
||||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
super(FIROInterface, self).__init__(coin_settings, network, swap_client)
|
||||
# No multiwallet support
|
||||
self.rpc_wallet = make_rpc_func(self._rpcport, self._rpcauth, host=self._rpc_host)
|
||||
|
||||
def checkWallets(self) -> int:
|
||||
return 1
|
||||
|
||||
def getExchangeName(self, exchange_name):
|
||||
return 'zcoin'
|
||||
|
||||
def initialiseWallet(self, key):
|
||||
# load with -hdseed= parameter
|
||||
pass
|
||||
|
||||
def getNewAddress(self, use_segwit, label='swap_receive'):
|
||||
return self.rpc_callback('getnewaddress', [label])
|
||||
# addr_plain = self.rpc_callback('getnewaddress', [label])
|
||||
# return self.rpc_callback('addwitnessaddress', [addr_plain])
|
||||
return self.rpc('getnewaddress', [label])
|
||||
# addr_plain = self.rpc('getnewaddress', [label])
|
||||
# return self.rpc('addwitnessaddress', [addr_plain])
|
||||
|
||||
def decodeAddress(self, address):
|
||||
return decodeAddress(address)[1:]
|
||||
@@ -51,33 +67,38 @@ class FIROInterface(BTCInterface):
|
||||
raise ValueError('TODO')
|
||||
|
||||
def isWatchOnlyAddress(self, address):
|
||||
addr_info = self.rpc_callback('validateaddress', [address])
|
||||
addr_info = self.rpc('validateaddress', [address])
|
||||
return addr_info['iswatchonly']
|
||||
|
||||
def isAddressMine(self, address: str, or_watch_only: bool = False) -> bool:
|
||||
addr_info = self.rpc('validateaddress', [address])
|
||||
if not or_watch_only:
|
||||
return addr_info['ismine']
|
||||
return addr_info['ismine'] or addr_info['iswatchonly']
|
||||
|
||||
def getSCLockScriptAddress(self, lock_script):
|
||||
lock_tx_dest = self.getScriptDest(lock_script)
|
||||
address = self.encodeScriptDest(lock_tx_dest)
|
||||
|
||||
if not self.isWatchOnlyAddress(address):
|
||||
if not self.isAddressMine(address, or_watch_only=True):
|
||||
# Expects P2WSH nested in BIP16_P2SH
|
||||
ro = self.rpc_callback('importaddress', [lock_tx_dest.hex(), 'bid lock', False, True])
|
||||
addr_info = self.rpc_callback('validateaddress', [address])
|
||||
ro = self.rpc('importaddress', [lock_tx_dest.hex(), 'bid lock', False, True])
|
||||
addr_info = self.rpc('validateaddress', [address])
|
||||
|
||||
return address
|
||||
|
||||
def getLockTxHeightFiro(self, txid, lock_script, bid_amount, rescan_from, find_index=False):
|
||||
def getLockTxHeight(self, txid, dest_address, bid_amount, rescan_from, find_index: bool = False):
|
||||
# Add watchonly address and rescan if required
|
||||
lock_tx_dest = self.getScriptDest(lock_script)
|
||||
dest_address = self.encodeScriptDest(lock_tx_dest)
|
||||
if not self.isWatchOnlyAddress(dest_address):
|
||||
self.rpc_callback('importaddress', [lock_tx_dest.hex(), 'bid lock', False, True])
|
||||
|
||||
if not self.isAddressMine(dest_address, or_watch_only=True):
|
||||
self.importWatchOnlyAddress(dest_address, 'bid')
|
||||
self._log.info('Imported watch-only addr: {}'.format(dest_address))
|
||||
self._log.info('Rescanning {} chain from height: {}'.format(self.coin_name(), rescan_from))
|
||||
self.rpc_callback('rescanblockchain', [rescan_from])
|
||||
self.rescanBlockchainForAddress(rescan_from, dest_address)
|
||||
|
||||
return_txid = True if txid is None else False
|
||||
if txid is None:
|
||||
txns = self.rpc_callback('listunspent', [0, 9999999, [dest_address, ]])
|
||||
txns = self.rpc('listunspent', [0, 9999999, [dest_address, ]])
|
||||
|
||||
for tx in txns:
|
||||
if self.make_int(tx['amount']) == bid_amount:
|
||||
@@ -88,11 +109,11 @@ class FIROInterface(BTCInterface):
|
||||
return None
|
||||
|
||||
try:
|
||||
tx = self.rpc_callback('gettransaction', [txid.hex()])
|
||||
tx = self.rpc('gettransaction', [txid.hex()])
|
||||
|
||||
block_height = 0
|
||||
if 'blockhash' in tx:
|
||||
block_header = self.rpc_callback('getblockheader', [tx['blockhash']])
|
||||
block_header = self.rpc('getblockheader', [tx['blockhash']])
|
||||
block_height = block_header['height']
|
||||
|
||||
rv = {
|
||||
@@ -104,7 +125,7 @@ class FIROInterface(BTCInterface):
|
||||
return None
|
||||
|
||||
if find_index:
|
||||
tx_obj = self.rpc_callback('decoderawtransaction', [tx['hex']])
|
||||
tx_obj = self.rpc('decoderawtransaction', [tx['hex']])
|
||||
rv['index'] = find_vout_for_address_from_txobj(tx_obj, dest_address)
|
||||
|
||||
if return_txid:
|
||||
@@ -112,64 +133,235 @@ class FIROInterface(BTCInterface):
|
||||
|
||||
return rv
|
||||
|
||||
def createSCLockTx(self, value, Kal, Kaf, vkbv=None):
|
||||
script = self.genScriptLockTxScript(Kal, Kaf)
|
||||
def createSCLockTx(self, value: int, script: bytearray, vkbv: bytes = None) -> bytes:
|
||||
tx = CTransaction()
|
||||
tx.nVersion = self.txVersion()
|
||||
tx.vout.append(self.txoType()(value, self.getScriptDest(script)))
|
||||
|
||||
return tx.serialize(), script
|
||||
return tx.serialize()
|
||||
|
||||
def fundSCLockTx(self, tx_bytes, feerate, vkbv=None):
|
||||
return self.fundTx(tx_bytes, feerate)
|
||||
|
||||
def signTxWithWallet(self, tx):
|
||||
rv = self.rpc_callback('signrawtransaction', [tx.hex()])
|
||||
rv = self.rpc('signrawtransaction', [tx.hex()])
|
||||
return bytes.fromhex(rv['hex'])
|
||||
|
||||
def createRawSignedTransaction(self, addr_to, amount):
|
||||
txn = self.rpc_callback('createrawtransaction', [[], {addr_to: self.format_amount(amount)}])
|
||||
|
||||
def createRawFundedTransaction(self, addr_to: str, amount: int, sub_fee: bool = False, lock_unspents: bool = True) -> str:
|
||||
txn = self.rpc('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,
|
||||
'lockUnspents': lock_unspents,
|
||||
'feeRate': fee_rate,
|
||||
}
|
||||
txn_funded = self.rpc_callback('fundrawtransaction', [txn, options])['hex']
|
||||
txn_signed = self.rpc_callback('signrawtransaction', [txn_funded])['hex']
|
||||
return txn_signed
|
||||
if sub_fee:
|
||||
options['subtractFeeFromOutputs'] = [0,]
|
||||
return self.rpc('fundrawtransaction', [txn, options])['hex']
|
||||
|
||||
def getScriptForPubkeyHash(self, pkh):
|
||||
# Return P2WPKH nested in BIP16 P2SH
|
||||
def createRawSignedTransaction(self, addr_to, amount) -> str:
|
||||
txn_funded = self.createRawFundedTransaction(addr_to, amount)
|
||||
return self.rpc('signrawtransaction', [txn_funded])['hex']
|
||||
|
||||
def getScriptForPubkeyHash(self, pkh: bytes) -> bytearray:
|
||||
# Return P2PKH
|
||||
return CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG])
|
||||
|
||||
def getScriptDest(self, script):
|
||||
# P2WSH nested in BIP16_P2SH
|
||||
def getScriptDest(self, script: bytearray) -> bytearray:
|
||||
# P2SH
|
||||
|
||||
script_hash = hashlib.sha256(script).digest()
|
||||
assert len(script_hash) == 32
|
||||
script_hash_hash = hash160(script_hash)
|
||||
assert len(script_hash_hash) == 20
|
||||
script_hash = hash160(script)
|
||||
assert len(script_hash) == 20
|
||||
|
||||
return CScript([OP_HASH160, script_hash_hash, OP_EQUAL])
|
||||
return CScript([OP_HASH160, script_hash, OP_EQUAL])
|
||||
|
||||
def getSeedHash(self, seed):
|
||||
def getSeedHash(self, seed: bytes) -> bytes:
|
||||
return hash160(seed)[::-1]
|
||||
|
||||
def encodeScriptDest(self, script):
|
||||
def encodeScriptDest(self, script_dest: bytes) -> str:
|
||||
# Extract hash from script
|
||||
script_hash = script[2:-1]
|
||||
script_hash = script_dest[2:-1]
|
||||
return self.sh_to_address(script_hash)
|
||||
|
||||
def getScriptScriptSig(self, script):
|
||||
return CScript([OP_0, hashlib.sha256(script).digest()])
|
||||
def getDestForScriptHash(self, script_hash):
|
||||
assert len(script_hash) == 20
|
||||
return CScript([OP_HASH160, script_hash, OP_EQUAL])
|
||||
|
||||
def withdrawCoin(self, value, addr_to, subfee):
|
||||
params = [addr_to, value, '', '', subfee]
|
||||
return self.rpc_callback('sendtoaddress', params)
|
||||
return self.rpc('sendtoaddress', params)
|
||||
|
||||
def getWalletSeedID(self):
|
||||
return self.rpc_callback('getwalletinfo')['hdmasterkeyid']
|
||||
return self.rpc('getwalletinfo')['hdmasterkeyid']
|
||||
|
||||
def getSpendableBalance(self) -> int:
|
||||
return self.make_int(self.rpc('getwalletinfo')['balance'])
|
||||
|
||||
def getBLockSpendTxFee(self, tx, fee_rate: int) -> int:
|
||||
add_bytes = 107
|
||||
size = len(tx.serialize_with_witness()) + add_bytes
|
||||
pay_fee = round(fee_rate * size / 1000)
|
||||
self._log.info(f'BLockSpendTx fee_rate, size, fee: {fee_rate}, {size}, {pay_fee}.')
|
||||
return pay_fee
|
||||
|
||||
def signTxWithKey(self, tx: bytes, key: bytes) -> bytes:
|
||||
key_wif = self.encodeKey(key)
|
||||
rv = self.rpc('signrawtransaction', [tx.hex(), [], [key_wif, ]])
|
||||
return bytes.fromhex(rv['hex'])
|
||||
|
||||
def findTxnByHash(self, txid_hex: str):
|
||||
# Only works for wallet txns
|
||||
try:
|
||||
rv = self.rpc('gettransaction', [txid_hex])
|
||||
except Exception as ex:
|
||||
self._log.debug('findTxnByHash getrawtransaction failed: {}'.format(txid_hex))
|
||||
return None
|
||||
if 'confirmations' in rv and rv['confirmations'] >= self.blocks_confirmed:
|
||||
block_height = self.getBlockHeader(rv['blockhash'])['height']
|
||||
return {'txid': txid_hex, 'amount': 0, 'height': block_height}
|
||||
return None
|
||||
|
||||
def getProofOfFunds(self, amount_for, extra_commit_bytes):
|
||||
# TODO: Lock unspent and use same output/s to fund bid
|
||||
|
||||
unspents_by_addr = dict()
|
||||
unspents = self.rpc('listunspent')
|
||||
for u in unspents:
|
||||
if u['spendable'] is not True:
|
||||
continue
|
||||
if u['address'] not in unspents_by_addr:
|
||||
unspents_by_addr[u['address']] = {'total': 0, 'utxos': []}
|
||||
utxo_amount: int = self.make_int(u['amount'], r=1)
|
||||
unspents_by_addr[u['address']]['total'] += utxo_amount
|
||||
unspents_by_addr[u['address']]['utxos'].append((utxo_amount, u['txid'], u['vout']))
|
||||
|
||||
max_utxos: int = 4
|
||||
|
||||
viable_addrs = []
|
||||
for addr, data in unspents_by_addr.items():
|
||||
if data['total'] >= amount_for:
|
||||
# Sort from largest to smallest amount
|
||||
sorted_utxos = sorted(data['utxos'], key=lambda x: x[0])
|
||||
|
||||
# Max outputs required to reach amount_for
|
||||
utxos_req: int = 0
|
||||
sum_value: int = 0
|
||||
for utxo in sorted_utxos:
|
||||
sum_value += utxo[0]
|
||||
utxos_req += 1
|
||||
if sum_value >= amount_for:
|
||||
break
|
||||
|
||||
if utxos_req <= max_utxos:
|
||||
viable_addrs.append(addr)
|
||||
continue
|
||||
|
||||
ensure(len(viable_addrs) > 0, 'Could not find address with enough funds for proof')
|
||||
|
||||
sign_for_addr: str = random.choice(viable_addrs)
|
||||
self._log.debug('sign_for_addr %s', sign_for_addr)
|
||||
|
||||
prove_utxos = []
|
||||
sorted_utxos = sorted(unspents_by_addr[sign_for_addr]['utxos'], key=lambda x: x[0])
|
||||
|
||||
hasher = hashlib.sha256()
|
||||
|
||||
sum_value: int = 0
|
||||
for utxo in sorted_utxos:
|
||||
sum_value += utxo[0]
|
||||
outpoint = (bytes.fromhex(utxo[1]), utxo[2])
|
||||
prove_utxos.append(outpoint)
|
||||
hasher.update(outpoint[0])
|
||||
hasher.update(outpoint[1].to_bytes(2, 'big'))
|
||||
if sum_value >= amount_for:
|
||||
break
|
||||
utxos_hash = hasher.digest()
|
||||
|
||||
self._log.debug('sign_for_addr %s', sign_for_addr)
|
||||
|
||||
if self.using_segwit(): # TODO: Use isSegwitAddress when scantxoutset can use combo
|
||||
# 'Address does not refer to key' for non p2pkh
|
||||
pkh = self.decodeAddress(sign_for_addr)
|
||||
sign_for_addr = self.pkh_to_address(pkh)
|
||||
self._log.debug('sign_for_addr converted %s', sign_for_addr)
|
||||
|
||||
signature = self.rpc('signmessage', [sign_for_addr, sign_for_addr + '_swap_proof_' + utxos_hash.hex() + extra_commit_bytes.hex()])
|
||||
|
||||
return (sign_for_addr, signature, prove_utxos)
|
||||
|
||||
def verifyProofOfFunds(self, address, signature, utxos, extra_commit_bytes):
|
||||
hasher = hashlib.sha256()
|
||||
sum_value: int = 0
|
||||
for outpoint in utxos:
|
||||
hasher.update(outpoint[0])
|
||||
hasher.update(outpoint[1].to_bytes(2, 'big'))
|
||||
utxos_hash = hasher.digest()
|
||||
|
||||
passed = self.verifyMessage(address, address + '_swap_proof_' + utxos_hash.hex() + extra_commit_bytes.hex(), signature)
|
||||
ensure(passed is True, 'Proof of funds signature invalid')
|
||||
|
||||
if self.using_segwit():
|
||||
address = self.encodeSegwitAddress(decodeAddress(address)[1:])
|
||||
|
||||
sum_value: int = 0
|
||||
for outpoint in utxos:
|
||||
txout = self.rpc('gettxout', [outpoint[0].hex(), outpoint[1]])
|
||||
sum_value += self.make_int(txout['value'])
|
||||
|
||||
return sum_value
|
||||
|
||||
def rescanBlockchainForAddress(self, height_start: int, addr_find: str):
|
||||
# Very ugly workaround for missing `rescanblockchain` rpc command
|
||||
|
||||
chain_blocks: int = self.getChainHeight()
|
||||
|
||||
current_height: int = chain_blocks
|
||||
block_hash = self.rpc('getblockhash', [current_height])
|
||||
|
||||
script_hash: bytes = self.decodeAddress(addr_find)
|
||||
find_scriptPubKey = self.getDestForScriptHash(script_hash)
|
||||
|
||||
while current_height > height_start:
|
||||
block_hash = self.rpc('getblockhash', [current_height])
|
||||
|
||||
block = self.rpc('getblock', [block_hash, False])
|
||||
decoded_block = CBlock()
|
||||
decoded_block = FromHex(decoded_block, block)
|
||||
for tx in decoded_block.vtx:
|
||||
for txo in tx.vout:
|
||||
if txo.scriptPubKey == find_scriptPubKey:
|
||||
tx.rehash()
|
||||
txid = i2b(tx.sha256)
|
||||
self._log.info('Found output to addr: {} in tx {} in block {}'.format(addr_find, txid.hex(), block_hash))
|
||||
self._log.info('rescanblockchain hack invalidateblock {}'.format(block_hash))
|
||||
self.rpc('invalidateblock', [block_hash])
|
||||
self.rpc('reconsiderblock', [block_hash])
|
||||
return
|
||||
current_height -= 1
|
||||
|
||||
def getBlockWithTxns(self, block_hash):
|
||||
# TODO: Bypass decoderawtransaction and getblockheader
|
||||
block = self.rpc('getblock', [block_hash, False])
|
||||
block_header = self.rpc('getblockheader', [block_hash])
|
||||
decoded_block = CBlock()
|
||||
decoded_block = FromHex(decoded_block, block)
|
||||
|
||||
tx_rv = []
|
||||
for tx in decoded_block.vtx:
|
||||
tx_hex = tx.serialize_with_witness().hex()
|
||||
tx_dec = self.rpc('decoderawtransaction', [tx_hex])
|
||||
if 'hex' not in tx_dec:
|
||||
tx_dec['hex'] = tx_hex
|
||||
|
||||
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,15 +1,122 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2020 tecnovert
|
||||
# Copyright (c) 2020-2023 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.rpc import make_rpc_func
|
||||
from basicswap.chainparams import Coins, chainparams
|
||||
|
||||
|
||||
class LTCInterface(BTCInterface):
|
||||
@staticmethod
|
||||
def coin_type():
|
||||
return Coins.LTC
|
||||
|
||||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
super(LTCInterface, self).__init__(coin_settings, network, swap_client)
|
||||
self._rpc_wallet_mweb = 'mweb'
|
||||
self.rpc_wallet_mweb = make_rpc_func(self._rpcport, self._rpcauth, host=self._rpc_host, wallet=self._rpc_wallet_mweb)
|
||||
|
||||
def getNewMwebAddress(self, use_segwit=False, label='swap_receive') -> str:
|
||||
return self.rpc_wallet_mweb('getnewaddress', [label, 'mweb'])
|
||||
|
||||
def getNewStealthAddress(self, label=''):
|
||||
return self.getNewMwebAddress(False, label)
|
||||
|
||||
def withdrawCoin(self, value, type_from: str, addr_to: str, subfee: bool) -> str:
|
||||
params = [addr_to, value, '', '', subfee, True, self._conf_target]
|
||||
if type_from == 'mweb':
|
||||
return self.rpc_wallet_mweb('sendtoaddress', params)
|
||||
return self.rpc_wallet('sendtoaddress', params)
|
||||
|
||||
def getWalletInfo(self):
|
||||
rv = super(LTCInterface, self).getWalletInfo()
|
||||
|
||||
mweb_info = self.rpc_wallet_mweb('getwalletinfo')
|
||||
rv['mweb_balance'] = mweb_info['balance']
|
||||
rv['mweb_unconfirmed'] = mweb_info['unconfirmed_balance']
|
||||
rv['mweb_immature'] = mweb_info['immature_balance']
|
||||
|
||||
# Add unconfirmed mweb -> plain txns to the unconfirmed_balance
|
||||
txns = self.rpc_wallet('listtransactions')
|
||||
for tx in reversed(txns):
|
||||
amount: float = tx.get('amount', 0.0)
|
||||
if tx['confirmations'] == 0 and tx.get('mweb_out', None) and amount > 0.0:
|
||||
rv['unconfirmed_balance'] += amount
|
||||
return rv
|
||||
|
||||
|
||||
class LTCInterfaceMWEB(LTCInterface):
|
||||
@staticmethod
|
||||
def coin_type():
|
||||
return Coins.LTC_MWEB
|
||||
|
||||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
super(LTCInterfaceMWEB, self).__init__(coin_settings, network, swap_client)
|
||||
self._rpc_wallet = 'mweb'
|
||||
self.rpc_wallet = make_rpc_func(self._rpcport, self._rpcauth, host=self._rpc_host, wallet=self._rpc_wallet)
|
||||
|
||||
def chainparams(self):
|
||||
return chainparams[Coins.LTC]
|
||||
|
||||
def chainparams_network(self):
|
||||
return chainparams[Coins.LTC][self._network]
|
||||
|
||||
def coin_name(self) -> str:
|
||||
coin_chainparams = chainparams[Coins.LTC]
|
||||
if coin_chainparams.get('use_ticker_as_name', False):
|
||||
return coin_chainparams['ticker'] + ' MWEB'
|
||||
return coin_chainparams['name'].capitalize() + ' MWEB'
|
||||
|
||||
def ticker(self) -> str:
|
||||
ticker = chainparams[Coins.LTC]['ticker']
|
||||
if self._network == 'testnet':
|
||||
ticker = 't' + ticker
|
||||
elif self._network == 'regtest':
|
||||
ticker = 'rt' + ticker
|
||||
return ticker + '_MWEB'
|
||||
|
||||
def getNewAddress(self, use_segwit=False, label='swap_receive') -> str:
|
||||
return self.getNewMwebAddress()
|
||||
|
||||
def has_mweb_wallet(self) -> bool:
|
||||
return 'mweb' in self.rpc('listwallets')
|
||||
|
||||
def init_wallet(self, password=None):
|
||||
# If system is encrypted mweb wallet will be created at first unlock
|
||||
|
||||
self._log.info('init_wallet - {}'.format(self.ticker()))
|
||||
|
||||
self._log.info('Creating mweb wallet for {}.'.format(self.coin_name()))
|
||||
# wallet_name, disable_private_keys, blank, passphrase, avoid_reuse, descriptors, load_on_startup
|
||||
self.rpc('createwallet', ['mweb', False, True, password, False, False, True])
|
||||
|
||||
if password is not None:
|
||||
# Max timeout value, ~3 years
|
||||
self.rpc_wallet('walletpassphrase', [password, 100000000])
|
||||
|
||||
if self.getWalletSeedID() == 'Not found':
|
||||
self._sc.initialiseWallet(self.coin_type())
|
||||
|
||||
# Workaround to trigger mweb_spk_man->LoadMWEBKeychain()
|
||||
self.rpc('unloadwallet', ['mweb'])
|
||||
self.rpc('loadwallet', ['mweb'])
|
||||
if password is not None:
|
||||
self.rpc_wallet('walletpassphrase', [password, 100000000])
|
||||
self.rpc_wallet('keypoolrefill')
|
||||
|
||||
def unlockWallet(self, password: str):
|
||||
if password == '':
|
||||
return
|
||||
self._log.info('unlockWallet - {}'.format(self.ticker()))
|
||||
|
||||
if not self.has_mweb_wallet():
|
||||
self.init_wallet(password)
|
||||
else:
|
||||
# Max timeout value, ~3 years
|
||||
self.rpc_wallet('walletpassphrase', [password, 100000000])
|
||||
|
||||
self._sc.checkWalletSeed(self.coin_type())
|
||||
|
||||
738
basicswap/interface/nav.py
Normal file
@@ -0,0 +1,738 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2023 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
import random
|
||||
import hashlib
|
||||
|
||||
from io import BytesIO
|
||||
from coincurve.keys import (
|
||||
PublicKey,
|
||||
PrivateKey,
|
||||
)
|
||||
from .btc import BTCInterface, find_vout_for_address_from_txobj, findOutput
|
||||
from basicswap.rpc import make_rpc_func
|
||||
from basicswap.chainparams import Coins
|
||||
from basicswap.interface.contrib.nav_test_framework.mininode import (
|
||||
CTxIn,
|
||||
CTxOut,
|
||||
CBlock,
|
||||
COutPoint,
|
||||
CTransaction,
|
||||
CTxInWitness,
|
||||
FromHex,
|
||||
uint256_from_str,
|
||||
)
|
||||
from basicswap.util.crypto import hash160
|
||||
from basicswap.util.address import (
|
||||
decodeWif,
|
||||
pubkeyToAddress,
|
||||
encodeAddress,
|
||||
)
|
||||
from basicswap.util import (
|
||||
i2b, i2h,
|
||||
ensure,
|
||||
)
|
||||
from basicswap.basicswap_util import (
|
||||
getVoutByScriptPubKey,
|
||||
)
|
||||
|
||||
from basicswap.interface.contrib.nav_test_framework.script import (
|
||||
CScript,
|
||||
OP_0,
|
||||
OP_EQUAL,
|
||||
OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG,
|
||||
SIGHASH_ALL,
|
||||
SegwitVersion1SignatureHash,
|
||||
)
|
||||
from mnemonic import Mnemonic
|
||||
|
||||
|
||||
class NAVInterface(BTCInterface):
|
||||
@staticmethod
|
||||
def coin_type():
|
||||
return Coins.NAV
|
||||
|
||||
@staticmethod
|
||||
def txVersion() -> int:
|
||||
return 3
|
||||
|
||||
@staticmethod
|
||||
def txoType():
|
||||
return CTxOut
|
||||
|
||||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
super(NAVInterface, self).__init__(coin_settings, network, swap_client)
|
||||
# No multiwallet support
|
||||
self.rpc_wallet = make_rpc_func(self._rpcport, self._rpcauth, host=self._rpc_host)
|
||||
|
||||
def checkWallets(self) -> int:
|
||||
return 1
|
||||
|
||||
def use_p2shp2wsh(self) -> bool:
|
||||
# p2sh-p2wsh
|
||||
return True
|
||||
|
||||
def seedToMnemonic(self, key):
|
||||
return Mnemonic('english').to_mnemonic(key)
|
||||
|
||||
def initialiseWallet(self, key):
|
||||
# load with -importmnemonic= parameter
|
||||
pass
|
||||
|
||||
def getWalletSeedID(self):
|
||||
return self.rpc('getwalletinfo')['hdmasterkeyid']
|
||||
|
||||
def withdrawCoin(self, value, addr_to: str, subfee: bool):
|
||||
strdzeel = ''
|
||||
params = [addr_to, value, '', '', strdzeel, subfee]
|
||||
return self.rpc('sendtoaddress', params)
|
||||
|
||||
def getSpendableBalance(self) -> int:
|
||||
return self.make_int(self.rpc('getwalletinfo')['balance'])
|
||||
|
||||
def signTxWithWallet(self, tx: bytes) -> bytes:
|
||||
rv = self.rpc('signrawtransaction', [tx.hex()])
|
||||
|
||||
return bytes.fromhex(rv['hex'])
|
||||
|
||||
def checkExpectedSeed(self, key_hash: str):
|
||||
try:
|
||||
rv = self.rpc('dumpmnemonic')
|
||||
entropy = Mnemonic('english').to_entropy(rv.split(' '))
|
||||
|
||||
entropy_hash = self.getAddressHashFromKey(entropy)[::-1].hex()
|
||||
self._have_checked_seed = True
|
||||
return entropy_hash == key_hash
|
||||
except Exception as e:
|
||||
self._log.warning('checkExpectedSeed failed: {}'.format(str(e)))
|
||||
return False
|
||||
|
||||
def getScriptForP2PKH(self, pkh: bytes) -> bytearray:
|
||||
# Return P2PKH
|
||||
return CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG])
|
||||
|
||||
def getScriptForPubkeyHash(self, pkh: bytes) -> bytearray:
|
||||
# Return P2SH-p2wpkh
|
||||
|
||||
script = CScript([OP_0, pkh])
|
||||
script_hash = hash160(script)
|
||||
assert len(script_hash) == 20
|
||||
|
||||
return CScript([OP_HASH160, script_hash, OP_EQUAL])
|
||||
|
||||
def getInputScriptForPubkeyHash(self, pkh: bytes) -> bytearray:
|
||||
script = CScript([OP_0, pkh])
|
||||
return bytes((len(script),)) + script
|
||||
|
||||
def encodeSegwitAddress(self, pkh: bytes) -> str:
|
||||
# P2SH-p2wpkh
|
||||
script = CScript([OP_0, pkh])
|
||||
script_hash = hash160(script)
|
||||
assert len(script_hash) == 20
|
||||
return encodeAddress(bytes((self.chainparams_network()['script_address'],)) + script_hash)
|
||||
|
||||
def encodeSegwitAddressScript(self, script: bytes) -> str:
|
||||
if len(script) == 23 and script[0] == OP_HASH160 and script[1] == 20 and script[22] == OP_EQUAL:
|
||||
script_hash = script[2:22]
|
||||
return encodeAddress(bytes((self.chainparams_network()['script_address'],)) + script_hash)
|
||||
raise ValueError('Unknown Script')
|
||||
|
||||
def loadTx(self, tx_bytes: bytes) -> CTransaction:
|
||||
# Load tx from bytes to internal representation
|
||||
tx = CTransaction()
|
||||
tx.deserialize(BytesIO(tx_bytes))
|
||||
return tx
|
||||
|
||||
def signTx(self, key_bytes: bytes, tx_bytes: bytes, input_n: int, prevout_script, prevout_value: int):
|
||||
tx = self.loadTx(tx_bytes)
|
||||
sig_hash = SegwitVersion1SignatureHash(prevout_script, tx, input_n, SIGHASH_ALL, prevout_value)
|
||||
eck = PrivateKey(key_bytes)
|
||||
return eck.sign(sig_hash, hasher=None) + bytes((SIGHASH_ALL,))
|
||||
|
||||
def setTxSignature(self, tx_bytes: bytes, stack) -> bytes:
|
||||
tx = self.loadTx(tx_bytes)
|
||||
tx.wit.vtxinwit.clear()
|
||||
tx.wit.vtxinwit.append(CTxInWitness())
|
||||
tx.wit.vtxinwit[0].scriptWitness.stack = stack
|
||||
return tx.serialize_with_witness()
|
||||
|
||||
def getProofOfFunds(self, amount_for, extra_commit_bytes):
|
||||
# TODO: Lock unspent and use same output/s to fund bid
|
||||
|
||||
unspents_by_addr = dict()
|
||||
unspents = self.rpc('listunspent')
|
||||
for u in unspents:
|
||||
if u['spendable'] is not True:
|
||||
continue
|
||||
if u['address'] not in unspents_by_addr:
|
||||
unspents_by_addr[u['address']] = {'total': 0, 'utxos': []}
|
||||
utxo_amount: int = self.make_int(u['amount'], r=1)
|
||||
unspents_by_addr[u['address']]['total'] += utxo_amount
|
||||
unspents_by_addr[u['address']]['utxos'].append((utxo_amount, u['txid'], u['vout']))
|
||||
|
||||
max_utxos: int = 4
|
||||
|
||||
viable_addrs = []
|
||||
for addr, data in unspents_by_addr.items():
|
||||
if data['total'] >= amount_for:
|
||||
# Sort from largest to smallest amount
|
||||
sorted_utxos = sorted(data['utxos'], key=lambda x: x[0])
|
||||
|
||||
# Max outputs required to reach amount_for
|
||||
utxos_req: int = 0
|
||||
sum_value: int = 0
|
||||
for utxo in sorted_utxos:
|
||||
sum_value += utxo[0]
|
||||
utxos_req += 1
|
||||
if sum_value >= amount_for:
|
||||
break
|
||||
|
||||
if utxos_req <= max_utxos:
|
||||
viable_addrs.append(addr)
|
||||
continue
|
||||
|
||||
ensure(len(viable_addrs) > 0, 'Could not find address with enough funds for proof')
|
||||
|
||||
sign_for_addr: str = random.choice(viable_addrs)
|
||||
self._log.debug('sign_for_addr %s', sign_for_addr)
|
||||
|
||||
prove_utxos = []
|
||||
sorted_utxos = sorted(unspents_by_addr[sign_for_addr]['utxos'], key=lambda x: x[0])
|
||||
|
||||
hasher = hashlib.sha256()
|
||||
|
||||
sum_value: int = 0
|
||||
for utxo in sorted_utxos:
|
||||
sum_value += utxo[0]
|
||||
outpoint = (bytes.fromhex(utxo[1]), utxo[2])
|
||||
prove_utxos.append(outpoint)
|
||||
hasher.update(outpoint[0])
|
||||
hasher.update(outpoint[1].to_bytes(2, 'big'))
|
||||
if sum_value >= amount_for:
|
||||
break
|
||||
utxos_hash = hasher.digest()
|
||||
|
||||
self._log.debug('sign_for_addr %s', sign_for_addr)
|
||||
|
||||
if self.using_segwit(): # TODO: Use isSegwitAddress when scantxoutset can use combo
|
||||
# 'Address does not refer to key' for non p2pkh
|
||||
addr_info = self.rpc('validateaddress', [addr, ])
|
||||
if 'isscript' in addr_info and addr_info['isscript'] and 'hex' in addr_info:
|
||||
pkh = bytes.fromhex(addr_info['hex'])[2:]
|
||||
sign_for_addr = self.pkh_to_address(pkh)
|
||||
self._log.debug('sign_for_addr converted %s', sign_for_addr)
|
||||
|
||||
signature = self.rpc('signmessage', [sign_for_addr, sign_for_addr + '_swap_proof_' + utxos_hash.hex() + extra_commit_bytes.hex()])
|
||||
|
||||
return (sign_for_addr, signature, prove_utxos)
|
||||
|
||||
def verifyProofOfFunds(self, address, signature, utxos, extra_commit_bytes):
|
||||
hasher = hashlib.sha256()
|
||||
sum_value: int = 0
|
||||
for outpoint in utxos:
|
||||
hasher.update(outpoint[0])
|
||||
hasher.update(outpoint[1].to_bytes(2, 'big'))
|
||||
utxos_hash = hasher.digest()
|
||||
|
||||
passed = self.verifyMessage(address, address + '_swap_proof_' + utxos_hash.hex() + extra_commit_bytes.hex(), signature)
|
||||
ensure(passed is True, 'Proof of funds signature invalid')
|
||||
|
||||
if self.using_segwit():
|
||||
address = self.encodeSegwitAddress(self.decodeAddress(address)[1:])
|
||||
|
||||
sum_value: int = 0
|
||||
for outpoint in utxos:
|
||||
txout = self.rpc('gettxout', [outpoint[0].hex(), outpoint[1]])
|
||||
sum_value += self.make_int(txout['value'])
|
||||
|
||||
return sum_value
|
||||
|
||||
def createRawFundedTransaction(self, addr_to: str, amount: int, sub_fee: bool = False, lock_unspents: bool = True) -> str:
|
||||
txn = self.rpc('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}')
|
||||
if sub_fee:
|
||||
raise ValueError('Navcoin fundrawtransaction is missing the subtractFeeFromOutputs parameter')
|
||||
# options['subtractFeeFromOutputs'] = [0,]
|
||||
|
||||
fee_rate = self.make_int(fee_rate, r=1)
|
||||
return self.fundTx(txn, fee_rate, lock_unspents).hex()
|
||||
|
||||
def isAddressMine(self, address: str, or_watch_only: bool = False) -> bool:
|
||||
addr_info = self.rpc('validateaddress', [address])
|
||||
if not or_watch_only:
|
||||
return addr_info['ismine']
|
||||
return addr_info['ismine'] or addr_info['iswatchonly']
|
||||
|
||||
def createRawSignedTransaction(self, addr_to, amount) -> str:
|
||||
txn_funded = self.createRawFundedTransaction(addr_to, amount)
|
||||
return self.rpc('signrawtransaction', [txn_funded])['hex']
|
||||
|
||||
def getBlockchainInfo(self):
|
||||
rv = self.rpc('getblockchaininfo')
|
||||
synced = round(rv['verificationprogress'], 3)
|
||||
if synced >= 0.997:
|
||||
rv['verificationprogress'] = 1.0
|
||||
return rv
|
||||
|
||||
def encodeScriptDest(self, script_dest: bytes) -> str:
|
||||
script_hash = script_dest[2:-1] # Extract hash from script
|
||||
return self.sh_to_address(script_hash)
|
||||
|
||||
def encode_p2wsh(self, script: bytes) -> str:
|
||||
return pubkeyToAddress(self.chainparams_network()['script_address'], script)
|
||||
|
||||
def find_prevout_info(self, txn_hex: str, txn_script: bytes):
|
||||
txjs = self.rpc('decoderawtransaction', [txn_hex])
|
||||
n = getVoutByScriptPubKey(txjs, self.getScriptDest(txn_script).hex())
|
||||
|
||||
return {
|
||||
'txid': txjs['txid'],
|
||||
'vout': n,
|
||||
'scriptPubKey': txjs['vout'][n]['scriptPubKey']['hex'],
|
||||
'redeemScript': txn_script.hex(),
|
||||
'amount': txjs['vout'][n]['value']
|
||||
}
|
||||
|
||||
def getNewAddress(self, use_segwit: bool, label: str = 'swap_receive') -> str:
|
||||
address: str = self.rpc('getnewaddress', [label,])
|
||||
if use_segwit:
|
||||
return self.rpc('addwitnessaddress', [address,])
|
||||
return address
|
||||
|
||||
def createRedeemTxn(self, prevout, output_addr: str, output_value: int, txn_script: bytes) -> str:
|
||||
tx = CTransaction()
|
||||
tx.nVersion = self.txVersion()
|
||||
prev_txid = uint256_from_str(bytes.fromhex(prevout['txid'])[::-1])
|
||||
|
||||
tx.vin.append(CTxIn(COutPoint(prev_txid, prevout['vout']),
|
||||
scriptSig=self.getScriptScriptSig(txn_script)))
|
||||
pkh = self.decodeAddress(output_addr)
|
||||
script = self.getScriptForPubkeyHash(pkh)
|
||||
tx.vout.append(self.txoType()(output_value, script))
|
||||
tx.rehash()
|
||||
return tx.serialize().hex()
|
||||
|
||||
def createRefundTxn(self, prevout, output_addr: str, output_value: int, locktime: int, sequence: int, txn_script: bytes) -> str:
|
||||
tx = CTransaction()
|
||||
tx.nVersion = self.txVersion()
|
||||
tx.nLockTime = locktime
|
||||
prev_txid = uint256_from_str(bytes.fromhex(prevout['txid'])[::-1])
|
||||
tx.vin.append(CTxIn(COutPoint(prev_txid, prevout['vout']),
|
||||
nSequence=sequence,
|
||||
scriptSig=self.getScriptScriptSig(txn_script)))
|
||||
pkh = self.decodeAddress(output_addr)
|
||||
script = self.getScriptForPubkeyHash(pkh)
|
||||
tx.vout.append(self.txoType()(output_value, script))
|
||||
tx.rehash()
|
||||
return tx.serialize().hex()
|
||||
|
||||
def getTxSignature(self, tx_hex: str, prevout_data, key_wif: str) -> str:
|
||||
key = decodeWif(key_wif)
|
||||
redeem_script = bytes.fromhex(prevout_data['redeemScript'])
|
||||
sig = self.signTx(key, bytes.fromhex(tx_hex), 0, redeem_script, self.make_int(prevout_data['amount']))
|
||||
|
||||
return sig.hex()
|
||||
|
||||
def verifyTxSig(self, tx_bytes: bytes, sig: bytes, K: bytes, input_n: int, prevout_script: bytes, prevout_value: int) -> bool:
|
||||
tx = self.loadTx(tx_bytes)
|
||||
sig_hash = SegwitVersion1SignatureHash(prevout_script, tx, input_n, SIGHASH_ALL, prevout_value)
|
||||
|
||||
pubkey = PublicKey(K)
|
||||
return pubkey.verify(sig[: -1], sig_hash, hasher=None) # Pop the hashtype byte
|
||||
|
||||
def verifyRawTransaction(self, tx_hex: str, prevouts):
|
||||
# Only checks signature
|
||||
# verifyrawtransaction
|
||||
self._log.warning('NAV verifyRawTransaction only checks signature')
|
||||
inputs_valid: bool = False
|
||||
validscripts: int = 0
|
||||
|
||||
tx_bytes = bytes.fromhex(tx_hex)
|
||||
tx = self.loadTx(bytes.fromhex(tx_hex))
|
||||
|
||||
signature = tx.wit.vtxinwit[0].scriptWitness.stack[0]
|
||||
pubkey = tx.wit.vtxinwit[0].scriptWitness.stack[1]
|
||||
|
||||
input_n: int = 0
|
||||
prevout_data = prevouts[input_n]
|
||||
redeem_script = bytes.fromhex(prevout_data['redeemScript'])
|
||||
prevout_value = self.make_int(prevout_data['amount'])
|
||||
|
||||
if self.verifyTxSig(tx_bytes, signature, pubkey, input_n, redeem_script, prevout_value):
|
||||
validscripts += 1
|
||||
|
||||
# TODO: validate inputs
|
||||
inputs_valid = True
|
||||
|
||||
return {
|
||||
'inputs_valid': inputs_valid,
|
||||
'validscripts': validscripts,
|
||||
}
|
||||
|
||||
def getHTLCSpendTxVSize(self, redeem: bool = True) -> int:
|
||||
tx_vsize = 5 # Add a few bytes, sequence in script takes variable amount of bytes
|
||||
|
||||
tx_vsize += 184 if redeem else 187
|
||||
return tx_vsize
|
||||
|
||||
def getTxid(self, tx) -> bytes:
|
||||
if isinstance(tx, str):
|
||||
tx = bytes.fromhex(tx)
|
||||
if isinstance(tx, bytes):
|
||||
tx = self.loadTx(tx)
|
||||
tx.rehash()
|
||||
return i2b(tx.sha256)
|
||||
|
||||
def rescanBlockchainForAddress(self, height_start: int, addr_find: str):
|
||||
# Very ugly workaround for missing `rescanblockchain` rpc command
|
||||
|
||||
chain_blocks: int = self.getChainHeight()
|
||||
|
||||
current_height: int = chain_blocks
|
||||
block_hash = self.rpc('getblockhash', [current_height])
|
||||
|
||||
script_hash: bytes = self.decodeAddress(addr_find)
|
||||
find_scriptPubKey = self.getDestForScriptHash(script_hash)
|
||||
|
||||
while current_height > height_start:
|
||||
block_hash = self.rpc('getblockhash', [current_height])
|
||||
|
||||
block = self.rpc('getblock', [block_hash, False])
|
||||
decoded_block = CBlock()
|
||||
decoded_block = FromHex(decoded_block, block)
|
||||
for tx in decoded_block.vtx:
|
||||
for txo in tx.vout:
|
||||
if txo.scriptPubKey == find_scriptPubKey:
|
||||
tx.rehash()
|
||||
txid = i2b(tx.sha256)
|
||||
self._log.info('Found output to addr: {} in tx {} in block {}'.format(addr_find, txid.hex(), block_hash))
|
||||
self._log.info('rescanblockchain hack invalidateblock {}'.format(block_hash))
|
||||
self.rpc('invalidateblock', [block_hash])
|
||||
self.rpc('reconsiderblock', [block_hash])
|
||||
return
|
||||
current_height -= 1
|
||||
|
||||
def getLockTxHeight(self, txid, dest_address, bid_amount, rescan_from, find_index: bool = False):
|
||||
# Add watchonly address and rescan if required
|
||||
|
||||
if not self.isAddressMine(dest_address, or_watch_only=True):
|
||||
self.importWatchOnlyAddress(dest_address, 'bid')
|
||||
self._log.info('Imported watch-only addr: {}'.format(dest_address))
|
||||
self._log.info('Rescanning {} chain from height: {}'.format(self.coin_name(), rescan_from))
|
||||
self.rescanBlockchainForAddress(rescan_from, dest_address)
|
||||
|
||||
return_txid = True if txid is None else False
|
||||
if txid is None:
|
||||
txns = self.rpc('listunspent', [0, 9999999, [dest_address, ]])
|
||||
|
||||
for tx in txns:
|
||||
if self.make_int(tx['amount']) == bid_amount:
|
||||
txid = bytes.fromhex(tx['txid'])
|
||||
break
|
||||
|
||||
if txid is None:
|
||||
return None
|
||||
|
||||
try:
|
||||
tx = self.rpc('gettransaction', [txid.hex()])
|
||||
|
||||
block_height = 0
|
||||
if 'blockhash' in tx:
|
||||
block_header = self.rpc('getblockheader', [tx['blockhash']])
|
||||
block_height = block_header['height']
|
||||
|
||||
rv = {
|
||||
'depth': 0 if 'confirmations' not in tx else tx['confirmations'],
|
||||
'height': block_height}
|
||||
|
||||
except Exception as e:
|
||||
self._log.debug('getLockTxHeight gettransaction failed: %s, %s', txid.hex(), str(e))
|
||||
return None
|
||||
|
||||
if find_index:
|
||||
tx_obj = self.rpc('decoderawtransaction', [tx['hex']])
|
||||
rv['index'] = find_vout_for_address_from_txobj(tx_obj, dest_address)
|
||||
|
||||
if return_txid:
|
||||
rv['txid'] = txid.hex()
|
||||
|
||||
return rv
|
||||
|
||||
def getBlockWithTxns(self, block_hash):
|
||||
# TODO: Bypass decoderawtransaction and getblockheader
|
||||
block = self.rpc('getblock', [block_hash, False])
|
||||
block_header = self.rpc('getblockheader', [block_hash])
|
||||
decoded_block = CBlock()
|
||||
decoded_block = FromHex(decoded_block, block)
|
||||
|
||||
tx_rv = []
|
||||
for tx in decoded_block.vtx:
|
||||
tx_hex = tx.serialize_with_witness().hex()
|
||||
tx_dec = self.rpc('decoderawtransaction', [tx_hex])
|
||||
if 'hex' not in tx_dec:
|
||||
tx_dec['hex'] = tx_hex
|
||||
|
||||
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
|
||||
|
||||
def getScriptScriptSig(self, script: bytes) -> bytearray:
|
||||
return self.getP2SHP2WSHScriptSig(script)
|
||||
|
||||
def getScriptDest(self, script):
|
||||
return self.getP2SHP2WSHDest(script)
|
||||
|
||||
def getDestForScriptHash(self, script_hash):
|
||||
assert len(script_hash) == 20
|
||||
return CScript([OP_HASH160, script_hash, OP_EQUAL])
|
||||
|
||||
def pubkey_to_segwit_address(self, pk: bytes) -> str:
|
||||
pkh = hash160(pk)
|
||||
script_out = self.getScriptForPubkeyHash(pkh)
|
||||
return self.encodeSegwitAddressScript(script_out)
|
||||
|
||||
def createBLockTx(self, Kbs: bytes, output_amount: int, vkbv=None) -> bytes:
|
||||
tx = CTransaction()
|
||||
tx.nVersion = self.txVersion()
|
||||
script_pk = self.getPkDest(Kbs)
|
||||
tx.vout.append(self.txoType()(output_amount, script_pk))
|
||||
return tx.serialize()
|
||||
|
||||
def spendBLockTx(self, chain_b_lock_txid: bytes, address_to: str, kbv: bytes, kbs: bytes, cb_swap_value: int, b_fee: int, restore_height: int) -> bytes:
|
||||
self._log.info('spendBLockTx %s:\n', chain_b_lock_txid.hex())
|
||||
wtx = self.rpc('gettransaction', [chain_b_lock_txid.hex(), ])
|
||||
lock_tx = self.loadTx(bytes.fromhex(wtx['hex']))
|
||||
|
||||
Kbs = self.getPubkey(kbs)
|
||||
script_pk = self.getPkDest(Kbs)
|
||||
locked_n = findOutput(lock_tx, script_pk)
|
||||
ensure(locked_n is not None, 'Output not found in tx')
|
||||
pkh_to = self.decodeAddress(address_to)
|
||||
|
||||
tx = CTransaction()
|
||||
tx.nVersion = self.txVersion()
|
||||
|
||||
chain_b_lock_txid_int = uint256_from_str(chain_b_lock_txid[::-1])
|
||||
|
||||
script_sig = self.getInputScriptForPubkeyHash(self.getPubkeyHash(Kbs))
|
||||
|
||||
tx.vin.append(CTxIn(COutPoint(chain_b_lock_txid_int, locked_n),
|
||||
nSequence=0,
|
||||
scriptSig=script_sig))
|
||||
tx.vout.append(self.txoType()(cb_swap_value, self.getScriptForPubkeyHash(pkh_to)))
|
||||
|
||||
pay_fee = self.getBLockSpendTxFee(tx, b_fee)
|
||||
tx.vout[0].nValue = cb_swap_value - pay_fee
|
||||
|
||||
b_lock_spend_tx = tx.serialize()
|
||||
b_lock_spend_tx = self.signTxWithKey(b_lock_spend_tx, kbs, cb_swap_value)
|
||||
|
||||
return bytes.fromhex(self.publishTx(b_lock_spend_tx))
|
||||
|
||||
def signTxWithKey(self, tx: bytes, key: bytes, prev_amount: int) -> bytes:
|
||||
Key = self.getPubkey(key)
|
||||
pkh = self.getPubkeyHash(Key)
|
||||
script = self.getScriptForP2PKH(pkh)
|
||||
|
||||
sig = self.signTx(key, tx, 0, script, prev_amount)
|
||||
|
||||
stack = [
|
||||
sig,
|
||||
Key,
|
||||
]
|
||||
return self.setTxSignature(tx, stack)
|
||||
|
||||
def findTxnByHash(self, txid_hex: str):
|
||||
# Only works for wallet txns
|
||||
try:
|
||||
rv = self.rpc('gettransaction', [txid_hex])
|
||||
except Exception as ex:
|
||||
self._log.debug('findTxnByHash getrawtransaction failed: {}'.format(txid_hex))
|
||||
return None
|
||||
if 'confirmations' in rv and rv['confirmations'] >= self.blocks_confirmed:
|
||||
block_height = self.getBlockHeader(rv['blockhash'])['height']
|
||||
return {'txid': txid_hex, 'amount': 0, 'height': block_height}
|
||||
return None
|
||||
|
||||
def createSCLockTx(self, value: int, script: bytearray, vkbv: bytes = None) -> bytes:
|
||||
tx = CTransaction()
|
||||
tx.nVersion = self.txVersion()
|
||||
tx.vout.append(self.txoType()(value, self.getScriptDest(script)))
|
||||
|
||||
return tx.serialize()
|
||||
|
||||
def fundTx(self, tx_hex: str, feerate: int, lock_unspents: bool = True):
|
||||
feerate_str = self.format_amount(feerate)
|
||||
# TODO: unlock unspents if bid cancelled
|
||||
options = {
|
||||
'lockUnspents': lock_unspents,
|
||||
'feeRate': feerate_str,
|
||||
}
|
||||
rv = self.rpc('fundrawtransaction', [tx_hex, options])
|
||||
|
||||
# Sign transaction then strip witness data to fill scriptsig
|
||||
rv = self.rpc('signrawtransaction', [rv['hex']])
|
||||
|
||||
tx_signed = self.loadTx(bytes.fromhex(rv['hex']))
|
||||
if len(tx_signed.vin) != len(tx_signed.wit.vtxinwit):
|
||||
raise ValueError('txn has non segwit input')
|
||||
for witness_data in tx_signed.wit.vtxinwit:
|
||||
if len(witness_data.scriptWitness.stack) < 2:
|
||||
raise ValueError('txn has non segwit input')
|
||||
|
||||
return tx_signed.serialize_without_witness()
|
||||
|
||||
def fundSCLockTx(self, tx_bytes: bytes, feerate, vkbv=None) -> bytes:
|
||||
tx_funded = self.fundTx(tx_bytes.hex(), feerate)
|
||||
return tx_funded
|
||||
|
||||
def createSCLockRefundTx(self, tx_lock_bytes, script_lock, Kal, Kaf, lock1_value, csv_val, tx_fee_rate, vkbv=None):
|
||||
tx_lock = CTransaction()
|
||||
tx_lock = self.loadTx(tx_lock_bytes)
|
||||
|
||||
output_script = self.getScriptDest(script_lock)
|
||||
locked_n = findOutput(tx_lock, output_script)
|
||||
ensure(locked_n is not None, 'Output not found in tx')
|
||||
locked_coin = tx_lock.vout[locked_n].nValue
|
||||
|
||||
tx_lock.rehash()
|
||||
tx_lock_id_int = tx_lock.sha256
|
||||
|
||||
refund_script = self.genScriptLockRefundTxScript(Kal, Kaf, csv_val)
|
||||
tx = CTransaction()
|
||||
tx.nVersion = self.txVersion()
|
||||
tx.vin.append(CTxIn(COutPoint(tx_lock_id_int, locked_n),
|
||||
nSequence=lock1_value,
|
||||
scriptSig=self.getScriptScriptSig(script_lock)))
|
||||
tx.vout.append(self.txoType()(locked_coin, self.getScriptDest(refund_script)))
|
||||
|
||||
dummy_witness_stack = self.getScriptLockTxDummyWitness(script_lock)
|
||||
witness_bytes = self.getWitnessStackSerialisedLength(dummy_witness_stack)
|
||||
vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
|
||||
pay_fee = round(tx_fee_rate * vsize / 1000)
|
||||
tx.vout[0].nValue = locked_coin - pay_fee
|
||||
|
||||
tx.rehash()
|
||||
self._log.info('createSCLockRefundTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.',
|
||||
i2h(tx.sha256), tx_fee_rate, vsize, pay_fee)
|
||||
|
||||
return tx.serialize(), refund_script, tx.vout[0].nValue
|
||||
|
||||
def createSCLockRefundSpendTx(self, tx_lock_refund_bytes, script_lock_refund, pkh_refund_to, tx_fee_rate, vkbv=None):
|
||||
# Returns the coinA locked coin to the leader
|
||||
# The follower will sign the multisig path with a signature encumbered by the leader's coinB spend pubkey
|
||||
# If the leader publishes the decrypted signature the leader's coinB spend privatekey will be revealed to the follower
|
||||
|
||||
tx_lock_refund = self.loadTx(tx_lock_refund_bytes)
|
||||
|
||||
output_script = self.getScriptDest(script_lock_refund)
|
||||
locked_n = findOutput(tx_lock_refund, output_script)
|
||||
ensure(locked_n is not None, 'Output not found in tx')
|
||||
locked_coin = tx_lock_refund.vout[locked_n].nValue
|
||||
|
||||
tx_lock_refund.rehash()
|
||||
tx_lock_refund_hash_int = tx_lock_refund.sha256
|
||||
|
||||
tx = CTransaction()
|
||||
tx.nVersion = self.txVersion()
|
||||
tx.vin.append(CTxIn(COutPoint(tx_lock_refund_hash_int, locked_n),
|
||||
nSequence=0,
|
||||
scriptSig=self.getScriptScriptSig(script_lock_refund)))
|
||||
|
||||
tx.vout.append(self.txoType()(locked_coin, self.getScriptForPubkeyHash(pkh_refund_to)))
|
||||
|
||||
dummy_witness_stack = self.getScriptLockRefundSpendTxDummyWitness(script_lock_refund)
|
||||
witness_bytes = self.getWitnessStackSerialisedLength(dummy_witness_stack)
|
||||
vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
|
||||
pay_fee = round(tx_fee_rate * vsize / 1000)
|
||||
tx.vout[0].nValue = locked_coin - pay_fee
|
||||
|
||||
tx.rehash()
|
||||
self._log.info('createSCLockRefundSpendTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.',
|
||||
i2h(tx.sha256), tx_fee_rate, vsize, pay_fee)
|
||||
|
||||
return tx.serialize()
|
||||
|
||||
def createSCLockRefundSpendToFTx(self, tx_lock_refund_bytes, script_lock_refund, pkh_dest, tx_fee_rate, vkbv=None):
|
||||
# lock refund swipe tx
|
||||
# Sends the coinA locked coin to the follower
|
||||
|
||||
tx_lock_refund = self.loadTx(tx_lock_refund_bytes)
|
||||
|
||||
output_script = self.getScriptDest(script_lock_refund)
|
||||
locked_n = findOutput(tx_lock_refund, output_script)
|
||||
ensure(locked_n is not None, 'Output not found in tx')
|
||||
locked_coin = tx_lock_refund.vout[locked_n].nValue
|
||||
|
||||
A, B, lock2_value, C = self.extractScriptLockRefundScriptValues(script_lock_refund)
|
||||
|
||||
tx_lock_refund.rehash()
|
||||
tx_lock_refund_hash_int = tx_lock_refund.sha256
|
||||
|
||||
tx = CTransaction()
|
||||
tx.nVersion = self.txVersion()
|
||||
tx.vin.append(CTxIn(COutPoint(tx_lock_refund_hash_int, locked_n),
|
||||
nSequence=lock2_value,
|
||||
scriptSig=self.getScriptScriptSig(script_lock_refund)))
|
||||
|
||||
tx.vout.append(self.txoType()(locked_coin, self.getScriptForPubkeyHash(pkh_dest)))
|
||||
|
||||
dummy_witness_stack = self.getScriptLockRefundSwipeTxDummyWitness(script_lock_refund)
|
||||
witness_bytes = self.getWitnessStackSerialisedLength(dummy_witness_stack)
|
||||
vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
|
||||
pay_fee = round(tx_fee_rate * vsize / 1000)
|
||||
tx.vout[0].nValue = locked_coin - pay_fee
|
||||
|
||||
tx.rehash()
|
||||
self._log.info('createSCLockRefundSpendToFTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.',
|
||||
i2h(tx.sha256), tx_fee_rate, vsize, pay_fee)
|
||||
|
||||
return tx.serialize()
|
||||
|
||||
def createSCLockSpendTx(self, tx_lock_bytes, script_lock, pkh_dest, tx_fee_rate, vkbv=None, fee_info={}):
|
||||
tx_lock = self.loadTx(tx_lock_bytes)
|
||||
output_script = self.getScriptDest(script_lock)
|
||||
locked_n = findOutput(tx_lock, output_script)
|
||||
ensure(locked_n is not None, 'Output not found in tx')
|
||||
locked_coin = tx_lock.vout[locked_n].nValue
|
||||
|
||||
tx_lock.rehash()
|
||||
tx_lock_id_int = tx_lock.sha256
|
||||
|
||||
tx = CTransaction()
|
||||
tx.nVersion = self.txVersion()
|
||||
tx.vin.append(CTxIn(COutPoint(tx_lock_id_int, locked_n),
|
||||
scriptSig=self.getScriptScriptSig(script_lock)))
|
||||
|
||||
tx.vout.append(self.txoType()(locked_coin, self.getScriptForPubkeyHash(pkh_dest)))
|
||||
|
||||
dummy_witness_stack = self.getScriptLockTxDummyWitness(script_lock)
|
||||
witness_bytes = self.getWitnessStackSerialisedLength(dummy_witness_stack)
|
||||
vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
|
||||
pay_fee = round(tx_fee_rate * vsize / 1000)
|
||||
tx.vout[0].nValue = locked_coin - pay_fee
|
||||
|
||||
fee_info['fee_paid'] = pay_fee
|
||||
fee_info['rate_used'] = tx_fee_rate
|
||||
fee_info['witness_bytes'] = witness_bytes
|
||||
fee_info['vsize'] = vsize
|
||||
|
||||
tx.rehash()
|
||||
self._log.info('createSCLockSpendTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.',
|
||||
i2h(tx.sha256), tx_fee_rate, vsize, pay_fee)
|
||||
|
||||
return tx.serialize()
|
||||
@@ -19,7 +19,7 @@ class NMCInterface(BTCInterface):
|
||||
|
||||
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
|
||||
ro = self.rpc('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']:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2020-2022 tecnovert
|
||||
# Copyright (c) 2020-2024 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
@@ -17,7 +17,6 @@ from basicswap.contrib.test_framework.script import (
|
||||
OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG
|
||||
)
|
||||
from basicswap.util import (
|
||||
i2b,
|
||||
ensure,
|
||||
make_int,
|
||||
TemporaryError,
|
||||
@@ -43,6 +42,8 @@ class BalanceTypes(IntEnum):
|
||||
class PARTInterface(BTCInterface):
|
||||
@staticmethod
|
||||
def coin_type():
|
||||
# Returns the base coin type
|
||||
# ANON and BLIND PART will return Coins.PART
|
||||
return Coins.PART
|
||||
|
||||
@staticmethod
|
||||
@@ -58,8 +59,12 @@ class PARTInterface(BTCInterface):
|
||||
return 0xa0
|
||||
|
||||
@staticmethod
|
||||
def xmr_swap_alock_spend_tx_vsize() -> int:
|
||||
return 213
|
||||
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
||||
return 200
|
||||
|
||||
@staticmethod
|
||||
def xmr_swap_b_lock_spend_tx_vsize() -> int:
|
||||
return 138
|
||||
|
||||
@staticmethod
|
||||
def txoType():
|
||||
@@ -77,15 +82,15 @@ class PARTInterface(BTCInterface):
|
||||
# TODO: Double check
|
||||
return True
|
||||
|
||||
def getNewAddress(self, use_segwit, label='swap_receive'):
|
||||
return self.rpc_callback('getnewaddress', [label])
|
||||
def getNewAddress(self, use_segwit, label='swap_receive') -> str:
|
||||
return self.rpc_wallet('getnewaddress', [label])
|
||||
|
||||
def getNewStealthAddress(self, label='swap_stealth'):
|
||||
return self.rpc_callback('getnewstealthaddress', [label])
|
||||
def getNewStealthAddress(self, label='swap_stealth') -> str:
|
||||
return self.rpc_wallet('getnewstealthaddress', [label])
|
||||
|
||||
def haveSpentIndex(self):
|
||||
version = self.getDaemonVersion()
|
||||
index_info = self.rpc_callback('getinsightinfo' if int(str(version)[:2]) > 19 else 'getindexinfo')
|
||||
index_info = self.rpc('getinsightinfo' if int(str(version)[:2]) > 19 else 'getindexinfo')
|
||||
return index_info['spentindex']
|
||||
|
||||
def initialiseWallet(self, key):
|
||||
@@ -93,33 +98,33 @@ class PARTInterface(BTCInterface):
|
||||
|
||||
def withdrawCoin(self, value, addr_to, subfee):
|
||||
params = [addr_to, value, '', '', subfee, '', True, self._conf_target]
|
||||
return self.rpc_callback('sendtoaddress', params)
|
||||
return self.rpc_wallet('sendtoaddress', params)
|
||||
|
||||
def sendTypeTo(self, type_from, type_to, value, addr_to, subfee):
|
||||
params = [type_from, type_to,
|
||||
[{'address': addr_to, 'amount': value, 'subfee': subfee}, ],
|
||||
'', '', self._anon_tx_ring_size, 1, False,
|
||||
{'conf_target': self._conf_target}]
|
||||
return self.rpc_callback('sendtypeto', params)
|
||||
return self.rpc_wallet('sendtypeto', params)
|
||||
|
||||
def getScriptForPubkeyHash(self, pkh):
|
||||
def getScriptForPubkeyHash(self, pkh: bytes) -> CScript:
|
||||
return CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG])
|
||||
|
||||
def formatStealthAddress(self, scan_pubkey, spend_pubkey):
|
||||
def formatStealthAddress(self, scan_pubkey, spend_pubkey) -> str:
|
||||
prefix_byte = chainparams[self.coin_type()][self._network]['stealth_key_prefix']
|
||||
|
||||
return encodeStealthAddress(prefix_byte, scan_pubkey, spend_pubkey)
|
||||
|
||||
def getWitnessStackSerialisedLength(self, witness_stack):
|
||||
length = getCompactSizeLen(len(witness_stack))
|
||||
def getWitnessStackSerialisedLength(self, witness_stack) -> int:
|
||||
length: int = getCompactSizeLen(len(witness_stack))
|
||||
for e in witness_stack:
|
||||
length += getWitnessElementLen(len(e) // 2) # hex -> bytes
|
||||
length += getWitnessElementLen(len(e))
|
||||
return length
|
||||
|
||||
def getWalletRestoreHeight(self):
|
||||
start_time = self.rpc_callback('getwalletinfo')['keypoololdest']
|
||||
def getWalletRestoreHeight(self) -> int:
|
||||
start_time = self.rpc_wallet('getwalletinfo')['keypoololdest']
|
||||
|
||||
blockchaininfo = self.rpc_callback('getblockchaininfo')
|
||||
blockchaininfo = self.getBlockchainInfo()
|
||||
best_block = blockchaininfo['bestblockhash']
|
||||
|
||||
chain_synced = round(blockchaininfo['verificationprogress'], 3)
|
||||
@@ -127,17 +132,41 @@ class PARTInterface(BTCInterface):
|
||||
raise ValueError('{} chain isn\'t synced.'.format(self.coin_name()))
|
||||
|
||||
self._log.debug('Finding block at time: {}'.format(start_time))
|
||||
block_hash = self.rpc_callback('getblockhashafter', [start_time])
|
||||
block_header = self.rpc_callback('getblockheader', [block_hash])
|
||||
block_hash = self.rpc('getblockhashafter', [start_time])
|
||||
block_header = self.rpc('getblockheader', [block_hash])
|
||||
return block_header['height']
|
||||
|
||||
def getHTLCSpendTxVSize(self, redeem: bool = True) -> int:
|
||||
tx_vsize = 5 # Add a few bytes, sequence in script takes variable amount of bytes
|
||||
tx_vsize += 204 if redeem else 187
|
||||
return tx_vsize
|
||||
|
||||
def getUnspentsByAddr(self):
|
||||
unspent_addr = dict()
|
||||
unspent = self.rpc_wallet('listunspent')
|
||||
for u in unspent:
|
||||
if u['spendable'] is not True:
|
||||
continue
|
||||
if 'address' not in u:
|
||||
continue
|
||||
unspent_addr[u['address']] = unspent_addr.get(u['address'], 0) + self.make_int(u['amount'], r=1)
|
||||
return unspent_addr
|
||||
|
||||
|
||||
class PARTInterfaceBlind(PARTInterface):
|
||||
@staticmethod
|
||||
def balance_type():
|
||||
return BalanceTypes.BLIND
|
||||
|
||||
def coin_name(self):
|
||||
@staticmethod
|
||||
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
||||
return 1032
|
||||
|
||||
@staticmethod
|
||||
def xmr_swap_b_lock_spend_tx_vsize() -> int:
|
||||
return 980
|
||||
|
||||
def coin_name(self) -> str:
|
||||
return super().coin_name() + ' Blind'
|
||||
|
||||
def getScriptLockTxNonce(self, data):
|
||||
@@ -153,24 +182,23 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
if txo['type'] != 'blind':
|
||||
continue
|
||||
try:
|
||||
blinded_info = self.rpc_callback('rewindrangeproof', [txo['rangeproof'], txo['valueCommitment'], nonce.hex()])
|
||||
blinded_info = self.rpc('rewindrangeproof', [txo['rangeproof'], txo['valueCommitment'], nonce.hex()])
|
||||
output_n = txo['n']
|
||||
|
||||
self.rpc_callback('rewindrangeproof', [txo['rangeproof'], txo['valueCommitment'], nonce.hex()])
|
||||
self.rpc('rewindrangeproof', [txo['rangeproof'], txo['valueCommitment'], nonce.hex()])
|
||||
break
|
||||
except Exception as e:
|
||||
self._log.debug('Searching for locked output: {}'.format(str(e)))
|
||||
continue
|
||||
# Should not be possible for commitment not to match
|
||||
v = self.rpc_callback('verifycommitment', [txo['valueCommitment'], blinded_info['blind'], blinded_info['amount']])
|
||||
v = self.rpc('verifycommitment', [txo['valueCommitment'], blinded_info['blind'], blinded_info['amount']])
|
||||
ensure(v['result'] is True, 'verifycommitment failed')
|
||||
return output_n, blinded_info
|
||||
|
||||
def createSCLockTx(self, value, Kal, Kaf, vkbv):
|
||||
script = self.genScriptLockTxScript(Kal, Kaf)
|
||||
def createSCLockTx(self, value: int, script: bytearray, vkbv: bytes) -> bytes:
|
||||
|
||||
# Nonce is derived from vkbv, ephemeral_key isn't used
|
||||
ephemeral_key = i2b(self.getNewSecretKey())
|
||||
ephemeral_key = self.getNewSecretKey()
|
||||
ephemeral_pubkey = self.getPubkey(ephemeral_key)
|
||||
assert (len(ephemeral_pubkey) == 33)
|
||||
nonce = self.getScriptLockTxNonce(vkbv)
|
||||
@@ -178,23 +206,23 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
inputs = []
|
||||
outputs = [{'type': 'blind', 'amount': self.format_amount(value), 'address': p2wsh_addr, 'nonce': nonce.hex(), 'data': ephemeral_pubkey.hex()}]
|
||||
params = [inputs, outputs]
|
||||
rv = self.rpc_callback('createrawparttransaction', params)
|
||||
rv = self.rpc_wallet('createrawparttransaction', params)
|
||||
|
||||
tx_bytes = bytes.fromhex(rv['hex'])
|
||||
return tx_bytes, script
|
||||
return tx_bytes
|
||||
|
||||
def fundSCLockTx(self, tx_bytes, feerate, vkbv):
|
||||
def fundSCLockTx(self, tx_bytes: bytes, feerate: int, vkbv: bytes) -> bytes:
|
||||
feerate_str = self.format_amount(feerate)
|
||||
# TODO: unlock unspents if bid cancelled
|
||||
|
||||
tx_hex = tx_bytes.hex()
|
||||
nonce = self.getScriptLockTxNonce(vkbv)
|
||||
|
||||
tx_obj = self.rpc_callback('decoderawtransaction', [tx_hex])
|
||||
tx_obj = self.rpc('decoderawtransaction', [tx_hex])
|
||||
|
||||
assert (len(tx_obj['vout']) == 1)
|
||||
txo = tx_obj['vout'][0]
|
||||
blinded_info = self.rpc_callback('rewindrangeproof', [txo['rangeproof'], txo['valueCommitment'], nonce.hex()])
|
||||
blinded_info = self.rpc('rewindrangeproof', [txo['rangeproof'], txo['valueCommitment'], nonce.hex()])
|
||||
|
||||
outputs_info = {0: {'value': blinded_info['amount'], 'blind': blinded_info['blind'], 'nonce': nonce.hex()}}
|
||||
|
||||
@@ -202,14 +230,14 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
'lockUnspents': True,
|
||||
'feeRate': feerate_str,
|
||||
}
|
||||
rv = self.rpc_callback('fundrawtransactionfrom', ['blind', tx_hex, {}, outputs_info, options])
|
||||
rv = self.rpc('fundrawtransactionfrom', ['blind', tx_hex, {}, outputs_info, options])
|
||||
return bytes.fromhex(rv['hex'])
|
||||
|
||||
def createSCLockRefundTx(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()])
|
||||
lock_tx_obj = self.rpc('decoderawtransaction', [tx_lock_bytes.hex()])
|
||||
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_key = self.getNewSecretKey()
|
||||
ephemeral_pubkey = self.getPubkey(ephemeral_key)
|
||||
assert (len(ephemeral_pubkey) == 33)
|
||||
nonce = self.getScriptLockTxNonce(vkbv)
|
||||
@@ -227,14 +255,15 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
inputs = [{'txid': tx_lock_id, 'vout': spend_n, 'sequence': lock1_value, 'blindingfactor': input_blinded_info['blind']}]
|
||||
outputs = [{'type': 'blind', 'amount': locked_coin, 'address': p2wsh_addr, 'nonce': output_nonce.hex(), 'data': ephemeral_pubkey.hex()}]
|
||||
params = [inputs, outputs]
|
||||
rv = self.rpc_callback('createrawparttransaction', params)
|
||||
rv = self.rpc_wallet('createrawparttransaction', params)
|
||||
lock_refund_tx_hex = rv['hex']
|
||||
|
||||
# Set dummy witness data for fee estimation
|
||||
dummy_witness_stack = self.getScriptLockTxDummyWitness(script_lock)
|
||||
dummy_witness_stack = [x.hex() for x in dummy_witness_stack]
|
||||
|
||||
# Use a junk change pubkey to avoid adding unused keys to the wallet
|
||||
zero_change_key = i2b(self.getNewSecretKey())
|
||||
zero_change_key = self.getNewSecretKey()
|
||||
zero_change_pubkey = self.getPubkey(zero_change_key)
|
||||
inputs_info = {'0': {'value': input_blinded_info['amount'], 'blind': input_blinded_info['blind'], 'witnessstack': dummy_witness_stack}}
|
||||
outputs_info = rv['amounts']
|
||||
@@ -243,7 +272,7 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
'feeRate': self.format_amount(tx_fee_rate),
|
||||
'subtractFeeFromOutputs': [0, ]
|
||||
}
|
||||
rv = self.rpc_callback('fundrawtransactionfrom', ['blind', lock_refund_tx_hex, inputs_info, outputs_info, options])
|
||||
rv = self.rpc_wallet('fundrawtransactionfrom', ['blind', lock_refund_tx_hex, inputs_info, outputs_info, options])
|
||||
lock_refund_tx_hex = rv['hex']
|
||||
|
||||
for vout, txo in rv['output_amounts'].items():
|
||||
@@ -257,7 +286,7 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
# The follower will sign the multisig path with a signature encumbered by the leader's coinB spend pubkey
|
||||
# If the leader publishes the decrypted signature the leader's coinB spend privatekey will be revealed to the follower
|
||||
|
||||
lock_refund_tx_obj = self.rpc_callback('decoderawtransaction', [tx_lock_refund_bytes.hex()])
|
||||
lock_refund_tx_obj = self.rpc('decoderawtransaction', [tx_lock_refund_bytes.hex()])
|
||||
# Nonce is derived from vkbv
|
||||
nonce = self.getScriptLockRefundTxNonce(vkbv)
|
||||
|
||||
@@ -267,7 +296,7 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
|
||||
tx_lock_refund_id = lock_refund_tx_obj['txid']
|
||||
addr_out = self.pkh_to_address(pkh_refund_to)
|
||||
addr_info = self.rpc_callback('getaddressinfo', [addr_out])
|
||||
addr_info = self.rpc_wallet('getaddressinfo', [addr_out])
|
||||
output_pubkey_hex = addr_info['pubkey']
|
||||
|
||||
# Follower won't be able to decode output to check amount, shouldn't matter as fee is public and output is to leader, sum has to balance
|
||||
@@ -275,14 +304,15 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
inputs = [{'txid': tx_lock_refund_id, 'vout': spend_n, 'sequence': 0, 'blindingfactor': input_blinded_info['blind']}]
|
||||
outputs = [{'type': 'blind', 'amount': input_blinded_info['amount'], 'address': addr_out, 'pubkey': output_pubkey_hex}]
|
||||
params = [inputs, outputs]
|
||||
rv = self.rpc_callback('createrawparttransaction', params)
|
||||
rv = self.rpc_wallet('createrawparttransaction', params)
|
||||
lock_refund_spend_tx_hex = rv['hex']
|
||||
|
||||
# Set dummy witness data for fee estimation
|
||||
dummy_witness_stack = self.getScriptLockRefundSpendTxDummyWitness(script_lock_refund)
|
||||
dummy_witness_stack = [x.hex() for x in dummy_witness_stack]
|
||||
|
||||
# Use a junk change pubkey to avoid adding unused keys to the wallet
|
||||
zero_change_key = i2b(self.getNewSecretKey())
|
||||
zero_change_key = self.getNewSecretKey()
|
||||
zero_change_pubkey = self.getPubkey(zero_change_key)
|
||||
inputs_info = {'0': {'value': input_blinded_info['amount'], 'blind': input_blinded_info['blind'], 'witnessstack': dummy_witness_stack}}
|
||||
outputs_info = rv['amounts']
|
||||
@@ -292,7 +322,7 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
'subtractFeeFromOutputs': [0, ]
|
||||
}
|
||||
|
||||
rv = self.rpc_callback('fundrawtransactionfrom', ['blind', lock_refund_spend_tx_hex, inputs_info, outputs_info, options])
|
||||
rv = self.rpc_wallet('fundrawtransactionfrom', ['blind', lock_refund_spend_tx_hex, inputs_info, outputs_info, options])
|
||||
lock_refund_spend_tx_hex = rv['hex']
|
||||
|
||||
return bytes.fromhex(lock_refund_spend_tx_hex)
|
||||
@@ -302,7 +332,7 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
Kal, Kaf,
|
||||
feerate,
|
||||
check_lock_tx_inputs, vkbv):
|
||||
lock_tx_obj = self.rpc_callback('decoderawtransaction', [tx_bytes.hex()])
|
||||
lock_tx_obj = self.rpc('decoderawtransaction', [tx_bytes.hex()])
|
||||
lock_txid_hex = lock_tx_obj['txid']
|
||||
self._log.info('Verifying lock tx: {}.'.format(lock_txid_hex))
|
||||
|
||||
@@ -344,7 +374,7 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
def verifySCLockRefundTx(self, tx_bytes, lock_tx_bytes, script_out,
|
||||
prevout_id, prevout_n, prevout_seq, prevout_script,
|
||||
Kal, Kaf, csv_val_expect, swap_value, feerate, vkbv):
|
||||
lock_refund_tx_obj = self.rpc_callback('decoderawtransaction', [tx_bytes.hex()])
|
||||
lock_refund_tx_obj = self.rpc('decoderawtransaction', [tx_bytes.hex()])
|
||||
lock_refund_txid_hex = lock_refund_tx_obj['txid']
|
||||
self._log.info('Verifying lock refund tx: {}.'.format(lock_refund_txid_hex))
|
||||
|
||||
@@ -377,10 +407,10 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
ensure(C == Kaf, 'Bad script pubkey')
|
||||
|
||||
# Check rangeproofs and commitments sum
|
||||
lock_tx_obj = self.rpc_callback('decoderawtransaction', [lock_tx_bytes.hex()])
|
||||
lock_tx_obj = self.rpc('decoderawtransaction', [lock_tx_bytes.hex()])
|
||||
prevout = lock_tx_obj['vout'][prevout_n]
|
||||
prevtxns = [{'txid': prevout_id.hex(), 'vout': prevout_n, 'scriptPubKey': prevout['scriptPubKey']['hex'], 'amount_commitment': prevout['valueCommitment']}]
|
||||
rv = self.rpc_callback('verifyrawtransaction', [tx_bytes.hex(), prevtxns])
|
||||
rv = self.rpc('verifyrawtransaction', [tx_bytes.hex(), prevtxns])
|
||||
ensure(rv['outputs_valid'] is True, 'Invalid outputs')
|
||||
ensure(rv['inputs_valid'] is True, 'Invalid inputs')
|
||||
|
||||
@@ -403,7 +433,7 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
lock_refund_tx_id, prevout_script,
|
||||
Kal,
|
||||
prevout_n, prevout_value, feerate, vkbv):
|
||||
lock_refund_spend_tx_obj = self.rpc_callback('decoderawtransaction', [tx_bytes.hex()])
|
||||
lock_refund_spend_tx_obj = self.rpc('decoderawtransaction', [tx_bytes.hex()])
|
||||
lock_refund_spend_txid_hex = lock_refund_spend_tx_obj['txid']
|
||||
self._log.info('Verifying lock refund spend tx: {}.'.format(lock_refund_spend_txid_hex))
|
||||
|
||||
@@ -422,10 +452,10 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
# Follower is not concerned with them as they pay to leader
|
||||
|
||||
# Check rangeproofs and commitments sum
|
||||
lock_refund_tx_obj = self.rpc_callback('decoderawtransaction', [lock_refund_tx_bytes.hex()])
|
||||
lock_refund_tx_obj = self.rpc('decoderawtransaction', [lock_refund_tx_bytes.hex()])
|
||||
prevout = lock_refund_tx_obj['vout'][prevout_n]
|
||||
prevtxns = [{'txid': lock_refund_tx_id.hex(), 'vout': prevout_n, 'scriptPubKey': prevout['scriptPubKey']['hex'], 'amount_commitment': prevout['valueCommitment']}]
|
||||
rv = self.rpc_callback('verifyrawtransaction', [tx_bytes.hex(), prevtxns])
|
||||
rv = self.rpc('verifyrawtransaction', [tx_bytes.hex(), prevtxns])
|
||||
ensure(rv['outputs_valid'] is True, 'Invalid outputs')
|
||||
ensure(rv['inputs_valid'] is True, 'Invalid inputs')
|
||||
|
||||
@@ -440,28 +470,28 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
return True
|
||||
|
||||
def getLockTxSwapOutputValue(self, bid, xmr_swap):
|
||||
lock_tx_obj = self.rpc_callback('decoderawtransaction', [xmr_swap.a_lock_tx.hex()])
|
||||
lock_tx_obj = self.rpc('decoderawtransaction', [xmr_swap.a_lock_tx.hex()])
|
||||
nonce = self.getScriptLockTxNonce(xmr_swap.vkbv)
|
||||
output_n, _ = self.findOutputByNonce(lock_tx_obj, nonce)
|
||||
ensure(output_n is not None, 'Output not found in tx')
|
||||
return bytes.fromhex(lock_tx_obj['vout'][output_n]['valueCommitment'])
|
||||
|
||||
def getLockRefundTxSwapOutputValue(self, bid, xmr_swap):
|
||||
lock_refund_tx_obj = self.rpc_callback('decoderawtransaction', [xmr_swap.a_lock_refund_tx.hex()])
|
||||
lock_refund_tx_obj = self.rpc('decoderawtransaction', [xmr_swap.a_lock_refund_tx.hex()])
|
||||
nonce = self.getScriptLockRefundTxNonce(xmr_swap.vkbv)
|
||||
output_n, _ = self.findOutputByNonce(lock_refund_tx_obj, nonce)
|
||||
ensure(output_n is not None, 'Output not found in tx')
|
||||
return bytes.fromhex(lock_refund_tx_obj['vout'][output_n]['valueCommitment'])
|
||||
|
||||
def getLockRefundTxSwapOutput(self, xmr_swap):
|
||||
lock_refund_tx_obj = self.rpc_callback('decoderawtransaction', [xmr_swap.a_lock_refund_tx.hex()])
|
||||
lock_refund_tx_obj = self.rpc('decoderawtransaction', [xmr_swap.a_lock_refund_tx.hex()])
|
||||
nonce = self.getScriptLockRefundTxNonce(xmr_swap.vkbv)
|
||||
output_n, _ = self.findOutputByNonce(lock_refund_tx_obj, nonce)
|
||||
ensure(output_n is not None, 'Output not found in tx')
|
||||
return output_n
|
||||
|
||||
def createSCLockSpendTx(self, tx_lock_bytes, script_lock, pk_dest, tx_fee_rate, vkbv):
|
||||
lock_tx_obj = self.rpc_callback('decoderawtransaction', [tx_lock_bytes.hex()])
|
||||
def createSCLockSpendTx(self, tx_lock_bytes: bytes, script_lock: bytes, pk_dest: bytes, tx_fee_rate: int, vkbv: bytes, fee_info={}) -> bytes:
|
||||
lock_tx_obj = self.rpc('decoderawtransaction', [tx_lock_bytes.hex()])
|
||||
lock_txid_hex = lock_tx_obj['txid']
|
||||
|
||||
ensure(lock_tx_obj['version'] == self.txVersion(), 'Bad version')
|
||||
@@ -477,16 +507,16 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
inputs = [{'txid': lock_txid_hex, 'vout': spend_n, 'sequence': 0, 'blindingfactor': blinded_info['blind']}]
|
||||
outputs = [{'type': 'blind', 'amount': blinded_info['amount'], 'address': addr_out, 'pubkey': pk_dest.hex()}]
|
||||
params = [inputs, outputs]
|
||||
rv = self.rpc_callback('createrawparttransaction', params)
|
||||
rv = self.rpc_wallet('createrawparttransaction', params)
|
||||
lock_spend_tx_hex = rv['hex']
|
||||
|
||||
# Set dummy witness data for fee estimation
|
||||
dummy_witness_stack = self.getScriptLockTxDummyWitness(script_lock)
|
||||
|
||||
# Use a junk change pubkey to avoid adding unused keys to the wallet
|
||||
zero_change_key = i2b(self.getNewSecretKey())
|
||||
zero_change_key = self.getNewSecretKey()
|
||||
zero_change_pubkey = self.getPubkey(zero_change_key)
|
||||
inputs_info = {'0': {'value': blinded_info['amount'], 'blind': blinded_info['blind'], 'witnessstack': dummy_witness_stack}}
|
||||
inputs_info = {'0': {'value': blinded_info['amount'], 'blind': blinded_info['blind'], 'witnessstack': [x.hex() for x in dummy_witness_stack]}}
|
||||
outputs_info = rv['amounts']
|
||||
options = {
|
||||
'changepubkey': zero_change_pubkey.hex(),
|
||||
@@ -494,22 +524,29 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
'subtractFeeFromOutputs': [0, ]
|
||||
}
|
||||
|
||||
rv = self.rpc_callback('fundrawtransactionfrom', ['blind', lock_spend_tx_hex, inputs_info, outputs_info, options])
|
||||
rv = self.rpc_wallet('fundrawtransactionfrom', ['blind', lock_spend_tx_hex, inputs_info, outputs_info, options])
|
||||
lock_spend_tx_hex = rv['hex']
|
||||
lock_spend_tx_obj = self.rpc_callback('decoderawtransaction', [lock_spend_tx_hex])
|
||||
|
||||
vsize = lock_spend_tx_obj['vsize']
|
||||
lock_spend_tx_obj = self.rpc('decoderawtransaction', [lock_spend_tx_hex])
|
||||
pay_fee = make_int(lock_spend_tx_obj['vout'][0]['ct_fee'])
|
||||
|
||||
# lock_spend_tx_hex does not include the dummy witness stack
|
||||
witness_bytes = self.getWitnessStackSerialisedLength(dummy_witness_stack)
|
||||
vsize = self.getTxVSize(self.loadTx(bytes.fromhex(lock_spend_tx_hex)), add_witness_bytes=witness_bytes)
|
||||
actual_tx_fee_rate = pay_fee * 1000 // vsize
|
||||
self._log.info('createSCLockSpendTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.',
|
||||
lock_spend_tx_obj['txid'], actual_tx_fee_rate, vsize, pay_fee)
|
||||
|
||||
fee_info['vsize'] = vsize
|
||||
fee_info['fee_paid'] = pay_fee
|
||||
fee_info['rate_input'] = tx_fee_rate
|
||||
fee_info['rate_actual'] = actual_tx_fee_rate
|
||||
|
||||
return bytes.fromhex(lock_spend_tx_hex)
|
||||
|
||||
def verifySCLockSpendTx(self, tx_bytes,
|
||||
lock_tx_bytes, lock_tx_script,
|
||||
a_pk_f, feerate, vkbv):
|
||||
lock_spend_tx_obj = self.rpc_callback('decoderawtransaction', [tx_bytes.hex()])
|
||||
lock_spend_tx_obj = self.rpc('decoderawtransaction', [tx_bytes.hex()])
|
||||
lock_spend_txid_hex = lock_spend_tx_obj['txid']
|
||||
self._log.info('Verifying lock spend tx: {}.'.format(lock_spend_txid_hex))
|
||||
|
||||
@@ -517,7 +554,7 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
ensure(lock_spend_tx_obj['locktime'] == 0, 'Bad nLockTime')
|
||||
ensure(len(lock_spend_tx_obj['vin']) == 1, 'tx doesn\'t have one input')
|
||||
|
||||
lock_tx_obj = self.rpc_callback('decoderawtransaction', [lock_tx_bytes.hex()])
|
||||
lock_tx_obj = self.rpc('decoderawtransaction', [lock_tx_bytes.hex()])
|
||||
lock_txid_hex = lock_tx_obj['txid']
|
||||
|
||||
# Find the output of the lock tx to verify
|
||||
@@ -533,7 +570,7 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
ensure(len(lock_spend_tx_obj['vout']) == 3, 'tx doesn\'t have three outputs')
|
||||
|
||||
addr_out = self.pubkey_to_address(a_pk_f)
|
||||
privkey = self.rpc_callback('dumpprivkey', [addr_out])
|
||||
privkey = self.rpc_wallet('dumpprivkey', [addr_out])
|
||||
|
||||
# Find output:
|
||||
output_blinded_info = None
|
||||
@@ -542,7 +579,7 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
if txo['type'] != 'blind':
|
||||
continue
|
||||
try:
|
||||
output_blinded_info = self.rpc_callback('rewindrangeproof', [txo['rangeproof'], txo['valueCommitment'], privkey, txo['data_hex']])
|
||||
output_blinded_info = self.rpc('rewindrangeproof', [txo['rangeproof'], txo['valueCommitment'], privkey, txo['data_hex']])
|
||||
output_n = txo['n']
|
||||
break
|
||||
except Exception as e:
|
||||
@@ -551,13 +588,13 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
ensure(output_n is not None, 'Output not found in tx')
|
||||
|
||||
# Commitment
|
||||
v = self.rpc_callback('verifycommitment', [lock_spend_tx_obj['vout'][output_n]['valueCommitment'], output_blinded_info['blind'], output_blinded_info['amount']])
|
||||
v = self.rpc('verifycommitment', [lock_spend_tx_obj['vout'][output_n]['valueCommitment'], output_blinded_info['blind'], output_blinded_info['amount']])
|
||||
ensure(v['result'] is True, 'verifycommitment failed')
|
||||
|
||||
# Check rangeproofs and commitments sum
|
||||
prevout = lock_tx_obj['vout'][spend_n]
|
||||
prevtxns = [{'txid': lock_txid_hex, 'vout': spend_n, 'scriptPubKey': prevout['scriptPubKey']['hex'], 'amount_commitment': prevout['valueCommitment']}]
|
||||
rv = self.rpc_callback('verifyrawtransaction', [tx_bytes.hex(), prevtxns])
|
||||
rv = self.rpc('verifyrawtransaction', [tx_bytes.hex(), prevtxns])
|
||||
ensure(rv['outputs_valid'] is True, 'Invalid outputs')
|
||||
ensure(rv['inputs_valid'] is True, 'Invalid inputs')
|
||||
|
||||
@@ -581,7 +618,7 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
def createSCLockRefundSpendToFTx(self, tx_lock_refund_bytes, script_lock_refund, pkh_dest, tx_fee_rate, vkbv):
|
||||
# lock refund swipe tx
|
||||
# Sends the coinA locked coin to the follower
|
||||
lock_refund_tx_obj = self.rpc_callback('decoderawtransaction', [tx_lock_refund_bytes.hex()])
|
||||
lock_refund_tx_obj = self.rpc('decoderawtransaction', [tx_lock_refund_bytes.hex()])
|
||||
nonce = self.getScriptLockRefundTxNonce(vkbv)
|
||||
|
||||
# Find the output of the lock refund tx to spend
|
||||
@@ -590,7 +627,7 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
|
||||
tx_lock_refund_id = lock_refund_tx_obj['txid']
|
||||
addr_out = self.pkh_to_address(pkh_dest)
|
||||
addr_info = self.rpc_callback('getaddressinfo', [addr_out])
|
||||
addr_info = self.rpc_wallet('getaddressinfo', [addr_out])
|
||||
output_pubkey_hex = addr_info['pubkey']
|
||||
|
||||
A, B, lock2_value, C = self.extractScriptLockRefundScriptValues(script_lock_refund)
|
||||
@@ -600,15 +637,16 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
inputs = [{'txid': tx_lock_refund_id, 'vout': spend_n, 'sequence': lock2_value, 'blindingfactor': input_blinded_info['blind']}]
|
||||
outputs = [{'type': 'blind', 'amount': input_blinded_info['amount'], 'address': addr_out, 'pubkey': output_pubkey_hex}]
|
||||
params = [inputs, outputs]
|
||||
rv = self.rpc_callback('createrawparttransaction', params)
|
||||
rv = self.rpc_wallet('createrawparttransaction', params)
|
||||
|
||||
lock_refund_swipe_tx_hex = rv['hex']
|
||||
|
||||
# Set dummy witness data for fee estimation
|
||||
dummy_witness_stack = self.getScriptLockRefundSwipeTxDummyWitness(script_lock_refund)
|
||||
dummy_witness_stack = [x.hex() for x in dummy_witness_stack]
|
||||
|
||||
# Use a junk change pubkey to avoid adding unused keys to the wallet
|
||||
zero_change_key = i2b(self.getNewSecretKey())
|
||||
zero_change_key = self.getNewSecretKey()
|
||||
zero_change_pubkey = self.getPubkey(zero_change_key)
|
||||
inputs_info = {'0': {'value': input_blinded_info['amount'], 'blind': input_blinded_info['blind'], 'witnessstack': dummy_witness_stack}}
|
||||
outputs_info = rv['amounts']
|
||||
@@ -618,13 +656,129 @@ class PARTInterfaceBlind(PARTInterface):
|
||||
'subtractFeeFromOutputs': [0, ]
|
||||
}
|
||||
|
||||
rv = self.rpc_callback('fundrawtransactionfrom', ['blind', lock_refund_swipe_tx_hex, inputs_info, outputs_info, options])
|
||||
rv = self.rpc_wallet('fundrawtransactionfrom', ['blind', lock_refund_swipe_tx_hex, inputs_info, outputs_info, options])
|
||||
lock_refund_swipe_tx_hex = rv['hex']
|
||||
|
||||
return bytes.fromhex(lock_refund_swipe_tx_hex)
|
||||
|
||||
def getSpendableBalance(self):
|
||||
return self.make_int(self.rpc_callback('getbalances')['mine']['blind_trusted'])
|
||||
def getSpendableBalance(self) -> int:
|
||||
return self.make_int(self.rpc_wallet('getbalances')['mine']['blind_trusted'])
|
||||
|
||||
def publishBLockTx(self, vkbv: bytes, Kbs: bytes, output_amount: int, feerate: int, delay_for: int = 10, unlock_time: int = 0) -> bytes:
|
||||
Kbv = self.getPubkey(vkbv)
|
||||
sx_addr = self.formatStealthAddress(Kbv, Kbs)
|
||||
self._log.debug('sx_addr: {}'.format(sx_addr))
|
||||
|
||||
# TODO: Fund from other balances
|
||||
params = ['blind', 'blind',
|
||||
[{'address': sx_addr, 'amount': self.format_amount(output_amount)}, ],
|
||||
'', '', self._anon_tx_ring_size, 1, False,
|
||||
{'conf_target': self._conf_target, 'blind_watchonly_visible': True}]
|
||||
|
||||
txid = self.rpc_wallet('sendtypeto', params)
|
||||
return bytes.fromhex(txid)
|
||||
|
||||
def findTxB(self, kbv, Kbs, cb_swap_value, cb_block_confirmed, restore_height: int, bid_sender: bool):
|
||||
Kbv = self.getPubkey(kbv)
|
||||
sx_addr = self.formatStealthAddress(Kbv, Kbs)
|
||||
|
||||
# Tx recipient must import the stealth address as watch only
|
||||
if bid_sender:
|
||||
cb_swap_value *= -1
|
||||
else:
|
||||
addr_info = self.rpc_wallet('getaddressinfo', [sx_addr])
|
||||
if not addr_info['iswatchonly']:
|
||||
wif_prefix = self.chainparams_network()['key_prefix']
|
||||
wif_scan_key = toWIF(wif_prefix, kbv)
|
||||
self.rpc_wallet('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(self.coin_name(), restore_height))
|
||||
self.rpc_wallet('rescanblockchain', [restore_height])
|
||||
|
||||
params = [{'include_watchonly': True, 'search': sx_addr}]
|
||||
txns = self.rpc_wallet('filtertransactions', params)
|
||||
|
||||
if len(txns) == 1:
|
||||
tx = txns[0]
|
||||
assert (tx['outputs'][0]['stealth_address'] == sx_addr) # Should not be possible
|
||||
ensure(tx['outputs'][0]['type'] == 'blind', 'Output is not anon')
|
||||
|
||||
if make_int(tx['outputs'][0]['amount']) == cb_swap_value:
|
||||
height = 0
|
||||
if tx['confirmations'] > 0:
|
||||
chain_height = self.rpc('getblockcount')
|
||||
height = chain_height - (tx['confirmations'] - 1)
|
||||
return {'txid': tx['txid'], 'amount': cb_swap_value, 'height': height}
|
||||
else:
|
||||
self._log.warning('Incorrect amount detected for coin b lock txn: {}'.format(tx['txid']))
|
||||
return -1
|
||||
return None
|
||||
|
||||
def spendBLockTx(self, chain_b_lock_txid: bytes, address_to: str, kbv: bytes, kbs: bytes, cb_swap_value: int, b_fee: int, restore_height: int, spend_actual_balance: bool = False) -> bytes:
|
||||
Kbv = self.getPubkey(kbv)
|
||||
Kbs = self.getPubkey(kbs)
|
||||
sx_addr = self.formatStealthAddress(Kbv, Kbs)
|
||||
addr_info = self.rpc_wallet('getaddressinfo', [sx_addr])
|
||||
if not addr_info['ismine']:
|
||||
wif_prefix = self.chainparams_network()['key_prefix']
|
||||
wif_scan_key = toWIF(wif_prefix, kbv)
|
||||
wif_spend_key = toWIF(wif_prefix, kbs)
|
||||
self.rpc_wallet('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(self.coin_name(), restore_height))
|
||||
self.rpc_wallet('rescanblockchain', [restore_height])
|
||||
|
||||
# TODO: Remove workaround
|
||||
# utxos = self.rpc_wallet('listunspentblind', [1, 9999999, [sx_addr]])
|
||||
utxos = []
|
||||
all_utxos = self.rpc_wallet('listunspentblind', [1, 9999999])
|
||||
for utxo in all_utxos:
|
||||
if utxo.get('stealth_address', '_') == sx_addr:
|
||||
utxos.append(utxo)
|
||||
if len(utxos) < 1:
|
||||
raise TemporaryError('No spendable outputs')
|
||||
elif len(utxos) > 1:
|
||||
raise ValueError('Too many spendable outputs')
|
||||
|
||||
utxo = utxos[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
|
||||
|
||||
inputs = [{'tx': utxo['txid'], 'n': utxo['vout']}, ]
|
||||
params = ['blind', 'blind',
|
||||
[{'address': address_to, 'amount': self.format_amount(cb_swap_value), 'subfee': True}, ],
|
||||
'', '', self._anon_tx_ring_size, 1, False,
|
||||
{'conf_target': self._conf_target, 'inputs': inputs, 'show_fee': True}]
|
||||
rv = self.rpc_wallet('sendtypeto', params)
|
||||
return bytes.fromhex(rv['txid'])
|
||||
|
||||
def findTxnByHash(self, txid_hex):
|
||||
# txindex is enabled for Particl
|
||||
|
||||
try:
|
||||
rv = self.rpc('getrawtransaction', [txid_hex, True])
|
||||
except Exception as ex:
|
||||
self._log.debug('findTxnByHash getrawtransaction failed: {}'.format(txid_hex))
|
||||
return None
|
||||
|
||||
if 'confirmations' in rv and rv['confirmations'] >= self.blocks_confirmed:
|
||||
return {'txid': txid_hex, 'amount': 0, 'height': rv['height']}
|
||||
|
||||
return None
|
||||
|
||||
def createRawFundedTransaction(self, addr_to: str, amount: int, sub_fee: bool = False, lock_unspents: bool = True) -> str:
|
||||
txn = self.rpc_wallet('createrawtransaction', [[], {addr_to: self.format_amount(amount)}])
|
||||
|
||||
options = {
|
||||
'lockUnspents': lock_unspents,
|
||||
'conf_target': self._conf_target,
|
||||
}
|
||||
if sub_fee:
|
||||
options['subtractFeeFromOutputs'] = [0,]
|
||||
return self.rpc_wallet('fundrawtransactionfrom', ['blind', txn, options])['hex']
|
||||
|
||||
|
||||
class PARTInterfaceAnon(PARTInterface):
|
||||
@@ -632,16 +786,25 @@ class PARTInterfaceAnon(PARTInterface):
|
||||
def balance_type():
|
||||
return BalanceTypes.ANON
|
||||
|
||||
@staticmethod
|
||||
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
||||
raise ValueError('Not possible')
|
||||
|
||||
@staticmethod
|
||||
def xmr_swap_b_lock_spend_tx_vsize() -> int:
|
||||
# TODO: Estimate with ringsize
|
||||
return 1153
|
||||
|
||||
@staticmethod
|
||||
def depth_spendable() -> int:
|
||||
return 12
|
||||
|
||||
def coin_name(self):
|
||||
def coin_name(self) -> str:
|
||||
return super().coin_name() + ' Anon'
|
||||
|
||||
def publishBLockTx(self, Kbv, Kbs, output_amount, feerate):
|
||||
def publishBLockTx(self, kbv: bytes, Kbs: bytes, output_amount: int, feerate: int, delay_for: int = 10, unlock_time: int = 0) -> bytes:
|
||||
Kbv = self.getPubkey(kbv)
|
||||
sx_addr = self.formatStealthAddress(Kbv, Kbs)
|
||||
self._log.debug('sx_addr: {}'.format(sx_addr))
|
||||
|
||||
# TODO: Fund from other balances
|
||||
params = ['anon', 'anon',
|
||||
@@ -649,7 +812,7 @@ class PARTInterfaceAnon(PARTInterface):
|
||||
'', '', self._anon_tx_ring_size, 1, False,
|
||||
{'conf_target': self._conf_target, 'blind_watchonly_visible': True}]
|
||||
|
||||
txid = self.rpc_callback('sendtypeto', params)
|
||||
txid = self.rpc_wallet('sendtypeto', params)
|
||||
return bytes.fromhex(txid)
|
||||
|
||||
def findTxB(self, kbv, Kbs, cb_swap_value, cb_block_confirmed, restore_height, bid_sender):
|
||||
@@ -661,17 +824,17 @@ class PARTInterfaceAnon(PARTInterface):
|
||||
if bid_sender:
|
||||
cb_swap_value *= -1
|
||||
else:
|
||||
addr_info = self.rpc_callback('getaddressinfo', [sx_addr])
|
||||
addr_info = self.rpc_wallet('getaddressinfo', [sx_addr])
|
||||
if not addr_info['iswatchonly']:
|
||||
wif_prefix = self.chainparams_network()['key_prefix']
|
||||
wif_scan_key = toWIF(wif_prefix, kbv)
|
||||
self.rpc_callback('importstealthaddress', [wif_scan_key, Kbs.hex()])
|
||||
self.rpc_wallet('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(self.coin_name(), restore_height))
|
||||
self.rpc_callback('rescanblockchain', [restore_height])
|
||||
self.rpc_wallet('rescanblockchain', [restore_height])
|
||||
|
||||
params = [{'include_watchonly': True, 'search': sx_addr}]
|
||||
txns = self.rpc_callback('filtertransactions', params)
|
||||
txns = self.rpc_wallet('filtertransactions', params)
|
||||
|
||||
if len(txns) == 1:
|
||||
tx = txns[0]
|
||||
@@ -681,7 +844,7 @@ class PARTInterfaceAnon(PARTInterface):
|
||||
if make_int(tx['outputs'][0]['amount']) == cb_swap_value:
|
||||
height = 0
|
||||
if tx['confirmations'] > 0:
|
||||
chain_height = self.rpc_callback('getblockcount')
|
||||
chain_height = self.rpc('getblockcount')
|
||||
height = chain_height - (tx['confirmations'] - 1)
|
||||
return {'txid': tx['txid'], 'amount': cb_swap_value, 'height': height}
|
||||
else:
|
||||
@@ -689,21 +852,21 @@ class PARTInterfaceAnon(PARTInterface):
|
||||
return -1
|
||||
return None
|
||||
|
||||
def spendBLockTx(self, chain_b_lock_txid, address_to, kbv, kbs, cb_swap_value, b_fee, restore_height, spend_actual_balance=False):
|
||||
def spendBLockTx(self, chain_b_lock_txid: bytes, address_to: str, kbv: bytes, kbs: bytes, cb_swap_value: int, b_fee: int, restore_height: int, spend_actual_balance: bool = False) -> bytes:
|
||||
Kbv = self.getPubkey(kbv)
|
||||
Kbs = self.getPubkey(kbs)
|
||||
sx_addr = self.formatStealthAddress(Kbv, Kbs)
|
||||
addr_info = self.rpc_callback('getaddressinfo', [sx_addr])
|
||||
addr_info = self.rpc_wallet('getaddressinfo', [sx_addr])
|
||||
if not addr_info['ismine']:
|
||||
wif_prefix = self.chainparams_network()['key_prefix']
|
||||
wif_scan_key = toWIF(wif_prefix, kbv)
|
||||
wif_spend_key = toWIF(wif_prefix, kbs)
|
||||
self.rpc_callback('importstealthaddress', [wif_scan_key, wif_spend_key])
|
||||
self.rpc_wallet('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(self.coin_name(), restore_height))
|
||||
self.rpc_callback('rescanblockchain', [restore_height])
|
||||
self.rpc_wallet('rescanblockchain', [restore_height])
|
||||
|
||||
autxos = self.rpc_callback('listunspentanon', [1, 9999999, [sx_addr]])
|
||||
autxos = self.rpc_wallet('listunspentanon', [1, 9999999, [sx_addr]])
|
||||
|
||||
if len(autxos) < 1:
|
||||
raise TemporaryError('No spendable outputs')
|
||||
@@ -722,14 +885,14 @@ class PARTInterfaceAnon(PARTInterface):
|
||||
[{'address': address_to, 'amount': self.format_amount(cb_swap_value), 'subfee': True}, ],
|
||||
'', '', self._anon_tx_ring_size, 1, False,
|
||||
{'conf_target': self._conf_target, 'inputs': inputs, 'show_fee': True}]
|
||||
rv = self.rpc_callback('sendtypeto', params)
|
||||
rv = self.rpc_wallet('sendtypeto', params)
|
||||
return bytes.fromhex(rv['txid'])
|
||||
|
||||
def findTxnByHash(self, txid_hex):
|
||||
def findTxnByHash(self, txid_hex: str):
|
||||
# txindex is enabled for Particl
|
||||
|
||||
try:
|
||||
rv = self.rpc_callback('getrawtransaction', [txid_hex, True])
|
||||
rv = self.rpc('getrawtransaction', [txid_hex, True])
|
||||
except Exception as ex:
|
||||
self._log.debug('findTxnByHash getrawtransaction failed: {}'.format(txid_hex))
|
||||
return None
|
||||
@@ -739,5 +902,5 @@ class PARTInterfaceAnon(PARTInterface):
|
||||
|
||||
return None
|
||||
|
||||
def getSpendableBalance(self):
|
||||
return self.make_int(self.rpc_callback('getbalances')['mine']['anon_trusted'])
|
||||
def getSpendableBalance(self) -> int:
|
||||
return self.make_int(self.rpc_wallet('getbalances')['mine']['anon_trusted'])
|
||||
|
||||
@@ -1,16 +1,28 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2020 tecnovert
|
||||
# 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 io import BytesIO
|
||||
|
||||
from .btc import BTCInterface
|
||||
from basicswap.rpc import make_rpc_func
|
||||
from basicswap.chainparams import Coins
|
||||
from basicswap.util.address import decodeAddress
|
||||
from .contrib.pivx_test_framework.messages import (
|
||||
CBlock,
|
||||
ToHex,
|
||||
FromHex)
|
||||
FromHex,
|
||||
CTransaction)
|
||||
from basicswap.contrib.test_framework.script import (
|
||||
CScript,
|
||||
OP_DUP,
|
||||
OP_HASH160,
|
||||
OP_CHECKSIG,
|
||||
OP_EQUALVERIFY,
|
||||
)
|
||||
|
||||
|
||||
class PIVXInterface(BTCInterface):
|
||||
@@ -18,30 +30,47 @@ class PIVXInterface(BTCInterface):
|
||||
def coin_type():
|
||||
return Coins.PIVX
|
||||
|
||||
def createRawSignedTransaction(self, addr_to, amount):
|
||||
txn = self.rpc_callback('createrawtransaction', [[], {addr_to: self.format_amount(amount)}])
|
||||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
super(PIVXInterface, self).__init__(coin_settings, network, swap_client)
|
||||
# No multiwallet support
|
||||
self.rpc_wallet = make_rpc_func(self._rpcport, self._rpcauth, host=self._rpc_host)
|
||||
|
||||
def checkWallets(self) -> int:
|
||||
return 1
|
||||
|
||||
def signTxWithWallet(self, tx):
|
||||
rv = self.rpc('signrawtransaction', [tx.hex()])
|
||||
return bytes.fromhex(rv['hex'])
|
||||
|
||||
def createRawFundedTransaction(self, addr_to: str, amount: int, sub_fee: bool = False, lock_unspents: bool = True) -> str:
|
||||
txn = self.rpc('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,
|
||||
'lockUnspents': lock_unspents,
|
||||
'feeRate': fee_rate,
|
||||
}
|
||||
txn_funded = self.rpc_callback('fundrawtransaction', [txn, options])['hex']
|
||||
txn_signed = self.rpc_callback('signrawtransaction', [txn_funded])['hex']
|
||||
return txn_signed
|
||||
if sub_fee:
|
||||
options['subtractFeeFromOutputs'] = [0,]
|
||||
return self.rpc('fundrawtransaction', [txn, options])['hex']
|
||||
|
||||
def createRawSignedTransaction(self, addr_to, amount) -> str:
|
||||
txn_funded = self.createRawFundedTransaction(addr_to, amount)
|
||||
return self.rpc('signrawtransaction', [txn_funded])['hex']
|
||||
|
||||
def decodeAddress(self, address):
|
||||
return decodeAddress(address)[1:]
|
||||
|
||||
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])
|
||||
block = self.rpc('getblock', [block_hash, False])
|
||||
block_header = self.rpc('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_dec = self.rpc('decoderawtransaction', [ToHex(tx)])
|
||||
tx_rv.append(tx_dec)
|
||||
|
||||
block_rv = {
|
||||
@@ -57,4 +86,41 @@ class PIVXInterface(BTCInterface):
|
||||
|
||||
def withdrawCoin(self, value, addr_to, subfee):
|
||||
params = [addr_to, value, '', '', subfee]
|
||||
return self.rpc_callback('sendtoaddress', params)
|
||||
return self.rpc('sendtoaddress', params)
|
||||
|
||||
def getSpendableBalance(self) -> int:
|
||||
return self.make_int(self.rpc('getwalletinfo')['balance'])
|
||||
|
||||
def loadTx(self, tx_bytes):
|
||||
# Load tx from bytes to internal representation
|
||||
tx = CTransaction()
|
||||
tx.deserialize(BytesIO(tx_bytes))
|
||||
return tx
|
||||
|
||||
def getScriptForPubkeyHash(self, pkh: bytes) -> bytearray:
|
||||
# Return P2PKH
|
||||
return CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG])
|
||||
|
||||
def getBLockSpendTxFee(self, tx, fee_rate: int) -> int:
|
||||
add_bytes = 107
|
||||
size = len(tx.serialize_with_witness()) + add_bytes
|
||||
pay_fee = round(fee_rate * size / 1000)
|
||||
self._log.info(f'BLockSpendTx fee_rate, size, fee: {fee_rate}, {size}, {pay_fee}.')
|
||||
return pay_fee
|
||||
|
||||
def signTxWithKey(self, tx: bytes, key: bytes) -> bytes:
|
||||
key_wif = self.encodeKey(key)
|
||||
rv = self.rpc('signrawtransaction', [tx.hex(), [], [key_wif, ]])
|
||||
return bytes.fromhex(rv['hex'])
|
||||
|
||||
def findTxnByHash(self, txid_hex: str):
|
||||
# Only works for wallet txns
|
||||
try:
|
||||
rv = self.rpc('gettransaction', [txid_hex])
|
||||
except Exception as ex:
|
||||
self._log.debug('findTxnByHash getrawtransaction failed: {}'.format(txid_hex))
|
||||
return None
|
||||
if 'confirmations' in rv and rv['confirmations'] >= self.blocks_confirmed:
|
||||
block_height = self.getBlockHeader(rv['blockhash'])['height']
|
||||
return {'txid': txid_hex, 'amount': 0, 'height': block_height}
|
||||
return None
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2020-2022 tecnovert
|
||||
# Copyright (c) 2020-2024 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
@@ -24,21 +24,27 @@ from coincurve.dleag import (
|
||||
verify_ed25519_point,
|
||||
)
|
||||
|
||||
from basicswap.interface import (
|
||||
Curves)
|
||||
from basicswap.util import (
|
||||
i2b, b2i, b2h,
|
||||
dumpj,
|
||||
ensure,
|
||||
make_int,
|
||||
TemporaryError)
|
||||
from basicswap.util.network import (
|
||||
is_private_ip_address)
|
||||
from basicswap.rpc_xmr import (
|
||||
make_xmr_rpc_func,
|
||||
make_xmr_rpc2_func,
|
||||
make_xmr_wallet_rpc_func)
|
||||
from basicswap.util import (
|
||||
b2i, b2h)
|
||||
make_xmr_rpc2_func)
|
||||
from basicswap.chainparams import XMR_COIN, CoinInterface, Coins
|
||||
|
||||
|
||||
class XMRInterface(CoinInterface):
|
||||
@staticmethod
|
||||
def curve_type():
|
||||
return Curves.ed25519
|
||||
|
||||
@staticmethod
|
||||
def coin_type():
|
||||
return Coins.XMR
|
||||
@@ -63,17 +69,65 @@ class XMRInterface(CoinInterface):
|
||||
def depth_spendable() -> int:
|
||||
return 10
|
||||
|
||||
@staticmethod
|
||||
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
||||
raise ValueError('Not possible')
|
||||
|
||||
@staticmethod
|
||||
def xmr_swap_b_lock_spend_tx_vsize() -> int:
|
||||
# TODO: Estimate with ringsize
|
||||
return 1507
|
||||
|
||||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
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'], 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)
|
||||
self.setFeePriority(coin_settings.get('fee_priority', 0))
|
||||
self._sc = swap_client
|
||||
self._log = self._sc.log if self._sc and self._sc.log else logging
|
||||
self._wallet_password = None
|
||||
self._have_checked_seed = False
|
||||
|
||||
daemon_login = None
|
||||
if coin_settings.get('rpcuser', '') != '':
|
||||
daemon_login = (coin_settings.get('rpcuser', ''), coin_settings.get('rpcpassword', ''))
|
||||
|
||||
rpchost = coin_settings.get('rpchost', '127.0.0.1')
|
||||
proxy_host = None
|
||||
proxy_port = None
|
||||
# Connect to the daemon over a proxy if not running locally
|
||||
if swap_client:
|
||||
chain_client_settings = swap_client.getChainClientSettings(self.coin_type())
|
||||
manage_daemon: bool = chain_client_settings['manage_daemon']
|
||||
if swap_client.use_tor_proxy:
|
||||
if manage_daemon is False:
|
||||
log_str: str = ''
|
||||
have_cc_tor_opt = 'use_tor' in chain_client_settings
|
||||
if have_cc_tor_opt and chain_client_settings['use_tor'] is False:
|
||||
log_str = ' bypassing proxy (use_tor false for XMR)'
|
||||
elif have_cc_tor_opt is False and is_private_ip_address(rpchost):
|
||||
log_str = ' bypassing proxy (private ip address)'
|
||||
else:
|
||||
proxy_host = swap_client.tor_proxy_host
|
||||
proxy_port = swap_client.tor_proxy_port
|
||||
log_str = f' through proxy at {proxy_host}'
|
||||
self._log.info(f'Connecting to remote {self.coin_name()} daemon at {rpchost}{log_str}.')
|
||||
else:
|
||||
self._log.info(f'Not connecting to local {self.coin_name()} daemon through proxy.')
|
||||
elif manage_daemon is False:
|
||||
self._log.info(f'Connecting to remote {self.coin_name()} daemon at {rpchost}.')
|
||||
|
||||
self._rpctimeout = coin_settings.get('rpctimeout', 60)
|
||||
self._walletrpctimeout = coin_settings.get('walletrpctimeout', 120)
|
||||
self._walletrpctimeoutlong = coin_settings.get('walletrpctimeoutlong', 600)
|
||||
|
||||
self.rpc = make_xmr_rpc_func(coin_settings['rpcport'], daemon_login, host=rpchost, proxy_host=proxy_host, proxy_port=proxy_port, default_timeout=self._rpctimeout, tag='Node(j) ')
|
||||
self.rpc2 = make_xmr_rpc2_func(coin_settings['rpcport'], daemon_login, host=rpchost, proxy_host=proxy_host, proxy_port=proxy_port, default_timeout=self._rpctimeout, tag='Node ') # non-json endpoint
|
||||
self.rpc_wallet = make_xmr_rpc_func(coin_settings['walletrpcport'], coin_settings['walletrpcauth'], host=coin_settings.get('walletrpchost', '127.0.0.1'), default_timeout=self._walletrpctimeout, tag='Wallet ')
|
||||
|
||||
def checkWallets(self) -> int:
|
||||
return 1
|
||||
|
||||
def setFeePriority(self, new_priority):
|
||||
ensure(new_priority >= 0 and new_priority < 4, 'Invalid fee_priority value')
|
||||
@@ -82,10 +136,28 @@ class XMRInterface(CoinInterface):
|
||||
def setWalletFilename(self, wallet_filename):
|
||||
self._wallet_filename = wallet_filename
|
||||
|
||||
def createWallet(self, params):
|
||||
if self._wallet_password is not None:
|
||||
params['password'] = self._wallet_password
|
||||
rv = self.rpc_wallet('generate_from_keys', params)
|
||||
self._log.info('generate_from_keys %s', dumpj(rv))
|
||||
|
||||
def openWallet(self, filename):
|
||||
params = {'filename': filename}
|
||||
if self._wallet_password is not None:
|
||||
params['password'] = self._wallet_password
|
||||
|
||||
try:
|
||||
# Can't reopen the same wallet in windows, !is_keys_file_locked()
|
||||
self.rpc_wallet('close_wallet')
|
||||
except Exception:
|
||||
pass
|
||||
self.rpc_wallet('open_wallet', params)
|
||||
|
||||
def initialiseWallet(self, key_view, key_spend, restore_height=None):
|
||||
with self._mx_wallet:
|
||||
try:
|
||||
self.rpc_wallet_cb('open_wallet', {'filename': self._wallet_filename})
|
||||
self.openWallet(self._wallet_filename)
|
||||
# TODO: Check address
|
||||
return # Wallet exists
|
||||
except Exception as e:
|
||||
@@ -102,22 +174,21 @@ class XMRInterface(CoinInterface):
|
||||
'spendkey': b2h(key_spend[::-1]),
|
||||
'restore_height': self._restore_height,
|
||||
}
|
||||
rv = self.rpc_wallet_cb('generate_from_keys', params)
|
||||
self._log.info('generate_from_keys %s', dumpj(rv))
|
||||
self.rpc_wallet_cb('open_wallet', {'filename': self._wallet_filename})
|
||||
self.createWallet(params)
|
||||
self.openWallet(self._wallet_filename)
|
||||
|
||||
def ensureWalletExists(self):
|
||||
def ensureWalletExists(self) -> None:
|
||||
with self._mx_wallet:
|
||||
self.rpc_wallet_cb('open_wallet', {'filename': self._wallet_filename})
|
||||
self.openWallet(self._wallet_filename)
|
||||
|
||||
def testDaemonRPC(self, with_wallet=True):
|
||||
self.rpc_wallet_cb('get_languages')
|
||||
def testDaemonRPC(self, with_wallet=True) -> None:
|
||||
self.rpc_wallet('get_languages')
|
||||
|
||||
def getDaemonVersion(self):
|
||||
return self.rpc_wallet_cb('get_version')['version']
|
||||
return self.rpc_wallet('get_version')['version']
|
||||
|
||||
def getBlockchainInfo(self):
|
||||
get_height = self.rpc_cb2('get_height', timeout=30)
|
||||
get_height = self.rpc2('get_height', timeout=self._rpctimeout)
|
||||
rv = {
|
||||
'blocks': get_height['height'],
|
||||
'verificationprogress': 0.0,
|
||||
@@ -128,7 +199,7 @@ class XMRInterface(CoinInterface):
|
||||
# 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)
|
||||
get_info = self.rpc2('get_info', timeout=self._rpctimeout)
|
||||
if 'height_without_bootstrap' in get_info:
|
||||
rv['blocks'] = get_info['height_without_bootstrap']
|
||||
|
||||
@@ -136,7 +207,7 @@ class XMRInterface(CoinInterface):
|
||||
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['known_block_count'] = self.rpc('get_block_count', timeout=self._rpctimeout)['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))
|
||||
@@ -145,48 +216,60 @@ class XMRInterface(CoinInterface):
|
||||
return rv
|
||||
|
||||
def getChainHeight(self):
|
||||
return self.rpc_cb2('get_height', timeout=30)['height']
|
||||
return self.rpc2('get_height', timeout=self._rpctimeout)['height']
|
||||
|
||||
def getWalletInfo(self):
|
||||
with self._mx_wallet:
|
||||
self.rpc_wallet_cb('open_wallet', {'filename': self._wallet_filename})
|
||||
try:
|
||||
self.openWallet(self._wallet_filename)
|
||||
except Exception as e:
|
||||
if 'Failed to open wallet' in str(e):
|
||||
rv = {'encrypted': True, 'locked': True, 'balance': 0, 'unconfirmed_balance': 0}
|
||||
return rv
|
||||
raise e
|
||||
|
||||
rv = {}
|
||||
self.rpc_wallet_cb('refresh')
|
||||
balance_info = self.rpc_wallet_cb('get_balance')
|
||||
self.rpc_wallet('refresh')
|
||||
balance_info = self.rpc_wallet('get_balance')
|
||||
rv['balance'] = self.format_amount(balance_info['unlocked_balance'])
|
||||
rv['unconfirmed_balance'] = self.format_amount(balance_info['balance'] - balance_info['unlocked_balance'])
|
||||
rv['encrypted'] = False if self._wallet_password is None else True
|
||||
rv['locked'] = False
|
||||
return rv
|
||||
|
||||
def walletRestoreHeight(self):
|
||||
return self._restore_height
|
||||
|
||||
def getMainWalletAddress(self):
|
||||
def getMainWalletAddress(self) -> str:
|
||||
with self._mx_wallet:
|
||||
self.rpc_wallet_cb('open_wallet', {'filename': self._wallet_filename})
|
||||
return self.rpc_wallet_cb('get_address')['address']
|
||||
self.openWallet(self._wallet_filename)
|
||||
return self.rpc_wallet('get_address')['address']
|
||||
|
||||
def getNewAddress(self, placeholder):
|
||||
def getNewAddress(self, placeholder) -> str:
|
||||
with self._mx_wallet:
|
||||
self.rpc_wallet_cb('open_wallet', {'filename': self._wallet_filename})
|
||||
return self.rpc_wallet_cb('create_address', {'account_index': 0})['address']
|
||||
self.openWallet(self._wallet_filename)
|
||||
new_address = self.rpc_wallet('create_address', {'account_index': 0})['address']
|
||||
self.rpc_wallet('store')
|
||||
return new_address
|
||||
|
||||
def get_fee_rate(self, conf_target=2):
|
||||
self._log.warning('TODO - estimate fee rate?')
|
||||
def get_fee_rate(self, conf_target: int = 2):
|
||||
self._log.warning('TODO - estimate XMR fee rate?')
|
||||
return 0.0, 'unused'
|
||||
|
||||
def getNewSecretKey(self):
|
||||
return edu.get_secret()
|
||||
def getNewSecretKey(self) -> bytes:
|
||||
# Note: Returned bytes are in big endian order
|
||||
return i2b(edu.get_secret())
|
||||
|
||||
def pubkey(self, key):
|
||||
def pubkey(self, key: bytes) -> bytes:
|
||||
return edf.scalarmult_B(key)
|
||||
|
||||
def encodeKey(self, vk):
|
||||
def encodeKey(self, vk: bytes) -> str:
|
||||
return vk[::-1].hex()
|
||||
|
||||
def decodeKey(self, k_hex):
|
||||
def decodeKey(self, k_hex: str) -> bytes:
|
||||
return bytes.fromhex(k_hex)[::-1]
|
||||
|
||||
def encodePubkey(self, pk):
|
||||
def encodePubkey(self, pk: bytes) -> str:
|
||||
return edu.encodepoint(pk)
|
||||
|
||||
def decodePubkey(self, pke):
|
||||
@@ -195,12 +278,12 @@ class XMRInterface(CoinInterface):
|
||||
def getPubkey(self, privkey):
|
||||
return ed25519_get_pubkey(privkey)
|
||||
|
||||
def getAddressFromKeys(self, key_view, key_spend):
|
||||
def getAddressFromKeys(self, key_view: bytes, key_spend: bytes) -> str:
|
||||
pk_view = self.getPubkey(key_view)
|
||||
pk_spend = self.getPubkey(key_spend)
|
||||
return xmr_util.encode_address(pk_view, pk_spend)
|
||||
|
||||
def verifyKey(self, k):
|
||||
def verifyKey(self, k: int) -> bool:
|
||||
i = b2i(k)
|
||||
return (i < edf.l and i > 8)
|
||||
|
||||
@@ -209,43 +292,45 @@ class XMRInterface(CoinInterface):
|
||||
# Checks for small order
|
||||
return verify_ed25519_point(pubkey_bytes)
|
||||
|
||||
def proveDLEAG(self, key):
|
||||
def proveDLEAG(self, key: bytes) -> bytes:
|
||||
privkey = PrivateKey(key)
|
||||
return dleag_prove(privkey)
|
||||
|
||||
def verifyDLEAG(self, dleag_bytes):
|
||||
def verifyDLEAG(self, dleag_bytes: bytes) -> bool:
|
||||
return dleag_verify(dleag_bytes)
|
||||
|
||||
def lengthDLEAG(self):
|
||||
def lengthDLEAG(self) -> int:
|
||||
return dleag_proof_len()
|
||||
|
||||
def sumKeys(self, ka, kb):
|
||||
def sumKeys(self, ka: bytes, kb: bytes) -> bytes:
|
||||
return ed25519_scalar_add(ka, kb)
|
||||
|
||||
def sumPubkeys(self, Ka, Kb):
|
||||
def sumPubkeys(self, Ka: bytes, Kb: bytes) -> bytes:
|
||||
return ed25519_add(Ka, Kb)
|
||||
|
||||
def encodeSharedAddress(self, Kbv, Kbs):
|
||||
def encodeSharedAddress(self, Kbv: bytes, Kbs: bytes) -> str:
|
||||
return xmr_util.encode_address(Kbv, Kbs)
|
||||
|
||||
def publishBLockTx(self, Kbv, Kbs, output_amount, feerate, delay_for=10):
|
||||
def publishBLockTx(self, kbv: bytes, Kbs: bytes, output_amount: int, feerate: int, delay_for: int = 10, unlock_time: int = 0) -> bytes:
|
||||
with self._mx_wallet:
|
||||
self.rpc_wallet_cb('open_wallet', {'filename': self._wallet_filename})
|
||||
self.openWallet(self._wallet_filename)
|
||||
self.rpc_wallet('refresh')
|
||||
|
||||
Kbv = self.getPubkey(kbv)
|
||||
shared_addr = xmr_util.encode_address(Kbv, Kbs)
|
||||
|
||||
params = {'destinations': [{'amount': output_amount, 'address': shared_addr}]}
|
||||
params = {'destinations': [{'amount': output_amount, 'address': shared_addr}], 'unlock_time': unlock_time}
|
||||
if self._fee_priority > 0:
|
||||
params['priority'] = self._fee_priority
|
||||
rv = self.rpc_wallet_cb('transfer', params)
|
||||
rv = self.rpc_wallet('transfer', params)
|
||||
self._log.info('publishBLockTx %s to address_b58 %s', rv['tx_hash'], shared_addr)
|
||||
tx_hash = bytes.fromhex(rv['tx_hash'])
|
||||
|
||||
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)
|
||||
gt_params = {'out': True, 'pending': True, 'failed': True, 'pool': True, }
|
||||
rv = self.rpc_wallet('get_transfers', gt_params)
|
||||
self._log.debug('get_transfers {}'.format(dumpj(rv)))
|
||||
if 'pending' not in rv:
|
||||
break
|
||||
@@ -260,11 +345,6 @@ class XMRInterface(CoinInterface):
|
||||
Kbv = self.getPubkey(kbv)
|
||||
address_b58 = xmr_util.encode_address(Kbv, Kbs)
|
||||
|
||||
try:
|
||||
self.rpc_wallet_cb('close_wallet')
|
||||
except Exception as e:
|
||||
self._log.warning('close_wallet failed %s', str(e))
|
||||
|
||||
kbv_le = kbv[::-1]
|
||||
params = {
|
||||
'restore_height': restore_height,
|
||||
@@ -274,111 +354,57 @@ class XMRInterface(CoinInterface):
|
||||
}
|
||||
|
||||
try:
|
||||
rv = self.rpc_wallet_cb('open_wallet', {'filename': address_b58})
|
||||
self.openWallet(address_b58)
|
||||
except Exception as e:
|
||||
rv = self.rpc_wallet_cb('generate_from_keys', params)
|
||||
self._log.info('generate_from_keys %s', dumpj(rv))
|
||||
rv = self.rpc_wallet_cb('open_wallet', {'filename': address_b58})
|
||||
self.createWallet(params)
|
||||
self.openWallet(address_b58)
|
||||
|
||||
self.rpc_wallet_cb('refresh', timeout=600)
|
||||
self.rpc_wallet('refresh', timeout=self._walletrpctimeoutlong)
|
||||
|
||||
'''
|
||||
# Debug
|
||||
try:
|
||||
current_height = self.rpc_wallet_cb('get_height')['height']
|
||||
current_height = self.rpc_wallet('get_height')['height']
|
||||
self._log.info('findTxB XMR current_height %d\nAddress: %s', current_height, address_b58)
|
||||
except Exception as e:
|
||||
self._log.info('rpc_cb failed %s', str(e))
|
||||
self._log.info('rpc failed %s', str(e))
|
||||
current_height = None # If the transfer is available it will be deep enough
|
||||
# and (current_height is None or current_height - transfer['block_height'] > cb_block_confirmed):
|
||||
'''
|
||||
params = {'transfer_type': 'available'}
|
||||
rv = self.rpc_wallet_cb('incoming_transfers', params)
|
||||
if 'transfers' in rv:
|
||||
for transfer in rv['transfers']:
|
||||
transfers = self.rpc_wallet('incoming_transfers', params)
|
||||
rv = None
|
||||
if 'transfers' in transfers:
|
||||
for transfer in transfers['transfers']:
|
||||
# unlocked <- wallet->is_transfer_unlocked() checks unlock_time and CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE
|
||||
if not transfer['unlocked']:
|
||||
full_tx = self.rpc_wallet('get_transfer_by_txid', {'txid': transfer['tx_hash']})
|
||||
unlock_time = full_tx['transfer']['unlock_time']
|
||||
if unlock_time != 0:
|
||||
self._log.warning('Coin b lock txn is locked: {}, unlock_time {}'.format(transfer['tx_hash'], unlock_time))
|
||||
rv = -1
|
||||
continue
|
||||
if transfer['amount'] == cb_swap_value:
|
||||
return {'txid': transfer['tx_hash'], 'amount': transfer['amount'], 'height': 0 if 'block_height' not in transfer else transfer['block_height']}
|
||||
else:
|
||||
self._log.warning('Incorrect amount detected for coin b lock txn: {}'.format(transfer['tx_hash']))
|
||||
return -1
|
||||
return None
|
||||
|
||||
def waitForLockTxB(self, kbv, Kbs, cb_swap_value, cb_block_confirmed, restore_height):
|
||||
with self._mx_wallet:
|
||||
Kbv_enc = self.encodePubkey(self.pubkey(kbv))
|
||||
address_b58 = xmr_util.encode_address(Kbv_enc, self.encodePubkey(Kbs))
|
||||
|
||||
try:
|
||||
self.rpc_wallet_cb('close_wallet')
|
||||
except Exception as e:
|
||||
self._log.warning('close_wallet failed %s', str(e))
|
||||
|
||||
params = {
|
||||
'filename': address_b58,
|
||||
'address': address_b58,
|
||||
'viewkey': b2h(kbv[::-1]),
|
||||
'restore_height': restore_height,
|
||||
}
|
||||
self.rpc_wallet_cb('generate_from_keys', params)
|
||||
|
||||
self.rpc_wallet_cb('open_wallet', {'filename': address_b58})
|
||||
# For a while after opening the wallet rpc cmds return empty data
|
||||
|
||||
num_tries = 40
|
||||
for i in range(num_tries + 1):
|
||||
try:
|
||||
current_height = self.rpc_cb2('get_height')['height']
|
||||
print('current_height', current_height)
|
||||
except Exception as e:
|
||||
self._log.warning('rpc_cb failed %s', str(e))
|
||||
current_height = None # If the transfer is available it will be deep enough
|
||||
|
||||
# TODO: Make accepting current_height == None a user selectable option
|
||||
# Or look for all transfers and check height
|
||||
|
||||
params = {'transfer_type': 'available'}
|
||||
rv = self.rpc_wallet_cb('incoming_transfers', params)
|
||||
print('rv', rv)
|
||||
|
||||
if 'transfers' in rv:
|
||||
for transfer in rv['transfers']:
|
||||
if transfer['amount'] == cb_swap_value \
|
||||
and (current_height is None or current_height - transfer['block_height'] > cb_block_confirmed):
|
||||
return True
|
||||
|
||||
# TODO: Is it necessary to check the address?
|
||||
'''
|
||||
rv = self.rpc_wallet_cb('get_balance')
|
||||
print('get_balance', rv)
|
||||
|
||||
if 'per_subaddress' in rv:
|
||||
for sub_addr in rv['per_subaddress']:
|
||||
if sub_addr['address'] == address_b58:
|
||||
|
||||
'''
|
||||
|
||||
if i >= num_tries:
|
||||
raise ValueError('Balance not confirming on node')
|
||||
self._sc.delay_event.wait(1.0)
|
||||
if self._sc.delay_event.is_set():
|
||||
raise ValueError('Stopped')
|
||||
|
||||
return False
|
||||
rv = -1
|
||||
return rv
|
||||
|
||||
def findTxnByHash(self, txid):
|
||||
with self._mx_wallet:
|
||||
self.rpc_wallet_cb('open_wallet', {'filename': self._wallet_filename})
|
||||
self.rpc_wallet_cb('refresh')
|
||||
self.openWallet(self._wallet_filename)
|
||||
self.rpc_wallet('refresh', timeout=self._walletrpctimeoutlong)
|
||||
|
||||
try:
|
||||
current_height = self.rpc_cb2('get_height', timeout=30)['height']
|
||||
current_height = self.rpc2('get_height', timeout=self._rpctimeout)['height']
|
||||
self._log.info('findTxnByHash XMR current_height %d\nhash: %s', current_height, txid)
|
||||
except Exception as e:
|
||||
self._log.info('rpc_cb failed %s', str(e))
|
||||
self._log.info('rpc failed %s', str(e))
|
||||
current_height = None # If the transfer is available it will be deep enough
|
||||
|
||||
params = {'transfer_type': 'available'}
|
||||
rv = self.rpc_wallet_cb('incoming_transfers', params)
|
||||
rv = self.rpc_wallet('incoming_transfers', params)
|
||||
if 'transfers' in rv:
|
||||
for transfer in rv['transfers']:
|
||||
if transfer['tx_hash'] == txid \
|
||||
@@ -387,17 +413,16 @@ class XMRInterface(CoinInterface):
|
||||
|
||||
return None
|
||||
|
||||
def spendBLockTx(self, chain_b_lock_txid, address_to, kbv, kbs, cb_swap_value, b_fee_rate, restore_height, spend_actual_balance=False):
|
||||
def spendBLockTx(self, chain_b_lock_txid: bytes, address_to: str, kbv: bytes, kbs: bytes, cb_swap_value: int, b_fee_rate: int, restore_height: int, spend_actual_balance: bool = False) -> bytes:
|
||||
'''
|
||||
Notes:
|
||||
"Error: No unlocked balance in the specified subaddress(es)" can mean not enough funds after tx fee.
|
||||
'''
|
||||
with self._mx_wallet:
|
||||
Kbv = self.getPubkey(kbv)
|
||||
Kbs = self.getPubkey(kbs)
|
||||
address_b58 = xmr_util.encode_address(Kbv, Kbs)
|
||||
|
||||
try:
|
||||
self.rpc_wallet_cb('close_wallet')
|
||||
except Exception as e:
|
||||
self._log.warning('close_wallet failed %s', str(e))
|
||||
|
||||
wallet_filename = address_b58 + '_spend'
|
||||
|
||||
params = {
|
||||
@@ -409,18 +434,16 @@ class XMRInterface(CoinInterface):
|
||||
}
|
||||
|
||||
try:
|
||||
self.rpc_wallet_cb('open_wallet', {'filename': wallet_filename})
|
||||
self.openWallet(wallet_filename)
|
||||
except Exception as e:
|
||||
rv = self.rpc_wallet_cb('generate_from_keys', params)
|
||||
self._log.info('generate_from_keys %s', dumpj(rv))
|
||||
self.rpc_wallet_cb('open_wallet', {'filename': wallet_filename})
|
||||
|
||||
self.rpc_wallet_cb('refresh')
|
||||
rv = self.rpc_wallet_cb('get_balance')
|
||||
self.createWallet(params)
|
||||
self.openWallet(wallet_filename)
|
||||
|
||||
self.rpc_wallet('refresh')
|
||||
rv = self.rpc_wallet('get_balance')
|
||||
if rv['balance'] < cb_swap_value:
|
||||
self._log.warning('Balance is too low, checking for existing spend.')
|
||||
txns = self.rpc_wallet_cb('get_transfers', {'out': True})
|
||||
txns = self.rpc_wallet('get_transfers', {'out': True})
|
||||
if 'out' in txns:
|
||||
txns = txns['out']
|
||||
if len(txns) > 0:
|
||||
@@ -445,57 +468,106 @@ class XMRInterface(CoinInterface):
|
||||
if self._fee_priority > 0:
|
||||
params['priority'] = self._fee_priority
|
||||
|
||||
rv = self.rpc_wallet_cb('sweep_all', params)
|
||||
rv = self.rpc_wallet('sweep_all', params)
|
||||
self._log.debug('sweep_all {}'.format(json.dumps(rv)))
|
||||
|
||||
return bytes.fromhex(rv['tx_hash_list'][0])
|
||||
|
||||
def withdrawCoin(self, value, addr_to, subfee):
|
||||
def withdrawCoin(self, value: int, addr_to: str, subfee: bool) -> str:
|
||||
with self._mx_wallet:
|
||||
value_sats = make_int(value, self.exp())
|
||||
|
||||
self.rpc_wallet_cb('open_wallet', {'filename': self._wallet_filename})
|
||||
self.openWallet(self._wallet_filename)
|
||||
self.rpc_wallet('refresh')
|
||||
|
||||
if subfee:
|
||||
balance = self.rpc_wallet_cb('get_balance')
|
||||
if balance['unlocked_balance'] - value_sats <= 10:
|
||||
balance = self.rpc_wallet('get_balance')
|
||||
diff = balance['unlocked_balance'] - value_sats
|
||||
if diff >= 0 and diff <= 10:
|
||||
self._log.info('subfee enabled and value close to total, using sweep_all.')
|
||||
params = {'address': addr_to}
|
||||
if self._fee_priority > 0:
|
||||
params['priority'] = self._fee_priority
|
||||
rv = self.rpc_wallet_cb('sweep_all', params)
|
||||
rv = self.rpc_wallet('sweep_all', params)
|
||||
return rv['tx_hash_list'][0]
|
||||
raise ValueError('Withdraw value must be close to total to use subfee/sweep_all.')
|
||||
|
||||
params = {'destinations': [{'amount': value_sats, 'address': addr_to}]}
|
||||
if self._fee_priority > 0:
|
||||
params['priority'] = self._fee_priority
|
||||
rv = self.rpc_wallet_cb('transfer', params)
|
||||
rv = self.rpc_wallet('transfer', params)
|
||||
return rv['tx_hash']
|
||||
|
||||
def showLockTransfers(self, Kbv, Kbs):
|
||||
def showLockTransfers(self, kbv, Kbs, restore_height):
|
||||
with self._mx_wallet:
|
||||
try:
|
||||
Kbv = self.getPubkey(kbv)
|
||||
address_b58 = xmr_util.encode_address(Kbv, Kbs)
|
||||
wallet_file = address_b58 + '_spend'
|
||||
try:
|
||||
self.rpc_wallet_cb('open_wallet', {'filename': wallet_file})
|
||||
self.openWallet(wallet_file)
|
||||
except Exception:
|
||||
wallet_file = address_b58
|
||||
self.rpc_wallet_cb('open_wallet', {'filename': wallet_file})
|
||||
try:
|
||||
self.openWallet(wallet_file)
|
||||
except Exception:
|
||||
self._log.info(f'showLockTransfers trying to create wallet for address {address_b58}.')
|
||||
kbv_le = kbv[::-1]
|
||||
params = {
|
||||
'restore_height': restore_height,
|
||||
'filename': address_b58,
|
||||
'address': address_b58,
|
||||
'viewkey': b2h(kbv_le),
|
||||
}
|
||||
self.createWallet(params)
|
||||
self.openWallet(address_b58)
|
||||
|
||||
self.rpc_wallet_cb('refresh')
|
||||
self.rpc_wallet('refresh')
|
||||
|
||||
rv = self.rpc_wallet_cb('get_transfers', {'in': True, 'out': True, 'pending': True, 'failed': True})
|
||||
rv = self.rpc_wallet('get_transfers', {'in': True, 'out': True, 'pending': True, 'failed': True})
|
||||
rv['filename'] = wallet_file
|
||||
return rv
|
||||
except Exception as e:
|
||||
return {'error': str(e)}
|
||||
|
||||
def getSpendableBalance(self):
|
||||
def getSpendableBalance(self) -> int:
|
||||
with self._mx_wallet:
|
||||
self.rpc_wallet_cb('open_wallet', {'filename': self._wallet_filename})
|
||||
self.openWallet(self._wallet_filename)
|
||||
|
||||
self.rpc_wallet_cb('refresh')
|
||||
balance_info = self.rpc_wallet_cb('get_balance')
|
||||
self.rpc_wallet('refresh')
|
||||
balance_info = self.rpc_wallet('get_balance')
|
||||
return balance_info['unlocked_balance']
|
||||
|
||||
def changeWalletPassword(self, old_password, new_password):
|
||||
self._log.info('changeWalletPassword - {}'.format(self.ticker()))
|
||||
orig_password = self._wallet_password
|
||||
if old_password != '':
|
||||
self._wallet_password = old_password
|
||||
try:
|
||||
self.openWallet(self._wallet_filename)
|
||||
self.rpc_wallet('change_wallet_password', {'old_password': old_password, 'new_password': new_password})
|
||||
except Exception as e:
|
||||
self._wallet_password = orig_password
|
||||
raise e
|
||||
|
||||
def unlockWallet(self, password: str) -> None:
|
||||
self._log.info('unlockWallet - {}'.format(self.ticker()))
|
||||
self._wallet_password = password
|
||||
|
||||
if not self._have_checked_seed:
|
||||
self._sc.checkWalletSeed(self.coin_type())
|
||||
|
||||
def lockWallet(self) -> None:
|
||||
self._log.info('lockWallet - {}'.format(self.ticker()))
|
||||
self._wallet_password = None
|
||||
|
||||
def isAddressMine(self, address):
|
||||
# TODO
|
||||
return True
|
||||
|
||||
def ensureFunds(self, amount: int) -> None:
|
||||
if self.getSpendableBalance() < amount:
|
||||
raise ValueError('Balance too low')
|
||||
|
||||
def getTransaction(self, txid: bytes):
|
||||
return self.rpc2('get_transactions', {'txs_hashes': [txid.hex(), ]})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2020-2022 tecnovert
|
||||
# Copyright (c) 2020-2024 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
@@ -9,6 +9,7 @@ import random
|
||||
import urllib.parse
|
||||
|
||||
from .util import (
|
||||
ensure,
|
||||
toBool,
|
||||
)
|
||||
from .basicswap_util import (
|
||||
@@ -32,22 +33,25 @@ from .ui.util import (
|
||||
have_data_entry,
|
||||
tickerToCoinId,
|
||||
listOldBidStates,
|
||||
checkAddressesOwned,
|
||||
)
|
||||
from .ui.page_offers import postNewOffer
|
||||
from .protocols.xmr_swap_1 import recoverNoScriptTxnWithKey, getChainBSplitKey
|
||||
|
||||
|
||||
def js_error(self, error_str):
|
||||
error_str_json = json.dumps({'error': error_str})
|
||||
return bytes(error_str_json, 'UTF-8')
|
||||
def getFormData(post_string: str, is_json: bool):
|
||||
if post_string == '':
|
||||
raise ValueError('No post data')
|
||||
if is_json:
|
||||
form_data = json.loads(post_string)
|
||||
form_data['is_json'] = True
|
||||
else:
|
||||
form_data = urllib.parse.parse_qs(post_string)
|
||||
return form_data
|
||||
|
||||
|
||||
def withdraw_coin(swap_client, coin_type, post_string, is_json):
|
||||
if is_json:
|
||||
post_data = json.loads(post_string)
|
||||
post_data['is_json'] = True
|
||||
else:
|
||||
post_data = urllib.parse.parse_qs(post_string)
|
||||
post_data = getFormData(post_string, is_json)
|
||||
|
||||
value = get_data_entry(post_data, 'value')
|
||||
address = get_data_entry(post_data, 'address')
|
||||
@@ -59,28 +63,43 @@ def withdraw_coin(swap_client, coin_type, post_string, is_json):
|
||||
type_from = get_data_entry_or(post_data, 'type_from', 'plain')
|
||||
type_to = get_data_entry_or(post_data, 'type_to', 'plain')
|
||||
txid_hex = swap_client.withdrawParticl(type_from, type_to, value, address, subfee)
|
||||
elif coin_type == Coins.LTC:
|
||||
type_from = get_data_entry_or(post_data, 'type_from', 'plain')
|
||||
txid_hex = swap_client.withdrawLTC(type_from, value, address, subfee)
|
||||
else:
|
||||
txid_hex = swap_client.withdrawCoin(coin_type, value, address, subfee)
|
||||
|
||||
return {'txid': txid_hex}
|
||||
|
||||
|
||||
def js_coins(self, url_split, post_string, is_json):
|
||||
def js_error(self, error_str) -> bytes:
|
||||
error_str_json = json.dumps({'error': error_str})
|
||||
return bytes(error_str_json, 'UTF-8')
|
||||
|
||||
|
||||
def js_coins(self, url_split, post_string, is_json) -> bytes:
|
||||
swap_client = self.server.swap_client
|
||||
|
||||
coins = []
|
||||
for coin in Coins:
|
||||
cc = swap_client.coin_clients[coin]
|
||||
coin_chainparams = chainparams[cc['coin']]
|
||||
coin_active: bool = False if cc['connection_type'] == 'none' else True
|
||||
if coin == Coins.LTC_MWEB:
|
||||
coin_active = False
|
||||
entry = {
|
||||
'id': int(coin),
|
||||
'ticker': chainparams[cc['coin']]['ticker'],
|
||||
'ticker': coin_chainparams['ticker'],
|
||||
'name': getCoinName(coin),
|
||||
'active': False if cc['connection_type'] == 'none' else True,
|
||||
'active': coin_active,
|
||||
'decimal_places': coin_chainparams['decimal_places'],
|
||||
}
|
||||
if coin == Coins.PART_ANON:
|
||||
entry['variant'] = 'Anon'
|
||||
elif coin == Coins.PART_BLIND:
|
||||
entry['variant'] = 'Blind'
|
||||
elif coin == Coins.LTC_MWEB:
|
||||
entry['variant'] = 'MWEB'
|
||||
coins.append(entry)
|
||||
|
||||
return bytes(json.dumps(coins), 'UTF-8')
|
||||
@@ -88,6 +107,7 @@ def js_coins(self, url_split, post_string, is_json):
|
||||
|
||||
def js_wallets(self, url_split, post_string, is_json):
|
||||
swap_client = self.server.swap_client
|
||||
swap_client.checkSystemStatus()
|
||||
if len(url_split) > 3:
|
||||
ticker_str = url_split[3]
|
||||
coin_type = tickerToCoinId(ticker_str)
|
||||
@@ -96,33 +116,52 @@ def js_wallets(self, url_split, post_string, is_json):
|
||||
cmd = url_split[4]
|
||||
if cmd == 'withdraw':
|
||||
return bytes(json.dumps(withdraw_coin(swap_client, coin_type, post_string, is_json)), 'UTF-8')
|
||||
if cmd == 'nextdepositaddr':
|
||||
elif cmd == 'nextdepositaddr':
|
||||
return bytes(json.dumps(swap_client.cacheNewAddressForCoin(coin_type)), 'UTF-8')
|
||||
elif cmd == 'createutxo':
|
||||
post_data = getFormData(post_string, is_json)
|
||||
ci = swap_client.ci(coin_type)
|
||||
value = ci.make_int(get_data_entry(post_data, 'value'))
|
||||
txid_hex, new_addr = ci.createUTXO(value)
|
||||
return bytes(json.dumps({'txid': txid_hex, 'address': new_addr}), 'UTF-8')
|
||||
elif cmd == 'reseed':
|
||||
swap_client.reseedWallet(coin_type)
|
||||
return bytes(json.dumps({'reseeded': True}), 'UTF-8')
|
||||
elif cmd == 'newstealthaddress':
|
||||
if coin_type != Coins.PART:
|
||||
raise ValueError('Invalid coin for command')
|
||||
return bytes(json.dumps(swap_client.ci(coin_type).getNewStealthAddress()), 'UTF-8')
|
||||
elif cmd == 'newmwebaddress':
|
||||
if coin_type not in (Coins.LTC, Coins.LTC_MWEB):
|
||||
raise ValueError('Invalid coin for command')
|
||||
return bytes(json.dumps(swap_client.ci(coin_type).getNewMwebAddress()), 'UTF-8')
|
||||
|
||||
raise ValueError('Unknown command')
|
||||
|
||||
if coin_type == Coins.LTC_MWEB:
|
||||
coin_type = Coins.LTC
|
||||
rv = swap_client.getWalletInfo(coin_type)
|
||||
rv.update(swap_client.getBlockchainInfo(coin_type))
|
||||
ci = swap_client.ci(coin_type)
|
||||
checkAddressesOwned(swap_client, ci, rv)
|
||||
return bytes(json.dumps(rv), 'UTF-8')
|
||||
return bytes(json.dumps(self.server.swap_client.getWalletsInfo({'ticker_key': True})), 'UTF-8')
|
||||
|
||||
return bytes(json.dumps(swap_client.getWalletsInfo({'ticker_key': True})), 'UTF-8')
|
||||
|
||||
|
||||
def js_offers(self, url_split, post_string, is_json, sent=False):
|
||||
def js_offers(self, url_split, post_string, is_json, sent=False) -> bytes:
|
||||
swap_client = self.server.swap_client
|
||||
swap_client.checkSystemStatus()
|
||||
offer_id = None
|
||||
if len(url_split) > 3:
|
||||
if url_split[3] == 'new':
|
||||
if post_string == '':
|
||||
raise ValueError('No post data')
|
||||
if is_json:
|
||||
form_data = json.loads(post_string)
|
||||
form_data['is_json'] = True
|
||||
else:
|
||||
form_data = urllib.parse.parse_qs(post_string)
|
||||
form_data = getFormData(post_string, is_json)
|
||||
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])
|
||||
|
||||
with_extra_info = False
|
||||
filters = {
|
||||
'coin_from': -1,
|
||||
'coin_to': -1,
|
||||
@@ -136,11 +175,7 @@ def js_offers(self, url_split, post_string, is_json, sent=False):
|
||||
filters['offer_id'] = offer_id
|
||||
|
||||
if post_string != '':
|
||||
if is_json:
|
||||
post_data = json.loads(post_string)
|
||||
post_data['is_json'] = True
|
||||
else:
|
||||
post_data = urllib.parse.parse_qs(post_string)
|
||||
post_data = getFormData(post_string, is_json)
|
||||
filters['coin_from'] = setCoinFilter(post_data, 'coin_from')
|
||||
filters['coin_to'] = setCoinFilter(post_data, 'coin_to')
|
||||
|
||||
@@ -153,18 +188,26 @@ def js_offers(self, url_split, post_string, is_json, sent=False):
|
||||
assert (sort_dir in ['asc', 'desc']), 'Invalid sort dir'
|
||||
filters['sort_dir'] = sort_dir
|
||||
|
||||
if b'offset' in post_data:
|
||||
if have_data_entry(post_data, 'offset'):
|
||||
filters['offset'] = int(get_data_entry(post_data, 'offset'))
|
||||
if b'limit' in post_data:
|
||||
if have_data_entry(post_data, 'limit'):
|
||||
filters['limit'] = int(get_data_entry(post_data, 'limit'))
|
||||
assert (filters['limit'] > 0 and filters['limit'] <= PAGE_LIMIT), 'Invalid limit'
|
||||
if have_data_entry(post_data, 'active'):
|
||||
filters['active'] = get_data_entry(post_data, 'active')
|
||||
if have_data_entry(post_data, 'include_sent'):
|
||||
filters['include_sent'] = toBool(get_data_entry(post_data, 'include_sent'))
|
||||
|
||||
offers = self.server.swap_client.listOffers(sent, filters)
|
||||
if have_data_entry(post_data, 'with_extra_info'):
|
||||
with_extra_info = toBool(get_data_entry(post_data, 'with_extra_info'))
|
||||
|
||||
offers = swap_client.listOffers(sent, filters)
|
||||
rv = []
|
||||
for o in offers:
|
||||
ci_from = self.server.swap_client.ci(o.coin_from)
|
||||
ci_to = self.server.swap_client.ci(o.coin_to)
|
||||
rv.append({
|
||||
ci_from = swap_client.ci(o.coin_from)
|
||||
ci_to = swap_client.ci(o.coin_to)
|
||||
offer_data = {
|
||||
'swap_type': o.swap_type,
|
||||
'addr_from': o.addr_from,
|
||||
'addr_to': o.addr_to,
|
||||
'offer_id': o.offer_id.hex(),
|
||||
@@ -175,26 +218,90 @@ def js_offers(self, url_split, post_string, is_json, sent=False):
|
||||
'amount_from': ci_from.format_amount(o.amount_from),
|
||||
'amount_to': ci_to.format_amount((o.amount_from * o.rate) // ci_from.COIN()),
|
||||
'rate': ci_to.format_amount(o.rate),
|
||||
})
|
||||
}
|
||||
if with_extra_info:
|
||||
offer_data['amount_negotiable'] = o.amount_negotiable
|
||||
offer_data['rate_negotiable'] = o.rate_negotiable
|
||||
|
||||
if o.swap_type == SwapTypes.XMR_SWAP:
|
||||
_, xmr_offer = swap_client.getXmrOffer(o.offer_id)
|
||||
offer_data['lock_time_1'] = xmr_offer.lock_time_1
|
||||
offer_data['lock_time_2'] = xmr_offer.lock_time_2
|
||||
|
||||
offer_data['feerate_from'] = xmr_offer.a_fee_rate
|
||||
offer_data['feerate_to'] = xmr_offer.b_fee_rate
|
||||
else:
|
||||
offer_data['feerate_from'] = o.from_feerate
|
||||
offer_data['feerate_to'] = o.to_feerate
|
||||
|
||||
rv.append(offer_data)
|
||||
return bytes(json.dumps(rv), 'UTF-8')
|
||||
|
||||
|
||||
def js_sentoffers(self, url_split, post_string, is_json):
|
||||
return self.js_offers(url_split, post_string, is_json, True)
|
||||
def js_sentoffers(self, url_split, post_string, is_json) -> bytes:
|
||||
return js_offers(self, url_split, post_string, is_json, True)
|
||||
|
||||
|
||||
def js_bids(self, url_split, post_string, is_json):
|
||||
def parseBidFilters(post_data):
|
||||
offer_id = None
|
||||
filters = {}
|
||||
|
||||
if have_data_entry(post_data, 'offer_id'):
|
||||
offer_id = bytes.fromhex(get_data_entry(post_data, 'offer_id'))
|
||||
assert (len(offer_id) == 28)
|
||||
|
||||
if have_data_entry(post_data, 'sort_by'):
|
||||
sort_by = get_data_entry(post_data, 'sort_by')
|
||||
assert (sort_by in ['created_at', ]), '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'
|
||||
filters['sort_dir'] = sort_dir
|
||||
|
||||
if have_data_entry(post_data, 'offset'):
|
||||
filters['offset'] = int(get_data_entry(post_data, 'offset'))
|
||||
if have_data_entry(post_data, 'limit'):
|
||||
filters['limit'] = int(get_data_entry(post_data, 'limit'))
|
||||
assert (filters['limit'] > 0 and filters['limit'] <= PAGE_LIMIT), 'Invalid limit'
|
||||
|
||||
if have_data_entry(post_data, 'with_available_or_active'):
|
||||
filters['with_available_or_active'] = toBool(get_data_entry(post_data, 'with_available_or_active'))
|
||||
elif have_data_entry(post_data, 'with_expired'):
|
||||
filters['with_expired'] = toBool(get_data_entry(post_data, 'with_expired'))
|
||||
|
||||
if have_data_entry(post_data, 'with_extra_info'):
|
||||
filters['with_extra_info'] = toBool(get_data_entry(post_data, 'with_extra_info'))
|
||||
|
||||
return offer_id, filters
|
||||
|
||||
|
||||
def formatBids(swap_client, bids, filters) -> bytes:
|
||||
with_extra_info = filters.get('with_extra_info', False)
|
||||
rv = []
|
||||
for b in bids:
|
||||
bid_data = {
|
||||
'bid_id': b[2].hex(),
|
||||
'offer_id': b[3].hex(),
|
||||
'created_at': b[0],
|
||||
'expire_at': b[1],
|
||||
'coin_from': b[9],
|
||||
'amount_from': swap_client.ci(b[9]).format_amount(b[4]),
|
||||
'bid_rate': swap_client.ci(b[14]).format_amount(b[10]),
|
||||
'bid_state': strBidState(b[5])
|
||||
}
|
||||
if with_extra_info:
|
||||
bid_data['addr_from'] = b[11]
|
||||
rv.append(bid_data)
|
||||
return bytes(json.dumps(rv), 'UTF-8')
|
||||
|
||||
|
||||
def js_bids(self, url_split, post_string: str, is_json: bool) -> bytes:
|
||||
swap_client = self.server.swap_client
|
||||
swap_client.checkSystemStatus()
|
||||
if len(url_split) > 3:
|
||||
if url_split[3] == 'new':
|
||||
if post_string == '':
|
||||
raise ValueError('No post data')
|
||||
if is_json:
|
||||
post_data = json.loads(post_string)
|
||||
post_data['is_json'] = True
|
||||
else:
|
||||
post_data = urllib.parse.parse_qs(post_string)
|
||||
post_data = getFormData(post_string, is_json)
|
||||
|
||||
offer_id = bytes.fromhex(get_data_entry(post_data, 'offer_id'))
|
||||
assert (len(offer_id) == 28)
|
||||
@@ -243,13 +350,11 @@ def js_bids(self, url_split, post_string, is_json):
|
||||
|
||||
show_txns = False
|
||||
if post_string != '':
|
||||
if is_json:
|
||||
post_data = json.loads(post_string)
|
||||
post_data['is_json'] = True
|
||||
else:
|
||||
post_data = urllib.parse.parse_qs(post_string)
|
||||
post_data = getFormData(post_string, is_json)
|
||||
if have_data_entry(post_data, 'accept'):
|
||||
swap_client.acceptBid(bid_id)
|
||||
elif have_data_entry(post_data, 'abandon'):
|
||||
swap_client.abandonBid(bid_id)
|
||||
elif have_data_entry(post_data, 'debugind'):
|
||||
swap_client.setBidDebugInd(bid_id, int(get_data_entry(post_data, 'debugind')))
|
||||
|
||||
@@ -274,43 +379,43 @@ def js_bids(self, url_split, post_string, is_json):
|
||||
data = describeBid(swap_client, bid, xmr_swap, offer, xmr_offer, events, edit_bid, show_txns, for_api=True)
|
||||
return bytes(json.dumps(data), 'UTF-8')
|
||||
|
||||
bids = swap_client.listBids()
|
||||
return bytes(json.dumps([{
|
||||
'bid_id': b[2].hex(),
|
||||
'offer_id': b[3].hex(),
|
||||
'created_at': b[0],
|
||||
'expire_at': b[1],
|
||||
'coin_from': b[9],
|
||||
'amount_from': swap_client.ci(b[9]).format_amount(b[4]),
|
||||
'bid_state': strBidState(b[5])
|
||||
} for b in bids]), 'UTF-8')
|
||||
post_data = {} if post_string == '' else getFormData(post_string, is_json)
|
||||
offer_id, filters = parseBidFilters(post_data)
|
||||
|
||||
bids = swap_client.listBids(offer_id=offer_id, filters=filters)
|
||||
return formatBids(swap_client, bids, filters)
|
||||
|
||||
|
||||
def js_sentbids(self, url_split, post_string, is_json):
|
||||
return bytes(json.dumps(self.server.swap_client.listBids(sent=True)), 'UTF-8')
|
||||
def js_sentbids(self, url_split, post_string, is_json) -> bytes:
|
||||
swap_client = self.server.swap_client
|
||||
swap_client.checkSystemStatus()
|
||||
post_data = getFormData(post_string, is_json)
|
||||
offer_id, filters = parseBidFilters(post_data)
|
||||
|
||||
bids = swap_client.listBids(sent=True, offer_id=offer_id, filters=filters)
|
||||
return formatBids(swap_client, bids, filters)
|
||||
|
||||
|
||||
def js_network(self, url_split, post_string, is_json):
|
||||
return bytes(json.dumps(self.server.swap_client.get_network_info()), 'UTF-8')
|
||||
def js_network(self, url_split, post_string, is_json) -> bytes:
|
||||
swap_client = self.server.swap_client
|
||||
swap_client.checkSystemStatus()
|
||||
return bytes(json.dumps(swap_client.get_network_info()), 'UTF-8')
|
||||
|
||||
|
||||
def js_revokeoffer(self, url_split, post_string, is_json):
|
||||
def js_revokeoffer(self, url_split, post_string, is_json) -> bytes:
|
||||
swap_client = self.server.swap_client
|
||||
swap_client.checkSystemStatus()
|
||||
offer_id = bytes.fromhex(url_split[3])
|
||||
assert (len(offer_id) == 28)
|
||||
self.server.swap_client.revokeOffer(offer_id)
|
||||
swap_client.revokeOffer(offer_id)
|
||||
return bytes(json.dumps({'revoked_offer': offer_id.hex()}), 'UTF-8')
|
||||
|
||||
|
||||
def js_smsgaddresses(self, url_split, post_string, is_json):
|
||||
def js_smsgaddresses(self, url_split, post_string, is_json) -> bytes:
|
||||
swap_client = self.server.swap_client
|
||||
swap_client.checkSystemStatus()
|
||||
post_data = {} if post_string == '' else getFormData(post_string, is_json)
|
||||
if len(url_split) > 3:
|
||||
if post_string == '':
|
||||
raise ValueError('No post data')
|
||||
if is_json:
|
||||
post_data = json.loads(post_string)
|
||||
post_data['is_json'] = True
|
||||
else:
|
||||
post_data = urllib.parse.parse_qs(post_string)
|
||||
if url_split[3] == 'new':
|
||||
addressnote = get_data_entry_or(post_data, 'addressnote', '')
|
||||
new_addr, pubkey = swap_client.newSMSGAddress(addressnote=addressnote)
|
||||
@@ -327,17 +432,15 @@ def js_smsgaddresses(self, url_split, post_string, is_json):
|
||||
new_addr = swap_client.editSMSGAddress(address, activeind, addressnote)
|
||||
return bytes(json.dumps({'edited_address': address}), 'UTF-8')
|
||||
|
||||
return bytes(json.dumps(swap_client.listAllSMSGAddresses()), 'UTF-8')
|
||||
filters = {
|
||||
'exclude_inactive': post_data.get('exclude_inactive', True),
|
||||
}
|
||||
|
||||
return bytes(json.dumps(swap_client.listAllSMSGAddresses(filters)), 'UTF-8')
|
||||
|
||||
|
||||
def js_rates(self, url_split, post_string, is_json):
|
||||
if post_string == '':
|
||||
raise ValueError('No post data')
|
||||
if is_json:
|
||||
post_data = json.loads(post_string)
|
||||
post_data['is_json'] = True
|
||||
else:
|
||||
post_data = urllib.parse.parse_qs(post_string)
|
||||
def js_rates(self, url_split, post_string, is_json) -> bytes:
|
||||
post_data = getFormData(post_string, is_json)
|
||||
|
||||
sc = self.server.swap_client
|
||||
coin_from = get_data_entry(post_data, 'coin_from')
|
||||
@@ -345,7 +448,7 @@ 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):
|
||||
def js_rates_list(self, url_split, query_string, is_json) -> bytes:
|
||||
get_data = urllib.parse.parse_qs(query_string)
|
||||
|
||||
sc = self.server.swap_client
|
||||
@@ -354,14 +457,8 @@ def js_rates_list(self, url_split, query_string, is_json):
|
||||
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')
|
||||
if is_json:
|
||||
post_data = json.loads(post_string)
|
||||
post_data['is_json'] = True
|
||||
else:
|
||||
post_data = urllib.parse.parse_qs(post_string)
|
||||
def js_rate(self, url_split, post_string, is_json) -> bytes:
|
||||
post_data = getFormData(post_string, is_json)
|
||||
|
||||
sc = self.server.swap_client
|
||||
coin_from = getCoinType(get_data_entry(post_data, 'coin_from'))
|
||||
@@ -393,11 +490,13 @@ def js_rate(self, url_split, post_string, is_json):
|
||||
return bytes(json.dumps({'rate': rate}), 'UTF-8')
|
||||
|
||||
|
||||
def js_index(self, url_split, post_string, is_json):
|
||||
return bytes(json.dumps(self.server.swap_client.getSummary()), 'UTF-8')
|
||||
def js_index(self, url_split, post_string, is_json) -> bytes:
|
||||
swap_client = self.server.swap_client
|
||||
swap_client.checkSystemStatus()
|
||||
return bytes(json.dumps(swap_client.getSummary()), 'UTF-8')
|
||||
|
||||
|
||||
def js_generatenotification(self, url_split, post_string, is_json):
|
||||
def js_generatenotification(self, url_split, post_string, is_json) -> bytes:
|
||||
swap_client = self.server.swap_client
|
||||
|
||||
if not swap_client.debug:
|
||||
@@ -411,42 +510,243 @@ def js_generatenotification(self, url_split, post_string, is_json):
|
||||
elif r == 2:
|
||||
swap_client.notify(NT.BID_ACCEPTED, {'bid_id': random.randbytes(28).hex()})
|
||||
elif r == 3:
|
||||
swap_client.notify(NT.BID_RECEIVED, {'type': 'xmr', 'bid_id': random.randbytes(28).hex(), 'offer_id': random.randbytes(28).hex()})
|
||||
swap_client.notify(NT.BID_RECEIVED, {'type': 'ads', 'bid_id': random.randbytes(28).hex(), 'offer_id': random.randbytes(28).hex()})
|
||||
|
||||
return bytes(json.dumps({'type': r}), 'UTF-8')
|
||||
|
||||
|
||||
def js_notifications(self, url_split, post_string, is_json):
|
||||
def js_notifications(self, url_split, post_string, is_json) -> bytes:
|
||||
swap_client = self.server.swap_client
|
||||
swap_client.getNotifications()
|
||||
swap_client.checkSystemStatus()
|
||||
|
||||
return bytes(json.dumps(swap_client.getNotifications()), 'UTF-8')
|
||||
|
||||
|
||||
def js_vacuumdb(self, url_split, post_string, is_json):
|
||||
def js_identities(self, url_split, post_string: str, is_json: bool) -> bytes:
|
||||
swap_client = self.server.swap_client
|
||||
swap_client.checkSystemStatus()
|
||||
|
||||
filters = {
|
||||
'page_no': 1,
|
||||
'limit': PAGE_LIMIT,
|
||||
'sort_by': 'created_at',
|
||||
'sort_dir': 'desc',
|
||||
}
|
||||
|
||||
if len(url_split) > 3:
|
||||
address = url_split[3]
|
||||
filters['address'] = address
|
||||
|
||||
if post_string != '':
|
||||
post_data = getFormData(post_string, is_json)
|
||||
|
||||
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'
|
||||
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'
|
||||
filters['sort_dir'] = sort_dir
|
||||
|
||||
if have_data_entry(post_data, 'offset'):
|
||||
filters['offset'] = int(get_data_entry(post_data, 'offset'))
|
||||
if have_data_entry(post_data, 'limit'):
|
||||
filters['limit'] = int(get_data_entry(post_data, 'limit'))
|
||||
assert (filters['limit'] > 0 and filters['limit'] <= PAGE_LIMIT), 'Invalid limit'
|
||||
|
||||
set_data = {}
|
||||
if have_data_entry(post_data, 'set_label'):
|
||||
set_data['label'] = get_data_entry(post_data, 'set_label')
|
||||
if have_data_entry(post_data, 'set_automation_override'):
|
||||
set_data['automation_override'] = get_data_entry(post_data, 'set_automation_override')
|
||||
if have_data_entry(post_data, 'set_visibility_override'):
|
||||
set_data['visibility_override'] = get_data_entry(post_data, 'set_visibility_override')
|
||||
if have_data_entry(post_data, 'set_note'):
|
||||
set_data['note'] = get_data_entry(post_data, 'set_note')
|
||||
|
||||
if set_data:
|
||||
ensure('address' in filters, 'Must provide an address to modify data')
|
||||
swap_client.setIdentityData(filters, set_data)
|
||||
|
||||
return bytes(json.dumps(swap_client.listIdentities(filters)), 'UTF-8')
|
||||
|
||||
|
||||
def js_automationstrategies(self, url_split, post_string: str, is_json: bool) -> bytes:
|
||||
swap_client = self.server.swap_client
|
||||
swap_client.checkSystemStatus()
|
||||
|
||||
filters = {
|
||||
'page_no': 1,
|
||||
'limit': PAGE_LIMIT,
|
||||
'sort_by': 'created_at',
|
||||
'sort_dir': 'desc',
|
||||
}
|
||||
|
||||
if post_string != '':
|
||||
post_data = getFormData(post_string, is_json)
|
||||
|
||||
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'
|
||||
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'
|
||||
filters['sort_dir'] = sort_dir
|
||||
|
||||
if have_data_entry(post_data, 'offset'):
|
||||
filters['offset'] = int(get_data_entry(post_data, 'offset'))
|
||||
if have_data_entry(post_data, 'limit'):
|
||||
filters['limit'] = int(get_data_entry(post_data, 'limit'))
|
||||
assert (filters['limit'] > 0 and filters['limit'] <= PAGE_LIMIT), 'Invalid limit'
|
||||
|
||||
if len(url_split) > 3:
|
||||
strat_id = int(url_split[3])
|
||||
strat_data = swap_client.getAutomationStrategy(strat_id)
|
||||
rv = {
|
||||
'record_id': strat_data.record_id,
|
||||
'label': strat_data.label,
|
||||
'type_ind': strat_data.type_ind,
|
||||
'only_known_identities': strat_data.only_known_identities,
|
||||
'num_concurrent': strat_data.num_concurrent,
|
||||
'data': json.loads(strat_data.data.decode('utf-8')),
|
||||
'note': '' if strat_data.note is None else strat_data.note,
|
||||
}
|
||||
return bytes(json.dumps(rv), 'UTF-8')
|
||||
|
||||
rv = []
|
||||
strats = swap_client.listAutomationStrategies(filters)
|
||||
for row in strats:
|
||||
rv.append((row[0], row[1], row[2]))
|
||||
return bytes(json.dumps(rv), 'UTF-8')
|
||||
|
||||
|
||||
def js_vacuumdb(self, url_split, post_string, is_json) -> bytes:
|
||||
swap_client = self.server.swap_client
|
||||
swap_client.checkSystemStatus()
|
||||
swap_client.vacuumDB()
|
||||
|
||||
return bytes(json.dumps({'completed': True}), 'UTF-8')
|
||||
|
||||
|
||||
def js_getcoinseed(self, url_split, post_string, is_json) -> bytes:
|
||||
swap_client = self.server.swap_client
|
||||
swap_client.checkSystemStatus()
|
||||
post_data = getFormData(post_string, is_json)
|
||||
|
||||
coin = getCoinType(get_data_entry(post_data, 'coin'))
|
||||
if coin in (Coins.PART, Coins.PART_ANON, Coins.PART_BLIND):
|
||||
raise ValueError('Particl wallet seed is set from the Basicswap mnemonic.')
|
||||
|
||||
ci = swap_client.ci(coin)
|
||||
if coin == Coins.XMR:
|
||||
key_view = swap_client.getWalletKey(coin, 1, for_ed25519=True)
|
||||
key_spend = swap_client.getWalletKey(coin, 2, for_ed25519=True)
|
||||
address = ci.getAddressFromKeys(key_view, key_spend)
|
||||
return bytes(json.dumps({'coin': ci.ticker(), 'key_view': ci.encodeKey(key_view), 'key_spend': ci.encodeKey(key_spend), 'address': address}), 'UTF-8')
|
||||
|
||||
seed_key = swap_client.getWalletKey(coin, 1)
|
||||
if coin == Coins.DASH:
|
||||
return bytes(json.dumps({'coin': ci.ticker(), 'seed': seed_key.hex(), 'mnemonic': ci.seedToMnemonic(seed_key)}), 'UTF-8')
|
||||
seed_id = ci.getSeedHash(seed_key)
|
||||
return bytes(json.dumps({'coin': ci.ticker(), 'seed': seed_key.hex(), 'seed_id': seed_id.hex()}), 'UTF-8')
|
||||
|
||||
|
||||
def js_setpassword(self, url_split, post_string, is_json) -> bytes:
|
||||
# Set or change wallet passwords
|
||||
# Only works with currently enabled coins
|
||||
# Will fail if any coin does not unlock on the old password
|
||||
swap_client = self.server.swap_client
|
||||
post_data = getFormData(post_string, is_json)
|
||||
|
||||
old_password = get_data_entry(post_data, 'oldpassword')
|
||||
new_password = get_data_entry(post_data, 'newpassword')
|
||||
|
||||
if have_data_entry(post_data, 'coin'):
|
||||
# Set password for one coin
|
||||
coin = getCoinType(get_data_entry(post_data, 'coin'))
|
||||
if coin in (Coins.PART_ANON, Coins.PART_BLIND, Coins.LTC_MWEB):
|
||||
raise ValueError('Invalid coin.')
|
||||
swap_client.changeWalletPasswords(old_password, new_password, coin)
|
||||
return bytes(json.dumps({'success': True}), 'UTF-8')
|
||||
|
||||
# Set password for all coins
|
||||
swap_client.changeWalletPasswords(old_password, new_password)
|
||||
return bytes(json.dumps({'success': True}), 'UTF-8')
|
||||
|
||||
|
||||
def js_unlock(self, url_split, post_string, is_json) -> bytes:
|
||||
swap_client = self.server.swap_client
|
||||
post_data = getFormData(post_string, is_json)
|
||||
|
||||
password = get_data_entry(post_data, 'password')
|
||||
|
||||
if have_data_entry(post_data, 'coin'):
|
||||
coin = getCoinType(str(get_data_entry(post_data, 'coin')))
|
||||
if coin in (Coins.PART_ANON, Coins.PART_BLIND):
|
||||
raise ValueError('Invalid coin.')
|
||||
swap_client.unlockWallets(password, coin)
|
||||
return bytes(json.dumps({'success': True}), 'UTF-8')
|
||||
|
||||
swap_client.unlockWallets(password)
|
||||
return bytes(json.dumps({'success': True}), 'UTF-8')
|
||||
|
||||
|
||||
def js_lock(self, url_split, post_string, is_json) -> bytes:
|
||||
swap_client = self.server.swap_client
|
||||
post_data = {} if post_string == '' else getFormData(post_string, is_json)
|
||||
|
||||
if have_data_entry(post_data, 'coin'):
|
||||
coin = getCoinType(get_data_entry(post_data, 'coin'))
|
||||
if coin in (Coins.PART_ANON, Coins.PART_BLIND):
|
||||
raise ValueError('Invalid coin.')
|
||||
swap_client.lockWallets(coin)
|
||||
return bytes(json.dumps({'success': True}), 'UTF-8')
|
||||
|
||||
swap_client.lockWallets()
|
||||
return bytes(json.dumps({'success': True}), 'UTF-8')
|
||||
|
||||
|
||||
def js_404(self, url_split, post_string, is_json) -> bytes:
|
||||
return bytes(json.dumps({'Error': 'path unknown'}), 'UTF-8')
|
||||
|
||||
|
||||
def js_help(self, url_split, post_string, is_json) -> bytes:
|
||||
# TODO: Add details and examples
|
||||
commands = []
|
||||
for k in pages:
|
||||
commands.append(k)
|
||||
return bytes(json.dumps({'commands': commands}), 'UTF-8')
|
||||
|
||||
|
||||
pages = {
|
||||
'coins': js_coins,
|
||||
'wallets': js_wallets,
|
||||
'offers': js_offers,
|
||||
'sentoffers': js_sentoffers,
|
||||
'bids': js_bids,
|
||||
'sentbids': js_sentbids,
|
||||
'network': js_network,
|
||||
'revokeoffer': js_revokeoffer,
|
||||
'smsgaddresses': js_smsgaddresses,
|
||||
'rate': js_rate,
|
||||
'rates': js_rates,
|
||||
'rateslist': js_rates_list,
|
||||
'generatenotification': js_generatenotification,
|
||||
'notifications': js_notifications,
|
||||
'identities': js_identities,
|
||||
'automationstrategies': js_automationstrategies,
|
||||
'vacuumdb': js_vacuumdb,
|
||||
'getcoinseed': js_getcoinseed,
|
||||
'setpassword': js_setpassword,
|
||||
'unlock': js_unlock,
|
||||
'lock': js_lock,
|
||||
'help': js_help,
|
||||
}
|
||||
|
||||
|
||||
def js_url_to_function(url_split):
|
||||
if len(url_split) > 2:
|
||||
return {
|
||||
'coins': js_coins,
|
||||
'wallets': js_wallets,
|
||||
'offers': js_offers,
|
||||
'sentoffers': js_sentoffers,
|
||||
'bids': js_bids,
|
||||
'sentbids': js_sentbids,
|
||||
'network': js_network,
|
||||
'revokeoffer': js_revokeoffer,
|
||||
'smsgaddresses': js_smsgaddresses,
|
||||
'rate': js_rate,
|
||||
'rates': js_rates,
|
||||
'rateslist': js_rates_list,
|
||||
'generatenotification': js_generatenotification,
|
||||
'notifications': js_notifications,
|
||||
'vacuumdb': js_vacuumdb,
|
||||
}.get(url_split[2], js_index)
|
||||
return pages.get(url_split[2], js_404)
|
||||
return js_index
|
||||
|
||||
@@ -33,6 +33,8 @@ message OfferMessage {
|
||||
uint32 protocol_version = 16;
|
||||
bool amount_negotiable = 17;
|
||||
bool rate_negotiable = 18;
|
||||
|
||||
bytes proof_utxos = 19; /* 32 byte txid 2 byte vout, repeated */
|
||||
}
|
||||
|
||||
/* Step 2, buyer -> seller */
|
||||
@@ -40,8 +42,21 @@ message BidMessage {
|
||||
bytes offer_msg_id = 1;
|
||||
uint64 time_valid = 2; /* seconds bid is valid for */
|
||||
uint64 amount = 3; /* amount of amount_from bid is for */
|
||||
uint64 rate = 4;
|
||||
bytes pkhash_buyer = 5; /* buyer's address to receive amount_from */
|
||||
string proof_address = 6;
|
||||
string proof_signature = 7;
|
||||
|
||||
/* optional */
|
||||
uint32 protocol_version = 8;
|
||||
|
||||
bytes proof_utxos = 9; /* 32 byte txid 2 byte vout, repeated */
|
||||
}
|
||||
|
||||
/* For tests */
|
||||
message BidMessage_v1Deprecated {
|
||||
bytes offer_msg_id = 1;
|
||||
uint64 time_valid = 2; /* seconds bid is valid for */
|
||||
uint64 amount = 3; /* amount of amount_from bid is for */
|
||||
uint64 rate = 4;
|
||||
bytes pkhash_buyer = 5; /* buyer's address to receive amount_from */
|
||||
string proof_address = 6;
|
||||
@@ -50,6 +65,8 @@ message BidMessage {
|
||||
uint32 protocol_version = 8;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Step 3, seller -> buyer */
|
||||
message BidAcceptMessage {
|
||||
bytes bid_msg_id = 1;
|
||||
@@ -131,3 +148,24 @@ message XmrBidLockReleaseMessage {
|
||||
|
||||
bytes al_lock_spend_tx_esig = 2;
|
||||
}
|
||||
|
||||
message ADSBidIntentMessage {
|
||||
/* L -> F Sent from bidder, construct a reverse bid */
|
||||
bytes offer_msg_id = 1;
|
||||
uint64 time_valid = 2; /* seconds bid is valid for */
|
||||
uint64 amount_from = 3; /* amount of offer.coin_from bid is for */
|
||||
uint64 amount_to = 4; /* amount of offer.coin_to bid is for, equivalent to bid.amount */
|
||||
uint64 rate = 5; /* amount of offer.coin_from bid is for */
|
||||
|
||||
uint32 protocol_version = 6;
|
||||
}
|
||||
|
||||
message ADSBidIntentAcceptMessage {
|
||||
/* F -> L Sent from offerer, construct a reverse bid */
|
||||
bytes bid_msg_id = 1;
|
||||
|
||||
bytes pkaf = 2;
|
||||
bytes kbvf = 3;
|
||||
bytes kbsf_dleag = 4;
|
||||
bytes dest_af = 5;
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
# 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 descriptor_pool as _descriptor_pool
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
from google.protobuf.internal import builder as _builder
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
@@ -13,35 +13,41 @@ _sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
|
||||
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')
|
||||
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\"\xc9\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\x12\x13\n\x0bproof_utxos\x18\t \x01(\x0c\"\xc1\x01\n\x17\x42idMessage_v1Deprecated\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\"\x8f\x01\n\x13\x41\x44SBidIntentMessage\x12\x14\n\x0coffer_msg_id\x18\x01 \x01(\x0c\x12\x12\n\ntime_valid\x18\x02 \x01(\x04\x12\x13\n\x0b\x61mount_from\x18\x03 \x01(\x04\x12\x11\n\tamount_to\x18\x04 \x01(\x04\x12\x0c\n\x04rate\x18\x05 \x01(\x04\x12\x18\n\x10protocol_version\x18\x06 \x01(\r\"p\n\x19\x41\x44SBidIntentAcceptMessage\x12\x12\n\nbid_msg_id\x18\x01 \x01(\x0c\x12\x0c\n\x04pkaf\x18\x02 \x01(\x0c\x12\x0c\n\x04kbvf\x18\x03 \x01(\x0c\x12\x12\n\nkbsf_dleag\x18\x04 \x01(\x0c\x12\x0f\n\x07\x64\x65st_af\x18\x05 \x01(\x0c\x62\x06proto3')
|
||||
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'messages_pb2', globals())
|
||||
_globals = globals()
|
||||
_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
|
||||
_globals['_OFFERMESSAGE']._serialized_start=30
|
||||
_globals['_OFFERMESSAGE']._serialized_end=580
|
||||
_globals['_OFFERMESSAGE_LOCKTYPE']._serialized_start=467
|
||||
_globals['_OFFERMESSAGE_LOCKTYPE']._serialized_end=580
|
||||
_globals['_BIDMESSAGE']._serialized_start=583
|
||||
_globals['_BIDMESSAGE']._serialized_end=784
|
||||
_globals['_BIDMESSAGE_V1DEPRECATED']._serialized_start=787
|
||||
_globals['_BIDMESSAGE_V1DEPRECATED']._serialized_end=980
|
||||
_globals['_BIDACCEPTMESSAGE']._serialized_start=982
|
||||
_globals['_BIDACCEPTMESSAGE']._serialized_end=1068
|
||||
_globals['_OFFERREVOKEMESSAGE']._serialized_start=1070
|
||||
_globals['_OFFERREVOKEMESSAGE']._serialized_end=1131
|
||||
_globals['_BIDREJECTMESSAGE']._serialized_start=1133
|
||||
_globals['_BIDREJECTMESSAGE']._serialized_end=1192
|
||||
_globals['_XMRBIDMESSAGE']._serialized_start=1195
|
||||
_globals['_XMRBIDMESSAGE']._serialized_end=1373
|
||||
_globals['_XMRSPLITMESSAGE']._serialized_start=1375
|
||||
_globals['_XMRSPLITMESSAGE']._serialized_end=1459
|
||||
_globals['_XMRBIDACCEPTMESSAGE']._serialized_start=1462
|
||||
_globals['_XMRBIDACCEPTMESSAGE']._serialized_end=1718
|
||||
_globals['_XMRBIDLOCKTXSIGSMESSAGE']._serialized_start=1720
|
||||
_globals['_XMRBIDLOCKTXSIGSMESSAGE']._serialized_end=1834
|
||||
_globals['_XMRBIDLOCKSPENDTXMESSAGE']._serialized_start=1836
|
||||
_globals['_XMRBIDLOCKSPENDTXMESSAGE']._serialized_end=1924
|
||||
_globals['_XMRBIDLOCKRELEASEMESSAGE']._serialized_start=1926
|
||||
_globals['_XMRBIDLOCKRELEASEMESSAGE']._serialized_end=2003
|
||||
_globals['_ADSBIDINTENTMESSAGE']._serialized_start=2006
|
||||
_globals['_ADSBIDINTENTMESSAGE']._serialized_end=2149
|
||||
_globals['_ADSBIDINTENTACCEPTMESSAGE']._serialized_start=2151
|
||||
_globals['_ADSBIDINTENTACCEPTMESSAGE']._serialized_end=2263
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
@@ -24,7 +24,6 @@ import queue
|
||||
import random
|
||||
import select
|
||||
import socket
|
||||
import struct
|
||||
import hashlib
|
||||
import logging
|
||||
import secrets
|
||||
@@ -41,7 +40,7 @@ from basicswap.contrib.rfc6979 import (
|
||||
|
||||
|
||||
START_TOKEN = 0xabcd
|
||||
MSG_START_TOKEN = struct.pack('>H', START_TOKEN)
|
||||
MSG_START_TOKEN = START_TOKEN.to_bytes(2, 'big')
|
||||
|
||||
MSG_MAX_SIZE = 0x200000 # 2MB
|
||||
|
||||
@@ -83,8 +82,8 @@ class MsgHandshake:
|
||||
pass
|
||||
|
||||
def encode_aad(self): # Additional Authenticated Data
|
||||
return struct.pack('>H', NetMessageTypes.HANDSHAKE) + \
|
||||
struct.pack('>Q', self._timestamp) + \
|
||||
return int(NetMessageTypes.HANDSHAKE).to_bytes(2, 'big') + \
|
||||
self._timestamp.to_bytes(8, 'big') + \
|
||||
self._ephem_pk
|
||||
|
||||
def encode(self):
|
||||
@@ -92,7 +91,7 @@ class MsgHandshake:
|
||||
|
||||
def decode(self, msg_mv):
|
||||
o = 2
|
||||
self._timestamp = struct.unpack('>Q', msg_mv[o: o + 8])[0]
|
||||
self._timestamp = int.from_bytes(msg_mv[o: o + 8], 'big')
|
||||
o += 8
|
||||
self._ephem_pk = bytes(msg_mv[o: o + 33])
|
||||
o += 33
|
||||
@@ -333,7 +332,7 @@ class Network:
|
||||
|
||||
ss = k.ecdh(peer._pubkey)
|
||||
|
||||
hashed = hashlib.sha512(ss + struct.pack('>Q', msg._timestamp)).digest()
|
||||
hashed = hashlib.sha512(ss + msg._timestamp.to_bytes(8, 'big')).digest()
|
||||
peer._ke = hashed[:32]
|
||||
peer._km = hashed[32:]
|
||||
|
||||
@@ -386,7 +385,7 @@ class Network:
|
||||
nk = PrivateKey(self._network_key)
|
||||
ss = nk.ecdh(msg._ephem_pk)
|
||||
|
||||
hashed = hashlib.sha512(ss + struct.pack('>Q', msg._timestamp)).digest()
|
||||
hashed = hashlib.sha512(ss + msg._timestamp.to_bytes(8, 'big')).digest()
|
||||
peer._ke = hashed[:32]
|
||||
peer._km = hashed[32:]
|
||||
|
||||
@@ -427,7 +426,7 @@ class Network:
|
||||
mac = msg_mv[-16:]
|
||||
plaintext = cipher.decrypt_and_verify(msg_mv[2: -16], mac)
|
||||
|
||||
ping_nonce = struct.unpack('>I', plaintext[:4])[0]
|
||||
ping_nonce = int.from_bytes(plaintext[:4], 'big')
|
||||
# Version is added to a ping following a handshake message
|
||||
if len(plaintext) >= 10:
|
||||
peer._ready = True
|
||||
@@ -450,7 +449,7 @@ class Network:
|
||||
mac = msg_mv[-16:]
|
||||
plaintext = cipher.decrypt_and_verify(msg_mv[2: -16], mac)
|
||||
|
||||
pong_nonce = struct.unpack('>I', plaintext[:4])[0]
|
||||
pong_nonce = int.from_bytes(plaintext[:4], 'big')
|
||||
|
||||
if pong_nonce == peer._ping_nonce:
|
||||
peer._last_ping_rtt = (time.time_ns() // 1000) - peer._last_ping_at
|
||||
@@ -462,14 +461,14 @@ class Network:
|
||||
def send_ping(self, peer):
|
||||
ping_nonce = random.getrandbits(32)
|
||||
|
||||
msg_bytes = struct.pack('>H', NetMessageTypes.PING)
|
||||
msg_bytes = int(NetMessageTypes.PING).to_bytes(2, 'big')
|
||||
nonce = peer._sent_nonce[:24]
|
||||
|
||||
cipher = ChaCha20_Poly1305.new(key=peer._ke, nonce=nonce)
|
||||
cipher.update(msg_bytes)
|
||||
cipher.update(nonce)
|
||||
|
||||
payload = struct.pack('>I', ping_nonce)
|
||||
payload = ping_nonce.to_bytes(4, 'big')
|
||||
if peer._last_ping_at == 0:
|
||||
payload += self._sc._version
|
||||
ct, mac = cipher.encrypt_and_digest(payload)
|
||||
@@ -484,14 +483,14 @@ class Network:
|
||||
self.send_msg(peer, msg_bytes)
|
||||
|
||||
def send_pong(self, peer, ping_nonce):
|
||||
msg_bytes = struct.pack('>H', NetMessageTypes.PONG)
|
||||
msg_bytes = int(NetMessageTypes.PONG).to_bytes(2, 'big')
|
||||
nonce = peer._sent_nonce[:24]
|
||||
|
||||
cipher = ChaCha20_Poly1305.new(key=peer._ke, nonce=nonce)
|
||||
cipher.update(msg_bytes)
|
||||
cipher.update(nonce)
|
||||
|
||||
payload = struct.pack('>I', ping_nonce)
|
||||
payload = ping_nonce.to_bytes(4, 'big')
|
||||
ct, mac = cipher.encrypt_and_digest(payload)
|
||||
msg_bytes += ct + mac
|
||||
|
||||
@@ -503,7 +502,7 @@ class Network:
|
||||
msg_encoded = msg if isinstance(msg, bytes) else msg.encode()
|
||||
len_encoded = len(msg_encoded)
|
||||
|
||||
msg_packed = bytearray(MSG_START_TOKEN) + struct.pack('>I', len_encoded) + msg_encoded
|
||||
msg_packed = bytearray(MSG_START_TOKEN) + len_encoded.to_bytes(4, 'big') + msg_encoded
|
||||
peer._socket.sendall(msg_packed)
|
||||
|
||||
peer._bytes_sent += len_encoded
|
||||
@@ -515,7 +514,7 @@ class Network:
|
||||
try:
|
||||
mv = memoryview(msg_bytes)
|
||||
o = 0
|
||||
msg_type = struct.unpack('>H', mv[o: o + 2])[0]
|
||||
msg_type = int.from_bytes(mv[o: o + 2], 'big')
|
||||
if msg_type == NetMessageTypes.HANDSHAKE:
|
||||
self.process_handshake(peer, mv)
|
||||
elif msg_type == NetMessageTypes.PING:
|
||||
@@ -548,13 +547,13 @@ class Network:
|
||||
raise ValueError('Invalid start token')
|
||||
o += 2
|
||||
|
||||
msg_len = struct.unpack('>I', mv[o: o + 4])[0]
|
||||
msg_len = int.from_bytes(mv[o: o + 4], 'big')
|
||||
o += 4
|
||||
if msg_len < 2 or msg_len > MSG_MAX_SIZE:
|
||||
raise ValueError('Invalid data length')
|
||||
|
||||
# Precheck msg_type
|
||||
msg_type = struct.unpack('>H', mv[o: o + 2])[0]
|
||||
msg_type = int.from_bytes(mv[o: o + 2], 'big')
|
||||
# o += 2 # Don't inc offset, msg includes type
|
||||
if not NetMessageTypes.has_value(msg_type):
|
||||
raise ValueError('Invalid msg type')
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2022-2023 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from basicswap.script import (
|
||||
OpCodes,
|
||||
)
|
||||
from basicswap.interface.btc import (
|
||||
find_vout_for_address_from_txobj,
|
||||
)
|
||||
|
||||
|
||||
class ProtocolInterface:
|
||||
swap_type = None
|
||||
|
||||
def getFundedInitiateTxTemplate(self, ci, amount: int, sub_fee: bool) -> bytes:
|
||||
raise ValueError('base class')
|
||||
|
||||
def getMockScript(self) -> bytearray:
|
||||
return bytearray([
|
||||
OpCodes.OP_RETURN, OpCodes.OP_1])
|
||||
|
||||
def getMockScriptScriptPubkey(self, ci) -> bytearray:
|
||||
script = self.getMockScript()
|
||||
return ci.getScriptDest(script) if ci._use_segwit else ci.get_p2sh_script_pubkey(script)
|
||||
|
||||
def getMockAddrTo(self, ci):
|
||||
script = self.getMockScript()
|
||||
return ci.encodeScriptDest(ci.getScriptDest(script)) if ci._use_segwit else ci.encode_p2sh(script)
|
||||
|
||||
def findMockVout(self, ci, itx_decoded):
|
||||
mock_addr = self.getMockAddrTo(ci)
|
||||
return find_vout_for_address_from_txobj(itx_decoded, mock_addr)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2020-2022 tecnovert
|
||||
# Copyright (c) 2020-2023 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
@@ -14,14 +14,16 @@ from basicswap.script import (
|
||||
OpCodes,
|
||||
)
|
||||
from basicswap.basicswap_util import (
|
||||
SwapTypes,
|
||||
EventLogTypes,
|
||||
)
|
||||
from . import ProtocolInterface
|
||||
|
||||
INITIATE_TX_TIMEOUT = 40 * 60 # TODO: make variable per coin
|
||||
ABS_LOCK_TIME_LEEWAY = 10 * 60
|
||||
|
||||
|
||||
def buildContractScript(lock_val, secret_hash, pkh_redeem, pkh_refund, op_lock=OpCodes.OP_CHECKSEQUENCEVERIFY):
|
||||
def buildContractScript(lock_val: int, secret_hash: bytes, pkh_redeem: bytes, pkh_refund: bytes, op_lock=OpCodes.OP_CHECKSEQUENCEVERIFY) -> bytearray:
|
||||
script = bytearray([
|
||||
OpCodes.OP_IF,
|
||||
OpCodes.OP_SIZE,
|
||||
@@ -56,7 +58,7 @@ def extractScriptSecretHash(script):
|
||||
return script[7:39]
|
||||
|
||||
|
||||
def redeemITx(self, bid_id, session):
|
||||
def redeemITx(self, bid_id: bytes, session):
|
||||
bid, offer = self.getBidAndOffer(bid_id, session)
|
||||
ci_from = self.ci(offer.coin_from)
|
||||
|
||||
@@ -66,3 +68,33 @@ def redeemITx(self, bid_id, session):
|
||||
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())
|
||||
self.logEvent(Concepts.BID, bid_id, EventLogTypes.ITX_REDEEM_PUBLISHED, '', session)
|
||||
|
||||
|
||||
class AtomicSwapInterface(ProtocolInterface):
|
||||
swap_type = SwapTypes.SELLER_FIRST
|
||||
|
||||
def getFundedInitiateTxTemplate(self, ci, amount: int, sub_fee: bool) -> bytes:
|
||||
addr_to = self.getMockAddrTo(ci)
|
||||
funded_tx = ci.createRawFundedTransaction(addr_to, amount, sub_fee, lock_unspents=False)
|
||||
|
||||
return bytes.fromhex(funded_tx)
|
||||
|
||||
def promoteMockTx(self, ci, mock_tx: bytes, script: bytearray) -> bytearray:
|
||||
mock_txo_script = self.getMockScriptScriptPubkey(ci)
|
||||
real_txo_script = ci.getScriptDest(script) if ci._use_segwit else ci.get_p2sh_script_pubkey(script)
|
||||
|
||||
found: int = 0
|
||||
ctx = ci.loadTx(mock_tx)
|
||||
for txo in ctx.vout:
|
||||
if txo.scriptPubKey == mock_txo_script:
|
||||
txo.scriptPubKey = real_txo_script
|
||||
found += 1
|
||||
|
||||
if found < 1:
|
||||
raise ValueError('Mocked output not found')
|
||||
if found > 1:
|
||||
raise ValueError('Too many mocked outputs found')
|
||||
ctx.nLockTime = 0
|
||||
|
||||
funded_tx = ctx.serialize()
|
||||
return ci.signTxWithWallet(funded_tx)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2020 tecnovert
|
||||
# Copyright (c) 2020-2023 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
@@ -9,13 +9,19 @@ from sqlalchemy.orm import scoped_session
|
||||
from basicswap.util import (
|
||||
ensure,
|
||||
)
|
||||
from basicswap.interface import Curves
|
||||
from basicswap.chainparams import (
|
||||
Coins,
|
||||
)
|
||||
from basicswap.basicswap_util import (
|
||||
KeyTypes,
|
||||
SwapTypes,
|
||||
EventLogTypes,
|
||||
)
|
||||
from . import ProtocolInterface
|
||||
from basicswap.contrib.test_framework.script import (
|
||||
CScript, CScriptOp,
|
||||
OP_CHECKMULTISIG)
|
||||
|
||||
|
||||
def addLockRefundSigs(self, xmr_swap, ci):
|
||||
@@ -32,17 +38,17 @@ def addLockRefundSigs(self, xmr_swap, ci):
|
||||
xmr_swap.a_lock_refund_tx = signed_tx
|
||||
|
||||
|
||||
def recoverNoScriptTxnWithKey(self, bid_id, encoded_key):
|
||||
def recoverNoScriptTxnWithKey(self, bid_id: bytes, encoded_key):
|
||||
self.log.info('Manually recovering %s', bid_id.hex())
|
||||
# Manually recover txn if other key is known
|
||||
session = scoped_session(self.session_factory)
|
||||
try:
|
||||
bid, xmr_swap = self.getXmrBidFromSession(session, bid_id)
|
||||
ensure(bid, 'Bid not found: {}.'.format(bid_id.hex()))
|
||||
ensure(xmr_swap, 'XMR swap not found: {}.'.format(bid_id.hex()))
|
||||
ensure(xmr_swap, 'Adaptor-sig swap not found: {}.'.format(bid_id.hex()))
|
||||
offer, xmr_offer = self.getXmrOfferFromSession(session, bid.offer_id, sent=False)
|
||||
ensure(offer, 'Offer not found: {}.'.format(bid.offer_id.hex()))
|
||||
ensure(xmr_offer, 'XMR offer not found: {}.'.format(bid.offer_id.hex()))
|
||||
ensure(xmr_offer, 'Adaptor-sig offer not found: {}.'.format(bid.offer_id.hex()))
|
||||
ci_to = self.ci(offer.coin_to)
|
||||
|
||||
for_ed25519 = True if Coins(offer.coin_to) == Coins.XMR else False
|
||||
@@ -80,7 +86,78 @@ def recoverNoScriptTxnWithKey(self, bid_id, encoded_key):
|
||||
|
||||
|
||||
def getChainBSplitKey(swap_client, bid, xmr_swap, offer):
|
||||
ci_to = swap_client.ci(offer.coin_to)
|
||||
reverse_bid: bool = offer.bid_reversed
|
||||
ci_follower = swap_client.ci(offer.coin_from if reverse_bid else offer.coin_to)
|
||||
|
||||
key_type = KeyTypes.KBSF if bid.was_sent else KeyTypes.KBSL
|
||||
return ci_to.encodeKey(swap_client.getPathKey(offer.coin_from, offer.coin_to, bid.created_at, xmr_swap.contract_count, key_type, True if offer.coin_to == Coins.XMR else False))
|
||||
return ci_follower.encodeKey(swap_client.getPathKey(offer.coin_from, offer.coin_to, bid.created_at, xmr_swap.contract_count, key_type, True if ci_follower.coin_type() == Coins.XMR else False))
|
||||
|
||||
|
||||
def getChainBRemoteSplitKey(swap_client, bid, xmr_swap, offer):
|
||||
reverse_bid: bool = offer.bid_reversed
|
||||
ci_leader = swap_client.ci(offer.coin_to if reverse_bid else offer.coin_from)
|
||||
ci_follower = swap_client.ci(offer.coin_from if reverse_bid else offer.coin_to)
|
||||
|
||||
if bid.was_sent:
|
||||
if xmr_swap.a_lock_refund_spend_tx:
|
||||
af_lock_refund_spend_tx_sig = ci_leader.extractFollowerSig(xmr_swap.a_lock_refund_spend_tx)
|
||||
kbsl = ci_leader.recoverEncKey(xmr_swap.af_lock_refund_spend_tx_esig, af_lock_refund_spend_tx_sig, xmr_swap.pkasl)
|
||||
return ci_follower.encodeKey(kbsl)
|
||||
else:
|
||||
if xmr_swap.a_lock_spend_tx:
|
||||
al_lock_spend_tx_sig = ci_leader.extractLeaderSig(xmr_swap.a_lock_spend_tx)
|
||||
kbsf = ci_leader.recoverEncKey(xmr_swap.al_lock_spend_tx_esig, al_lock_spend_tx_sig, xmr_swap.pkasf)
|
||||
return ci_follower.encodeKey(kbsf)
|
||||
return None
|
||||
|
||||
|
||||
def setDLEAG(xmr_swap, ci_to, kbsf: bytes) -> None:
|
||||
if ci_to.curve_type() == Curves.ed25519:
|
||||
xmr_swap.kbsf_dleag = ci_to.proveDLEAG(kbsf)
|
||||
xmr_swap.pkasf = xmr_swap.kbsf_dleag[0: 33]
|
||||
elif ci_to.curve_type() == Curves.secp256k1:
|
||||
for i in range(10):
|
||||
xmr_swap.kbsf_dleag = ci_to.signRecoverable(kbsf, 'proof kbsf owned for swap')
|
||||
pk_recovered: bytes = ci_to.verifySigAndRecover(xmr_swap.kbsf_dleag, 'proof kbsf owned for swap')
|
||||
if pk_recovered == xmr_swap.pkbsf:
|
||||
break
|
||||
# self.log.debug('kbsl recovered pubkey mismatch, retrying.')
|
||||
assert (pk_recovered == xmr_swap.pkbsf)
|
||||
xmr_swap.pkasf = xmr_swap.pkbsf
|
||||
else:
|
||||
raise ValueError('Unknown curve')
|
||||
|
||||
|
||||
class XmrSwapInterface(ProtocolInterface):
|
||||
swap_type = SwapTypes.XMR_SWAP
|
||||
|
||||
def genScriptLockTxScript(self, ci, Kal: bytes, Kaf: bytes) -> CScript:
|
||||
Kal_enc = Kal if len(Kal) == 33 else ci.encodePubkey(Kal)
|
||||
Kaf_enc = Kaf if len(Kaf) == 33 else ci.encodePubkey(Kaf)
|
||||
|
||||
return CScript([2, Kal_enc, Kaf_enc, 2, CScriptOp(OP_CHECKMULTISIG)])
|
||||
|
||||
def getFundedInitiateTxTemplate(self, ci, amount: int, sub_fee: bool) -> bytes:
|
||||
addr_to = self.getMockAddrTo(ci)
|
||||
funded_tx = ci.createRawFundedTransaction(addr_to, amount, sub_fee, lock_unspents=False)
|
||||
|
||||
return bytes.fromhex(funded_tx)
|
||||
|
||||
def promoteMockTx(self, ci, mock_tx: bytes, script: bytearray) -> bytearray:
|
||||
mock_txo_script = self.getMockScriptScriptPubkey(ci)
|
||||
real_txo_script = ci.getScriptDest(script)
|
||||
|
||||
found: int = 0
|
||||
ctx = ci.loadTx(mock_tx)
|
||||
for txo in ctx.vout:
|
||||
if txo.scriptPubKey == mock_txo_script:
|
||||
txo.scriptPubKey = real_txo_script
|
||||
found += 1
|
||||
|
||||
if found < 1:
|
||||
raise ValueError('Mocked output not found')
|
||||
if found > 1:
|
||||
raise ValueError('Too many mocked outputs found')
|
||||
ctx.nLockTime = 0
|
||||
|
||||
return ctx.serialize()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2020-2022 tecnovert
|
||||
# Copyright (c) 2020-2023 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
@@ -23,10 +23,7 @@ from .util import jsonDecimal
|
||||
def waitForRPC(rpc_func, expect_wallet=True, max_tries=7):
|
||||
for i in range(max_tries + 1):
|
||||
try:
|
||||
if expect_wallet:
|
||||
rpc_func('getwalletinfo')
|
||||
else:
|
||||
rpc_func('getblockchaininfo')
|
||||
rpc_func('getwalletinfo' if expect_wallet else 'getblockchaininfo')
|
||||
return
|
||||
except Exception as ex:
|
||||
if i < max_tries:
|
||||
@@ -111,7 +108,7 @@ def callrpc(rpc_port, auth, method, params=[], wallet=None, host='127.0.0.1'):
|
||||
r = json.loads(v.decode('utf-8'))
|
||||
except Exception as ex:
|
||||
traceback.print_exc()
|
||||
raise ValueError('RPC server error ' + str(ex))
|
||||
raise ValueError('RPC server error ' + str(ex) + ', method: ' + method)
|
||||
|
||||
if 'error' in r and r['error'] is not None:
|
||||
raise ValueError('RPC error ' + str(r['error']))
|
||||
@@ -130,13 +127,15 @@ def openrpc(rpc_port, auth, wallet=None, host='127.0.0.1'):
|
||||
raise ValueError('RPC error ' + str(ex))
|
||||
|
||||
|
||||
def callrpc_cli(bindir, datadir, chain, cmd, cli_bin='particl-cli'):
|
||||
def callrpc_cli(bindir, datadir, chain, cmd, cli_bin='particl-cli', wallet=None):
|
||||
cli_bin = os.path.join(bindir, cli_bin)
|
||||
|
||||
args = [cli_bin, ]
|
||||
if chain != 'mainnet':
|
||||
args.append('-' + chain)
|
||||
args.append('-datadir=' + datadir)
|
||||
if wallet is not None:
|
||||
args.append('-rpcwallet=' + wallet)
|
||||
args += shlex.split(cmd)
|
||||
|
||||
p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
@@ -163,3 +162,9 @@ def make_rpc_func(port, auth, wallet=None, host='127.0.0.1'):
|
||||
nonlocal port, auth, wallet, host
|
||||
return callrpc(port, auth, method, params, wallet if wallet_override is None else wallet_override, host)
|
||||
return rpc_func
|
||||
|
||||
|
||||
def escape_rpcauth(auth_str: str) -> str:
|
||||
username, password = auth_str.split(':', 1)
|
||||
password = urllib.parse.quote(password, safe='')
|
||||
return f'{username}:{password}'
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import os
|
||||
import json
|
||||
import socks
|
||||
import time
|
||||
import urllib
|
||||
import hashlib
|
||||
@@ -10,9 +11,32 @@ from xmlrpc.client import (
|
||||
Transport,
|
||||
SafeTransport,
|
||||
)
|
||||
from sockshandler import SocksiPyConnection
|
||||
from .util import jsonDecimal
|
||||
|
||||
|
||||
class SocksTransport(Transport):
|
||||
|
||||
def set_proxy(self, proxy_host, proxy_port):
|
||||
self.proxy_host = proxy_host
|
||||
self.proxy_port = proxy_port
|
||||
|
||||
self.proxy_type = socks.PROXY_TYPE_SOCKS5
|
||||
self.proxy_rdns = True
|
||||
self.proxy_username = None
|
||||
self.proxy_password = None
|
||||
|
||||
def make_connection(self, host):
|
||||
# return an existing connection if possible. This allows
|
||||
# HTTP/1.1 keep-alive.
|
||||
if self._connection and host == self._connection[0]:
|
||||
return self._connection[1]
|
||||
# create a HTTP connection object from a host descriptor
|
||||
chost, self._extra_headers, x509 = self.get_host_info(host)
|
||||
self._connection = host, SocksiPyConnection(self.proxy_type, self.proxy_host, self.proxy_port, self.proxy_rdns, self.proxy_username, self.proxy_password, chost)
|
||||
return self._connection[1]
|
||||
|
||||
|
||||
class JsonrpcDigest():
|
||||
# __getattr__ complicates extending ServerProxy
|
||||
def __init__(self, uri, transport=None, encoding=None, verbose=False,
|
||||
@@ -37,12 +61,15 @@ class JsonrpcDigest():
|
||||
self.__verbose = verbose
|
||||
self.__allow_none = allow_none
|
||||
|
||||
self.__request_id = 1
|
||||
self.__request_id = 0
|
||||
|
||||
def close(self):
|
||||
if self.__transport is not None:
|
||||
self.__transport.close()
|
||||
|
||||
def request_id(self):
|
||||
return self.__request_id
|
||||
|
||||
def post_request(self, method, params, timeout=None):
|
||||
try:
|
||||
connection = self.__transport.make_connection(self.__host)
|
||||
@@ -66,7 +93,7 @@ class JsonrpcDigest():
|
||||
self.__transport.close()
|
||||
raise
|
||||
|
||||
def json_request(self, method, params, username='', password='', timeout=None):
|
||||
def json_request(self, request_body, username='', password='', timeout=None):
|
||||
try:
|
||||
connection = self.__transport.make_connection(self.__host)
|
||||
if timeout:
|
||||
@@ -74,18 +101,11 @@ class JsonrpcDigest():
|
||||
|
||||
headers = self.__transport._extra_headers[:]
|
||||
|
||||
request_body = {
|
||||
'method': method,
|
||||
'params': params,
|
||||
'jsonrpc': '2.0',
|
||||
'id': self.__request_id
|
||||
}
|
||||
|
||||
connection.putrequest('POST', self.__handler)
|
||||
headers.append(('Content-Type', 'application/json'))
|
||||
headers.append(('Connection', 'keep-alive'))
|
||||
self.__transport.send_headers(connection, headers)
|
||||
self.__transport.send_content(connection, json.dumps(request_body, default=jsonDecimal).encode('utf-8'))
|
||||
self.__transport.send_content(connection, json.dumps(request_body, default=jsonDecimal).encode('utf-8') if request_body else '')
|
||||
resp = connection.getresponse()
|
||||
|
||||
if resp.status == 401:
|
||||
@@ -135,18 +155,11 @@ class JsonrpcDigest():
|
||||
headers = self.__transport._extra_headers[:]
|
||||
headers.append(('Authorization', header_value))
|
||||
|
||||
request_body = {
|
||||
'method': method,
|
||||
'params': params,
|
||||
'jsonrpc': '2.0',
|
||||
'id': self.__request_id
|
||||
}
|
||||
|
||||
connection.putrequest('POST', self.__handler)
|
||||
headers.append(('Content-Type', 'application/json'))
|
||||
headers.append(('Connection', 'keep-alive'))
|
||||
self.__transport.send_headers(connection, headers)
|
||||
self.__transport.send_content(connection, json.dumps(request_body, default=jsonDecimal).encode('utf-8'))
|
||||
self.__transport.send_content(connection, json.dumps(request_body, default=jsonDecimal).encode('utf-8') if request_body else '')
|
||||
resp = connection.getresponse()
|
||||
|
||||
self.__request_id += 1
|
||||
@@ -159,7 +172,7 @@ class JsonrpcDigest():
|
||||
raise
|
||||
|
||||
|
||||
def callrpc_xmr(rpc_port, auth, method, params=[], rpc_host='127.0.0.1', path='json_rpc', timeout=120):
|
||||
def callrpc_xmr(rpc_port, method, params=[], rpc_host='127.0.0.1', path='json_rpc', auth=None, timeout=120, transport=None, tag=''):
|
||||
# auth is a tuple: (username, password)
|
||||
try:
|
||||
if rpc_host.count('://') > 0:
|
||||
@@ -167,84 +180,79 @@ def callrpc_xmr(rpc_port, auth, method, params=[], rpc_host='127.0.0.1', path='j
|
||||
else:
|
||||
url = 'http://{}:{}/{}'.format(rpc_host, rpc_port, path)
|
||||
|
||||
x = JsonrpcDigest(url)
|
||||
v = x.json_request(method, params, username=auth[0], password=auth[1], timeout=timeout)
|
||||
x.close()
|
||||
r = json.loads(v.decode('utf-8'))
|
||||
except Exception as ex:
|
||||
raise ValueError('RPC Server Error: {}'.format(str(ex)))
|
||||
|
||||
if 'error' in r and r['error'] is not None:
|
||||
raise ValueError('RPC error ' + str(r['error']))
|
||||
|
||||
return r['result']
|
||||
|
||||
|
||||
def callrpc_xmr_na(rpc_port, method, params=[], rpc_host='127.0.0.1', path='json_rpc', timeout=120):
|
||||
try:
|
||||
if rpc_host.count('://') > 0:
|
||||
url = '{}:{}/{}'.format(rpc_host, rpc_port, path)
|
||||
x = JsonrpcDigest(url, transport=transport)
|
||||
request_body = {
|
||||
'method': method,
|
||||
'params': params,
|
||||
'jsonrpc': '2.0',
|
||||
'id': x.request_id()
|
||||
}
|
||||
if auth:
|
||||
v = x.json_request(request_body, username=auth[0], password=auth[1], timeout=timeout)
|
||||
else:
|
||||
url = 'http://{}:{}/{}'.format(rpc_host, rpc_port, path)
|
||||
|
||||
x = JsonrpcDigest(url)
|
||||
v = x.json_request(method, params, timeout=timeout)
|
||||
v = x.json_request(request_body, timeout=timeout)
|
||||
x.close()
|
||||
r = json.loads(v.decode('utf-8'))
|
||||
except Exception as ex:
|
||||
raise ValueError('RPC Server Error: {}'.format(str(ex)))
|
||||
raise ValueError('{}RPC Server Error: {}'.format(tag, str(ex)))
|
||||
|
||||
if 'error' in r and r['error'] is not None:
|
||||
raise ValueError('RPC error ' + str(r['error']))
|
||||
raise ValueError(tag + 'RPC error ' + str(r['error']))
|
||||
|
||||
return r['result']
|
||||
|
||||
|
||||
def callrpc_xmr2(rpc_port, method, params=None, rpc_host='127.0.0.1', timeout=120):
|
||||
def callrpc_xmr2(rpc_port: int, method: str, params=None, auth=None, rpc_host='127.0.0.1', timeout=120, transport=None, tag=''):
|
||||
try:
|
||||
if rpc_host.count('://') > 0:
|
||||
url = '{}:{}/{}'.format(rpc_host, rpc_port, method)
|
||||
else:
|
||||
url = 'http://{}:{}/{}'.format(rpc_host, rpc_port, method)
|
||||
|
||||
x = JsonrpcDigest(url)
|
||||
v = x.post_request(method, params, timeout=timeout)
|
||||
x = JsonrpcDigest(url, transport=transport)
|
||||
if auth:
|
||||
v = x.json_request(params, username=auth[0], password=auth[1], timeout=timeout)
|
||||
else:
|
||||
v = x.json_request(params, timeout=timeout)
|
||||
x.close()
|
||||
r = json.loads(v.decode('utf-8'))
|
||||
except Exception as ex:
|
||||
raise ValueError('RPC Server Error: {}'.format(str(ex)))
|
||||
raise ValueError('{}RPC Server Error: {}'.format(tag, str(ex)))
|
||||
|
||||
return r
|
||||
|
||||
|
||||
def make_xmr_rpc_func(port, host='127.0.0.1'):
|
||||
port = port
|
||||
host = host
|
||||
|
||||
def rpc_func(method, params=None, wallet=None, timeout=120):
|
||||
nonlocal port
|
||||
nonlocal host
|
||||
return callrpc_xmr_na(port, method, params, rpc_host=host, timeout=timeout)
|
||||
return rpc_func
|
||||
|
||||
|
||||
def make_xmr_rpc2_func(port, host='127.0.0.1'):
|
||||
port = port
|
||||
host = host
|
||||
|
||||
def rpc_func(method, params=None, wallet=None, timeout=120):
|
||||
nonlocal port
|
||||
nonlocal host
|
||||
return callrpc_xmr2(port, method, params, rpc_host=host, timeout=timeout)
|
||||
return rpc_func
|
||||
|
||||
|
||||
def make_xmr_wallet_rpc_func(port, auth, host='127.0.0.1'):
|
||||
def make_xmr_rpc2_func(port, auth, host='127.0.0.1', proxy_host=None, proxy_port=None, default_timeout=120, tag=''):
|
||||
port = port
|
||||
auth = auth
|
||||
host = host
|
||||
transport = None
|
||||
default_timeout = default_timeout
|
||||
tag = tag
|
||||
|
||||
def rpc_func(method, params=None, wallet=None, timeout=120):
|
||||
nonlocal port, auth, host
|
||||
return callrpc_xmr(port, auth, method, params, rpc_host=host, timeout=timeout)
|
||||
if proxy_host:
|
||||
transport = SocksTransport()
|
||||
transport.set_proxy(proxy_host, proxy_port)
|
||||
|
||||
def rpc_func(method, params=None, wallet=None, timeout=default_timeout):
|
||||
nonlocal port, auth, host, transport, tag
|
||||
return callrpc_xmr2(port, method, params, auth=auth, rpc_host=host, timeout=timeout, transport=transport, tag=tag)
|
||||
return rpc_func
|
||||
|
||||
|
||||
def make_xmr_rpc_func(port, auth, host='127.0.0.1', proxy_host=None, proxy_port=None, default_timeout=120, tag=''):
|
||||
port = port
|
||||
auth = auth
|
||||
host = host
|
||||
transport = None
|
||||
default_timeout = default_timeout
|
||||
tag = tag
|
||||
|
||||
if proxy_host:
|
||||
transport = SocksTransport()
|
||||
transport.set_proxy(proxy_host, proxy_port)
|
||||
|
||||
def rpc_func(method, params=None, wallet=None, timeout=default_timeout):
|
||||
nonlocal port, auth, host, transport, tag
|
||||
return callrpc_xmr(port, method, params, rpc_host=host, auth=auth, timeout=timeout, transport=transport, tag=tag)
|
||||
return rpc_func
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- 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.
|
||||
|
||||
@@ -15,6 +15,7 @@ class OpCodes(IntEnum):
|
||||
OP_IF = 0x63,
|
||||
OP_ELSE = 0x67,
|
||||
OP_ENDIF = 0x68,
|
||||
OP_RETURN = 0x6a,
|
||||
OP_DROP = 0x75,
|
||||
OP_DUP = 0x76,
|
||||
OP_SIZE = 0x82,
|
||||
|
||||
179
basicswap/static/css/libs/dd.min.css
vendored
@@ -1,179 +0,0 @@
|
||||
/*! *
|
||||
/** * * MSDropdown - dd.js * * @author: Marghoob Suleman * * @website: https://www.marghoobsuleman.com/ * * @version: 4.0.2 * * @date: Wed Oct 13 2021 15:19:38 GMT+0530 (India Standard Time) * * msDropdown is free web component: you can redistribute it and/or modify * * it under the terms of the either the MIT License or the Gnu General Public License (GPL) Version 2 * * / */
|
||||
/*! */
|
||||
.ms-pr {
|
||||
position: relative;
|
||||
border-width: 1px;
|
||||
border-color: #fff
|
||||
}
|
||||
.blaat:active {
|
||||
border-radius: 0.5rem;
|
||||
border-width: 1px;
|
||||
border-color: #3b82f6;
|
||||
outline: none !important
|
||||
}
|
||||
.blaat:focus {
|
||||
border-radius: 0.5rem;
|
||||
border-width: 1px;
|
||||
border-color: #3b82f6;
|
||||
outline: none !important
|
||||
}
|
||||
.ms-dd {
|
||||
border-radius: 0.5rem;
|
||||
border-width: 1px;
|
||||
border-color: #d1d5db;
|
||||
background: none
|
||||
}
|
||||
.ms-list-option:active {
|
||||
border-radius: 0.5rem;
|
||||
border-width: 1px;
|
||||
border-color: #3b82f6;
|
||||
outline: none !important
|
||||
}
|
||||
.ms-dd .ms-value-input {
|
||||
left: 20px;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
width: 50%
|
||||
}
|
||||
.ms-dd .ms-dd-header {
|
||||
c cursor: pointer;
|
||||
min-height: 35px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
.ms-dd .ms-dd-header .option-selected {
|
||||
color: #333f51;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
padding: 9px 19px 9px 9px;
|
||||
pointer-events: none;
|
||||
border-radius: 0.5rem;
|
||||
border-width: 1px;
|
||||
border-color: #f9fafb;
|
||||
background: none
|
||||
}
|
||||
.ms-dd .ms-dd-header .ms-list-counter, .ms-dd .ms-dd-header input[type=checkbox] {
|
||||
display: none
|
||||
}
|
||||
.ms-dd .ms-dd-header .ms-header-counter {
|
||||
color: #0e76a8
|
||||
}
|
||||
.ms-dd .ms-dd-header .ms-filter-box {
|
||||
box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
padding: 6px 10px;
|
||||
min-height: 44px
|
||||
}
|
||||
.ms-dd .ms-dd-option-image, .ms-dd .ms-dd-selected-img {
|
||||
margin-right: 5px;
|
||||
max-width: 40px;
|
||||
vertical-align: middle
|
||||
}
|
||||
.ms-dd .ms-dd-arrow {
|
||||
height: 0;
|
||||
margin-top: -3px;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 50%;
|
||||
width: 0
|
||||
}
|
||||
.ms-dd .ms-dd-arrow.ms-dd-pointer-down {
|
||||
display: none
|
||||
}
|
||||
.ms-dd .ms-dd-arrow.ms-dd-pointer-up {
|
||||
display: none
|
||||
}
|
||||
.ms-dd .ms-options {
|
||||
background: #fff;
|
||||
border: 1px solid #767676;
|
||||
box-shadow: 0 1px 5px #ddd;
|
||||
display: none;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
overflow: auto;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
z-index: 9999
|
||||
}
|
||||
.ms-dd .ms-list-option, .ms-dd .ms-optgroup ul .ms-list-option {
|
||||
align-items: center;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
overflow: hidden;
|
||||
padding: 10px;
|
||||
position: relative;
|
||||
text-decoration: none
|
||||
}
|
||||
.ms-dd .ms-list-option input[type=checkbox], .ms-dd .ms-optgroup ul .ms-list-option input[type=checkbox] {
|
||||
margin-right: 5px;
|
||||
vertical-align: middle
|
||||
}
|
||||
.ms-dd .ms-list-option:last-child, .ms-dd .ms-optgroup ul .ms-list-option:last-child {
|
||||
border-bottom: none
|
||||
}
|
||||
.ms-dd .ms-list-option:hover, .ms-dd .ms-optgroup ul .ms-list-option:hover {
|
||||
background: #e0e0e6;
|
||||
color: #000
|
||||
}
|
||||
.ms-dd .ms-list-option.option-selected, .ms-dd .ms-optgroup ul .ms-list-option.option-selected {
|
||||
background: #f9fafb
|
||||
}
|
||||
.ms-dd .ms-list-option.ico-align-right .ms-dd-option-image, .ms-dd .ms-list-option.ico-align-right .ms-dd-selected-img, .ms-dd .ms-optgroup ul .ms-list-option.ico-align-right .ms-dd-option-image, .ms-dd .ms-optgroup ul .ms-list-option.ico-align-right .ms-dd-selected-img {
|
||||
order: 2
|
||||
}
|
||||
.ms-dd .ms-list-option.disabled, .ms-dd .ms-list-option:disabled, .ms-dd .ms-optgroup ul .ms-list-option.disabled, .ms-dd .ms-optgroup ul .ms-list-option:disabled {
|
||||
cursor: default;
|
||||
opacity: .4
|
||||
}
|
||||
.ms-dd .ms-list-option .ms-dd-desc, .ms-dd .ms-optgroup ul .ms-list-option .ms-dd-desc {
|
||||
color: #aaa;
|
||||
display: block;
|
||||
line-height: 1.5em;
|
||||
overflow: hidden;
|
||||
text-shadow: 0 1px .5px #fff
|
||||
}
|
||||
.ms-dd .ms-header-middle-content, .ms-dd .ms-middle {
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
font-size: 0.875rem;
|
||||
min-width: 100px
|
||||
}
|
||||
.ms-dd.disabled, .ms-dd:disabled {
|
||||
cursor: default;
|
||||
opacity: .4;
|
||||
pointer-events: none
|
||||
}
|
||||
.ms-dd .ms-optgroup-padding {
|
||||
padding: 10px 10px 0
|
||||
}
|
||||
.ms-dd .ms-optgroup {
|
||||
display: block
|
||||
}
|
||||
.ms-dd .ms-optgroup:hover {
|
||||
background: #fff;
|
||||
color: #000
|
||||
}
|
||||
.ms-dd .ms-optgroup ul {
|
||||
margin: 0;
|
||||
padding: 0
|
||||
}
|
||||
.ms-dd .ms-optgroup ul .ms-list-option {
|
||||
padding-left: 20px
|
||||
}
|
||||
.ms-dd .ms-optgroup ul:last-child {
|
||||
}
|
||||
.ms-dd .ms-dd-option-content {
|
||||
width: 100%
|
||||
}
|
||||
.ms-value-input {
|
||||
display:none;
|
||||
}
|
||||
1
basicswap/static/css/libs/flowbite.min.css
vendored
Normal file
232075
basicswap/static/css/libs/tailwind.min.css
vendored
@@ -17,18 +17,125 @@
|
||||
.floatright
|
||||
{
|
||||
position: fixed;
|
||||
top: 2em;
|
||||
right: 2em;
|
||||
margin: 0;
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
.error
|
||||
{
|
||||
border: 1px solid red;
|
||||
top:1.25rem;
|
||||
right:1.25rem;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.error_msg
|
||||
{
|
||||
color:red;
|
||||
}
|
||||
|
||||
#hide {
|
||||
-moz-animation: cssAnimation 0s ease-in 15s forwards;
|
||||
/* Firefox */
|
||||
-webkit-animation: cssAnimation 0s ease-in 15s forwards;
|
||||
/* Safari and Chrome */
|
||||
-o-animation: cssAnimation 0s ease-in 15s forwards;
|
||||
/* Opera */
|
||||
animation: cssAnimation 0s ease-in 15s forwards;
|
||||
-webkit-animation-fill-mode: forwards;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
@keyframes cssAnimation {
|
||||
to {
|
||||
width:0;
|
||||
height:0;
|
||||
overflow:hidden;
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes cssAnimation {
|
||||
to {
|
||||
width:0;
|
||||
height:0;
|
||||
visibility:hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-select .select {
|
||||
appearance: none;
|
||||
background-image: url('/static/images/other/coin.png');
|
||||
background-position: 10px center;
|
||||
background-repeat: no-repeat;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.custom-select select::-webkit-scrollbar {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.custom-select .select option {
|
||||
padding-left: 0;
|
||||
text-indent: 0;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 0 50%;
|
||||
}
|
||||
|
||||
.custom-select .select option.no-space {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.custom-select .select option[data-image] {
|
||||
background-image: url('');
|
||||
}
|
||||
|
||||
.custom-select .select-icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 10px;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.custom-select .select-image {
|
||||
display: none;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.custom-select .select:focus + .select-dropdown .select-image {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.blurred {
|
||||
filter: blur(4px);
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.error-overlay.non-blurred {
|
||||
filter: none;
|
||||
pointer-events: auto;
|
||||
user-select: auto;
|
||||
}
|
||||
|
||||
/* Disable opacity on disabled form elements in Chrome */
|
||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||
select:disabled,
|
||||
input:disabled,
|
||||
textarea:disabled {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add this to your existing CSS file */
|
||||
.error {
|
||||
border: 1px solid red !important;
|
||||
}
|
||||
|
||||
.active-container {
|
||||
position: relative;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.active-container::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border: 1px solid rgb(77, 132, 240);
|
||||
border-radius: inherit;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
BIN
basicswap/static/images/coins/Doge-20.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
basicswap/static/images/coins/Doge.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
basicswap/static/images/coins/Ethereum-20.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
basicswap/static/images/coins/Ethereum.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
basicswap/static/images/coins/Firo-20.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
basicswap/static/images/coins/Firo.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
basicswap/static/images/coins/Litecoin%MWEB.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
basicswap/static/images/coins/Litecoin-MWEB-20.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
basicswap/static/images/coins/Litecoin-MWEB.png
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
25
basicswap/static/images/elements/circle2-violet.svg
Normal file
@@ -0,0 +1,25 @@
|
||||
<svg width="219" height="219" viewBox="0 0 219 219" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<mask id="mask0_1384_30420" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="219" height="219">
|
||||
<circle cx="109.5" cy="109.5" r="107.99" fill="#C4C4C4" stroke="#F8BB54" stroke-width="3.02069"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_1384_30420)">
|
||||
<path d="M-63.6362 45.9223C-63.6362 45.9223 -12.6466 36.4769 0.509327 3.41839C13.0912 -28.2719 64.9198 -36.79 64.9198 -36.79" stroke="#A855F7" stroke-width="3.39535" stroke-miterlimit="10"/>
|
||||
<path d="M-63.6362 61.8112C-63.6362 61.8112 -1.07997 50.9096 15.0337 11.01C30.3968 -27.0802 93.9243 -36.79 93.9243 -36.79" stroke="#A855F7" stroke-width="3.39535" stroke-miterlimit="10"/>
|
||||
<path d="M-63.6362 77.7447C-63.6362 77.7447 10.4424 65.3423 29.5139 18.6898C47.7024 -25.9324 122.929 -36.79 122.929 -36.79" stroke="#A855F7" stroke-width="3.39535" stroke-miterlimit="10"/>
|
||||
<path d="M-63.6362 93.634C-63.6362 93.634 22.0089 79.7751 44.0382 26.2814C65.0081 -24.7407 151.934 -36.79 151.934 -36.79" stroke="#A855F7" stroke-width="3.39535" stroke-miterlimit="10"/>
|
||||
<path d="M-63.6362 109.523C-63.6362 109.523 33.5754 94.2079 58.5626 33.9169C82.3136 -23.549 180.894 -36.79 180.894 -36.79" stroke="#A855F7" stroke-width="3.39535" stroke-miterlimit="10"/>
|
||||
<path d="M-63.6362 125.456C-63.6362 125.456 45.0978 108.685 73.0428 41.5524C99.6633 -22.4016 209.898 -36.79 209.898 -36.79" stroke="#A855F7" stroke-width="3.39535" stroke-miterlimit="10"/>
|
||||
<path d="M-63.6362 141.346C-63.6362 141.346 56.6643 123.073 87.5672 49.1883C116.969 -21.2099 238.903 -36.79 238.903 -36.79" stroke="#A855F7" stroke-width="3.39535" stroke-miterlimit="10"/>
|
||||
<path d="M-63.6362 157.235C-63.6362 157.235 68.2309 137.506 102.047 56.7799C134.275 -20.0182 267.908 -36.79 267.908 -36.79" stroke="#A855F7" stroke-width="3.39535" stroke-miterlimit="10"/>
|
||||
<path d="M-63.6362 173.169C-63.6362 173.169 79.753 151.983 116.572 64.4594C151.58 -18.8707 296.868 -36.79 296.868 -36.79" stroke="#A855F7" stroke-width="3.39535" stroke-miterlimit="10"/>
|
||||
<path d="M-63.6362 189.056C-63.6362 189.056 91.3198 166.416 131.052 72.0953C168.886 -17.679 325.873 -36.79 325.873 -36.79" stroke="#A855F7" stroke-width="3.39535" stroke-miterlimit="10"/>
|
||||
<path d="M-63.6362 204.99C-63.6362 204.99 102.886 180.848 145.576 79.7308C186.192 -16.4873 354.877 -36.79 354.877 -36.79" stroke="#A855F7" stroke-width="3.39535" stroke-miterlimit="10"/>
|
||||
<path d="M-63.6362 220.881C-63.6362 220.881 114.409 195.281 160.057 87.3667C203.497 -15.3396 383.881 -36.79 383.881 -36.79" stroke="#A855F7" stroke-width="3.39535" stroke-miterlimit="10"/>
|
||||
<path d="M-63.6362 236.768C-63.6362 236.768 125.975 209.714 174.581 94.9579C220.803 -14.1479 412.885 -36.79 412.885 -36.79" stroke="#A855F7" stroke-width="3.39535" stroke-miterlimit="10"/>
|
||||
<path d="M-63.6362 252.701C-63.6362 252.701 137.542 224.147 189.105 102.638C238.108 -12.9562 441.847 -36.79 441.847 -36.79" stroke="#A855F7" stroke-width="3.39535" stroke-miterlimit="10"/>
|
||||
<path d="M-63.6362 268.592C-63.6362 268.592 149.064 238.579 203.585 110.229C255.414 -11.8087 470.851 -36.79 470.851 -36.79" stroke="#A855F7" stroke-width="3.39535" stroke-miterlimit="10"/>
|
||||
<path d="M-63.6362 284.483C-63.6362 284.483 160.631 253.012 218.11 117.865C272.72 -10.617 499.856 -36.79 499.856 -36.79" stroke="#A855F7" stroke-width="3.39535" stroke-miterlimit="10"/>
|
||||
<path d="M-63.6362 300.416C-63.6362 300.416 172.197 267.488 232.59 125.501C290.025 -9.42532 528.86 -36.79 528.86 -36.79" stroke="#A855F7" stroke-width="3.39535" stroke-miterlimit="10"/>
|
||||
<path d="M-63.6362 316.303C-63.6362 316.303 183.719 281.878 247.114 133.136C307.331 -8.27792 557.822 -36.79 557.822 -36.79" stroke="#A855F7" stroke-width="3.39535" stroke-miterlimit="10"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
84
basicswap/static/images/elements/dots2-red.svg
Normal file
@@ -0,0 +1,84 @@
|
||||
<svg width="199" height="149" viewBox="0 0 199 149" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1384_30010)">
|
||||
<path d="M198.91 2.75063C198.91 1.29259 197.728 0.110626 196.27 0.110626C194.812 0.110626 193.63 1.29259 193.63 2.75063C193.63 4.20866 194.812 5.39062 196.27 5.39062C197.728 5.39062 198.91 4.20866 198.91 2.75063Z" fill="#EF5944"/>
|
||||
<path d="M198.91 26.6901C198.91 25.232 197.728 24.0501 196.27 24.0501C194.812 24.0501 193.63 25.232 193.63 26.6901C193.63 28.1481 194.812 29.3301 196.27 29.3301C197.728 29.3301 198.91 28.1481 198.91 26.6901Z" fill="#EF5944"/>
|
||||
<path d="M198.91 50.6198C198.912 50.0952 198.759 49.5819 198.469 49.1448C198.179 48.7077 197.765 48.3664 197.281 48.1643C196.797 47.9622 196.264 47.9083 195.75 48.0094C195.235 48.1105 194.762 48.3621 194.39 48.7323C194.018 49.1025 193.765 49.5747 193.662 50.089C193.559 50.6033 193.611 51.1367 193.811 51.6215C194.011 52.1063 194.351 52.5207 194.787 52.8124C195.223 53.104 195.736 53.2598 196.26 53.2598C196.961 53.2598 197.634 52.9819 198.131 52.4871C198.627 51.9922 198.908 51.3208 198.91 50.6198V50.6198Z" fill="#EF5944"/>
|
||||
<path d="M198.91 74.5502C198.91 74.0261 198.755 73.5137 198.464 73.0779C198.173 72.6421 197.759 72.3025 197.274 72.1019C196.79 71.9014 196.257 71.8489 195.743 71.9512C195.229 72.0534 194.757 72.3058 194.387 72.6764C194.016 73.047 193.764 73.5192 193.661 74.0332C193.559 74.5473 193.612 75.0801 193.812 75.5643C194.013 76.0486 194.352 76.4624 194.788 76.7535C195.224 77.0447 195.736 77.2002 196.26 77.2002C196.608 77.2002 196.953 77.1317 197.274 76.9985C197.596 76.8654 197.888 76.6701 198.134 76.4241C198.38 76.178 198.576 75.8858 198.709 75.5643C198.842 75.2428 198.91 74.8982 198.91 74.5502V74.5502Z" fill="#EF5944"/>
|
||||
<path d="M198.91 98.4899C198.91 97.0319 197.728 95.8499 196.27 95.8499C194.812 95.8499 193.63 97.0319 193.63 98.4899C193.63 99.9479 194.812 101.13 196.27 101.13C197.728 101.13 198.91 99.9479 198.91 98.4899Z" fill="#EF5944"/>
|
||||
<path d="M198.595 121.41C198.041 120.061 196.498 119.418 195.15 119.972C193.801 120.527 193.157 122.069 193.712 123.418C194.266 124.766 195.809 125.41 197.157 124.855C198.506 124.301 199.15 122.758 198.595 121.41Z" fill="#EF5944"/>
|
||||
<path d="M193.66 146.35C193.658 146.875 193.812 147.388 194.103 147.826C194.393 148.263 194.807 148.604 195.291 148.806C195.776 149.008 196.31 149.061 196.825 148.959C197.34 148.857 197.813 148.605 198.184 148.234C198.555 147.862 198.808 147.389 198.91 146.874C199.012 146.359 198.958 145.826 198.756 145.341C198.555 144.856 198.214 144.443 197.776 144.152C197.339 143.862 196.825 143.708 196.3 143.71C195.601 143.712 194.931 143.991 194.436 144.486C193.942 144.98 193.663 145.65 193.66 146.35V146.35Z" fill="#EF5944"/>
|
||||
<path d="M177.5 2.75063C177.5 1.29259 176.318 0.110626 174.86 0.110626C173.402 0.110626 172.22 1.29259 172.22 2.75063C172.22 4.20866 173.402 5.39062 174.86 5.39062C176.318 5.39062 177.5 4.20866 177.5 2.75063Z" fill="#EF5944"/>
|
||||
<path d="M177.5 26.6901C177.5 25.232 176.318 24.0501 174.86 24.0501C173.402 24.0501 172.22 25.232 172.22 26.6901C172.22 28.1481 173.402 29.3301 174.86 29.3301C176.318 29.3301 177.5 28.1481 177.5 26.6901Z" fill="#EF5944"/>
|
||||
<path d="M177.5 50.6197C177.502 50.0948 177.348 49.581 177.058 49.1437C176.767 48.7063 176.354 48.3651 175.869 48.1633C175.384 47.9615 174.851 47.9082 174.336 48.0101C173.821 48.112 173.348 48.3646 172.976 48.7358C172.605 49.107 172.353 49.5802 172.251 50.0952C172.149 50.6102 172.202 51.1438 172.404 51.6284C172.606 52.113 172.947 52.5268 173.384 52.8172C173.821 53.1077 174.335 53.2617 174.86 53.2597C175.56 53.2571 176.229 52.978 176.724 52.4835C177.219 51.989 177.498 51.3191 177.5 50.6197V50.6197Z" fill="#EF5944"/>
|
||||
<path d="M177.5 74.5502C177.5 74.0256 177.345 73.5129 177.053 73.0769C176.761 72.6409 176.347 72.3012 175.862 72.1009C175.377 71.9007 174.844 71.8488 174.329 71.9519C173.815 72.0549 173.343 72.3083 172.973 72.6799C172.603 73.0515 172.351 73.5247 172.25 74.0394C172.149 74.5541 172.203 75.0872 172.405 75.5713C172.607 76.0553 172.948 76.4684 173.385 76.7584C173.822 77.0484 174.336 77.2022 174.86 77.2002C175.208 77.2002 175.552 77.1316 175.873 76.9983C176.194 76.865 176.485 76.6696 176.73 76.4234C176.976 76.1772 177.17 75.885 177.302 75.5636C177.434 75.2421 177.502 74.8977 177.5 74.5502V74.5502Z" fill="#EF5944"/>
|
||||
<path d="M177.5 98.4899C177.5 97.0319 176.318 95.8499 174.86 95.8499C173.402 95.8499 172.22 97.0319 172.22 98.4899C172.22 99.9479 173.402 101.13 174.86 101.13C176.318 101.13 177.5 99.9479 177.5 98.4899Z" fill="#EF5944"/>
|
||||
<path d="M177.193 121.408C176.639 120.059 175.096 119.416 173.747 119.97C172.399 120.525 171.755 122.067 172.31 123.416C172.864 124.764 174.407 125.408 175.755 124.854C177.104 124.299 177.747 122.756 177.193 121.408Z" fill="#EF5944"/>
|
||||
<path d="M177.5 146.35C177.5 145.825 177.345 145.313 177.053 144.877C176.761 144.441 176.347 144.101 175.862 143.901C175.377 143.7 174.844 143.649 174.329 143.752C173.815 143.855 173.343 144.108 172.973 144.48C172.603 144.851 172.351 145.324 172.25 145.839C172.149 146.354 172.203 146.887 172.405 147.371C172.607 147.855 172.948 148.268 173.385 148.558C173.822 148.848 174.336 149.002 174.86 149C175.208 149 175.552 148.931 175.873 148.798C176.194 148.665 176.485 148.469 176.73 148.223C176.976 147.977 177.17 147.685 177.302 147.363C177.434 147.042 177.502 146.697 177.5 146.35Z" fill="#EF5944"/>
|
||||
<path d="M156.09 2.75063C156.09 1.29259 154.908 0.110626 153.45 0.110626C151.992 0.110626 150.81 1.29259 150.81 2.75063C150.81 4.20866 151.992 5.39062 153.45 5.39062C154.908 5.39062 156.09 4.20866 156.09 2.75063Z" fill="#EF5944"/>
|
||||
<path d="M156.09 26.6901C156.09 25.232 154.908 24.0501 153.45 24.0501C151.992 24.0501 150.81 25.232 150.81 26.6901C150.81 28.1481 151.992 29.3301 153.45 29.3301C154.908 29.3301 156.09 28.1481 156.09 26.6901Z" fill="#EF5944"/>
|
||||
<path d="M156.1 50.6198C156.102 50.0952 155.948 49.5819 155.658 49.1448C155.368 48.7077 154.955 48.3664 154.471 48.1643C153.987 47.9622 153.454 47.9083 152.939 48.0094C152.424 48.1105 151.951 48.3621 151.58 48.7323C151.208 49.1025 150.955 49.5747 150.851 50.089C150.748 50.6033 150.8 51.1367 151.001 51.6215C151.201 52.1063 151.541 52.5207 151.977 52.8124C152.413 53.104 152.925 53.2598 153.45 53.2598C154.151 53.2598 154.823 52.9819 155.32 52.4871C155.817 51.9922 156.097 51.3208 156.1 50.6198V50.6198Z" fill="#EF5944"/>
|
||||
<path d="M156.1 74.5502C156.1 74.0261 155.944 73.5137 155.653 73.0779C155.362 72.6421 154.948 72.3025 154.464 72.1019C153.98 71.9014 153.447 71.8489 152.933 71.9512C152.419 72.0534 151.947 72.3058 151.576 72.6764C151.205 73.047 150.953 73.5192 150.851 74.0332C150.748 74.5473 150.801 75.0801 151.002 75.5643C151.202 76.0486 151.542 76.4624 151.978 76.7535C152.413 77.0447 152.926 77.2002 153.45 77.2002C153.798 77.2002 154.142 77.1317 154.464 76.9985C154.785 76.8654 155.078 76.6701 155.324 76.4241C155.57 76.178 155.765 75.8858 155.898 75.5643C156.031 75.2428 156.1 74.8982 156.1 74.5502V74.5502Z" fill="#EF5944"/>
|
||||
<path d="M156.09 98.4899C156.09 97.0319 154.908 95.8499 153.45 95.8499C151.992 95.8499 150.81 97.0319 150.81 98.4899C150.81 99.9479 151.992 101.13 153.45 101.13C154.908 101.13 156.09 99.9479 156.09 98.4899Z" fill="#EF5944"/>
|
||||
<path d="M155.792 121.409C155.237 120.06 153.694 119.417 152.346 119.971C150.997 120.526 150.354 122.068 150.908 123.417C151.463 124.765 153.005 125.409 154.354 124.855C155.702 124.3 156.346 122.757 155.792 121.409Z" fill="#EF5944"/>
|
||||
<path d="M156.1 146.35C156.1 145.826 155.944 145.313 155.653 144.878C155.362 144.442 154.948 144.102 154.464 143.902C153.98 143.701 153.447 143.649 152.933 143.751C152.419 143.853 151.947 144.105 151.576 144.476C151.205 144.847 150.953 145.319 150.851 145.833C150.748 146.347 150.801 146.88 151.002 147.364C151.202 147.848 151.542 148.262 151.978 148.553C152.413 148.845 152.926 149 153.45 149C154.153 149 154.827 148.721 155.324 148.224C155.821 147.727 156.1 147.053 156.1 146.35V146.35Z" fill="#EF5944"/>
|
||||
<path d="M134.69 2.75063C134.69 1.29259 133.508 0.110626 132.05 0.110626C130.592 0.110626 129.41 1.29259 129.41 2.75063C129.41 4.20866 130.592 5.39062 132.05 5.39062C133.508 5.39062 134.69 4.20866 134.69 2.75063Z" fill="#EF5944"/>
|
||||
<path d="M134.69 26.6901C134.69 25.232 133.508 24.0501 132.05 24.0501C130.592 24.0501 129.41 25.232 129.41 26.6901C129.41 28.1481 130.592 29.3301 132.05 29.3301C133.508 29.3301 134.69 28.1481 134.69 26.6901Z" fill="#EF5944"/>
|
||||
<path d="M134.66 50.6197C134.662 50.0948 134.508 49.581 134.218 49.1437C133.927 48.7063 133.514 48.3651 133.029 48.1633C132.544 47.9615 132.011 47.9082 131.496 48.0101C130.981 48.112 130.508 48.3646 130.137 48.7358C129.765 49.107 129.513 49.5802 129.411 50.0952C129.309 50.6102 129.362 51.1438 129.564 51.6284C129.766 52.113 130.107 52.5268 130.544 52.8172C130.982 53.1077 131.495 53.2617 132.02 53.2597C132.72 53.2571 133.39 52.978 133.884 52.4835C134.379 51.989 134.658 51.3191 134.66 50.6197V50.6197Z" fill="#EF5944"/>
|
||||
<path d="M134.66 74.5502C134.66 74.0256 134.505 73.5129 134.213 73.0769C133.921 72.6409 133.507 72.3012 133.022 72.1009C132.537 71.9007 132.004 71.8488 131.49 71.9519C130.975 72.0549 130.503 72.3083 130.133 72.6799C129.763 73.0515 129.511 73.5247 129.41 74.0394C129.309 74.5541 129.363 75.0872 129.565 75.5713C129.767 76.0553 130.108 76.4684 130.545 76.7584C130.982 77.0484 131.496 77.2022 132.02 77.2002C132.368 77.2002 132.712 77.1316 133.033 76.9983C133.354 76.865 133.645 76.6696 133.891 76.4234C134.136 76.1772 134.33 75.885 134.462 75.5636C134.594 75.2421 134.662 74.8977 134.66 74.5502V74.5502Z" fill="#EF5944"/>
|
||||
<path d="M134.69 98.4899C134.69 97.0319 133.508 95.8499 132.05 95.8499C130.592 95.8499 129.41 97.0319 129.41 98.4899C129.41 99.9479 130.592 101.13 132.05 101.13C133.508 101.13 134.69 99.9479 134.69 98.4899Z" fill="#EF5944"/>
|
||||
<path d="M134.389 121.406C133.835 120.057 132.292 119.414 130.944 119.968C129.595 120.523 128.951 122.065 129.506 123.414C130.06 124.762 131.603 125.406 132.951 124.852C134.3 124.297 134.944 122.754 134.389 121.406Z" fill="#EF5944"/>
|
||||
<path d="M134.66 146.35C134.66 145.825 134.505 145.313 134.213 144.877C133.921 144.441 133.507 144.101 133.022 143.901C132.537 143.7 132.004 143.649 131.49 143.752C130.975 143.855 130.503 144.108 130.133 144.48C129.763 144.851 129.511 145.324 129.41 145.839C129.309 146.354 129.363 146.887 129.565 147.371C129.767 147.855 130.108 148.268 130.545 148.558C130.982 148.848 131.496 149.002 132.02 149C132.368 149 132.712 148.931 133.033 148.798C133.354 148.665 133.645 148.469 133.891 148.223C134.136 147.977 134.33 147.685 134.462 147.363C134.594 147.042 134.662 146.697 134.66 146.35V146.35Z" fill="#EF5944"/>
|
||||
<path d="M113.28 2.75063C113.28 1.29259 112.098 0.110626 110.64 0.110626C109.182 0.110626 108 1.29259 108 2.75063C108 4.20866 109.182 5.39062 110.64 5.39062C112.098 5.39062 113.28 4.20866 113.28 2.75063Z" fill="#EF5944"/>
|
||||
<path d="M113.28 26.6901C113.28 25.232 112.098 24.0501 110.64 24.0501C109.182 24.0501 108 25.232 108 26.6901C108 28.1481 109.182 29.3301 110.64 29.3301C112.098 29.3301 113.28 28.1481 113.28 26.6901Z" fill="#EF5944"/>
|
||||
<path d="M113.29 50.6198C113.292 50.0952 113.139 49.5819 112.849 49.1448C112.559 48.7077 112.145 48.3664 111.661 48.1643C111.177 47.9622 110.644 47.9083 110.129 48.0094C109.615 48.1105 109.142 48.3621 108.77 48.7323C108.398 49.1025 108.145 49.5747 108.042 50.089C107.939 50.6033 107.991 51.1367 108.191 51.6215C108.391 52.1063 108.731 52.5207 109.167 52.8124C109.603 53.104 110.116 53.2598 110.64 53.2598C111.341 53.2598 112.014 52.9819 112.511 52.4871C113.007 51.9922 113.288 51.3208 113.29 50.6198V50.6198Z" fill="#EF5944"/>
|
||||
<path d="M113.29 74.5502C113.29 74.0261 113.135 73.5137 112.844 73.0779C112.552 72.6421 112.139 72.3025 111.654 72.1019C111.17 71.9014 110.637 71.8489 110.123 71.9512C109.609 72.0534 109.137 72.3058 108.766 72.6764C108.396 73.047 108.143 73.5192 108.041 74.0332C107.939 74.5473 107.991 75.0801 108.192 75.5643C108.393 76.0486 108.732 76.4624 109.168 76.7535C109.604 77.0447 110.116 77.2002 110.64 77.2002C110.988 77.2002 111.333 77.1317 111.654 76.9985C111.976 76.8654 112.268 76.6701 112.514 76.4241C112.76 76.178 112.955 75.8858 113.089 75.5643C113.222 75.2428 113.29 74.8982 113.29 74.5502V74.5502Z" fill="#EF5944"/>
|
||||
<path d="M113.28 98.4899C113.28 97.0319 112.098 95.8499 110.64 95.8499C109.182 95.8499 108 97.0319 108 98.4899C108 99.9479 109.182 101.13 110.64 101.13C112.098 101.13 113.28 99.9479 113.28 98.4899Z" fill="#EF5944"/>
|
||||
<path d="M112.977 121.408C112.423 120.059 110.88 119.416 109.531 119.97C108.183 120.525 107.539 122.067 108.094 123.416C108.648 124.764 110.191 125.408 111.539 124.854C112.888 124.299 113.532 122.756 112.977 121.408Z" fill="#EF5944"/>
|
||||
<path d="M113.29 146.35C113.29 145.826 113.135 145.313 112.844 144.878C112.552 144.442 112.139 144.102 111.654 143.902C111.17 143.701 110.637 143.649 110.123 143.751C109.609 143.853 109.137 144.105 108.766 144.476C108.396 144.847 108.143 145.319 108.041 145.833C107.939 146.347 107.991 146.88 108.192 147.364C108.393 147.848 108.732 148.262 109.168 148.553C109.604 148.845 110.116 149 110.64 149C111.343 149 112.017 148.721 112.514 148.224C113.011 147.727 113.29 147.053 113.29 146.35V146.35Z" fill="#EF5944"/>
|
||||
</g>
|
||||
<g clip-path="url(#clip1_1384_30010)">
|
||||
<path d="M90.9099 2.75063C90.9099 1.29259 89.7279 0.110626 88.2699 0.110626C86.8119 0.110626 85.6299 1.29259 85.6299 2.75063C85.6299 4.20866 86.8119 5.39062 88.2699 5.39062C89.7279 5.39062 90.9099 4.20866 90.9099 2.75063Z" fill="#EF5944"/>
|
||||
<path d="M90.9099 26.6901C90.9099 25.232 89.7279 24.0501 88.2699 24.0501C86.8119 24.0501 85.6299 25.232 85.6299 26.6901C85.6299 28.1481 86.8119 29.3301 88.2699 29.3301C89.7279 29.3301 90.9099 28.1481 90.9099 26.6901Z" fill="#EF5944"/>
|
||||
<path d="M90.9104 50.6198C90.9123 50.0952 90.7586 49.5819 90.4686 49.1448C90.1787 48.7077 89.7654 48.3664 89.2814 48.1643C88.7973 47.9622 88.2642 47.9083 87.7495 48.0094C87.2348 48.1105 86.7617 48.3621 86.3901 48.7323C86.0185 49.1025 85.7651 49.5747 85.662 50.089C85.5589 50.6033 85.6108 51.1367 85.8111 51.6215C86.0114 52.1063 86.3511 52.5207 86.7871 52.8124C87.2231 53.104 87.7358 53.2598 88.2603 53.2598C88.9614 53.2598 89.6339 52.9819 90.1306 52.4871C90.6273 51.9922 90.9077 51.3208 90.9104 50.6198V50.6198Z" fill="#EF5944"/>
|
||||
<path d="M90.9104 74.5502C90.9104 74.0261 90.7549 73.5137 90.4638 73.0779C90.1726 72.6421 89.7587 72.3025 89.2745 72.1019C88.7903 71.9014 88.2574 71.8489 87.7434 71.9512C87.2293 72.0534 86.7572 72.3058 86.3865 72.6764C86.0159 73.047 85.7635 73.5192 85.6613 74.0332C85.559 74.5473 85.6115 75.0801 85.8121 75.5643C86.0127 76.0486 86.3523 76.4624 86.7881 76.7535C87.2239 77.0447 87.7362 77.2002 88.2603 77.2002C88.6083 77.2002 88.953 77.1317 89.2745 76.9985C89.596 76.8654 89.8881 76.6701 90.1342 76.4241C90.3802 76.178 90.5755 75.8858 90.7087 75.5643C90.8419 75.2428 90.9104 74.8982 90.9104 74.5502V74.5502Z" fill="#EF5944"/>
|
||||
<path d="M90.9099 98.4899C90.9099 97.0319 89.7279 95.8499 88.2699 95.8499C86.8119 95.8499 85.6299 97.0319 85.6299 98.4899C85.6299 99.9479 86.8119 101.13 88.2699 101.13C89.7279 101.13 90.9099 99.9479 90.9099 98.4899Z" fill="#EF5944"/>
|
||||
<path d="M90.5952 121.41C90.0408 120.061 88.4982 119.418 87.1497 119.972C85.8012 120.527 85.1575 122.069 85.7119 123.418C86.2664 124.766 87.809 125.41 89.1575 124.855C90.506 124.301 91.1497 122.758 90.5952 121.41Z" fill="#EF5944"/>
|
||||
<path d="M85.6602 146.35C85.6582 146.875 85.8122 147.388 86.1026 147.826C86.393 148.263 86.8068 148.604 87.2915 148.806C87.7761 149.008 88.3097 149.061 88.8247 148.959C89.3397 148.857 89.8128 148.605 90.184 148.234C90.5552 147.862 90.8078 147.389 90.9097 146.874C91.0116 146.359 90.9583 145.826 90.7565 145.341C90.5547 144.856 90.2135 144.443 89.7762 144.152C89.3389 143.862 88.8252 143.708 88.3002 143.71C87.6008 143.712 86.9308 143.991 86.4363 144.486C85.9418 144.98 85.6628 145.65 85.6602 146.35V146.35Z" fill="#EF5944"/>
|
||||
<path d="M69.4997 2.75063C69.4997 1.29259 68.3178 0.110626 66.8597 0.110626C65.4017 0.110626 64.2197 1.29259 64.2197 2.75063C64.2197 4.20866 65.4017 5.39062 66.8597 5.39062C68.3178 5.39062 69.4997 4.20866 69.4997 2.75063Z" fill="#EF5944"/>
|
||||
<path d="M69.4997 26.6901C69.4997 25.232 68.3178 24.0501 66.8597 24.0501C65.4017 24.0501 64.2197 25.232 64.2197 26.6901C64.2197 28.1481 65.4017 29.3301 66.8597 29.3301C68.3178 29.3301 69.4997 28.1481 69.4997 26.6901Z" fill="#EF5944"/>
|
||||
<path d="M69.5002 50.6197C69.5022 50.0948 69.3482 49.581 69.0578 49.1437C68.7673 48.7063 68.3535 48.3651 67.8689 48.1633C67.3843 47.9615 66.8506 47.9082 66.3357 48.0101C65.8207 48.112 65.3475 48.3646 64.9763 48.7358C64.6051 49.107 64.3526 49.5802 64.2506 50.0952C64.1487 50.6102 64.202 51.1438 64.4038 51.6284C64.6056 52.113 64.9469 52.5268 65.3842 52.8172C65.8215 53.1077 66.3352 53.2617 66.8602 53.2597C67.5595 53.2571 68.2295 52.978 68.724 52.4835C69.2185 51.989 69.4976 51.3191 69.5002 50.6197V50.6197Z" fill="#EF5944"/>
|
||||
<path d="M69.5002 74.5502C69.5002 74.0256 69.3445 73.5129 69.0529 73.0769C68.7612 72.6409 68.3468 72.3012 67.862 72.1009C67.3772 71.9007 66.8438 71.8488 66.3295 71.9519C65.8152 72.0549 65.343 72.3083 64.9728 72.6799C64.6026 73.0515 64.351 73.5247 64.2499 74.0394C64.1488 74.5541 64.2027 75.0872 64.4048 75.5713C64.6069 76.0553 64.9481 76.4684 65.3852 76.7584C65.8223 77.0484 66.3356 77.2022 66.8602 77.2002C67.2077 77.2002 67.5518 77.1316 67.8728 76.9983C68.1937 76.865 68.4852 76.6696 68.7305 76.4234C68.9758 76.1772 69.17 75.885 69.3021 75.5636C69.4342 75.2421 69.5015 74.8977 69.5002 74.5502V74.5502Z" fill="#EF5944"/>
|
||||
<path d="M69.4997 98.4899C69.4997 97.0319 68.3178 95.8499 66.8597 95.8499C65.4017 95.8499 64.2197 97.0319 64.2197 98.4899C64.2197 99.9479 65.4017 101.13 66.8597 101.13C68.3178 101.13 69.4997 99.9479 69.4997 98.4899Z" fill="#EF5944"/>
|
||||
<path d="M69.193 121.408C68.6385 120.059 67.0959 119.416 65.7474 119.97C64.3989 120.525 63.7551 122.067 64.3096 123.416C64.864 124.764 66.4067 125.408 67.7552 124.854C69.1037 124.299 69.7474 122.756 69.193 121.408Z" fill="#EF5944"/>
|
||||
<path d="M69.5002 146.35C69.5002 145.825 69.3445 145.313 69.0529 144.877C68.7612 144.441 68.3468 144.101 67.862 143.901C67.3772 143.7 66.8438 143.649 66.3295 143.752C65.8152 143.855 65.343 144.108 64.9728 144.48C64.6026 144.851 64.351 145.324 64.2499 145.839C64.1488 146.354 64.2027 146.887 64.4048 147.371C64.6069 147.855 64.9481 148.268 65.3852 148.558C65.8223 148.848 66.3356 149.002 66.8602 149C67.2077 149 67.5518 148.931 67.8728 148.798C68.1937 148.665 68.4852 148.469 68.7305 148.223C68.9758 147.977 69.17 147.685 69.3021 147.363C69.4342 147.042 69.5015 146.697 69.5002 146.35Z" fill="#EF5944"/>
|
||||
<path d="M48.0896 2.75063C48.0896 1.29259 46.9076 0.110626 45.4496 0.110626C43.9915 0.110626 42.8096 1.29259 42.8096 2.75063C42.8096 4.20866 43.9915 5.39062 45.4496 5.39062C46.9076 5.39062 48.0896 4.20866 48.0896 2.75063Z" fill="#EF5944"/>
|
||||
<path d="M48.0896 26.6901C48.0896 25.232 46.9076 24.0501 45.4496 24.0501C43.9915 24.0501 42.8096 25.232 42.8096 26.6901C42.8096 28.1481 43.9915 29.3301 45.4496 29.3301C46.9076 29.3301 48.0896 28.1481 48.0896 26.6901Z" fill="#EF5944"/>
|
||||
<path d="M48.0998 50.6198C48.1018 50.0952 47.9481 49.5819 47.6581 49.1448C47.3681 48.7077 46.9549 48.3664 46.4708 48.1643C45.9868 47.9622 45.4537 47.9083 44.939 48.0094C44.4243 48.1105 43.9512 48.3621 43.5795 48.7323C43.2079 49.1025 42.9545 49.5747 42.8515 50.089C42.7484 50.6033 42.8003 51.1367 43.0006 51.6215C43.2008 52.1063 43.5405 52.5207 43.9765 52.8124C44.4125 53.104 44.9252 53.2598 45.4498 53.2598C46.1509 53.2598 46.8234 52.9819 47.3201 52.4871C47.8168 51.9922 48.0972 51.3208 48.0998 50.6198V50.6198Z" fill="#EF5944"/>
|
||||
<path d="M48.0998 74.5502C48.0998 74.0261 47.9444 73.5137 47.6532 73.0779C47.362 72.6421 46.9482 72.3025 46.4639 72.1019C45.9797 71.9014 45.4469 71.8489 44.9328 71.9512C44.4188 72.0534 43.9465 72.3058 43.5759 72.6764C43.2053 73.047 42.953 73.5192 42.8507 74.0332C42.7485 74.5473 42.801 75.0801 43.0015 75.5643C43.2021 76.0486 43.5418 76.4624 43.9776 76.7535C44.4133 77.0447 44.9257 77.2002 45.4498 77.2002C45.7978 77.2002 46.1424 77.1317 46.4639 76.9985C46.7855 76.8654 47.0775 76.6701 47.3236 76.4241C47.5697 76.178 47.7649 75.8858 47.8981 75.5643C48.0313 75.2428 48.0998 74.8982 48.0998 74.5502V74.5502Z" fill="#EF5944"/>
|
||||
<path d="M48.0896 98.4899C48.0896 97.0319 46.9076 95.8499 45.4496 95.8499C43.9915 95.8499 42.8096 97.0319 42.8096 98.4899C42.8096 99.9479 43.9915 101.13 45.4496 101.13C46.9076 101.13 48.0896 99.9479 48.0896 98.4899Z" fill="#EF5944"/>
|
||||
<path d="M47.7915 121.409C47.2371 120.06 45.6944 119.417 44.3459 119.971C42.9974 120.526 42.3538 122.068 42.9082 123.417C43.4626 124.765 45.0052 125.409 46.3537 124.855C47.7022 124.3 48.346 122.757 47.7915 121.409Z" fill="#EF5944"/>
|
||||
<path d="M48.0998 146.35C48.0998 145.826 47.9444 145.313 47.6532 144.878C47.362 144.442 46.9482 144.102 46.4639 143.902C45.9797 143.701 45.4469 143.649 44.9328 143.751C44.4188 143.853 43.9465 144.105 43.5759 144.476C43.2053 144.847 42.953 145.319 42.8507 145.833C42.7485 146.347 42.801 146.88 43.0015 147.364C43.2021 147.848 43.5418 148.262 43.9776 148.553C44.4133 148.845 44.9257 149 45.4498 149C46.1526 149 46.8266 148.721 47.3236 148.224C47.8206 147.727 48.0998 147.053 48.0998 146.35V146.35Z" fill="#EF5944"/>
|
||||
<path d="M26.6902 2.75063C26.6902 1.29259 25.5082 0.110626 24.0502 0.110626C22.5921 0.110626 21.4102 1.29259 21.4102 2.75063C21.4102 4.20866 22.5921 5.39062 24.0502 5.39062C25.5082 5.39062 26.6902 4.20866 26.6902 2.75063Z" fill="#EF5944"/>
|
||||
<path d="M26.6902 26.6901C26.6902 25.232 25.5082 24.0501 24.0502 24.0501C22.5921 24.0501 21.4102 25.232 21.4102 26.6901C21.4102 28.1481 22.5921 29.3301 24.0502 29.3301C25.5082 29.3301 26.6902 28.1481 26.6902 26.6901Z" fill="#EF5944"/>
|
||||
<path d="M26.6604 50.6197C26.6623 50.0948 26.5083 49.581 26.2179 49.1437C25.9275 48.7063 25.5137 48.3651 25.0291 48.1633C24.5444 47.9615 24.0108 47.9082 23.4958 48.0101C22.9808 48.112 22.5078 48.3646 22.1366 48.7358C21.7653 49.107 21.5127 49.5802 21.4108 50.0952C21.3089 50.6102 21.3622 51.1438 21.564 51.6284C21.7658 52.113 22.107 52.5268 22.5443 52.8172C22.9817 53.1077 23.4954 53.2617 24.0203 53.2597C24.7197 53.2571 25.3897 52.978 25.8842 52.4835C26.3788 51.989 26.6577 51.3191 26.6604 50.6197V50.6197Z" fill="#EF5944"/>
|
||||
<path d="M26.6604 74.5502C26.6604 74.0256 26.5047 73.5129 26.213 73.0769C25.9214 72.6409 25.507 72.3012 25.0222 72.1009C24.5374 71.9007 24.004 71.8488 23.4897 71.9519C22.9754 72.0549 22.5032 72.3083 22.133 72.6799C21.7628 73.0515 21.5112 73.5247 21.4101 74.0394C21.3089 74.5541 21.3629 75.0872 21.565 75.5713C21.7671 76.0553 22.1083 76.4684 22.5454 76.7584C22.9825 77.0484 23.4958 77.2022 24.0203 77.2002C24.3679 77.2002 24.712 77.1316 25.033 76.9983C25.3539 76.865 25.6454 76.6696 25.8906 76.4234C26.1359 76.1772 26.3302 75.885 26.4623 75.5636C26.5944 75.2421 26.6617 74.8977 26.6604 74.5502V74.5502Z" fill="#EF5944"/>
|
||||
<path d="M26.6902 98.4899C26.6902 97.0319 25.5082 95.8499 24.0502 95.8499C22.5921 95.8499 21.4102 97.0319 21.4102 98.4899C21.4102 99.9479 22.5921 101.13 24.0502 101.13C25.5082 101.13 26.6902 99.9479 26.6902 98.4899Z" fill="#EF5944"/>
|
||||
<path d="M26.3892 121.406C25.8348 120.057 24.2922 119.414 22.9437 119.968C21.5952 120.523 20.9514 122.065 21.5059 123.414C22.0603 124.762 23.603 125.406 24.9515 124.852C26.3 124.297 26.9437 122.754 26.3892 121.406Z" fill="#EF5944"/>
|
||||
<path d="M26.6604 146.35C26.6604 145.825 26.5047 145.313 26.213 144.877C25.9214 144.441 25.507 144.101 25.0222 143.901C24.5374 143.7 24.004 143.649 23.4897 143.752C22.9754 143.855 22.5032 144.108 22.133 144.48C21.7628 144.851 21.5112 145.324 21.4101 145.839C21.3089 146.354 21.3629 146.887 21.565 147.371C21.7671 147.855 22.1083 148.268 22.5454 148.558C22.9825 148.848 23.4958 149.002 24.0203 149C24.3679 149 24.712 148.931 25.033 148.798C25.3539 148.665 25.6454 148.469 25.8906 148.223C26.1359 147.977 26.3302 147.685 26.4623 147.363C26.5944 147.042 26.6617 146.697 26.6604 146.35V146.35Z" fill="#EF5944"/>
|
||||
<path d="M5.28 2.75063C5.28 1.29259 4.09803 0.110626 2.64 0.110626C1.18197 0.110626 -1.79131e-07 1.29259 -1.15398e-07 2.75063C-5.16654e-08 4.20866 1.18197 5.39062 2.64 5.39062C4.09803 5.39062 5.28 4.20866 5.28 2.75063Z" fill="#EF5944"/>
|
||||
<path d="M5.28 26.6901C5.28 25.232 4.09803 24.0501 2.64 24.0501C1.18197 24.0501 -1.79131e-07 25.232 -1.15398e-07 26.6901C-5.16654e-08 28.1481 1.18197 29.3301 2.64 29.3301C4.09803 29.3301 5.28 28.1481 5.28 26.6901Z" fill="#EF5944"/>
|
||||
<path d="M5.29024 50.6198C5.29222 50.0952 5.13851 49.5819 4.84853 49.1448C4.55855 48.7077 4.14532 48.3664 3.66127 48.1643C3.17723 47.9622 2.64412 47.9083 2.12941 48.0094C1.61471 48.1105 1.14159 48.3621 0.769978 48.7323C0.398367 49.1025 0.144953 49.5747 0.0418893 50.089C-0.0611746 50.6033 -0.00927678 51.1367 0.190998 51.6215C0.391273 52.1063 0.730946 52.5207 1.16695 52.8124C1.60295 53.104 2.11567 53.2598 2.64022 53.2598C3.34131 53.2598 4.01383 52.9819 4.51052 52.4871C5.0072 51.9922 5.2876 51.3208 5.29024 50.6198V50.6198Z" fill="#EF5944"/>
|
||||
<path d="M5.29023 74.5502C5.29023 74.0261 5.13483 73.5137 4.84364 73.0779C4.55245 72.6421 4.13859 72.3025 3.65437 72.1019C3.17015 71.9014 2.63729 71.8489 2.12324 71.9512C1.60919 72.0534 1.13698 72.3058 0.76637 72.6764C0.39576 73.047 0.143401 73.5192 0.0411499 74.0332C-0.0611009 74.5473 -0.00860443 75.0801 0.191968 75.5643C0.39254 76.0486 0.732191 76.4624 1.16798 76.7535C1.60377 77.0447 2.11609 77.2002 2.64021 77.2002C2.98821 77.2002 3.33286 77.1317 3.65437 76.9985C3.97588 76.8654 4.26798 76.6701 4.51405 76.4241C4.76013 76.178 4.95534 75.8858 5.08851 75.5643C5.22169 75.2428 5.29023 74.8982 5.29023 74.5502V74.5502Z" fill="#EF5944"/>
|
||||
<path d="M5.28 98.4899C5.28 97.0319 4.09803 95.8499 2.64 95.8499C1.18197 95.8499 -1.79131e-07 97.0319 -1.15398e-07 98.4899C-5.16654e-08 99.9479 1.18197 101.13 2.64 101.13C4.09803 101.13 5.28 99.9479 5.28 98.4899Z" fill="#EF5944"/>
|
||||
<path d="M4.97708 121.408C4.42265 120.059 2.87999 119.416 1.53149 119.97C0.182988 120.525 -0.460681 122.067 0.0937552 123.416C0.648191 124.764 2.19079 125.408 3.53929 124.854C4.88779 124.299 5.53152 122.756 4.97708 121.408Z" fill="#EF5944"/>
|
||||
<path d="M5.29023 146.35C5.29023 145.826 5.13483 145.313 4.84364 144.878C4.55245 144.442 4.13859 144.102 3.65437 143.902C3.17015 143.701 2.63729 143.649 2.12324 143.751C1.60919 143.853 1.13698 144.105 0.76637 144.476C0.39576 144.847 0.143401 145.319 0.0411499 145.833C-0.0611009 146.347 -0.00860443 146.88 0.191968 147.364C0.39254 147.848 0.732191 148.262 1.16798 148.553C1.60377 148.845 2.11609 149 2.64021 149C3.34303 149 4.01708 148.721 4.51405 148.224C5.01102 147.727 5.29023 147.053 5.29023 146.35V146.35Z" fill="#EF5944"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_1384_30010">
|
||||
<rect width="148.89" height="90.91" fill="white" transform="translate(108 149) rotate(-90)"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip1_1384_30010">
|
||||
<rect width="148.89" height="90.91" fill="white" transform="translate(0 149) rotate(-90)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 26 KiB |
BIN
basicswap/static/images/gfx/dashboard.jpg
Normal file
|
After Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 88 KiB |
BIN
basicswap/static/images/gfx/dashboard2.jpg
Normal file
|
After Width: | Height: | Size: 127 KiB |
BIN
basicswap/static/images/gfx/macbook2.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
basicswap/static/images/other/coin.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
70
basicswap/static/js/coin_icons.js
Normal file
@@ -0,0 +1,70 @@
|
||||
// Define a cache object to store selected option data
|
||||
const selectCache = {};
|
||||
|
||||
// Function to update the cache with the selected option data for a given select element
|
||||
function updateSelectCache(select) {
|
||||
const selectedOption = select.options[select.selectedIndex];
|
||||
const image = selectedOption.getAttribute('data-image');
|
||||
const name = selectedOption.textContent.trim();
|
||||
selectCache[select.id] = { image, name };
|
||||
}
|
||||
|
||||
// Function to set the selected option and associated image and name for a given select element
|
||||
|
||||
|
||||
function setSelectData(select) {
|
||||
const selectedOption = select.options[select.selectedIndex];
|
||||
const image = selectedOption.getAttribute('data-image') || '/static/images/other/coin.png'; // set a default image URL
|
||||
const name = selectedOption.textContent.trim();
|
||||
if (image) {
|
||||
select.style.backgroundImage = `url(${image})`;
|
||||
select.nextElementSibling.querySelector('.select-image').src = image;
|
||||
} else {
|
||||
select.style.backgroundImage = '';
|
||||
select.nextElementSibling.querySelector('.select-image').src = '';
|
||||
}
|
||||
select.nextElementSibling.querySelector('.select-name').textContent = name;
|
||||
updateSelectCache(select);
|
||||
}
|
||||
|
||||
|
||||
// Function to get the selected option data from cache for a given select element
|
||||
function getSelectData(select) {
|
||||
return selectCache[select.id] || {};
|
||||
}
|
||||
|
||||
// Update all custom select elements on the page
|
||||
const selects = document.querySelectorAll('.custom-select .select');
|
||||
selects.forEach((select) => {
|
||||
// Set the initial select data based on the cached data (if available) or the selected option (if any)
|
||||
const cachedData = getSelectData(select);
|
||||
if (cachedData.image) {
|
||||
select.style.backgroundImage = `url(${cachedData.image})`;
|
||||
select.nextElementSibling.querySelector('.select-image').src = cachedData.image;
|
||||
}
|
||||
if (cachedData.name) {
|
||||
select.nextElementSibling.querySelector('.select-name').textContent = cachedData.name;
|
||||
}
|
||||
if (select.selectedIndex >= 0) {
|
||||
setSelectData(select);
|
||||
}
|
||||
|
||||
// Add event listener to update select data when an option is selected
|
||||
select.addEventListener('change', () => {
|
||||
setSelectData(select);
|
||||
});
|
||||
});
|
||||
|
||||
// Hide the select image and name on page load
|
||||
const selectIcons = document.querySelectorAll('.custom-select .select-icon');
|
||||
const selectImages = document.querySelectorAll('.custom-select .select-image');
|
||||
const selectNames = document.querySelectorAll('.custom-select .select-name');
|
||||
selectIcons.forEach((icon) => {
|
||||
icon.style.display = 'none';
|
||||
});
|
||||
selectImages.forEach((image) => {
|
||||
image.style.display = 'none';
|
||||
});
|
||||
selectNames.forEach((name) => {
|
||||
name.style.display = 'none';
|
||||
});
|
||||
60
basicswap/static/js/coin_icons_2.js
Normal file
@@ -0,0 +1,60 @@
|
||||
// Define the function for setting up the custom select element
|
||||
function setupCustomSelect(select) {
|
||||
const options = select.querySelectorAll('option');
|
||||
const selectIcon = select.parentElement.querySelector('.select-icon');
|
||||
const selectImage = select.parentElement.querySelector('.select-image');
|
||||
|
||||
// Set the background image for each option that has a data-image attribute
|
||||
options.forEach(option => {
|
||||
const image = option.getAttribute('data-image');
|
||||
if (image) {
|
||||
option.style.backgroundImage = `url(${image})`;
|
||||
}
|
||||
});
|
||||
|
||||
if (select.value == '-1') {
|
||||
// Set the selected option based on the stored value
|
||||
const storedValue = localStorage.getItem(select.name);
|
||||
if (storedValue) {
|
||||
select.value = storedValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the selected option image based on the selected value
|
||||
const selectedOption = select.querySelector(`option[value="${select.value}"]`);
|
||||
if (selectedOption) {
|
||||
const image = selectedOption.getAttribute('data-image');
|
||||
if (image) {
|
||||
select.style.backgroundImage = `url(${image})`;
|
||||
selectImage.src = image;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the select element and image when the user makes a selection
|
||||
select.addEventListener('change', () => {
|
||||
const selectedOption = select.options[select.selectedIndex];
|
||||
const image = selectedOption.getAttribute('data-image');
|
||||
if (image) {
|
||||
select.style.backgroundImage = `url(${image})`;
|
||||
selectImage.src = image;
|
||||
} else {
|
||||
select.style.backgroundImage = '';
|
||||
selectImage.src = '';
|
||||
}
|
||||
|
||||
// Save the selected value to localStorage
|
||||
localStorage.setItem(select.name, select.value);
|
||||
});
|
||||
|
||||
// Hide the select icon and image on page load
|
||||
selectIcon.style.display = 'none';
|
||||
selectImage.style.display = 'none';
|
||||
}
|
||||
|
||||
// Call the setupCustomSelect function for each custom select element
|
||||
const customSelects = document.querySelectorAll('.custom-select select');
|
||||
customSelects.forEach(select => {
|
||||
setupCustomSelect(select);
|
||||
});
|
||||
|
||||
|
||||
20
basicswap/static/js/libs/chart.js
Normal file
1015
basicswap/static/js/libs/dd.min.js
vendored
@@ -37,4 +37,4 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,27 +1,40 @@
|
||||
|
||||
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');
|
||||
}
|
||||
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');
|
||||
}
|
||||
if (err_msg.indexOf('Select coin you send') >= 0) {
|
||||
e = document.getElementById('coin_from').parentNode;
|
||||
e.classList.add('error');
|
||||
}
|
||||
}
|
||||
|
||||
// remove error class on input or select focus
|
||||
let inputs = document.querySelectorAll('input.error');
|
||||
let selects = document.querySelectorAll('select.error');
|
||||
let elements = [...inputs, ...selects];
|
||||
elements.forEach((element) => {
|
||||
element.addEventListener('focus', (event) => {
|
||||
event.target.classList.remove('error');
|
||||
});
|
||||
});
|
||||
});
|
||||
414
basicswap/static/sequence_diagrams/ads.bidder.alt.xu.min.svg
Normal file
@@ -0,0 +1,414 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="mscgenjsreplaceme" width="1272" height="2063.25" class="mscgenjsreplaceme" 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" version="1.1">
|
||||
<defs>
|
||||
<marker id="mscgenjsreplacemecallback-#0000FF" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path d="m1 1 8 2-8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:#00f"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacemecallback-l-#0000FF" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path d="M17 1 9 3l8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:#00f"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacemecallback-#008800" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path d="m1 1 8 2-8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:#080"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacemecallback-l-#008800" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path d="M17 1 9 3l8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:#080"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacemecallback-#FF0000" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path d="m1 1 8 2-8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:red"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacemecallback-l-#FF0000" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path d="M17 1 9 3l8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:red"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacememethod-#0000FF" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path fill="#00F" stroke="#00F" d="m1 1 8 2-8 2z" class="arrow-style"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacememethod-l-#0000FF" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path fill="#00F" stroke="#00F" d="M17 1 9 3l8 2z" class="arrow-style"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacememethod-#FF0000" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path fill="red" stroke="red" d="m1 1 8 2-8 2z" class="arrow-style"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacememethod-l-#FF0000" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path fill="red" stroke="red" d="M17 1 9 3l8 2z" class="arrow-style"/>
|
||||
</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 id="mscgenjsreplaceme_background" d="M-51-3h1272v2063.25H-51z" class="bglayer" style="fill:#fff;stroke:#fff;stroke-width:0"/>
|
||||
<g id="mscgenjsreplaceme_arcspans">
|
||||
<path d="M-41 869.1h1044v1169.15H-41z" class="box inline_expression alt"/>
|
||||
<path d="M-37 1410.15H999v590.1H-37z" class="box inline_expression alt"/>
|
||||
</g>
|
||||
<g id="mscgenjsreplaceme_lifelines">
|
||||
<path d="M65 38v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 38v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 38v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 38v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 38v38M1105 38v38M65 76v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 76v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 76v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 76v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 76v38M1105 76v38M65 114v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 114v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 114v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 114v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 114v38M1105 114v38M65 152v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 152v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 152v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 152v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 152v38M1105 152v38M65 190v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 190v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 190v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 190v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 190v38M1105 190v38M65 228v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 228v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 228v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 228v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 228v38M1105 228v38M65 266v54" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 266v54" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 266v54" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 266v54" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 266v54M1105 266v54M65 320v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 320v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 320v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 320v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 320v38M1105 320v38M65 358v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 358v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 358v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 358v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 358v38M1105 358v38M65 396v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 396v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 396v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 396v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 396v38M1105 396v38M65 434v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 434v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 434v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 434v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 434v38M1105 434v38M65 472v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 472v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 472v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 472v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 472v38M1105 472v38M65 510v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 510v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 510v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 510v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 510v38M1105 510v38M65 548v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 548v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 548v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 548v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 548v38M1105 548v38M65 586v75.05" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 586v75.05" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 586v75.05" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 586v75.05" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 586v75.05M1105 586v75.05M65 661.05v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 661.05v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 661.05v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 661.05v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 661.05v38M1105 661.05v38M65 699.05v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 699.05v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 699.05v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 699.05v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 699.05v38M1105 699.05v38M65 737.05v75.05" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 737.05v75.05" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 737.05v75.05" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 737.05v75.05" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 737.05v75.05M1105 737.05v75.05M65 812.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 812.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 812.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 812.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 812.1v38M1105 812.1v38M65 850.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 850.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 850.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 850.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 850.1v38M1105 850.1v38M65 888.1v86" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 888.1v86" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 888.1v86" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 888.1v86" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 888.1v86M1105 888.1v86M65 974.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 974.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 974.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 974.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 974.1v38M1105 974.1v38M65 1012.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1012.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1012.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1012.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1012.1v38M1105 1012.1v38M65 1050.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1050.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1050.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1050.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1050.1v38M1105 1050.1v38M65 1088.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1088.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1088.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1088.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1088.1v38M1105 1088.1v38M65 1126.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1126.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1126.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1126.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1126.1v38M1105 1126.1v38M65 1164.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1164.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1164.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1164.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1164.1v38M1105 1164.1v38M65 1202.1v75.05" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1202.1v75.05" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1202.1v75.05" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1202.1v75.05" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1202.1v75.05M1105 1202.1v75.05M65 1277.15v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1277.15v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1277.15v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1277.15v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1277.15v38M1105 1277.15v38M65 1315.15v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1315.15v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1315.15v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1315.15v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1315.15v38M1105 1315.15v38M65 1353.15v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1353.15v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1353.15v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1353.15v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1353.15v38M1105 1353.15v38M65 1391.15v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1391.15v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1391.15v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1391.15v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1391.15v38M1105 1391.15v38M65 1429.15v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1429.15v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1429.15v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1429.15v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1429.15v38M1105 1429.15v38M65 1467.15v59.05" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1467.15v59.05" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1467.15v59.05" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1467.15v59.05" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1467.15v59.05M1105 1467.15v59.05M65 1526.2v54" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1526.2v54" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1526.2v54" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1526.2v54" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1526.2v54M1105 1526.2v54M65 1580.2v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1580.2v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1580.2v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1580.2v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1580.2v38M1105 1580.2v38M65 1618.2v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1618.2v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1618.2v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1618.2v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1618.2v38M1105 1618.2v38M65 1656.2v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1656.2v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1656.2v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1656.2v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1656.2v38M1105 1656.2v38M65 1694.2v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1694.2v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1694.2v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1694.2v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1694.2v38M1105 1694.2v38M65 1732.2v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1732.2v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1732.2v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1732.2v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1732.2v38M1105 1732.2v38M65 1770.2v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1770.2v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1770.2v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1770.2v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1770.2v38M1105 1770.2v38M65 1808.2v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1808.2v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1808.2v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1808.2v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1808.2v38M1105 1808.2v38M65 1846.2v59.05" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1846.2v59.05" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1846.2v59.05" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1846.2v59.05" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1846.2v59.05M1105 1846.2v59.05M65 1905.25v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1905.25v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1905.25v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1905.25v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1905.25v38M1105 1905.25v38M65 1943.25v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1943.25v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1943.25v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1943.25v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1943.25v38M1105 1943.25v38M65 1981.25v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1981.25v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1981.25v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1981.25v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1981.25v38M1105 1981.25v38M65 2019.25v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 2019.25v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 2019.25v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 2019.25v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 2019.25v38M1105 2019.25v38" class="arcrow" style="stroke:transparent"/>
|
||||
</g>
|
||||
<g id="mscgenjsreplaceme_sequence">
|
||||
<path d="M0 0h130v38H0z" class="entity" style="stroke:transparent"/>
|
||||
<text x="65" y="22.75" class="entity-text"><tspan> </tspan></text>
|
||||
<path d="M208 0h130v38H208z" class="entity" style="fill:#cfc;stroke:#080"/>
|
||||
<text x="273" y="22.75" class="entity-text"><tspan>Network</tspan></text>
|
||||
<path d="M416 0h130v38H416z" class="entity" style="fill:#fcc;stroke:red"/>
|
||||
<text x="481" y="22.75" class="entity-text"><tspan>Offerer</tspan></text>
|
||||
<path d="M624 0h130v38H624z" class="entity" style="fill:#ccf;stroke:#00f"/>
|
||||
<text x="689" y="22.75" class="entity-text"><tspan>Bidder</tspan></text>
|
||||
<path d="M832 0h130v38H832z" class="entity" style="stroke:transparent"/>
|
||||
<text x="897" y="22.75" class="entity-text"><tspan> </tspan></text>
|
||||
<path d="M1040 0h130v38h-130z" class="entity" style="stroke:transparent"/>
|
||||
<text x="1105" y="22.75" class="entity-text"><tspan> </tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 95H273" class="arc directional callback" style="stroke:red"/>
|
||||
<path d="M345.09 79.25h63.83v14h-63.83z" class="label-text-background"/>
|
||||
<text x="377" y="90.25" class="directional-text callback-text"><tspan>Sends Offer</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#008800)" d="M273 133h416" class="arc directional return" style="stroke:#080"/>
|
||||
<path d="M445.76 117.25h70.48v14h-70.48z" class="label-text-background"/>
|
||||
<text x="481" y="128.25" class="directional-text return-text"><tspan>Detects Offer</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 171H481" class="arc directional callback" style="stroke:#00f"/>
|
||||
<path d="M557.65 155.25h54.7v14h-54.7z" class="label-text-background"/>
|
||||
<text x="585" y="166.25" class="directional-text callback-text"><tspan>Sends Bid</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 293h208" class="arc directional callback" style="stroke:red"/>
|
||||
<path d="M513.29 277.25h143.74v14H513.29z" class="label-text-background"/>
|
||||
<text x="585" y="288.25" class="directional-text callback-text"><tspan>Sends BidAccept message</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 415H481" class="arc directional callback" style="stroke:#00f"/>
|
||||
<path d="M491.29 399.25h187.74v14H491.29z" class="label-text-background"/>
|
||||
<text x="585" y="410.25" class="directional-text callback-text"><tspan>Sends XmrBidLockTxSigsMessage</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 491h208" class="arc directional callback" style="stroke:red"/>
|
||||
<path d="M485.61 475.25H684.7v14H485.61z" class="label-text-background"/>
|
||||
<text x="585" y="486.25" class="directional-text callback-text"><tspan>Sends XmrBidLockSpendTxMessage</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 529H273" class="arc directional callback" style="stroke:red"/>
|
||||
<path d="M311.64 513.25h130.72v14H311.64z" class="label-text-background"/>
|
||||
<text x="377" y="524.25" class="directional-text callback-text"><tspan>Sends script-coin-lock-tx</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacememethod-#0000FF)" d="M689 615.92c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:#00f"/>
|
||||
<path d="M692 567.67h40.91v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="578.67" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path d="M692 583.67h107.01v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="594.67" class="directional-text method-text anchor-start"><tspan>script-coin-lock-tx to</tspan></text>
|
||||
<path d="M692 599.67h39.34v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="610.67" class="directional-text method-text anchor-start"><tspan>confirm</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 718.05H273" class="arc directional callback" style="stroke:#00f"/>
|
||||
<path d="M408.97 702.3h144.06v14H408.97z" class="label-text-background"/>
|
||||
<text x="481" y="713.3" class="directional-text callback-text"><tspan>Sends noscript-coin-lock-tx</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacememethod-#0000FF)" d="M689 766.97c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:#00f"/>
|
||||
<path d="M692 718.72h40.91v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="729.72" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path d="M692 734.72h120.36v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="745.72" class="directional-text method-text anchor-start"><tspan>noscript-coin-lock-tx to</tspan></text>
|
||||
<path d="M692 750.72h39.34v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="761.72" class="directional-text method-text anchor-start"><tspan>confirm</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacememethod-#FF0000)" d="M481 766.97c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:red"/>
|
||||
<path d="M484 718.72h40.91v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="729.72" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path d="M484 734.72h120.36v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="745.72" class="directional-text method-text anchor-start"><tspan>noscript-coin-lock-tx to</tspan></text>
|
||||
<path d="M484 750.72h39.34v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="761.72" class="directional-text method-text anchor-start"><tspan>confirm</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacememethod-#FF0000)" d="M481 931.1h208" class="arc directional method" style="stroke:red"/>
|
||||
<path d="M519.64 915.35h130.72v14H519.64z" class="label-text-background"/>
|
||||
<text x="585" y="926.35" class="directional-text method-text"><tspan>Sends script-coin-lock-tx</tspan></text>
|
||||
<path d="M539.3 933.35h91.71v14H539.3z" class="label-text-background"/>
|
||||
<text x="585" y="944.35" class="directional-text method-text"><tspan>release message</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 1031.1H273" class="arc directional callback" style="stroke:#00f"/>
|
||||
<path d="M397.3 1015.35h167.41v14H397.3z" class="label-text-background"/>
|
||||
<text x="481" y="1026.35" class="directional-text callback-text"><tspan>Sends script-coin-lock-spend-tx</tspan></text>
|
||||
<path d="M-41 1145.1h1044" class="inline_expression_divider"/>
|
||||
<path d="M459.98 1137.85h42.03v14h-42.03z" class="label-text-background"/>
|
||||
<text x="481" y="1148.85" class="empty-text comment-row-text"><tspan>fail path</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacememethod-#0000FF)" d="M689 1232.02c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:#00f"/>
|
||||
<path d="M692 1183.77h40.91v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="1194.77" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path d="M692 1199.77h131.69v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="1210.77" class="directional-text method-text anchor-start"><tspan>script-coin-lock-tx lock to</tspan></text>
|
||||
<path d="M692 1215.77h33.01v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="1226.77" class="directional-text method-text anchor-start"><tspan>expire</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 1296.15H273" class="arc directional callback" style="stroke:red"/>
|
||||
<path d="M359.98 1280.4h34.03v14h-34.03z" class="label-text-background"/>
|
||||
<text x="377" y="1291.4" class="directional-text callback-text"><tspan>Sends</tspan></text>
|
||||
<path d="M300.64 1298.4h152.72v14H300.64z" class="label-text-background"/>
|
||||
<text x="377" y="1309.4" class="directional-text callback-text"><tspan>script-coin-lock-pre-refund-tx</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#008800)" d="M273 1334.15h208" class="arc directional return" style="stroke:#080"/>
|
||||
<path d="M300.64 1318.4h152.72v14H300.64z" class="label-text-background"/>
|
||||
<text x="377" y="1329.4" class="directional-text return-text"><tspan>script-coin-lock-pre-refund-tx</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacememethod-#FF0000)" d="M481 1489.07c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:red"/>
|
||||
<path d="M484 1456.82h40.91v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="1467.82" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path d="M484 1472.82h124.06v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="1483.82" class="directional-text method-text anchor-start"><tspan>pre-refund tx to confirm</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 1553.2H273" class="arc directional callback" style="stroke:red"/>
|
||||
<path d="M359.98 1537.45h34.03v14h-34.03z" class="label-text-background"/>
|
||||
<text x="377" y="1548.45" class="directional-text callback-text"><tspan>Sends</tspan></text>
|
||||
<path d="M282.3 1555.45h189.41v14H282.3z" class="label-text-background"/>
|
||||
<text x="377" y="1566.45" class="directional-text callback-text"><tspan>script-coin-lock-pre-refund-spend-tx</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#008800)" d="M273 1637.2h416" class="arc directional return" style="stroke:#080"/>
|
||||
<path d="M364.29 1621.45h233.42v14H364.29z" class="label-text-background"/>
|
||||
<text x="481" y="1632.45" class="directional-text return-text"><tspan>Detects script-coin-lock-pre-refund-spend-tx</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 1675.2H273" class="arc directional callback" style="stroke:#00f"/>
|
||||
<path d="M382.97 1659.45h196.06v14H382.97z" class="label-text-background"/>
|
||||
<text x="481" y="1670.45" class="directional-text callback-text"><tspan>Sends scriptless-coin-lock-recover-tx</tspan></text>
|
||||
<path d="M-37 1789.2H999" class="inline_expression_divider"/>
|
||||
<path d="M396.96 1781.95h168.08v14H396.96z" class="label-text-background"/>
|
||||
<text x="481" y="1792.95" class="empty-text comment-row-text"><tspan>bidder swipes script coin lock tx</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacememethod-#0000FF)" d="M689 1868.12c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:#00f"/>
|
||||
<path d="M692 1835.87h40.91v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="1846.87" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path d="M692 1851.87h142.39v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="1862.87" class="directional-text method-text anchor-start"><tspan>pre-refund tx lock to expire</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 1924.25H273" class="arc directional callback" style="stroke:#00f"/>
|
||||
<path d="M368.63 1908.5h224.75v14H368.63z" class="label-text-background"/>
|
||||
<text x="481" y="1919.5" 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 d="M383 230h196v34H383z" class="box" style="stroke:red"/>
|
||||
<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>proof the offerer can sign 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.05 3-17h190l3 17-3 17H594z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="683.8" class="box-text abox-text"><tspan>Bid Script coin locked</tspan></text>
|
||||
<path d="m591 831.1 3-17h190l3 17-3 17H594z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="834.85" class="box-text abox-text"><tspan>Bid Scriptless coin locked</tspan></text>
|
||||
<path d="M-40 869.1h98.39v11l-7 7H-40" class="box inline_expression_label"/>
|
||||
<text x="-38" y="882.35" class="inline_expression-text alt-text anchor-start"><tspan>alt: success path</tspan></text>
|
||||
<path d="M799 890.1h395v9h9m-9-9 9 9v73H799v-82z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="902.85" class="box-text note-text"><tspan>The XmrBidLockReleaseMessage contains the offerer's OTVES for it. </tspan></text>
|
||||
<text x="1001" y="918.85" class="box-text note-text"><tspan> The bidder decodes the offerer's signature</tspan></text>
|
||||
<text x="1001" y="934.85" class="box-text note-text"><tspan>from the OTVES. When the offerer has the</tspan></text>
|
||||
<text x="1001" y="950.85" 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.85" class="box-text note-text"><tspan>signature.</tspan></text>
|
||||
<path d="m591 993.1 3-17h190l3 17-3 17H594z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="996.85" class="box-text abox-text"><tspan>Script coin lock released</tspan></text>
|
||||
<path d="m591 1069.1 3-17h190l3 17-3 17H594z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="1072.85" class="box-text abox-text"><tspan>Script tx redeemed</tspan></text>
|
||||
<path d="m591 1107.1 3-17h190l3 17-3 17H594z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="1110.85" class="box-text abox-text"><tspan>Bid Completed</tspan></text>
|
||||
<path d="M799 1279.15h395v9h9m-9-9 9 9v25H799v-34z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="1299.9" class="box-text note-text"><tspan>tx can be sent by either party.</tspan></text>
|
||||
<path d="m591 1372.15 3-17h190l3 17-3 17H594z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="1367.9" class="box-text abox-text"><tspan>Bid Script pre-refund tx in</tspan></text>
|
||||
<text x="689" y="1383.9" class="box-text abox-text"><tspan>chain</tspan></text>
|
||||
<path d="M-36 1410.15h200.86v11l-7 7H-36" class="box inline_expression_label"/>
|
||||
<text x="-34" y="1423.4" class="inline_expression-text alt-text anchor-start"><tspan>alt: offerer refunds script coin lock tx</tspan></text>
|
||||
<path d="M799 1528.2h395v9h9m-9-9 9 9v41H799v-50z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="1540.95" class="box-text note-text"><tspan>Refunds the script lock tx, with the offerer's cleartext signature</tspan></text>
|
||||
<text x="1001" y="1556.95" class="box-text note-text"><tspan>the bidder can refund the noscript lock tx. </tspan></text>
|
||||
<text x="1001" y="1572.95" 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.2 3-17h190l3 17-3 17H386z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="1602.95" class="box-text abox-text"><tspan>Bid Failed, refunded</tspan></text>
|
||||
<path d="M799 1620.2h395v9h9m-9-9 9 9v25H799v-34z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="1640.95" class="box-text note-text"><tspan>Bidder recovers the offerer's scriptless chain key-shard.</tspan></text>
|
||||
<path d="m591 1713.2 3-17h190l3 17-3 17H594z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="1716.95" class="box-text abox-text"><tspan>Bid Scriptless tx recovered</tspan></text>
|
||||
<path d="m591 1751.2 3-17h190l3 17-3 17H594z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="1754.95" class="box-text abox-text"><tspan>Bid Failed, refunded</tspan></text>
|
||||
<path d="m591 1962.25 3-17h190l3 17-3 17H594z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="1966" class="box-text abox-text"><tspan>Bid Failed, swiped</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 38 KiB |
388
basicswap/static/sequence_diagrams/ads.offerer.alt.xu.min.svg
Normal file
@@ -0,0 +1,388 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="mscgenjsreplaceme" width="1264" height="1933.25" class="mscgenjsreplaceme" 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" version="1.1">
|
||||
<defs>
|
||||
<marker id="mscgenjsreplacemecallback-#0000FF" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path d="m1 1 8 2-8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:#00f"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacemecallback-l-#0000FF" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path d="M17 1 9 3l8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:#00f"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacemecallback-#008800" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path d="m1 1 8 2-8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:#080"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacemecallback-l-#008800" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path d="M17 1 9 3l8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:#080"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacemecallback-#FF0000" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path d="m1 1 8 2-8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:red"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacemecallback-l-#FF0000" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path d="M17 1 9 3l8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:red"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacememethod-#0000FF" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path fill="#00F" stroke="#00F" d="m1 1 8 2-8 2z" class="arrow-style"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacememethod-l-#0000FF" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path fill="#00F" stroke="#00F" d="M17 1 9 3l8 2z" class="arrow-style"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacememethod-#FF0000" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path fill="red" stroke="red" d="m1 1 8 2-8 2z" class="arrow-style"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacememethod-l-#FF0000" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path fill="red" stroke="red" d="M17 1 9 3l8 2z" class="arrow-style"/>
|
||||
</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 id="mscgenjsreplaceme_background" d="M-47-3h1264v1933.25H-47z" class="bglayer" style="fill:#fff;stroke:#fff;stroke-width:0"/>
|
||||
<path id="mscgenjsreplaceme_arcspans" d="M-39 778.05h1040v1130.2H-39z" class="box inline_expression alt"/>
|
||||
<g id="mscgenjsreplaceme_lifelines">
|
||||
<path d="M65 38v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 38v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 38v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 38v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 38v38M1105 38v38M65 76v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 76v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 76v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 76v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 76v38M1105 76v38M65 114v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 114v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 114v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 114v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 114v38M1105 114v38M65 152v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 152v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 152v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 152v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 152v38M1105 152v38M65 190v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 190v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 190v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 190v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 190v38M1105 190v38M65 228v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 228v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 228v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 228v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 228v38M1105 228v38M65 266v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 266v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 266v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 266v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 266v38M1105 266v38M65 304v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 304v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 304v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 304v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 304v38M1105 304v38M65 342v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 342v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 342v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 342v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 342v38M1105 342v38M65 380v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 380v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 380v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 380v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 380v38M1105 380v38M65 418v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 418v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 418v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 418v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 418v38M1105 418v38M65 456v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 456v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 456v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 456v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 456v38M1105 456v38M65 494v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 494v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 494v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 494v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 494v38M1105 494v38M65 532v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 532v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 532v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 532v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 532v38M1105 532v38M65 570v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 570v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 570v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 570v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 570v38M1105 570v38M65 608v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 608v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 608v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 608v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 608v38M1105 608v38M65 646v75.05" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 646v75.05" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 646v75.05" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 646v75.05" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 646v75.05M1105 646v75.05M65 721.05v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 721.05v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 721.05v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 721.05v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 721.05v38M1105 721.05v38M65 759.05v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 759.05v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 759.05v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 759.05v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 759.05v38M1105 759.05v38M65 797.05v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 797.05v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 797.05v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 797.05v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 797.05v38M1105 797.05v38M65 835.05v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 835.05v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 835.05v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 835.05v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 835.05v38M1105 835.05v38M65 873.05v75.05" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 873.05v75.05" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 873.05v75.05" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 873.05v75.05" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 873.05v75.05M1105 873.05v75.05M65 948.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 948.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 948.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 948.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 948.1v38M1105 948.1v38M65 986.1v86" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 986.1v86" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 986.1v86" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 986.1v86" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 986.1v86M1105 986.1v86M65 1072.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1072.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1072.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1072.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1072.1v38M1105 1072.1v38M65 1110.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1110.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1110.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1110.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1110.1v38M1105 1110.1v38M65 1148.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1148.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1148.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1148.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1148.1v38M1105 1148.1v38M65 1186.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1186.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1186.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1186.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1186.1v38M1105 1186.1v38M65 1224.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1224.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1224.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1224.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1224.1v38M1105 1224.1v38M65 1262.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1262.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1262.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1262.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1262.1v38M1105 1262.1v38M65 1300.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1300.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1300.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1300.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1300.1v38M1105 1300.1v38M65 1338.1v75.05" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1338.1v75.05" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1338.1v75.05" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1338.1v75.05" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1338.1v75.05M1105 1338.1v75.05M65 1413.15v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1413.15v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1413.15v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1413.15v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1413.15v38M1105 1413.15v38M65 1451.15v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1451.15v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1451.15v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1451.15v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1451.15v38M1105 1451.15v38M65 1489.15v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1489.15v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1489.15v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1489.15v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1489.15v38M1105 1489.15v38M65 1527.15v75.05" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1527.15v75.05" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1527.15v75.05" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1527.15v75.05" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1527.15v75.05M1105 1527.15v75.05M65 1602.2v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1602.2v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1602.2v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1602.2v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1602.2v38M1105 1602.2v38M65 1640.2v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1640.2v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1640.2v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1640.2v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1640.2v38M1105 1640.2v38M65 1678.2v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1678.2v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1678.2v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1678.2v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1678.2v38M1105 1678.2v38M65 1716.2v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1716.2v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1716.2v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1716.2v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1716.2v38M1105 1716.2v38M65 1754.2v59.05" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1754.2v59.05" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1754.2v59.05" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1754.2v59.05" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1754.2v59.05M1105 1754.2v59.05M65 1813.25v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1813.25v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1813.25v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1813.25v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1813.25v38M1105 1813.25v38M65 1851.25v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1851.25v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1851.25v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1851.25v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1851.25v38M1105 1851.25v38M65 1889.25v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1889.25v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1889.25v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1889.25v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1889.25v38M1105 1889.25v38" class="arcrow" style="stroke:transparent"/>
|
||||
</g>
|
||||
<g id="mscgenjsreplaceme_sequence">
|
||||
<path d="M0 0h130v38H0z" class="entity" style="stroke:transparent"/>
|
||||
<text x="65" y="22.75" class="entity-text"><tspan> </tspan></text>
|
||||
<path d="M208 0h130v38H208z" class="entity" style="fill:#cfc;stroke:#080"/>
|
||||
<text x="273" y="22.75" class="entity-text"><tspan>Network</tspan></text>
|
||||
<path d="M416 0h130v38H416z" class="entity" style="fill:#fcc;stroke:red"/>
|
||||
<text x="481" y="22.75" class="entity-text"><tspan>Offerer</tspan></text>
|
||||
<path d="M624 0h130v38H624z" class="entity" style="fill:#ccf;stroke:#00f"/>
|
||||
<text x="689" y="22.75" class="entity-text"><tspan>Bidder</tspan></text>
|
||||
<path d="M832 0h130v38H832z" class="entity" style="stroke:transparent"/>
|
||||
<text x="897" y="22.75" class="entity-text"><tspan> </tspan></text>
|
||||
<path d="M1040 0h130v38h-130z" class="entity" style="stroke:transparent"/>
|
||||
<text x="1105" y="22.75" class="entity-text"><tspan> </tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 95H273" class="arc directional callback" style="stroke:red"/>
|
||||
<path d="M345.09 79.25h63.83v14h-63.83z" class="label-text-background"/>
|
||||
<text x="377" y="90.25" class="directional-text callback-text"><tspan>Sends Offer</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#008800)" d="M273 133h416" class="arc directional return" style="stroke:#080"/>
|
||||
<path d="M445.76 117.25h70.48v14h-70.48z" class="label-text-background"/>
|
||||
<text x="481" y="128.25" class="directional-text return-text"><tspan>Detects Offer</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 171H481" class="arc directional callback" style="stroke:#00f"/>
|
||||
<path d="M557.65 155.25h54.7v14h-54.7z" class="label-text-background"/>
|
||||
<text x="585" y="166.25" class="directional-text callback-text"><tspan>Sends Bid</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 323h208" class="arc directional callback" style="stroke:red"/>
|
||||
<path d="M513.29 307.25h143.74v14H513.29z" class="label-text-background"/>
|
||||
<text x="585" y="318.25" class="directional-text callback-text"><tspan>Sends BidAccept message</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 399H481" class="arc directional callback" style="stroke:#00f"/>
|
||||
<path d="M491.29 383.25h187.74v14H491.29z" class="label-text-background"/>
|
||||
<text x="585" y="394.25" class="directional-text callback-text"><tspan>Sends XmrBidLockTxSigsMessage</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 475H273" class="arc directional callback" style="stroke:red"/>
|
||||
<path d="M311.64 459.25h130.72v14H311.64z" class="label-text-background"/>
|
||||
<text x="377" y="470.25" class="directional-text callback-text"><tspan>Sends script-coin-lock-tx</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 551h208" class="arc directional callback" style="stroke:red"/>
|
||||
<path d="M485.61 535.25H684.7v14H485.61z" class="label-text-background"/>
|
||||
<text x="585" y="546.25" class="directional-text callback-text"><tspan>Sends XmrBidLockSpendTxMessage</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacememethod-#0000FF)" d="M689 675.92c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:#00f"/>
|
||||
<path d="M692 627.67h40.91v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="638.67" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path d="M692 643.67h107.01v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="654.67" class="directional-text method-text anchor-start"><tspan>script-coin-lock-tx to</tspan></text>
|
||||
<path d="M692 659.67h39.34v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="670.67" class="directional-text method-text anchor-start"><tspan>confirm</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacememethod-#FF0000)" d="M481 675.92c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:red"/>
|
||||
<path d="M484 627.67h40.91v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="638.67" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path d="M484 643.67h107.01v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="654.67" class="directional-text method-text anchor-start"><tspan>script-coin-lock-tx to</tspan></text>
|
||||
<path d="M484 659.67h39.34v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="670.67" class="directional-text method-text anchor-start"><tspan>confirm</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 816.05H273" class="arc directional callback" style="stroke:#00f"/>
|
||||
<path d="M408.97 800.3h144.06v14H408.97z" class="label-text-background"/>
|
||||
<text x="481" y="811.3" class="directional-text callback-text"><tspan>Sends noscript-coin-lock-tx</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacememethod-#FF0000)" d="M481 902.97c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:red"/>
|
||||
<path d="M484 854.72h40.91v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="865.72" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path d="M484 870.72h120.36v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="881.72" class="directional-text method-text anchor-start"><tspan>noscript-coin-lock-tx to</tspan></text>
|
||||
<path d="M484 886.72h39.34v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="897.72" class="directional-text method-text anchor-start"><tspan>confirm</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacememethod-#FF0000)" d="M481 1029.1h208" class="arc directional method" style="stroke:red"/>
|
||||
<path d="M519.64 1013.35h130.72v14H519.64z" class="label-text-background"/>
|
||||
<text x="585" y="1024.35" class="directional-text method-text"><tspan>Sends script-coin-lock-tx</tspan></text>
|
||||
<path d="M539.3 1031.35h91.71v14H539.3z" class="label-text-background"/>
|
||||
<text x="585" y="1042.35" class="directional-text method-text"><tspan>release message</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 1129.1H273" class="arc directional callback" style="stroke:#00f"/>
|
||||
<path d="M397.3 1113.35h167.41v14H397.3z" class="label-text-background"/>
|
||||
<text x="481" y="1124.35" class="directional-text callback-text"><tspan>Sends script-coin-lock-spend-tx</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#008800)" d="M273 1167.1h208" class="arc directional return" style="stroke:#080"/>
|
||||
<path d="M289.97 1151.35h174.06v14H289.97z" class="label-text-background"/>
|
||||
<text x="377" y="1162.35" class="directional-text return-text"><tspan>Detects script-coin-lock-spend-tx</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 1243.1H273" class="arc directional callback" style="stroke:red"/>
|
||||
<path d="M286.63 1227.35h180.75v14H286.63z" class="label-text-background"/>
|
||||
<text x="377" y="1238.35" class="directional-text callback-text"><tspan>Sends noscript-coin-lock-spend-tx</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacememethod-#FF0000)" d="M481 1368.02c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:red"/>
|
||||
<path d="M484 1319.77h40.91v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="1330.77" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path d="M484 1335.77h143.39v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="1346.77" class="directional-text method-text anchor-start"><tspan>noscript-coin-lock-spend-tx</tspan></text>
|
||||
<path d="M484 1351.77h52.69v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="1362.77" class="directional-text method-text anchor-start"><tspan>to confirm</tspan></text>
|
||||
<path d="M-39 1470.15h1040" class="inline_expression_divider" style="stroke-dasharray:10,5"/>
|
||||
<path d="M459.98 1462.9h42.03v14h-42.03z" class="label-text-background"/>
|
||||
<text x="481" y="1473.9" class="empty-text comment-row-text"><tspan>fail path</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacememethod-#FF0000)" d="M481 1557.07c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:red"/>
|
||||
<path d="M484 1508.82h40.91v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="1519.82" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path d="M484 1524.82h93.36v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="1535.82" class="directional-text method-text anchor-start"><tspan>script-coin-lock-tx</tspan></text>
|
||||
<path d="M484 1540.82h93.7v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="1551.82" class="directional-text method-text anchor-start"><tspan>locktime to expire</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 1621.2H273" class="arc directional callback" style="stroke:red"/>
|
||||
<path d="M359.98 1605.45h34.03v14h-34.03z" class="label-text-background"/>
|
||||
<text x="377" y="1616.45" class="directional-text callback-text"><tspan>Sends</tspan></text>
|
||||
<path d="M300.64 1623.45h152.72v14H300.64z" class="label-text-background"/>
|
||||
<text x="377" y="1634.45" class="directional-text callback-text"><tspan>script-coin-lock-pre-refund-tx</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#008800)" d="M273 1659.2h208" class="arc directional return" style="stroke:#080"/>
|
||||
<path d="M300.64 1643.45h152.72v14H300.64z" class="label-text-background"/>
|
||||
<text x="377" y="1654.45" class="directional-text return-text"><tspan>script-coin-lock-pre-refund-tx</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacememethod-#FF0000)" d="M481 1776.12c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:red"/>
|
||||
<path d="M484 1743.87h40.91v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="1754.87" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path d="M484 1759.87h124.06v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="1770.87" class="directional-text method-text anchor-start"><tspan>pre-refund tx to confirm</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 1832.25H273" class="arc directional callback" style="stroke:red"/>
|
||||
<path d="M359.98 1816.5h34.03v14h-34.03z" class="label-text-background"/>
|
||||
<text x="377" y="1827.5" class="directional-text callback-text"><tspan>Sends</tspan></text>
|
||||
<path d="M282.3 1834.5h189.41v14H282.3z" class="label-text-background"/>
|
||||
<text x="377" y="1845.5" 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 d="M381 268h200v34H381z" class="box" style="stroke:red"/>
|
||||
<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</tspan></text>
|
||||
<text x="1001" y="562.75" class="box-text note-text"><tspan>proof the offerer can sign 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.05 3-17h194l3 17-3 17H384z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="743.8" class="box-text abox-text"><tspan>Bid Script coin locked</tspan></text>
|
||||
<path d="M-38 778.05h98.39v11l-7 7H-38" class="box inline_expression_label"/>
|
||||
<text x="-36" y="791.3" class="inline_expression-text alt-text anchor-start"><tspan>alt: success path</tspan></text>
|
||||
<path d="m381 967.1 3-17h194l3 17-3 17H384z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="970.85" class="box-text abox-text"><tspan>Bid Scriptless coin locked</tspan></text>
|
||||
<path d="M797 988.1h399v9h9m-9-9 9 9v73H797v-82z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="1000.85" class="box-text note-text"><tspan>The XmrBidLockReleaseMessage contains the offerer's OTVES for the</tspan></text>
|
||||
<text x="1001" y="1016.85" class="box-text note-text"><tspan>script-coin-lock-tx. The bidder decodes the</tspan></text>
|
||||
<text x="1001" y="1032.85" class="box-text note-text"><tspan>offerer's signature from the OTVES. When the</tspan></text>
|
||||
<text x="1001" y="1048.85" 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.85" class="box-text note-text"><tspan>for the noscript-lock-tx.</tspan></text>
|
||||
<path d="m381 1091.1 3-17h194l3 17-3 17H384z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="1094.85" class="box-text abox-text"><tspan>Bid Script coin lock released</tspan></text>
|
||||
<path d="m381 1205.1 3-17h194l3 17-3 17H384z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="1208.85" class="box-text abox-text"><tspan>Bid Script tx redeemed</tspan></text>
|
||||
<path d="M797 1188.1h399v9h9m-9-9 9 9v25H797v-34z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="1200.85" class="box-text note-text"><tspan>The offerer extracts the bidder's plaintext signature and derives the</tspan></text>
|
||||
<text x="1001" y="1216.85" class="box-text note-text"><tspan>bidder's noscript-lock-tx keyhalf.</tspan></text>
|
||||
<path d="m381 1281.1 3-17h194l3 17-3 17H384z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="1284.85" class="box-text abox-text"><tspan>Bid Scriptless tx redeemed</tspan></text>
|
||||
<path d="m381 1432.15 3-17h194l3 17-3 17H384z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="1435.9" class="box-text abox-text"><tspan>Bid Completed</tspan></text>
|
||||
<path d="M797 1604.2h399v9h9m-9-9 9 9v25H797v-34z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="1624.95" class="box-text note-text"><tspan>tx can be sent by either party.</tspan></text>
|
||||
<path d="m381 1697.2 3-17h194l3 17-3 17H384z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="1692.95" class="box-text abox-text"><tspan>Bid Script pre-refund tx in</tspan></text>
|
||||
<text x="481" y="1708.95" class="box-text abox-text"><tspan>chain</tspan></text>
|
||||
<path d="M797 1815.25h399v9h9m-9-9 9 9v25H797v-34z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="1828" class="box-text note-text"><tspan>Refunds the script lock tx, with the offerer's cleartext signature</tspan></text>
|
||||
<text x="1001" y="1844" class="box-text note-text"><tspan>the bidder can refund the noscript lock tx.</tspan></text>
|
||||
<path d="m381 1870.25 3-17h194l3 17-3 17H384z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="1874" class="box-text abox-text"><tspan>Bid Failed, refunded</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 36 KiB |
391
basicswap/static/sequence_diagrams/ads.rev.bidder.xu.min.svg
Normal file
@@ -0,0 +1,391 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="mscgenjsreplaceme" width="1264" height="1971.25" class="mscgenjsreplaceme" 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" version="1.1">
|
||||
<defs>
|
||||
<marker id="mscgenjsreplacemecallback-#0000FF" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path d="m1 1 8 2-8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:#00f"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacemecallback-l-#0000FF" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path d="M17 1 9 3l8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:#00f"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacemecallback-#008800" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path d="m1 1 8 2-8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:#080"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacemecallback-l-#008800" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path d="M17 1 9 3l8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:#080"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacemecallback-#FF0000" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path d="m1 1 8 2-8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:red"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacemecallback-l-#FF0000" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path d="M17 1 9 3l8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:red"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacememethod-#0000FF" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path fill="#00F" stroke="#00F" d="m1 1 8 2-8 2z" class="arrow-style"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacememethod-l-#0000FF" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path fill="#00F" stroke="#00F" d="M17 1 9 3l8 2z" class="arrow-style"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacememethod-#FF0000" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path fill="red" stroke="red" d="m1 1 8 2-8 2z" class="arrow-style"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacememethod-l-#FF0000" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path fill="red" stroke="red" d="M17 1 9 3l8 2z" class="arrow-style"/>
|
||||
</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 id="mscgenjsreplaceme_background" d="M-47-3h1264v1971.25H-47z" class="bglayer" style="fill:#fff;stroke:#fff;stroke-width:0"/>
|
||||
<path id="mscgenjsreplaceme_arcspans" d="M-39 816.05h1040v1130.2H-39z" class="box inline_expression alt"/>
|
||||
<g id="mscgenjsreplaceme_lifelines">
|
||||
<path d="M65 38v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 38v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 38v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 38v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 38v38M1105 38v38M65 76v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 76v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 76v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 76v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 76v38M1105 76v38M65 114v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 114v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 114v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 114v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 114v38M1105 114v38M65 152v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 152v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 152v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 152v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 152v38M1105 152v38M65 190v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 190v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 190v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 190v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 190v38M1105 190v38M65 228v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 228v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 228v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 228v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 228v38M1105 228v38M65 266v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 266v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 266v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 266v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 266v38M1105 266v38M65 304v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 304v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 304v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 304v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 304v38M1105 304v38M65 342v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 342v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 342v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 342v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 342v38M1105 342v38M65 380v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 380v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 380v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 380v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 380v38M1105 380v38M65 418v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 418v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 418v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 418v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 418v38M1105 418v38M65 456v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 456v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 456v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 456v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 456v38M1105 456v38M65 494v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 494v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 494v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 494v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 494v38M1105 494v38M65 532v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 532v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 532v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 532v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 532v38M1105 532v38M65 570v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 570v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 570v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 570v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 570v38M1105 570v38M65 608v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 608v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 608v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 608v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 608v38M1105 608v38M65 646v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 646v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 646v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 646v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 646v38M1105 646v38M65 684v75.05" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 684v75.05" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 684v75.05" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 684v75.05" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 684v75.05M1105 684v75.05M65 759.05v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 759.05v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 759.05v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 759.05v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 759.05v38M1105 759.05v38M65 797.05v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 797.05v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 797.05v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 797.05v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 797.05v38M1105 797.05v38M65 835.05v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 835.05v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 835.05v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 835.05v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 835.05v38M1105 835.05v38M65 873.05v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 873.05v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 873.05v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 873.05v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 873.05v38M1105 873.05v38M65 911.05v75.05" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 911.05v75.05" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 911.05v75.05" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 911.05v75.05" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 911.05v75.05M1105 911.05v75.05M65 986.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 986.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 986.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 986.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 986.1v38M1105 986.1v38M65 1024.1v86" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1024.1v86" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1024.1v86" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1024.1v86" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1024.1v86M1105 1024.1v86M65 1110.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1110.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1110.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1110.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1110.1v38M1105 1110.1v38M65 1148.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1148.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1148.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1148.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1148.1v38M1105 1148.1v38M65 1186.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1186.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1186.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1186.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1186.1v38M1105 1186.1v38M65 1224.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1224.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1224.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1224.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1224.1v38M1105 1224.1v38M65 1262.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1262.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1262.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1262.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1262.1v38M1105 1262.1v38M65 1300.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1300.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1300.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1300.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1300.1v38M1105 1300.1v38M65 1338.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1338.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1338.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1338.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1338.1v38M1105 1338.1v38M65 1376.1v75.05" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1376.1v75.05" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1376.1v75.05" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1376.1v75.05" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1376.1v75.05M1105 1376.1v75.05M65 1451.15v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1451.15v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1451.15v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1451.15v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1451.15v38M1105 1451.15v38M65 1489.15v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1489.15v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1489.15v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1489.15v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1489.15v38M1105 1489.15v38M65 1527.15v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1527.15v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1527.15v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1527.15v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1527.15v38M1105 1527.15v38M65 1565.15v75.05" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1565.15v75.05" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1565.15v75.05" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1565.15v75.05" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1565.15v75.05M1105 1565.15v75.05M65 1640.2v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1640.2v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1640.2v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1640.2v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1640.2v38M1105 1640.2v38M65 1678.2v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1678.2v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1678.2v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1678.2v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1678.2v38M1105 1678.2v38M65 1716.2v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1716.2v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1716.2v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1716.2v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1716.2v38M1105 1716.2v38M65 1754.2v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1754.2v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1754.2v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1754.2v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1754.2v38M1105 1754.2v38M65 1792.2v59.05" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1792.2v59.05" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1792.2v59.05" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1792.2v59.05" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1792.2v59.05M1105 1792.2v59.05M65 1851.25v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1851.25v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1851.25v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1851.25v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1851.25v38M1105 1851.25v38M65 1889.25v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1889.25v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1889.25v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1889.25v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1889.25v38M1105 1889.25v38M65 1927.25v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1927.25v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1927.25v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1927.25v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1927.25v38M1105 1927.25v38" class="arcrow" style="stroke:transparent"/>
|
||||
</g>
|
||||
<g id="mscgenjsreplaceme_sequence">
|
||||
<path d="M0 0h130v38H0z" class="entity" style="stroke:transparent"/>
|
||||
<text x="65" y="22.75" class="entity-text"><tspan> </tspan></text>
|
||||
<path d="M208 0h130v38H208z" class="entity" style="fill:#cfc;stroke:#080"/>
|
||||
<text x="273" y="22.75" class="entity-text"><tspan>Network</tspan></text>
|
||||
<path d="M416 0h130v38H416z" class="entity" style="fill:#fcc;stroke:red"/>
|
||||
<text x="481" y="22.75" class="entity-text"><tspan>Offerer</tspan></text>
|
||||
<path d="M624 0h130v38H624z" class="entity" style="fill:#ccf;stroke:#00f"/>
|
||||
<text x="689" y="22.75" class="entity-text"><tspan>Bidder</tspan></text>
|
||||
<path d="M832 0h130v38H832z" class="entity" style="stroke:transparent"/>
|
||||
<text x="897" y="22.75" class="entity-text"><tspan> </tspan></text>
|
||||
<path d="M1040 0h130v38h-130z" class="entity" style="stroke:transparent"/>
|
||||
<text x="1105" y="22.75" class="entity-text"><tspan> </tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 95H273" class="arc directional callback" style="stroke:red"/>
|
||||
<path d="M345.09 79.25h63.83v14h-63.83z" class="label-text-background"/>
|
||||
<text x="377" y="90.25" class="directional-text callback-text"><tspan>Sends Offer</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#008800)" d="M273 133h416" class="arc directional return" style="stroke:#080"/>
|
||||
<path d="M445.76 117.25h70.48v14h-70.48z" class="label-text-background"/>
|
||||
<text x="481" y="128.25" class="directional-text return-text"><tspan>Detects Offer</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 171H481" class="arc directional callback" style="stroke:#00f"/>
|
||||
<path d="M516.62 155.25H653.7v14H516.62z" class="label-text-background"/>
|
||||
<text x="585" y="166.25" class="directional-text callback-text"><tspan>Sends BidIntent message</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 285h208" class="arc directional callback" style="stroke:red"/>
|
||||
<path d="M498.28 269.25h173.76v14H498.28z" class="label-text-background"/>
|
||||
<text x="585" y="280.25" class="directional-text callback-text"><tspan>Sends BidIntentAccept message</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 361H481" class="arc directional callback" style="stroke:#00f"/>
|
||||
<path d="M513.29 345.25h143.74v14H513.29z" class="label-text-background"/>
|
||||
<text x="585" y="356.25" class="directional-text callback-text"><tspan>Sends BidAccept message</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 437h208" class="arc directional callback" style="stroke:red"/>
|
||||
<path d="M491.29 421.25h187.74v14H491.29z" class="label-text-background"/>
|
||||
<text x="585" y="432.25" class="directional-text callback-text"><tspan>Sends XmrBidLockTxSigsMessage</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 513H273" class="arc directional callback" style="stroke:#00f"/>
|
||||
<path d="M415.64 497.25h130.72v14H415.64z" class="label-text-background"/>
|
||||
<text x="481" y="508.25" class="directional-text callback-text"><tspan>Sends script-coin-lock-tx</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 589H481" class="arc directional callback" style="stroke:#00f"/>
|
||||
<path d="M485.61 573.25H684.7v14H485.61z" class="label-text-background"/>
|
||||
<text x="585" y="584.25" class="directional-text callback-text"><tspan>Sends XmrBidLockSpendTxMessage</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacememethod-#FF0000)" d="M481 713.92c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:red"/>
|
||||
<path d="M484 665.67h40.91v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="676.67" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path d="M484 681.67h107.01v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="692.67" class="directional-text method-text anchor-start"><tspan>script-coin-lock-tx to</tspan></text>
|
||||
<path d="M484 697.67h39.34v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="708.67" class="directional-text method-text anchor-start"><tspan>confirm</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacememethod-#0000FF)" d="M689 713.92c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:#00f"/>
|
||||
<path d="M692 665.67h40.91v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="676.67" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path d="M692 681.67h107.01v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="692.67" class="directional-text method-text anchor-start"><tspan>script-coin-lock-tx to</tspan></text>
|
||||
<path d="M692 697.67h39.34v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="708.67" class="directional-text method-text anchor-start"><tspan>confirm</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 854.05H273" class="arc directional callback" style="stroke:red"/>
|
||||
<path d="M304.97 838.3h144.06v14H304.97z" class="label-text-background"/>
|
||||
<text x="377" y="849.3" class="directional-text callback-text"><tspan>Sends noscript-coin-lock-tx</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacememethod-#0000FF)" d="M689 940.97c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:#00f"/>
|
||||
<path d="M692 892.72h40.91v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="903.72" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path d="M692 908.72h120.36v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="919.72" class="directional-text method-text anchor-start"><tspan>noscript-coin-lock-tx to</tspan></text>
|
||||
<path d="M692 924.72h39.34v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="935.72" class="directional-text method-text anchor-start"><tspan>confirm</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacememethod-#0000FF)" d="M689 1067.1H481" class="arc directional method" style="stroke:#00f"/>
|
||||
<path d="M519.64 1051.35h130.72v14H519.64z" class="label-text-background"/>
|
||||
<text x="585" y="1062.35" class="directional-text method-text"><tspan>Sends script-coin-lock-tx</tspan></text>
|
||||
<path d="M539.3 1069.35h91.71v14H539.3z" class="label-text-background"/>
|
||||
<text x="585" y="1080.35" class="directional-text method-text"><tspan>release message</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 1167.1H273" class="arc directional callback" style="stroke:red"/>
|
||||
<path d="M293.3 1151.35h167.41v14H293.3z" class="label-text-background"/>
|
||||
<text x="377" y="1162.35" class="directional-text callback-text"><tspan>Sends script-coin-lock-spend-tx</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#008800)" d="M273 1205.1h416" class="arc directional return" style="stroke:#080"/>
|
||||
<path d="M393.97 1189.35h174.06v14H393.97z" class="label-text-background"/>
|
||||
<text x="481" y="1200.35" class="directional-text return-text"><tspan>Detects script-coin-lock-spend-tx</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 1281.1H273" class="arc directional callback" style="stroke:#00f"/>
|
||||
<path d="M390.63 1265.35h180.75v14H390.63z" class="label-text-background"/>
|
||||
<text x="481" y="1276.35" class="directional-text callback-text"><tspan>Sends noscript-coin-lock-spend-tx</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacememethod-#0000FF)" d="M689 1406.02c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:#00f"/>
|
||||
<path d="M692 1357.77h40.91v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="1368.77" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path d="M692 1373.77h143.39v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="1384.77" class="directional-text method-text anchor-start"><tspan>noscript-coin-lock-spend-tx</tspan></text>
|
||||
<path d="M692 1389.77h52.69v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="1400.77" class="directional-text method-text anchor-start"><tspan>to confirm</tspan></text>
|
||||
<path d="M-39 1508.15h1040" class="inline_expression_divider" style="stroke-dasharray:10,5"/>
|
||||
<path d="M459.98 1500.9h42.03v14h-42.03z" class="label-text-background"/>
|
||||
<text x="481" y="1511.9" class="empty-text comment-row-text"><tspan>fail path</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacememethod-#0000FF)" d="M689 1595.07c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:#00f"/>
|
||||
<path d="M692 1546.82h40.91v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="1557.82" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path d="M692 1562.82h93.36v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="1573.82" class="directional-text method-text anchor-start"><tspan>script-coin-lock-tx</tspan></text>
|
||||
<path d="M692 1578.82h93.7v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="1589.82" class="directional-text method-text anchor-start"><tspan>locktime to expire</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 1659.2H273" class="arc directional callback" style="stroke:#00f"/>
|
||||
<path d="M385.96 1643.45h190.08v14H385.96z" class="label-text-background"/>
|
||||
<text x="481" y="1654.45" class="directional-text callback-text"><tspan>Sends script-coin-lock-pre-refund-tx</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#008800)" d="M273 1697.2h416" class="arc directional return" style="stroke:#080"/>
|
||||
<path d="M404.64 1681.45h152.72v14H404.64z" class="label-text-background"/>
|
||||
<text x="481" y="1692.45" class="directional-text return-text"><tspan>script-coin-lock-pre-refund-tx</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacememethod-#0000FF)" d="M689 1814.12c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:#00f"/>
|
||||
<path d="M692 1781.87h40.91v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="1792.87" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path d="M692 1797.87h124.06v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="1808.87" class="directional-text method-text anchor-start"><tspan>pre-refund tx to confirm</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 1870.25H273" class="arc directional callback" style="stroke:#00f"/>
|
||||
<path d="M367.62 1854.5h226.77v14H367.62z" class="label-text-background"/>
|
||||
<text x="481" y="1865.5" class="directional-text callback-text"><tspan>Sends script-coin-lock-pre-refund-spend-tx</tspan></text>
|
||||
</g>
|
||||
<g id="mscgenjsreplaceme_notes">
|
||||
<path d="m589 209 3-17h194l3 17-3 17H592z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="212.75" class="box-text abox-text"><tspan>Bid Request sent</tspan></text>
|
||||
<path d="M381 230h200v34H381z" class="box" style="stroke:red"/>
|
||||
<text x="481" y="250.75" class="box-text"><tspan>User accepts bid</tspan></text>
|
||||
<path d="M797 268h399v9h9m-9-9 9 9v25H797v-34z" 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 a</tspan></text>
|
||||
<text x="1001" y="296.75" class="box-text note-text"><tspan>DLEAG proof one key will work across both chains of the swapping coins</tspan></text>
|
||||
<path d="m589 323 3-17h194l3 17-3 17H592z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="326.75" class="box-text abox-text"><tspan>Bid Receiving accept</tspan></text>
|
||||
<path d="m589 399 3-17h194l3 17-3 17H592z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="402.75" class="box-text abox-text"><tspan>Bid Accepted</tspan></text>
|
||||
<path d="M797 420h399v9h9m-9-9 9 9v25H797v-34z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="432.75" class="box-text note-text"><tspan>The XmrBidLockTxSigsMessage contains the offerer's signatures for the</tspan></text>
|
||||
<text x="1001" y="448.75" class="box-text note-text"><tspan>script-coin-lock-refund and script-coin-lock-refund-spend txns.</tspan></text>
|
||||
<path d="m589 475 3-17h194l3 17-3 17H592z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="470.75" class="box-text abox-text"><tspan>Exchanged script lock tx sigs</tspan></text>
|
||||
<text x="689" y="486.75" class="box-text abox-text"><tspan>msg</tspan></text>
|
||||
<path d="m589 551 3-17h194l3 17-3 17H592z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="554.75" class="box-text abox-text"><tspan>Bid Script coin spend tx valid</tspan></text>
|
||||
<path d="M797 572h399v9h9m-9-9 9 9v25H797v-34z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="584.75" class="box-text note-text"><tspan>The XmrBidLockSpendTxMessage contains the script-coin-lock-tx and</tspan></text>
|
||||
<text x="1001" y="600.75" class="box-text note-text"><tspan>proof the bidder can sign it.</tspan></text>
|
||||
<path d="m589 627 3-17h194l3 17-3 17H592z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="622.75" class="box-text abox-text"><tspan>Exchanged script lock spend tx</tspan></text>
|
||||
<text x="689" y="638.75" class="box-text abox-text"><tspan>msg</tspan></text>
|
||||
<path d="m589 778.05 3-17h194l3 17-3 17H592z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="781.8" class="box-text abox-text"><tspan>Bid Script coin locked</tspan></text>
|
||||
<path d="M-38 816.05h98.39v11l-7 7H-38" class="box inline_expression_label"/>
|
||||
<text x="-36" y="829.3" class="inline_expression-text alt-text anchor-start"><tspan>alt: success path</tspan></text>
|
||||
<path d="m589 1005.1 3-17h194l3 17-3 17H592z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="1008.85" class="box-text abox-text"><tspan>Bid Scriptless coin locked</tspan></text>
|
||||
<path d="M797 1026.1h399v9h9m-9-9 9 9v73H797v-82z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="1038.85" class="box-text note-text"><tspan>The XmrBidLockReleaseMessage contains the bidder's OTVES for the</tspan></text>
|
||||
<text x="1001" y="1054.85" class="box-text note-text"><tspan>script-coin-lock-tx. The offerer decodes the</tspan></text>
|
||||
<text x="1001" y="1070.85" class="box-text note-text"><tspan>bidder's signature from the OTVES. When the</tspan></text>
|
||||
<text x="1001" y="1086.85" class="box-text note-text"><tspan>bidder has the plaintext signature, they can decode the offerer's key</tspan></text>
|
||||
<text x="1001" y="1102.85" class="box-text note-text"><tspan>for the noscript-lock-tx.</tspan></text>
|
||||
<path d="m589 1129.1 3-17h194l3 17-3 17H592z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="1132.85" class="box-text abox-text"><tspan>Bid Script coin lock released</tspan></text>
|
||||
<path d="m589 1243.1 3-17h194l3 17-3 17H592z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="1246.85" class="box-text abox-text"><tspan>Bid Script tx redeemed</tspan></text>
|
||||
<path d="M797 1226.1h399v9h9m-9-9 9 9v25H797v-34z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="1238.85" class="box-text note-text"><tspan>The bidder extracts the offerer's plaintext signature and derives the</tspan></text>
|
||||
<text x="1001" y="1254.85" class="box-text note-text"><tspan>offerer's noscript-lock-tx keyhalf.</tspan></text>
|
||||
<path d="m589 1319.1 3-17h194l3 17-3 17H592z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="1322.85" class="box-text abox-text"><tspan>Bid Scriptless tx redeemed</tspan></text>
|
||||
<path d="m589 1470.15 3-17h194l3 17-3 17H592z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="1473.9" class="box-text abox-text"><tspan>Bid Completed</tspan></text>
|
||||
<path d="M797 1642.2h399v9h9m-9-9 9 9v25H797v-34z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="1662.95" class="box-text note-text"><tspan>tx can be sent by either party.</tspan></text>
|
||||
<path d="m589 1735.2 3-17h194l3 17-3 17H592z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="1730.95" class="box-text abox-text"><tspan>Bid Script pre-refund tx in</tspan></text>
|
||||
<text x="689" y="1746.95" class="box-text abox-text"><tspan>chain</tspan></text>
|
||||
<path d="M797 1853.25h399v9h9m-9-9 9 9v25H797v-34z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="1866" class="box-text note-text"><tspan>Refunds the script lock tx, with the bidder's cleartext signature the</tspan></text>
|
||||
<text x="1001" y="1882" class="box-text note-text"><tspan>offerer can refund the noscript lock tx.</tspan></text>
|
||||
<path d="m589 1908.25 3-17h194l3 17-3 17H592z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="1912" class="box-text abox-text"><tspan>Bid Failed, refunded</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 36 KiB |
424
basicswap/static/sequence_diagrams/ads.rev.offerer.xu.min.svg
Normal file
@@ -0,0 +1,424 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="mscgenjsreplaceme" width="1272" height="2101.25" class="mscgenjsreplaceme" 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" version="1.1">
|
||||
<defs>
|
||||
<marker id="mscgenjsreplacemecallback-#0000FF" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path d="m1 1 8 2-8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:#00f"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacemecallback-l-#0000FF" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path d="M17 1 9 3l8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:#00f"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacemecallback-#008800" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path d="m1 1 8 2-8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:#080"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacemecallback-l-#008800" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path d="M17 1 9 3l8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:#080"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacemecallback-#FF0000" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path d="m1 1 8 2-8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:red"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacemecallback-l-#FF0000" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path d="M17 1 9 3l8 2" class="arrow-style" style="stroke-dasharray:100,1;stroke:red"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacememethod-#0000FF" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path fill="#00F" stroke="#00F" d="m1 1 8 2-8 2z" class="arrow-style"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacememethod-l-#0000FF" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path fill="#00F" stroke="#00F" d="M17 1 9 3l8 2z" class="arrow-style"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacememethod-#FF0000" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path fill="red" stroke="red" d="m1 1 8 2-8 2z" class="arrow-style"/>
|
||||
</marker>
|
||||
<marker id="mscgenjsreplacememethod-l-#FF0000" class="arrow-marker" markerHeight="10" markerUnits="strokeWidth" markerWidth="10" orient="auto" refX="9" refY="3" viewBox="0 0 10 10">
|
||||
<path fill="red" stroke="red" d="M17 1 9 3l8 2z" class="arrow-style"/>
|
||||
</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 id="mscgenjsreplaceme_background" d="M-51-3h1272v2101.25H-51z" class="bglayer" style="fill:#fff;stroke:#fff;stroke-width:0"/>
|
||||
<g id="mscgenjsreplaceme_arcspans">
|
||||
<path d="M-41 907.1h1044v1169.15H-41z" class="box inline_expression alt"/>
|
||||
<path d="M-37 1448.15H999v590.1H-37z" class="box inline_expression alt"/>
|
||||
</g>
|
||||
<g id="mscgenjsreplaceme_lifelines">
|
||||
<path d="M65 38v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 38v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 38v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 38v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 38v38M1105 38v38M65 76v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 76v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 76v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 76v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 76v38M1105 76v38M65 114v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 114v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 114v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 114v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 114v38M1105 114v38M65 152v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 152v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 152v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 152v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 152v38M1105 152v38M65 190v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 190v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 190v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 190v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 190v38M1105 190v38M65 228v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 228v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 228v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 228v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 228v38M1105 228v38M65 266v54" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 266v54" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 266v54" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 266v54" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 266v54M1105 266v54M65 320v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 320v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 320v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 320v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 320v38M1105 320v38M65 358v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 358v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 358v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 358v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 358v38M1105 358v38M65 396v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 396v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 396v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 396v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 396v38M1105 396v38M65 434v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 434v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 434v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 434v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 434v38M1105 434v38M65 472v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 472v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 472v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 472v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 472v38M1105 472v38M65 510v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 510v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 510v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 510v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 510v38M1105 510v38M65 548v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 548v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 548v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 548v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 548v38M1105 548v38M65 586v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 586v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 586v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 586v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 586v38M1105 586v38M65 624v75.05" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 624v75.05" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 624v75.05" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 624v75.05" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 624v75.05M1105 624v75.05M65 699.05v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 699.05v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 699.05v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 699.05v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 699.05v38M1105 699.05v38M65 737.05v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 737.05v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 737.05v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 737.05v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 737.05v38M1105 737.05v38M65 775.05v75.05" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 775.05v75.05" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 775.05v75.05" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 775.05v75.05" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 775.05v75.05M1105 775.05v75.05M65 850.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 850.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 850.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 850.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 850.1v38M1105 850.1v38M65 888.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 888.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 888.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 888.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 888.1v38M1105 888.1v38M65 926.1v86" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 926.1v86" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 926.1v86" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 926.1v86" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 926.1v86M1105 926.1v86M65 1012.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1012.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1012.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1012.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1012.1v38M1105 1012.1v38M65 1050.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1050.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1050.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1050.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1050.1v38M1105 1050.1v38M65 1088.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1088.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1088.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1088.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1088.1v38M1105 1088.1v38M65 1126.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1126.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1126.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1126.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1126.1v38M1105 1126.1v38M65 1164.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1164.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1164.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1164.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1164.1v38M1105 1164.1v38M65 1202.1v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1202.1v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1202.1v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1202.1v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1202.1v38M1105 1202.1v38M65 1240.1v75.05" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1240.1v75.05" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1240.1v75.05" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1240.1v75.05" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1240.1v75.05M1105 1240.1v75.05M65 1315.15v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1315.15v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1315.15v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1315.15v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1315.15v38M1105 1315.15v38M65 1353.15v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1353.15v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1353.15v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1353.15v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1353.15v38M1105 1353.15v38M65 1391.15v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1391.15v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1391.15v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1391.15v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1391.15v38M1105 1391.15v38M65 1429.15v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1429.15v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1429.15v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1429.15v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1429.15v38M1105 1429.15v38M65 1467.15v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1467.15v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1467.15v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1467.15v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1467.15v38M1105 1467.15v38M65 1505.15v59.05" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1505.15v59.05" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1505.15v59.05" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1505.15v59.05" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1505.15v59.05M1105 1505.15v59.05M65 1564.2v54" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1564.2v54" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1564.2v54" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1564.2v54" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1564.2v54M1105 1564.2v54M65 1618.2v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1618.2v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1618.2v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1618.2v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1618.2v38M1105 1618.2v38M65 1656.2v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1656.2v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1656.2v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1656.2v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1656.2v38M1105 1656.2v38M65 1694.2v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1694.2v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1694.2v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1694.2v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1694.2v38M1105 1694.2v38M65 1732.2v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1732.2v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1732.2v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1732.2v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1732.2v38M1105 1732.2v38M65 1770.2v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1770.2v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1770.2v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1770.2v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1770.2v38M1105 1770.2v38M65 1808.2v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1808.2v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1808.2v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1808.2v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1808.2v38M1105 1808.2v38M65 1846.2v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1846.2v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1846.2v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1846.2v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1846.2v38M1105 1846.2v38M65 1884.2v59.05" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1884.2v59.05" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1884.2v59.05" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1884.2v59.05" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1884.2v59.05M1105 1884.2v59.05M65 1943.25v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1943.25v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1943.25v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1943.25v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1943.25v38M1105 1943.25v38M65 1981.25v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 1981.25v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 1981.25v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 1981.25v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 1981.25v38M1105 1981.25v38M65 2019.25v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 2019.25v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 2019.25v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 2019.25v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 2019.25v38M1105 2019.25v38M65 2057.25v38" class="arcrow" style="stroke:transparent"/>
|
||||
<path d="M273 2057.25v38" class="arcrow" style="stroke:#080"/>
|
||||
<path d="M481 2057.25v38" class="arcrow" style="stroke:red"/>
|
||||
<path d="M689 2057.25v38" class="arcrow" style="stroke:#00f"/>
|
||||
<path d="M897 2057.25v38M1105 2057.25v38" class="arcrow" style="stroke:transparent"/>
|
||||
</g>
|
||||
<g id="mscgenjsreplaceme_sequence">
|
||||
<path d="M0 0h130v38H0z" class="entity" style="stroke:transparent"/>
|
||||
<text x="65" y="22.75" class="entity-text"><tspan> </tspan></text>
|
||||
<path d="M208 0h130v38H208z" class="entity" style="fill:#cfc;stroke:#080"/>
|
||||
<text x="273" y="22.75" class="entity-text"><tspan>Network</tspan></text>
|
||||
<path d="M416 0h130v38H416z" class="entity" style="fill:#fcc;stroke:red"/>
|
||||
<text x="481" y="22.75" class="entity-text"><tspan>Offerer</tspan></text>
|
||||
<path d="M624 0h130v38H624z" class="entity" style="fill:#ccf;stroke:#00f"/>
|
||||
<text x="689" y="22.75" class="entity-text"><tspan>Bidder</tspan></text>
|
||||
<path d="M832 0h130v38H832z" class="entity" style="stroke:transparent"/>
|
||||
<text x="897" y="22.75" class="entity-text"><tspan> </tspan></text>
|
||||
<path d="M1040 0h130v38h-130z" class="entity" style="stroke:transparent"/>
|
||||
<text x="1105" y="22.75" class="entity-text"><tspan> </tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 95H273" class="arc directional callback" style="stroke:red"/>
|
||||
<path d="M345.09 79.25h63.83v14h-63.83z" class="label-text-background"/>
|
||||
<text x="377" y="90.25" class="directional-text callback-text"><tspan>Sends Offer</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#008800)" d="M273 133h416" class="arc directional return" style="stroke:#080"/>
|
||||
<path d="M445.76 117.25h70.48v14h-70.48z" class="label-text-background"/>
|
||||
<text x="481" y="128.25" class="directional-text return-text"><tspan>Detects Offer</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 171H481" class="arc directional callback" style="stroke:#00f"/>
|
||||
<path d="M516.62 155.25H653.7v14H516.62z" class="label-text-background"/>
|
||||
<text x="585" y="166.25" class="directional-text callback-text"><tspan>Sends BidIntent message</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 293h208" class="arc directional callback" style="stroke:red"/>
|
||||
<path d="M498.28 277.25h173.76v14H498.28z" class="label-text-background"/>
|
||||
<text x="585" y="288.25" class="directional-text callback-text"><tspan>Sends BidIntentAccept message</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 339H481" class="arc directional callback" style="stroke:#00f"/>
|
||||
<path d="M513.29 323.25h143.74v14H513.29z" class="label-text-background"/>
|
||||
<text x="585" y="334.25" class="directional-text callback-text"><tspan>Sends BidAccept message</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 415h208" class="arc directional callback" style="stroke:red"/>
|
||||
<path d="M491.29 399.25h187.74v14H491.29z" class="label-text-background"/>
|
||||
<text x="585" y="410.25" class="directional-text callback-text"><tspan>Sends XmrBidLockTxSigsMessage</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 491H481" class="arc directional callback" style="stroke:#00f"/>
|
||||
<path d="M485.61 475.25H684.7v14H485.61z" class="label-text-background"/>
|
||||
<text x="585" y="486.25" class="directional-text callback-text"><tspan>Sends XmrBidLockSpendTxMessage</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 529H273" class="arc directional callback" style="stroke:#00f"/>
|
||||
<path d="M415.64 513.25h130.72v14H415.64z" class="label-text-background"/>
|
||||
<text x="481" y="524.25" class="directional-text callback-text"><tspan>Sends script-coin-lock-tx</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacememethod-#FF0000)" d="M481 653.92c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:red"/>
|
||||
<path d="M484 605.67h40.91v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="616.67" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path d="M484 621.67h107.01v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="632.67" class="directional-text method-text anchor-start"><tspan>script-coin-lock-tx to</tspan></text>
|
||||
<path d="M484 637.67h39.34v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="648.67" class="directional-text method-text anchor-start"><tspan>confirm</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 756.05H273" class="arc directional callback" style="stroke:red"/>
|
||||
<path d="M304.97 740.3h144.06v14H304.97z" class="label-text-background"/>
|
||||
<text x="377" y="751.3" class="directional-text callback-text"><tspan>Sends noscript-coin-lock-tx</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacememethod-#FF0000)" d="M481 804.97c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:red"/>
|
||||
<path d="M484 756.72h40.91v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="767.72" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path d="M484 772.72h120.36v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="783.72" class="directional-text method-text anchor-start"><tspan>noscript-coin-lock-tx to</tspan></text>
|
||||
<path d="M484 788.72h39.34v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="799.72" class="directional-text method-text anchor-start"><tspan>confirm</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacememethod-#0000FF)" d="M689 804.97c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:#00f"/>
|
||||
<path d="M692 756.72h40.91v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="767.72" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path d="M692 772.72h120.36v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="783.72" class="directional-text method-text anchor-start"><tspan>noscript-coin-lock-tx to</tspan></text>
|
||||
<path d="M692 788.72h39.34v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="799.72" class="directional-text method-text anchor-start"><tspan>confirm</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacememethod-#0000FF)" d="M689 969.1H481" class="arc directional method" style="stroke:#00f"/>
|
||||
<path d="M519.64 953.35h130.72v14H519.64z" class="label-text-background"/>
|
||||
<text x="585" y="964.35" class="directional-text method-text"><tspan>Sends script-coin-lock-tx</tspan></text>
|
||||
<path d="M539.3 971.35h91.71v14H539.3z" class="label-text-background"/>
|
||||
<text x="585" y="982.35" class="directional-text method-text"><tspan>release message</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 1069.1H273" class="arc directional callback" style="stroke:red"/>
|
||||
<path d="M293.3 1053.35h167.41v14H293.3z" class="label-text-background"/>
|
||||
<text x="377" y="1064.35" class="directional-text callback-text"><tspan>Sends script-coin-lock-spend-tx</tspan></text>
|
||||
<path d="M-41 1183.1h1044" class="inline_expression_divider"/>
|
||||
<path d="M459.98 1175.85h42.03v14h-42.03z" class="label-text-background"/>
|
||||
<text x="481" y="1186.85" class="empty-text comment-row-text"><tspan>fail path</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacememethod-#FF0000)" d="M481 1270.02c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:red"/>
|
||||
<path d="M484 1221.77h40.91v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="1232.77" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path d="M484 1237.77h131.69v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="1248.77" class="directional-text method-text anchor-start"><tspan>script-coin-lock-tx lock to</tspan></text>
|
||||
<path d="M484 1253.77h33.01v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="1264.77" class="directional-text method-text anchor-start"><tspan>expire</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 1334.15H273" class="arc directional callback" style="stroke:#00f"/>
|
||||
<path d="M385.96 1318.4h190.08v14H385.96z" class="label-text-background"/>
|
||||
<text x="481" y="1329.4" class="directional-text callback-text"><tspan>Sends script-coin-lock-pre-refund-tx</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#008800)" d="M273 1372.15h416" class="arc directional return" style="stroke:#080"/>
|
||||
<path d="M404.64 1356.4h152.72v14H404.64z" class="label-text-background"/>
|
||||
<text x="481" y="1367.4" class="directional-text return-text"><tspan>script-coin-lock-pre-refund-tx</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacememethod-#0000FF)" d="M689 1527.07c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:#00f"/>
|
||||
<path d="M692 1494.82h40.91v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="1505.82" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path d="M692 1510.82h124.06v14H692z" class="label-text-background"/>
|
||||
<text x="692" y="1521.82" class="directional-text method-text anchor-start"><tspan>pre-refund tx to confirm</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#0000FF)" d="M689 1591.2H273" class="arc directional callback" style="stroke:#00f"/>
|
||||
<path d="M367.62 1575.45h226.77v14H367.62z" class="label-text-background"/>
|
||||
<text x="481" y="1586.45" class="directional-text callback-text"><tspan>Sends script-coin-lock-pre-refund-spend-tx</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#008800)" d="M273 1675.2h208" class="arc directional return" style="stroke:#080"/>
|
||||
<path d="M356.66 1659.45h40.69v14h-40.69z" class="label-text-background"/>
|
||||
<text x="377" y="1670.45" class="directional-text return-text"><tspan>Detects</tspan></text>
|
||||
<path d="M282.3 1677.45h189.41v14H282.3z" class="label-text-background"/>
|
||||
<text x="377" y="1688.45" class="directional-text return-text"><tspan>script-coin-lock-pre-refund-spend-tx</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 1713.2H273" class="arc directional callback" style="stroke:red"/>
|
||||
<path d="M359.98 1697.45h34.03v14h-34.03z" class="label-text-background"/>
|
||||
<text x="377" y="1708.45" class="directional-text callback-text"><tspan>Sends</tspan></text>
|
||||
<path d="M297.65 1715.45h158.7v14h-158.7z" class="label-text-background"/>
|
||||
<text x="377" y="1726.45" class="directional-text callback-text"><tspan>scriptless-coin-lock-recover-tx</tspan></text>
|
||||
<path d="M-37 1827.2H999" class="inline_expression_divider"/>
|
||||
<path d="M396.41 1819.95h169.17v14H396.41z" class="label-text-background"/>
|
||||
<text x="481" y="1830.95" class="empty-text comment-row-text"><tspan>offerer swipes script coin lock tx</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacememethod-#FF0000)" d="M481 1906.12c104 .1 104 22.8 0 22.8" class="arc directional method" style="stroke:red"/>
|
||||
<path d="M484 1873.87h40.91v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="1884.87" class="directional-text method-text anchor-start"><tspan>Wait for</tspan></text>
|
||||
<path d="M484 1889.87h142.39v14H484z" class="label-text-background"/>
|
||||
<text x="484" y="1900.87" class="directional-text method-text anchor-start"><tspan>pre-refund tx lock to expire</tspan></text>
|
||||
<path marker-end="url(#mscgenjsreplacemecallback-#FF0000)" d="M481 1962.25H273" class="arc directional callback" style="stroke:red"/>
|
||||
<path d="M359.98 1946.5h34.03v14h-34.03z" class="label-text-background"/>
|
||||
<text x="377" y="1957.5" class="directional-text callback-text"><tspan>Sends</tspan></text>
|
||||
<path d="M283.3 1964.5h187.39v14H283.3z" class="label-text-background"/>
|
||||
<text x="377" y="1975.5" class="directional-text callback-text"><tspan>script-coin-lock-pre-refund-swipe-tx</tspan></text>
|
||||
</g>
|
||||
<g id="mscgenjsreplaceme_notes">
|
||||
<path d="m383 209 3-17h190l3 17-3 17H386z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="212.75" class="box-text abox-text"><tspan>Bid Received</tspan></text>
|
||||
<path d="M383 230h196v34H383z" class="box" style="stroke:red"/>
|
||||
<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="M799 322h395v9h9m-9-9 9 9v25H799v-34z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="334.75" class="box-text note-text"><tspan>The BidAccept message contains the pubkeys the bidder will use and a</tspan></text>
|
||||
<text x="1001" y="350.75" class="box-text note-text"><tspan>DLEAG proof one key will work across both chains of the swapping coins</tspan></text>
|
||||
<path d="m383 377 3-17h190l3 17-3 17H386z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" 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 offerer's signatures for</tspan></text>
|
||||
<text x="1001" y="426.75" class="box-text note-text"><tspan>the script-coin-lock-refund and script-coin-lock-refund-spend txns.</tspan></text>
|
||||
<path d="m383 453 3-17h190l3 17-3 17H386z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="448.75" class="box-text abox-text"><tspan>Exchanged script lock tx sigs</tspan></text>
|
||||
<text x="481" 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>proof the bidder can sign it.</tspan></text>
|
||||
<path d="m383 567 3-17h190l3 17-3 17H386z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="570.75" class="box-text abox-text"><tspan>Bid Script coin spend tx valid</tspan></text>
|
||||
<path d="m383 605 3-17h190l3 17-3 17H386z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="600.75" class="box-text abox-text"><tspan>Exchanged script lock spend tx</tspan></text>
|
||||
<text x="481" y="616.75" class="box-text abox-text"><tspan>msg</tspan></text>
|
||||
<path d="m383 718.05 3-17h190l3 17-3 17H386z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="721.8" class="box-text abox-text"><tspan>Bid Script coin locked</tspan></text>
|
||||
<path d="m383 869.1 3-17h190l3 17-3 17H386z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="872.85" class="box-text abox-text"><tspan>Bid Scriptless coin locked</tspan></text>
|
||||
<path d="M-40 907.1h98.39v11l-7 7H-40" class="box inline_expression_label"/>
|
||||
<text x="-38" y="920.35" class="inline_expression-text alt-text anchor-start"><tspan>alt: success path</tspan></text>
|
||||
<path d="M799 928.1h395v9h9m-9-9 9 9v73H799v-82z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="940.85" class="box-text note-text"><tspan>The XmrBidLockReleaseMessage contains the bidder's OTVES for it. </tspan></text>
|
||||
<text x="1001" y="956.85" class="box-text note-text"><tspan> The offerer decodes the bidder's signature</tspan></text>
|
||||
<text x="1001" y="972.85" class="box-text note-text"><tspan>from the OTVES. When the bidder has the</tspan></text>
|
||||
<text x="1001" y="988.85" class="box-text note-text"><tspan>plaintext signature, they can decode the offerer's noscript-coin-lock-tx</tspan></text>
|
||||
<text x="1001" y="1004.85" class="box-text note-text"><tspan>signature.</tspan></text>
|
||||
<path d="m383 1031.1 3-17h190l3 17-3 17H386z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="1034.85" class="box-text abox-text"><tspan>Script coin lock released</tspan></text>
|
||||
<path d="m383 1107.1 3-17h190l3 17-3 17H386z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="1110.85" class="box-text abox-text"><tspan>Script tx redeemed</tspan></text>
|
||||
<path d="m383 1145.1 3-17h190l3 17-3 17H386z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="1148.85" class="box-text abox-text"><tspan>Bid Completed</tspan></text>
|
||||
<path d="M799 1317.15h395v9h9m-9-9 9 9v25H799v-34z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="1337.9" class="box-text note-text"><tspan>tx can be sent by either party.</tspan></text>
|
||||
<path d="m383 1410.15 3-17h190l3 17-3 17H386z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="1405.9" class="box-text abox-text"><tspan>Bid Script pre-refund tx in</tspan></text>
|
||||
<text x="481" y="1421.9" class="box-text abox-text"><tspan>chain</tspan></text>
|
||||
<path d="M-36 1448.15h199.77v11l-7 7H-36" class="box inline_expression_label"/>
|
||||
<text x="-34" y="1461.4" class="inline_expression-text alt-text anchor-start"><tspan>alt: bidder refunds script coin lock tx</tspan></text>
|
||||
<path d="M799 1566.2h395v9h9m-9-9 9 9v41H799v-50z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="1578.95" class="box-text note-text"><tspan>Refunds the script lock tx, with the bidder's cleartext signature</tspan></text>
|
||||
<text x="1001" y="1594.95" class="box-text note-text"><tspan>the offerer can refund the noscript lock tx. </tspan></text>
|
||||
<text x="1001" y="1610.95" class="box-text note-text"><tspan>Once the lock expires the pre-refund tx can be spent by the offerer.</tspan></text>
|
||||
<path d="m591 1637.2 3-17h190l3 17-3 17H594z" class="box abox" style="stroke:#00f"/>
|
||||
<text x="689" y="1640.95" class="box-text abox-text"><tspan>Bid Failed, refunded</tspan></text>
|
||||
<path d="M799 1658.2h395v9h9m-9-9 9 9v25H799v-34z" class="box note" style="fill:#ffc"/>
|
||||
<text x="1001" y="1678.95" class="box-text note-text"><tspan>offerer recovers the bidder's scriptless chain key-shard.</tspan></text>
|
||||
<path d="m383 1751.2 3-17h190l3 17-3 17H386z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="1754.95" class="box-text abox-text"><tspan>Bid Scriptless tx recovered</tspan></text>
|
||||
<path d="m383 1789.2 3-17h190l3 17-3 17H386z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="1792.95" class="box-text abox-text"><tspan>Bid Failed, refunded</tspan></text>
|
||||
<path d="m383 2000.25 3-17h190l3 17-3 17H386z" class="box abox" style="stroke:red"/>
|
||||
<text x="481" y="2004" class="box-text abox-text"><tspan>Bid Failed, swiped</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 40 KiB |
@@ -1,414 +0,0 @@
|
||||
<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>
|
||||
|
Before Width: | Height: | Size: 39 KiB |
@@ -1,388 +0,0 @@
|
||||
<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>
|
||||
|
Before Width: | Height: | Size: 36 KiB |
@@ -1,12 +1,11 @@
|
||||
{% include 'header.html' %}
|
||||
<div class="container mx-auto">
|
||||
|
||||
<section class="bg-white p-5 mt-5">
|
||||
<section class="p-5 mt-5">
|
||||
<div class="flex flex-wrap items-center -m-2">
|
||||
<div class="w-full md:w-1/2 p-2">
|
||||
<ul class="flex flex-wrap items-center gap-x-3 mb-2">
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 hover:text-coolGray-700" href="/">
|
||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/">
|
||||
<p>Home</p>
|
||||
</a>
|
||||
</li>
|
||||
@@ -15,7 +14,9 @@
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li><a class="flex font-medium text-xs text-coolGray-500 hover:text-coolGray-700" href="/404">404</a></li>
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/404">404</a>
|
||||
</li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
@@ -25,33 +26,27 @@
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% include 'inc_messages.html' %}
|
||||
|
||||
<section class="relative py-24 md:py-40 bg-white">
|
||||
<div class="relative z-10 container px-4 mx-auto">
|
||||
<div class="flex flex-wrap items-center -mx-4">
|
||||
<div class="w-full md:w-1/2 px-4 mb-16 md:mb-0">
|
||||
<div class="md:max-w-xl md:mx-auto text-center md:text-left">
|
||||
<span class="inline-block mb-6 px-3 py-1 font-semibold bg-blue-100 rounded-full text-sm">Error 404</span>
|
||||
<h2 class="mb-4 text-4xl md:text-5xl leading-tight font-bold tracking-tighter">Oh no! Error 404</h2>
|
||||
<p class="mb-6 text-lg md:text-xl text-coolGray-500">Something went wrong, so this page is broken.</p>
|
||||
<div class="flex flex-wrap">
|
||||
<div class="w-full lg:w-auto py-1 lg:py-0 lg:mr-6"><a class="inline-block py-5 px-7 w-full text-base md:text-lg leading-4 text-blue-50 font-medium text-center bg-blue-500 hover:bg-blue-600 border border-blue-500 rounded-md shadow-sm focus:ring-0 focus:outline-none" href="/">Go back to Homepage</a></div>
|
||||
<div class="w-full lg:w-auto py-1 lg:py-0"><a class="inline-block py-5 px-7 w-full text-base md:text-lg leading-4 text-coolGray-800 font-medium text-center bg-white hover:bg-coolGray-100 border border-coolGray-200 rounded-md shadow-sm focus:ring-0 focus:outline-none" href="/refresh">Try Again</a></div> <!-- todo add last URL page visit -->
|
||||
<section class="relative py-24 md:py-40">
|
||||
<div class="relative z-10 container px-4 mx-auto">
|
||||
<div class="flex flex-wrap items-center -mx-4">
|
||||
<div class="w-full md:w-1/2 px-4 mb-16 md:mb-0">
|
||||
<div class="md:max-w-xl md:mx-auto text-center md:text-left"> <span class="inline-block py-1 px-3 mb-4 text-xs leading-5 bg-blue-500 text-white font-medium rounded-full shadow-sm">Error 404</span>
|
||||
<h2 class="mb-4 text-4xl md:text-5xl leading-tight font-bold tracking-tighter dark:text-white">Oh no! Error 404</h2>
|
||||
<p class="mb-6 text-lg md:text-xl text-coolGray-500 dark:text-gray-300 ">Something went wrong, so this page is broken.</p>
|
||||
<div class="flex flex-wrap">
|
||||
<div class="w-full lg:w-auto py-1 lg:py-0 lg:mr-6"><a class="inline-block py-5 px-7 w-full text-base md:text-lg leading-4 text-blue-50 font-medium text-center bg-blue-500 hover:bg-blue-600 border border-blue-500 rounded-md shadow-sm focus:ring-0 focus:outline-none" href="/">Go Back</a></div>
|
||||
<div class="w-full lg:w-auto py-1 lg:py-0"><a class="inline-block py-5 px-7 w-full text-base md:text-lg leading-4 text-coolGray-800 font-medium text-center bg-white hover:bg-coolGray-100 border border-coolGray-200 rounded-md shadow-sm focus:ring-0 focus:outline-none" href="/refresh">Try Again</a></div>
|
||||
<!-- todo add last URL page visit -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-1/3 px-4">
|
||||
<img class="mx-auto" src="/static/images/other/what-why.gif" alt="">
|
||||
<div class="w-full md:w-1/3 px-4"><img class="mx-auto" src="/static/images/other/what-why.gif" alt=""></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
{% include 'footer.html' %}
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
@@ -1,97 +1,132 @@
|
||||
{% include 'header.html' %}
|
||||
<div class="container mx-auto">
|
||||
|
||||
<section class="bg-white p-5 mt-5">
|
||||
<div class="flex flex-wrap items-center -m-2">
|
||||
<div class="w-full md:w-1/2 p-2">
|
||||
<ul class="flex flex-wrap items-center gap-x-3 mb-2">
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 hover:text-coolGray-700" href="/">
|
||||
<p>Home</p>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li><a class="flex font-medium text-xs text-coolGray-500 hover:text-coolGray-700" href="/active">Swaps In Progress</a></li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
</ul>
|
||||
<section class="p-5 mt-5">
|
||||
<div class="flex flex-wrap items-center -m-2">
|
||||
<div class="w-full md:w-1/2 p-2">
|
||||
<ul class="flex flex-wrap items-center gap-x-3 mb-2">
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/">
|
||||
<p>Home</p>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/active">Swaps In Progress</a>
|
||||
</li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="py-4">
|
||||
<div class="container px-4 mx-auto">
|
||||
<div class="relative py-11 px-16 bg-coolGray-900 dark:bg-blue-500 rounded-md overflow-hidden">
|
||||
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="">
|
||||
<div class="relative z-20 flex flex-wrap items-center -m-3">
|
||||
<div class="w-full md:w-1/2 p-3">
|
||||
<h2 class="mb-6 text-4xl font-bold text-white tracking-tighter">Swaps in Progress</h2>
|
||||
<p class="font-normal text-coolGray-200 dark:text-white">Your swaps that are currently in progress.</p>
|
||||
</div>
|
||||
<div class="w-full md:w-1/2 p-3 p-6 container flex flex-wrap items-center justify-end items-center mx-auto">
|
||||
{% if refresh %}
|
||||
<a id="refresh" href="/active" class="flex flex-wrap justify-center px-5 py-4 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white borderdark:text-white dark:hover:text-white dark:bg-gray-600 dark:hover:bg-gray-700 dark:border-gray-600 dark:hover:border-gray-600 border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<svg class="text-gray-500 w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24">
|
||||
<g fill="#ffffff">
|
||||
<path fill="#ffffff" d="M12,3c1.989,0,3.873,0.65,5.43,1.833l-3.604,3.393l9.167,0.983L22.562,0l-3.655,3.442 C16.957,1.862,14.545,1,12,1C5.935,1,1,5.935,1,12h2C3,7.037,7.037,3,12,3z"></path>
|
||||
<path data-color="color-2" d="M12,21c-1.989,0-3.873-0.65-5.43-1.833l3.604-3.393l-9.167-0.983L1.438,24l3.655-3.442 C7.043,22.138,9.455,23,12,23c6.065,0,11-4.935,11-11h-2C21,16.963,16.963,21,12,21z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
<span>Refresh 30 seconds</span>
|
||||
</a>
|
||||
{% else %}
|
||||
<a id="refresh" href="/active" class="flex flex-wrap justify-center px-5 py-4 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white borderdark:text-white dark:hover:text-white dark:bg-gray-600 dark:hover:bg-gray-700 dark:border-gray-600 dark:hover:border-gray-600 border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<svg class="text-gray-500 w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24">
|
||||
<g fill="#ffffff">
|
||||
<path fill="#ffffff" d="M12,3c1.989,0,3.873,0.65,5.43,1.833l-3.604,3.393l9.167,0.983L22.562,0l-3.655,3.442 C16.957,1.862,14.545,1,12,1C5.935,1,1,5.935,1,12h2C3,7.037,7.037,3,12,3z"></path>
|
||||
<path data-color="color-2" d="M12,21c-1.989,0-3.873-0.65-5.43-1.833l3.604-3.393l-9.167-0.983L1.438,24l3.655-3.442 C7.043,22.138,9.455,23,12,23c6.065,0,11-4.935,11-11h-2C21,16.963,16.963,21,12,21z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
<span>Refresh</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="py-4">
|
||||
<div class="container px-4 mx-auto">
|
||||
<div class="relative py-11 px-16 bg-coolGray-900 rounded-md overflow-hidden">
|
||||
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="">
|
||||
<div class="relative z-20 flex flex-wrap items-center">
|
||||
<div class="w-full md:w-1/2 p-3">
|
||||
<h2 class="mb-6 text-4xl font-bold text-white tracking-tighter">Swaps in Progress / Active Swaps</h2>
|
||||
<p class="font-semibold text-coolGray-200">
|
||||
{% if refresh %}
|
||||
Page Refresh {{ refresh }} seconds
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
<div class="w-full md:w-1/2 p-3">
|
||||
<a id="refresh" href="/active"><button class="block md:ml-auto px-5 py-3 font-medium text-lcg text-white bg-blue-500 hover:bg-blue-600 rounded-md focus:ring-0 focus:outline-none">Refresh</button></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="bg-white">
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 mt-5 h-full overflow-hidden bg-white">
|
||||
<div class="pb-6 border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
|
||||
<div class="container px-0 mx-auto mt-5">
|
||||
<div class="overflow-x-auto relative border sm:rounded-lg">
|
||||
<table class="w-full text-sm text-left text-gray-500 outline-none border-gray-300">
|
||||
|
||||
<thead class="text-xs text-gray-700 border-b uppercase bg-gray-50 outline-none border-gray-300">
|
||||
<tr>
|
||||
<th scope="col" class="py-3 px-6"> Bid ID </th>
|
||||
<th scope="col" class="py-3 px-6"> Offer ID </th>
|
||||
<th scope="col" class="py-3 px-6"> Bid Status </th>
|
||||
<th scope="col" class="py-4 px-7"> ITX Status </th>
|
||||
<th scope="col" class="py-3 px-7"> PTX Status </th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for s in active_swaps %}
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 text-xs monospace"><a href=/bid/{{ s[0] }}>{{ s[0] }}</a></td>
|
||||
<td class="py-4 px-6 text-xs monospace"><a href=/offer/{{ s[1] }}>{{ s[1] }}</a></td>
|
||||
<td class="py-4 px-6">{{ s[2] }}</td>
|
||||
<td class="py-4 px-6">{{ s[3] }}</td>
|
||||
<td class="py-4 px-6">{{ s[4] }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden">
|
||||
<div class="pb-6 border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="container mt-5 mx-auto">
|
||||
<div class="pt-6 pb-8 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="px-6">
|
||||
<div class="w-full mt-6 pb-6 overflow-x-auto">
|
||||
<table class="w-full min-w-max text-sm">
|
||||
<thead class="uppercase">
|
||||
<tr class="text-left">
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Bid ID</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Offer ID </span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Bid Status </span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">ITX Status </span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tr-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">PTX Status </span>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for s in active_swaps %}
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="py-3 px-6 monospace">
|
||||
<a href=/bid/{{ s[0] }}>{{ s[0]|truncate(50,true,'...',0) }}</a>
|
||||
</td>
|
||||
<td class="py-3 px-6 monospace">
|
||||
<a href=/offer/{{ s[1] }}>{{ s[1]|truncate(50,true,'...',0) }}</a>
|
||||
</td>
|
||||
<td class="py-3 px-6 w-52 whitespace-normal break-words">{{ s[2] }}</td>
|
||||
<td class="py-3 px-6">{{ s[3] }}</td>
|
||||
<td class="py-3 px-6">{{ s[4] }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{% include 'footer.html' %}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -1,173 +1,242 @@
|
||||
{% include 'header.html' %}
|
||||
<div class="container mx-auto">
|
||||
<section class="bg-white p-5 mt-5">
|
||||
<div class="flex flex-wrap items-center -m-2">
|
||||
<div class="w-full md:w-1/2 p-2">
|
||||
<ul class="flex flex-wrap items-center gap-x-3 mb-2">
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 hover:text-coolGray-700" href="/">
|
||||
<p>Home</p>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li><a class="flex font-medium text-xs text-coolGray-500 hover:text-coolGray-700" href="/automation">Automation Strategies</a></li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<section class="p-5 mt-5">
|
||||
<div class="flex flex-wrap items-center -m-2">
|
||||
<div class="w-full md:w-1/2 p-2">
|
||||
<ul class="flex flex-wrap items-center gap-x-3 mb-2">
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/">
|
||||
<p>Home</p>
|
||||
</a>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/automation">Automation Strategies</a>
|
||||
</li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
</ul>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="py-4">
|
||||
<div class="container px-4 mx-auto">
|
||||
<div class="relative py-11 px-16 bg-coolGray-900 dark:bg-blue-500 rounded-md overflow-hidden">
|
||||
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="">
|
||||
<div class="relative z-20 flex flex-wrap items-center -m-3">
|
||||
<div class="w-full md:w-1/2 p-3">
|
||||
<h2 class="mb-6 text-4xl font-bold text-white tracking-tighter">Automation Strategies</h2>
|
||||
<p class="font-normal text-coolGray-200 dark:text-white"></p>
|
||||
</div>
|
||||
<div class="w-full md:w-1/2 p-3 p-6 container flex flex-wrap items-center justify-end items-center mx-auto">
|
||||
<a href="/newautomationstrategy" class="flex flex-wrap justify-center px-5 py-4 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white borderdark:text-white dark:hover:text-white dark:bg-gray-600 dark:hover:bg-gray-700 dark:border-gray-600 dark:hover:border-gray-600 border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<svg class="text-gray-500 w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#ffffff" stroke-linejoin="round">
|
||||
<line data-cap="butt" x1="5" y1="1" x2="5" y2="6" stroke="#ffffff"></line>
|
||||
<line x1="3" y1="1" x2="7" y2="1" stroke="#fffffwww"></line>
|
||||
<line data-cap="butt" x1="19" y1="1" x2="19" y2="6" stroke="#ffffff"></line>
|
||||
<line x1="17" y1="1" x2="21" y2="1" stroke="#ffffff"></line>
|
||||
<rect x="6" y="15" width="12" height="4" stroke="#ffffff"></rect>
|
||||
<line data-cap="butt" x1="10" y1="19" x2="10" y2="15" stroke="#ffffff"></line>
|
||||
<line data-cap="butt" x1="14" y1="19" x2="14" y2="15" stroke="#ffffff"></line>
|
||||
<line x1="6" y1="11" x2="8" y2="11" stroke="#ffffff"></line>
|
||||
<line x1="16" y1="11" x2="18" y2="11" stroke="#fff"></line>
|
||||
<polygon points="23 6 5 6 1 6 1 23 23 23 23 6"></polygon>
|
||||
</g>
|
||||
</svg>
|
||||
<span>Create New Strategy</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="py-4">
|
||||
<div class="container px-4 mx-auto">
|
||||
<div class="relative py-11 px-16 bg-coolGray-900 rounded-md overflow-hidden">
|
||||
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/elements/wave.svg" alt="">
|
||||
<div class="relative z-20 flex flex-wrap items-center -m-3">
|
||||
<div class="w-full md:w-1/2 p-3">
|
||||
<h2 class="mb-6 text-4xl font-bold text-white tracking-tighter">Automation Strategies</h2>
|
||||
<p class="font-semibold text-coolGray-200"></p>
|
||||
</div>
|
||||
<div class="w-full md:w-1/2 p-3">
|
||||
<a id="refresh" href="/newautomationstrategy"><button class="block md:ml-auto px-5 py-3 font-medium text-lcg text-white bg-blue-500 hover:bg-blue-600 rounded-md focus:ring-0 focus:outline-none">Create New Strategy</button></a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% include 'inc_messages.html' %}
|
||||
<section>
|
||||
<form method="post">
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden">
|
||||
<div class="border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="container mt-5 mx-auto">
|
||||
<div class="pt-6 pb-8 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="px-6">
|
||||
<div class="w-full mt-6 pb-6 overflow-x-auto">
|
||||
<table class="w-full min-w-max text-sm">
|
||||
<thead class="uppercase">
|
||||
<tr class="text-left">
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Filters</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold py-3 px-6"></span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tr-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold py-3 px-6"></span>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-3 px-6 bold">Sort by:</td>
|
||||
<td class="py-3 px-6">
|
||||
<select class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" name="sort_by">
|
||||
<option value="created_at" {% if filters.sort_by=='created_at' %} selected{% endif %}>Created At</option>
|
||||
</select>
|
||||
</td>
|
||||
<td class="py-3 px-6">
|
||||
<select class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" 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>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% include 'inc_messages.html' %}
|
||||
|
||||
<section class="bg-white">
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 mt-5 h-full overflow-hidden bg-white ">
|
||||
<div class="pb-6 border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
|
||||
<div class="container px-0 mx-auto mt-5">
|
||||
<div class="overflow-x-auto relative border sm:rounded-lg">
|
||||
|
||||
<form method="post">
|
||||
|
||||
<table class="w-full text-sm text-left text-gray-500 outline-none border-gray-300">
|
||||
|
||||
<thead class="text-xs text-gray-700 border-b uppercase bg-gray-50 outline-none border-gray-300">
|
||||
<tr>
|
||||
<th scope="col" class="py-3 px-6">Filter</th>
|
||||
<th scope="col" class="py-3 px-6"></th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold w-96">
|
||||
Sort by:
|
||||
</td>
|
||||
<td class="py-4 bold w-96">
|
||||
<select class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" name="sort_by">
|
||||
<option value="created_at"{% if filters.sort_by=='created_at' %} selected{% endif %}>Created At</option>
|
||||
</select>
|
||||
</td>
|
||||
<td class="py-4 px-6 pr-5">
|
||||
<select class="pr-15 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" 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>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pt-10 bg-white bg-opacity-60 rounded-b-md">
|
||||
<div class="w-full md:w-0/12">
|
||||
<div class="flex flex-wrap justify-end -m-1.5">
|
||||
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<button type="submit" name='pageback' value="Page Back" class="outline-none flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<svg aria-hidden="true" class="mr-2 w-5 h-5" fill="#556987" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M7.707 14.707a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 1.414L5.414 9H17a1 1 0 110 2H5.414l2.293 2.293a1 1 0 010 1.414z" clip-rule="evenodd"></path>
|
||||
</svg> <span>Page Back</span> </button>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center">
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<p class="text-sm font-heading">Page: {{ filters.page_no }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<button type="submit" name='pageforwards' value="Page Forwards" class="outline-none flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none"> <span>Page Forwards</span>
|
||||
<svg aria-hidden="true" class="ml-2 w-5 h-5" fill="#556987" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="w-full md:w-auto p-1.5 ml-2">
|
||||
<button name='clearfilters' value="Clear Filters" class="outline-none flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<span>Clear Filters</span> </button>
|
||||
</div>
|
||||
|
||||
<div class="w-full md:w-auto p-1.5 ml-2">
|
||||
<button value="Submit" type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<span>Submit</span> </button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||
<input type="hidden" name="pageno" value="{{ filters.page_no }}">
|
||||
|
||||
</form>
|
||||
|
||||
<div class="container px-0 mx-auto mt-10">
|
||||
<div class="overflow-x-auto relative border sm:rounded-lg">
|
||||
|
||||
<table class="w-full text-sm text-left text-gray-500 outline-none border-gray-300">
|
||||
|
||||
|
||||
<thead class="text-xs text-gray-700 border-b uppercase bg-gray-50 outline-none border-gray-300">
|
||||
<tr>
|
||||
<th scope="col" class="py-3 px-6">Name</th>
|
||||
<th scope="col" class="py-3">Type</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
{% for s in strategies %}
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold w-96">
|
||||
<a class="monospace bolder-t" href=/automationstrategy/{{ s[0] }}>{{ s[1] }}</a>
|
||||
</td>
|
||||
<td class="py-4 pr-5">{{ s[2] }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden ">
|
||||
<div class="pb-6 ">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="container mx-auto">
|
||||
<div class="pt-6 pb-6 bg-coolGray-100 border-t border-gray-100 dark:border-gray-400 dark:bg-gray-500 rounded-bl-xl rounded-br-xl">
|
||||
<div class="px-6">
|
||||
<div class="flex flex-wrap justify-end">
|
||||
<div class="w-full md:w-auto p-1.5 ml-2">
|
||||
<button name='clearfilters' value="Clear Filters" class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<svg class="mr-2 w-5 h-5" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#ffffff" stroke-linejoin="round">
|
||||
<line x1="20" y1="2" x2="12.329" y2="11.506"></line>
|
||||
<path d="M11,11a2,2,0,0,1,2,2,3.659,3.659,0,0,1-.2.891A9.958,9.958,0,0,0,13.258,23H1C1,16.373,4.373,11,11,11Z"></path>
|
||||
<line x1="18" y1="15" x2="23" y2="15" stroke="#ffffff"></line>
|
||||
<line x1="17" y1="19" x2="23" y2="19" stroke="#ffffff"></line>
|
||||
<line x1="19" y1="23" x2="23" y2="23" stroke="#ffffff"></line>
|
||||
<path d="M8.059,11.415A3.9,3.9,0,0,0,12,16c.041,0,.079-.011.12-.012" data-cap="butt"></path>
|
||||
<path d="M5,23a13.279,13.279,0,0,1,.208-3.4" data-cap="butt"></path>
|
||||
<path d="M9.042,23c-.688-1.083-.313-3.4-.313-3.4" data-cap="butt"></path>
|
||||
</g>
|
||||
</svg> Clear</button>
|
||||
</div>
|
||||
<div class="w-full md:w-auto p-1.5 ml-2">
|
||||
<button name="" value="Submit" type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<svg class="mr-2 w-5 h-5" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#ffffff" stroke-linejoin="round">
|
||||
<rect x="2" y="2" width="7" height="7"></rect>
|
||||
<rect x="15" y="15" width="7" height="7"></rect>
|
||||
<rect x="2" y="15" width="7" height="7"></rect>
|
||||
<polyline points="15 6 17 8 22 3" stroke="#ffffff"></polyline>
|
||||
</g>
|
||||
</svg>Apply Filters</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden">
|
||||
<div class="border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="container mt-5 mx-auto">
|
||||
<div class="pt-6 pb-8 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="px-6">
|
||||
<div class="w-full mt-6 pb-6 overflow-x-auto">
|
||||
<table class="w-full min-w-max text-sm">
|
||||
<thead class="uppercase">
|
||||
<tr class="text-left">
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Name</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Type</span>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for s in strategies %}
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="py-3 px-6 bold monospace">
|
||||
<a href=/automationstrategy/{{ s[0] }}>{{ s[1] }}</a>
|
||||
</td>
|
||||
<td class="py-3 px-6">{{ s[2] }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden ">
|
||||
<div class="pb-6 ">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="container mx-auto">
|
||||
<div class="pt-6 pb-6 bg-coolGray-100 border-t border-gray-100 dark:border-gray-400 dark:bg-gray-500 rounded-bl-xl rounded-br-xl">
|
||||
<div class="px-6">
|
||||
<div class="flex flex-wrap justify-end">
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<button type="submit" name='pageback' value="Page Back" class="inline-flex items-center h-9 py-1 px-4 text-xs text-blue-50 font-semibold bg-blue-500 hover:bg-blue-600 rounded-lg transition duration-200 focus:ring-0 focus:outline-none">
|
||||
<svg aria-hidden="true" class="mr-2 w-5 h-5" fill="#ffffff" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M7.707 14.707a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 1.414L5.414 9H17a1 1 0 110 2H5.414l2.293 2.293a1 1 0 010 1.414z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
<span>Page Back</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<p class="text-sm font-heading dark:text-white">Page: {{ filters.page_no }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<button type="submit" name='pageforwards' value="Page Forwards" class="inline-flex items-center h-9 py-1 px-4 text-xs text-blue-50 font-semibold bg-blue-500 hover:bg-blue-600 rounded-lg transition duration-200 focus:ring-0 focus:outline-none">
|
||||
<span>Page Forwards</span>
|
||||
<svg aria-hidden="true" class="ml-2 w-5 h-5" fill="#ffffff" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||
<input type="hidden" name="pageno" value="{{ filters.page_no }}">
|
||||
</form>
|
||||
</div>
|
||||
{% include 'footer.html' %}
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
@@ -1,122 +1,171 @@
|
||||
{% include 'header.html' %}
|
||||
<div class="container mx-auto">
|
||||
<section class="bg-white p-5 mt-5">
|
||||
<section class="p-5 mt-5">
|
||||
<div class="flex flex-wrap items-center -m-2">
|
||||
<div class="w-full md:w-1/2 p-2">
|
||||
<ul class="flex flex-wrap items-center gap-x-3 mb-2">
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 hover:text-coolGray-700" href="/">
|
||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/">
|
||||
<p>Home</p>
|
||||
</a>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/automation">Automation Strategies</a>
|
||||
</li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li><a class="flex font-medium text-xs text-coolGray-500 hover:text-coolGray-700" href="/automation">Automation Strategies</a></li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/automation">ID: <!-- todo ID here {{ strategy_id }} --></a>
|
||||
</li>
|
||||
<li><a class="flex font-medium text-xs text-coolGray-500 hover:text-coolGray-700" href="/automation">ID: {{ strategy_id }}</a></li>
|
||||
</ul>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="py-4">
|
||||
<div class="container px-4 mx-auto">
|
||||
<div class="relative py-11 px-16 bg-coolGray-900 rounded-md overflow-hidden">
|
||||
<div class="relative py-11 px-16 bg-coolGray-900 dark:bg-blue-500 rounded-md overflow-hidden">
|
||||
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="">
|
||||
<div class="relative z-20 flex flex-wrap items-center -m-3">
|
||||
<div class="w-full md:w-1/2 p-3">
|
||||
<h2 class="mb-6 text-4xl font-bold text-white tracking-tighter">Automation Strategy {{ strategy_id }}</h2>
|
||||
<p class="font-normal text-coolGray-200 dark:text-white">ID: <!-- todo ID here {{ strategy_id }} -->
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% include 'inc_messages.html' %}
|
||||
|
||||
<section class="bg-white">
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 mt-5 h-full overflow-hidden bg-white ">
|
||||
<div class="pb-6 border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2 text-center ">
|
||||
|
||||
<div class="container px-0 mx-auto mt-5">
|
||||
<div class="overflow-x-auto relative border sm:rounded-lg">
|
||||
|
||||
<table class="w-full text-sm text-left text-gray-500 outline-none border-gray-300">
|
||||
|
||||
<thead class="text-xs text-gray-700 border-b uppercase bg-gray-50 outline-none border-gray-300">
|
||||
<tr>
|
||||
<th scope="col" class="py-3 px-6">Option</th>
|
||||
<th scope="col" class="py-3">Type</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold w-96">Label</td>
|
||||
<td>{{ strategy.label }}</td>
|
||||
</tr>
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold w-96">Type</td>
|
||||
<td>{{ strategy.type }}</td>
|
||||
</tr>
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold w-96">Only known identities</td>
|
||||
<td>{{ strategy.only_known_identities }}</td>
|
||||
</tr>
|
||||
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold w-96">Data</td>
|
||||
<td class="py-4 pr-5">
|
||||
<textarea class="outline-none block p-2.5 text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500 w-full monospace" rows="5" readonly>
|
||||
{{ strategy.data }}
|
||||
</textarea>
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold w-96">Notes</td>
|
||||
<td class="py-4 pr-5">
|
||||
<textarea class="outline-none block p-2.5 text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500 w-full monospace" rows="5" readonly>
|
||||
{{ strategy.note }}
|
||||
</textarea>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-6 pt-10 bg-white bg-opacity-60 rounded-b-md">
|
||||
<div class="w-full md:w-0/12">
|
||||
<div class="flex flex-wrap justify-end -m-1.5">
|
||||
|
||||
<div class="w-full md:w-auto p-1.5 ml-2">
|
||||
<a href="/automation" type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<span>Back</span> </a>
|
||||
{% include 'inc_messages.html' %}
|
||||
<section>
|
||||
<form method="post" autocomplete="off">
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden min-height-50">
|
||||
<div class="border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="container mt-5 mx-auto">
|
||||
<div class="pt-6 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="px-6">
|
||||
<div class="w-full mt-6 pb-6 overflow-x-auto">
|
||||
<table class="w-full min-w-max text-sm">
|
||||
<thead class="uppercase">
|
||||
<tr class="text-left">
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Options</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Type</span>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-3 px-6 bold">Label</td>
|
||||
<td class="py-3 px-6">{{ strategy.label }}</td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-3 px-6 bold">Type</td>
|
||||
<td class="py-3 px-6">{{ strategy.type }}</td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-3 px-6 bold">Only known identities</td>
|
||||
<td class="py-3 px-6">{{ strategy.only_known_identities }}</td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-3 px-6 bold">Data</td>
|
||||
<td class="py-3 px-6">
|
||||
<textarea name="data" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0 monospace" rows="20" {% if not show_edit_form %}readonly{% endif %}>{{ strategy.data }}</textarea>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-3 px-6 bold">Notes</td>
|
||||
<td class="py-3 px-6">
|
||||
<textarea name="note" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0 monospace" rows="20" {% if not show_edit_form %}readonly{% endif %}>{{ strategy.note }}</textarea>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden ">
|
||||
<div class="pb-6 ">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="container mx-auto">
|
||||
<div class="pt-6 pb-6 bg-coolGray-100 border-t border-gray-100 dark:border-gray-400 dark:bg-gray-500 rounded-bl-xl rounded-br-xl">
|
||||
<div class="px-6">
|
||||
<div class="flex flex-wrap justify-end">
|
||||
{% if show_edit_form %}
|
||||
<div class="w-full md:w-auto p-1.5 ml-2">
|
||||
<button name="apply" value="Apply" type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<svg class="text-gray-500 w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#ffffff" stroke-linejoin="round">
|
||||
<polyline points=" 6,12 10,16 18,8 " stroke="#ffffff"></polyline>
|
||||
<circle cx="12" cy="12" r="11"></circle>
|
||||
</g>
|
||||
</svg>Apply</button>
|
||||
</div>
|
||||
<div class="w-full md:w-auto p-1.5 ml-2">
|
||||
<button name="cancel" value="Cancel" type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-red-500 hover:bg-red-600 font-medium text-sm text-white border border-red-500 rounded-md shadow-button focus:ring-0 focus:outline-none" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#ef5844" stroke-linejoin="round">
|
||||
<line x1="16" y1="8" x2="8" y2="16" stroke="#ef5844"></line>
|
||||
<line x1="16" y1="16" x2="8" y2="8" stroke="#ef5844"></line>
|
||||
<circle cx="12" cy="12" r="11"></circle>
|
||||
</g>
|
||||
</svg>Cancel</button>
|
||||
</div> {% else %} <div class="w-full md:w-auto p-1.5">
|
||||
<button name="edit" value="edit" type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<svg class="text-gray-500 w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#ffffff" stroke-linejoin="round">
|
||||
<line x1="2" y1="23" x2="22" y2="23" stroke="#ffffff"></line>
|
||||
<line data-cap="butt" x1="13" y1="5" x2="17" y2="9"></line>
|
||||
<polygon points="8 18 3 19 4 14 16 2 20 6 8 18"></polygon>
|
||||
</g>
|
||||
</svg>Edit</button>
|
||||
</div>
|
||||
<div class="w-full md:w-auto p-1.5 ml-2">
|
||||
<a href="/automation" type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<svg class="text-gray-500 w-5 h-5 mr-2" height="24" width="24" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="square" stroke-width="2" fill="none" stroke="#ffffff" stroke-linejoin="miter" class="nc-icon-wrapper" stroke-miterlimit="10">
|
||||
<line data-cap="butt" x1="18" y1="12" x2="7" y2="12" stroke-linecap="butt" stroke="#ffffff"></line>
|
||||
<polyline points=" 11,16 7,12 11,8 " stroke="#ffffff"></polyline>
|
||||
<circle cx="12" cy="12" r="11"></circle>
|
||||
</g>
|
||||
</svg>
|
||||
<span>Back</span>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
</div>
|
||||
{% include 'footer.html' %}
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
@@ -1,63 +1,62 @@
|
||||
{% include 'header.html' %}
|
||||
<div class="container mx-auto">
|
||||
<section class="bg-white p-5 mt-5">
|
||||
<section class="p-5 mt-5">
|
||||
<div class="flex flex-wrap items-center -m-2">
|
||||
<div class="w-full md:w-1/2 p-2">
|
||||
<ul class="flex flex-wrap items-center gap-x-3 mb-2">
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 hover:text-coolGray-700" href="/">
|
||||
<p>Home</p>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li><a class="flex font-medium text-xs text-coolGray-500 hover:text-coolGray-700" href="/automation">Automation Strategies</a></li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li><a class="flex font-medium text-xs text-coolGray-500 hover:text-coolGray-700" href="/newautomationstrategy">New</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="w-full md:w-1/2 p-2">
|
||||
<ul class="flex flex-wrap items-center gap-x-3 mb-2">
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/">
|
||||
<p>Home</p>
|
||||
</a>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/automation">Automation Strategies</a>
|
||||
</li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/newautomationstrategy">New</a>
|
||||
</li>
|
||||
</ul>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="py-4">
|
||||
</section>
|
||||
<section class="py-4">
|
||||
<div class="container px-4 mx-auto">
|
||||
<div class="relative py-11 px-16 bg-coolGray-900 rounded-md overflow-hidden">
|
||||
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="">
|
||||
<div class="relative z-20 flex flex-wrap items-center -m-3">
|
||||
<div class="w-full md:w-1/2 p-3">
|
||||
<h2 class="mb-6 text-4xl font-bold text-white tracking-tighter">New Automation Strategy (todo)</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative py-11 px-16 bg-coolGray-900 dark:bg-blue-500 rounded-md overflow-hidden">
|
||||
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="">
|
||||
<div class="relative z-20 flex flex-wrap items-center -m-3">
|
||||
<div class="w-full md:w-1/2 p-3">
|
||||
<h2 class="mb-6 text-4xl font-bold text-white tracking-tighter">New Automation Strategy</h2>
|
||||
<p class="font-normal text-coolGray-200 dark:text-white">Todo <!-- todo -->
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% include 'inc_messages.html' %}
|
||||
|
||||
<section class="bg-white">
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 mt-5 h-full overflow-hidden bg-white ">
|
||||
<div class="pb-6 border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2 text-center ">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% include 'inc_messages.html' %}
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 mt-5 h-full overflow-hidden">
|
||||
<div class="pb-6 border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2 text-center "></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{% include 'footer.html' %}
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
@@ -1,412 +1,592 @@
|
||||
{% include 'header.html' %}
|
||||
|
||||
<div class="container mx-auto">
|
||||
<section class="bg-white p-5 mt-5">
|
||||
<div class="flex flex-wrap items-center -m-2">
|
||||
<div class="w-full md:w-1/2 p-2">
|
||||
<ul class="flex flex-wrap items-center gap-x-3 mb-2">
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 hover:text-coolGray-700" href="/">
|
||||
<p>Home</p>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li><a class="flex font-medium text-xs text-coolGray-500 hover:text-coolGray-700" href="#">Bids</a></li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li><a class="flex font-medium text-xs text-coolGray-500 hover:text-coolGray-700" href="{{ bid_id }}">BID ID:{{ bid_id }}</a></li>
|
||||
</ul>
|
||||
<section class="p-5 mt-5">
|
||||
<div class="flex flex-wrap items-center -m-2">
|
||||
<div class="w-full md:w-1/2 p-2">
|
||||
<ul class="flex flex-wrap items-center gap-x-3 mb-2">
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/">
|
||||
<p>Home</p>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="#">Bids</a>
|
||||
</li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="{{ bid_id }}">BID ID: {{ bid_id }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="py-3">
|
||||
<div class="container px-4 mx-auto">
|
||||
<div class="relative py-11 px-16 bg-coolGray-900 dark:bg-blue-500 rounded-md overflow-hidden">
|
||||
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="">
|
||||
<div class="relative z-20 flex flex-wrap items-center -m-3">
|
||||
<div class="w-full md:w-1/2 p-3">
|
||||
<h2 class="mb-6 text-4xl font-bold text-white tracking-tighter">Bid {% if debug_mode == true %} (Debug: bid template) {% endif %}</h2>
|
||||
<p class="font-normal text-coolGray-200 dark:text-white">Bid ID: {{ bid_id }}</p>
|
||||
</div>
|
||||
<div class="w-full md:w-1/2 p-3 p-6 container flex flex-wrap items-center justify-end items-center mx-auto"> {% if refresh %} <a id="refresh" href="/bid/{{ bid_id }}" class="flex flex-wrap justify-center px-5 py-3 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border dark:bg-gray-500 dark:hover:bg-gray-700 border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<svg class="text-gray-500 w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24">
|
||||
<g fill="#ffffff">
|
||||
<path fill="#ffffff" d="M12,3c1.989,0,3.873,0.65,5.43,1.833l-3.604,3.393l9.167,0.983L22.562,0l-3.655,3.442 C16.957,1.862,14.545,1,12,1C5.935,1,1,5.935,1,12h2C3,7.037,7.037,3,12,3z"></path>
|
||||
<path data-color="color-2" d="M12,21c-1.989,0-3.873-0.65-5.43-1.833l3.604-3.393l-9.167-0.983L1.438,24l3.655-3.442 C7.043,22.138,9.455,23,12,23c6.065,0,11-4.935,11-11h-2C21,16.963,16.963,21,12,21z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
<span>Refresh {{ refresh }} seconds</span>
|
||||
</a> {% else %} <a id="refresh" href="/bid/{{ bid_id }}" class="flex flex-wrap justify-center px-5 py-3 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border dark:bg-gray-500 dark:hover:bg-gray-700 border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<svg class="text-gray-500 w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24">
|
||||
<g fill="#ffffff">
|
||||
<path fill="#ffffff" d="M12,3c1.989,0,3.873,0.65,5.43,1.833l-3.604,3.393l9.167,0.983L22.562,0l-3.655,3.442 C16.957,1.862,14.545,1,12,1C5.935,1,1,5.935,1,12h2C3,7.037,7.037,3,12,3z"></path>
|
||||
<path data-color="color-2" d="M12,21c-1.989,0-3.873-0.65-5.43-1.833l3.604-3.393l-9.167-0.983L1.438,24l3.655-3.442 C7.043,22.138,9.455,23,12,23c6.065,0,11-4.935,11-11h-2C21,16.963,16.963,21,12,21z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
<span>Refresh</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="py-4">
|
||||
<div class="container px-4 mx-auto">
|
||||
<div class="relative py-11 px-16 bg-coolGray-900 rounded-md overflow-hidden">
|
||||
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="">
|
||||
<div class="relative z-20 flex flex-wrap items-center -m-3">
|
||||
<div class="w-full md:w-1/2 p-3">
|
||||
<h2 class="mb-6 text-4xl font-bold text-white tracking-tighter">Bid
|
||||
{% if debug_mode == true %}
|
||||
(Debug: bid template)
|
||||
{% endif %}
|
||||
</h2>
|
||||
<p class="font-semibold text-coolGray-200">Bid ID: {{ bid_id }}</p>
|
||||
</div>
|
||||
<div class="w-full md:w-1/2 p-3">
|
||||
{% if refresh %}
|
||||
<a id="refresh" href=/bid/{{ bid_id }}><button class="block md:ml-auto px-5 py-3 font-medium text-lcg text-white bg-blue-500 hover:bg-blue-600 rounded-md focus:ring-0 focus:outline-none">Refresh {{ refresh }} seconds</button></a>
|
||||
{% else %}
|
||||
<a id="refresh" href=/bid/{{ bid_id }}><button class="block md:ml-auto px-5 py-3 font-medium text-lcg text-white bg-blue-500 hover:bg-blue-600 rounded-md focus:ring-0 focus:outline-none">Refresh</button></a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% include 'inc_messages.html' %}
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 mt-5 h-full overflow-hidden">
|
||||
<div class="pb-6 border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="container mt-5 mx-auto">
|
||||
<div class="pt-6 pb-8 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="px-6">
|
||||
<div class="w-full mt-6 pb-6 overflow-x-auto">
|
||||
<table class="w-full min-w-max text-sm">
|
||||
<thead class="uppercase">
|
||||
<tr class="text-left">
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Options</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tr-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Details</span>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% if data.was_sent == 'True' %}
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="py-3 px-6 bold">Swap</td>
|
||||
<td class="py-3 px-6">
|
||||
<div class="content flex py-2">
|
||||
<span class="bold">{{ data.amt_to }} {{ data.ticker_to }}</span>
|
||||
<svg aria-hidden="true " class="w-5 h-5 ml-3 mr-3" fill="currentColor " viewBox="0 0 20 20 " xmlns="http://www.w3.org/2000/svg ">
|
||||
<path fill-rule="evenodd " d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z " clip-rule="evenodd "></path>
|
||||
</svg>
|
||||
<span class="text-xs bold">{{ data.amt_from }} {{ data.ticker_from }}</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="py-3 px-6 bold">Swap</td>
|
||||
<td class="py-3 px-6">
|
||||
<div class="content flex py-2">
|
||||
<span class="bold">{{ data.amt_from }} {{ data.ticker_from }}</span>
|
||||
<svg aria-hidden="true " class="w-5 h-5 ml-3 mr-3" fill="currentColor " viewBox="0 0 20 20 " xmlns="http://www.w3.org/2000/svg ">
|
||||
<path fill-rule="evenodd " d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z " clip-rule="evenodd "></path>
|
||||
</svg>
|
||||
<span class="bold">{{ data.amt_to }} {{ data.ticker_to }}</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="py-3 px-6 bold">Bid Rate</td>
|
||||
<td class="py-3 px-6">{{ data.bid_rate }}</td>
|
||||
</tr>
|
||||
{% if data.was_sent == 'True' %}
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="py-3 px-6 bold">You Send</td>
|
||||
<td class="py-3 px-6">
|
||||
<span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded">
|
||||
<img class="h-7" src="/static/images/coins/{{ data.coin_to }}.png" alt="{{ data.coin_to }}">
|
||||
</span>{{ data.coin_to }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="py-3 px-6 bold">You Get</td>
|
||||
<td class="py-3 px-6">
|
||||
<span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded">
|
||||
<img class="h-7" src="/static/images/coins/{{ data.coin_from }}.png" alt="{{ data.coin_from }}">
|
||||
</span>{{ data.coin_from }}
|
||||
</td>
|
||||
</tr> {% else %} <tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="py-3 px-6 bold">You Send</td>
|
||||
<td class="py-3 px-6">
|
||||
<span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded">
|
||||
<img class="h-7" src="/static/images/coins/{{ data.coin_from }}.png" alt="{{ data.coin_from }}">
|
||||
</span>{{ data.coin_from }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="py-3 px-6 bold">You Get</td>
|
||||
<td class="py-3 px-6">
|
||||
<span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded">
|
||||
<img class="h-7" src="/static/images/coins/{{ data.coin_to }}.png" alt="{{ data.coin_to }}">
|
||||
</span>{{ data.coin_to }}
|
||||
</td>
|
||||
</tr> {% endif %} <tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="py-3 px-6 bold">Bid State</td>
|
||||
<td class="py-3 px-6">{{ data.bid_state }}</td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="py-3 px-6 bold">State Description </td>
|
||||
<td class="py-3 px-6">{{ data.state_description }}</td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="py-3 px-6 bold">Offer</td>
|
||||
<td class="py-3 px-6">
|
||||
<a class="monospace bold select-all" href="/offer/{{ data.offer_id }}">{{ data.offer_id }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="py-3 px-6 bold">Address From</td>
|
||||
<td class="py-3 px-6">
|
||||
<a class="monospace bold select-all" href="/identity/{{ data.addr_from }}">{{ data.addr_from }}</a> {{ data.addr_from_label }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="flex items-center px-46 whitespace-nowrap">
|
||||
<svg alt="" class="w-5 h-5 rounded-full ml-5" xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#3B82F6" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="11"></circle>
|
||||
<polyline points=" 12,6 12,12 18,12 " stroke="#3B82F6"></polyline>
|
||||
</g>
|
||||
</svg>
|
||||
<div class="py-3 pl-2 bold">
|
||||
<div>Created At</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="py-3 px-6">{{ data.created_at }}</td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="flex items-center px-46 whitespace-nowrap">
|
||||
<svg alt="" class="w-5 h-5 rounded-full ml-5" xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#6b7280" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="11"></circle>
|
||||
<polyline points=" 12,6 12,12 18,12 " stroke="#6b7280"></polyline>
|
||||
</g>
|
||||
</svg>
|
||||
<div class="py-3 pl-2 bold">
|
||||
<div>Expired At</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="py-3 px-6">{{ data.expired_at }}</td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="py-3 px-6 bold">Sent</td>
|
||||
<td class="py-3 px-6">{{ data.was_sent }}</td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="py-3 px-6 bold">Received</td>
|
||||
<td class="py-3 px-6">{{ data.was_received }}</td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="py-3 px-6 bold">Initiate Tx</td>
|
||||
<td class="py-3 px-6 monospace">{{ data.initiate_tx }}</td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="py-3 px-6 bold">Initiate Conf</td>
|
||||
<td class="py-3 px-6">{{ data.initiate_conf }}</td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="py-3 px-6 bold">Participate Tx</td>
|
||||
<td class="py-3 px-6 monospace">{{ data.participate_tx }}</td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="py-3 px-6 bold">Participate Conf</td>
|
||||
<td class="py-3 px-6">{{ data.participate_conf }}</td>
|
||||
</tr>
|
||||
{% if data.show_txns %}
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="py-3 px-6 bold">Initiate Tx Refund</td>
|
||||
<td class="py-3 px-6 monospace">{{ data.initiate_tx_refund }}</td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="py-3 px-6 bold">Participate Tx Refund</td>
|
||||
<td class="py-3 px-6 monospace">{{ data.participate_tx_refund }}</td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="py-3 px-6 bold">Initiate Tx Spend Tx</td>
|
||||
<td class="py-3 px-6 monospace">{{ data.initiate_tx_spend }}</td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="py-3 px-6 bold">Participate Tx Spend Tx</td>
|
||||
<td class="py-3 px-6 monospace">{{ data.participate_tx_spend }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="p-6">
|
||||
<div class="flex flex-wrap items-center">
|
||||
<div class="w-full">
|
||||
<h4 class="font-semibold text-black dark:text-white text-2xl">Old states</h4>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden">
|
||||
<div class="pb-6 border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="container mt-5 mx-auto">
|
||||
<div class="pt-6 pb-8 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="px-6">
|
||||
<div class="w-full mt-6 pb-6 overflow-x-auto">
|
||||
<table class="w-full min-w-max text-sm">
|
||||
<thead class="uppercase">
|
||||
<tr class="text-left">
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Set at time</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tr-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Old states</span>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for s in old_states %}
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="flex items-center whitespace-nowrap">
|
||||
<svg alt="" class="w-5 h-5 rounded-full ml-5" xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#6b7280" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="11"></circle>
|
||||
<polyline points=" 12,6 12,12 18,12 " stroke="#6b7280"></polyline>
|
||||
</g>
|
||||
</svg>
|
||||
<div class="py-3 pl-2 bold">
|
||||
<div>{{ s[0] | formatts }}</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="py-3 px-6">{{ s[1] }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% if data.events %}
|
||||
<section class="p-6">
|
||||
<div class="flex flex-wrap items-center">
|
||||
<div class="w-full">
|
||||
<h4 class="font-semibold text-black dark:text-white text-2xl">Events</h4>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden">
|
||||
<div class="pb-6 border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="container mt-5 mx-auto">
|
||||
<div class="pt-6 pb-8 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="px-6">
|
||||
<div class="w-full mt-6 pb-6 overflow-x-auto">
|
||||
<table class="w-full min-w-max text-sm">
|
||||
<thead class="uppercase">
|
||||
<tr class="text-left">
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Time</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tr-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Events</span>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for e in data.events %}
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<td class="flex items-center px-46 whitespace-nowrap">
|
||||
<svg alt="" class="w-5 h-5 rounded-full ml-5" xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#6b7280" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="11"></circle>
|
||||
<polyline points=" 12,6 12,12 18,12 " stroke="#6b7280"></polyline>
|
||||
</g>
|
||||
</svg>
|
||||
<div class="py-3 pl-2 bold">
|
||||
<div>{{ e.at | formatts }}</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="py-3 px-6">{{ e.desc }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% else %}
|
||||
{% endif %}
|
||||
<form method="post"> {% if data.show_bidder_seq_diagram %} <section class="p-6">
|
||||
<div class="flex flex-wrap items-center">
|
||||
<div class="w-full">
|
||||
<h4 class="font-semibold text-black dark:text-white text-2xl">Bidder Sequence Diagram</h4>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% include 'inc_messages.html' %}
|
||||
|
||||
<section class="bg-white">
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 mt-5 h-full overflow-hidden bg-white ">
|
||||
<div class="pb-6 border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
|
||||
|
||||
|
||||
<div class="container px-0 mx-auto mt-5">
|
||||
<div class="overflow-x-auto relative border sm:rounded-lg">
|
||||
|
||||
|
||||
<table class="w-full text-sm text-left text-gray-500 outline-none border-gray-300">
|
||||
|
||||
<thead class="text-xs text-gray-700 border-b uppercase bg-gray-50 outline-none border-gray-300">
|
||||
<tr>
|
||||
<th scope="col" class="py-3 px-6">Item</th>
|
||||
<th scope="col">Data </th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
|
||||
{% if data.was_sent == 'True' %}
|
||||
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold">Swap</td>
|
||||
<td>{{ data.amt_to }} {{ data.ticker_to }} for {{ data.amt_from }} {{ data.ticker_from }}</td>
|
||||
</tr>
|
||||
|
||||
{% else %}
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold">Swap</td>
|
||||
<td>{{ data.amt_from }} {{ data.ticker_from }} for {{ data.amt_to }} {{ data.ticker_to }}</td>
|
||||
</tr>
|
||||
|
||||
{% endif %}
|
||||
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold">Bid Rate</td>
|
||||
<td>{{ data.bid_rate }}</td>
|
||||
</tr>
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold">Bid State</td>
|
||||
<td>{{ data.bid_state }}</td>
|
||||
</tr>
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold">State Description </td>
|
||||
<td>{{ data.state_description }}</td>
|
||||
</tr>
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold">ITX State</td>
|
||||
<td>{{ data.itx_state }}</td>
|
||||
</tr>
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold">PTX State</td>
|
||||
<td>{{ data.ptx_state }}</td>
|
||||
</tr>
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold">Offer</td>
|
||||
<td><a class="monospace" href="/offer/{{ data.offer_id }}">{{ data.offer_id }}</a></td>
|
||||
</tr>
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold">Address From</td>
|
||||
<td><a class="monospace" href="/identity/{{ data.addr_from }}">{{ data.addr_from }}</a> {{ data.addr_from_label }}</td>
|
||||
</tr>
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold">Proof of Funds</td>
|
||||
<td>{{ data.proof_address }}</td>
|
||||
</tr>
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold">Created At</td>
|
||||
<td>{{ data.created_at }}</td>
|
||||
</tr>
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold">Expired At</td>
|
||||
<td>{{ data.expired_at }}</td>
|
||||
</tr>
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold">Sent</td>
|
||||
<td>{{ data.was_sent }}</td>
|
||||
</tr>
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold">Received</td>
|
||||
<td>{{ data.was_received }}</td>
|
||||
</tr>
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold">Initiate Tx</td>
|
||||
<td class="monospace">{{ data.initiate_tx }}</td>
|
||||
</tr>
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold">Initiate Conf</td>
|
||||
<td>{{ data.initiate_conf }}</td>
|
||||
</tr>
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold">Participate Tx</td>
|
||||
<td class="monospace">{{ data.participate_tx }}</td>
|
||||
</tr>
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold">Participate Conf</td>
|
||||
<td>{{ data.participate_conf }}</td>
|
||||
</tr>
|
||||
{% if data.show_txns %}
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold">Initiate Tx Refund</td>
|
||||
<td class="monospace">{{ data.initiate_tx_refund }}</td>
|
||||
</tr>
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold">Participate Tx Refund</td>
|
||||
<td class="monospace">{{ data.participate_tx_refund }}</td>
|
||||
</tr>
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold">Initiate Tx Spend Tx</td>
|
||||
<td class="monospace">{{ data.initiate_tx_spend }}</td>
|
||||
</tr>
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold">Participate Tx Spend Tx</td>
|
||||
<td class="monospace">{{ data.participate_tx_spend }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden">
|
||||
<div class="pb-6 border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="container mt-5 mx-auto">
|
||||
<div class="pt-6 pb-8 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="px-6">
|
||||
<div class="w-full mt-6 pb-6 overflow-x-auto">
|
||||
<div class="overflow-x-auto items-center justify-center relative">
|
||||
<div class="flex items-center justify-center min-h-screen">
|
||||
<div class="flex items-center justify-between text-white">
|
||||
<img class="h-full py-2 pr-4 ml-8" src="/static/sequence_diagrams/bidder.alt.xu.min.svg">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="bg-white p-6">
|
||||
<div class="flex flex-wrap items-center">
|
||||
<div class="w-full">
|
||||
<h4 class="font-semibold text-black text-2xl">Old states</h4>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="container px-0 mx-auto mt-5">
|
||||
<div class="overflow-x-auto relative border sm:rounded-lg">
|
||||
|
||||
|
||||
<table class="w-full text-sm text-left text-gray-500 outline-none border-gray-300">
|
||||
|
||||
<thead class="text-xs text-gray-700 border-b uppercase bg-gray-50 outline-none border-gray-300">
|
||||
<tr>
|
||||
<th scope="col" class="py-3 px-6">Old States</th>
|
||||
<th scope="col">Set at Time</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
{% for s in old_states %}
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6">{{ s[1] }}</td>
|
||||
<td>{{ s[0] | formatts }} </td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
{% if data.show_offerer_seq_diagram %}
|
||||
<section class="p-6">
|
||||
<div class="flex flex-wrap items-center">
|
||||
<div class="w-full">
|
||||
<h4 class="font-semibold text-black dark:text-white text-2xl">Offerer Sequence Diagram</h4>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden">
|
||||
<div class="pb-6 border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="container mt-5 mx-auto">
|
||||
<div class="pt-6 pb-8 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="px-6">
|
||||
<div class="w-full mt-6 pb-6 overflow-x-auto">
|
||||
<div class="overflow-x-auto items-center justify-center relative">
|
||||
<div class="flex items-center justify-center min-h-screen">
|
||||
<div class="flex items-center justify-between text-white">
|
||||
<img class="h-full py-2 pr-4 ml-8" src="/static/sequence_diagrams/offerer.alt.xu.min.svg">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% if data.events %}
|
||||
|
||||
<section class="bg-white p-6">
|
||||
<div class="flex flex-wrap items-center">
|
||||
<div class="w-full">
|
||||
<h4 class="font-semibold text-black text-2xl">Events</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
{% if edit_bid %}
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden">
|
||||
<div class="pb-6 border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="container mt-5 mx-auto">
|
||||
<div class="pt-6 pb-8 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="px-6">
|
||||
<div class="w-full mt-6 pb-6 overflow-x-auto">
|
||||
<table class="w-full min-w-max text-sm">
|
||||
<thead class="uppercase">
|
||||
<tr class="text-left">
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Option</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tr-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Settings</span>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-3 px-6 bold">Change Bid State:</td>
|
||||
<td class="py-3 px-6">
|
||||
<div class="relative">
|
||||
<svg class="absolute right-4 top-1/2 transform -translate-y-1/2" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.3333 6.1133C11.2084 5.98913 11.0395 5.91943 10.8633 5.91943C10.6872 5.91943 10.5182 5.98913 10.3933 6.1133L8.00001 8.47329L5.64001 6.1133C5.5151 5.98913 5.34613 5.91943 5.17001 5.91943C4.99388 5.91943 4.82491 5.98913 4.70001 6.1133C4.63752 6.17527 4.58792 6.249 4.55408 6.33024C4.52023 6.41148 4.50281 6.49862 4.50281 6.58663C4.50281 6.67464 4.52023 6.76177 4.55408 6.84301C4.58792 6.92425 4.63752 6.99799 4.70001 7.05996L7.52667 9.88663C7.58865 9.94911 7.66238 9.99871 7.74362 10.0326C7.82486 10.0664 7.912 10.0838 8.00001 10.0838C8.08801 10.0838 8.17515 10.0664 8.25639 10.0326C8.33763 9.99871 8.41136 9.94911 8.47334 9.88663L11.3333 7.05996C11.3958 6.99799 11.4454 6.92425 11.4793 6.84301C11.5131 6.76177 11.5305 6.67464 11.5305 6.58663C11.5305 6.49862 11.5131 6.41148 11.4793 6.33024C11.4454 6.249 11.3958 6.17527 11.3333 6.1133Z" fill="#8896AB"></path>
|
||||
</svg>
|
||||
<select class="bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-" name="new_state">
|
||||
{% for s in data.bid_states %}
|
||||
<option value="{{ s[0] }}" {% if data.bid_state_ind==s[0] %} selected{% endif %}>{{ s[1] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<div class="container px-0 mx-auto mt-5">
|
||||
<div class="overflow-x-auto relative border sm:rounded-lg">
|
||||
|
||||
<table class="w-full text-sm text-left text-gray-500 outline-none border-gray-300">
|
||||
|
||||
<thead class="text-xs text-gray-700 border-b uppercase bg-gray-50 outline-none border-gray-300">
|
||||
<tr>
|
||||
<th scope="col" class="py-3 px-6">Time</th>
|
||||
<th scope="col">Events</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
{% for e in data.events %}
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6">{{ e.at | formatts }}</td>
|
||||
<td>{{ e.desc }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
{% if data.debug_ui == true %}
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-3 px-6 bold">Debug Option</td>
|
||||
<td class="py-3 px-6">
|
||||
<div class="relative">
|
||||
<svg class="absolute right-4 top-1/2 transform -translate-y-1/2" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.3333 6.1133C11.2084 5.98913 11.0395 5.91943 10.8633 5.91943C10.6872 5.91943 10.5182 5.98913 10.3933 6.1133L8.00001 8.47329L5.64001 6.1133C5.5151 5.98913 5.34613 5.91943 5.17001 5.91943C4.99388 5.91943 4.82491 5.98913 4.70001 6.1133C4.63752 6.17527 4.58792 6.249 4.55408 6.33024C4.52023 6.41148 4.50281 6.49862 4.50281 6.58663C4.50281 6.67464 4.52023 6.76177 4.55408 6.84301C4.58792 6.92425 4.63752 6.99799 4.70001 7.05996L7.52667 9.88663C7.58865 9.94911 7.66238 9.99871 7.74362 10.0326C7.82486 10.0664 7.912 10.0838 8.00001 10.0838C8.08801 10.0838 8.17515 10.0664 8.25639 10.0326C8.33763 9.99871 8.41136 9.94911 8.47334 9.88663L11.3333 7.05996C11.3958 6.99799 11.4454 6.92425 11.4793 6.84301C11.5131 6.76177 11.5305 6.67464 11.5305 6.58663C11.5305 6.49862 11.5131 6.41148 11.4793 6.33024C11.4454 6.249 11.3958 6.17527 11.3333 6.1133Z" fill="#8896AB"></path>
|
||||
</svg>
|
||||
<select class="bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-" name="debugind">
|
||||
<option{% if data.debug_ind=="-1" %} selected{% endif %} value="-1">None</option>
|
||||
{% for a in data.debug_options %}
|
||||
<option{% if data.debug_ind==a[0] %} selected{% endif %} value="{{ a[0] }}">{{ a[1] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
{% endif %}
|
||||
|
||||
<form method="post">
|
||||
|
||||
<div class="p-6 pt-10 bg-white bg-opacity-60 rounded-b-md">
|
||||
<div class="w-full md:w-0/12">
|
||||
<div class="flex flex-wrap justify-end -m-1.5">
|
||||
|
||||
{% if edit_bid %}
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td class="bold pr-5">Change Bid State:</td>
|
||||
<td>
|
||||
<select class="pappearance-none pr-15 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" name="new_state">
|
||||
{% for s in data.bid_states %}
|
||||
<option value="{{ s[0] }}" {% if data.bid_state_ind==s[0] %} selected{% endif %}>{{ s[1] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{% if data.debug_ui == true %}
|
||||
<tr>
|
||||
<td class="bold pr-5">Debug Option:</td>
|
||||
<td>
|
||||
<select class="apappearance-none pr-15 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" name="debugind">
|
||||
<option{% if data.debug_ind=="-1" %} selected{% endif %} value="-1">None</option>
|
||||
{% for a in data.debug_options %}
|
||||
<option{% if data.debug_ind==a[0] %} selected{% endif %} value="{{ a[0] }}">{{ a[1] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
</table>
|
||||
|
||||
|
||||
<div class="w-full md:w-auto p-1.5 ml-2">
|
||||
<button name="edit_bid_cancel" value="Cancel" type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<span>Cancel</span> </button>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden">
|
||||
<div class="pb-6 border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="container mt-5 mx-auto">
|
||||
<div class="pt-6 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="px-6">
|
||||
<div class="flex flex-wrap justify-end">
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<button name="edit_bid_cancel" value="Cancel" type="submit" class="lex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none dark:text-white dark:hover:text-white dark:bg-gray-600 dark:hover:bg-gray-700 dark:border-gray-600 dark:hover:border-gray-600">Cancel</button>
|
||||
</div>
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<button name="edit_bid_submit" value="Submit" type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">Submit Edit</button>
|
||||
</div>
|
||||
{% else %}
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden">
|
||||
<div class="pb-6 border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="container mt-5 mx-auto">
|
||||
<div class="pt-6 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="px-6">
|
||||
<div class="flex flex-wrap justify-end">
|
||||
{% if data.show_bidder_seq_diagram %}
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<button name="edit_bid_submit" value="Submit" type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<span>Submit Edit Bid</span> </button>
|
||||
<button name="hide_bidder_seq_diagram" type="submit" value="Hide Bidder Sequence Diagram" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md focus:ring-0 focus:outline-none dark:text-white dark:hover:text-white dark:bg-gray-600 dark:hover:bg-gray-700 dark:border-gray-600 dark:hover:border-gray-600">Hide Bidder Sequence Diagram</button>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
|
||||
{% if data.show_bidder_seq_diagram %}
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<button name="hide_bidder_seq_diagram" type="submit" value="Hide Bidder Sequence Diagram" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<span>Hide Bidder Sequence Diagram</span>
|
||||
</button>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<button name="show_bidder_seq_diagram" type="submit" value="Show Bidder Sequence Diagram" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<span>Show Bidder Sequence Diagram</span>
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if data.show_offerer_seq_diagram %}
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<button name="hide_offerer_seq_diagram" type="submit" value="Hide Offerer Sequence Diagram" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<span>Hide Offerer Sequence Diagram</span>
|
||||
</button>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<button name="show_offerer_seq_diagram" type="submit" value="Show Offerer Sequence Diagram" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<span>Show Offerer Sequence Diagram</span>
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if data.can_abandon == true %}
|
||||
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<button name="abandon_bid" type="submit" value="Abandon Bid" onclick="return confirmPopup();" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-red-500 hover:text-red-600 border border-red-400 hover:border-red-500 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<span>Abandon Bid</span> </button>
|
||||
<button name="show_bidder_seq_diagram" type="submit" value="Show Bidder Sequence Diagram" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md focus:ring-0 focus:outline-none dark:text-white dark:hover:text-white dark:bg-gray-600 dark:hover:bg-gray-700 dark:border-gray-600 dark:hover:border-gray-600">Show Bidder Sequence Diagram</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if data.show_offerer_seq_diagram %}
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<button name="hide_offerer_seq_diagram" type="submit" value="Hide Offerer Sequence Diagram" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md focus:ring-0 focus:outline-none dark:text-white dark:hover:text-white dark:bg-gray-600 dark:hover:bg-gray-700 dark:border-gray-600 dark:hover:border-gray-600">Hide Offerer Sequence Diagram</button>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<button name="show_offerer_seq_diagram" type="submit" value="Show Offerer Sequence Diagram" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md focus:ring-0 focus:outline-none dark:text-white dark:hover:text-white dark:bg-gray-600 dark:hover:bg-gray-700 dark:border-gray-600 dark:hover:border-gray-600">Show Offerer Sequence Diagram</button>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% if data.show_txns %}
|
||||
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<button name="hide_txns" type="submit" value="Hide Info" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<span>Hide info</span> </button>
|
||||
<button name="hide_txns" type="submit" value="Hide Info" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none dark:text-white dark:hover:text-white dark:bg-gray-600 dark:hover:bg-gray-700 dark:border-gray-600 dark:hover:border-gray-600">Hide More info</button>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<button name="show_txns" type="submit" value="Show More Info" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<span>Show More Info</span> </button>
|
||||
<button name="show_txns" type="submit" value="Show More Info" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none dark:text-white dark:hover:text-white dark:bg-gray-600 dark:hover:bg-gray-700 dark:border-gray-600 dark:hover:border-gray-600">Show More Info </button>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<button name="edit_bid" type="submit" value="Edit Bid" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<span>Edit bit</span> </button>
|
||||
<button name="edit_bid" type="submit" value="Edit Bid" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none dark:text-white dark:hover:text-white dark:bg-gray-600 dark:hover:bg-gray-700 dark:border-gray-600 dark:hover:border-gray-600">Edit Bid</button>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% if data.was_received == 'True' %}
|
||||
|
||||
{% if data.can_abandon == true and not edit_bid %}
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<button name="accept_bid" value="Accept Bid" type="submit" onclick='return confirmPopup("Accept");' class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<span>Accept Bid</span> </button>
|
||||
<button name="abandon_bid" type="submit" value="Abandon Bid" onclick="return confirmPopup();" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-white hover:text-red border border-red-500 hover:border-red-500 hover:bg-red-600 bg-red-500 rounded-md shadow-button focus:ring-0 focus:outline-none">Abandon Bid</button>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
||||
{% if data.was_received == 'True' and not edit_bid %}
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<button name="accept_bid" value="Accept Bid" type="submit" onclick='return confirmPopup("Accept");' class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">Accept Bid</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<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 %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<script>
|
||||
function confirmPopup() {
|
||||
return confirm("Are you sure?");
|
||||
}
|
||||
</script>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
<script>
|
||||
function confirmPopup(name) {
|
||||
return confirm(name + " Bid - Are you sure?");
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
{% include 'footer.html' %}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -1,267 +1,286 @@
|
||||
{% include 'header.html' %}
|
||||
|
||||
<div class="container mx-auto">
|
||||
|
||||
<section class="bg-white p-5 mt-5">
|
||||
<div class="flex flex-wrap items-center -m-2">
|
||||
<div class="w-full md:w-1/2 p-2">
|
||||
<ul class="flex flex-wrap items-center gap-x-3 mb-2">
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 hover:text-coolGray-700" href="/">
|
||||
<p>Home</p>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li><a class="flex font-medium text-xs text-coolGray-500 hover:text-coolGray-700" href="#">{{ page_type_available }} {{ page_type_received }} {{ page_type_sent }}</a></li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
</ul>
|
||||
<section class="p-5 mt-5">
|
||||
<div class="flex flex-wrap items-center -m-2">
|
||||
<div class="w-full md:w-1/2 p-2">
|
||||
<ul class="flex flex-wrap items-center gap-x-3 mb-2">
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/">
|
||||
<p>Home</p>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="#">{{ page_type_available }} {{ page_type_received }} {{ page_type_sent }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="py-4">
|
||||
<div class="container px-4 mx-auto">
|
||||
<div class="relative py-11 px-16 bg-coolGray-900 dark:bg-blue-500 rounded-md overflow-hidden">
|
||||
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="">
|
||||
<div class="relative z-20 flex flex-wrap items-center -m-3">
|
||||
<div class="w-full md:w-1/2 p-3">
|
||||
<h2 class="mb-6 text-4xl font-bold text-white tracking-tighter">{{ page_type_available }} {{ page_type_received }} {{ page_type_sent }}</h2>
|
||||
<p class="font-normal text-coolGray-200 dark:text-white">{{ page_type_available_description }} {{ page_type_received_description }} {{ page_type_sent_description }}</p>
|
||||
</div>
|
||||
<div class="w-full md:w-1/2 p-3 p-6 container flex flex-wrap items-center justify-end items-center mx-auto"> {% if refresh %} <a id="refresh" href="/bid/{{ bid_id }}" class="flex flex-wrap justify-center px-5 py-4 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<svg class="text-gray-500 w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24">
|
||||
<g fill="#ffffff">
|
||||
<path fill="#ffffff" d="M12,3c1.989,0,3.873,0.65,5.43,1.833l-3.604,3.393l9.167,0.983L22.562,0l-3.655,3.442 C16.957,1.862,14.545,1,12,1C5.935,1,1,5.935,1,12h2C3,7.037,7.037,3,12,3z"></path>
|
||||
<path data-color="color-2" d="M12,21c-1.989,0-3.873-0.65-5.43-1.833l3.604-3.393l-9.167-0.983L1.438,24l3.655-3.442 C7.043,22.138,9.455,23,12,23c6.065,0,11-4.935,11-11h-2C21,16.963,16.963,21,12,21z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
<span>Refresh {{ refresh }} seconds</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="py-4">
|
||||
<div class="container px-4 mx-auto">
|
||||
<div class="relative py-11 px-16 bg-coolGray-900 rounded-md overflow-hidden">
|
||||
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="">
|
||||
<div class="relative z-20 flex flex-wrap items-center -m-3">
|
||||
<div class="w-full md:w-1/2 p-3">
|
||||
<h2 class="mb-6 text-4xl font-bold text-white tracking-tighter">{{ page_type_available }} {{ page_type_received }} {{ page_type_sent }}</h2>
|
||||
<p class="font-semibold text-coolGray-200">{{ page_type_available_description }} {{ page_type_received_description }} {{ page_type_sent_description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% include 'inc_messages.html' %}
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 mt-5 h-full overflow-hidden">
|
||||
<div class="pb-6 border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<form method="post">
|
||||
<div class="flex justify-between items-center pb-4 dark:text-white">
|
||||
<div class="rounded-b-md">
|
||||
<div class="w-full md:w-0/12">
|
||||
<div class="flex flex-wrap justify-end -m-1.5">
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<div class="relative">
|
||||
<svg class="absolute right-4 top-1/2 transform -translate-y-1/2" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.3333 6.1133C11.2084 5.98913 11.0395 5.91943 10.8633 5.91943C10.6872 5.91943 10.5182 5.98913 10.3933 6.1133L8.00001 8.47329L5.64001 6.1133C5.5151 5.98913 5.34613 5.91943 5.17001 5.91943C4.99388 5.91943 4.82491 5.98913 4.70001 6.1133C4.63752 6.17527 4.58792 6.249 4.55408 6.33024C4.52023 6.41148 4.50281 6.49862 4.50281 6.58663C4.50281 6.67464 4.52023 6.76177 4.55408 6.84301C4.58792 6.92425 4.63752 6.99799 4.70001 7.05996L7.52667 9.88663C7.58865 9.94911 7.66238 9.99871 7.74362 10.0326C7.82486 10.0664 7.912 10.0838 8.00001 10.0838C8.08801 10.0838 8.17515 10.0664 8.25639 10.0326C8.33763 9.99871 8.41136 9.94911 8.47334 9.88663L11.3333 7.05996C11.3958 6.99799 11.4454 6.92425 11.4793 6.84301C11.5131 6.76177 11.5305 6.67464 11.5305 6.58663C11.5305 6.49862 11.5131 6.41148 11.4793 6.33024C11.4454 6.249 11.3958 6.17527 11.3333 6.1133Z" fill="#8896AB"></path>
|
||||
</svg>
|
||||
<select name="sort_by" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0">
|
||||
<option value="created_at" {% if filters.sort_by=='created_at' %} selected{% endif %}>Time At</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<div class="relative">
|
||||
<svg class="absolute right-4 top-1/2 transform -translate-y-1/2" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.3333 6.1133C11.2084 5.98913 11.0395 5.91943 10.8633 5.91943C10.6872 5.91943 10.5182 5.98913 10.3933 6.1133L8.00001 8.47329L5.64001 6.1133C5.5151 5.98913 5.34613 5.91943 5.17001 5.91943C4.99388 5.91943 4.82491 5.98913 4.70001 6.1133C4.63752 6.17527 4.58792 6.249 4.55408 6.33024C4.52023 6.41148 4.50281 6.49862 4.50281 6.58663C4.50281 6.67464 4.52023 6.76177 4.55408 6.84301C4.58792 6.92425 4.63752 6.99799 4.70001 7.05996L7.52667 9.88663C7.58865 9.94911 7.66238 9.99871 7.74362 10.0326C7.82486 10.0664 7.912 10.0838 8.00001 10.0838C8.08801 10.0838 8.17515 10.0664 8.25639 10.0326C8.33763 9.99871 8.41136 9.94911 8.47334 9.88663L11.3333 7.05996C11.3958 6.99799 11.4454 6.92425 11.4793 6.84301C11.5131 6.76177 11.5305 6.67464 11.5305 6.58663C11.5305 6.49862 11.5131 6.41148 11.4793 6.33024C11.4454 6.249 11.3958 6.17527 11.3333 6.1133Z" fill="#8896AB"></path>
|
||||
</svg>
|
||||
<select name="sort_dir" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<p class="text-sm font-heading bold">State:</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<div class="relative">
|
||||
<svg class="absolute right-4 top-1/2 transform -translate-y-1/2" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.3333 6.1133C11.2084 5.98913 11.0395 5.91943 10.8633 5.91943C10.6872 5.91943 10.5182 5.98913 10.3933 6.1133L8.00001 8.47329L5.64001 6.1133C5.5151 5.98913 5.34613 5.91943 5.17001 5.91943C4.99388 5.91943 4.82491 5.98913 4.70001 6.1133C4.63752 6.17527 4.58792 6.249 4.55408 6.33024C4.52023 6.41148 4.50281 6.49862 4.50281 6.58663C4.50281 6.67464 4.52023 6.76177 4.55408 6.84301C4.58792 6.92425 4.63752 6.99799 4.70001 7.05996L7.52667 9.88663C7.58865 9.94911 7.66238 9.99871 7.74362 10.0326C7.82486 10.0664 7.912 10.0838 8.00001 10.0838C8.08801 10.0838 8.17515 10.0664 8.25639 10.0326C8.33763 9.99871 8.41136 9.94911 8.47334 9.88663L11.3333 7.05996C11.3958 6.99799 11.4454 6.92425 11.4793 6.84301C11.5131 6.76177 11.5305 6.67464 11.5305 6.58663C11.5305 6.49862 11.5131 6.41148 11.4793 6.33024C11.4454 6.249 11.3958 6.17527 11.3333 6.1133Z" fill="#8896AB"></path>
|
||||
</svg>
|
||||
<select name="state" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<p class="text-sm font-heading bold">Include Expired:</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<div class="relative">
|
||||
<svg class="absolute right-4 top-1/2 transform -translate-y-1/2" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.3333 6.1133C11.2084 5.98913 11.0395 5.91943 10.8633 5.91943C10.6872 5.91943 10.5182 5.98913 10.3933 6.1133L8.00001 8.47329L5.64001 6.1133C5.5151 5.98913 5.34613 5.91943 5.17001 5.91943C4.99388 5.91943 4.82491 5.98913 4.70001 6.1133C4.63752 6.17527 4.58792 6.249 4.55408 6.33024C4.52023 6.41148 4.50281 6.49862 4.50281 6.58663C4.50281 6.67464 4.52023 6.76177 4.55408 6.84301C4.58792 6.92425 4.63752 6.99799 4.70001 7.05996L7.52667 9.88663C7.58865 9.94911 7.66238 9.99871 7.74362 10.0326C7.82486 10.0664 7.912 10.0838 8.00001 10.0838C8.08801 10.0838 8.17515 10.0664 8.25639 10.0326C8.33763 9.99871 8.41136 9.94911 8.47334 9.88663L11.3333 7.05996C11.3958 6.99799 11.4454 6.92425 11.4793 6.84301C11.5131 6.76177 11.5305 6.67464 11.5305 6.58663C11.5305 6.49862 11.5131 6.41148 11.4793 6.33024C11.4454 6.249 11.3958 6.17527 11.3333 6.1133Z" fill="#8896AB"></path>
|
||||
</svg>
|
||||
<select name="with_expired" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<div class="relative">
|
||||
<button type="submit" name='clearfilters' value="Clear Filters" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-white bg-blue-500 hover:bg-blue-600 rounded-lg transition duration-200 border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<svg class="mr-2 w-5 h-5" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#fff" stroke-linejoin="round">
|
||||
<line x1="20" y1="2" x2="12.329" y2="11.506"></line>
|
||||
<path d="M11,11a2,2,0,0,1,2,2,3.659,3.659,0,0,1-.2.891A9.958,9.958,0,0,0,13.258,23H1C1,16.373,4.373,11,11,11Z"></path>
|
||||
<line x1="18" y1="15" x2="23" y2="15" stroke="#fff"></line>
|
||||
<line x1="17" y1="19" x2="23" y2="19" stroke="#fff"></line>
|
||||
<line x1="19" y1="23" x2="23" y2="23" stroke="#fff"></line>
|
||||
<path d="M8.059,11.415A3.9,3.9,0,0,0,12,16c.041,0,.079-.011.12-.012" data-cap="butt"></path>
|
||||
<path d="M5,23a13.279,13.279,0,0,1,.208-3.4" data-cap="butt"></path>
|
||||
<path d="M9.042,23c-.688-1.083-.313-3.4-.313-3.4" data-cap="butt"></path>
|
||||
</g>
|
||||
</svg>
|
||||
<span>Clear</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<div class="relative">
|
||||
<button type="submit" name='applyfilters' value="Apply Filters" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-white bg-blue-500 hover:bg-blue-600 rounded-lg transition duration-200 border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<svg class="mr-2 w-5 h-5" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#fff" stroke-linejoin="round">
|
||||
<rect x="2" y="2" width="7" height="7"></rect>
|
||||
<rect x="15" y="15" width="7" height="7"></rect>
|
||||
<rect x="2" y="15" width="7" height="7"></rect>
|
||||
<polyline points="15 6 17 8 22 3" stroke="#fff"></polyline>
|
||||
</g>
|
||||
</svg>
|
||||
<span>Apply Filters</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- todo -->
|
||||
{% if refresh %}
|
||||
<p>Page Refresh: {{ refresh }} seconds</p> {% endif %} {% for m in messages %}
|
||||
<p>{{ m }}</p>
|
||||
{% endfor %}
|
||||
<!-- todo -->
|
||||
|
||||
<section class="bg-white">
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 mt-5 h-full overflow-hidden bg-white">
|
||||
<div class="pb-6 border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
|
||||
<form method="post">
|
||||
|
||||
<div class="flex justify-between items-center pb-4 bg-white">
|
||||
<div class="bg-white bg-opacity-60 rounded-b-md">
|
||||
<div class="w-full md:w-0/12">
|
||||
<div class="flex flex-wrap justify-end -m-1.5">
|
||||
|
||||
<div class="flex items-center">
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<p class="text-sm font-heading bold">Sort By:</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<div class="relative">
|
||||
<svg class="absolute right-4 top-1/2 transform -translate-y-1/2" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.3333 6.1133C11.2084 5.98913 11.0395 5.91943 10.8633 5.91943C10.6872 5.91943 10.5182 5.98913 10.3933 6.1133L8.00001 8.47329L5.64001 6.1133C5.5151 5.98913 5.34613 5.91943 5.17001 5.91943C4.99388 5.91943 4.82491 5.98913 4.70001 6.1133C4.63752 6.17527 4.58792 6.249 4.55408 6.33024C4.52023 6.41148 4.50281 6.49862 4.50281 6.58663C4.50281 6.67464 4.52023 6.76177 4.55408 6.84301C4.58792 6.92425 4.63752 6.99799 4.70001 7.05996L7.52667 9.88663C7.58865 9.94911 7.66238 9.99871 7.74362 10.0326C7.82486 10.0664 7.912 10.0838 8.00001 10.0838C8.08801 10.0838 8.17515 10.0664 8.25639 10.0326C8.33763 9.99871 8.41136 9.94911 8.47334 9.88663L11.3333 7.05996C11.3958 6.99799 11.4454 6.92425 11.4793 6.84301C11.5131 6.76177 11.5305 6.67464 11.5305 6.58663C11.5305 6.49862 11.5131 6.41148 11.4793 6.33024C11.4454 6.249 11.3958 6.17527 11.3333 6.1133Z" fill="#8896AB">
|
||||
</path>
|
||||
</svg>
|
||||
<select name="sort_by" class="appearance-none pr-10 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5">
|
||||
<option value="created_at" {% if filters.sort_by=='created_at' %} selected{% endif %}>Time At</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<div class="relative">
|
||||
<svg class="absolute right-4 top-1/2 transform -translate-y-1/2" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.3333 6.1133C11.2084 5.98913 11.0395 5.91943 10.8633 5.91943C10.6872 5.91943 10.5182 5.98913 10.3933 6.1133L8.00001 8.47329L5.64001 6.1133C5.5151 5.98913 5.34613 5.91943 5.17001 5.91943C4.99388 5.91943 4.82491 5.98913 4.70001 6.1133C4.63752 6.17527 4.58792 6.249 4.55408 6.33024C4.52023 6.41148 4.50281 6.49862 4.50281 6.58663C4.50281 6.67464 4.52023 6.76177 4.55408 6.84301C4.58792 6.92425 4.63752 6.99799 4.70001 7.05996L7.52667 9.88663C7.58865 9.94911 7.66238 9.99871 7.74362 10.0326C7.82486 10.0664 7.912 10.0838 8.00001 10.0838C8.08801 10.0838 8.17515 10.0664 8.25639 10.0326C8.33763 9.99871 8.41136 9.94911 8.47334 9.88663L11.3333 7.05996C11.3958 6.99799 11.4454 6.92425 11.4793 6.84301C11.5131 6.76177 11.5305 6.67464 11.5305 6.58663C11.5305 6.49862 11.5131 6.41148 11.4793 6.33024C11.4454 6.249 11.3958 6.17527 11.3333 6.1133Z" fill="#8896AB">
|
||||
</path>
|
||||
</svg>
|
||||
<select name="sort_dir" class="appearance-none pr-10 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center">
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<p class="text-sm font-heading bold">State:</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<div class="relative">
|
||||
<svg class="absolute right-4 top-1/2 transform -translate-y-1/2" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.3333 6.1133C11.2084 5.98913 11.0395 5.91943 10.8633 5.91943C10.6872 5.91943 10.5182 5.98913 10.3933 6.1133L8.00001 8.47329L5.64001 6.1133C5.5151 5.98913 5.34613 5.91943 5.17001 5.91943C4.99388 5.91943 4.82491 5.98913 4.70001 6.1133C4.63752 6.17527 4.58792 6.249 4.55408 6.33024C4.52023 6.41148 4.50281 6.49862 4.50281 6.58663C4.50281 6.67464 4.52023 6.76177 4.55408 6.84301C4.58792 6.92425 4.63752 6.99799 4.70001 7.05996L7.52667 9.88663C7.58865 9.94911 7.66238 9.99871 7.74362 10.0326C7.82486 10.0664 7.912 10.0838 8.00001 10.0838C8.08801 10.0838 8.17515 10.0664 8.25639 10.0326C8.33763 9.99871 8.41136 9.94911 8.47334 9.88663L11.3333 7.05996C11.3958 6.99799 11.4454 6.92425 11.4793 6.84301C11.5131 6.76177 11.5305 6.67464 11.5305 6.58663C11.5305 6.49862 11.5131 6.41148 11.4793 6.33024C11.4454 6.249 11.3958 6.17527 11.3333 6.1133Z" fill="#8896AB">
|
||||
</path>
|
||||
</svg>
|
||||
<select name="state" class="appearance-none pr-10 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center">
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<p class="text-sm font-heading bold">Include Expired:</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<div class="relative">
|
||||
<svg class="absolute right-4 top-1/2 transform -translate-y-1/2" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.3333 6.1133C11.2084 5.98913 11.0395 5.91943 10.8633 5.91943C10.6872 5.91943 10.5182 5.98913 10.3933 6.1133L8.00001 8.47329L5.64001 6.1133C5.5151 5.98913 5.34613 5.91943 5.17001 5.91943C4.99388 5.91943 4.82491 5.98913 4.70001 6.1133C4.63752 6.17527 4.58792 6.249 4.55408 6.33024C4.52023 6.41148 4.50281 6.49862 4.50281 6.58663C4.50281 6.67464 4.52023 6.76177 4.55408 6.84301C4.58792 6.92425 4.63752 6.99799 4.70001 7.05996L7.52667 9.88663C7.58865 9.94911 7.66238 9.99871 7.74362 10.0326C7.82486 10.0664 7.912 10.0838 8.00001 10.0838C8.08801 10.0838 8.17515 10.0664 8.25639 10.0326C8.33763 9.99871 8.41136 9.94911 8.47334 9.88663L11.3333 7.05996C11.3958 6.99799 11.4454 6.92425 11.4793 6.84301C11.5131 6.76177 11.5305 6.67464 11.5305 6.58663C11.5305 6.49862 11.5131 6.41148 11.4793 6.33024C11.4454 6.249 11.3958 6.17527 11.3333 6.1133Z" fill="#8896AB">
|
||||
</path>
|
||||
</svg>
|
||||
<select name="with_expired" class="appearance-none pr-10 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<div class="relative">
|
||||
<button type="submit" name='clearfilters' value="Clear Filters" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<svg class="mr-2 w-5 h-5" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#556987" stroke-linejoin="round" class="nc-icon-wrapper">
|
||||
<line x1="20" y1="2" x2="12.329" y2="11.506"></line>
|
||||
<path d="M11,11a2,2,0,0,1,2,2,3.659,3.659,0,0,1-.2.891A9.958,9.958,0,0,0,13.258,23H1C1,16.373,4.373,11,11,11Z"></path>
|
||||
<line x1="18" y1="15" x2="23" y2="15" stroke="#556987"></line>
|
||||
<line x1="17" y1="19" x2="23" y2="19" stroke="#556987"></line>
|
||||
<line x1="19" y1="23" x2="23" y2="23" stroke="#556987"></line>
|
||||
<path d="M8.059,11.415A3.9,3.9,0,0,0,12,16c.041,0,.079-.011.12-.012" data-cap="butt"></path>
|
||||
<path d="M5,23a13.279,13.279,0,0,1,.208-3.4" data-cap="butt"></path>
|
||||
<path d="M9.042,23c-.688-1.083-.313-3.4-.313-3.4" data-cap="butt"></path>
|
||||
</g>
|
||||
</svg> <span>Clear</span> </button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<div class="relative">
|
||||
<button type="submit" name='applyfilters' value="Apply Filters" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<svg class="mr-2 w-5 h-5" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#ffff" stroke-linejoin="round" class="nc-icon-wrapper">
|
||||
<rect x="2" y="2" width="7" height="7"></rect>
|
||||
<rect x="15" y="15" width="7" height="7"></rect>
|
||||
<rect x="2" y="15" width="7" height="7"></rect>
|
||||
<polyline points="15 6 17 8 22 3" stroke="#fff"></polyline>
|
||||
</g>
|
||||
</svg> <span>Apply Filters</span> </button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container mt-5 mx-auto">
|
||||
<div class="pt-6 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="px-6">
|
||||
<div class="w-full mt-6 pb-6 overflow-x-auto">
|
||||
<table class="w-full min-w-max">
|
||||
<thead class="uppercase">
|
||||
<tr class="text-left">
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Date/Time at</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="container px-0 mx-auto mt-5">
|
||||
<div class="overflow-x-auto relative border sm:rounded-lg">
|
||||
<table class="w-full text-sm text-left text-gray-500 outline-none border-gray-300">
|
||||
|
||||
<thead class="text-xs text-gray-700 border-b uppercase bg-gray-50 outline-none border-gray-300">
|
||||
<tr>
|
||||
<th scope="col" class="py-3 px-6"> Time At </th>
|
||||
<th scope="col" class="py-3 px-6"> Bid ID </th>
|
||||
<th scope="col" class="py-3 px-6"> Offer ID </th>
|
||||
<th scope="col" class="py-4 px-7"> Bid From </th>
|
||||
<th scope="col" class="py-3 px-7"> Bid Status </th>
|
||||
<th scope="col" class="py-3 px-6"> ITX Status </th>
|
||||
<th scope="col" class="py-3 px-6"> PTX Status </th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for b in bids %}
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<th scope="row" class="flex items-center py-7 px-46 text-gray-900 whitespace-nowrap">
|
||||
<svg class="w-5 h-5 rounded-full ml-5" xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#3B82F6" stroke-linejoin="round" class="nc-icon-wrapper">
|
||||
<circle cx="12" cy="12" r="11"></circle>
|
||||
<polyline points=" 12,6 12,12 18,12 " stroke="#3B82F6"></polyline>
|
||||
</g>
|
||||
</svg>
|
||||
<div class="pl-3">
|
||||
<div class="font-semibold text-xs">{{ b[0] }}</div>
|
||||
</div>
|
||||
</th>
|
||||
<td class="py-4 px-6 text-xs monospace"><a href=/bid/{{ b[1] }}>{{ b[1]|truncate(20, True) }}</a></td>
|
||||
<td class="py-4 px-6 text-xs monospace"><a href=/offer/{{ b[2] }}>{{ b[2]|truncate(20, True) }}</a></td>
|
||||
<td class="py-4 px-6 text-xs monospace"><a href=/identity/{{ b[6] }}>{{ b[6] }}</a></td>
|
||||
<td class="py-4 px-6">{{ b[3] }}</td>
|
||||
<td class="py-4 px-6">{{ b[4] }}</td>
|
||||
<td class="py-4 px-6">{{ b[5] }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
{% endfor %}
|
||||
|
||||
</table>
|
||||
|
||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||
<input type="hidden" name="pageno" value="{{ filters.page_no }}">
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pt-10 bg-white bg-opacity-60 rounded-b-md">
|
||||
<div class="w-full md:w-0/12">
|
||||
|
||||
<div class="flex flex-wrap justify-end -m-1.5">
|
||||
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<button type="submit" name='pageback' value="Page Back" class="outline-none flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<svg aria-hidden="true" class="mr-2 w-5 h-5" fill="#556987" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M7.707 14.707a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 1.414L5.414 9H17a1 1 0 110 2H5.414l2.293 2.293a1 1 0 010 1.414z" clip-rule="evenodd"></path>
|
||||
</svg> <span>Page Back</span> </button>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center">
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<p class="text-sm font-heading">Page: {{ filters.page_no }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<button type="submit" name='pageforwards' value="Page Forwards" class="outline-none flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none"> <span>Page Forwards</span>
|
||||
<svg aria-hidden="true" class="ml-2 w-5 h-5" fill="#556987" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Bid ID</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Offer ID</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Bid From</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Bid Status</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">ITX Status</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">PTX Status</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tr-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Details</span>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for b in bids %}
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||
<th scope="row" class="flex items-center py-7 px-46 text-gray-900 whitespace-nowrap">
|
||||
<svg class="w-5 h-5 rounded-full ml-5" xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#6b7280" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="11"></circle>
|
||||
<polyline points=" 12,6 12,12 18,12 " stroke="#6b7280"></polyline>
|
||||
</g>
|
||||
</svg>
|
||||
<div class="pl-3">
|
||||
<div class="font-semibold text-xs dark:text-white">{{ b[0] }}</div>
|
||||
</div>
|
||||
</th>
|
||||
<td class="py-3 px-6 text-xs monospace">
|
||||
<a href=/bid/{{ b[1] }}>{{ b[1]|truncate(20, True) }}</a>
|
||||
</td>
|
||||
<td class="py-3 px-6 text-xs monospace">
|
||||
<a href=/offer/{{ b[2] }}>{{ b[2]|truncate(20, True) }}</a>
|
||||
</td>
|
||||
<td class="py-3 px-6 text-xs monospace">
|
||||
<a href=/identity/{{ b[6] }}>{{ b[6] }}</a>
|
||||
</td>
|
||||
<td class="py-3 px-6 text-xs">{{ b[3] }}</td>
|
||||
<td class="py-3 px-6 text-xs">{{ b[4] }}</td>
|
||||
<td class="py-3 px-6 text-xs">{{ b[5] }}</td>
|
||||
{% if page_type_received or page_type_sent %}
|
||||
<td class="py-3 px-6 text-xs">
|
||||
<a class="inline-block w-20 py-1 px-2 font-medium text-center text-sm rounded-md bg-blue-500 text-white border border-blue-500 hover:bg-blue-600 transition duration-200 bg-blue-500 text-white hover:bg-blue-600 transition duration-200" href="/bid/{{ b[1] }}">Details</a>
|
||||
</td>
|
||||
{% elif page_type_available %}
|
||||
<td class="py-3 px-6 text-xs">
|
||||
<a class="inline-block w-20 py-1 px-2 font-medium text-center text-sm rounded-md bg-blue-500 text-white border border-blue-500 hover:bg-blue-600 transition duration-200 bg-blue-500 text-white hover:bg-blue-600 transition duration-200" href="/bid/{{ b[1] }}">Accept</a>
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</tbody>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||
<input type="hidden" name="pageno" value="{{ filters.page_no }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<div class="rounded-b-md">
|
||||
<div class="w-full md:w-0/12">
|
||||
<div class="flex flex-wrap justify-end pt-6 pr-6 border-t border-gray-100 dark:border-gray-400">
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<button type="submit" name='pageback' value="Page Back" class="inline-flex items-center h-9 py-1 px-4 text-xs text-blue-50 font-semibold bg-blue-500 hover:bg-blue-600 rounded-lg transition duration-200 focus:ring-0 focus:outline-none">
|
||||
<svg aria-hidden="true" class="mr-2 w-5 h-5" fill="#fff" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M7.707 14.707a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 1.414L5.414 9H17a1 1 0 110 2H5.414l2.293 2.293a1 1 0 010 1.414z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
<span>Page Back</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<p class="text-sm font-heading dark:text-white">Page: {{ filters.page_no }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<button type="submit" name='pageforwards' value="Page Forwards" class="inline-flex items-center h-9 py-1 px-4 text-xs text-blue-50 font-semibold bg-blue-500 hover:bg-blue-600 rounded-lg transition duration-200 focus:ring-0 focus:outline-none">
|
||||
<span>Page Forwards</span>
|
||||
<svg aria-hidden="true" class="ml-2 w-5 h-5" fill="#fff" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z" clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{% include 'footer.html' %}
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
203
basicswap/templates/changepassword.html
Normal file
@@ -0,0 +1,203 @@
|
||||
{% include 'header.html' %}
|
||||
<div class="container mx-auto">
|
||||
<section class="p-5 mt-5">
|
||||
<div class="flex flex-wrap items-center -m-2">
|
||||
<div class="w-full md:w-1/2 p-2">
|
||||
<ul class="flex flex-wrap items-center gap-x-3 mb-2">
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/">
|
||||
<p>Home</p>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/wallets">
|
||||
<p>Home</p>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/changepassword">Change Password</a>
|
||||
</li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="py-4">
|
||||
<div class="container px-4 mx-auto">
|
||||
<div class="relative py-11 px-16 bg-coolGray-900 dark:bg-blue-500 rounded-md overflow-hidden">
|
||||
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="">
|
||||
<div class="relative z-20 flex flex-wrap items-center -m-3">
|
||||
<div class="w-full md:w-1/2 p-3">
|
||||
<h2 class="mb-6 text-4xl font-bold text-white tracking-tighter">Change Password</h2>
|
||||
<p class="font-normal text-coolGray-200 dark:text-white">Update your BasicSwap / Wallets password.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% include 'inc_messages.html' %}
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden">
|
||||
<div class="border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="container mt-5 mx-auto">
|
||||
<div class="pt-6 pb-8 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="px-6">
|
||||
<div class="w-full mt-6 pb-6 overflow-x-auto">
|
||||
<table class="w-full min-w-max text-sm">
|
||||
<thead class="uppercase">
|
||||
<tr class="text-left">
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Password</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold py-3 px-6"></span>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<form method="post" autocomplete="off">
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-3 px-6 bold">Old Password</td>
|
||||
<td td class="py-3 px-6">
|
||||
<div class="relative w-full">
|
||||
<div class="absolute inset-y-0 right-0 flex items-center px-2">
|
||||
<input class="hidden js-password-toggle" id="toggle-old" type="checkbox" />
|
||||
<label class="px-2 py-1 text-sm text-gray-600 font-mono cursor-pointer js-password-label" for="toggle-old" id="input-old-label">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24">
|
||||
<g fill="#8896ab">
|
||||
<path d="M23.444,10.239C21.905,8.062,17.708,3,12,3S2.1,8.062.555,10.24a3.058,3.058,0,0,0,0,3.52h0C2.1,15.938,6.292,21,12,21s9.905-5.062,11.445-7.24A3.058,3.058,0,0,0,23.444,10.239ZM12,17a5,5,0,1,1,5-5A5,5,0,0,1,12,17Z" fill="#8896ab"></path>
|
||||
</g>
|
||||
</svg>
|
||||
</label>
|
||||
</div>
|
||||
<input class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" type="password" name="oldpassword" id="input-old">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-3 px-6 bold">New Password</td>
|
||||
<td td class="py-3 px-6">
|
||||
<div class="relative w-full">
|
||||
<div class="absolute inset-y-0 right-0 flex items-center px-2">
|
||||
<input class="hidden js-password-toggle" id="toggle-new" type="checkbox" />
|
||||
<label class="px-2 py-1 text-sm text-gray-600 font-mono cursor-pointer js-password-label" for="toggle-new" id="input-new-label">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24">
|
||||
<g fill="#8896ab">
|
||||
<path d="M23.444,10.239C21.905,8.062,17.708,3,12,3S2.1,8.062.555,10.24a3.058,3.058,0,0,0,0,3.52h0C2.1,15.938,6.292,21,12,21s9.905-5.062,11.445-7.24A3.058,3.058,0,0,0,23.444,10.239ZM12,17a5,5,0,1,1,5-5A5,5,0,0,1,12,17Z" fill="#8896ab"></path>
|
||||
</g>
|
||||
</svg>
|
||||
</label>
|
||||
</div>
|
||||
<input class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" type="password" name="newpassword" id="input-new">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-3 px-6 bold">Confirm Password</td>
|
||||
<td td class="py-3 px-6">
|
||||
<div class="relative w-full">
|
||||
<div class="absolute inset-y-0 right-0 flex items-center px-2">
|
||||
<input class="hidden js-password-toggle" id="toggle-conf" type="checkbox" />
|
||||
<label class="px-2 py-1 text-sm text-gray-600 font-mono cursor-pointer js-password-label" for="toggle-conf" id="input-confirm-label">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24">
|
||||
<g fill="#8896ab">
|
||||
<path d="M23.444,10.239C21.905,8.062,17.708,3,12,3S2.1,8.062.555,10.24a3.058,3.058,0,0,0,0,3.52h0C2.1,15.938,6.292,21,12,21s9.905-5.062,11.445-7.24A3.058,3.058,0,0,0,23.444,10.239ZM12,17a5,5,0,1,1,5-5A5,5,0,0,1,12,17Z" fill="#8896ab"></path>
|
||||
</g>
|
||||
</svg>
|
||||
</label>
|
||||
</div>
|
||||
<input class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" type="password" name="confirmpassword" id="input-confirm">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden ">
|
||||
<div class="pb-6 ">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="container mx-auto">
|
||||
<div class="pt-6 pb-6 bg-coolGray-100 border-t border-gray-100 dark:border-gray-400 dark:bg-gray-500 rounded-bl-xl rounded-br-xl">
|
||||
<div class="px-6">
|
||||
<div class="flex flex-wrap justify-end">
|
||||
<div class="w-full md:w-auto p-1.5 ml-2">
|
||||
<button type="submit" name="unlock" value="Unlock" class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<svg class="text-gray-500 w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#ffffff" stroke-linejoin="round">
|
||||
<polyline points=" 6,12 10,16 18,8 " stroke="#ffffff"></polyline>
|
||||
<circle cx="12" cy="12" r="11"></circle>
|
||||
</g>
|
||||
</svg>Apply </button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
{% include 'footer.html' %}
|
||||
<script>
|
||||
function togglePassword(event) {
|
||||
let input_name = 'input-new';
|
||||
if (event.target.id == 'toggle-old') {
|
||||
input_name = 'input-old';
|
||||
} else
|
||||
if (event.target.id == 'toggle-conf') {
|
||||
input_name = 'input-confirm';
|
||||
}
|
||||
const password = document.getElementById(input_name),
|
||||
passwordLabel = document.getElementById(input_name + '-label');
|
||||
if (password.type === 'password') {
|
||||
password.type = 'text';
|
||||
passwordLabel.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24"><g fill="#8896ab"><path d="M23.444,10.239a22.936,22.936,0,0,0-2.492-2.948l-4.021,4.021A5.026,5.026,0,0,1,17,12a5,5,0,0,1-5,5,5.026,5.026,0,0,1-.688-.069L8.055,20.188A10.286,10.286,0,0,0,12,21c5.708,0,9.905-5.062,11.445-7.24A3.058,3.058,0,0,0,23.444,10.239Z" fill="#8896ab"></path><path d="M12,3C6.292,3,2.1,8.062.555,10.24a3.058,3.058,0,0,0,0,3.52h0a21.272,21.272,0,0,0,4.784,4.9l3.124-3.124a5,5,0,0,1,7.071-7.072L8.464,15.536l10.2-10.2A11.484,11.484,0,0,0,12,3Z" fill="#8896ab"></path><path data-color="color-2" d="M1,24a1,1,0,0,1-.707-1.707l22-22a1,1,0,0,1,1.414,1.414l-22,22A1,1,0,0,1,1,24Z"></path></g></svg>';
|
||||
} else {
|
||||
password.type = 'password';
|
||||
passwordLabel.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24"><g fill="#8896ab" ><path d="M23.444,10.239C21.905,8.062,17.708,3,12,3S2.1,8.062.555,10.24a3.058,3.058,0,0,0,0,3.52h0C2.1,15.938,6.292,21,12,21s9.905-5.062,11.445-7.24A3.058,3.058,0,0,0,23.444,10.239ZM12,17a5,5,0,1,1,5-5A5,5,0,0,1,12,17Z" fill="#8896ab"></path></g></svg>';
|
||||
}
|
||||
password.focus();
|
||||
}
|
||||
|
||||
const toggles = ["toggle-old", "toggle-new", "toggle-conf"]
|
||||
toggles.forEach(function (toggle_id, index) {
|
||||
document.getElementById(toggle_id).addEventListener('change', togglePassword, false);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,12 +1,11 @@
|
||||
{% include 'header.html' %}
|
||||
<div class="container mx-auto">
|
||||
|
||||
<section class="bg-white p-5 mt-5">
|
||||
<section class="p-5 mt-5">
|
||||
<div class="flex flex-wrap items-center -m-2">
|
||||
<div class="w-full md:w-1/2 p-2">
|
||||
<ul class="flex flex-wrap items-center gap-x-3 mb-2">
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 hover:text-coolGray-700" href="/">
|
||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/">
|
||||
<p>Home</p>
|
||||
</a>
|
||||
</li>
|
||||
@@ -15,7 +14,9 @@
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li><a class="flex font-medium text-xs text-coolGray-500 hover:text-coolGray-700" href="/debug">Debug</a></li>
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/debug">Debug</a>
|
||||
</li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
@@ -25,71 +26,111 @@
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="py-4">
|
||||
<div class="container px-4 mx-auto">
|
||||
<div class="relative py-11 px-16 bg-coolGray-900 rounded-md overflow-hidden">
|
||||
<div class="relative py-11 px-16 bg-coolGray-900 dark:bg-blue-500 rounded-md overflow-hidden">
|
||||
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="">
|
||||
<div class="relative z-20 flex flex-wrap items-center -m-3">
|
||||
<div class="w-full md:w-1/2 p-3">
|
||||
<h2 class="mb-6 text-4xl font-bold text-white tracking-tighte r">Debug</h2>
|
||||
<p class="font-semibold text-coolGray-200"></p>
|
||||
<h2 class="mb-6 text-4xl font-bold text-white tracking-tighter">Debug</h2>
|
||||
<p class="font-normal text-coolGray-200 dark:text-white">Debug options & Easter eggs.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% include 'inc_messages.html' %}
|
||||
|
||||
<section class="bg-white">
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 mt-5 h-full overflow-hidden bg-white ">
|
||||
<div class="pb-6 border-coolGray-100">
|
||||
{% include 'inc_messages.html' %}
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden min-height-50">
|
||||
<div class="border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
|
||||
<div class="container px-0 mx-auto mt-5">
|
||||
<div class="overflow-x-auto relative border sm:rounded-lg">
|
||||
|
||||
<table class="w-full text-sm text-left text-gray-500 outline-none border-gray-300">
|
||||
|
||||
<thead class="text-xs text-gray-700 border-b uppercase bg-gray-50 outline-none border-gray-300">
|
||||
<tr>
|
||||
<th scope="col" class="py-3 px-6 w-80">Name</th>
|
||||
<th scope="col" class="py-3">Input</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<form method="post">
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 pl-5 pr-5 bold">Reinitialise XMR wallet</td>
|
||||
<td td class="py-4"><input name="reinit_xmr" type="submit" value="Yes" class="inline-block w-20 py-1 px-2 font-medium text-center text-sm rounded-md shadow-button bg-blue-500 text-white border border-coolGray-200 hover:border-coolGray-300"></td>
|
||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||
</tr>
|
||||
</form>
|
||||
</table>
|
||||
</div>
|
||||
</div
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<!-- todo
|
||||
<div class="container mt-5 mx-auto">
|
||||
<div class="pt-6 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="px-6">
|
||||
<div class="w-full mt-6 pb-6 overflow-x-auto">
|
||||
<table class="w-full min-w-max text-sm">
|
||||
<thead class="uppercase">
|
||||
<tr class="text-left">
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Options</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tr-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold py-3 px-6"></span>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<form method="post">
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-3 px-6 bold">Remove expired offers and bids</td>
|
||||
<td td class="py-3 px-6 ">
|
||||
<button name="remove_expired" type="submit" value="Yes" class="flex flex-wrap justify-center py-2 px-4 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" onclick="return confirmRemoveExpired();">
|
||||
Yes - Remove Data</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-3 px-6 bold">Reinitialise XMR wallet</td>
|
||||
<td td class="py-3 px-6 ">
|
||||
<button name="reinit_xmr" type="submit" value="Yes" class="flex flex-wrap justify-center py-2 px-4 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<svg class="text-gray-500 w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="square" stroke-width="2" fill="none" stroke="#ffffff" stroke-linejoin="miter" class="nc-icon-wrapper" stroke-miterlimit="10">
|
||||
<polyline points="2.966 1.13 2.237 6.927 7.929 5.341"></polyline>
|
||||
<path d="M2.921,18.2a11.006,11.006,0,0,1-1.041-1.9" stroke="#ffffff"></path>
|
||||
<path d="M8.461,22.412a11.07,11.07,0,0,1-1.529-.654q-.2-.1-.392-.214" stroke="#ffffff"></path>
|
||||
<path d="M15.539,22.41a11.062,11.062,0,0,1-2.06.486" stroke="#ffffff"></path>
|
||||
<path d="M21.08,18.206a10.984,10.984,0,0,1-1.438,1.705" stroke="#ffffff"></path>
|
||||
<path d="M2.759,6.027A11,11,0,0,1,22.9,13.464"></path>
|
||||
</g>
|
||||
</svg>Yes - Start Process</button>
|
||||
</td>
|
||||
</tr>
|
||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||
</form>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden ">
|
||||
<div class="pb-6 ">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="container mx-auto">
|
||||
<div class="pt-6 pb-6 bg-coolGray-100 border-t border-gray-100 dark:border-gray-400 dark:bg-gray-500 rounded-bl-xl rounded-br-xl">
|
||||
<div class="px-6"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<!-- todo
|
||||
{% if result %}
|
||||
<textarea class="monospace" rows="40" cols="160">
|
||||
{{ result }}
|
||||
</textarea>
|
||||
{% endif %}
|
||||
-->
|
||||
|
||||
</div>
|
||||
{% include 'footer.html' %}
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
function confirmRemoveExpired() {
|
||||
return confirm("This will remove all expired offers and bids from the database - Are you sure?");
|
||||
}
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
<!DOCTYPE html><html lang="en"><head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel=icon sizes="32x32" type="image/png" href="/static/images/favicon-32.png">
|
||||
<title>{{ title }}</title>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel=icon sizes="32x32" type="image/png" href="/static/images/favicon-32.png">
|
||||
<title>(BSX) BasicSwap - v{{ version }}</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>{{ title_str }}</h2>
|
||||
<p>Info: {{ message_str }}</p>
|
||||
<p><a href="/">home</a></p>
|
||||
<h2>{{ title_str }}</h2>
|
||||
<p>Info: {{ message_str }}</p>
|
||||
<p><a href="/">home</a></p>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -1,161 +1,174 @@
|
||||
{% include 'header.html' %}
|
||||
<div class="container mx-auto">
|
||||
<section class="bg-white p-5 mt-5">
|
||||
<div class="flex flex-wrap items-center -m-2">
|
||||
<div class="w-full md:w-1/2 p-2">
|
||||
<ul class="flex flex-wrap items-center gap-x-3 mb-2">
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 hover:text-coolGray-700" href="/">
|
||||
<p>Home</p>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li><a class="flex font-medium text-xs text-coolGray-500 hover:text-coolGray-700" href="/explorers">Explorers</a></li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<section class="p-5 mt-5">
|
||||
<div class="flex flex-wrap items-center -m-2">
|
||||
<div class="w-full md:w-1/2 p-2">
|
||||
<ul class="flex flex-wrap items-center gap-x-3 mb-2">
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/">
|
||||
<p>Home</p>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
<li>
|
||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/explores">Explorers</a>
|
||||
</li>
|
||||
<li>
|
||||
<svg width="6" height="15" viewBox="0 0 6 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.34 0.671999L2.076 14.1H0.732L3.984 0.671999H5.34Z" fill="#BBC3CF"></path>
|
||||
</svg>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="py-4">
|
||||
<div class="container px-4 mx-auto">
|
||||
<div class="relative py-11 px-16 bg-coolGray-900 dark:bg-blue-500 rounded-md overflow-hidden">
|
||||
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="">
|
||||
<div class="relative z-20 flex flex-wrap items-center -m-3">
|
||||
<div class="w-full md:w-1/2 p-3">
|
||||
<h2 class="mb-6 text-4xl font-bold text-white tracking-tighter">Explorers</h2>
|
||||
<p class="font-normal text-coolGray-200 dark:text-white">Query blockchain explorers with quick commands.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="py-4">
|
||||
<div class="container px-4 mx-auto">
|
||||
<div class="relative py-11 px-16 bg-coolGray-900 rounded-md overflow-hidden">
|
||||
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="">
|
||||
<div class="relative z-20 flex flex-wrap items-center -m-3">
|
||||
<div class="w-full md:w-1/2 p-3">
|
||||
<h2 class="mb-6 text-4xl font-bold text-white tracking-tighter">Explorers</h2>
|
||||
<p class="font-semibold text-coolGray-200"></p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% include 'inc_messages.html' %}
|
||||
<section>
|
||||
<form method="post">
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden min-height-50">
|
||||
<div class="border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="container mt-5 mx-auto">
|
||||
<div class="pt-6 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="px-6">
|
||||
<div class="w-full mt-6 pb-6 overflow-x-auto">
|
||||
<table class="w-full min-w-max text-sm">
|
||||
<thead class="uppercase">
|
||||
<tr class="text-left">
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Select Explorer</span>
|
||||
</div>
|
||||
</th>
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tr-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold py-3 px-6">Action</span>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-3 px-6">
|
||||
<div class="relative">
|
||||
<svg class="absolute right-4 top-1/2 transform -translate-y-1/2" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.3333 6.1133C11.2084 5.98913 11.0395 5.91943 10.8633 5.91943C10.6872 5.91943 10.5182 5.98913 10.3933 6.1133L8.00001 8.47329L5.64001 6.1133C5.5151 5.98913 5.34613 5.91943 5.17001 5.91943C4.99388 5.91943 4.82491 5.98913 4.70001 6.1133C4.63752 6.17527 4.58792 6.249 4.55408 6.33024C4.52023 6.41148 4.50281 6.49862 4.50281 6.58663C4.50281 6.67464 4.52023 6.76177 4.55408 6.84301C4.58792 6.92425 4.63752 6.99799 4.70001 7.05996L7.52667 9.88663C7.58865 9.94911 7.66238 9.99871 7.74362 10.0326C7.82486 10.0664 7.912 10.0838 8.00001 10.0838C8.08801 10.0838 8.17515 10.0664 8.25639 10.0326C8.33763 9.99871 8.41136 9.94911 8.47334 9.88663L11.3333 7.05996C11.3958 6.99799 11.4454 6.92425 11.4793 6.84301C11.5131 6.76177 11.5305 6.67464 11.5305 6.58663C11.5305 6.49862 11.5131 6.41148 11.4793 6.33024C11.4454 6.249 11.3958 6.17527 11.3333 6.1133Z" fill="#8896AB"></path>
|
||||
</svg>
|
||||
<select class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" name="explorer">
|
||||
<!-- <option value="-1" {% if explorer==-1 %} selected{% endif %}>Select Explorer</option> -->
|
||||
{% for e in explorers %}
|
||||
<option value="{{ e[0] }}" {% if explorer==e[0] %} selected{% endif %}>{{ e[1] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</td>
|
||||
<td class="py-3 px-6">
|
||||
<select class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" name="action">
|
||||
<option value="-1" {% if action==-1 %} selected{% endif %}>Select Action</option>
|
||||
{% for a in actions %} <option value="{{ a[0] }}" {% if action==a[0] %} selected{% endif %}>{{ a[1] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-3 px-6 bold">Arguments:</td>
|
||||
<td class="py-3 px-6">
|
||||
<input class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" type="text" name="args" placeholder="Arguments">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% include 'inc_messages.html' %}
|
||||
|
||||
<section class="bg-white">
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 mt-5 h-full overflow-hidden bg-white ">
|
||||
<div class="pb-6 border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
|
||||
<div class="container px-0 mx-auto mt-5">
|
||||
<div class="overflow-x-auto relative border sm:rounded-lg">
|
||||
|
||||
<form method="post">
|
||||
|
||||
<table class="w-full text-sm text-left text-gray-500 outline-none border-gray-300">
|
||||
|
||||
<thead class="text-xs text-gray-700 border-b uppercase bg-gray-50 outline-none border-gray-300">
|
||||
<tr>
|
||||
<th scope="col" class="py-3 px-6">Select Explorer</th>
|
||||
<th scope="col">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tr class="bg-white border-t">
|
||||
<td class="py-4 px-6 bold w-96">
|
||||
|
||||
|
||||
|
||||
<div class="relative">
|
||||
<svg class="absolute right-4 top-1/2 transform -translate-y-1/2" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.3333 6.1133C11.2084 5.98913 11.0395 5.91943 10.8633 5.91943C10.6872 5.91943 10.5182 5.98913 10.3933 6.1133L8.00001 8.47329L5.64001 6.1133C5.5151 5.98913 5.34613 5.91943 5.17001 5.91943C4.99388 5.91943 4.82491 5.98913 4.70001 6.1133C4.63752 6.17527 4.58792 6.249 4.55408 6.33024C4.52023 6.41148 4.50281 6.49862 4.50281 6.58663C4.50281 6.67464 4.52023 6.76177 4.55408 6.84301C4.58792 6.92425 4.63752 6.99799 4.70001 7.05996L7.52667 9.88663C7.58865 9.94911 7.66238 9.99871 7.74362 10.0326C7.82486 10.0664 7.912 10.0838 8.00001 10.0838C8.08801 10.0838 8.17515 10.0664 8.25639 10.0326C8.33763 9.99871 8.41136 9.94911 8.47334 9.88663L11.3333 7.05996C11.3958 6.99799 11.4454 6.92425 11.4793 6.84301C11.5131 6.76177 11.5305 6.67464 11.5305 6.58663C11.5305 6.49862 11.5131 6.41148 11.4793 6.33024C11.4454 6.249 11.3958 6.17527 11.3333 6.1133Z" fill="#8896AB"> </path>
|
||||
</svg>
|
||||
<select class="appearance-none bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"name="explorer">
|
||||
<!-- <option value="-1" {% if explorer==-1 %} selected{% endif %}>Select Explorer</option> -->
|
||||
{% for e in explorers %}
|
||||
<option value="{{ e[0] }}" {% if explorer==e[0] %} selected{% endif %}>{{ e[1] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</td>
|
||||
<td class="py-4 pr-5">
|
||||
<select class="pr-15 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" name="action">
|
||||
<option value="-1" {% if action==-1 %} selected{% endif %}>Select Action</option>
|
||||
{% for a in actions %}
|
||||
<option value="{{ a[0] }}" {% if action==a[0] %} selected{% endif %}>{{ a[1] }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 px-6 bold">
|
||||
Arguments:
|
||||
</td>
|
||||
<td class="py-4 pr-5">
|
||||
<input class="appearance-none bg-gray-50 border w-full border-gray-300 text-gray-900 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block p-2.5" type="text" name="args" placeholder="Arguments">
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-6 pt-10 bg-white bg-opacity-60 rounded-b-md">
|
||||
<div class="w-full md:w-0/12">
|
||||
<div class="flex flex-wrap justify-end -m-1.5">
|
||||
|
||||
<div class="w-full md:w-auto p-1.5 ml-2">
|
||||
<button value="Submit" type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<span>Submit</span> </button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||
|
||||
{% if result %}
|
||||
|
||||
<div class="container px-0 mx-auto mt-5">
|
||||
<div class="overflow-x-auto relative border sm:rounded-lg">
|
||||
|
||||
<table class="w-full text-sm text-left text-gray-500 outline-none border-gray-300">
|
||||
|
||||
<thead class="text-xs text-gray-700 border-b uppercase bg-gray-50 outline-none border-gray-300">
|
||||
<tr>
|
||||
<th scope="col" class="py-3 px-6">Explorer output:</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tr class="bg-white border-t hover:bg-gray-50">
|
||||
<td class="py-4 pl-5 pr-5">
|
||||
<textarea class="whitespace-pre-line outline-none block p-2.5 text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500 w-full monospace" rows="15">
|
||||
{{ result }}
|
||||
</textarea>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden ">
|
||||
<div class="pb-6 ">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="container mx-auto">
|
||||
<div class="pt-6 pb-6 bg-coolGray-100 border-t border-gray-100 dark:border-gray-400 dark:bg-gray-500 rounded-bl-xl rounded-br-xl">
|
||||
<div class="px-6">
|
||||
<div class="flex flex-wrap justify-end">
|
||||
<div class="w-full md:w-auto p-1.5 ml-2">
|
||||
<button type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||
<svg class="text-gray-500 w-5 h-5 mr-2" xmlns="http://www.w3.org/2000/svg" height="24" width="24" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#ffffff" stroke-linejoin="round">
|
||||
<polyline points=" 6,12 10,16 18,8 " stroke="#ffffff"></polyline>
|
||||
<circle cx="12" cy="12" r="11"></circle>
|
||||
</g>
|
||||
</svg>Apply</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
{% include 'footer.html' %}
|
||||
</div>
|
||||
</section>
|
||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||
{% if result %}
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden">
|
||||
<div class="border-coolGray-100">
|
||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="container mt-5 mx-auto">
|
||||
<div class="pt-6 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="px-6">
|
||||
<div class="w-full mt-6 pb-6 overflow-x-auto">
|
||||
<table class="w-full min-w-max text-sm">
|
||||
<thead class="uppercase">
|
||||
<tr class="text-left">
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Explorer output</span>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-3 px-6">
|
||||
<textarea class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" rows="20">{{ result }}</textarea>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{% include 'footer.html' %}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
<!-- todo round corners -->
|
||||
@@ -1,40 +1,36 @@
|
||||
<section class="bg-white overflow-hidden">
|
||||
<section class="overflow-hidden">
|
||||
<div class="container px-4 mx-auto">
|
||||
<div class="flex flex-wrap lg:items-center pt-24 pb-12 -mx-4">
|
||||
<div class="w-full md:w-3/4 px-4">
|
||||
<a class="block mb-8 max-w-max" href="#">
|
||||
<img class="h-8" src="/static/images/logos/basicswap-logo-dark.svg" alt="">
|
||||
<a class="block mb-8 max-w-max" href="/">
|
||||
<img src="/static/images/logos/basicswap-logo.svg" class="h-8 imageshow dark-image">
|
||||
<img src="/static/images/logos/basicswap-logo-dark.svg" class="h-8 imageshow light-image">
|
||||
</a>
|
||||
<div class="mb-12 md:mb-0 flex flex-wrap -mx-3 md:-mx-6">
|
||||
<div class="w-full md:w-auto p-3 md:py-0 md:px-6"><a class="inline-block text-coolGray-500 hover:text-coolGray-600 font-medium" href="https://academy.particl.io/en/latest/basicswap-dex/basicswap_explained.html" target="_blank">BasicSwap Explained</a></div>
|
||||
<div class="w-full md:w-auto p-3 md:py-0 md:px-6"><a class="inline-block text-coolGray-500 hover:text-coolGray-600 font-medium" href="https://academy.particl.io/en/latest/basicswap-guides/basicswapguides_installation.html" target="_blank">Tutorials and Guides</a></div>
|
||||
<div class="w-full md:w-auto p-3 md:py-0 md:px-6"><a class="inline-block text-coolGray-500 hover:text-coolGray-600 font-medium" href="https://academy.particl.io/en/latest/faq/get_support.html" target="_blank">Get Support</a></div>
|
||||
<div class="w-full md:w-auto p-3 md:py-0 md:px-6"><a class="inline-block text-coolGray-500 dark:text-gray-300 font-medium" href="https://academy.particl.io/en/latest/basicswap-dex/basicswap_explained.html" target="_blank">BasicSwap Explained</a></div>
|
||||
<div class="w-full md:w-auto p-3 md:py-0 md:px-6"><a class="inline-block text-coolGray-500 dark:text-gray-300 font-medium" href="https://academy.particl.io/en/latest/basicswap-guides/basicswapguides_installation.html" target="_blank">Tutorials and Guides</a></div>
|
||||
<div class="w-full md:w-auto p-3 md:py-0 md:px-6"><a class="inline-block text-coolGray-500 dark:text-gray-300 font-medium" href="https://academy.particl.io/en/latest/faq/get_support.html" target="_blank">Get Support</a></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-1/4 px-4">
|
||||
|
||||
</div>
|
||||
<div class="w-full md:w-1/4 px-4"> </div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border-b border-coolGray-100"></div>
|
||||
<div class="border-b border-gray-100 dark:border-gray-500 dark:bg-body dark:border-b-2"></div>
|
||||
<div class="container px-4 mx-auto">
|
||||
<div class="flex flex-wrap items-center py-12 md:pb-32">
|
||||
<div class="w-full md:w-1/2 mb-6 md:mb-0">
|
||||
|
||||
<div class="flex items-center">
|
||||
<div class="flex items-center">
|
||||
|
||||
<p class="mr-1 text-sm text-gray-90 font-medium ">© 2022~ </p>
|
||||
<p class="text-sm text-coolGray-400 font-medium">{{ title }}</p>
|
||||
<span class="w-1 h-1 mx-1.5 bg-gray-500 rounded-full"></span>
|
||||
<p class="text-sm text-coolGray-400 font-medium">GUI 0.1.1 </p>
|
||||
<span class="w-1 h-1 mx-1.5 bg-gray-500 rounded-full"></span>
|
||||
<p class="mr-2 text-sm font-bold text-gray-90 ">Made with </p>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24"><g stroke-linecap="round" stroke-width="2" fill="none" stroke="#f80b0b" stroke-linejoin="round" class="nc-icon-wrapper"><path d="M21.243,3.757 c-2.343-2.343-6.142-2.343-8.485,0c-0.289,0.289-0.54,0.6-0.757,0.927c-0.217-0.327-0.469-0.639-0.757-0.927 c-2.343-2.343-6.142-2.343-8.485,0c-2.343,2.343-2.343,6.142,0,8.485L12,21.485l9.243-9.243C23.586,9.899,23.586,6.1,21.243,3.757z"></path></g></svg>
|
||||
<span class="ml-2 text-sm font-bold text-gray-90 ">by TV and CRZ</span>
|
||||
<p class="text-sm text-gray-90 dark:text-white font-medium">© 2024~ (BSX) BasicSwap</p> <span class="w-1 h-1 mx-1.5 bg-gray-500 dark:bg-white rounded-full"></span>
|
||||
<p class="text-sm text-coolGray-400 font-medium">BSX: v{{ version }}</p> <span class="w-1 h-1 mx-1.5 bg-gray-500 dark:bg-white rounded-full"></span>
|
||||
<p class="text-sm text-coolGray-400 font-medium">GUI: v2.0.3</p> <span class="w-1 h-1 mx-1.5 bg-gray-500 dark:bg-white rounded-full"></span>
|
||||
<p class="mr-2 text-sm font-bold dark:text-white text-gray-90 ">Made with </p>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24">
|
||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#f80b0b" stroke-linejoin="round">
|
||||
<path d="M21.243,3.757 c-2.343-2.343-6.142-2.343-8.485,0c-0.289,0.289-0.54,0.6-0.757,0.927c-0.217-0.327-0.469-0.639-0.757-0.927 c-2.343-2.343-6.142-2.343-8.485,0c-2.343,2.343-2.343,6.142,0,8.485L12,21.485l9.243-9.243C23.586,9.899,23.586,6.1,21.243,3.757z"></path>
|
||||
</g>
|
||||
</svg> <span class="ml-2 text-sm font-bold dark:text-white text-gray-90 ">by TV and CRZ</span></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-1/2">
|
||||
<div class="flex flex-wrap md:justify-end -mx-5">
|
||||
@@ -47,8 +43,7 @@
|
||||
</div>
|
||||
<div class="px-5">
|
||||
<a class="inline-block text-coolGray-300 hover:text-coolGray-400" href="#">
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
</svg>
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg"></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -56,4 +51,74 @@
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<script src="/static/js/libs/dd.min.js"></script>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
var toggleImages = function() {
|
||||
var html = document.querySelector('html');
|
||||
var darkImages = document.querySelectorAll('.dark-image');
|
||||
var lightImages = document.querySelectorAll('.light-image');
|
||||
|
||||
if (html && html.classList.contains('dark')) {
|
||||
toggleImageDisplay(darkImages, 'block');
|
||||
toggleImageDisplay(lightImages, 'none');
|
||||
} else {
|
||||
toggleImageDisplay(darkImages, 'none');
|
||||
toggleImageDisplay(lightImages, 'block');
|
||||
}
|
||||
}
|
||||
|
||||
var toggleImageDisplay = function(images, display) {
|
||||
images.forEach(function(img) {
|
||||
img.style.display = display;
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var themeToggle = document.getElementById('theme-toggle');
|
||||
|
||||
if (themeToggle) {
|
||||
themeToggle.addEventListener('click', function() {
|
||||
toggleImages();
|
||||
});
|
||||
}
|
||||
|
||||
toggleImages();
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
var themeToggleDarkIcon = document.getElementById('theme-toggle-dark-icon');
|
||||
var themeToggleLightIcon = document.getElementById('theme-toggle-light-icon');
|
||||
|
||||
if (localStorage.getItem('color-theme') === 'dark' || (!('color-theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||||
themeToggleLightIcon.classList.remove('hidden');
|
||||
} else {
|
||||
themeToggleDarkIcon.classList.remove('hidden');
|
||||
}
|
||||
|
||||
function setTheme(theme) {
|
||||
if (theme === 'light') {
|
||||
document.documentElement.classList.remove('dark');
|
||||
localStorage.setItem('color-theme', 'light');
|
||||
} else {
|
||||
document.documentElement.classList.add('dark');
|
||||
localStorage.setItem('color-theme', 'dark');
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('theme-toggle').addEventListener('click', () => {
|
||||
if (localStorage.getItem('color-theme') === 'dark') {
|
||||
setTheme('light');
|
||||
} else {
|
||||
setTheme('dark');
|
||||
}
|
||||
themeToggleDarkIcon.classList.toggle('hidden');
|
||||
themeToggleLightIcon.classList.toggle('hidden');
|
||||
toggleImages();
|
||||
});
|
||||
|
||||
</script>
|
||||