def setUp(self): self.pubKeyList = [self.makePubKey(a) for a in ['\xaa','\x33','\x44']] self.binScriptMS = pubkeylist_to_multisig_script(self.pubKeyList, 2) self.binScriptP2PK = pubkey_to_p2pk_script(self.pubKeyList[0]) self.p2pkHash160 = hash160(self.pubKeyList[0]) self.binScriptP2PKH = hash160_to_p2pkhash_script(self.p2pkHash160) self.binScriptP2SH_MS = script_to_p2sh_script(self.binScriptMS) self.binScriptNonStd = '\x01'*25 pubKeyCompr = '\x02' + '\xaa'*32 self.binScriptP2PKCompr = pubkey_to_p2pk_script(pubKeyCompr) self.binScriptP2PKHCompr = hash160_to_p2pkhash_script(hash160(pubKeyCompr)) # Create two wallets, one will be used, one won't (both in map) self.wlt = MockWallet('AbCd1234z', 'Primary Long-Term Savings Wallet', script_to_scrAddr(self.binScriptP2PKH)) self.wlt2 = MockWallet('BcDe2345y', 'Another long-term savings wallet', script_to_scrAddr(self.binScriptP2PKCompr)) self.wltNull = MockWallet('YyBb7788b', 'This wallet is not used', script_to_scrAddr('\x00')) self.lboxID = calcLockboxID(self.binScriptMS) self.lbox = MockLockbox(self.lboxID, 'My Ultra-Secure Savings Lockbox', self.binScriptMS, 2, 3) # Create some other public keys to create a lockbox that won't be used self.pubKeyListOther = [self.makePubKey(a) for a in ['\x32','\x19','\xfc']] self.binScriptMSOther = pubkeylist_to_multisig_script(self.pubKeyListOther, 2) lboxIDOther = calcLockboxID(self.binScriptMSOther) self.lboxNull = MockLockbox(lboxIDOther, 'This lockbox is not used', self.binScriptMSOther, 3, 5) # When all wallets and lockboxes are avail, these will all be recognized self.validInputStrings = [ \ 'n4TMVYp9BX4yDtjqPgJHrnRPu7zdZ2aLGj', '2N1j5GnC97dXRprEzbQVJJAYXMqCsffdX29', 'Lockbox[%s]' % self.lboxID, 'Lockbox[Bare:%s]' % self.lboxID, binary_to_hex(self.pubKeyList[0]), binary_to_hex(pubKeyCompr)] # a compressed key # Now a few others that should fail to return useful info self.badInputStrings = [ \ 'mpduARVk95R9EXDtbb54fZG1VVQ4CWZmY6', # bad checksum on addr 'Lockbox{%s}' % self.lboxID, 'Lockbox[BARE:%s]' % self.lboxID, # wrong casing 'abc123', '']
def setUp(self): self.pubKeyList = [self.makePubKey(a) for a in ['\xbb','\xaa','\xcc']] self.binScriptMS = pubkeylist_to_multisig_script(self.pubKeyList, 2) self.binScriptP2PK = pubkey_to_p2pk_script(self.pubKeyList[0]) self.p2pkHash160 = hash160(self.pubKeyList[0]) self.binScriptP2PKH = hash160_to_p2pkhash_script(self.p2pkHash160) self.binScriptP2SH_MS = script_to_p2sh_script(self.binScriptMS) self.binScriptNonStd = '\x01'*25 # Create two wallets, one will be used, one won't (both in map) self.wlt = MockWallet('AbCd1234z', 'Primary Long-Term Savings Wallet', script_to_scrAddr(self.binScriptP2PKH)) self.wltNull = MockWallet('YyBb7788b', 'This wallet is not used', script_to_scrAddr('\x00')) # Create two lockboxes, one will be used, one won't (both in map) self.lboxID = calcLockboxID(self.binScriptMS) self.lbox = MockLockbox(self.lboxID, 'My Ultra-Secure Savings Lockbox', self.binScriptP2SH_MS, 2, 3) # Create some other public keys to create a lockbox that won't be used self.pubKeyListOther = [self.makePubKey(a) for a in ['\x32','\x19','\xfc']] self.binScriptMSOther = pubkeylist_to_multisig_script(self.pubKeyListOther, 2) self.lboxNull = MockLockbox('ZzCc8899a', 'This lockbox is not used', self.binScriptMSOther, 3, 5) self.binScriptTestList = [ \ [self.binScriptP2PKH, 'mfi7ZGQSEUSudTFeXrSnZUpo9u1dAtTBon'], [self.binScriptP2PK, 'mfi7ZGQSEUSudTFeXrSnZUpo9u1dAtTBon'], [self.binScriptMS, '2N8pebcLPhfPhJpre5v1biNNTXxd9Qt3rcx'], [self.binScriptP2SH_MS, '2N8pebcLPhfPhJpre5v1biNNTXxd9Qt3rcx'], [self.binScriptNonStd, '2NEF6nBRNhSDcsZu8UB877nnqwCWdKXbZXq']] self.maxLengthTestList = [256, 60, 58, 54, 45, 32]
def getDisplayStringForScript(binScript, wltMap, lboxList, maxChars=256, doBold=0, prefIDOverAddr=False, lblTrunc=12, lastTrunc=12): """ NOTE: This was originally in ArmoryQt.py, but we really needed this to be more widely accessible. And it's easier to test when this is in ArmoryUtils. Yes, I realize that it's awkward that we have wltMap {WltID-->Wallet} but we have a lboxList [Lbox0, Lbox1, ...]. I will have to standardize the member names and lookup structures. We have a script and want to show the user something useful about it. We have a couple different display modes, since some formats are space- constrained. For instance, the DlgConfirmSend dialog only has space for 34 letters, as it was designed to be showing only an address string. This is similar to self.getContribStr, but that method is more focused on identifying participants of a multi-sig transaction. It almost works here, but we need one that's more general. Consider a 3-of-5 lockbox with ID Abcd1234z and label "My long-term savings super-secure" and p2sh address 2m83zQr9981pmKnrSwa32 10 20 30 40 50 60 70 | | | | | | | | 256 Lockbox 3-of-5 "Long-term savings" (2m83zQr9981p...) 256 Lockbox 3-of-5 "Long-term savings" (Abcd1234z) 50 Lockbox 3-of-5 "Long-term sa..." (2m83zQr9981p...) 35 Lockbox 3-of-5 "Long-term savings" 32 Lockbox 3-of-5 "Long-term sa..." 256 Wallet "LabelLabelLabelLabelLabelLabel" (1j93CnrAA3xn...) 256 Wallet "LabelLabelLabelLabelLabelLabel" (Abcd1234z) 50 Wallet "LabelLabelLa..." (1j93CnrAA3xn...) 35 Wallet "LabelLabelLa..." So we will always show the type and the label (possibly truncated). If we can show the ID or Addr (whichever is preferred) we will do so, otherwise it gets left out. If our truncation leaves the string too short, we usually try to add back in a few chars. It's not perfect, but it seems to work reliably. The doBold arg indicates that we want to add html bold tags around the first N parts of the return. This is applied after the length calculations are performed, as bolding will have a very small impact on width """ if maxChars<32: LOGERROR('getDisplayStringForScript() req at least 32 bytes output') return None scriptType = getTxOutScriptType(binScript) scrAddr = script_to_scrAddr(binScript) if scriptType != CPP_TXOUT_OPRETURN: wlt = None for iterID,iterWlt in wltMap.iteritems(): if iterWlt.hasScrAddr(scrAddr): wlt = iterWlt break lbox = None if wlt is None: searchScrAddr = scrAddr if scriptType==CPP_TXOUT_MULTISIG: searchScrAddr = script_to_scrAddr(script_to_p2sh_script(binScript)) for iterLbox in lboxList: if iterLbox.hasScrAddr(searchScrAddr): lbox = iterLbox break else: wlt = None lbox = None # Return these with the display string wltID = wlt.uniqueIDB58 if wlt else None lboxID = lbox.uniqueIDB58 if lbox else None if wlt is not None: strType = 'Wallet:' strLabel = wlt.labelName addrStr = None if scriptType in CPP_TXOUT_HAS_ADDRSTR: addrStr = scrAddr_to_addrStr(scrAddr) strLast = wlt.uniqueIDB58 if addrStr is None else addrStr strLast = wlt.uniqueIDB58 if prefIDOverAddr else strLast elif lbox is not None: strType = 'Lockbox %d-of-%d:' % (lbox.M, lbox.N) strLabel = lbox.shortName addrStr = scrAddr_to_addrStr(scrAddr) strLast = lbox.uniqueIDB58 if prefIDOverAddr else addrStr else: strType = '' strLabel = '' strLast = '' addrStr = None def truncateStr(theStr, maxLen): if len(theStr) <= maxLen: return theStr else: return theStr[:maxLen-3] + '...' if len(strType) > 0: # We have something to display... do it and return lenType = len(strType) lenLabel = len(strLabel) + 3 lenLast = len(strLast) + 3 lenLabelTrunc = min(lenLabel + 3, lblTrunc + 6) lenLastTrunc = min(lenLast + 3, lastTrunc + 6) if lenType + lenLabel + lenLast <= maxChars: strLabel = ' "%s"' % strLabel strLast = ' (%s)' % strLast if lenLast>0 else '' elif lenType + lenLabel + lenLastTrunc <= maxChars: extraChars = maxChars - (lenType + lenLabel + lenLastTrunc) lastTrunc += extraChars strLabel = ' "%s"' % strLabel strLast = ' (%s)' % truncateStr(strLast,lastTrunc) if lenLast>0 else '' elif lenType + lenLabelTrunc + lenLastTrunc <= maxChars: extraChars = maxChars - (lenType + lenLabelTrunc + lenLastTrunc) lblTrunc += extraChars/2 lastTrunc += extraChars/2 strLabel = ' "%s"' % truncateStr(strLabel, lblTrunc) strLast = ' (%s)' % truncateStr(strLast, lastTrunc) if lenLast>0 else '' elif lenType + lenLabel <= maxChars: strLabel = ' "%s"' % strLabel strLast = '' elif lenType + lenLabelTrunc <= maxChars: lblTrunc += maxChars - (lenType + lenLabelTrunc) strLabel = ' "%s"' % truncateStr(strLabel, lblTrunc) strLast = '' else: # Total last resort, actually leave the label out... # If we still can't fit it... too bad, we have to show something lastTrunc = max(maxChars - (lenType + 4), 6) strLabel = '' strLast = ' %s' % truncateStr(strLast, lastTrunc) if doBold>0: strType = '<b>%s</b>' % strType if doBold>1: strLabel = '<b>%s</b>' % strLabel if doBold>2: strLast = '<b>%s</b>' % strLast displayStr = ''.join([strType, strLabel, strLast]) return {'String': displayStr, 'WltID': wltID, 'LboxID': lboxID, 'AddrStr': addrStr} # If we're here, it didn't match any loaded wlt or lockbox dispStr = '' if scriptType in CPP_TXOUT_HAS_ADDRSTR: addrStr = script_to_addrStr(binScript) if len(addrStr) <= maxChars: dispStr = addrStr else: dispStr = addrStr[:maxChars-3] + '...' elif scriptType == CPP_TXOUT_MULTISIG: M,N,a160s,pubs = getMultisigScriptInfo(binScript) lbID = calcLockboxID(binScript) dispStr = 'Unknown %d-of-%d (%s)' % (M,N,lbID) addrStr = script_to_addrStr(script_to_p2sh_script(binScript)) if len(dispStr) + len(addrStr) + 3 <= maxChars: dispStr += ' [%s]' % addrStr elif len(dispStr) + lastTrunc + 6 <= maxChars: dispStr += ' [%s...]' % addrStr[:lastTrunc] elif len(binScript) == 0: dispStr = 'Unknown Input' elif scriptType == CPP_TXOUT_OPRETURN: msgPos = 2 if len(binScript) > 77: msgPos = 3 msgStr = binScript[msgPos:] dispStr = 'Msg (%d bytes): %s' % (len(binScript) - msgPos, msgStr) else: addrStr = script_to_addrStr(script_to_p2sh_script(binScript)) dispStr = 'Non-Standard: %s' % addrStr if len(dispStr) > maxChars: dispStr = dispStr[:maxChars-3] + '...' return {'String': dispStr, 'WltID': None, 'LboxID': None, 'AddrStr': addrStr}
def getDisplayStringForScript(binScript, wltMap, lboxList, maxChars=256, doBold=0, prefIDOverAddr=False, lblTrunc=12, lastTrunc=12): """ NOTE: This was originally in ArmoryQt.py, but we really needed this to be more widely accessible. And it's easier to test when this is in ArmoryUtils. Yes, I realize that it's awkward that we have wltMap {WltID-->Wallet} but we have a lboxList [Lbox0, Lbox1, ...]. I will have to standardize the member names and lookup structures. We have a script and want to show the user something useful about it. We have a couple different display modes, since some formats are space- constrained. For instance, the DlgConfirmSend dialog only has space for 34 letters, as it was designed to be showing only an address string. This is similar to self.getContribStr, but that method is more focused on identifying participants of a multi-sig transaction. It almost works here, but we need one that's more general. Consider a 3-of-5 lockbox with ID Abcd1234z and label "My long-term savings super-secure" and p2sh address 2m83zQr9981pmKnrSwa32 10 20 30 40 50 60 70 | | | | | | | | 256 Lockbox 3-of-5 "Long-term savings" (2m83zQr9981p...) 256 Lockbox 3-of-5 "Long-term savings" (Abcd1234z) 50 Lockbox 3-of-5 "Long-term sa..." (2m83zQr9981p...) 35 Lockbox 3-of-5 "Long-term savings" 32 Lockbox 3-of-5 "Long-term sa..." 256 Wallet "LabelLabelLabelLabelLabelLabel" (1j93CnrAA3xn...) 256 Wallet "LabelLabelLabelLabelLabelLabel" (Abcd1234z) 50 Wallet "LabelLabelLa..." (1j93CnrAA3xn...) 35 Wallet "LabelLabelLa..." So we will always show the type and the label (possibly truncated). If we can show the ID or Addr (whichever is preferred) we will do so, otherwise it gets left out. If our truncation leaves the string too short, we usually try to add back in a few chars. It's not perfect, but it seems to work reliably. The doBold arg indicates that we want to add html bold tags around the first N parts of the return. This is applied after the length calculations are performed, as bolding will have a very small impact on width """ if maxChars < 32: LOGERROR('getDisplayStringForScript() req at least 32 bytes output') return None scriptType = getTxOutScriptType(binScript) scrAddr = script_to_scrAddr(binScript) wlt = None for iterID, iterWlt in wltMap.iteritems(): if iterWlt.hasScrAddr(scrAddr): wlt = iterWlt break lbox = None if wlt is None: searchScrAddr = scrAddr if scriptType == CPP_TXOUT_MULTISIG: searchScrAddr = script_to_scrAddr(script_to_p2sh_script(binScript)) for iterLbox in lboxList: if searchScrAddr == iterLbox.p2shScrAddr: lbox = iterLbox break # Return these with the display string wltID = wlt.uniqueIDB58 if wlt else None lboxID = lbox.uniqueIDB58 if lbox else None if wlt is not None: strType = 'Wallet:' strLabel = wlt.labelName addrStr = None if scriptType in CPP_TXOUT_HAS_ADDRSTR: addrStr = scrAddr_to_addrStr(scrAddr) strLast = wlt.uniqueIDB58 if addrStr is None else addrStr strLast = wlt.uniqueIDB58 if prefIDOverAddr else strLast elif lbox is not None: strType = 'Lockbox %d-of-%d:' % (lbox.M, lbox.N) strLabel = lbox.shortName addrStr = scrAddr_to_addrStr(lbox.p2shScrAddr) strLast = lbox.uniqueIDB58 if prefIDOverAddr else addrStr else: strType = '' strLabel = '' strLast = '' addrStr = None def truncateStr(theStr, maxLen): if len(theStr) <= maxLen: return theStr else: return theStr[:maxLen - 3] + '...' if len(strType) > 0: # We have something to display... do it and return lenType = len(strType) lenLabel = len(strLabel) + 3 lenLast = len(strLast) + 3 lenLabelTrunc = min(lenLabel + 3, lblTrunc + 6) lenLastTrunc = min(lenLast + 3, lastTrunc + 6) if lenType + lenLabel + lenLast <= maxChars: strLabel = ' "%s"' % strLabel strLast = ' (%s)' % strLast if lenLast > 0 else '' elif lenType + lenLabel + lenLastTrunc <= maxChars: extraChars = maxChars - (lenType + lenLabel + lenLastTrunc) lastTrunc += extraChars strLabel = ' "%s"' % strLabel strLast = ' (%s)' % truncateStr(strLast, lastTrunc) if lenLast > 0 else '' elif lenType + lenLabelTrunc + lenLastTrunc <= maxChars: extraChars = maxChars - (lenType + lenLabelTrunc + lenLastTrunc) lblTrunc += extraChars / 2 lastTrunc += extraChars / 2 strLabel = ' "%s"' % truncateStr(strLabel, lblTrunc) strLast = ' (%s)' % truncateStr(strLast, lastTrunc) if lenLast > 0 else '' elif lenType + lenLabel <= maxChars: strLabel = ' "%s"' % strLabel strLast = '' elif lenType + lenLabelTrunc <= maxChars: lblTrunc += maxChars - (lenType + lenLabelTrunc) strLabel = ' "%s"' % truncateStr(strLabel, lblTrunc) strLast = '' else: # Total last resort, actually leave the label out... # If we still can't fit it... too bad, we have to show something lastTrunc = max(maxChars - (lenType + 4), 6) strLabel = '' strLast = ' %s' % truncateStr(strLast, lastTrunc) if doBold > 0: strType = '<b>%s</b>' % strType if doBold > 1: strLabel = '<b>%s</b>' % strLabel if doBold > 2: strLast = '<b>%s</b>' % strLast displayStr = ''.join([strType, strLabel, strLast]) return { 'String': displayStr, 'WltID': wltID, 'LboxID': lboxID, 'AddrStr': addrStr } # If we're here, it didn't match any loaded wlt or lockbox dispStr = '' if scriptType in CPP_TXOUT_HAS_ADDRSTR: addrStr = script_to_addrStr(binScript) if len(addrStr) <= maxChars: dispStr = addrStr else: dispStr = addrStr[:maxChars - 3] + '...' elif scriptType == CPP_TXOUT_MULTISIG: M, N, a160s, pubs = getMultisigScriptInfo(binScript) lbID = calcLockboxID(binScript) dispStr = 'Unknown %d-of-%d (%s)' % (M, N, lbID) addrStr = script_to_addrStr(script_to_p2sh_script(binScript)) if len(dispStr) + len(addrStr) + 3 <= maxChars: dispStr += ' [%s]' % addrStr elif len(dispStr) + lastTrunc + 6 <= maxChars: dispStr += ' [%s...]' % addrStr[:lastTrunc] elif len(binScript) == 0: dispStr = 'Unknown Input' else: addrStr = script_to_addrStr(script_to_p2sh_script(binScript)) dispStr = 'Non-Standard: %s' % addrStr if len(dispStr) > maxChars: dispStr = dispStr[:maxChars - 3] + '...' return { 'String': dispStr, 'WltID': None, 'LboxID': None, 'AddrStr': addrStr }