<oembed><type>rich</type><version>1.0</version><author_name>npub1sgs97fe0n9wehe6zw7drcxdz4cy9yt9pfqjv8gasz5jlk4zezc0quppx3c</author_name><author_url>https://nostr.ae/npub1sgs97fe0n9wehe6zw7drcxdz4cy9yt9pfqjv8gasz5jlk4zezc0quppx3c</author_url><provider_name>njump</provider_name><provider_url>https://nostr.ae</provider_url><html>📅 Original date posted:2017-04-08&#xA;📝 Original message:-----BEGIN PGP SIGNED MESSAGE-----&#xA;Hash: SHA256&#xA;&#xA;On 04/06/2017 05:17 PM, Tomas wrote:&#xA;&gt; Thanks, but I get the impression that the similarity is rather &#xA;&gt; superficial.&#xA;&#xA;My point was that &#34;Using a storage engine without UTXO-index&#34; has been&#xA;done, and may be a useful reference, not that implementation details&#xA;are the same.&#xA;&#xA;&gt; To address your points:&#xA;&#xA;Below you addressed two points I made regarding the downside of the&#xA;original libbitcoin implementation. These were initial learnings that&#xA;informed future implementations (also without a UTXO index). These&#xA;were not comparisons to your implementation.&#xA;&#xA;&gt;&gt; (1) higher than necessary storage space requirement due to&#xA;&gt;&gt; storing the indexing data required for correlate the spends, and&#xA;&gt; &#xA;&gt; Hmm. No. Spends are simply scanned in the spend-tree (full tree, &#xA;&gt; prunable, fully 5.6gb), or caught by the spend-index (bit index, &#xA;&gt; non-prunable, fully 180mb). Neither impose significant storage &#xA;&gt; requirements.&#xA;&gt; &#xA;&gt;&gt; 2) higher than necessary validation complexity and cost in terms&#xA;&gt;&gt; of computing the spent-ness (including spender height) of an&#xA;&gt;&gt; output.&#xA;&gt;&gt; &#xA;&gt;&gt; With the exception of de-linking (not deleted) in the case of&#xA;&gt;&gt; reorgs, the entire store is append only, implemented in a small&#xA;&gt;&gt; set of memory mapped file&#xA;&gt; &#xA;&gt; I guess this is the key difference. As the spend-tree stores the&#xA;&gt; spend information in a tree structure, no reorgs are required, and&#xA;&gt; the resulting code is actually much less complex.&#xA;&#xA;The references to &#34;higher than necessary storage&#34; and &#34;higher than&#xA;necessary validation cost&#34; are explicitly relative statements,&#xA;comparing earlier and later libbitcoin implementations.&#xA;&#xA;It is not clear to me how you are relating both the storage cost&#xA;(&#34;Hmm. No. ... Neither impose significant storage requirements.&#34;) and&#xA;code complexity (&#34;... resulting code is actually much less complex&#34;)&#xA;of your tx ordering software to my statements. Do you think I am wrong&#xA;and libbitcoin v3 is not actually more space and code efficient than&#xA;libbitcoin v2?&#xA;&#xA;But given that you have thrown some numbers and ideas out in a request&#xA;for feedback, I&#39;m happy to give you some based on several years of&#xA;experience working closely with these issues.&#xA;&#xA;First, I remain confused on your comments pertaining to UTXO growth&#xA;and network protocol. I followed your conversation with Greg and it&#xA;remains unclear to me. From what I understand you have isolated order&#xA;(double spend) from script validation. I think we all understand that&#xA;script validation requires inputs and outputs while double spend&#xA;detection requires correlation of inputs. What I do not understand is&#xA;your choice of optimization axis.&#xA;&#xA;Detection of double spend is not useful in isolation. One must also&#xA;validate scripts, which requires outputs. I can see that there is an&#xA;opportunity to reject blocks (within the same branch) faster by&#xA;validating for double spends before validating script. But unconfirmed&#xA;transactions do not exist in a branch, and are therefore not truly&#xA;conflicting, until they are mined. And even after they are mined&#xA;conflicting txs remain potentially valid in other branches. So&#xA;rejecting txs due to conflict comes down to a denial of service&#xA;policy, which ultimately must be based on fee increment (e.g. RBF).&#xA;But fees are based on the amount of the output value that remains&#xA;unspent in the transaction. So this in turn requires the retrieval of&#xA;outputs.&#xA;&#xA;And yet the remaining scenario of fast rejection of invalid blocks is&#xA;not a meaningful optimization. Optimizing for the case where a block&#xA;has valid and sufficient PoW and yet is invalid (for double spend) is&#xA;counterproductive. And even so, the txs within the invalid block may&#xA;be entirely valid independent of the block, so you are back to looking&#xA;up their outputs to obtain fees in the case of a double spend or to&#xA;validate script otherwise. In all cases you need to get the outputs.&#xA;&#xA;&gt; Bitcrust simply scans the tree. Although earlier designs used a &#xA;&gt; skip-list, it turns out that accompanied by a spent-index lagging a&#xA;&gt; few blocks behind, raw scanning is faster then anything even though&#xA;&gt; it needs to scan ~5 blocks times ~4000 inputs before reaching the&#xA;&gt; first spent-index,  the actual scan is highly cache efficient and&#xA;&gt; little more then a &#34;REP SCASQ&#34;, reaching sub-microsecond per input&#xA;&gt; on each core *including* the lookup in the spend index.&#xA;&#xA;I realize that you see the implementation of the ordering validation&#xA;as interesting detail, but I find it hard to justify contemplating the&#xA;implementation in isolation from the output lookup requirement. And if&#xA;one must looking up both outputs and spends for each validation, it&#xA;makes more sense to co-locate that data.&#xA;&#xA;Recovering in one step all data necessary to validate a tx has real&#xA;advantages over either interleaving queries and validation or&#xA;splitting input vs. output validation queries into two steps. It is a&#xA;significantly more test-friendly approach, has better performance&#xA;characteristics, and simplifies code. I cannot see any reason to&#xA;perform the data read for double spend validation in isolation of that&#xA;for script validation.&#xA;&#xA;&gt;&gt; I don&#39;t follow this part, maybe you could clarify. A spends&#xA;&gt;&gt; index grows with the size of the spend set (forever) as it cannot&#xA;&gt;&gt; be pruned, which certainly exceeds the size of the UTXO set&#xA;&gt;&gt; (unless nothing is spent). The advantage is that you don&#39;t have&#xA;&gt;&gt; to keep rewriting the store when you use a spends set (because&#xA;&gt;&gt; the store can be append only).&#xA;&gt; &#xA;&gt; My point is, that the spend tree grows per *input* of a&#xA;&gt; transaction instead of per *output* of a transaction, because this&#xA;&gt; is what is scanned on order validation.&#xA;&#xA;I think the conversation with Greg resolved my questions in this area.&#xA;What I find interesting is the reliance on Core&#39;s UTXO store to&#xA;implement script validation. This is not, &#34;a storage engine without a&#xA;UTXO-index&#34; as it has a dependency on Core&#39;s UTXO index.&#xA;&#xA;On the other hand the initial libbitcoin implementation that I&#xA;described to you is *actually* a bitcoin store with no UTXO index. The&#xA;current implementation is as well, however it is implemented&#xA;differently and is much more efficient than the original. How it&#xA;compares to your design is not really the point and impossible to&#xA;measure until you have production code.&#xA;&#xA;I can say however that your assumptions about the storage (and&#xA;performance) superiority of the design, or at least its&#xA;implementation, seem unfounded. If you are storing more index data&#xA;(5.6gb) than 32 bits per output, you are using more space than&#xA;production implementations. As for complexity, I don&#39;t think you&#39;ll&#xA;get any simpler than a loop to populate spend heights from a hash&#xA;table and a loop to test their integer values.&#xA;&#xA;&gt; The spend tree can be pruned because the spend index (~200mb)&#xA;&gt; catches early spends.&#xA;&gt; &#xA;&gt; Disregarding the baseload script validation, the peak load order &#xA;&gt; validation of bitcrust is more negatively effected by a transaction&#xA;&gt; with many inputs than by a transaction of many outputs.&#xA;&gt; &#xA;&gt; I encourage you to check out the results at https://bitcrust.org&#xA;&#xA;If by results you are referring to performance numbers, it&#39;s very hard&#xA;to draw any conclusions without a full benchmark. It&#39;s great that if&#xA;you are able to boost Core, but from my perspective the numbers aren&#39;t&#xA;especially compelling.&#xA;&#xA;As for some of the site&#39;s comments, these again cause me to question&#xA;the optimization choices:&#xA;&#xA;&#34;Blocks can be verified in parallel...&#34;&#xA;&#xA;Despite the site&#39;s explanation I cannot think of any reason to ever&#xA;validate two blocks at the same time. You would always prioritize the&#xA;block with the greatest PoW. Doing otherwise just slows down the net&#xA;validation in all but the pathological case where a miner has produced&#xA;an *invalid* block with *more* PoW than another valid block which&#xA;arrived at the node within the same second. Rejecting a *valid* block&#xA;with more PoW in favor of one with *less* &#34;processing&#34; is a hard fork,&#xA;so you probably wouldn&#39;t want to do that either. But with compact&#xA;block validation times approaching 25ms it&#39;s hard to justify stopping&#xA;a block validation for any reason.&#xA;&#xA;That&#39;s not to say parallel block validation difficult to do. If you&#xA;can validate one block&#39;s full set of inputs in parallel (which is not&#xA;novel) doing the same with additional blocks has trivial additional&#xA;complexity.&#xA;&#xA;&#34;The storage engine is optimized from ground up for&#xA;xthin/compact-block synchronization. This ensures that when the&#xA;majority of transactions are already synced, incoming blocks can be&#xA;verified at minimal resources using order-validation only.&#34;&#xA;&#xA;There are two distinct considerations here. One is pre-validation of&#xA;txs and the other is compact announcements. Just to be clear, the&#xA;former does not require the latter. Libbitcoin for example fully&#xA;exploits the former, independent of compactness. With a low min fee&#xA;setting and a few peers it is typical for the node to have&#xA;pre-validated 100% of non-coinbase txs. Averages at 1 satoshi per byte&#xA;are about 99.9%, effectively amortizing all script validation cost. So&#xA;this optimization is neither novel nor limited to compactness (which&#xA;is about reducing latency).&#xA;&#xA;I am also interested in your previous comments about soft forks. These&#xA;are material considerations that Greg touched on but it doesn&#39;t sound&#xA;like you fully appreciate just yet. When a tx is pre-validated the&#xA;rules applied must be the same rules as those of some future block.&#xA;Yet a tx can be included in more than one block (different branches).&#xA;Across branches and even in one branch, validation rules change, and&#xA;can change back. The changes are based on accumulated branch history.&#xA;Pre-validation can later become invalidated, and differently in&#xA;different branches. And maintaining proper context requires either&#xA;storing state that you are apparently not storing, or invalidating&#xA;optimizations. Based on your comments you do not seem to be accounting&#xA;for this in your storage assumptions or in your results. A recent post&#xA;by Greg highlights the complexity and consensus criticality of these&#xA;considerations.&#xA;&#xA;By &#34;order-validation only&#34; I believe you are referring to a&#xA;determination of whether the txs organized into a candidate block&#xA;double spend internal to the block or in the ancestry. Assuming that&#xA;one recovers outputs at the same time (and presumably from the same&#xA;location) as spender height (which is required both for validating&#xA;spends of a coinbase and for determination of whether the spend is&#xA;above the fork point), this determination is straightforward. One&#xA;simply loops over the spender records and invalidates a tx that has a&#xA;spender height not above the fork point (while also validating&#xA;coinbase maturity using the same height). A loop over the set of&#xA;in-memory spend heights of each output a tx is certainly fast enough&#xA;to not be worthy of any further optimization. And as previously&#xA;discussed, the population of the spender heights is not even a&#xA;material additional cost over obtaining the (necessary) output scripts.&#xA;&#xA;The hash table store that I described can fully navigate the block&#xA;tree and transaction DAG, since the stored tx, parent and point hashes&#xA;are also natural keys and each link is navigable in constant time. It&#xA;is also lock-free, can concurrently write any number of blocks during&#xA;initial block download and supports read/write concurrency. It has&#xA;successfully indexed and stored the entire blockchain from the P2P&#xA;network in 16 minutes (locally). It also stores both confirmed and&#xA;unconfirmed transactions in the same store, so there is nothing to&#xA;write when a block is confirmed except for the block header/hashes and&#xA;updates to spender heights for any output spent by the new block&#39;s&#xA;txs. It is similarly capable of storage in the block table of weak&#xA;chain blocks...&#xA;&#xA;But one thing it does *not* do is maintain spender and fork state for&#xA;multiple branches. In other words it is optimized for one long chain,&#xA;not multiple long branches. Your approach has a limited (in terms of&#xA;double spend identification) optimization for reorganization (i.e. a&#xA;change to the strong chain identity). However, applying that&#xA;optimization to the full store and supportive of soft forks, as&#xA;opposed to just input ordering, is a much larger task than it appears&#xA;you have attempted. I know, as I created a design for that approach&#xA;and after some time scrapped it. The cost of performing the&#xA;reorganization in the above store is low enough and very long reorgs&#xA;infrequent enough, for the optimization to be counterproductive. It&#39;s&#xA;elegant in theory, but in practice it increases storage requirements,&#xA;impacts general performance and significantly increases complexity.&#xA;Bitcoin&#39;s data model pushes one away from a tree design in that it is&#xA;always pruning the tree. Having the tree is necessary, but it&#39;s not&#xA;something to optimize for.&#xA;&#xA;e&#xA;&#xA;&#xA;&gt; Regards, Tomas&#xA;&gt; &#xA;&gt; On Fri, Apr 7, 2017, at 01:38, Eric Voskuil wrote: On 04/06/2017&#xA;&gt; 03:12 PM, Tomas via bitcoin-dev wrote:&#xA;&gt; &#xA;&gt; Hi Tomas,&#xA;&gt; &#xA;&gt;&gt;&gt;&gt; I have been working on a bitcoin implementation that uses a &#xA;&gt;&gt;&gt;&gt; different approach to indexing for verifying the order of &#xA;&gt;&gt;&gt;&gt; transactions. Instead of using an index of unspent outputs,&#xA;&gt;&gt;&gt;&gt; double spends are verified by using a spend-tree where spends&#xA;&gt;&gt;&gt;&gt; are scanned against spent outputs instead of unspent&#xA;&gt;&gt;&gt;&gt; outputs.&#xA;&gt; &#xA;&gt; This is the approach that genjix used in libbitcoin version2. With&#xA;&gt; the exception of de-linking (not deleted) in the case of reorgs,&#xA;&gt; the entire store is append only, implemented in a small set of&#xA;&gt; memory mapped files. The downsides to the approach are:&#xA;&gt; &#xA;&gt; (1) higher than necessary storage space requirement due to storing&#xA;&gt; the indexing data required for correlate the spends, and&#xA;&gt; &#xA;&gt; (2) higher than necessary validation complexity and cost in terms&#xA;&gt; of computing the spent-ness (including spender height) of an&#xA;&gt; output.&#xA;&gt; &#xA;&gt; His implementation used a hash table, so performance-wise it did&#xA;&gt; quite well and would theoretically outperform a tree, O(1) vs.&#xA;&gt; O(log2(N)).&#xA;&gt; &#xA;&gt;&gt;&gt;&gt; This allows for much better concurrency, as not only blocks,&#xA;&gt;&gt;&gt;&gt; but also individual inputs can be verified fully in&#xA;&gt;&gt;&gt;&gt; parallel.&#xA;&gt; &#xA;&gt; I was successful in parallelizing input validation (across the&#xA;&gt; inputs of an unconfirmed tx and across the set of all inputs in a&#xA;&gt; block) using the v2 store. However, it is not the case that the&#xA;&gt; spends approach is necessary for concurrency.&#xA;&gt; &#xA;&gt; To resolve the above two problems the version3 store does not use&#xA;&gt; a spends table/index. Nor does it store any table of UTXOs. Yet &#xA;&gt; validation is highly parallelized. Instead of additional indexes&#xA;&gt; it uses the tx hash table, augmented with 32 bits per output for&#xA;&gt; spender height. So there is a O(1) cost of finding the tx and a&#xA;&gt; O(N) cost of finding the spender height where N is the number of&#xA;&gt; outputs in the tx. But because the number of outputs in a tx is&#xA;&gt; bounded (by block size) this is constant time in the number of&#xA;&gt; transactions.&#xA;&gt; &#xA;&gt; This works out much faster than the spends table, and without the &#xA;&gt; storage cost or complexity disadvantages. It also scales with &#xA;&gt; available hardware, as the memory mapped files become in-memory&#xA;&gt; hash tables. For low memory machines we found it was important to&#xA;&gt; implement an opaque UTXO cache to limit paging, but for higher end&#xA;&gt; systems zero cache is optimal.&#xA;&gt; &#xA;&gt;&gt;&gt;&gt; I am sharing this not only to ask for your feedback, but also&#xA;&gt;&gt;&gt;&gt; to call for a clear separation of protocol and&#xA;&gt;&gt;&gt;&gt; implementations: As this solution, reversing the costs of&#xA;&gt;&gt;&gt;&gt; outputs and inputs, seems to have excellent performance&#xA;&gt;&gt;&gt;&gt; characteristics (as shown in the test results), updates to&#xA;&gt;&gt;&gt;&gt; the protocol addressing the UTXO growth, might not be worth&#xA;&gt;&gt;&gt;&gt; considering *protocol improvements* and it might be best to&#xA;&gt;&gt;&gt;&gt; address these concerns as implementation details.&#xA;&gt; &#xA;&gt; I don&#39;t follow this part, maybe you could clarify. A spends index &#xA;&gt; grows with the size of the spend set (forever) as it cannot be&#xA;&gt; pruned, which certainly exceeds the size of the UTXO set (unless&#xA;&gt; nothing is spent). The advantage is that you don&#39;t have to keep&#xA;&gt; rewriting the store when you use a spends set (because the store&#xA;&gt; can be append only).&#xA;&gt; &#xA;&gt; Feel free to message me if you&#39;d like to discuss in more detail, or&#xA;&gt; to continue on the libbitcoin mailing list (copied).&#xA;&gt; &#xA;&gt; e&#xA;-----BEGIN PGP SIGNATURE-----&#xA;Version: GnuPG v2.0.22 (GNU/Linux)&#xA;&#xA;iQEcBAEBCAAGBQJY6WY4AAoJEDzYwH8LXOFO+wwH/1uE/+P1+KLJWTkcttVWsO//&#xA;QAlikqg0HLFDtkd5jaYsBtx6op/Uz2o53ohZwVJt71ITCjQQI+yYK2RjBX92xIhd&#xA;K0rE901Np4PfMFbDA60LB0c/65aPlkUCr3f2PYIlizJs4Qq5Kn2sIpC5v9T3B7H4&#xA;MPq5UJwoPP+m3RZ9TSsVyee3ejHYXM7y2VNNnnWD3edIioA3cLh+y6sczpco2Hpa&#xA;P+GSDnv2cwV6FA22Is1Z15tpfLyQnPrrGJ9QEJJ15vnhCTxZe0j1PQ4y+OOZh5Iq&#xA;mqBkGRNPeUnPAPDM+/qvhr2kUyxFbaJNtwg5HDGHWFOq5B/YeKxVk8Qjnk+9epA=&#xA;=XRKl&#xA;-----END PGP SIGNATURE-----</html></oembed>