3838from enum import IntEnum
3939import itertools
4040import binascii
41+ import requests
42+ import copy
4143
4244from . import ecc , bitcoin , constants , segwit_addr , bip32
4345from .bip32 import BIP32Node
@@ -1345,12 +1347,14 @@ def scriptpubkey(self) -> Optional[bytes]:
13451347 return None
13461348
13471349 def is_complete (self ) -> bool :
1348- if self .script_sig is not None and self .witness is not None :
1350+ if self .script_sig is not None or self .witness is not None :
13491351 return True
13501352 if self .is_coinbase_input ():
13511353 return True
13521354 if self .script_sig is not None and not Transaction .is_segwit_input (self ):
13531355 return True
1356+ if self .witness is not None and Transaction .is_segwit_input (self ):
1357+ return True
13541358 signatures = list (self .part_sigs .values ())
13551359 s = len (signatures )
13561360 # note: The 'script_type' field is currently only set by the wallet,
@@ -1374,7 +1378,7 @@ def clear_fields_when_finalized():
13741378 self .redeem_script = None
13751379 self .witness_script = None
13761380
1377- if self .script_sig is not None and self .witness is not None :
1381+ if self .script_sig is not None or self .witness is not None :
13781382 clear_fields_when_finalized ()
13791383 return # already finalized
13801384 if self .is_complete ():
@@ -1992,29 +1996,115 @@ def remove_signatures(self):
19921996 self .invalidate_ser_cache ()
19931997
19941998
1995- class PayjoinTransaction (PartialTransaction ):
1999+ class PayjoinTransaction ():
19962000
1997- @classmethod
1998- def from_tx (cls , tx : Transaction ) -> 'PayjoinTransaction' :
1999- res = cls (None )
2000- res ._inputs = [PartialTxInput .from_txin (txin , strip_witness = False ) for txin in tx .inputs ()]
2001- res ._outputs = [PartialTxOutput .from_txout (txout ) for txout in tx .outputs ()]
2002- res .version = tx .version
2003- res .locktime = tx .locktime
2004- return res
2001+ def __init__ (self , payjoin_link = None ):
2002+ self ._version = 1
2003+ self .pj = payjoin_link .get ('pj' ) if payjoin_link else None
2004+ self .pjos = payjoin_link .get ('pjos' ) if payjoin_link else None
20052005
2006- def serialize_as_base64 (self , force_psbt = True ) -> str :
2007- raw_bytes = self .serialize_as_bytes (force_psbt = force_psbt )
2008- return base64 .b64encode (raw_bytes ).decode ('ascii' )
2006+ self .payjoin_original = None
2007+ self .pj_proposal_received = False
20092008
2010- def check_for_encrypted_connection (self ):
2011- pass
20122009
2013- def create_original_psbt (self ):
2014- pass
2010+ def is_available (self ):
2011+ return self . pj is not None
20152012
2016- def check_payjoin_proposal (self ):
2017- pass
2013+ def set_tx (self , tx : PartialTransaction ) -> None :
2014+ if self .is_available ():
2015+ self .tx = copy .deepcopy (tx )
2016+
2017+ def prepare_original_psbt (self ):
2018+ assert not self .pj_proposal_received
2019+ assert self .tx .is_complete ()
2020+ self .payjoin_original = copy .deepcopy (self .tx )
2021+ self .payjoin_original .prepare_for_export_for_coinjoin ()
2022+ self .payjoin_original .convert_all_utxos_to_witness_utxos ()
2023+
2024+ def do_payjoin (self ):
2025+ self .prepare_original_psbt ()
2026+ print ('\n original psbt' ,self .payjoin_original .to_json ())#
2027+ for i ,txin in enumerate (self .payjoin_original .inputs ()):#
2028+ print ('\n txinputs:' ,i ,txin .to_json ()) #
2029+
2030+
2031+ if not self .exchange_payjoin_original ():
2032+ return None
2033+ self .pj_proposal = PartialTransaction .from_raw_psbt (self .payjoin_proposal_b64 )
2034+ self .pj_proposal_received = True
2035+ if not self .validate_payjoin_proposal ():
2036+ return None
2037+ return self .pj_proposal
2038+
2039+
2040+
2041+ def exchange_payjoin_original (self , url = None ):
2042+ """ """
2043+ url = self .pj
2044+ payload = self .payjoin_original .serialize_as_base64 ()
2045+ headers = {'content-type' : 'text/plain' ,
2046+ 'content-length' : str (len (payload ))
2047+ }
2048+ print ('header ' ,headers )#
2049+ try :
2050+ r = requests .post (url , data = payload , headers = headers )
2051+ print (r .status_code )#
2052+ print (r .text )#
2053+ if r .status_code == 200 :
2054+ self .payjoin_proposal_b64 = r .text
2055+ print (self .payjoin_proposal_b64 )#
2056+ else :
2057+ _logger .debug (f"payjoin is flawed { r .text } " )
2058+ return False
2059+ except Exception as e :
2060+ print (repr (e ))#
2061+ return False
2062+ return True
2063+
2064+ @staticmethod
2065+ def validate_inputs_and_outputs (pj_original : PartialTransaction , pj_proposal : PartialTransaction ) -> Optional [bool ]:
2066+ for txin in pj_original .inputs ():
2067+ # check if order is important
2068+ if not txin .prevout .to_str () in [x .prevout .to_str () for x in pj_proposal .inputs ()]:
2069+ # list(payjoin_proposal.inputs()).prevout.to_str():
2070+ raise Exception (f"Inputs from the original payjoin missing in payjoin proposal." )
2071+ for txin in pj_proposal .inputs ():
2072+ if not txin .is_complete () and not txin .prevout .to_str () in [x .prevout .to_str () for x in pj_original .inputs ()]:
2073+ raise Exception (f"Newly added inputs are not signed." )
2074+
2075+ for txout in pj_original .outputs ():
2076+ # check if order is important
2077+ if txout .is_mine and not txout .scriptpubkey .hex () in [x .scriptpubkey .hex () for x in pj_proposal .outputs ()]:
2078+ raise Exception (f"Sender outputs from the original payjoin missing in payjoin proposal." )
2079+ for txout in pj_proposal .outputs ():
2080+ if not txout .scriptpubkey .hex () in [x .scriptpubkey .hex () for x in pj_original .outputs ()]:
2081+ raise Exception (f"Newly added outputs." )
2082+ return True
2083+
2084+
2085+ def validate_payjoin_proposal (self ):
2086+
2087+ if not self .validate_inputs_and_outputs (self .payjoin_original , self .pj_proposal ):
2088+ return False
2089+ """
2090+ if self.payjoin_original.get_fee() > self.payjoin_proposal.get_fee():
2091+ return False
2092+ """
2093+ for i , txin in enumerate (self .tx .inputs ()):
2094+ if self .pj_proposal ._inputs [i ].prevout .to_str () != txin .prevout .to_str ():
2095+ raise Exception (f"Inputs from the original payjoin missing in payjoin proposal2." )
2096+ else :
2097+ print ('\n sig \n ' ,txin .to_json ())
2098+ #procedure for parttxin
2099+ txin .part_sigs = {}
2100+ txin .script_sig = None
2101+ txin .witness = None
2102+ self .pj_proposal ._inputs [i ] = txin
2103+ print ('\n sig2 \n ' , txin .to_json ())
2104+
2105+
2106+ self .pj_proposal .invalidate_ser_cache ()
2107+ return True
20182108
20192109
20202110def pack_bip32_root_fingerprint_and_int_path (xfp : bytes , path : Sequence [int ]) -> bytes :
0 commit comments