Repository: counterpartyxcp/counterparty-lib
Issue: allow_unconfirmed_inputs: True does not appear to take effect.

As per the slack channel..

Previously, when using V9.49.3, I was able to chain several dozen transactions together using unconfirmed inputs, which is what the FLDC and LTBCOIN projects rely on to be able to distribute coins out weekly/daily. Since upgrading to v9.51, this no longer appears to work and I just run into constant "Insufficient BTC (try using --unconfirmed)" errors, despite allow_unconfirmed_inputs being set to true. It will send one or two transactions, and then fail for the rest of the batch until those previous ones confirm, then it will send one or two more, and so on... you can see how that quickly makes things difficult when trying to send several hundred transactions. 

This is the payload defined in the script im using, which hasn't changed at all since it was working with 9.49.3 - other than replacing sign_raw and send_raw commands with signrawtransaction and sendrawtransaction in native bitcoind. 

```
payload = {                                                             
           "method": "create_send",                                            
           "params": {'source': source, 'destination': destination, 'asset': asset, 'quantity': int(quantity), 'fee': int(fee), 'allow_unconfirmed_inputs': True, 'regular_dust_size': int(sys.argv[2]), 'multisig_dust_size': int(sys.argv[2]), 'pubkey': sys.argv[3], 'encoding': 'multisig'},
           "jsonrpc": "2.0",                                                   
           "id": 0                                                             
       }
```

any ideas?


Changed files:
counterpartylib/server.py, counterpartylib/lib/config.py, counterpartylib/lib/backend/__init__.py, counterpartylib/lib/blocks.py

diff --git a/counterpartylib/lib/backend/__init__.py b/counterpartylib/lib/backend/__init__.py
index 95d3f7cd..6bb17d1a 100644
--- a/counterpartylib/lib/backend/__init__.py
+++ b/counterpartylib/lib/backend/__init__.py
@@ -135,31 +135,28 @@ def get_unspent_txouts(source, unconfirmed=False, multisig_inputs=False):
     """
     global MEMPOOL_CACHE_INITIALIZED
 
-    if unconfirmed:
-        if not config.ENABLE_MEMPOOL:
-            raise MempoolError('Mempool is not enabled.')
-        elif not MEMPOOL_CACHE_INITIALIZED:
-            raise MempoolError('Mempool is not yet ready; please try again in a few minutes.')
+    if unconfirmed and not MEMPOOL_CACHE_INITIALIZED:
+        raise MempoolError('Mempool is not yet ready; please try again in a few minutes.')
 
     # Get all outputs.
     logger.debug('Getting outputs.')
     if script.is_multisig(source):
         pubkeyhashes = script.pubkeyhash_array(source)
-        raw_transactions = searchrawtransactions(pubkeyhashes[1], unconfirmed=unconfirmed)
+        raw_transactions = searchrawtransactions(pubkeyhashes[1], unconfirmed=True) # unconfirmed=True to prune unconfirmed spent outputs
     else:
         pubkeyhashes = [source]
-        raw_transactions = searchrawtransactions(source, unconfirmed=unconfirmed)
+        raw_transactions = searchrawtransactions(source, unconfirmed=True)
 
     # Change format.
     # TODO: Slow.
     logger.debug('Formatting outputs.')
-    outputs = []
+    outputs = {}
     for tx in raw_transactions:
         for vout in tx['vout']:
             txid = tx['txid']
             confirmations = tx['confirmations'] if 'confirmations' in tx else 0
-            outkey = '{}{}'.format(txid, vout['n'])
-            if outkey not in outputs or outputs[outkey]['confirmations'] < confirmations:
+            outkey = '{}{}'.format(txid, vout['n']) # edge case: avoid duplicate output
+            if outkey not in outputs or outputs[outkey]['confirmations'] < confirmations: 
                 coin = {
                         'amount': float(vout['value']),
                         'confirmations': confirmations,
@@ -167,8 +164,8 @@ def get_unspent_txouts(source, unconfirmed=False, multisig_inputs=False):
                         'txid': txid,
                         'vout': vout['n']
                        }
-                outputs.append(coin)
-    outputs = sorted(outputs, key=lambda x: x['confirmations'])
+                outputs[outkey] = coin
+    outputs = sorted(outputs.values(), key=lambda x: x['confirmations'])
 
     # Prune unspendable.
     logger.debug('Pruning unspendable outputs.')
@@ -185,7 +182,7 @@ def get_unspent_txouts(source, unconfirmed=False, multisig_inputs=False):
     unspent = sorted(unspent, key=lambda x: x['txid'])
 
     # Remove unconfirmed txouts, if desired.
-    if unconfirmed:
+    if not unconfirmed:
         unspent = [output for output in unspent if output['confirmations'] > 0]
     else:
         # Hackish: Allow only inputs which are either already confirmed or were seen only recently. (Skip outputs from slow‐to‐confirm transanctions.)
diff --git a/counterpartylib/lib/blocks.py b/counterpartylib/lib/blocks.py
index bfd550af..3a75018c 100644
--- a/counterpartylib/lib/blocks.py
+++ b/counterpartylib/lib/blocks.py
@@ -909,8 +909,7 @@ def follow(db):
     not_supported_sorted = collections.deque()
     # ^ Entries in form of (block_index, tx_hash), oldest first. Allows for easy removal of past, unncessary entries
     mempool_initialised = False
-    if config.ENABLE_MEMPOOL:
-        backend.init_mempool_cache()
+    backend.init_mempool_cache()
     cursor = db.cursor()
     # a reorg can happen without the block count increasing, or even for that
     # matter, with the block count decreasing. This should only delay
@@ -1027,98 +1026,96 @@ def follow(db):
             block_count = backend.getblockcount()
             block_index += 1
 
-        else:
-            if config.ENABLE_MEMPOOL:    
-                # First mempool fill for session?
-                if mempool_initialised:
-                    logger.debug('Updating mempool.')
-                else:
-                    logger.debug('Initialising mempool.')
-
-                # Get old mempool.
-                old_mempool = list(cursor.execute('''SELECT * FROM mempool'''))
-                old_mempool_hashes = [message['tx_hash'] for message in old_mempool]
-
-                # Fake values for fake block.
-                curr_time = int(time.time())
-                mempool_tx_index = tx_index
-
-                # For each transaction in Bitcoin Core mempool, if it’s new, create
-                # a fake block, a fake transaction, capture the generated messages,
-                # and then save those messages.
-                # Every transaction in mempool is parsed independently. (DB is rolled back after each one.)
-                mempool = []
-                for tx_hash in backend.getrawmempool():
-
-                    # If already in mempool, copy to new one.
-                    if tx_hash in old_mempool_hashes:
-                        for message in old_mempool:
-                            if message['tx_hash'] == tx_hash:
-                                mempool.append((tx_hash, message))
+        else:   
+            # First mempool fill for session?
+            if mempool_initialised:
+                logger.debug('Updating mempool.')
+            else:
+                logger.debug('Initialising mempool.')
+
+            # Get old mempool.
+            old_mempool = list(cursor.execute('''SELECT * FROM mempool'''))
+            old_mempool_hashes = [message['tx_hash'] for message in old_mempool]
+
+            # Fake values for fake block.
+            curr_time = int(time.time())
+            mempool_tx_index = tx_index
+
+            # For each transaction in Bitcoin Core mempool, if it’s new, create
+            # a fake block, a fake transaction, capture the generated messages,
+            # and then save those messages.
+            # Every transaction in mempool is parsed independently. (DB is rolled back after each one.)
+            mempool = []
+            for tx_hash in backend.getrawmempool():
+
+                # If already in mempool, copy to new one.
+                if tx_hash in old_mempool_hashes:
+                    for message in old_mempool:
+                        if message['tx_hash'] == tx_hash:
+                            mempool.append((tx_hash, message))
+
+                # If already skipped, skip it again.
+                elif tx_hash not in not_supported:
+
+                    # Else: list, parse and save it.
+                    try:
+                        with db:
+                            # List the fake block.
+                            cursor.execute('''INSERT INTO blocks(
+                                                block_index,
+                                                block_hash,
+                                                block_time) VALUES(?,?,?)''',
+                                                (config.MEMPOOL_BLOCK_INDEX,
+                                                 config.MEMPOOL_BLOCK_HASH,
+                                                 curr_time)
+                                          )
+
+                            # List transaction.
+                            try:    # Sometimes the transactions can’t be found: `{'code': -5, 'message': 'No information available about transaction'} Is txindex enabled in Bitcoind?`
+                                mempool_tx_index = list_tx(db, None, block_index, curr_time, tx_hash, mempool_tx_index)
+                            except backend.addrindex.BackendRPCError:
+                                raise MempoolError
 
-                    # If already skipped, skip it again.
-                    elif tx_hash not in not_supported:
-
-                        # Else: list, parse and save it.
-                        try:
-                            with db:
-                                # List the fake block.
-                                cursor.execute('''INSERT INTO blocks(
-                                                    block_index,
-                                                    block_hash,
-                                                    block_time) VALUES(?,?,?)''',
-                                                    (config.MEMPOOL_BLOCK_INDEX,
-                                                     config.MEMPOOL_BLOCK_HASH,
-                                                     curr_time)
-                                              )
-
-                                # List transaction.
-                                try:    # Sometimes the transactions can’t be found: `{'code': -5, 'message': 'No information available about transaction'} Is txindex enabled in Bitcoind?`
-                                    mempool_tx_index = list_tx(db, None, block_index, curr_time, tx_hash, mempool_tx_index)
-                                except backend.addrindex.BackendRPCError:
-                                    raise MempoolError
-
-                                # Parse transaction.
-                                cursor.execute('''SELECT * FROM transactions \\
-                                                  WHERE tx_hash = ?''',
-                                               (tx_hash,))
-                                transactions = list(cursor)
-                                if transactions:
-                                    assert len(transactions) == 1
-                                    transaction = transactions[0]
-                                    supported = parse_tx(db, transaction)
-                                    if not supported:
-                                        not_supported[tx_hash] = ''
-                                        not_supported_sorted.append((block_index, tx_hash))
-                                else:
-                                    # If a transaction hasn’t been added to the
-                                    # table `transactions`, then it’s not a
-                                    # Counterparty transaction.
+                            # Parse transaction.
+                            cursor.execute('''SELECT * FROM transactions \\
+                                              WHERE tx_hash = ?''',
+                                           (tx_hash,))
+                            transactions = list(cursor)
+                            if transactions:
+                                assert len(transactions) == 1
+                                transaction = transactions[0]
+                                supported = parse_tx(db, transaction)
+                                if not supported:
                                     not_supported[tx_hash] = ''
                                     not_supported_sorted.append((block_index, tx_hash))
-                                    raise MempoolError
-
-                                # Save transaction and side‐effects in memory.
-                                cursor.execute('''SELECT * FROM messages WHERE block_index = ?''', (config.MEMPOOL_BLOCK_INDEX,))
-                                for message in list(cursor):
-                                    mempool.append((tx_hash, message))
-
-                                # Rollback.
+                            else:
+                                # If a transaction hasn’t been added to the
+                                # table `transactions`, then it’s not a
+                                # Counterparty transaction.
+                                not_supported[tx_hash] = ''
+                                not_supported_sorted.append((block_index, tx_hash))
                                 raise MempoolError
-                        except MempoolError:
-                            pass
 
-                # Re‐write mempool messages to database.
-                with db:
-                    cursor.execute('''DELETE FROM mempool''')
-                    for message in mempool:
-                        tx_hash, new_message = message
-                        new_message['tx_hash'] = tx_hash
-                        cursor.execute('''INSERT INTO mempool VALUES(:tx_hash, :command, :category, :bindings, :timestamp)''', (new_message))
+                            # Save transaction and side‐effects in memory.
+                            cursor.execute('''SELECT * FROM messages WHERE block_index = ?''', (config.MEMPOOL_BLOCK_INDEX,))
+                            for message in list(cursor):
+                                mempool.append((tx_hash, message))
 
-                # Wait
-                mempool_initialised = True
+                            # Rollback.
+                            raise MempoolError
+                    except MempoolError:
+                        pass
 
+            # Re‐write mempool messages to database.
+            with db:
+                cursor.execute('''DELETE FROM mempool''')
+                for message in mempool:
+                    tx_hash, new_message = message
+                    new_message['tx_hash'] = tx_hash
+                    cursor.execute('''INSERT INTO mempool VALUES(:tx_hash, :command, :category, :bindings, :timestamp)''', (new_message))
+
+            # Wait
+            mempool_initialised = True
             db.wal_checkpoint(mode=apsw.SQLITE_CHECKPOINT_PASSIVE)
             time.sleep(config.BACKEND_POLL_INTERVAL)
 
diff --git a/counterpartylib/lib/config.py b/counterpartylib/lib/config.py
index 04dfa15e..ff4bc107 100644
--- a/counterpartylib/lib/config.py
+++ b/counterpartylib/lib/config.py
@@ -95,6 +95,5 @@ EXITCODE_UPDATE_REQUIRED = 5
 
 
 DEFAULT_CHECK_ASSET_CONSERVATION = True
-DEFAULT_ENABLE_MEMPOOL = False
 
 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/counterpartylib/server.py b/counterpartylib/server.py
index 8e2b5a35..cc0ed9d2 100755
--- a/counterpartylib/server.py
+++ b/counterpartylib/server.py
@@ -83,7 +83,6 @@ def initialise(database_file=None, log_file=None, api_log_file=None,
                 requests_timeout=config.DEFAULT_REQUESTS_TIMEOUT,
                 rpc_batch_size=config.DEFAULT_RPC_BATCH_SIZE,
                 check_asset_conservation=config.DEFAULT_CHECK_ASSET_CONSERVATION,
-                enable_mempool = config.DEFAULT_ENABLE_MEMPOOL,
                 backend_ssl_verify=None, rpc_allow_cors=None):
 
      # Data directory
@@ -277,7 +276,6 @@ def initialise(database_file=None, log_file=None, api_log_file=None,
     config.REQUESTS_TIMEOUT = requests_timeout
     config.RPC_BATCH_SIZE = rpc_batch_size
     config.CHECK_ASSET_CONSERVATION = check_asset_conservation
-    config.ENABLE_MEMPOOL = enable_mempool
 
     ##############
     # OTHER SETTINGS