@@ -2364,17 +2364,20 @@ def validate(self):
23642364 if len (self .subsigs ) > constants .multisig_account_limit :
23652365 raise error .MultisigAccountSizeError
23662366
2367- def address (self ):
2368- """Return the multisig account address."""
2367+ def address_bytes (self ):
2368+ """Return the raw bytes of the multisig account address."""
23692369 msig_bytes = (
23702370 bytes (constants .msig_addr_prefix , "utf-8" )
23712371 + bytes ([self .version ])
23722372 + bytes ([self .threshold ])
23732373 )
23742374 for s in self .subsigs :
23752375 msig_bytes += s .public_key
2376- addr = encoding .checksum (msig_bytes )
2377- return encoding .encode_address (addr )
2376+ return encoding .checksum (msig_bytes )
2377+
2378+ def address (self ):
2379+ """Return the multisig account address."""
2380+ return encoding .encode_address (self .address_bytes ())
23782381
23792382 def verify (self , message ):
23802383 """Verify that the multisig is valid for the message."""
@@ -2509,6 +2512,7 @@ def __init__(self, program, args=None):
25092512 self .args = args
25102513 self .sig = None
25112514 self .msig = None
2515+ self .lmsig = None
25122516
25132517 @staticmethod
25142518 def _sanity_check_program (program ):
@@ -2561,6 +2565,8 @@ def dictify(self):
25612565 od ["sig" ] = base64 .b64decode (self .sig )
25622566 elif self .msig :
25632567 od ["msig" ] = self .msig .dictify ()
2568+ elif self .lmsig :
2569+ od ["lmsig" ] = self .lmsig .dictify ()
25642570 return od
25652571
25662572 @staticmethod
@@ -2570,6 +2576,8 @@ def undictify(d):
25702576 lsig .sig = base64 .b64encode (d ["sig" ]).decode ()
25712577 elif "msig" in d :
25722578 lsig .msig = Multisig .undictify (d ["msig" ])
2579+ elif "lmsig" in d :
2580+ lsig .lmsig = Multisig .undictify (d ["lmsig" ])
25732581 return lsig
25742582
25752583 def verify (self , public_key ):
@@ -2584,29 +2592,41 @@ def verify(self, public_key):
25842592 the logic hash or the signature is valid against the sender\
25852593 address), false otherwise
25862594 """
2587- if self .sig and self .msig :
2588- return False
2589-
25902595 try :
25912596 self ._sanity_check_program (self .logic )
25922597 except error .InvalidProgram :
25932598 return False
25942599
2595- to_sign = constants .logic_prefix + self .logic
2596-
2597- if not self .sig and not self .msig :
2598- checksum = encoding .checksum (to_sign )
2599- return checksum == public_key
2600-
26012600 if self .sig :
2601+ if self .msig or self .lmsig :
2602+ return False
26022603 verify_key = VerifyKey (public_key )
26032604 try :
2605+ to_sign = constants .logic_prefix + self .logic
26042606 verify_key .verify (to_sign , base64 .b64decode (self .sig ))
26052607 return True
26062608 except (BadSignatureError , ValueError , TypeError ):
26072609 return False
26082610
2609- return self .msig .verify (to_sign )
2611+ if self .msig :
2612+ if self .sig or self .lmsig :
2613+ return False
2614+ to_sign = constants .logic_prefix + self .logic
2615+ return self .msig .verify (to_sign )
2616+
2617+ if self .lmsig :
2618+ if self .sig or self .msig :
2619+ return False
2620+ to_sign = (
2621+ constants .multisig_logic_prefix
2622+ + self .lmsig .address_bytes ()
2623+ + self .logic
2624+ )
2625+ return self .lmsig .verify (to_sign )
2626+
2627+ # Non-delegated
2628+ to_sign = constants .logic_prefix + self .logic
2629+ return public_key == encoding .checksum (to_sign )
26102630
26112631 def address (self ):
26122632 """
@@ -2626,6 +2646,18 @@ def sign_program(program, private_key):
26262646 signed = signing_key .sign (to_sign )
26272647 return base64 .b64encode (signed .signature ).decode ()
26282648
2649+ @staticmethod
2650+ def multisig_sign_program (program , private_key , multisig ):
2651+ private_key = base64 .b64decode (private_key )
2652+ signing_key = SigningKey (private_key [: constants .key_len_bytes ])
2653+ to_sign = (
2654+ constants .multisig_logic_prefix
2655+ + multisig .address_bytes ()
2656+ + program
2657+ )
2658+ signed = signing_key .sign (to_sign )
2659+ return base64 .b64encode (signed .signature ).decode ()
2660+
26292661 @staticmethod
26302662 def single_sig_multisig (program , private_key , multisig ):
26312663 index = - 1
@@ -2637,7 +2669,7 @@ def single_sig_multisig(program, private_key, multisig):
26372669 break
26382670 if index == - 1 :
26392671 raise error .InvalidSecretKeyError
2640- sig = LogicSig .sign_program (program , private_key )
2672+ sig = LogicSig .multisig_sign_program (program , private_key , multisig )
26412673
26422674 return sig , index
26432675
@@ -2657,7 +2689,7 @@ def sign(self, private_key, multisig=None):
26572689 already been provided
26582690 """
26592691 if not multisig :
2660- if self .msig :
2692+ if self .msig or self . lmsig :
26612693 raise error .LogicSigOverspecifiedSignature
26622694 self .sig = LogicSig .sign_program (self .logic , private_key )
26632695 else :
@@ -2667,7 +2699,7 @@ def sign(self, private_key, multisig=None):
26672699 self .logic , private_key , multisig
26682700 )
26692701 multisig .subsigs [index ].signature = base64 .b64decode (sig )
2670- self .msig = multisig
2702+ self .lmsig = multisig
26712703
26722704 def append_to_multisig (self , private_key ):
26732705 """
@@ -2680,12 +2712,12 @@ def append_to_multisig(self, private_key):
26802712 InvalidSecretKeyError: if no matching private key in multisig\
26812713 object
26822714 """
2683- if self .msig is None :
2715+ if self .lmsig is None :
26842716 raise error .InvalidSecretKeyError
26852717 sig , index = LogicSig .single_sig_multisig (
2686- self .logic , private_key , self .msig
2718+ self .logic , private_key , self .lmsig
26872719 )
2688- self .msig .subsigs [index ].signature = base64 .b64decode (sig )
2720+ self .lmsig .subsigs [index ].signature = base64 .b64decode (sig )
26892721
26902722 def __eq__ (self , other ):
26912723 if not isinstance (other , LogicSig ):
@@ -2695,6 +2727,7 @@ def __eq__(self, other):
26952727 and self .args == other .args
26962728 and self .sig == other .sig
26972729 and self .msig == other .msig
2730+ and self .lmsig == other .lmsig
26982731 )
26992732
27002733
@@ -2744,7 +2777,7 @@ def is_delegated(self) -> bool:
27442777 Returns:
27452778 bool: True if and only if this is a delegated LogicSigAccount.
27462779 """
2747- return bool (self .lsig .sig or self .lsig .msig )
2780+ return bool (self .lsig .sig or self .lsig .msig or self . lsig . lmsig )
27482781
27492782 def verify (self ) -> bool :
27502783 """
@@ -2767,9 +2800,6 @@ def address(self) -> str:
27672800 If the LogicSig is not delegated to another account, this will return an
27682801 escrow address that is the hash of the LogicSig's program code.
27692802 """
2770- if self .lsig .sig and self .lsig .msig :
2771- raise error .LogicSigOverspecifiedSignature
2772-
27732803 if self .lsig .sig :
27742804 if not self .sigkey :
27752805 raise error .LogicSigSigningKeyMissing
@@ -2778,6 +2808,9 @@ def address(self) -> str:
27782808 if self .lsig .msig :
27792809 return self .lsig .msig .address ()
27802810
2811+ if self .lsig .lmsig :
2812+ return self .lsig .lmsig .address ()
2813+
27812814 return self .lsig .address ()
27822815
27832816 def sign_multisig (self , multisig : Multisig , private_key : str ) -> None :
@@ -2874,6 +2907,8 @@ def __init__(
28742907 lsigAddr = transaction .sender
28752908 elif lsig .msig :
28762909 lsigAddr = lsig .msig .address ()
2910+ elif lsig .lmsig :
2911+ lsigAddr = lsig .lmsig .address ()
28772912 else :
28782913 lsigAddr = lsig .address ()
28792914 self .lsig = lsig
0 commit comments