Conversation
|
It's not overly complicated to maintain this index outside of edit: See #8660 (comment) |
|
NACK. Txouts are not associated with addresses at all. |
|
@luke-jr They are, but this association is not useful for 99.9% If people need this, please contribute some description/work using the external indexing solution into |
|
Concept ACK from me. |
qa/rpc-tests/txoutsbyaddress.sh
Outdated
There was a problem hiding this comment.
We don't use bash anymore in /rpc-tests
There was a problem hiding this comment.
Yes, please look at the existing tests in qa/rpc-tests, use the Python framework. Also add the test to qa/pull-tester/rpc-tests.py so it gets executed by default.
|
The inspiration for this PR is to try and make it easier to build a fully validating wallet that does not use significantly more resources then a full node itself. It seems that most (all?) current solutions for building a wallet either involve added trust (SPV or centralized API services) or added resource requirements (bitcore.io needs 8GB RAM and 200GB disk space) Thats the rationale (which I probably should have stated at the outset) anyway. The last time I was trying to make a demo it was easier just to use the blockcypher API, or maybe there are projects that I missed that fill this gap? |
|
If the RPC was by script (as the internals seem to be) instead of by address, it might make sense. |
IMHO, that is where |
|
@luke-jr the RPC accepts both hex encoded script or addresses |
These are good point! Some thoughts:
|
As someone who does this approach, the only difficulties is ensuring that you handle orphans and forks properly. |
|
Indexes are desperately needed, there are quite a few users of my addrindex fork https://github.com/btcdrak/bitcoin/releases that demonstrate this need and there is a steady flow of people finding out about it. This PR is a more lightweight option and I think it should be part of Bitcoin Core in an easily accessible way (i.e. just turn on with a switch+reindex). While I could say add it to my fork, it is does reduce the visibility of it. |
|
Indexing the UTXO set like this PR does is less bad - at least it doesn't prevent pruning - but what function would it provide apart from RPCs? The wallet can't actually use it (as it would miss historical transactions), and it won't be available in SPV mode. |
|
Agree with @sipa. A maybe more scalable way how "wallet" (or lets say something like the BWS [bitpay wallet service]) could be built on top of bitcoin core would be:
"Wallets" would then be reduces to...
Restoring backups is the only feature I can think off that would require a addr-index (otherwise rescanning will be very slow). |
|
The entire point of running a node is to provide validation for a wallet somewhere. That is what makes a node significant. Is it actually simple to provide this service outside of bitcoin-core? Are there any projects that manage to do it without duplicating the entire blockchain into their own database? It seems a real shame if we go to the trouble of assembling the UTXO set and cant provide ways for external wallets to leverage it. @sipa are you sure it would miss historical transactions? Any transactions adding funds to the wallet will show up in the UTXO set and any transactions spending funds from the wallet can only be created by the wallet itself (I guess they can be malleated). @jonasschnelli I think I like this idea. It might not be as flexible in the kinds of external wallets it allows though. |
Of course, but this index is not small by any means. |
|
I think this functionality is very useful. E.g. to get the current balance for an arbitrary address/script.
Concept ACK.
There may be use for a quick-rescan that ignores historical transactions, which can be used with pruning. (out of scope for this pull, but it has been brought up before - see #8497) |
No it's not necessary - only if there is a straightforward binary representation like for transactions and blocks. You don't have to make up one. |
|
I wasn't aware this was just for UTXOs, not txouts at any point in history (aka, a full txout index, similar to the addrindex fork). light concept ACK |
|
Concept ACK. Several projects I know of use the addrindex stuff from years ago to derive information about UTXO state, and this is much more elegant. |
src/init.cpp
Outdated
There was a problem hiding this comment.
My personal preference would be something shorter like "txoutsindex", especially because it really indexes scripts and not addresses.
There was a problem hiding this comment.
Sure I dont see a problem with that
There was a problem hiding this comment.
Or just txoutindex (or utxoutindex or utxoindex)
There was a problem hiding this comment.
argh, paradox of choice!
Ok, how about if nobody has any objection I will change the name to "txoutindex" (command line parameter, RPC, REST etc)?
|
Ok I converted the RPC tests to python and added some REST tests |
qa/rpc-tests/rest.py
Outdated
There was a problem hiding this comment.
Nit: You can put something like the following up a few lines, so the scope is not limited to setup_network.
self.extra_args = [["-txoutsbyaddressindex"]] * 3
qa/rpc-tests/txoutsbyaddress.py
Outdated
There was a problem hiding this comment.
Nit: I think this is not required
qa/rpc-tests/txoutsbyaddress.py
Outdated
There was a problem hiding this comment.
self.nodes = start_nodes(3, self.options.tmpdir, self.extra_args)should work as well. (You can put self.extra_args = [['-txoutindex'], []. []] in __init__())
qa/rpc-tests/txoutsbyaddress.py
Outdated
There was a problem hiding this comment.
Nit: you can move the connect_*s up into setup_network(). I think all other tests do this
qa/rpc-tests/txoutsbyaddress.py
Outdated
There was a problem hiding this comment.
node 2 is stopped so that later on it can be restarted and then orphan a block created by node 1 (to test txoutindex handles the reorg)
basically its stopped so I will not build on a block created by node 1
There was a problem hiding this comment.
Should probably mention this in a comment.
|
addressed review nits by @MarcoFalke (thank you) |
qa/pull-tester/rpc-tests.py
Outdated
|
needs rebase |
|
Concept ACK. Comment, can others provide feeback: I think the argument should be changed to unspentoutputindex or utxoindex or something like that, otherwise it sounds like it indexes everything. Similar for the rpc names. |
|
Hmmm. If @djpnewton doesn't pick this up again in the next few days, I'll seriously consider keeping it going. |
- remove skeleton HEX/BIN format support from REST interface for gettxoutsbyaddress endpoint
| } | ||
| }; | ||
|
|
||
| typedef std::map<uint160, CCoinsByScript> CCoinsMapByScript; // uint160 = hash of script |
There was a problem hiding this comment.
Maybe replace uint160 with CScriptID to make the map type more obvious.
ryanofsky
left a comment
There was a problem hiding this comment.
Reviewed most of this PR, but not the whole thing. Mostly minor comments, except for a possible bug in CCoinsViewByScript::FetchCoinsByScript.
| // Return a modifiable reference to a CCoinsByScript. | ||
| CCoinsByScript &GetCoinsByScript(const CScript &script, bool fRequireExisting = true); | ||
|
|
||
| static uint160 getKey(const CScript &script); // we use the hash of the script as key in the database |
There was a problem hiding this comment.
Could remove if using CScriptID as the map key type.
| uint256 hashSerialized; | ||
| CAmount nTotalAmount; | ||
|
|
||
| CCoinsStats() : nHeight(0), nTransactions(0), nTransactionOutputs(0), nAddresses(0), nAddressesOutputs(0), nSerializedSize(0), nTotalAmount(0) {} |
There was a problem hiding this comment.
Could assign these values inline above with c++11.
| if (fRequireExisting) | ||
| return cacheCoinsByScript.end(); | ||
| } | ||
| CCoinsMapByScript::iterator ret = cacheCoinsByScript.insert(it, std::make_pair(key, CCoinsByScript())); |
There was a problem hiding this comment.
Shouldn't this be inserting tmp into the map, not an empty CCoinsByScript() set?
Also,
- It would be nice if this code had some basic c++ unit tests similar to coins_tests.cpp, even if they aren't as comprehensive.
- Don't really need the other temporary
retvariable here, could just return insert().second. - Could replace insert with emplace or emplace_hint here to avoid needing to call make_pair.
| CCoinsMapByScript::const_iterator it = mapCoinsByScript.find(CCoinsViewByScript::getKey(script)); | ||
| if (it != mapCoinsByScript.end()) | ||
| { | ||
| BOOST_FOREACH(const COutPoint &outpoint, it->second.setCoins) |
There was a problem hiding this comment.
could avoid the loop with
coinsByScript.setCoins.insert(it->second.setCoins.begin(), it->second.setCoins.end());
| i++; | ||
| pcursor->Next(); | ||
| } catch (std::exception &e) { | ||
| return 0; |
There was a problem hiding this comment.
Why is this exception being caught instead of passed on to the caller? I'd think it'd be better to remove the catch and have the error propogate up, or add a comment here which explains the cases where std::exception is expected and why they should return 0.
| BOOST_AUTO_TEST_CASE(MempoolIndexingTest) | ||
| { | ||
| CTxMemPool pool(CFeeRate(0)); | ||
| CTxMemPool pool(CFeeRate(0), false); |
There was a problem hiding this comment.
The CTxMemPool constructor saves a const reference to the second argument, so instead of passing literal false, you should pass a variable with a lifetime at least as long as CTxMemPool. Might be better to have the CTxMemPool class just store a bool instead of a reference to a bool to avoid the need to do this.
qa/rpc-tests/txoutsbyaddress.py
Outdated
There was a problem hiding this comment.
Should probably mention this in a comment.
| self.sync_all() | ||
| assert_equal(self.nodes[0].getbalance(), 5090) | ||
| assert_equal(self.nodes[1].getbalance(), 10) | ||
| txouts = self.nodes[0].gettxoutsbyaddress(1, (address,)) |
There was a problem hiding this comment.
Maybe check other txouts fields here as well.
| if (!hashBlock.IsNull()) | ||
| batch.Write(DB_BEST_BLOCK, hashBlock); | ||
|
|
||
| LogPrint("coindb", "Committing %u coin address indexes to coin database...\n", (unsigned int)count); |
There was a problem hiding this comment.
Could use %z instead of (unsigned int) cast, I think.
|
Needs rebase. |
|
rebased I will start addressing review by @ryanofsky (cheers mate) Agree with @gmaxwell that naming is confusing, I am thinking about unifying it around "-utxoindex" |
Sounds good. |
|
needs rebase again. |
|
@djpnewton or @droark have you done more work on this? I'd be happy to help continue the effort on this feature. |
|
@ryanofsky - I hadn't been working on this but I'm happy to run with the ball since the OP seems to have disappeared again. I'll post a new PR once it's up and running. Thanks. |
|
Closing this for now then. @Droak or @ryanofsky can open a new PR once they've restarted the work. |
This is an attempt to revive PR #5048
I have moved the index to its own database and also added a gettxoutsbyaddress REST command
I still need to think about the layout of binary input/output for the REST command, or is bin encoding necessary?
Also still missing tests for the new REST stuff