def __init__(self, lboxID, lboxName, script, M, N): self.uniqueIDB58 = lboxID self.shortName = lboxName self.binScript = script self.M = M self.N = N if getTxOutScriptType(script) == CPP_TXOUT_P2SH: self.p2shScrAddr = script_to_scrAddr(self.binScript) elif getTxOutScriptType(script) == CPP_TXOUT_MULTISIG: self.p2shScrAddr = script_to_scrAddr(script_to_p2sh_script(self.binScript)) else: raise BadAddressError('Must initialize [mocked] lbox with MS or P2SH')
def distributeBtc(masterWallet, amount, sendingAddrList): pytx = None setupTheBDM() try: recipValuePairs = [] utxoList = [] for sendingAddr in sendingAddrList: addr160 = sendingAddr.getAddr160() # Make sure the sending addresses are in the masterWallet if not masterWallet.hasAddr(addr160): raise WalletAddressError, 'Address is not in wallet! [%s]' % sendingAddr.getAddrStr() utxoList.extend(masterWallet.getAddrTxOutList(addr160)) for importedAddr in masterWallet.getLinearAddrList(): if importedAddr.chainIndex<0: recipValuePairs.append((importedAddr.getAddr160(),amount)) totalSpend = len(recipValuePairs)*amount fee = calcMinSuggestedFees(utxoList, totalSpend, MIN_RELAY_TX_FEE, len(recipValuePairs))[1] # Get the necessary utxo list selectedUtxoList = PySelectCoins(utxoList, totalSpend, fee) # get total value totalAvailable = sum([u.getValue() for u in selectedUtxoList]) totalChange = totalAvailable - (totalSpend + fee) # Make sure there are funds to cover the transaction. if totalChange < 0: print '***ERROR: you are trying to spend more than your balance!' raise NegativeValueError recipValuePairs.append((masterWallet.getNextUnusedAddress().getAddr160(), totalChange )) # ACR: To support P2SH in general, had to change createFromTxOutSelection # to take full scripts, not just hash160 values. Convert the list # before passing it in scrPairs = [[hash160_to_p2pkhash_script(r), v] for r,v in recipValuePairs] # we're providing a key map for the inputs. pubKeyMap = {} for utxo in selectedUtxoList: scrType = getTxOutScriptType(utxo.getScript()) if scrType in CPP_TXOUT_STDSINGLESIG: scrAddr = utxo.getRecipientScrAddr() a160 = scrAddr_to_hash160(scrAddr)[1] addrObj = masterWallet.getAddrByHash160(a160) if addrObj: pubKeyMap[scrAddr] = addrObj.binPublicKey65.toBinStr() ustx = UnsignedTransaction().createFromTxOutSelection(selectedUtxoList, scrPairs, pubKeyMap) masterWallet.unlock(securePassphrase = SecureBinaryData(getpass('Enter your secret string:'))) # Sign and prepare the final transaction for broadcast masterWallet.signUnsignedTx(ustx) pytx = ustx.getPyTxSignedIfPossible() print '\nSigned transaction to be broadcast using Armory "offline transactions"...' print ustx.serializeAscii() finally: TheBDM.beginCleanShutdown() return pytx
def distributeBtc(masterWallet, amount, sendingAddrList): pytx = None setupTheBDM() try: recipValuePairs = [] utxoList = [] for sendingAddr in sendingAddrList: addr160 = sendingAddr.getAddr160() # Make sure the sending addresses are in the masterWallet if not masterWallet.hasAddr(addr160): raise WalletAddressError, 'Address is not in wallet! [%s]' % sendingAddr.getAddrStr() utxoList.extend(masterWallet.getAddrTxOutList(addr160)) for importedAddr in masterWallet.getLinearAddrList(): if importedAddr.chainIndex<0: recipValuePairs.append((importedAddr.getAddr160(),amount)) totalSpend = len(recipValuePairs)*amount fee = calcMinSuggestedFees(utxoList, totalSpend, MIN_RELAY_TX_FEE, len(recipValuePairs)) # Get the necessary utxo list selectedUtxoList = PySelectCoins(utxoList, totalSpend, fee) # get total value totalAvailable = sum([u.getValue() for u in selectedUtxoList]) totalChange = totalAvailable - (totalSpend + fee) # Make sure there are funds to cover the transaction. if totalChange < 0: print '***ERROR: you are trying to spend more than your balance!' raise NegativeValueError recipValuePairs.append((masterWallet.getNextUnusedAddress().getAddr160(), totalChange )) # ACR: To support P2SH in general, had to change createFromTxOutSelection # to take full scripts, not just hash160 values. Convert the list # before passing it in scrPairs = [[hash160_to_p2pkhash_script(r), v] for r,v in recipValuePairs] # we're providing a key map for the inputs. pubKeyMap = {} for utxo in selectedUtxoList: scrType = getTxOutScriptType(utxo.getScript()) if scrType in CPP_TXOUT_STDSINGLESIG: scrAddr = utxo.getRecipientScrAddr() a160 = scrAddr_to_hash160(scrAddr)[1] addrObj = masterWallet.getAddrByHash160(a160) if addrObj: pubKeyMap[scrAddr] = addrObj.binPublicKey65.toBinStr() ustx = UnsignedTransaction().createFromTxOutSelection(selectedUtxoList, scrPairs, pubKeyMap) masterWallet.unlock(securePassphrase = SecureBinaryData(getpass('Enter your secret string:'))) # Sign and prepare the final transaction for broadcast masterWallet.signUnsignedTx(ustx) pytx = ustx.getPyTxSignedIfPossible() print '\nSigned transaction to be broadcast using Armory "offline transactions"...' print ustx.serializeAscii() finally: TheBDM.beginCleanShutdown() return pytx
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 }