@ -9,7 +9,7 @@ from .protocol import (COMMAND_PREFIX, ORDER_KEYS, NICK_HASH_LENGTH,
COMMITMENT_PREFIXES )
from . irc import IRCMessageChannel
from jmbase import ( hextobin , is_hs_uri , get_tor_agent , JMHiddenService ,
from jmbase import ( is_hs_uri , get_tor_agent , JMHiddenService ,
get_nontor_agent , BytesProducer , wrapped_urlparse ,
bdict_sdict_convert , JMHTTPResource )
from jmbase . commands import *
@ -32,7 +32,6 @@ import os
from io import BytesIO
import copy
from functools import wraps
from numbers import Integral
""" Joinmarket application protocol control flow.
For documentation on protocol ( formats , message sequence ) see
@ -158,6 +157,7 @@ class BIP78ReceiverResource(JMHTTPResource):
self . info_callback ( " Shutting down, payjoin negotiation failed. " )
self . shutdown_callback ( )
class HTTPPassThrough ( amp . AMP ) :
""" This class supports passing through
requests over HTTPS or over a socks proxy to a remote
@ -174,7 +174,6 @@ class HTTPPassThrough(amp.AMP):
filterconfig ( not yet defined )
credentials ( not yet defined )
"""
netconfig = json . loads ( netconfig )
self . socks5_host = netconfig [ " socks5_host " ]
self . socks5_port = int ( netconfig [ " socks5_port " ] )
self . servers = [ a for a in netconfig [ " servers " ] if a != " " ]
@ -272,7 +271,6 @@ class HTTPPassThrough(amp.AMP):
class BIP78ServerProtocol ( HTTPPassThrough ) :
@BIP78ReceiverInit . responder
def on_BIP78_RECEIVER_INIT ( self , netconfig ) :
netconfig = json . loads ( netconfig )
self . serving_port = int ( netconfig [ " port " ] )
self . tor_control_host = netconfig [ " tor_control_host " ]
self . tor_control_port = int ( netconfig [ " tor_control_port " ] )
@ -325,7 +323,7 @@ class BIP78ServerProtocol(HTTPPassThrough):
"""
self . post_request = request
d = self . callRemote ( BIP78ReceiverOriginalPSBT , body = body ,
params = json . dumps ( bdict_sdict_convert ( params ) ) )
params = bdict_sdict_convert ( params ) )
self . defaultCallbacks ( d )
@BIP78ReceiverSendProposal . responder
@ -355,7 +353,7 @@ class BIP78ServerProtocol(HTTPPassThrough):
def on_BIP78_SENDER_ORIGINAL_PSBT ( self , body , params ) :
self . postRequest ( body , self . servers [ 0 ] ,
self . bip78_receiver_response ,
params = json . loads ( params ) ,
params = params ,
headers = Headers ( { " Content-Type " : [ " text/plain " ] } ) )
return { " accepted " : True }
@ -475,6 +473,8 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
self . sig_lock = threading . Lock ( )
self . active_orders = { }
self . use_fidelity_bond = False
self . offerlist = None
self . kp = None
def checkClientResponse ( self , response ) :
""" A generic check of client acceptance; any failure
@ -502,11 +502,9 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
If a new message channel configuration is required , the current
one is shutdown in preparation .
"""
self . maker_timeout_sec = int ( maker_timeout_sec )
# used in OrderbookWatch:
self . maker_timeout_sec = maker_timeout_sec
self . minmakers = minmakers
self . dust_threshold = int ( dust_threshold )
self . minmakers = int ( minmakers )
irc_configs = json . loads ( irc_configs )
#(bitcoin) network only referenced in channel name construction
self . network = network
if irc_configs == self . irc_configs :
@ -554,7 +552,7 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
return { ' accepted ' : True }
@JMSetup . responder
def on_JM_SETUP ( self , role , offers , use_fidelity_bond ) :
def on_JM_SETUP ( self , role , initdata , use_fidelity_bond ) :
assert self . jm_state == 0
self . role = role
self . crypto_boxes = { }
@ -568,7 +566,7 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
if self . role == " TAKER " :
self . mcc . pubmsg ( COMMAND_PREFIX + " orderbook " )
elif self . role == " MAKER " :
self . offerlist = json . loads ( offers )
self . offerlist = initdata
self . use_fidelity_bond = use_fidelity_bond
self . mcc . announce_orders ( self . offerlist , None , None , None )
self . jm_state = 1
@ -614,15 +612,14 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
""" Takes the necessary data from the Taker and initiates the Stage 1
interaction with the Makers .
"""
if not ( self . jm_state == 1 and isinstance ( amount , Integral )
and amount > = 0 ) :
if self . jm_state != 1 or amount < 0 :
return { ' accepted ' : False }
self . cjamount = amount
self . commitment = commitment
self . revelation = revelation
#Reset utxo data to null for this new transaction
self . ioauth_data = { }
self . active_orders = json . loads ( filled_offers )
self . active_orders = filled_offers
for nick , offer_dict in self . active_orders . items ( ) :
offer_fill_msg = " " . join ( [ str ( offer_dict [ " oid " ] ) , str ( amount ) ,
self . kp . hex_pk ( ) . decode ( ' ascii ' ) , str ( commitment ) ] )
@ -632,20 +629,19 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
return { ' accepted ' : True }
@JMMakeTx . responder
def on_JM_MAKE_TX ( self , nick_list , txhex ) :
def on_JM_MAKE_TX ( self , nick_list , tx ) :
""" Taker sends the prepared unsigned transaction
to all the Makers in nick_list
"""
if not self . jm_state == 4 :
log . msg ( " Make tx was called in wrong state, rejecting " )
return { ' accepted ' : False }
nick_list = json . loads ( nick_list )
self . mcc . send_tx ( nick_list , txhex )
self . mcc . send_tx ( nick_list , tx )
return { ' accepted ' : True }
@JMPushTx . responder
def on_JM_PushTx ( self , nick , txhex ) :
self . mcc . push_tx ( nick , txhex )
def on_JM_PushTx ( self , nick , tx ) :
self . mcc . push_tx ( nick , tx )
return { ' accepted ' : True }
""" Maker specific responders
@ -659,9 +655,7 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
"""
if self . role != " MAKER " :
return
to_announce = json . loads ( to_announce )
to_cancel = json . loads ( to_cancel )
self . offerlist = json . loads ( offerlist )
self . offerlist = offerlist
if len ( to_cancel ) > 0 :
self . mcc . cancel_orders ( to_cancel )
if len ( to_announce ) > 0 :
@ -682,14 +676,13 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
"""
if not self . role == " MAKER " :
return
if not nick in self . active_orders :
if nick not in self . active_orders :
return
utxos = json . loads ( utxolist )
#completed population of order/offer object
self . active_orders [ nick ] [ " cjaddr " ] = cjaddr
self . active_orders [ nick ] [ " changeaddr " ] = changeaddr
self . active_orders [ nick ] [ " utxos " ] = utxos
msg = str ( " , " . join ( utxos . keys ( ) ) ) + " " + " " . join (
self . active_orders [ nick ] [ " utxos " ] = utxoli st
msg = str ( " , " . join ( utxolist ) ) + " " + " " . join (
[ pubkey , cjaddr , changeaddr , pubkeysig ] )
self . mcc . prepare_privmsg ( nick , " ioauth " , msg )
#In case of *blacklisted (ie already used) commitments, we already
@ -709,7 +702,6 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
broadcast one by one . TODO : could shorten this ,
have more than one sig per message .
"""
sigs = json . loads ( sigs )
for sig in sigs :
self . mcc . prepare_privmsg ( nick , " sig " , sig )
return { " accepted " : True }
@ -798,15 +790,15 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
commitment revelation against the existing commitment ,
which was already stored in active_orders [ nick ] .
"""
if not nick in self . active_orders :
if nick not in self . active_orders :
return
ao = self . active_orders [ nick ]
#ask the client to validate the commitment and prepare the utxo data
d = self . callRemote ( JMAuthReceived ,
nick = nick ,
offer = json . dumps ( ao [ " offer " ] ) ,
offer = ao [ " offer " ] ,
commitment = ao [ " commit " ] ,
revelation = json . dumps ( commitment_revelation ) ,
revelation = commitment_revelation ,
amount = ao [ " amount " ] ,
kphex = ao [ " kp " ] . hex_pk ( ) . decode ( ' ascii ' ) )
self . defaultCallbacks ( d )
@ -831,20 +823,14 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
self . mcc . pubmsg ( " !hp2 " + commitment )
@maker_only
def on_push_tx ( self , nick , txhex ) :
""" Broadcast unquestioningly, except checking
hex format .
def on_push_tx ( self , nick , tx ) :
""" Broadcast unquestioningly
"""
try :
dummy = hextobin ( txhex )
except :
return
d = self . callRemote ( JMTXBroadcast ,
txhex = txhex )
d = self . callRemote ( JMTXBroadcast , tx = tx )
self . defaultCallbacks ( d )
@maker_only
def on_seen_tx ( self , nick , txhex ) :
def on_seen_tx ( self , nick , tx ) :
""" Passes the txhex to the Maker for verification
and signing . Note the security checks occur in Maker .
"""
@ -855,10 +841,7 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
ao = copy . deepcopy ( self . active_orders [ nick ] )
del ao [ " crypto_box " ]
del ao [ " kp " ]
d = self . callRemote ( JMTXReceived ,
nick = nick ,
txhex = txhex ,
offer = json . dumps ( ao ) )
d = self . callRemote ( JMTXReceived , nick = nick , tx = tx , offer = ao )
self . defaultCallbacks ( d )
@taker_only
@ -898,9 +881,7 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
def on_sig ( self , nick , sig ) :
""" Pass signature through to Taker.
"""
d = self . callRemote ( JMSigReceived ,
nick = nick ,
sig = sig )
d = self . callRemote ( JMSigReceived , nick = nick , sig = sig )
self . defaultCallbacks ( d )
def on_error ( self , msg ) :
@ -990,12 +971,12 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
self . jm_state = 3
if not accepted :
#use ioauth data field to return the list of non-responsive makers
nonresponders = [ x for x in self . active_orders . keys ( ) if x not
in self . ioauth_data . keys ( ) ]
nonresponders = [ x for x in self . active_orders
if x not in self . ioauth_data ]
ioauth_data = self . ioauth_data if accepted else nonresponders
d = self . callRemote ( JMFillResponse ,
success = accepted ,
ioauth_data = json . dumps ( ioauth_data ) )
ioauth_data = ioauth_data )
if not accepted :
#Client simply accepts failure TODO
self . defaultCallbacks ( d )
@ -1010,7 +991,7 @@ class JMDaemonServerProtocol(amp.AMP, OrderbookWatch):
either send success + ioauth data if enough makers ,
else send failure to client .
"""
response = True if len ( self . ioauth_data . keys ( ) ) > = self . minmakers else False
response = True if len ( self . ioauth_data ) > = self . minmakers else False
self . respondToIoauths ( response )
def checkUtxosAccepted ( self , accepted ) :