示例#1
0
 def CheckAndSend_Funded(transactionType, outputs, outputPubKeys, details):
     TransactionEncoding.FromStateTransaction(
         transactionType, [], outputs, outputPubKeys,
         details)  # for initial parameter checking
     transactionBuildLayer.startTransactionConstruction()
     swapBillUnspent = transactionBuildLayer.getSwapBillUnspent(state)
     sourceAccounts = []
     while True:
         try:
             state.checkTransaction(transactionType,
                                    outputs=outputs,
                                    transactionDetails=details,
                                    sourceAccounts=sourceAccounts)
         except InsufficientFundsForTransaction:
             pass
         except TransactionFailsAgainstCurrentState as e:
             raise TransactionNotSuccessfulAgainstCurrentState(
                 'Transaction would not complete successfully against current state: '
                 + str(e))
         except BadlyFormedTransaction as e:
             raise ExceptionReportedToUser(
                 'Transaction does not meet protocol constraints: ' +
                 str(e))
         else:
             break
         if not swapBillUnspent:
             raise ExceptionReportedToUser(
                 'Insufficient swapbill for transaction.')
         transactionBuildLayer.swapBillUnspentUsed(swapBillUnspent[0])
         sourceAccounts.append(swapBillUnspent[0])
         swapBillUnspent = swapBillUnspent[1:]
     return CheckAndSend_Common(transactionType, sourceAccounts, outputs,
                                outputPubKeys, details)
示例#2
0
	def signAndSend(self, unsignedTransactionHex, privateKeys, maximumSignedSize):
		#print('\t\tunsignedTransactionHex =', unsignedTransactionHex.__repr__())
		#print('\t\tprivateKeys =', privateKeys.__repr__())
		#print('\t\tmaximumSignedSize =', maximumSignedSize.__repr__())
		#return None
		## lowest level transaction send interface
		signingResult = self._rpcHost.call('signrawtransaction', unsignedTransactionHex)
		if signingResult['complete'] != True:
			privateKeys_WIF = []
			for privateKey in privateKeys:
				privateKeys_WIF.append(Address.PrivateKeyToWIF(privateKey, self._privateKeyAddressVersion))
			signingResult = self._rpcHost.call('signrawtransaction', signingResult['hex'], None, privateKeys_WIF)
		if signingResult['complete'] != True:
			raise SigningFailed("RPC call to signrawtransaction did not set 'complete' to True")
		signedHex = signingResult['hex']
		byteSize = len(signedHex) / 2
		if byteSize > maximumSignedSize:
			raise MaximumSignedSizeExceeded()
		try:
			txID = self._rpcHost.call('sendrawtransaction', signedHex)
		except RPC.RPCFailureException as e:
			raise ExceptionReportedToUser('RPC error sending signed transaction: ' + str(e))
		with open(self._submittedTransactionsFileName, mode='a') as f:
			f.write(txID)
			f.write('\n')
			f.write(signedHex)
			f.write('\n')
		return txID
示例#3
0
def PercentFromString(s):
    result = _fromString(s, _percentDigits)
    if result == 0 or result >= percentDivisor:
        raise ExceptionReportedToUser(
            'Bad percentage string (value must be greater than 0.0 and less than 1.0).'
        )
    return result
示例#4
0
def _fromString(s, decimalDigits):
    if s[0] == '-':
        raise ExceptionReportedToUser(
            'Bad decimal string (negative values are not permitted).')
    pos = s.find('.')
    if pos == -1:
        integerString = s + '0' * decimalDigits
    else:
        digitsAfter = len(s) - 1 - pos
        if digitsAfter > decimalDigits:
            raise ExceptionReportedToUser(
                'Too much precision in decimal string (a maximum of {} digits are allowed after the decimal point).'
                .format(decimalDigits))
        digitsToAdd = decimalDigits - digitsAfter
        integerString = s[:pos] + s[pos + 1:] + '0' * digitsToAdd
    return int(integerString)
示例#5
0
	def getBlockHashAtIndexOrNone(self, blockIndex):
		try:
			return self._rpcHost.call('getblockhash', blockIndex)
		except RPC.RPCFailureWithMessage as e:
			if str(e) == 'Block number out of range.':
				return None
		except RPC.RPCFailureException:
			pass
		raise ExceptionReportedToUser('Unexpected RPC error in call to getblockhash.')
示例#6
0
 def CheckAndSend_UnFunded(transactionType, outputs, outputPubKeys,
                           details):
     TransactionEncoding.FromStateTransaction(
         transactionType, None, outputs, outputPubKeys,
         details)  # for initial parameter checking
     transactionBuildLayer.startTransactionConstruction()
     try:
         state.checkTransaction(transactionType,
                                outputs=outputs,
                                transactionDetails=details,
                                sourceAccounts=None)
     except TransactionFailsAgainstCurrentState as e:
         raise TransactionNotSuccessfulAgainstCurrentState(
             'Transaction would not complete successfully against current state: '
             + str(e))
     except BadlyFormedTransaction as e:
         raise ExceptionReportedToUser(
             'Transaction does not follow protocol rules: ' + str(e))
     return CheckAndSend_Common(transactionType, None, outputs,
                                outputPubKeys, details)
示例#7
0
 def _consumeUnspent(self, txID, vOut):
     unspentAfter = []
     found = None
     for entry in self._unspent:
         if entry['txid'] == txID and entry['vout'] == vOut:
             assert found is None
             found = entry
         else:
             unspentAfter.append(entry)
     if found is None:
         raise ExceptionReportedToUser(
             'RPC error sending signed transaction: (from Mock Host, no unspent found for input, maybe already spent?)'
         )
     pubKeyHash = found['address']
     if self._isHostAddress(pubKeyHash):
         pubKeyHashToBeSigned = None
     else:
         pubKeyHashToBeSigned = pubKeyHash
         assert pubKeyHashToBeSigned is not None
     self._unspent = unspentAfter
     return found['amount'], pubKeyHashToBeSigned
示例#8
0
def Main(startBlockIndex,
         startBlockHash,
         useTestNet,
         commandLineArgs=sys.argv[1:],
         host=None,
         keyGenerator=None,
         out=sys.stdout):
    args = parser.parse_args(commandLineArgs)

    if not path.isdir(args.dataDir):
        raise ExceptionReportedToUser(
            "The following path (specified for data directory parameter) is not a valid path to an existing directory: "
            + args.dataDir)

    dataDir = path.join(args.dataDir, 'swapBillData')
    if not path.exists(dataDir):
        try:
            os.mkdir(dataDir)
        except Exception as e:
            raise ExceptionReportedToUser(
                "Failed to create directory " + dataDir + ":", e)

    if useTestNet:
        addressVersion = b'\x6f'
        privateKeyAddressVersion = b'\xef'
    else:
        addressVersion = b'\x30'
        privateKeyAddressVersion = b'\xbf'

    wallet = Wallet.Wallet(path.join(dataDir, 'wallet.txt'),
                           privateKeyAddressVersion=privateKeyAddressVersion,
                           keyGenerator=keyGenerator
                           )  # litecoin testnet private key address version

    if host is None:
        configFile = args.configFile
        if configFile is None:
            if os.name == 'nt':
                configFile = path.join(path.expanduser("~"), 'AppData',
                                       'Roaming', 'Litecoin', 'litecoin.conf')
            else:
                configFile = path.join(path.expanduser("~"), '.litecoin',
                                       'litecoin.conf')
        with open(configFile, mode='rb') as f:
            configFileBuffer = f.read()
        clientConfig = ParseConfig.Parse(configFileBuffer)
        RPC_HOST = clientConfig.get('externalip', 'localhost')
        try:
            RPC_PORT = clientConfig['rpcport']
        except KeyError:
            if useTestNet:
                RPC_PORT = 19332
            else:
                RPC_PORT = 9332
        assert int(RPC_PORT) > 1 and int(RPC_PORT) < 65535
        try:
            RPC_USER = clientConfig['rpcuser']
            RPC_PASSWORD = clientConfig['rpcpassword']
        except KeyError:
            raise ExceptionReportedToUser(
                'Values for rpcuser and rpcpassword must both be set in your config file.'
            )
        rpcHost = RPC.Host('http://' + RPC_USER + ':' + RPC_PASSWORD + '@' +
                           RPC_HOST + ':' + str(RPC_PORT))
        submittedTransactionsLogFileName = path.join(
            dataDir, 'submittedTransactions.txt')
        host = Host.Host(
            rpcHost=rpcHost,
            addressVersion=addressVersion,
            privateKeyAddressVersion=privateKeyAddressVersion,
            submittedTransactionsLogFileName=submittedTransactionsLogFileName)

    includePending = hasattr(args, 'includepending') and args.includepending

    if args.action == 'get_state_info':
        syncOut = io.StringIO()
        startTime = time.clock()
        state, ownedAccounts = SyncAndReturnStateAndOwnedAccounts(
            dataDir,
            startBlockIndex,
            startBlockHash,
            wallet,
            host,
            includePending=includePending,
            forceRescan=args.forceRescan,
            out=syncOut)
        elapsedTime = time.clock() - startTime
        formattedBalances = {}
        for account in state._balances.balances:
            key = host.formatAccountForEndUser(account)
            formattedBalances[key] = state._balances.balanceFor(account)
        info = {
            'totalCreated': state._totalCreated,
            'atEndOfBlock': state._currentBlockIndex - 1,
            'balances': formattedBalances,
            'syncOutput': syncOut.getvalue(),
            'syncTime': elapsedTime,
            'numberOfLTCBuyOffers': state._ltcBuys.size(),
            'numberOfLTCSellOffers': state._ltcSells.size(),
            'numberOfPendingExchanges': len(state._pendingExchanges),
            'numberOfOutputs': len(ownedAccounts.accounts)
        }
        return info

    state, ownedAccounts = SyncAndReturnStateAndOwnedAccounts(
        dataDir,
        startBlockIndex,
        startBlockHash,
        wallet,
        host,
        includePending=includePending,
        forceRescan=args.forceRescan,
        out=out)

    transactionBuildLayer = TransactionBuildLayer.TransactionBuildLayer(
        host, ownedAccounts)

    def SetFeeAndSend(baseTX, baseTXInputsAmount, unspent):
        change = host.getNewNonSwapBillAddress()
        maximumSignedSize = TransactionFee.startingMaximumSize
        transactionFee = TransactionFee.startingFee
        while True:
            try:
                filledOutTX = BuildHostedTransaction.AddPaymentFeesAndChange(
                    baseTX, baseTXInputsAmount, TransactionFee.dustLimit,
                    transactionFee, unspent, change)
                return transactionBuildLayer.sendTransaction(
                    filledOutTX, maximumSignedSize)
            except Host.MaximumSignedSizeExceeded:
                print("Transaction fee increased.", file=out)
                maximumSignedSize += TransactionFee.sizeStep
                transactionFee += TransactionFee.feeStep

    def CheckAndSend_Common(transactionType, sourceAccounts, outputs,
                            outputPubKeys, details):
        change = host.getNewNonSwapBillAddress()
        print('attempting to send ' + FormatTransactionForUserDisplay.Format(
            host, transactionType, outputs, outputPubKeys, details),
              file=out)
        baseTX = TransactionEncoding.FromStateTransaction(
            transactionType, sourceAccounts, outputs, outputPubKeys, details)
        backingUnspent = transactionBuildLayer.getUnspent()
        baseInputsAmount = 0
        for i in range(baseTX.numberOfInputs()):
            txID = baseTX.inputTXID(i)
            vOut = baseTX.inputVOut(i)
            baseInputsAmount += ownedAccounts.accounts[(txID, vOut)][0]
        txID = SetFeeAndSend(baseTX, baseInputsAmount, backingUnspent)
        return {'transaction id': txID}

    def CheckAndSend_Funded(transactionType, outputs, outputPubKeys, details):
        TransactionEncoding.FromStateTransaction(
            transactionType, [], outputs, outputPubKeys,
            details)  # for initial parameter checking
        transactionBuildLayer.startTransactionConstruction()
        swapBillUnspent = transactionBuildLayer.getSwapBillUnspent(state)
        sourceAccounts = []
        while True:
            try:
                state.checkTransaction(transactionType,
                                       outputs=outputs,
                                       transactionDetails=details,
                                       sourceAccounts=sourceAccounts)
            except InsufficientFundsForTransaction:
                pass
            except TransactionFailsAgainstCurrentState as e:
                raise TransactionNotSuccessfulAgainstCurrentState(
                    'Transaction would not complete successfully against current state: '
                    + str(e))
            except BadlyFormedTransaction as e:
                raise ExceptionReportedToUser(
                    'Transaction does not meet protocol constraints: ' +
                    str(e))
            else:
                break
            if not swapBillUnspent:
                raise ExceptionReportedToUser(
                    'Insufficient swapbill for transaction.')
            transactionBuildLayer.swapBillUnspentUsed(swapBillUnspent[0])
            sourceAccounts.append(swapBillUnspent[0])
            swapBillUnspent = swapBillUnspent[1:]
        return CheckAndSend_Common(transactionType, sourceAccounts, outputs,
                                   outputPubKeys, details)

    def CheckAndSend_UnFunded(transactionType, outputs, outputPubKeys,
                              details):
        TransactionEncoding.FromStateTransaction(
            transactionType, None, outputs, outputPubKeys,
            details)  # for initial parameter checking
        transactionBuildLayer.startTransactionConstruction()
        try:
            state.checkTransaction(transactionType,
                                   outputs=outputs,
                                   transactionDetails=details,
                                   sourceAccounts=None)
        except TransactionFailsAgainstCurrentState as e:
            raise TransactionNotSuccessfulAgainstCurrentState(
                'Transaction would not complete successfully against current state: '
                + str(e))
        except BadlyFormedTransaction as e:
            raise ExceptionReportedToUser(
                'Transaction does not follow protocol rules: ' + str(e))
        return CheckAndSend_Common(transactionType, None, outputs,
                                   outputPubKeys, details)

    def CheckAndReturnPubKeyHash(address):
        try:
            pubKeyHash = host.addressFromEndUserFormat(address)
        except Address.BadAddress as e:
            raise BadAddressArgument(address)
        return pubKeyHash

    if args.action == 'burn':
        amount = Amounts.FromString(args.amount)
        if amount < TransactionFee.dustLimit:
            raise ExceptionReportedToUser('Burn amount is below dust limit.')
        transactionType = 'Burn'
        outputs = ('destination', )
        outputPubKeyHashes = (wallet.addKeyPairAndReturnPubKeyHash(), )
        details = {'amount': amount}
        return CheckAndSend_Funded(transactionType, outputs,
                                   outputPubKeyHashes, details)

    elif args.action == 'pay':
        transactionType = 'Pay'
        outputs = ('change', 'destination')
        outputPubKeyHashes = (wallet.addKeyPairAndReturnPubKeyHash(),
                              CheckAndReturnPubKeyHash(args.toAddress))
        details = {
            'amount': Amounts.FromString(args.amount),
            'maxBlock': state._currentBlockIndex + args.blocksUntilExpiry
        }
        return CheckAndSend_Funded(transactionType, outputs,
                                   outputPubKeyHashes, details)

    elif args.action == 'post_ltc_buy':
        transactionType = 'LTCBuyOffer'
        outputs = ('ltcBuy', )
        outputPubKeyHashes = (wallet.addKeyPairAndReturnPubKeyHash(), )
        details = {
            'swapBillOffered': Amounts.FromString(args.swapBillOffered),
            'exchangeRate': Amounts.PercentFromString(args.exchangeRate),
            'receivingAddress': host.getNewNonSwapBillAddress(),
            'maxBlock': state._currentBlockIndex + args.blocksUntilExpiry
        }
        return CheckAndSend_Funded(transactionType, outputs,
                                   outputPubKeyHashes, details)

    elif args.action == 'post_ltc_sell':
        details = {
            'exchangeRate': Amounts.PercentFromString(args.exchangeRate)
        }
        if args.backerID is None:
            transactionType = 'LTCSellOffer'
            outputs = ('ltcSell', )
            details[
                'maxBlock'] = state._currentBlockIndex + args.blocksUntilExpiry
            details['ltcOffered'] = Amounts.FromString(args.ltcOffered)
        else:
            backerID = int(args.backerID)
            if not backerID in state._ltcSellBackers:
                raise ExceptionReportedToUser(
                    'No backer with the specified ID.')
            backer = state._ltcSellBackers[backerID]
            transactionType = 'BackedLTCSellOffer'
            outputs = ('sellerReceive', )
            ltc = Amounts.FromString(args.ltcOffered)
            if args.includesCommission:
                details['ltcOfferedPlusCommission'] = ltc
            else:
                ltcCommission = ltc * backer.commission // Amounts.percentDivisor
                details['ltcOfferedPlusCommission'] = ltc + ltcCommission
            details['backerIndex'] = int(args.backerID)
            details['backerLTCReceiveAddress'] = backer.ltcReceiveAddress
        outputPubKeyHashes = (wallet.addKeyPairAndReturnPubKeyHash(), )
        return CheckAndSend_Funded(transactionType, outputs,
                                   outputPubKeyHashes, details)

    elif args.action == 'complete_ltc_sell':
        transactionType = 'LTCExchangeCompletion'
        pendingExchangeID = int(args.pendingExchangeID)
        if not pendingExchangeID in state._pendingExchanges:
            raise ExceptionReportedToUser(
                'No pending exchange with the specified ID.')
        exchange = state._pendingExchanges[pendingExchangeID]
        details = {
            'pendingExchangeIndex': pendingExchangeID,
            'destinationAddress': exchange.buyerLTCReceive,
            'destinationAmount': exchange.ltc
        }
        return CheckAndSend_UnFunded(transactionType, (), (), details)

    elif args.action == 'back_ltc_sells':
        transactionType = 'BackLTCSells'
        outputs = ('ltcSellBacker', )
        outputPubKeyHashes = (wallet.addKeyPairAndReturnPubKeyHash(), )
        details = {
            'backingAmount': Amounts.FromString(args.backingSwapBill),
            'transactionsBacked': int(args.transactionsBacked),
            'ltcReceiveAddress': host.getNewNonSwapBillAddress(),
            'commission': Amounts.PercentFromString(args.commission),
            'maxBlock': state._currentBlockIndex + args.blocksUntilExpiry
        }
        return CheckAndSend_Funded(transactionType, outputs,
                                   outputPubKeyHashes, details)

    elif args.action == 'get_receive_address':
        pubKeyHash = wallet.addKeyPairAndReturnPubKeyHash()
        return {'receive_address': host.formatAddressForEndUser(pubKeyHash)}

    elif args.action == 'get_balance':
        total = 0
        for account in ownedAccounts.accounts:
            total += state._balances.balanceFor(account)
        return {'balance': Amounts.ToString(total)}

    elif args.action == 'get_buy_offers':
        result = []
        for offer in state._ltcBuys.getSortedOffers():
            mine = offer.refundAccount in ownedAccounts.tradeOfferChangeCounts
            exchangeAmount = offer._swapBillOffered
            ltc = offer.ltcEquivalent()
            details = {
                'swapbill offered': Amounts.ToString(exchangeAmount),
                'ltc equivalent': Amounts.ToString(ltc),
                'mine': mine
            }
            result.append(('exchange rate',
                           Amounts.PercentToString(offer.rate), details))
        return result

    elif args.action == 'get_sell_offers':
        result = []
        for offer in state._ltcSells.getSortedOffers():
            mine = offer.receivingAccount in ownedAccounts.tradeOfferChangeCounts
            ltc = offer._ltcOffered
            depositAmount = offer._swapBillDeposit
            swapBillEquivalent = offer.swapBillEquivalent()
            details = {
                'ltc offered': Amounts.ToString(ltc),
                'deposit': Amounts.ToString(depositAmount),
                'swapbill equivalent': Amounts.ToString(swapBillEquivalent),
                'mine': mine
            }
            if offer.isBacked:
                details['backer id'] = offer.backerIndex
            result.append(('exchange rate',
                           Amounts.PercentToString(offer.rate), details))
        return result

    elif args.action == 'get_pending_exchanges':
        result = []
        for key in state._pendingExchanges:
            d = {}
            exchange = state._pendingExchanges[key]
            d['I am seller (and need to complete)'] = exchange.sellerAccount in ownedAccounts.tradeOfferChangeCounts
            d['I am buyer (and waiting for payment)'] = exchange.buyerAccount in ownedAccounts.tradeOfferChangeCounts
            d['deposit paid by seller'] = Amounts.ToString(
                exchange.swapBillDeposit)
            d['swap bill paid by buyer'] = Amounts.ToString(
                exchange.swapBillAmount)
            d['outstanding ltc payment amount'] = Amounts.ToString(
                exchange.ltc)
            d['expires on block'] = exchange.expiry
            d['blocks until expiry'] = exchange.expiry - state._currentBlockIndex + 1
            if exchange.backerIndex != -1:
                d['backer id'] = exchange.backerIndex
            result.append(('pending exchange index', key, d))
        return result

    elif args.action == 'get_ltc_sell_backers':
        result = []
        for key in state._ltcSellBackers:
            d = {}
            backer = state._ltcSellBackers[key]
            d['I am backer'] = backer.refundAccount in ownedAccounts.tradeOfferChangeCounts
            d['backing amount'] = Amounts.ToString(backer.backingAmount)
            d['maximum per transaction'] = Amounts.ToString(
                backer.transactionMax)
            d['expires on block'] = backer.expiry
            d['blocks until expiry'] = backer.expiry - state._currentBlockIndex + 1
            d['commission'] = Amounts.PercentToString(backer.commission)
            result.append(('ltc sell backer index', key, d))
        return result

    else:
        parser.print_help()
示例#9
0
def SyncAndReturnStateAndOwnedAccounts(cacheDirectory, startBlockIndex,
                                       startBlockHash, wallet, host,
                                       includePending, forceRescan, out):
    loaded = False
    if not forceRescan:
        try:
            (blockIndex, blockHash,
             state) = PickledCache.Load(cacheDirectory, 'State', stateVersion)
            ownedAccounts = PickledCache.Load(cacheDirectory, 'OwnedAccounts',
                                              ownedAccountsVersion)
            loaded = True
        except PickledCache.LoadFailedException as e:
            print(
                'Failed to load from cache, full index generation required (' +
                str(e) + ')',
                file=out)
    if loaded and host.getBlockHashAtIndexOrNone(blockIndex) != blockHash:
        print(
            'The block corresponding with cached state has been orphaned, full index generation required.',
            file=out)
        loaded = False
    if loaded and not state.startBlockMatches(startBlockHash):
        print(
            'Start config does not match config from loaded state, full index generation required.',
            file=out)
        loaded = False
    if loaded:
        print('Loaded cached state data successfully', file=out)
    else:
        blockIndex = startBlockIndex
        blockHash = host.getBlockHashAtIndexOrNone(blockIndex)
        if blockHash is None:
            raise ExceptionReportedToUser(
                'Block chain has not reached the swapbill start block (' +
                str(startBlockIndex) + ').')
        if blockHash != startBlockHash:
            raise ExceptionReportedToUser(
                'Block hash for swapbill start block does not match.')
        state = State.State(blockIndex, blockHash)
        ownedAccounts = OwnedAccounts.OwnedAccounts()

    print('State update starting from block', blockIndex, file=out)

    toProcess = deque()
    mostRecentHash = blockHash
    while True:
        nextBlockHash = host.getNextBlockHash(mostRecentHash)
        if nextBlockHash is None:
            break
        ## hard coded value used here for number of blocks to lag behind with persistent state
        if len(toProcess) == 20:
            ## advance cached state
            _processBlock(host,
                          state,
                          wallet,
                          ownedAccounts,
                          blockHash,
                          'committed',
                          out=out)
            popped = toProcess.popleft()
            blockIndex += 1
            blockHash = popped
        mostRecentHash = nextBlockHash
        toProcess.append(mostRecentHash)

    PickledCache.Save((blockIndex, blockHash, state), stateVersion,
                      cacheDirectory, 'State')
    PickledCache.Save(ownedAccounts, ownedAccountsVersion, cacheDirectory,
                      'OwnedAccounts')

    print("Committed state updated to start of block {}".format(
        state._currentBlockIndex),
          file=out)

    while len(toProcess) > 0:
        ## advance in memory state
        _processBlock(host,
                      state,
                      wallet,
                      ownedAccounts,
                      blockHash,
                      'in memory',
                      out=out)
        popped = toProcess.popleft()
        blockIndex += 1
        blockHash = popped
    _processBlock(host,
                  state,
                  wallet,
                  ownedAccounts,
                  blockHash,
                  'in memory',
                  out=out)
    blockIndex += 1

    assert state._currentBlockIndex == blockIndex
    print("In memory state updated to end of block {}".format(
        state._currentBlockIndex - 1),
          file=out)

    # note that the best block chain may have changed during the above
    # and so the following set of memory pool transactions may not correspond to the actual block chain endpoint we synchronised to
    # and this may then result in us trying to make double spends, in certain situations
    # the host should then refuse these transactions, and so this is not a disaster
    # (and double spend situations can probably also arise more generally in the case of block chain forks, with it not possible for us to always prevent this)
    # but we can potentially be more careful about this by checking best block chain after getting memory pool transactions
    # and restarting the block chain traversal if this does not match up
    memPoolTransactions = host.getMemPoolTransactions()
    _processTransactions(state, wallet, ownedAccounts, memPoolTransactions,
                         includePending, 'in memory pool', out)

    return state, ownedAccounts