Add scantxoutset RPC method #12196

pull jonasschnelli wants to merge 6 commits into bitcoin:master from jonasschnelli:2017/12/utxo_sweep changing 4 files +339 −0
  1. jonasschnelli commented at 7:09 am on January 16, 2018: contributor

    Alternative to #9152.

    This takes <n> pubkeys and optionally <n> xpubs (together with a definable lookup windows where the default is 0-1000) and looks up common scripts in the UTXO set of all given or derived keys.

    The output will be an array similar to listunspent. That array is compatible with createrawtransaction as well as with signrawtransaction.

    This makes it possible to prepare sweeps and have them signed in a secure (cold) space.

  2. jonasschnelli added the label RPC/REST/ZMQ on Jan 16, 2018
  3. gmaxwell commented at 7:14 am on January 16, 2018: contributor
    Why is it pubkeys and not addresses for the pubkey part? (obviously xpubs are xpubs and need to be)
  4. jonasschnelli commented at 7:33 am on January 16, 2018: contributor
    After a short discussion on IRC (https://botbot.me/freenode/bitcoin-core-dev/2018-01-16/?msg=95804115&page=2) support for addresses and pubkeys makes most sense. Will add support for an array of addresses.
  5. jonasschnelli force-pushed on Jan 16, 2018
  6. jonasschnelli force-pushed on Jan 16, 2018
  7. in src/rpc/blockchain.cpp:1679 in 4378347dea outdated
    1674+        result.pushKV("progress", g_scan_progress);
    1675+    }
    1676+    else if (request.params[0].get_str() == "abort") {
    1677+        CoinsViewScanReserver reserver;
    1678+        if (reserver.reserve()) {
    1679+            throw JSONRPCError(RPC_INVALID_PARAMETER, "No scan in progress");
    


    promag commented at 10:42 am on January 16, 2018:
    IMO there is no need to throw, a bool in the response is enough? Otherwise, missing test.
  8. in src/rpc/blockchain.cpp:1636 in 4378347dea outdated
    1631+
    1632+UniValue scantxoutset(const JSONRPCRequest& request)
    1633+{
    1634+    if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
    1635+        throw std::runtime_error(
    1636+            "scantxoutset <action> {\"pubkeys\": [\"pubkey\",...], \"xpubs\":[{\"xpub\": \"<xpub>\"}], other options}\n"
    


    promag commented at 10:43 am on January 16, 2018:
    Remove other options? There are none.
  9. in src/rpc/blockchain.cpp:2023 in 4378347dea outdated
    1629+    }
    1630+};
    1631+
    1632+UniValue scantxoutset(const JSONRPCRequest& request)
    1633+{
    1634+    if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
    


    promag commented at 10:44 am on January 16, 2018:
    request.params.size() != 2?

    jonasschnelli commented at 7:03 am on January 19, 2018:
    Nah. You can also call scantxoutset status

    promag commented at 3:09 pm on July 12, 2018:

    Commit “Blockchain/RPC: Add scantxoutset method to scan UTXO set”

    Nah. You can also call scantxoutset status

    Right.

    However should be request.params.size() > 3 (instead of 2)?

  10. in src/rpc/blockchain.cpp:1647 in 4378347dea outdated
    1642+            "                                          \"status\" for progress report (in %) of the current scan\n"
    1643+            "2. \"options\"                     (object, optional)\n"
    1644+            "      \"pubkeys\":[\"pubkey\",...]   (array of strings, optional) An array of HEX encoded public keys\n"
    1645+            "      \"xpubs\":                   (array of xpub objects that will be used to derive child keys with the given lookup window after m/0/k and m/1/k scheme)\n"
    1646+            "           [\n"
    1647+            "               {\"xpub\":\"<xpub>\", (base58check encoded extended public key (xpub)\n"
    


    promag commented at 10:44 am on January 16, 2018:
    Nit, newline after {.
  11. in src/rpc/blockchain.cpp:1744 in 4378347dea outdated
    1667+
    1668+    UniValue result(UniValue::VOBJ);
    1669+    if (request.params[0].get_str() == "status") {
    1670+        CoinsViewScanReserver reserver;
    1671+        if (reserver.reserve()) {
    1672+            throw JSONRPCError(RPC_INVALID_PARAMETER, "No scan in progress");
    


    promag commented at 10:45 am on January 16, 2018:
    Missing test for error.

    jonasschnelli commented at 7:07 am on January 19, 2018:
    You mean missing a functional test for that case. Yes. I though about it, but would require a mockup-slowdown argument (-testslowdown or similar). Otherwise I guess it’s hard to properly test this.

    MarcoFalke commented at 6:32 pm on May 21, 2018:
    Why would this be an exception, considering that the scan could normally finish between the last call and this one? Generally I think we should avoid exceptions for control flow in our code (and especially in third party code that uses the rpc interface)

    promag commented at 6:01 pm on May 22, 2018:
    Yes, agree that status should not raise an error when no scan is in progress.
  12. in src/rpc/blockchain.cpp:1825 in 4378347dea outdated
    1820+                unspents.push_back(unspent);
    1821+            }
    1822+        }
    1823+        result.push_back(Pair("unspents", unspents));
    1824+        result.push_back(Pair("total_amount", ValueFromAmount(total_in)));
    1825+    }
    


    promag commented at 10:46 am on January 16, 2018:
    Nit, else { here.
  13. in src/rpc/blockchain.cpp:1682 in 4378347dea outdated
    1677+        CoinsViewScanReserver reserver;
    1678+        if (reserver.reserve()) {
    1679+            throw JSONRPCError(RPC_INVALID_PARAMETER, "No scan in progress");
    1680+        }
    1681+        g_should_abourt_scan = true;
    1682+    }
    


    promag commented at 10:46 am on January 16, 2018:
    Nit, else if () { here.
  14. in src/rpc/blockchain.cpp:1605 in 4378347dea outdated
    1598@@ -1597,6 +1599,236 @@ UniValue savemempool(const JSONRPCRequest& request)
    1599     return NullUniValue;
    1600 }
    1601 
    1602+static std::mutex g_utxosetscan;
    1603+static std::atomic<int> g_scan_progress;
    1604+static std::atomic<bool> g_scan_in_progress;
    1605+static std::atomic<bool> g_should_abourt_scan;
    


    promag commented at 10:47 am on January 16, 2018:
    Typo, abort.
  15. in src/rpc/blockchain.cpp:1625 in 4378347dea outdated
    1620+        m_could_reserve = true;
    1621+        return true;
    1622+    }
    1623+
    1624+    ~CoinsViewScanReserver() {
    1625+        std::lock_guard<std::mutex> lock(g_utxosetscan);
    


    promag commented at 10:55 am on January 16, 2018:

    Lock only when changing g_scan_in_progress?

    0if (m_could_reserve) {
    1    std::lock_guard<std::mutex> lock(g_utxosetscan);
    2    g_scan_in_progress = false;
    3}
    
  16. in src/rpc/blockchain.cpp:1686 in 4378347dea outdated
    1681+        g_should_abourt_scan = true;
    1682+    }
    1683+    else if (request.params[0].get_str() == "start") {
    1684+        CoinsViewScanReserver reserver;
    1685+        if (!reserver.reserve()) {
    1686+            throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan already in progress, use action \"abort\" \"status\"");
    


    promag commented at 10:59 am on January 16, 2018:
    Remove “status”? Otherwise add or between actions.
  17. in src/rpc/blockchain.cpp:1806 in 4378347dea outdated
    1801+                bool res = pcoinsdbview->FindScriptPubKey(g_scan_progress, g_should_abourt_scan, count, needles, coins);
    1802+                result.push_back(Pair("success", res ? "yes" : "no"));
    1803+                result.push_back(Pair("searched_items", count));
    1804+            }
    1805+
    1806+            for (auto& it : coins) {
    


    promag commented at 11:00 am on January 16, 2018:
    const auto&
  18. in src/rpc/blockchain.cpp:1966 in 4378347dea outdated
    1610+    bool m_could_reserve;
    1611+public:
    1612+    explicit CoinsViewScanReserver() : m_could_reserve(false) {}
    1613+
    1614+    bool reserve() {
    1615+        std::lock_guard<std::mutex> lock(g_utxosetscan);
    


    promag commented at 11:01 am on January 16, 2018:

    Assert not reserved?

    0assert(!m_could_reserve);
    1std::lock_guard<std::mutex> lock(g_utxosetscan);
    2if (g_scan_in_progress) return false;
    3g_scan_in_progress = true;
    4m_could_reserve = true;
    5return true;
    

    jonasschnelli commented at 7:54 am on May 18, 2018:
    Fixed.
  19. laanwj commented at 12:41 pm on January 16, 2018: member
    Concept ACK, nice! I’ve wished for UTXO scanning functionality many times, much faster than importing into a watchonly wallet if you only care about spendable UTXOs.
  20. jonasschnelli force-pushed on Jan 19, 2018
  21. jonasschnelli force-pushed on Jan 19, 2018
  22. jonasschnelli force-pushed on Jan 19, 2018
  23. jonasschnelli force-pushed on Jan 20, 2018
  24. jonasschnelli commented at 0:16 am on January 20, 2018: contributor
    • Added support for addresses (can scan unspent outputs after given addresses)
    • Added support for an optional raw sweep transaction including optional feerate or optional confirmation target
  25. jonasschnelli commented at 0:26 am on January 20, 2018: contributor
    The raw sweep fee calculation is currently WIP (misses the dummy signer part)… will fix soon.
  26. jonasschnelli force-pushed on Jan 21, 2018
  27. jonasschnelli commented at 6:36 am on January 21, 2018: contributor
    Overhauled the fee calculation logic (see the dummy sign keystore).
  28. in src/rpc/blockchain.cpp:2149 in 1490e13b09 outdated
    1842+        g_scan_progress = 0;
    1843+        int64_t count = 0;
    1844+
    1845+        // flush utxo state and start the scan
    1846+        FlushStateToDisk();
    1847+        bool res = pcoinsdbview->FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, needles, coins);
    


    gmaxwell commented at 2:07 am on January 22, 2018:
    What prevents the state from being mutated out from under this scan?

    jonasschnelli commented at 7:04 pm on January 22, 2018:

    I think due to the time required to perform a scan, it’s something that may be tolerated (although it should be mentioned in the docs). Not sure, but I guess it’s the same with gettxoutsetinfo.

    Not sure if you can scan a CCoinsView of a snapshot state… I guess no. Locking cs_main would be “meh”.


    jonasschnelli commented at 7:05 pm on January 22, 2018:
    Also, the rawtx (sweep) is in the same way “outdated” the moment you have received it… maybe you could argue that this is the same for fundrawtx

    sipa commented at 9:05 pm on May 18, 2018:

    @gmaxwell @jonasschnelli The cursor iterates over the state of the CCoinsView at the time it was created; modifying it during iteration is fine. This only works because GetCursor is not implemented for CCoinsViewCache, and is invoked directly on the CCoinsViewDB LevelDB wrapper.

    The downside is that this requires a full flush of the database, hurting performance for all of the process (including validation).

  29. luke-jr commented at 7:23 pm on January 24, 2018: member
    NACK supporting addresses. Addresses have no relation to the UTXOs.
  30. jonasschnelli commented at 7:27 pm on January 24, 2018: contributor
    @luke-jr: Why? Addresses are encoded output scripts (scriptPubKey). The rational behind supporting addresses is that a) it may be more efficient then forming every possible known common script from a pubkey and b) that pubkeys are somewhat more difficult to export then the pure “used addresses”.
  31. luke-jr commented at 7:41 pm on January 24, 2018: member
    Addresses are opaque identifiers for a given invoice. That they are currently implemented by encoding a scriptPubKey is irrelevant.
  32. greenaddress commented at 6:44 pm on February 26, 2018: contributor

    Does it support mempool/unconfirmed utxos? I had a quick look and didn’t seem to, I think it would be useful to have mempool too.

    Would it make sense to avoid the background job? maybe by keeping the utxo set sorted by scriptPubKey and binary search on it or perhaps some utxo set limited indexing?

  33. jonasschnelli commented at 1:47 am on February 27, 2018: contributor

    Does it support mempool/unconfirmed utxos? I had a quick look and didn’t seem to, I think it would be useful to have mempool too.

    It currently does not scan the mempool (hence the command name scantxoutset), but I agree, that would be useful. But, since scans take a while, timing may be a problem for scanning the mempool. Maybe an additional RPC call would make sense (scanmempool)?

    Would it make sense to avoid the background job? maybe by keeping the utxo set sorted by scriptPubKey and binary search on it or perhaps some utxo set limited indexing?

    You mean speeding up the scan? I don’t think its worth to keep an extra index (changing the sort order would probably slow down verification a lot). My local tests did show a mainnet scan takes about ~30seconds (SSD, fast CPU). I don’t know what the space requirements for a by-scriptPubKey-index would be, …. maybe we can look into this once this PR did proceed.

  34. jonasschnelli commented at 1:52 am on February 27, 2018: contributor
    Rebased
  35. jonasschnelli force-pushed on Feb 27, 2018
  36. luke-jr commented at 2:15 am on February 28, 2018: member
    The commit separation here is ugly: CCoinsView::FindScriptPubKey initially checks ShutdownRequested directly, and then this is removed with the RPC changes.
  37. jonasschnelli force-pushed on Feb 28, 2018
  38. jonasschnelli force-pushed on Feb 28, 2018
  39. jonasschnelli commented at 2:22 pm on February 28, 2018: contributor
    Fixed the ugly commit separation in coins.cpp
  40. jonasschnelli force-pushed on Mar 6, 2018
  41. laanwj added this to the "Blockers" column in a project

  42. jonasschnelli force-pushed on May 17, 2018
  43. jonasschnelli force-pushed on May 18, 2018
  44. jonasschnelli commented at 8:43 am on May 18, 2018: contributor
    Rebased
  45. in src/coins.cpp:26 in a6019cd910 outdated
    22@@ -19,6 +23,41 @@ bool CCoinsView::HaveCoin(const COutPoint &outpoint) const
    23     return GetCoin(outpoint, coin);
    24 }
    25 
    26+bool CCoinsView::FindScriptPubKey(std::atomic<int>& scanProgress, std::atomic<bool>& shouldAbort, int64_t& count, CCoinsViewCursor& cursor, const std::set<CScript>& needles, std::map<COutPoint, Coin>& out_results) {
    


    sipa commented at 8:58 pm on May 18, 2018:
    Can this be made a function instead of a method? It doesn’t seem like it needs access to any of the class’s internals.

    jonasschnelli commented at 8:09 am on May 25, 2018:
    Since we access the instance cursor (CCoinsView()::Cursor()), wouldn’t it then require to access pcoinsdbview from within coins.cpp which seems not ideal?
  46. in src/coins.h:164 in a6019cd910 outdated
    159@@ -157,6 +160,10 @@ class CCoinsView
    160     //! Retrieve the block hash whose state this CCoinsView currently represents
    161     virtual uint256 GetBestBlock() const;
    162 
    163+    //! Search for a given set of pubkey scripts
    164+    static bool FindScriptPubKey(std::atomic<int>& scanProgress, std::atomic<bool>& shouldAbort, int64_t& count, CCoinsViewCursor& cursor, const std::set<CScript>& needles, std::map<COutPoint, Coin>& out_results);
    


    sipa commented at 8:58 pm on May 18, 2018:
    Nit: style for variable names.
  47. in src/rpc/blockchain.cpp:2114 in 31d4874978 outdated
    1790+                    pubkeys.push_back(k.pubkey);
    1791+                }
    1792+            }
    1793+        }
    1794+
    1795+        // add all common scripts for the given and derived pubkeys
    


    sipa commented at 9:22 pm on May 18, 2018:

    It’s unfortunate that this introduces yet another “default set of addresses derived from a given key”. Maintaining this will lead to incompatibilities between implementations as new types of scripts get added.

    It would be better to have an explicit way to describe a set of scripts to watch for that could be reused in multiple places. I’m working towards that (see https://gist.github.com/sipa/125cfa1615946d0c3f3eec2ad7f250a2), but it may be a while before it’s usable.

    However, would you consider changing the RPC interface to be more easily adaptable to such a design? I think an interface which just takes a list of descriptions, each of which is a JSON object with either an address, script, or xpub (where the xpub one also takes a window size in a separate field). That means we can later add extra fields to describe what derivations to use, for example.


    MarcoFalke commented at 6:39 pm on May 21, 2018:
    Agree that just checking for exact matches (scriptPubKey from address) is probably sufficient. If scripts really need to be translated between address types, it can be done in the client (wallet) software or a third party tool by the caller. Thus the implementation is simplified here by removing this whole code block.
  48. sipa commented at 9:47 pm on May 18, 2018: member

    GitHub doesn’t seem to let me add more comments on specific line numbers, so I’ll put it here:

    CPubKey pub_key should be const, and styled like a constant (all uppercase).

    Overall, I’m unsure about this. This is functionality that is more easily provided by software that maintains a UTXO index by script, and is not possible in general if we’d move to a design like UHF (see mailinglist) or other UTXO avoidance techniques. Those are far away of course, and features like this can be made optional (like txindex is) if needed. I’m just generally unconvinced a full node is the best place to put this.

  49. jonasschnelli commented at 9:24 am on May 19, 2018: contributor

    Overall, I’m unsure about this. This is functionality that is more easily provided by software that maintains a UTXO index by script, […]

    I think the scantxoutset approach is a low hanging fruit (relatively simple to implement) and may be replaced later with an (optional) UXTO index approach.

    It will immediately allow pruned peers to sweep seeds/wallets with a process that takes less then a minute. Also, pruned & non pruned peers could import a wallet without a rescan (if just interested in the unspent outputs rather then the whole transaction history).

    Additionally, it would allow to safely create a raw-sweep-transaction that can be later used to sign via a cold-store instance/HWW.

    I think it worth to finish the implementation because it fulfils direct user needs, even if we replace it later with an index based approach or even if we have to drop it completely once we move to a non-UTXO-set-based validation approach (far in future probably).

  50. in src/rpc/blockchain.cpp:2074 in 31d4874978 outdated
    1717+        }
    1718+        result.pushKV("progress", g_scan_progress);
    1719+    } else if (request.params[0].get_str() == "abort") {
    1720+        CoinsViewScanReserver reserver;
    1721+        if (reserver.reserve()) {
    1722+            return false;
    


    MarcoFalke commented at 6:27 pm on May 21, 2018:

    Since this return value is subject to several races [1], I believe you could just return NullUniValue;

    [1]:

    • e.g. the scan finishes between the last call and this one
    • e.g. this call returns true, but the last start call was not (yet) actually aborted

    jonasschnelli commented at 8:21 am on May 25, 2018:
    Another way would be to wait at this point (with a conditional variable) until the scan has aborted. Objections?
  51. in src/rpc/blockchain.cpp:2068 in 31d4874978 outdated
    1713+    if (request.params[0].get_str() == "status") {
    1714+        CoinsViewScanReserver reserver;
    1715+        if (reserver.reserve()) {
    1716+            throw JSONRPCError(RPC_INVALID_PARAMETER, "No scan in progress");
    1717+        }
    1718+        result.pushKV("progress", g_scan_progress);
    


    MarcoFalke commented at 6:29 pm on May 21, 2018:
    Could add return result (early return) to clarify that nothing else happens in the hundred lines below for this case.
  52. in src/rpc/blockchain.cpp:2159 in 31d4874978 outdated
    1835+                CTxDestination dest = DecodeDestination(address_uni.get_str());
    1836+                if (!IsValidDestination(dest)) {
    1837+                    throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
    1838+                }
    1839+                CScript script = GetScriptForDestination(dest);
    1840+                if (!script.empty()) {
    


    MarcoFalke commented at 6:48 pm on May 21, 2018:
    I am pretty sure this never happens when the destination is valid (which you checked two lines above.

    promag commented at 6:03 pm on May 22, 2018:
    How about assert(!script.empty())?
  53. in test/functional/rpc_scantxoutset.py:12 in 9ea4835210 outdated
     7+from test_framework.util import *
     8+
     9+import shutil
    10+import os
    11+
    12+class ScanrxoutsetTest(BitcoinTestFramework):
    


    MarcoFalke commented at 6:58 pm on May 21, 2018:
    typo-nit: ScantxoutsetTest
  54. in test/functional/rpc_scantxoutset.py:20 in 9ea4835210 outdated
    15+        self.setup_clean_chain = True
    16+    def run_test(self):
    17+        self.log.info("Mining blocks...")
    18+        self.nodes[0].generate(110)
    19+
    20+        addr_P2SH_SEGWIT = self.nodes[0].getnewaddress()
    


    MarcoFalke commented at 7:00 pm on May 21, 2018:
    nit: specify p2sh-segwit, so the tests don’t break when the defaults change.
  55. in test/functional/rpc_scantxoutset.py:33 in 9ea4835210 outdated
    28+        self.nodes[0].sendtoaddress(addr_BECH32, 3)
    29+        self.nodes[0].generate(1)
    30+
    31+        self.log.info("Stop node, remove wallet, mine again some blocks...")
    32+        self.stop_node(0)
    33+        shutil.rmtree(os.path.join(self.options.tmpdir, "node0/regtest/wallets"))
    


    MarcoFalke commented at 7:01 pm on May 21, 2018:
    nit: os.path.join(self.nodes[0].datadir, "regtest", 'wallets')
  56. in test/functional/rpc_scantxoutset.py:54 in 9ea4835210 outdated
    49+        self.nodes[0].sendtoaddress(k_bip44_child3_ard, 0.4)
    50+        self.nodes[0].sendtoaddress(k_bip44_child4_ard, 0.5)
    51+        self.nodes[0].generate(1)
    52+
    53+        self.stop_node(0)
    54+        os.remove(os.path.join(self.options.tmpdir, "node0/regtest/wallet.dat"))
    


    MarcoFalke commented at 7:03 pm on May 21, 2018:
    nit: os.path.join(self.nodes[1].datadir, "regtest", 'wallet.dat'
  57. in src/rpc/blockchain.cpp:1734 in 94c727074b outdated
    1728@@ -1704,6 +1729,9 @@ UniValue scantxoutset(const JSONRPCRequest& request)
    1729             "   }\n"
    1730             "   ,...], \n"
    1731             " \"total_amount\" : x.xxx,          (numeric) The total amount of all found unspent outputs in " + CURRENCY_UNIT + "\n"
    1732+            " \"rawsweep_tx\" : \"value\",       (string) The hex-encoded raw transaction of the optional sweep transaction\n"
    1733+            " \"rawsweep_vsize\" : \"value\",     (numeric) virtual transaction size of the sweep transaction including signatures\n"
    1734+            " \"rawsweep_fee\" : \"value\",       (numeric) Estimated fee for the sweep transaction in " + CURRENCY_UNIT + "\n"
    


    MarcoFalke commented at 7:07 pm on May 21, 2018:
    Nit: “Estimated fee” implies that the fee is unknown, which is not true given that the exact fee can be calculated.
  58. in src/rpc/blockchain.cpp:1971 in 94c727074b outdated
    1673@@ -1671,6 +1674,23 @@ class CoinsViewScanReserver
    1674     }
    1675 };
    1676 
    1677+/** A dummy keystore for the txout-set scan in order to calculate the right fees for the sweep transaction */
    1678+static CPubKey pub_key(std::vector<unsigned char>(33)); // always use a compress pubkey
    1679+class CCoinsViewScanDummySignKeyStore : public CBasicKeyStore
    1680+{
    1681+public:
    1682+    bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const {
    


    MarcoFalke commented at 7:09 pm on May 21, 2018:
    can be marked override
  59. in src/rpc/blockchain.cpp:1976 in 94c727074b outdated
    1682+    bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const {
    1683+        // return dummy pubkey
    1684+        vchPubKeyOut = pub_key;
    1685+        return true;
    1686+    }
    1687+    bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const {
    


    MarcoFalke commented at 7:10 pm on May 21, 2018:
    can be marked override
  60. in src/rpc/blockchain.cpp:1733 in 94c727074b outdated
    1728@@ -1704,6 +1729,9 @@ UniValue scantxoutset(const JSONRPCRequest& request)
    1729             "   }\n"
    1730             "   ,...], \n"
    1731             " \"total_amount\" : x.xxx,          (numeric) The total amount of all found unspent outputs in " + CURRENCY_UNIT + "\n"
    1732+            " \"rawsweep_tx\" : \"value\",       (string) The hex-encoded raw transaction of the optional sweep transaction\n"
    1733+            " \"rawsweep_vsize\" : \"value\",     (numeric) virtual transaction size of the sweep transaction including signatures\n"
    


    MarcoFalke commented at 7:19 pm on May 21, 2018:
    I think the vsizes of the signatures are only estimated?
  61. in src/rpc/blockchain.cpp:2006 in 94c727074b outdated
    1711@@ -1692,6 +1712,11 @@ UniValue scantxoutset(const JSONRPCRequest& request)
    1712             "                \"lookupwindow\": [<startindex>, <stopindex>] (An array with two integers that does define the range of keys that will be deriven for the given xpubs, default is 0 to 1000)\n"
    1713             "                }\n"
    1714             "           ]\n"
    1715+            "      \"rawsweep\": {\n             (object, optional) Optionally creates a raw sweep transaction\n"
    1716+            "          \"address\": \"address\",   (string, optional) Address where the funds should be sent to\n"
    1717+            "          \"feerate\": n,           (numeric, optional, default not set: makes wallet determine the fee) Set a specific fee rate in " + CURRENCY_UNIT + "/kB\n"
    


    MarcoFalke commented at 7:20 pm on May 21, 2018:
    I believe this doesn’t use the wallet, but the conf_target instead?
  62. in src/rpc/blockchain.cpp:2007 in 94c727074b outdated
    1711@@ -1692,6 +1712,11 @@ UniValue scantxoutset(const JSONRPCRequest& request)
    1712             "                \"lookupwindow\": [<startindex>, <stopindex>] (An array with two integers that does define the range of keys that will be deriven for the given xpubs, default is 0 to 1000)\n"
    1713             "                }\n"
    1714             "           ]\n"
    1715+            "      \"rawsweep\": {\n             (object, optional) Optionally creates a raw sweep transaction\n"
    1716+            "          \"address\": \"address\",   (string, optional) Address where the funds should be sent to\n"
    1717+            "          \"feerate\": n,           (numeric, optional, default not set: makes wallet determine the fee) Set a specific fee rate in " + CURRENCY_UNIT + "/kB\n"
    1718+            "          \"conf_target\": n,       (numeric, optional) Confirmation target (in blocks), has no effect if feerate is provided\n"
    


    MarcoFalke commented at 7:20 pm on May 21, 2018:
    I believe the default for conf_target is 6?
  63. MarcoFalke approved
  64. MarcoFalke commented at 7:21 pm on May 21, 2018: member

    utACK 94c727074b7aaac71f1560cdfc5b7d2a427d373f

    Left inline comments on some of the commits.

  65. in src/rpc/blockchain.cpp:1987 in 94c727074b outdated
    1693+
    1694+UniValue scantxoutset(const JSONRPCRequest& request)
    1695+{
    1696+    if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
    1697+        throw std::runtime_error(
    1698+            "scantxoutset <action> {\"pubkeys\": [\"pubkey\",...], \"xpubs\":[{\"xpub\": \"<xpub>\"}]}\n"
    


    promag commented at 5:54 pm on May 22, 2018:

    Maybe change this to

    0    "scantxoutset \"action\" ( option )\n"
    
  66. in src/rpc/blockchain.cpp:1715 in 94c727074b outdated
    1710+            "               {\n"
    1711+            "                \"xpub\":\"<xpub>\",  (base58check encoded extended public key (xpub)\n"
    1712+            "                \"lookupwindow\": [<startindex>, <stopindex>] (An array with two integers that does define the range of keys that will be deriven for the given xpubs, default is 0 to 1000)\n"
    1713+            "                }\n"
    1714+            "           ]\n"
    1715+            "      \"rawsweep\": {\n             (object, optional) Optionally creates a raw sweep transaction\n"
    


    promag commented at 5:56 pm on May 22, 2018:
    Remove \n.
  67. in src/rpc/blockchain.cpp:1712 in 94c727074b outdated
    1707+            "      \"addresses\":[\"address\",...] (array of strings, optional) An array of bitcoin addresses\n"
    1708+            "      \"xpubs\":                    (array of xpub objects that will be used to derive child keys with the given lookup window after m/0/k and m/1/k scheme)\n"
    1709+            "           [\n"
    1710+            "               {\n"
    1711+            "                \"xpub\":\"<xpub>\",  (base58check encoded extended public key (xpub)\n"
    1712+            "                \"lookupwindow\": [<startindex>, <stopindex>] (An array with two integers that does define the range of keys that will be deriven for the given xpubs, default is 0 to 1000)\n"
    


    promag commented at 5:56 pm on May 22, 2018:
    Needs 2 spaces between argument name and description.
  68. in src/rpc/blockchain.cpp:2056 in 94c727074b outdated
    1730+            "   ,...], \n"
    1731+            " \"total_amount\" : x.xxx,          (numeric) The total amount of all found unspent outputs in " + CURRENCY_UNIT + "\n"
    1732+            " \"rawsweep_tx\" : \"value\",       (string) The hex-encoded raw transaction of the optional sweep transaction\n"
    1733+            " \"rawsweep_vsize\" : \"value\",     (numeric) virtual transaction size of the sweep transaction including signatures\n"
    1734+            " \"rawsweep_fee\" : \"value\",       (numeric) Estimated fee for the sweep transaction in " + CURRENCY_UNIT + "\n"
    1735+            "]\n"
    


    promag commented at 5:57 pm on May 22, 2018:
    Replace ] with }.

    promag commented at 3:13 pm on July 12, 2018:

    Commit “Commit “Blockchain/RPC: Add scantxoutset method to scan UTXO set”

    The response is an object, not an array.

  69. in src/rpc/blockchain.cpp:1732 in 94c727074b outdated
    1727+            "    \"amount\" : x.xxx,             (numeric) The total amount in " + CURRENCY_UNIT + " received by the address\n"
    1728+            "    \"height\" : n,                 (numeric) Height of the unspent transaction output\n"
    1729+            "   }\n"
    1730+            "   ,...], \n"
    1731+            " \"total_amount\" : x.xxx,          (numeric) The total amount of all found unspent outputs in " + CURRENCY_UNIT + "\n"
    1732+            " \"rawsweep_tx\" : \"value\",       (string) The hex-encoded raw transaction of the optional sweep transaction\n"
    


    promag commented at 5:58 pm on May 22, 2018:
    Needs 2 spaces before description.

    promag commented at 6:23 pm on May 22, 2018:
    Put these in an object "rawsweep": { }?
  70. in src/rpc/blockchain.cpp:1733 in 94c727074b outdated
    1728+            "    \"height\" : n,                 (numeric) Height of the unspent transaction output\n"
    1729+            "   }\n"
    1730+            "   ,...], \n"
    1731+            " \"total_amount\" : x.xxx,          (numeric) The total amount of all found unspent outputs in " + CURRENCY_UNIT + "\n"
    1732+            " \"rawsweep_tx\" : \"value\",       (string) The hex-encoded raw transaction of the optional sweep transaction\n"
    1733+            " \"rawsweep_vsize\" : \"value\",     (numeric) virtual transaction size of the sweep transaction including signatures\n"
    


    promag commented at 5:58 pm on May 22, 2018:
    Needs 1 space before description.
  71. in src/rpc/blockchain.cpp:1734 in 94c727074b outdated
    1729+            "   }\n"
    1730+            "   ,...], \n"
    1731+            " \"total_amount\" : x.xxx,          (numeric) The total amount of all found unspent outputs in " + CURRENCY_UNIT + "\n"
    1732+            " \"rawsweep_tx\" : \"value\",       (string) The hex-encoded raw transaction of the optional sweep transaction\n"
    1733+            " \"rawsweep_vsize\" : \"value\",     (numeric) virtual transaction size of the sweep transaction including signatures\n"
    1734+            " \"rawsweep_fee\" : \"value\",       (numeric) Estimated fee for the sweep transaction in " + CURRENCY_UNIT + "\n"
    


    promag commented at 5:58 pm on May 22, 2018:
    Needs 1 space before description.
  72. promag commented at 6:04 pm on May 22, 2018: member
    Some comments.
  73. jonasschnelli force-pushed on May 25, 2018
  74. in src/rpc/blockchain.cpp:2095 in e6b1a46eb8 outdated
    2090+            CScript script = GetScriptForDestination(address);
    2091+            if (!script.empty()) {
    2092+                needles.insert(script);
    2093+                temp_keystore.AddWatchOnly(script);
    2094+            }
    2095+            // add P2SH-P2WPKH script
    


    achow101 commented at 2:57 am on May 27, 2018:
    I think we should avoid doing this step for uncompressed pubkeys as such outputs would be unspendable.
  75. in src/coins.cpp:56 in 468b034a64 outdated
    51+    }
    52+    scan_progress = 100;
    53+    return true;
    54+}
    55+
    56+bool CCoinsView::FindScriptPubKey(std::atomic<int>& scan_progress, std::atomic<bool>& should_abort, int64_t& searchItems, const std::set<CScript>& needles, std::map<COutPoint, Coin>& out_results) {
    


    achow101 commented at 3:01 am on May 27, 2018:
    nit: searchItems should be search_items to match the .h file.
  76. in src/rpc/blockchain.cpp:1982 in e6b1a46eb8 outdated
    1977+            "      \"xpubs\":                    (array of xpub objects that will be used to derive child keys with the given lookup window after m/0/k and m/1/k scheme)\n"
    1978+            "           [\n"
    1979+            "               {\n"
    1980+            "                \"xpub\":\"<xpub>\",  (base58check encoded extended public key (xpub)\n"
    1981+            "                \"lookupwindow\":   [<startindex>, <stopindex>] (An array with two integers that does define the range of keys that will be deriven for the given xpubs, default is 0 to 1000)\n"
    1982+            "                }\n"
    


    achow101 commented at 3:01 am on May 27, 2018:
    nit: should have ,... after this.
  77. in src/rpc/blockchain.cpp:2054 in e6b1a46eb8 outdated
    1989+            "    \"vout\": n,                    (numeric) the vout value\n"
    1990+            "    \"scriptPubKey\" : \"script\",    (string) the script key\n"
    1991+            "    \"amount\" : x.xxx,             (numeric) The total amount in " + CURRENCY_UNIT + " received by the address\n"
    1992+            "    \"height\" : n,                 (numeric) Height of the unspent transaction output\n"
    1993+            "   }\n"
    1994+            "   ,...], \n"
    


    achow101 commented at 3:02 am on May 27, 2018:
    nit: the spacing of this block looks a bit funny. I think the text should be indented more and the braces for the object should be aligned.
  78. achow101 commented at 3:07 am on May 27, 2018: member
    utACK 1945f5fd3f14d7d485fc00444cc0e8c4bdc98220
  79. promag commented at 9:59 pm on May 28, 2018: member

    Nit, remove fee handling? The caller could do:

    0tx = scantxoutset("start", { "sweep_to": address })["sweep_tx"]
    1fundrawtransaction(tx, { "subtractFeeFromOutputs": [0] })
    2...
    

    So scantxoutset should just add the unspents and one output.

  80. MarcoFalke commented at 3:43 pm on May 29, 2018: member
    Just noting that GitHub decides to hide half of my review comments by default and they were not addressed (mostly feedback on the tests).
  81. jonasschnelli commented at 7:25 am on May 30, 2018: contributor

    tx = scantxoutset(“start”, { “sweep_to”: address })[“sweep_tx”] fundrawtransaction(tx, { “subtractFeeFromOutputs”: [0] }) @promag: would that also work without the wallet? My idea was to make scantxoutset work without a wallet.

  82. jonasschnelli commented at 7:25 am on May 30, 2018: contributor

    Just noting that GitHub decides to hide half of my review comments by default and they were not addressed (mostly feedback on the tests). @MarcoFalke: Have not forgotten. Still working on it.

  83. Sjors commented at 2:47 pm on May 30, 2018: member

    Concept ACK

    I tested against a blockchain.info testnet wallet and was able to get the correct balance for an account including change (there’s an xpub export feature buried in settings).

    I also tested against a Ledger wallet with p2sh segwit addresses.

    Also tested status and abort.

    Background question: what index could be added to make this much faster? I’m guessing scriptPubKey -> unspents?

    Issues:

    • ~when I launch src/qt/bitcoin-qt it says the RPC method doesn’t exist~
    • mention in help that unconfirmed transactions are not included

    I can’t figure out what’s wrong with this syntax (and it only throws the error after doing the scan bit):

    0src/bitcoin-cli scantxoutset start '{"xpubs": [ {"xpub": "tpub..." }  ]}, "rawsweep": {}}'
    1error: Error parsing JSON:{"xpubs": [ {"xpub": "tpub..." }  ]}, "rawsweep": {}}
    

    When I do add an address I get Min relay fee not met (code -26), even if I set "conf_target": 6}, setting conf_target to 1 did help.

    I’m assuming that calling rawsweep without the optional address field makes it use the wallet? (if so, that should be in the help)

    Suggestions:

    • add scantxoutset to the PR description

    Suggestions for followup PR:

    • total_amount doesn’t include unconfirmed funds, maybe add a flag to show those?
    • a minimum block height / date for faster scans (if only to make RPC dev lives happier)
    • it would be really cool to sweep using an xpriv (with or without importing keys)
    • rawsweep should have an option to make 1 transaction per output
  84. jonasschnelli force-pushed on May 31, 2018
  85. jonasschnelli commented at 2:04 pm on May 31, 2018: contributor

    Followed @sipa advice and changed the API. The script type derivation is now controllable via the API.

    The command takes now scan objects that are either an address, script, pubkey or xpub. Xpub and pubkey have the option to pass in the script type (P2PKH, P2SH-P2WPKH, P2WPKH).

    The commit history is also more clean now.

    Thanks for reviewing again. @MarcoFalke: your review is lost in the nirvana.. can’t find the comments on the test anymore. Can you try to review again? @Sjors: the Min relay fee not met (code -26) is due to non initialise fee estimation. Use a feerate if you test on regtest without fee estimation available.

     0"scanobjects"                  (array, required) Array of scan objects (only one object type per scan object allowed)
     1      [
     2        { "address" : "<address>" },       (string, optional) Bitcoin address
     3        { "script"  : "<scriptPubKey>" },  (string, optional) HEX encoded script (scriptPubKey)
     4        { "pubkey"  :                      (object, optional) Public key
     5          {
     6            "pubkey" : "<pubkey">,         (string, required) HEX encoded public key
     7            "script_types" : [ ... ],      (array, optional) Array of script-types to derive from the pubkey (possible values: "P2PKH", "P2SH-P2WPKH", "P2WPKH")
     8          }
     9        },
    10        { "xpub"  :                        (object, optional) Use an extended public key child key range (m/0/k & m/1/k) to derive scripts from
    11          { 
    12            "xpub" : "<xpub">,             (string, required) Base58check encoded extended public key (xpub)
    13            "range" : [ <s>, <e> ],        (array, optional) Range of keys that will be deriven from the given xpubs (default is 0 to 1000)
    14            "script_types" : [ ... ],      (array, optional) Array of derivation type (possible values: "P2PKH", "P2SH-P2WPKH", "P2WPKH")
    15          }
    16        },
    17      ]
    
  86. jonasschnelli commented at 2:08 pm on May 31, 2018: contributor

    @Sjors

    total_amount doesn’t include unconfirmed funds, maybe add a flag to show those?

    This should be done in a separate command (scanmempool which would be a great follow up).

    a minimum block height / date for faster scans (if only to make RPC dev lives happier)

    Not possible AFAIK. We scan the UXTO set, not blocks.

    it would be really cool to sweep using an xpriv (with or without importing keys)

    IMO the command should not include any private keys. Ideally we add xpriv support similar to this command to signrawtransaction. But I agree that this would be a great.

    rawsweep should have an option to make 1 transaction per output

    Maybe, but could/should be added later.

    I’m assuming that calling rawsweep without the optional address field makes it use the wallet? (if so, that should be in the help)

    No. This command is totally de-coupled from the wallet (runs without wallet).

  87. jonasschnelli force-pushed on May 31, 2018
  88. in src/rpc/blockchain.cpp:2037 in 2776a951a4 outdated
    2047+            "          }\n"
    2048+            "        },\n"
    2049+            "      ]\n"
    2050+            "3. \"options\"                               (object, optional)\n"
    2051+            "      \"rawsweep\": {                        (object, optional) Optionally creates a raw sweep transaction\n"
    2052+            "          \"address\": \"address\",            (string, optional) Address where the funds should be sent to\n"
    


    promag commented at 11:02 pm on May 31, 2018:
    As @Sjors points out, this should be required if rawsweep is set?

    jonasschnelli commented at 1:52 pm on July 4, 2018:
    Fixed
  89. promag commented at 11:02 pm on May 31, 2018: member

    rawsweep should have an option to make 1 transaction per output @Sjors why?

  90. Sjors commented at 7:12 am on June 1, 2018: member

    @jonasschnelli the Min relay fee not met error was on testnet, not regtest. Maybe that doesn’t happen on mainnet much. Maybe it should detect the lack of fee estimation and throw a specific error (if no fee is specified by the user)?

    We scan the UXTO set, not blocks

    I wish there an easy way to “translate” Bitcoin Core’s internal data structures to SQL table descriptions, so it’s easier to understand what “columns” and “indexes” exist. I haven’t had much luck using external tools to load the various db files.

    I’m assuming that calling rawsweep without the optional address field makes it use the wallet? (if so, that should be in the help) No. This command is totally de-coupled from the wallet (runs without wallet).

    Ok, then I don’t understand why the address field is documented as optional. @promag 1 transaction per output (to a unique address) improves privacy when sweeping an existing wallet. Especially if you don’t broadcast them all at the same time.

  91. ajtowns commented at 11:24 am on June 2, 2018: member
    @Sjors you have an extra close curly-brace after the close square bracket in src/bitcoin-cli scantxoutset start '{"xpubs": [ {"xpub": "tpub..." } ]}, "rawsweep": {}}'
  92. laanwj commented at 1:33 pm on June 4, 2018: member

    I have an unspent txout (on testnet) like this:

     0  {
     1    "txid": "473abbc4eb4768d1fdad21f05d3485bcd96cb33f883dce3dd8bccebced3f4efc",
     2    "vout": 0,
     3    "address": "n2PaqLena7QZtpKowZok8FMPqCY21Xrp4x",
     4    "label": "null",
     5    "scriptPubKey": "21029b861186b49793708a4e00ecf3cec62c027dccfc12509b7e6ac2538b4b19b3abac",
     6    "amount": 25.00317000,
     7    "confirmations": 980258,
     8    "spendable": true,
     9    "solvable": true,
    10    "safe": true
    11  }
    

    By address gives me no output:

    0$ src/bitcoin-cli -testnet scantxoutset start '[{"address":"n2PaqLena7QZtpKowZok8FMPqCY21Xrp4x"}]'
    1{
    2  "success": "yes",
    3  "searched_items": 19143564,
    4  "unspents": [
    5  ],
    6  "total_amount": 0.00000000
    7}
    8$ 
    

    By script it works, however

     0$ src/bitcoin-cli -testnet scantxoutset start '[{"script":"21029b861186b49793708a4e00ecf3cec62c027dccfc12509b7e6ac2538b4b19b3abac"}]'
     1{
     2  "success": "yes",
     3  "searched_items": 19162149,
     4  "unspents": [
     5    {
     6      "txid": "473abbc4eb4768d1fdad21f05d3485bcd96cb33f883dce3dd8bccebced3f4efc",
     7      "vout": 0,
     8      "scriptPubKey": "21029b861186b49793708a4e00ecf3cec62c027dccfc12509b7e6ac2538b4b19b3abac",
     9      "amount": 25.00317000,
    10      "height": 330282
    11    }
    12  ],
    13  "total_amount": 25.00317000
    14}
    
  93. MarcoFalke added the label Needs rebase on Jun 6, 2018
  94. MarcoFalke commented at 2:38 pm on June 11, 2018: member
    Needs rebase
  95. jonasschnelli commented at 8:40 am on June 12, 2018: contributor

    @laanwj: If I execute getaddressinfo n2PaqLena7QZtpKowZok8FMPqCY21Xrp4x I get

    0{
    1  "address": "n2PaqLena7QZtpKowZok8FMPqCY21Xrp4x",
    2  "scriptPubKey": "76a914e4f5d0b9353f7715e1bfed11358a5b84fd7c4e3488ac",
    3  "ismine": false,
    4  "iswatchonly": false,
    5  "isscript": false,
    6  "iswitness": false,
    7  "labels": [
    8  ]
    9}
    

    AFAIK your script (P2PK) 21029b861186b49793708a4e00ecf3cec62c027dccfc12509b7e6ac2538b4b19b3abac can only be derived if one has the according pubkey.

  96. jonasschnelli force-pushed on Jun 12, 2018
  97. jonasschnelli commented at 8:41 am on June 12, 2018: contributor
    Rebased.
  98. DrahtBot removed the label Needs rebase on Jun 12, 2018
  99. jonasschnelli commented at 2:09 pm on June 13, 2018: contributor

    Added two commits:

    https://github.com/bitcoin/bitcoin/pull/12196/commits/fdd09d34a14ca4f416b9101b37187098f9145301 adds support for P2PK in script_types

    https://github.com/bitcoin/bitcoin/pull/12196/commits/3c546fd38e2bdb6957fa134f8847e2a7c5c7e23

    P2PK and P2PKH share the same address (two scripts result in the same address). Deriving a P2PK script requires the pubkey which is not available when the user provides an address.

    Since we not want to miss funds, we have to derive the P2PKH equivalent of every P2PK unspent in the txoutset and compare it against the provided address (only required when providing addresses).

  100. in src/coins.h:165 in 3c546fd38e outdated
    159@@ -157,6 +160,10 @@ class CCoinsView
    160     //! Retrieve the block hash whose state this CCoinsView currently represents
    161     virtual uint256 GetBestBlock() const;
    162 
    163+    //! Search for a given set of pubkey scripts
    164+    static bool FindScriptPubKey(std::atomic<int>& scan_progress, std::atomic<bool>& should_abort, int64_t& count, CCoinsViewCursor& cursor, std::function<bool(const CScript& script)> match_func, std::map<COutPoint, Coin>& out_results);
    165+    bool FindScriptPubKey(std::atomic<int>& scan_progress, std::atomic<bool>& should_abort, int64_t& search_items, std::function<bool(const CScript& script)> match_func, std::map<COutPoint, Coin>& out_results);
    


    MarcoFalke commented at 5:30 pm on June 13, 2018:
    std::function is defined in header <functional>, which needs to be included.
  101. jonasschnelli force-pushed on Jun 13, 2018
  102. jonasschnelli commented at 8:42 pm on June 13, 2018: contributor
    After discussion with @sipa on IRC, I removed the auto-derivation for P2PK scripts when scanning with addresses. Instead, I have added a short info to the RPC help that should make users aware for that particular edge case.
  103. in src/coins.cpp:27 in 219f7b1d22 outdated
    22@@ -19,6 +23,41 @@ bool CCoinsView::HaveCoin(const COutPoint &outpoint) const
    23     return GetCoin(outpoint, coin);
    24 }
    25 
    26+bool CCoinsView::FindScriptPubKey(std::atomic<int>& scan_progress, std::atomic<bool>& should_abort, int64_t& count, CCoinsViewCursor& cursor, const std::set<CScript>& needles, std::map<COutPoint, Coin>& out_results) {
    27+    scan_progress = 0;
    


    promag commented at 10:29 pm on June 13, 2018:
    Also initialize count = 0;?

    jonasschnelli commented at 1:38 pm on July 4, 2018:
    Fixed.
  104. in src/coins.cpp:31 in 219f7b1d22 outdated
    22@@ -19,6 +23,41 @@ bool CCoinsView::HaveCoin(const COutPoint &outpoint) const
    23     return GetCoin(outpoint, coin);
    24 }
    25 
    26+bool CCoinsView::FindScriptPubKey(std::atomic<int>& scan_progress, std::atomic<bool>& should_abort, int64_t& count, CCoinsViewCursor& cursor, const std::set<CScript>& needles, std::map<COutPoint, Coin>& out_results) {
    27+    scan_progress = 0;
    28+    while (cursor.Valid()) {
    29+        COutPoint key;
    30+        Coin coin;
    31+        if (cursor.GetKey(key) && cursor.GetValue(coin)) {
    


    promag commented at 10:30 pm on June 13, 2018:

    Nit,

    0if (!cursor.GetKey(key) || !cursor.GetValue(coin) return false;
    

    and reindent below.


    jonasschnelli commented at 1:38 pm on July 4, 2018:
    Fixed.
  105. in src/coins.cpp:26 in 219f7b1d22 outdated
    22@@ -19,6 +23,41 @@ bool CCoinsView::HaveCoin(const COutPoint &outpoint) const
    23     return GetCoin(outpoint, coin);
    24 }
    25 
    26+bool CCoinsView::FindScriptPubKey(std::atomic<int>& scan_progress, std::atomic<bool>& should_abort, int64_t& count, CCoinsViewCursor& cursor, const std::set<CScript>& needles, std::map<COutPoint, Coin>& out_results) {
    


    promag commented at 10:36 pm on June 13, 2018:
    Should be const std::atomic<bool>& should_abort.

    jonasschnelli commented at 1:38 pm on July 4, 2018:
    Fixed.

    sipa commented at 3:48 am on July 6, 2018:

    In commit “Add CCoinsView::FindScriptPubKey to search the UTXO set”.

    There is no need for this function to burden the CCoinsView interface I think, as it only uses publicly available data from that class. The implementation can just move to where scantxoutset is defined.


    laanwj commented at 10:00 am on July 11, 2018:
    Agree—if we can avoid introducing new methods to the CCoinsView interface, that’s preferable, it is good to have it as a minimal interface class.
  106. in src/coins.cpp:34 in 219f7b1d22 outdated
    29+        COutPoint key;
    30+        Coin coin;
    31+        if (cursor.GetKey(key) && cursor.GetValue(coin)) {
    32+            if (count++ % 8192 == 0) {
    33+                boost::this_thread::interruption_point();
    34+                if (should_abort) {
    


    promag commented at 10:55 pm on June 13, 2018:
    || ShutdownRequested()?

    jonasschnelli commented at 7:21 am on June 14, 2018:
    This would couple it to init.h,.. ideally we would finally extract the shutdown detection into its own space.
  107. in src/rpc/blockchain.cpp:2016 in 219f7b1d22 outdated
    2011+
    2012+UniValue scantxoutset(const JSONRPCRequest& request)
    2013+{
    2014+    if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
    2015+        throw std::runtime_error(
    2016+            "scantxoutset <action> <scanobjects> (<options>)\n"
    


    promag commented at 11:01 pm on June 13, 2018:
    nit, spaces inside ( )
  108. in test/functional/rpc_scantxoutset.py:53 in 219f7b1d22 outdated
    48+        self.nodes[0].sendtoaddress(k_bip44_child2_ard, 0.3)
    49+        self.nodes[0].sendtoaddress(k_bip44_child3_ard, 0.4)
    50+        self.nodes[0].sendtoaddress(k_bip44_child4_ard, 0.5)
    51+        self.nodes[0].generate(1)
    52+
    53+        self.stop_node(0)
    


    promag commented at 11:36 pm on June 13, 2018:
    0self.restart_node(0, ['-nowallet'])
    

    and remove next 2 lines.

  109. in test/functional/rpc_scantxoutset.py:41 in 219f7b1d22 outdated
    54+        os.remove(os.path.join(self.nodes[0].datadir, "regtest", "wallet.dat"))
    55+        self.start_node(0)
    56+        self.log.info("Test if we have found the non HD unspent outputs.")
    57+        assert_equal(self.nodes[0].scantxoutset("start", [ {"pubkey": {"pubkey": pubk1}}, {"pubkey": {"pubkey": pubk2}}, {"pubkey": {"pubkey": pubk3}}])['total_amount'], 6)
    58+        decodedsweeptx = self.nodes[0].decoderawtransaction(self.nodes[0].scantxoutset("start", [ {"pubkey": {"pubkey": pubk1}}, {"pubkey": {"pubkey": pubk2}}, {"pubkey": {"pubkey": pubk3}}], {"rawsweep" : {"address": addr_BECH32, "feerate": 0.00025000}})['rawsweep_tx'])
    59+        assert_equal(len(decodedsweeptx['vin']), 3)
    


    promag commented at 11:51 pm on June 13, 2018:

    Could improve coverage:

    0        result = self.nodes[1].scantxoutset("start", [ {"pubkey": {"pubkey": pubk1}}, {"pubkey": {"pubkey": pubk2}}, {"pubkey": {"pubkey": pubk3}}], {"rawsweep" : {"address": addr_BECH32, "feerate": 0.00025000}})
    1        decodedsweeptx = self.nodes[1].decoderawtransaction(result['rawsweep_tx'])
    2        assert_equal(len(decodedsweeptx['vout']), 1)
    3        assert_equal(len(decodedsweeptx['vin']), 3)
    4        assert_equal(decodedsweeptx['vout'][0]['scriptPubKey']['addresses'], [addr_BECH32])
    

    promag commented at 0:18 am on June 14, 2018:
    You could test sign and send the sweep transaction

    jonasschnelli commented at 9:22 am on June 14, 2018:
    Lets extend the tests later….
  110. in src/rpc/blockchain.cpp:2350 in 219f7b1d22 outdated
    2345+        }
    2346+
    2347+        result.push_back(Pair("unspents", unspents));
    2348+        result.push_back(Pair("total_amount", ValueFromAmount(total_in)));
    2349+    } else {
    2350+        throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid command");
    


    promag commented at 0:20 am on June 14, 2018:
    RPC_INVALID_PARAMETER instead?
  111. in src/rpc/blockchain.cpp:2321 in 219f7b1d22 outdated
    2316+            }
    2317+
    2318+            // look for user provided feerate
    2319+            UniValue feerate_uni = find_value(rawsweep_uni, "feerate");
    2320+            CFeeRate feerate;
    2321+            if(feerate_uni.isNum()) {
    


    promag commented at 0:20 am on June 14, 2018:
    nit, space after if.
  112. promag commented at 0:23 am on June 14, 2018: member
    Some comments.
  113. jonasschnelli force-pushed on Jun 14, 2018
  114. jonasschnelli commented at 9:23 am on June 14, 2018: contributor
    Fixed @promag’s points
  115. in src/rpc/blockchain.cpp:2027 in 12cb70c0ca outdated
    2013+{
    2014+    if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
    2015+        throw std::runtime_error(
    2016+            "scantxoutset <action> <scanobjects> ( <options> )\n"
    2017+            "\nScans the unspent transaction output set for possible entries that matches common scripts of given public keys.\n"
    2018+            "Using addresses as scanobjects will _not_ detect unspent P2PK txouts\n"
    


    laanwj commented at 3:47 pm on June 14, 2018:
    Ok, fair enough.
  116. luke-jr commented at 4:27 pm on June 16, 2018: member
    Again, addresses are opaque and do not get tied to UTXOs. It makes no sense to find UTXOs “by address”.
  117. in src/rpc/blockchain.cpp:2055 in 12cb70c0ca outdated
    2050+            "  \"unspents\": [\n"
    2051+            "    {\n"
    2052+            "    \"txid\" : \"transactionid\",     (string) The transaction id\n"
    2053+            "    \"vout\": n,                    (numeric) the vout value\n"
    2054+            "    \"scriptPubKey\" : \"script\",    (string) the script key\n"
    2055+            "    \"amount\" : x.xxx,             (numeric) The total amount in " + CURRENCY_UNIT + " received by the address\n"
    


    luke-jr commented at 5:41 pm on June 16, 2018:
    s/received by the address/of the UTXO/

    jonasschnelli commented at 12:48 pm on June 18, 2018:
    Fixed.
  118. jonasschnelli force-pushed on Jun 18, 2018
  119. jonasschnelli commented at 12:50 pm on June 18, 2018: contributor

    @luke-jr

    Again, addresses are opaque and do not get tied to UTXOs. It makes no sense to find UTXOs “by address”.

    You have already made that argument here. It seems like that the scans based on addresses are useful. Other developers opinions would be welcome.

  120. laanwj commented at 3:52 pm on June 18, 2018: member

    It seems like that the scans based on addresses are useful. Other developers opinions would be welcome.

    Yes, that is useful, and for better or worse, likely how most people will be using this in practice.

    utACK 4782d23938a8a5297319f024aed3fc6e2c1651ac

  121. ajtowns commented at 5:29 am on June 19, 2018: member

    Addresses are opaque identifiers for a given invoice. That they are currently implemented by encoding a scriptPubKey is irrelevant.

    This makes no sense to me: per the wiki “A Bitcoin address […] is an identifier […] that represents a possible destination for a bitcoin payment”, but if you want to send a bitcoin payment somewhere you have to be able to deduce the scriptPubKey from the address, or you can’t make a transaction. I don’t think it makes sense to generalise “bitcoin addresses” to things that you can’t generate a scriptPubKey from (eg, bech32 encoded lightning invoices, or some new address format you don’t yet understand).

    Finding UTXOs by scriptPubKey seems like it makes sense, and per the above finding the scriptPubKeys from an address should also always be straightforward. So ConceptACK fwiw, unless I’m missing something major here…

  122. in test/functional/rpc_scantxoutset.py:7 in 4782d23938 outdated
    0@@ -0,0 +1,76 @@
    1+#!/usr/bin/env python3
    2+# Copyright (c) 2018 The Bitcoin Core developers
    3+# Distributed under the MIT software license, see the accompanying
    4+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
    5+"""Test the scantxoutset rpc call."""
    6+from test_framework.test_framework import BitcoinTestFramework
    7+from test_framework.util import *
    


    promag commented at 7:00 pm on June 24, 2018:
    nit, could specify symbols instead.
  123. in src/rpc/blockchain.cpp:2098 in 4782d23938 outdated
    2101+            UniValue xpub_uni    = find_value(scanobject, "xpub");
    2102+
    2103+            // make sure only one object type is present
    2104+            if (1 != !address_uni.isNull() + !pubkey_uni.isNull() + !script_uni.isNull() + !xpub_uni.isNull()) {
    2105+                throw JSONRPCError(RPC_INVALID_PARAMETER, "Only one object type is allowed per scan object");
    2106+            }
    


    promag commented at 7:29 pm on June 24, 2018:

    nit, } else if (...) {

    There are a couple of more cases throughout.

  124. in src/rpc/blockchain.cpp:2212 in 4782d23938 outdated
    2286+
    2287+            input_txos.push_back(txo);
    2288+            total_in += txo.nValue;
    2289+
    2290+            UniValue unspent(UniValue::VOBJ);
    2291+            unspent.push_back(Pair("txid", outpoint.hash.GetHex()));
    


    promag commented at 7:39 pm on June 24, 2018:
    Should use pushKV instead, push_back(Pair(...)) occurrences were replaced recently.
  125. in src/rpc/blockchain.cpp:2199 in 4782d23938 outdated
    2280+
    2281+            // Fill in dummy signatures for fee calculation, ignore signature verification.
    2282+            const CScript& scriptPubKey = txo.scriptPubKey;
    2283+            SignatureData sigdata;
    2284+            ProduceSignature(temp_keystore, DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigdata);
    2285+            UpdateInput(tx.vin.at(nIn), sigdata);
    


    promag commented at 7:41 pm on June 24, 2018:
    Could drop nIn and use tx.vin.back() instead (like above).

    jonasschnelli commented at 1:53 pm on July 4, 2018:
    More readable like this IMO

    Empact commented at 4:49 am on July 6, 2018:
    Another option is to have a local for the current input, given it’s referenced on 2193 above.
  126. promag commented at 7:44 pm on June 24, 2018: member

    Some nits and comments. Tests LGTM. Could squash last commit.

    utACK.

  127. in src/coins.cpp:33 in 4782d23938 outdated
    28+    count = 0;
    29+    while (cursor.Valid()) {
    30+        COutPoint key;
    31+        Coin coin;
    32+        if (!cursor.GetKey(key) || !cursor.GetValue(coin)) return false;
    33+        if (count++ % 8192 == 0) {
    


    Empact commented at 5:04 pm on July 2, 2018:
    nit: ++count

    jonasschnelli commented at 1:55 pm on July 4, 2018:
    Fixed
  128. in src/rpc/blockchain.cpp:1965 in 4782d23938 outdated
    1934+    bool m_could_reserve;
    1935+public:
    1936+    explicit CoinsViewScanReserver() : m_could_reserve(false) {}
    1937+
    1938+    bool reserve() {
    1939+        assert (!m_could_reserve);
    


    Empact commented at 5:06 pm on July 2, 2018:
    include <assert.h> nit: whitespace could benefit from clang-format

    jonasschnelli commented at 1:55 pm on July 4, 2018:
    Fixed

    promag commented at 3:04 pm on July 12, 2018:

    Commit “Blockchain/RPC: Add scantxoutset method to scan UTXO set”

    Missing fix :trollface:

  129. in src/rpc/blockchain.cpp:1999 in 4782d23938 outdated
    1968+{
    1969+    if (outputtype == "P2PK") return OutputScriptType::P2PK;
    1970+    else if (outputtype == "P2PKH") return OutputScriptType::P2PKH;
    1971+    else if (outputtype == "P2SH_P2WPKH") return OutputScriptType::P2SH_P2WPKH;
    1972+    else if (outputtype == "P2WPKH") return OutputScriptType::P2WPKH;
    1973+    else return OutputScriptType::UNKNOWN;
    


    Empact commented at 5:08 pm on July 2, 2018:
    nit: this is basically an ideal case for a case statement. :P

    jimpo commented at 6:32 pm on July 2, 2018:
    You can’t switch on strings in C/C++

    Empact commented at 7:20 pm on July 2, 2018:
    Ah, thanks.
  130. in src/rpc/blockchain.cpp:2107 in 4782d23938 outdated
    2113+            else if (!xpub_uni.isNull() && !xpub_uni.isObject()) {
    2114+                throw JSONRPCError(RPC_INVALID_PARAMETER, "Scanobject \"xpub\" must contain an object as value");
    2115+            }
    2116+            else if (!script_uni.isNull() && !script_uni.isStr()) {
    2117+                throw JSONRPCError(RPC_INVALID_PARAMETER, "Scanobject \"xpub\" must contain a single string as value");
    2118+            }
    


    Empact commented at 5:13 pm on July 2, 2018:
    You could consider using RPCTypeCheckObj for some of this here and elsewhere. E.g. https://github.com/bitcoin/bitcoin/blob/686e97a0c7358291d628213447cf33e99cde7ce8/src/rpc/rawtransaction.cpp#L786-L791
  131. sipa commented at 6:40 pm on July 2, 2018: member
    As discussed in the meeting (http://www.erisian.com.au/meetbot/bitcoin-core-dev/2018/bitcoin-core-dev.2018-06-28-19.00.log.html), my suggestion is to (for now) drop the xpub support from this PR. That way we have some more time for a generic notation for chains of scripts to develop (something I’m currently working on) and be compatible with other places such a notation may be adopted.
  132. laanwj commented at 9:20 am on July 4, 2018: member
    Agree with @sipa - that way, we can move this forward without full agreement on xpub representation.
  133. jonasschnelli force-pushed on Jul 4, 2018
  134. jonasschnelli commented at 10:11 am on July 4, 2018: contributor
    Removed the xpub support. Thanks for reviewing again.
  135. laanwj commented at 12:34 pm on July 4, 2018: member
    re-utACK 3bf8dbe48a1f45b7aa0584bfb0cf19a7ddf492c9
  136. in src/rpc/blockchain.cpp:2157 in 3bf8dbe48a outdated
    2152+                    if (script_type == OutputScriptType::UNKNOWN) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid script type");
    2153+                    CScript script;
    2154+                    if (script_type == OutputScriptType::P2PK) {
    2155+                        // support legacy P2PK scripts
    2156+                        script << ToByteVector(pubkey) << OP_CHECKSIG;
    2157+                    }
    


    laanwj commented at 12:36 pm on July 4, 2018:
    nit: } else { (some other instances too)

    jonasschnelli commented at 1:54 pm on July 4, 2018:
    Fixed
  137. jonasschnelli commented at 1:54 pm on July 4, 2018: contributor
    Fixed nits.
  138. jonasschnelli force-pushed on Jul 4, 2018
  139. promag commented at 8:46 pm on July 4, 2018: member

    While testing I hit an edge case:

     0./src/bitcoin-cli -regtest  scantxoutset start '[]' & ;  ./src/bitcoin-cli -regtest  scantxoutset abort
     1[1] 90388
     2true
     3{
     4  "success": "yes",
     5  "searched_items": 6,
     6  "unspents": [
     7  ],
     8  "total_amount": 0.00000000
     9}
    10[1]  + 90388 done       ./src/bitcoin-cli -regtest scantxoutset start '[]'
    

    So abort returned true but the scan finished successfully.

    Not sure but if the use case is to call abort after status — because it’s taking too long — so maybe abort could just return null:

    0if (request.params[0].get_str() == "abort") {
    1    g_should_abort_scan = true;
    2    return NullUniValue;
    3}
    
  140. laanwj commented at 11:44 am on July 5, 2018: member
    I’m not sure that’s a harmful edge case. If you abort after the last opportunity to abort, I’d say it makes sense that it will finish successfully.
  141. promag commented at 11:51 am on July 5, 2018: member
    That’s why I ask if it makes sense to return a bool in abort.
  142. in src/coins.cpp:32 in e5520b0249 outdated
    43-                uint32_t high = 0x100 * *key.hash.begin() + *(key.hash.begin() + 1);
    44-                scan_progress = (int)(high * 100.0 / 65536.0 + 0.5);
    45-            }
    46-            if (needles.count(coin.out.scriptPubKey)) {
    47-                out_results.emplace(key, coin);
    48+        if (!cursor.GetKey(key) || !cursor.GetValue(coin)) return false;
    


    sipa commented at 3:51 am on July 6, 2018:

    In commit “Blockchain/RPC: Add scantxoutset method to scan UTXO set”.

    The implementation of “CCoinsView::FindScriptPubKey” is being changed significantly in this commit. Can you move the changes to the previous commit that introduces the function?

  143. in src/rpc/blockchain.cpp:2037 in e5520b0249 outdated
    2002+            "                                      \"abort\" for aborting the current scan (returns true when abort was successful)\n"
    2003+            "                                      \"status\" for progress report (in %) of the current scan\n"
    2004+            "2. \"scanobjects\"                  (array, required) Array of scan objects (only one object type per scan object allowed)\n"
    2005+            "      [\n"
    2006+            "        { \"address\" : \"<address>\" },       (string, optional) Bitcoin address\n"
    2007+            "        { \"pubkey\"  :                      (object, optional) Public key\n"
    


    sipa commented at 3:54 am on July 6, 2018:

    I would leave this out as well, as it’ll be handled equally by the descriptor based approach. It’s a lot of complexity to maintain which will be handled more generically later.

    Also, I don’t think it’s very useful and add - if you know the pubkey and derivation type you generally also know the address already.

  144. in src/rpc/blockchain.cpp:2037 in 625ac1e034 outdated
    2031@@ -2012,6 +2032,12 @@ UniValue scantxoutset(const JSONRPCRequest& request)
    2032             "          }\n"
    2033             "        },\n"
    2034             "      ]\n"
    2035+            "3. \"options\"                               (object, optional)\n"
    2036+            "      \"rawsweep\": {                        (object, optional) Optionally creates a raw sweep transaction\n"
    


    sipa commented at 4:01 am on July 6, 2018:

    In commit “scantxoutset: Add optional raw sweep transaction”.

    Is this the best place to implement this? As a separate wallet RPC it could work much more correctly (as it would know the actual scripts involved and be able to give a vsize estimate that’s more than a dumb guess), and be more useful too by being combinable with whatever way to find inputs.

    I feel this makes the RPC look like a kitchen sink.


    jonasschnelli commented at 7:31 pm on July 8, 2018:
    Agree. Will remove it and PR it later as a separate method

    promag commented at 0:20 am on July 11, 2018:

    From #12196 (comment)

    tx = scantxoutset(“start”, { “sweep_to”: address })[“sweep_tx”] fundrawtransaction(tx, { “subtractFeeFromOutputs”: [0] })

    @promag: would that also work without the wallet? My idea was to make scantxoutset work without a wallet. @sipa not sure if you are suggesting something similar to:

    0unspents = scantxoutset "start" ...
    1tx = createrawtransaction unspents ...
    2tx = fundrawtransaction ...
    

    And how could it work without the wallet?


    sipa commented at 4:05 pm on July 11, 2018:

    @promag, I just mean scantxoutset + createrawtransaction. Then a separate wallet RPC like “walletestimatesignedrawtransactionsize” (shorter names welcome…) can be used to guess the size of the tx ahead of time. “fundrawtransaction” already does this estimation internally.

    Would that also work without the wallet?

    No, that’s the point. You cannot implement that without knowledge of the scripts involved.

    It may be better to integrate this into PSBT, as it means there doesn’t need to a single wallet that knows about all scripts in a transaction.

  145. jonasschnelli force-pushed on Jul 12, 2018
  146. jonasschnelli force-pushed on Jul 12, 2018
  147. jonasschnelli force-pushed on Jul 12, 2018
  148. jonasschnelli commented at 10:17 am on July 12, 2018: contributor
    • Moved the FindScriptPubKey() method to rpc/blockchain.cpp (changes are no longer spread in multiple commits).
    • Removed the sweep transaction creation code (see #12196#pullrequestreview-134880350)

    I kept the pubkey search option since I think this is very helpful. I agree that we should move to the descriptors specified by @sipa. IMO we define that we wanted to have xpub/pub support (according those descriptors) in the same release to avoid API breaks and @laanwj agreed to let this in after the code freeze in form of a bugfix. Additionally, there is always the option to mark this RPC command “experimental” (API changes possible).

  149. in src/coins.cpp:10 in c0258ed687 outdated
     6@@ -7,6 +7,7 @@
     7 #include <consensus/consensus.h>
     8 #include <random.h>
     9 
    10+
    


    promag commented at 2:05 pm on July 12, 2018:

    Commit “Add FindScriptPubKey() to search the UTXO set”

    nit, unrelated change.

  150. in src/rpc/blockchain.cpp:1920 in c0258ed687 outdated
    1915@@ -1916,6 +1916,35 @@ static UniValue savemempool(const JSONRPCRequest& request)
    1916     return NullUniValue;
    1917 }
    1918 
    1919+//! Search for a given set of pubkey scripts
    1920+bool FindScriptPubKey(std::atomic<int>& scan_progress, const std::atomic<bool>& should_abort, int64_t& count, CCoinsViewCursor *cursor, const std::set<CScript>& needles, std::map<COutPoint, Coin>& out_results) {
    


    promag commented at 2:06 pm on July 12, 2018:

    Commit “Add FindScriptPubKey() to search the UTXO set”

    nit, CCoinsViewCursor* cursor.

  151. in src/rpc/blockchain.cpp:2183 in c3c4b144d6 outdated
    2178+
    2179+        std::unique_ptr<CCoinsViewCursor> pcursor(pcoinsdbview->Cursor());
    2180+        assert(pcursor);
    2181+        bool res = FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, pcursor.get(), needles, coins);
    2182+
    2183+        result.push_back(Pair("success", res ? "yes" : "no"));
    


    luke-jr commented at 2:24 pm on July 12, 2018:
    This should probably be true|false, not a string.
  152. luke-jr changes_requested
  153. luke-jr commented at 2:26 pm on July 12, 2018: member
    Maintaining a NACK so long as it confuses addresses (which are unrelated to UTXOs) with keys…
  154. in src/rpc/blockchain.cpp:2011 in 85eb5ea3f9 outdated
    2006+    case OutputScriptType::P2PKH: return key.GetID();
    2007+    case OutputScriptType::P2SH_P2WPKH:
    2008+    case OutputScriptType::P2WPKH: {
    2009+        if (!key.IsCompressed()) return key.GetID();
    2010+        CTxDestination witdest = WitnessV0KeyHash(key.GetID());
    2011+        CScript witprog = GetScriptForDestination(witdest);
    


    promag commented at 3:08 pm on July 12, 2018:

    Commit “Blockchain/RPC: Add scantxoutset method to scan UTXO set”

    This could be in the block below:

    0CTxDestination witdest = WitnessV0KeyHash(key.GetID());
    1if (type == OutputScriptType::P2SH_P2WPKH) {
    2    CScript witprog = GetScriptForDestination(witdest);
    3    return CScriptID(witprog);
    4} else {
    5    ...
    
  155. in src/rpc/blockchain.cpp:2026 in 85eb5ea3f9 outdated
    2021+
    2022+UniValue scantxoutset(const JSONRPCRequest& request)
    2023+{
    2024+    if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
    2025+        throw std::runtime_error(
    2026+            "scantxoutset <action> <scanobjects> ( <options> )\n"
    


    promag commented at 3:11 pm on July 12, 2018:

    Commit “Blockchain/RPC: Add scantxoutset method to scan UTXO set”

    nit, add ( ) to <scanobjects> because status action has no more arguments.

  156. in src/rpc/blockchain.cpp:2162 in 85eb5ea3f9 outdated
    2157+        g_should_abort_scan = false;
    2158+        g_scan_progress = 0;
    2159+        int64_t count = 0;
    2160+        FlushStateToDisk();
    2161+
    2162+        std::unique_ptr<CCoinsViewCursor> pcursor(pcoinsdbview->Cursor());
    


    sipa commented at 5:45 pm on July 12, 2018:
    You should hold cs_main while calling FlushStateToDisk and creating the cursor, or there is no guarantee that the on-disk state is consistent.

    promag commented at 7:50 pm on July 12, 2018:
    Looks like some annotations or lock assertions could be added?

    jonasschnelli commented at 8:04 pm on July 12, 2018:
    Agree with @promag, but unrelated to this PR.

    promag commented at 8:15 pm on July 12, 2018:
    Oh, I wasn’t implying that. I’ll follow up.

    promag commented at 8:19 pm on July 12, 2018:
    Ops, actually @sipa point is about locking during FlushStateToDisk() and Cursor(), so the lock must be added here, preferably in this PR IMO.
  157. in src/rpc/blockchain.cpp:2082 in 85eb5ea3f9 outdated
    2077+        CoinsViewScanReserver reserver;
    2078+        if (!reserver.reserve()) {
    2079+            throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan already in progress, use action \"abort\" or \"status\"");
    2080+        }
    2081+        std::set<CScript> needles;
    2082+        CBasicKeyStore temp_keystore;
    


    sipa commented at 5:45 pm on July 12, 2018:
    This looks unused.
  158. Add FindScriptPubKey() to search the UTXO set 9048575511
  159. jonasschnelli force-pushed on Jul 12, 2018
  160. jonasschnelli commented at 7:45 pm on July 12, 2018: contributor

    Fixed issues:

    • holds now cs_main during coins view cursor creation and FlushToDisk()
    • Removed temp_keystore (relict from raw-sweep-tx).
    • Fixed “yes”/“no” instead of true/false JSON returns
    • Fixed nits
  161. in src/rpc/blockchain.cpp:1941 in e24565cd41 outdated
    1936+            }
    1937+        }
    1938+        if (count % 256 == 0) {
    1939+            // update progress reference every 256 item
    1940+            uint32_t high = 0x100 * *key.hash.begin() + *(key.hash.begin() + 1);
    1941+            scan_progress = (int)(high * 100.0 / 65536.0 + 0.5);
    


    jamesob commented at 8:47 pm on July 12, 2018:

    Nit: code for this seems like it’s cropping up in a few different places, might be nice to have an abstraction for it.

    0 $ git grep -C 1 "/ 65536" | cat
    1
    2src/index/txindex.cpp-                (static_cast<uint32_t>(*(txid.begin() + 1)) << 0);
    3src/index/txindex.cpp:            int percentage_done = (int)(high_nibble * 100.0 / 65536.0 + 0.5);
    4src/index/txindex.cpp-
    5--
    6src/txdb.cpp-                uint32_t high = 0x100 * *key.second.begin() + *(key.second.begin() + 1);
    7src/txdb.cpp:                int percentageDone = (int)(high * 100.0 / 65536.0 + 0.5);
    8src/txdb.cpp-                uiInterface.ShowProgress(_("Upgrading UTXO database"), percentageDone, true);
    

    jonasschnelli commented at 8:58 am on July 13, 2018:
    IMO this should be done outside of this PR
  162. in src/rpc/blockchain.cpp:2025 in e24565cd41 outdated
    2020+
    2021+UniValue scantxoutset(const JSONRPCRequest& request)
    2022+{
    2023+    if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
    2024+        throw std::runtime_error(
    2025+            "scantxoutset <action> ( <scanobjects> ) ( <options> )\n"
    


    jamesob commented at 8:56 pm on July 12, 2018:
    If scanobjects is required (per doc below), should it be in parens here?

    jamesob commented at 8:57 pm on July 12, 2018:
    Also options doesn’t seem to exist.
  163. in src/rpc/blockchain.cpp:2177 in e24565cd41 outdated
    2172+        {
    2173+            LOCK(cs_main);
    2174+            FlushStateToDisk();
    2175+            std::unique_ptr<CCoinsViewCursor> pcursor(pcoinsdbview->Cursor());
    2176+            assert(pcursor);
    2177+            bool res = FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, pcursor.get(), needles, coins);
    


    jamesob commented at 9:13 pm on July 12, 2018:
    Can we release cs_main after initializing pcursor? My impression is that CCoinsViewCursor can be used for read-only ops without holding it.

    jonasschnelli commented at 8:57 am on July 13, 2018:
    Oh. Right. Fixed.
  164. jamesob commented at 9:15 pm on July 12, 2018: member

    Concept ACK

    Happy to do a more in-depth review. PR description looks in need of an update.

  165. jonasschnelli force-pushed on Jul 13, 2018
  166. jonasschnelli force-pushed on Jul 13, 2018
  167. jonasschnelli commented at 8:58 am on July 13, 2018: contributor
    Fixed points reported by @jamesob.
  168. in src/rpc/blockchain.cpp:2180 in bc357cb0f3 outdated
    2175+            FlushStateToDisk();
    2176+            pcursor = std::unique_ptr<CCoinsViewCursor>(pcoinsdbview->Cursor());
    2177+            assert(pcursor);
    2178+        }
    2179+        bool res = FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, pcursor.get(), needles, coins);
    2180+        result.pushKV("success", res ? true : false);
    


    luke-jr commented at 1:48 pm on July 13, 2018:
    res is already a boolean…

    jonasschnelli commented at 7:32 pm on July 13, 2018:
    Ouch! Fixed.
  169. jonasschnelli force-pushed on Jul 13, 2018
  170. in src/rpc/blockchain.cpp:2149 in bd345b7611 outdated
    2140@@ -2141,8 +2141,13 @@ UniValue scantxoutset(const JSONRPCRequest& request)
    2141                 for (const UniValue& script_type_uni : script_types_uni.get_array().getValues()) {
    2142                     OutputScriptType script_type = GetOutputScriptTypeFromString(script_type_uni.get_str());
    2143                     if (script_type == OutputScriptType::UNKNOWN) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid script type");
    2144-
    2145-                    CScript script = GetScriptForDestination(GetDestinationForKey(pubkey, script_type));
    2146+                    CScript script;
    2147+                    if (script_type == OutputScriptType::P2PK) {
    2148+                        // support legacy P2PK scripts
    2149+                        script << ToByteVector(pubkey) << OP_CHECKSIG;
    


    sipa commented at 2:10 am on July 14, 2018:
    Nit: use GetScriptForRawPubKey.
  171. sipa commented at 2:13 am on July 14, 2018: member
    utACK b9b59cc7ba742bb519d34f3011500f09a8c04911
  172. in src/rpc/blockchain.cpp:2079 in b9b59cc7ba outdated
    2072+        if (reserver.reserve()) {
    2073+            return false;
    2074+        }
    2075+        g_should_abort_scan = true;
    2076+        return true;
    2077+    } else if (request.params[0].get_str() == "start") {
    


    Empact commented at 1:15 pm on July 15, 2018:
    nit: Would be nice to store this repeated arg to a local
  173. in src/rpc/blockchain.cpp:2104 in b9b59cc7ba outdated
    2097+            } else if (!address_uni.isNull() && !address_uni.isStr()) {
    2098+                throw JSONRPCError(RPC_INVALID_PARAMETER, "Scanobject \"address\" must contain a single string as value");
    2099+            } else if (!pubkey_uni.isNull() && !pubkey_uni.isObject()) {
    2100+                throw JSONRPCError(RPC_INVALID_PARAMETER, "Scanobject \"pubkey\" must contain an object as value");
    2101+            } else if (!script_uni.isNull() && !script_uni.isStr()) {
    2102+                throw JSONRPCError(RPC_INVALID_PARAMETER, "Scanobject \"script\" must contain a single string as value");
    


    Empact commented at 1:36 pm on July 15, 2018:

    You could remove most of the above with:

    0RPCTypeCheckObj(scanobject,
    1    {
    2        {"address", UniValueType(UniValue::VSTR)},
    3        {"pubkey", UniValueType(UniValue::VOBJ)},
    4        {"script", UniValueType(UniValue::VSTR)},
    5    }, true /* fAllowNull */);
    

    jonasschnelli commented at 7:29 pm on July 15, 2018:
    I guess your proposed check does not ensure that one of the (either address, pubkey or script must be present, right?

    Empact commented at 9:09 pm on July 15, 2018:
    Right, it just handles the type testing.
  174. in src/rpc/blockchain.cpp:2097 in b9b59cc7ba outdated
    2090+            UniValue address_uni = find_value(scanobject, "address");
    2091+            UniValue pubkey_uni  = find_value(scanobject, "pubkey");
    2092+            UniValue script_uni  = find_value(scanobject, "script");
    2093+
    2094+            // make sure only one object type is present
    2095+            if (1 != !address_uni.isNull() + !pubkey_uni.isNull() + !script_uni.isNull()) {
    


    Empact commented at 1:36 pm on July 15, 2018:
    nit: explicit precedence?

    jonasschnelli commented at 7:30 pm on July 15, 2018:
    I think it helps for the readability? Or can you elaborate what you mean exactly?

    Empact commented at 9:08 pm on July 15, 2018:
    Suggesting to group the sum in parens so that it’s more clear that there is only the one top-level condition.
  175. in src/rpc/blockchain.cpp:2078 in b9b59cc7ba outdated
    2071+        CoinsViewScanReserver reserver;
    2072+        if (reserver.reserve()) {
    2073+            return false;
    2074+        }
    2075+        g_should_abort_scan = true;
    2076+        return true;
    


    Empact commented at 2:09 pm on July 15, 2018:

    Could you document the “status” and “abort” results above?

    nit: I’d somewhat prefer each of these having their own rpc

  176. in src/rpc/blockchain.cpp:2145 in b9b59cc7ba outdated
    2139+                }
    2140+
    2141+                // loop through the script types and derive the script
    2142+                for (const UniValue& script_type_uni : script_types_uni.get_array().getValues()) {
    2143+                    OutputScriptType script_type = GetOutputScriptTypeFromString(script_type_uni.get_str());
    2144+                    if (script_type == OutputScriptType::UNKNOWN) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid script type");
    


    Empact commented at 2:50 pm on July 15, 2018:
    Could throw above and do away with UNKNOWN

    jonasschnelli commented at 7:38 pm on July 15, 2018:
    I don’t understand your comment… can you rephrase or elaborate in detail?

    Empact commented at 9:07 pm on July 15, 2018:
    If you throw on UNKNOWN within GetOutputScriptTypeFromString, then there is no other use of UNKNOWN as an OutputScriptType.
  177. in src/rpc/blockchain.cpp:1983 in b9b59cc7ba outdated
    1978+            g_scan_in_progress = false;
    1979+        }
    1980+    }
    1981+};
    1982+
    1983+const char *g_default_scantxoutset_script_types[] = { "P2PKH", "P2SH_P2WPKH", "P2WPKH" };
    


    Empact commented at 2:50 pm on July 15, 2018:
    nit: maybe make this a unival to avoid translation below? nit: why not static?

    jonasschnelli commented at 8:18 pm on July 15, 2018:
    Agree. Made static.
  178. in src/rpc/blockchain.cpp:2136 in b9b59cc7ba outdated
    2131+                // check the acctual pubkey
    2132+                if (!pubkeydata_uni.isStr() || !IsHex(pubkeydata_uni.get_str())) {
    2133+                    throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Public key must be hex encoded");
    2134+                }
    2135+                std::vector<unsigned char> data(ParseHexV(pubkeydata_uni, "pubkey"));
    2136+                CPubKey pubkey(data.begin(), data.end());
    


    Empact commented at 2:53 pm on July 15, 2018:

    jonasschnelli commented at 8:19 pm on July 15, 2018:
    Indeed. Fixed.
  179. in src/rpc/blockchain.cpp:2159 in b9b59cc7ba outdated
    2154+                }
    2155+            } else if (script_uni.isStr()) {
    2156+                // type: script
    2157+                // check and add the script to the scan containers (needles array)
    2158+                std::vector<unsigned char> scriptData(ParseHexV(script_uni, "script"));
    2159+                CScript script(scriptData.begin(), scriptData.end());
    



    jonasschnelli commented at 8:19 pm on July 15, 2018:
    Fixed.
  180. in test/functional/rpc_scantxoutset.py:41 in b9b59cc7ba outdated
    36+
    37+        self.restart_node(0, ['-nowallet'])
    38+        self.log.info("Test if we have found the non HD unspent outputs.")
    39+        assert_equal(self.nodes[0].scantxoutset("start", [ {"pubkey": {"pubkey": pubk1}}, {"pubkey": {"pubkey": pubk2}}, {"pubkey": {"pubkey": pubk3}}])['total_amount'], 6)
    40+        assert_equal(self.nodes[0].scantxoutset("start", [ {"address": addr_P2SH_SEGWIT}, {"address": addr_LEGACY}, {"address": addr_BECH32}])['total_amount'], 6)
    41+        assert_equal(self.nodes[0].scantxoutset("start", [ {"address": addr_P2SH_SEGWIT}, {"address": addr_LEGACY}, {"pubkey": {"pubkey": pubk3}} ])['total_amount'], 6)
    


    Empact commented at 2:59 pm on July 15, 2018:
    Tests for “abort” and “status”?

    jonasschnelli commented at 7:56 pm on July 15, 2018:
    Both would probably require additional code for adding time delays or temporary creation of a large utxo set.
  181. in src/rpc/blockchain.cpp:1927 in b9b59cc7ba outdated
    1919@@ -1916,6 +1920,290 @@ static UniValue savemempool(const JSONRPCRequest& request)
    1920     return NullUniValue;
    1921 }
    1922 
    1923+//! Search for a given set of pubkey scripts
    1924+bool FindScriptPubKey(std::atomic<int>& scan_progress, const std::atomic<bool>& should_abort, int64_t& count, CCoinsViewCursor* cursor, const std::set<CScript>& needles, std::map<COutPoint, Coin>& out_results) {
    1925+    scan_progress = 0;
    1926+    count = 0;
    1927+    while (cursor->Valid()) {
    


    Empact commented at 3:03 pm on July 15, 2018:
    nit: for would localize the cursor access
  182. Blockchain/RPC: Add scantxoutset method to scan UTXO set 78304941f7
  183. scantxoutset: add support for scripts 892de1dfea
  184. scantxoutset: support legacy P2PK script type 94d73d32ab
  185. scantxoutset: mention that scanning by address will miss P2PK txouts eec7cf7b33
  186. [QA] Add scantxoutset test be98b2d9a8
  187. jonasschnelli force-pushed on Jul 15, 2018
  188. jonasschnelli commented at 8:20 pm on July 15, 2018: contributor
    Fixed relevant points from @Empact
  189. laanwj added this to the milestone 0.17.0 on Jul 17, 2018
  190. laanwj merged this on Jul 17, 2018
  191. laanwj closed this on Jul 17, 2018

  192. laanwj referenced this in commit 8fceae0d6f on Jul 17, 2018
  193. promag commented at 2:39 pm on July 17, 2018: member
    utACK be98b2d.
  194. jonasschnelli removed this from the "Blockers" column in a project

  195. laanwj referenced this in commit f030410e88 on Aug 1, 2018
  196. surindergiri commented at 11:44 am on October 1, 2020: none

    Thanks for your great efforts implementing this feature, On Oct 01, 2020, When I run this RPC command on Bitcoin Node version 0.20.0, then its response is too slow, I am getting rpc response after 1-2 minutes.

    Is there any way to speed up this “scantxoutset” RPC command or any alternative available?

    I asked the same question over bitcoin stackexchange forum, but nobody responded. I know its not the right place to ask such questions, but I am stuck, our mobile wallet is live and we are unable to fetch user “imported” address balances on the fly.

    Thanks.

    https://bitcoin.stackexchange.com/questions/99278/bitcoin-scantxoutset-rpc-command-return-response-too-slow

  197. promag commented at 2:53 pm on October 1, 2020: member

    I asked the same question over bitcoin stackexchange forum, but nobody responded @surindergiri that was like 3 hours before you posted here..

  198. MarcoFalke referenced this in commit c47979778d on Mar 15, 2021
  199. sidhujag referenced this in commit 1bd24c7600 on Mar 15, 2021
  200. kittywhiskers referenced this in commit 3a4ff82bf6 on Jun 4, 2021
  201. kittywhiskers referenced this in commit e3ff2aa9b9 on Jun 9, 2021
  202. kittywhiskers referenced this in commit 0492d5096e on Jul 9, 2021
  203. kittywhiskers referenced this in commit 63ea35b1d8 on Jul 13, 2021
  204. kittywhiskers referenced this in commit 3da1c7ab03 on Jul 13, 2021
  205. kittywhiskers referenced this in commit 931a88e2fd on Jul 14, 2021
  206. kittywhiskers referenced this in commit e493c8a74b on Jul 20, 2021
  207. PastaPastaPasta referenced this in commit edf0552c0c on Jul 26, 2021
  208. gades referenced this in commit 61ccba8d28 on May 2, 2022
  209. DrahtBot locked this on Aug 16, 2022

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2024-07-05 19:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me