Bitcoin full node and query server

July 2024

Archive of the libbitcoin Slack #general channel.

Tuesday, July 2, 2024

evoskuil17:33
I've spent a few days on a performance problem that I encountered when starting to optimize validation through the caching of blocks following download for later validation. After a couple days of frustration, I think I've isolated the issue. As sometimes happens there is both a problem and an opportunity...
The behavior is that block objects (owned by std::shared_ptr) are passed form the protocols, operating concurrently, into new chaser for the purpose of indexing them. This requires sequencing them into the chaser's strand. So there are objects created on any number of different threads, sequenced into a stranded function which itself can be on one of any number of other different threads at any given instantiation. What I finally concluded is that ~block is taking up about 95% of process time, where previously it was < 1%.
evoskuil17:44
Aside from passing the block pointer around, I hadn't actually added anything else yet (no caching/indexing) of the block. I finally concluding it had to be an issue of lock contention in the memory deallocation. So I looked at whether blocks or other dynamically created objects were previously spanning threads. Turns out that they are not (with some unrelated exceptions, notably address handling and message heads). Network messages are allocated on the same thread as they notify on, and upon which all subscribers receive and process the message (apart from address, which appears to have the same but much less impactful issue). That changed when I posted the block message to another strand, but would also occur if it was to be posted to another function on the same strand.
evoskuil17:50
I was surprised that I didn't see this when I tested concurrent validation. But a quick review shows that transactions are queried, populated, validated, and destroyed on a single thread. Only integral values and a single racer object are passed around. One racer per block is a trivial object destruction, so inconsequential. In other words, through a coincidence of design, so far, object allocation and destruction has been very efficient. So we've got to handle this if we want block/tx/prevout caching for validation, but it also presents unforeseen optimization opportunities.
evoskuil18:06
I tested posting the block back to the originating strand for destruction, but this shows little or no benefit. That makes sense if the problem is in fact contention for deallocation. Also it tried performing the same posts but with a null block pointer (no performance issue). So it only occurs with block destruct on a non-originating thread. There is really no way to post back to a specific thread, and that would produce its own set of significant performance issues. So the possible resolutions consist of (1) not doing it and (2) using a custom allocator/deallocator. #1 is possible by just avoiding the optimization and simply re-reading blocks from the store as before. This performs well on the high memory machine, but not otherwise. So I’m taking a look at #2.
This may present an opportunity to take on a custom allocator. Allocation and deallocation area material cost, even apart from this issue, and custom allocators can achieve 2-10x performance advantages depending on the scenario.
I know that @jungly will be excited about this.
evoskuil18:45
One option I've considered is having the store expose an allocator that can be passed to specific objects, such as for tx construction. A store blob table is a memory manager. Allocation is from a memory map - which can be sized via configuration and resized as necessary. When we hit a space limit we would just reset the allocation pointer to the beginning of the map (unlike store tables this cannot be allowed to grow indefinitely). This implies of course that all uses of the allocated memory are first deleted. We track this already for table remapping. However in this case we wouldn't want to track at the level of granularity of each allocation. We would just want to know (for example) that there are no tx objects. That would require the tx construction to obtain the memory object that we currently use, and hold it until destruct. Then we just block new allocations until all are released. As long as work progresses, this results in a periodic delay (like table remap) but makes allocation almost free - and deallocation becomes a nop. Basically this would allow us to allocate all tx object memory on a mmap and strictly control that level of allocation. Blocks and headers are trivial by comparison, and there's really not much else. Allocation would be cheap due to the large singular reservation and deallocation would be free. Memory full behavior would be similar if not identical to virtual memory, as pages would get swapped to disk as RAM becomes unavailable. However it may very well be better, due to the memory locality.
evoskuil19:53
In C++ there are several ways to replace memory allocations. new/delete can trivially be globally replaced, and class/struct can expose static member new/delete (for itself). The major cost comes from std::vector. We barely use std::string and std::array is stack-allocated. All member integrals and arrays are allocations of the containing class/struct. We also need to consider that this technique needs to be strictly limited to objects that we can track (or more specifically can track themselves). So for example, a block can encapsulate a memory tracking pointer and new memory can thus be allocated when all of these are freed, and no new memory allocated while waiting for that to be the case. Within that block all allocations can be based on a replaced allocator.
evoskuil20:06
The downside to using custom allocators other than globally is that objects are not polymorphic. A std::vector<T> is not the same type as a std::vector<T, A> where A is a custom allocator. This is why std::pmr::polymorphic_allocator was introduced in C++17. While pmr types instantiated with a memory resource are polymorphic, they require a constructor parameter, and aren't polymorphic with std::vector<T>:
template <typename T>
using pmr_vector = std::vector<T, std::pmr::polymorphic_allocator<T>>;

class my_memory_resource : public std::pmr::memory_resource
{
    ....
}

void foo()
{
    my_memory_resource mem_res;
    auto my_vector = pmr_vector<int>(0, &mem_res);
    std::vector<int> v;
    my_vector = v;
}
(edited)
✅ 1

Wednesday, July 3, 2024

evoskuil03:10
After some thinking on this it appears that I can most easily and cleanly implement a block/tx object targeted allocator using a new constructor on the fast io streams that we use to construct all objects from the network and store. By defining an interface and optionally accepting an instance on reader construct, the reader can hold and provide the common allocator. It can also provide a shared lock object that allows for reset of the mmap when full, by blocking and waiting until all objects are cleared. Of course these cannot be long-lived objects, or they cause a deadlock. This is the same pattern used by the store presently.
✅ 1
The memory can be managed by the store as a temporary table, created/deleted at start/stop of the store. mmap allows it to page to disk as required, and we can grow/remap as desired, or just pretend to remap when full but reset to the map start.
The only deviation I can think of is that there should be an manager::allocate() override that returns a raw pointer. The smart pointer would only be used for remap locking, with all other allocations within the object protected by it.
There are hash instances that are assigned to the object as cache by both the network deserialization and the store deserialization, but given the reader is local to both, these can also be so allocated.
I think only the std::unique_ptr member objects which hold cache cannot be custom allocated, but these are set to be replaced with std::optional.
This should significantly accelerate object construct and will make destruct almost entirely free, since it’s a nop. And of course there will no longer be and consequence for destruction across threads, because it’s a nop.
I’d say that would be around 5-10% improved sync time in the previous scenarios (based on previous reporting of object alloc/dealloc costs), and will make block/tx/prevout caching for validation not only feasible but fast.
(edited)
I would also set up a try_lock loop to reduce the mmap allocation when there is no activity (post sync).
✅ 1
"Today we are releasing 10 security advisories for the Bitcoin Core project. Those bugs affect versions of Bitcoin Core before (and not including) 0.21.0."
My favorite is CPU DoS and OOM crash due to maintaining an unbounded list of banned nodes, compounded by quadratic operations against it. Attributed to "many malicious peers".
Nobody thought maintaining an unbounded list of banned nodes was a really bad idea?

Thursday, July 4, 2024

evoskuil00:38
"The project has historically done a poor job at publicly disclosing security-critical bugs, whether externally reported or found by contributors. This has led to a situation where a lot of users perceive Bitcoin Core as never having bugs. This perception is dangerous and, unfortunately, not accurate." https://groups.google.com/g/bitcoindev/c/Q2ZGit2wF7w Props to the new crew for raising the bar on responsibility.
👍🏾 1
Shashank Shekhar Singh19:57
Will there be a meeting today?
pmienk19:58
It's a holiday in the U.S., I hadn't planned on it.
👍🏻 1
Shashank Shekhar Singh20:00
Ok then will be meeting next week thank you !!
👍 1

Thursday, July 11, 2024

P
parsevalbtc19:11
Does the meeting will take place today?
pmienk19:54
Yes, should start soon. Will be using slack as was discussed last time.
👍🏻 1👍 2
jungly20:21
Going through tunnels here, the network is really bad. I was enjoying that chat about polymorphic allocators! Now it won't reconnect to the huddle.
👍🏻 1
pmienk21:14
Screaming baby, had to go
👍🏻 1

Thursday, July 18, 2024

P
parsevalbtc15:31
@pmienk recompiling after your merge, let you know asap.
any meeting scheduled for today?
P
parsevalbtc15:37
It compiled just one of the two options
1 reply · parsevalbtc · last Jul 18, 2024
P
parsevalbtc15:44
Re-running this compilation to get the full log
evoskuil15:38
4pm NYC time
P
parsevalbtc15:41
ok like last week
thnx
Huddle with pmienk, Shashank Shekhar Singh, jungly, Tanay Raikhere, parsevalbtc, evoskuil · 82 min19:57
P
parsevalbtc22:06
After on-flight modification from @pmienk, optimizations are now fully compiled. 🙏
For potential linux testers, I have compiled launching the following command: CFLAGS="-O3" CXXFLAGS="-O3" ./install.sh --prefix=/home/user --build-boost --enable-ndebug --disable-shared --enable-avx2 --enable-sse41
✅ 1🔥 1
30 replies · Tanay Raikhere, parsevalbtc, evoskuil · last Jul 24, 2024
Tanay Raikhere05:08
I should try this on my old machine too! Let's see if this compiles :D
Tanay Raikhere08:38
Just started compiling it!
P
parsevalbtc10:47
Great! How much RAM do you have?
P
parsevalbtc10:53
In my case is 7 GB of physical RAM
evoskuil14:06
The vectorization options need to match the CPU, otherwise the process will crash.
👍 2
Tanay Raikhere21:01
I installed a fresh os (linux mint) in my system, and had a couple of issues with installing the node. firstly had to install build-essentials, lib-tool, and then had to run the script after compilation finally ran into this issue
src/messages/address.cpp:47:40: error: class template argument deduction failed:
   47 |     system::byte_reader reader{ source };
      |                                        ^
In file included from /usr/local/include/bitcoin/system/stream/streamers/bit_reader.hpp:27,
                 from /usr/local/include/bitcoin/system/stream/streamers/bit_flipper.hpp:27,
                 from /usr/local/include/bitcoin/system/stream/stream.hpp:33,
                 from /usr/local/include/bitcoin/system/chain/header.hpp:29,
                 from /usr/local/include/bitcoin/system/chain/block.hpp:26,
                 from /usr/local/include/bitcoin/system/chain/chain.hpp:22,
                 from /usr/local/include/bitcoin/system/settings.hpp:22,
                 from /usr/local/include/bitcoin/system.hpp:29,
                 from ./include/bitcoin/network/messages/address.hpp:23,
                 from src/messages/address.cpp:19:
/usr/local/include/bitcoin/system/stream/streamers/byte_reader.hpp: In function 'byte_reader(IStream&, libbitcoin::arena* const&)-> libbitcoin::system::byte_reader<IStream> [with IStream = libbitcoin::system::istream<>; libbitcoin::system::bytereader::memory_arena = libbitcoin::arena*]':
/usr/local/include/bitcoin/system/stream/streamers/byte_reader.hpp:44:48: error: 'static libbitcoin::arena* libbitcoin::system::byte_reader<IStream>::default_arena() [with IStream = libbitcoin::system::istream<>; libbitcoin::system::bytereader::memory_arena = libbitcoin::arena*]' is private within this context
   44 |         const memory_arena& arena=default_arena()) NOEXCEPT;
      |                                   ~~~~^
In file included from /usr/local/include/bitcoin/system/stream/streamers/byte_reader.hpp:229,
                 from /usr/local/include/bitcoin/system/stream/streamers/bit_reader.hpp:27,
                 from /usr/local/include/bitcoin/system/stream/streamers/bit_flipper.hpp:27,
                 from /usr/local/include/bitcoin/system/stream/stream.hpp:33,
                 from /usr/local/include/bitcoin/system/chain/header.hpp:29,
                 from /usr/local/include/bitcoin/system/chain/block.hpp:26,
                 from /usr/local/include/bitcoin/system/chain/chain.hpp:22,
                 from /usr/local/include/bitcoin/system/settings.hpp:22,
                 from /usr/local/include/bitcoin/system.hpp:29,
                 from ./include/bitcoin/network/messages/address.hpp:23,
                 from src/messages/address.cpp:19:
/usr/local/include/bitcoin/system/impl/stream/streamers/byte_reader.ipp:45:1: note: declared private here
   45 | byte_reader<IStream>::default_arena() NOEXCEPT
      | ^~~~~~~~
In file included from /usr/local/include/bitcoin/system/stream/streamers/bit_reader.hpp:27,
                 from /usr/local/include/bitcoin/system/stream/streamers/bit_flipper.hpp:27,
                 from /usr/local/include/bitcoin/system/stream/stream.hpp:33,
                 from /usr/local/include/bitcoin/system/chain/header.hpp:29,
                 from /usr/local/include/bitcoin/system/chain/block.hpp:26,
                 from /usr/local/include/bitcoin/system/chain/chain.hpp:22,
                 from /usr/local/include/bitcoin/system/settings.hpp:22,
                 from /usr/local/include/bitcoin/system.hpp:29,
                 from ./include/bitcoin/network/messages/address.hpp:23,
                 from src/messages/address.cpp:19:
/usr/local/include/bitcoin/system/stream/streamers/byte_reader.hpp:44:48: error: 'static libbitcoin::arena* libbitcoin::system::byte_reader<IStream>::default_arena() [with IStream = libbitcoin::system::istream<>; libbitcoin::system::bytereader::memory_arena = libbitcoin::arena*]' is private within this context
   44 |         const memory_arena& arena=default_arena()) NOEXCEPT;
      |                                   ~~~~^
In file included from /usr/local/include/bitcoin/system/stream/streamers/byte_reader.hpp:229,
                 from /usr/local/include/bitcoin/system/stream/streamers/bit_reader.hpp:27,
                 from /usr/local/include/bitcoin/system/stream/streamers/bit_flipper.hpp:27,
                 from /usr/local/include/bitcoin/system/stream/stream.hpp:33,
                 from /usr/local/include/bitcoin/system/chain/header.hpp:29,
                 from /usr/local/include/bitcoin/system/chain/block.hpp:26,
                 from /usr/local/include/bitcoin/system/chain/chain.hpp:22,
                 from /usr/local/include/bitcoin/system/settings.hpp:22,
                 from /usr/local/include/bitcoin/system.hpp:29,
                 from ./include/bitcoin/network/messages/address.hpp:23,
                 from src/messages/address.cpp:19:
/usr/local/include/bitcoin/system/impl/stream/streamers/byte_reader.ipp:45:1: note: declared private here
   45 | byte_reader<IStream>::default_arena() NOEXCEPT
      | ^~~~~~~~
src/messages/address.cpp:47:40: note:   when instantiating default argument for call to 'byte_reader(IStream&, libbitcoin::arena* const&)-> libbitcoin::system::byte_reader<IStream> [with IStream = libbitcoin::system::istream<>; libbitcoin::system::bytereader::memory_arena = libbitcoin::arena*]'
   47 |     system::byte_reader reader{ source };
      |                                        ^
  CXX      src/messages/libbitcoin_network_la-alert_item.lo
make: * [Makefile:2352: src/messages/libbitcoin_network_la-address.lo] Error 1
make: * Waiting for unfinished jobs....
src/messages/alert.cpp: In static member function 'static libbitcoin::network::messages::alert::cptr libbitcoin::network::messages::alert::deserialize(uint32_t, const data_chunk&)':
src/messages/alert.cpp:44:40: error: class template argument deduction failed:
   44 |     system::byte_reader reader{ source };
      |                                        ^
In file included from /usr/local/include/bitcoin/system/stream/streamers/bit_reader.hpp:27,
                 from /usr/local/include/bitcoin/system/stream/streamers/bit_flipper.hpp:27,
                 from /usr/local/include/bitcoin/system/stream/stream.hpp:33,
                 from /usr/local/include/bitcoin/system/chain/header.hpp:29,
                 from /usr/local/include/bitcoin/system/chain/block.hpp:26,
                 from /usr/local/include/bitcoin/system/chain/chain.hpp:22,
                 from /usr/local/include/bitcoin/system/settings.hpp:22,
                 from /usr/local/include/bitcoin/system.hpp:29,
                 from ./include/bitcoin/network/messages/alert.hpp:23,
                 from src/messages/alert.cpp:19:
/usr/local/include/bitcoin/system/stream/streamers/byte_reader.hpp: In function 'byte_reader(IStream&, libbitcoin::arena* const&)-> libbitcoin::system::byte_reader<IStream> [with IStream = libbitcoin::system::istream<>; libbitcoin::system::bytereader::memory_arena = libbitcoin::arena*]':
/usr/local/include/bitcoin/system/stream/streamers/byte_reader.hpp:44:48: error: 'static libbitcoin::arena* libbitcoin::system::byte_reader<IStream>::default_arena() [with IStream = libbitcoin::system::istream<>; libbitcoin::system::bytereader::memory_arena = libbitcoin::arena*]' is private within this context
   44 |         const memory_arena& arena=default_arena()) NOEXCEPT;
      |                                   ~~~~^
In file included from /usr/local/include/bitcoin/system/stream/streamers/byte_reader.hpp:229,
                 from /usr/local/include/bitcoin/system/stream/streamers/bit_reader.hpp:27,
                 from /usr/local/include/bitcoin/system/stream/streamers/bit_flipper.hpp:27,
                 from /usr/local/include/bitcoin/system/stream/stream.hpp:33,
                 from /usr/local/include/bitcoin/system/chain/header.hpp:29,
                 from /usr/local/include/bitcoin/system/chain/block.hpp:26,
                 from /usr/local/include/bitcoin/system/chain/chain.hpp:22,
                 from /usr/local/include/bitcoin/system/settings.hpp:22,
                 from /usr/local/include/bitcoin/system.hpp:29,
                 from ./include/bitcoin/network/messages/alert.hpp:23,
                 from src/messages/alert.cpp:19:
/usr/local/include/bitcoin/system/impl/stream/streamers/byte_reader.ipp:45:1: note: declared private here
   45 | byte_reader<IStream>::default_arena() NOEXCEPT
      | ^~~~~~~~
In file included from /usr/local/include/bitcoin/system/stream/streamers/bit_reader.hpp:27,
                 from /usr/local/include/bitcoin/system/stream/streamers/bit_flipper.hpp:27,
                 from /usr/local/include/bitcoin/system/stream/stream.hpp:33,
                 from /usr/local/include/bitcoin/system/chain/header.hpp:29,
                 from /usr/local/include/bitcoin/system/chain/block.hpp:26,
                 from /usr/local/include/bitcoin/system/chain/chain.hpp:22,
                 from /usr/local/include/bitcoin/system/settings.hpp:22,
                 from /usr/local/include/bitcoin/system.hpp:29,
                 from ./include/bitcoin/network/messages/alert.hpp:23,
                 from src/messages/alert.cpp:19:
/usr/local/include/bitcoin/system/stream/streamers/byte_reader.hpp:44:48: error: 'static libbitcoin::arena* libbitcoin::system::byte_reader<IStream>::default_arena() [with IStream = libbitcoin::system::istream<>; libbitcoin::system::bytereader::memory_arena = libbitcoin::arena*]' is private within this context
   44 |         const memory_arena& arena=default_arena()) NOEXCEPT;
      |                                   ~~~~^
In file included from /usr/local/include/bitcoin/system/stream/streamers/byte_reader.hpp:229,
                 from /usr/local/include/bitcoin/system/stream/streamers/bit_reader.hpp:27,
                 from /usr/local/include/bitcoin/system/stream/streamers/bit_flipper.hpp:27,
                 from /usr/local/include/bitcoin/system/stream/stream.hpp:33,
                 from /usr/local/include/bitcoin/system/chain/header.hpp:29,
                 from /usr/local/include/bitcoin/system/chain/block.hpp:26,
                 from /usr/local/include/bitcoin/system/chain/chain.hpp:22,
                 from /usr/local/include/bitcoin/system/settings.hpp:22,
                 from /usr/local/include/bitcoin/system.hpp:29,
                 from ./include/bitcoin/network/messages/alert.hpp:23,
                 from src/messages/alert.cpp:19:
/usr/local/include/bitcoin/system/impl/stream/streamers/byte_reader.ipp:45:1: note: declared private here
   45 | byte_reader<IStream>::default_arena() NOEXCEPT
      | ^~~~~~~~
src/messages/alert.cpp:44:40: note:   when instantiating default argument for call to 'byte_reader(IStream&, libbitcoin::arena* const&)-> libbitcoin::system::byte_reader<IStream> [with IStream = libbitcoin::system::istream<>; libbitcoin::system::bytereader::memory_arena = libbitcoin::arena*]'
   44 |     system::byte_reader reader{ source };
      |                                        ^
make: * [Makefile:2366: src/messages/libbitcoin_network_la-alert.lo] Error 1

real	11m29.040s
user	31m51.719s
sys	2m16.342s
I have no idea how to resolve this one :(
evoskuil21:04
Those are not issues, just documented minimum requirements. https://github.com/libbitcoin/libbitcoin-system?tab=readme-ov-file#debianubuntu
✅ 1
Tanay Raikhere21:04
Ohh let me try that again!
evoskuil21:05
Though it looks like we need to bump minimum versions on the readme.
evoskuil21:06
The compilation issue looks like a dated build. Maybe try again with latest.
👆🏻 1
Tanay Raikhere21:11
I have all the build systems installed and pulled latest update from GitHub, but it still doesn't compile! Maybe I'll have to play around with build system versions! Check each individuals ones to see if they are above the minimum required
(edited)
Tanay Raikhere21:15
Yup! all of them fulfill the minimum requirements!
evoskuil21:18
Regarding the build system I was just pointing out that there was no “issue” as that’s documented and normal/expected.
Tanay Raikhere21:31
Thank you for your clarification, but I am still not able to understand what is the actual problem. I'd be happy if you could clarify!
evoskuil21:32
Redownload the repos (or clear dir and rerun install.sh).
✅ 1
P
parsevalbtc09:42
@Tanay Raikhere probably silly question. The install.sh you are using is the one in libbitcoin-node? https://github.com/libbitcoin/libbitcoin-node/blob/master/install.sh
P
parsevalbtc10:04
This install script basically install everything automatically (all the repos required + compilations). So you don't need to configure each repo on your own.
(edited)
Tanay Raikhere12:04
Yep, I'm using the latest pulled version of node! and running the same command you've described!
Tanay Raikhere12:04
I ran the install script for libbitcoin-system and that installed the system without any issues!
Tanay Raikhere12:23
Cleaned the repo! (deleted the old one and pulled it again) , trying to install the node once again! using the install.sh script
(edited)
Tanay Raikhere12:56
Update! It seems to me that there are some errors unresolved with the libbitcoin-network library! I confirmed that by separately cloning the libbitcoin-network library, ran the install.sh command inside this repo, got logged with the same error I described above. If I am missing out on any extra details, please let me know!
Tanay Raikhere13:31
It compiled (libbitcoin-node) in my laptop which runs on Linux Manjaro. Seems like there is something up with my old machine running Linux Mint!
🙌 1
P
parsevalbtc20:16
Great to hear ! Maybe is the compiler? Even if the script should detect compiler version and libraries needed...
evoskuil20:34
It was a build issue limited to g++. It reproduced in the dependent builds, but not in the system builds, because it was header-based and not covered in system unit tests. The issue is well described by the error messages in the build output. It's not clear why g++ requires a public member for use as a default parameter value while other compilers do not. It has been resolved in the latest builds. I suspect it compiled on the laptop because it used clang++.
👍 2
P
parsevalbtc22:09
I used g++ as well, but the error didn't show...
evoskuil22:21
It may be version dependent. I assume it’s a g++ bug.
Tanay Raikhere16:59
export CC=gcc export CXX=g++ sudo ./install.sh Ah! This finally worked with these settings!
evoskuil21:05
The settings didn't change anything, I patched the build yesterday.
P
parsevalbtc22:54
Now moving onto trimming table size for improving performance on my machine
✅ 1

Thursday, July 25, 2024

Huddle with pmienk, Tanay Raikhere, Shashank Shekhar Singh · 24 min19:56
2 replies · Tanay Raikhere, pmienk · last Jul 25, 2024
Tanay Raikhere20:14
sudo ./configure PKG_CONFIG_PATH=/usr/local/lib/pkgconfig CPPFLAGS="-I/usr/local/include" LDFLAGS="-L/usr/local/lib" LDFLAGS="-L/usr/lib -ltbb
pmienk20:15
./developer_setup.sh