mirror of
https://github.com/JoinMarket-Org/joinmarket-clientserver.git
synced 2026-08-18 13:17:51 +02:00
This commit implements a command line script and a GUI dialog to receive a payment using the BIP78 protocol, by setting up an ephemeral hidden service. It also deprecates the pre-existing inter-Joinmarket protocol for payjoin payments, since we now have both sending and receiving support for BIP78. Thus, much code in Maker, Taker and client-daemon protocol is removed, as is some documentation in docs/PAYJOIN.md. Also the script `sendpayment.py` is altered to support only the BIP78 variant. The test in jmclient/test/test_payjoin now implements BIP78 over a TCP connection, while the custom tests in test/payjoinserver.py can support hidden service based tests, but the latter is not included in the test suite and may not always work (it is only for manual investigations). The following features of BIP78 are supported: minfeerate additionalfeeoutputindex - but *only* for single change output transactions maxadditionalfeecontribution The receiver does not have nor request payment output substitution. Utxo selection is no longer sophisticated, instead we only choose a single utxo to keep the size increase of the transaction minimal. Thus UIH is not addressed at the moment. Errors returned are in line with BIP78. Sequence numbers are checked by receiver, and kept identical if uniform, otherwise respected. Receiver uses transaction monitor to shut down when the payment is seen. The workflow is almost entirely implemented in jmclient/payjoin.py and the command line script is in scripts/receive-payjoin.py. The setup, including configuration changes for Tor, are documented in docs/PAYJOIN.md, including a user guide video linked.
78 lines
3.2 KiB
Python
Executable file
78 lines
3.2 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
|
|
from optparse import OptionParser
|
|
|
|
import sys
|
|
from twisted.python.log import startLogging
|
|
from twisted.internet import reactor
|
|
from jmbase import get_log, set_logging_level, jmprint
|
|
from jmclient import jm_single, load_program_config, \
|
|
WalletService, open_test_wallet_maybe, get_wallet_path, check_regtest, \
|
|
add_base_options, JMBIP78ReceiverManager
|
|
from jmbase.support import EXIT_FAILURE, EXIT_ARGERROR
|
|
from jmbitcoin import amount_to_sat
|
|
jlog = get_log()
|
|
|
|
def receive_payjoin_main():
|
|
parser = OptionParser(usage='usage: %prog [options] [wallet file] [amount-to-receive]')
|
|
add_base_options(parser)
|
|
parser.add_option('-P', '--hs-port', action='store', type='int',
|
|
dest='hsport', default=80,
|
|
help='port on which to serve the ephemeral hidden service.')
|
|
parser.add_option('-g', '--gap-limit', action='store', type="int",
|
|
dest='gaplimit', default=6,
|
|
help='gap limit for wallet, default=6')
|
|
parser.add_option('-m', '--mixdepth', action='store', type='int',
|
|
dest='mixdepth', default=0,
|
|
help="mixdepth to source coins from")
|
|
parser.add_option('-a',
|
|
'--amtmixdepths',
|
|
action='store',
|
|
type='int',
|
|
dest='amtmixdepths',
|
|
help='number of mixdepths in wallet, default 5',
|
|
default=5)
|
|
|
|
(options, args) = parser.parse_args()
|
|
if len(args) < 2:
|
|
parser.error('Needs a wallet, and a receiving amount in bitcoins or satoshis')
|
|
sys.exit(EXIT_ARGERROR)
|
|
wallet_name = args[0]
|
|
try:
|
|
# amount is stored internally in sats, but will be decimal in URL.
|
|
bip78_amount = amount_to_sat(args[1])
|
|
except:
|
|
parser.error("Invalid receiving amount passed: " + bip78_amount)
|
|
sys.exit(EXIT_FAILURE)
|
|
if bip78_amount < 0:
|
|
parser.error("Receiving amount must be a positive number")
|
|
sys.exit(EXIT_FAILURE)
|
|
load_program_config(config_path=options.datadir)
|
|
|
|
check_regtest()
|
|
|
|
wallet_path = get_wallet_path(wallet_name, None)
|
|
max_mix_depth = max([options.mixdepth, options.amtmixdepths - 1])
|
|
wallet = open_test_wallet_maybe(
|
|
wallet_path, wallet_name, max_mix_depth,
|
|
wallet_password_stdin=options.wallet_password_stdin,
|
|
gap_limit=options.gaplimit)
|
|
wallet_service = WalletService(wallet)
|
|
|
|
while not wallet_service.synced:
|
|
wallet_service.sync_wallet(fast=not options.recoversync)
|
|
wallet_service.startService()
|
|
# having enforced wallet sync, we can check if we have coins
|
|
# to do payjoin in the mixdepth
|
|
if wallet_service.get_balance_by_mixdepth()[options.mixdepth] == 0:
|
|
jlog.error("Cannot do payjoin from mixdepth " + str(
|
|
options.mixdepth) + ", no coins. Shutting down.")
|
|
sys.exit(EXIT_ARGERROR)
|
|
receiver_manager = JMBIP78ReceiverManager(wallet_service, options.mixdepth,
|
|
bip78_amount, options.hsport)
|
|
receiver_manager.start_pj_server_and_tor()
|
|
reactor.run()
|
|
|
|
if __name__ == "__main__":
|
|
receive_payjoin_main()
|
|
jmprint('done')
|