Bitcoin full node and query server

February 2021

Archive of the libbitcoin Slack #general channel.

Tuesday, February 2, 2021

P
Phil17:50
Hello guys! the mmap emulation code for Windows where in the codebase is it?
P
Phil17:57
Thank you!
P
Phil18:06
@neillm For mmap function, at least on windows, what kind of data block sizes are used, in other words, the len argument is what? what about if there is a btc transaction that spans such memory block boundary?
(edited)
You'll have to read the code for details, sorry don't know it all atm
P
Phil18:08
oh ok, no problem! Thank you for the reference! It’s very helpful! Checking that..
👍 1
evoskuil18:11
The size of data doesn’t matter.
evan18:14
That book is amazing you guys!
👍 1
Also as I am trying to plan for this server build is there any opinions on AMD or Intel?
1 reply · neillm · last Feb 02, 2021
neillm18:33
Whatever works. I don't think there are specific optimizations for one over the other (yet). I could see the SHA extensions being helpful, but they're available on some of either.
M
Mlmikael20:19
@evoskuil so, in order to implement a custom IO/persistence backend for LibBitcoin,
then there must be support for memory addressing for the size of the whole blockchain, right
This way if there's ARM64 or RISCV64 with only 256GB or 512GB addressable memory then those are game over, get another arch, yes?
1 reply · jungly · last Feb 02, 2021
jungly23:01
This is a whole big can of worms.You might find some of the papers covered here very interesting. Especially those in the 'Larger-than-memory databases' section. https://15721.courses.cs.cmu.edu/spring2020/schedule.html
CMU 15-721
Course schedule with slides, lecture notes, and videos.
evoskuil20:22
As I’ve explained in DM, no.
(edited)
You MUST implement the interface in Libbitcoin.
You CAN do whatever you want behind that.
M
Mlmikael20:23
@evoskuil but, uuh, the file_storage class is used as interface. And that one has a method .access()
evoskuil20:24
Given your description of objectives you SHOULD implement your own virtual memory layer over your hardware memory API.
1 reply · Mlmikael · last Feb 02, 2021
M
Mlmikael20:24
Right, for an architecture with so much virtual memory, that works, sure
M
Mlmikael20:24
that gives you the memory pointer (i.e. a void*) to the virtual memory address range for that file,
and that file then is hundreds of GB
evoskuil20:24
And then tie that to the Libbitcoin interface.
M
Mlmikael20:24
replied to a thread: view
Right, for an architecture with so much virtual memory, that works, sure
evoskuil20:25
It works for any architecture.
M
Mlmikael20:25
Given your description of objectives you SHOULD implement your own virtual memory layer over your hardware memory API.
This would be ultra clunky though
It's totally nonstandard
evoskuil20:26
Wikipedia
In computing, virtual memory, or virtual storage is a memory management technique that provides an "idealized abstraction of the storage resources that are actually available on a given machine" which "creates the illusion to users of a very large (main) memory".The computer's operating system, using a combination of hardware and software, maps memory addresses used by a program, called virtual addresses, into physical addresses in computer memory. Main storage, as seen by a process or task, appears as a contiguous address space or collection of contiguous segments. The operating system manage…
There is no other option.
M
Mlmikael20:26
One would be that our custom file_storage implementation would malloc() the whole intended memory range
evoskuil20:27
You must IMPLEMENT (or find an implementation) of VM over your hardware API.
M
Mlmikael20:27
And then propagate that memory range with said information before file_storage:open() returns
evoskuil20:27
How you implement it is entirely up to you.
M
Mlmikael20:27
Anyhow I see your point, I'd need some abstraction to actually give me userland virtual memory
Hm, I wonder if Linux or OpenBSD has any interface for this. I think not.
Internally an IO backend will be basically sector read and sector write
evoskuil20:28
You can expect no changes in Libbitcoin to do this. Not because it’s hard, but because it would be wrong.
M
Mlmikael20:28
Wrong in what sense?
evoskuil20:28
Architecturally, for both your implementation and for Libbitcoin.
(edited)
It would be a major abstraction leak.
M
Mlmikael20:29
Not for a high performance software usecase,
where the userland process does the direct hardware access.
evoskuil20:30
And performance would in no way be improved by accepting it.
(edited)
M
Mlmikael20:30
In this situation the NVMe etc. hardware will load chunks into given memory addresses and store given memory addresses to persistence, respcetively
evoskuil20:30
Under the VM
It’s not about WHAT you do, it’s about WHERE you do it.
M
Mlmikael20:31
mmap() is an abstraction that segregates the physical IO operations + caching logic, from the place where the information is actually processed
evoskuil20:31
Mmap isn’t relevant.
M
Mlmikael20:31
In a direct hardware access case, the userland process implements its own cache
evoskuil20:31
You are implementing the interface. It does not have to be or use mmap.
(edited)
M
Mlmikael20:32
Yeah though alternative to mmap is either malloc() based in which case RAM requirement for solution is hundreds of GB which is unpractical,
evoskuil20:32
Doesn’t matter how it does it. That’s what abstraction layers are for.
M
Mlmikael20:32
or implement my own virtual memory backing thing which would be really custom, not aware how feasible
evoskuil20:32
You optimize in the layer.
It’s totally feasible.
And of course it will be custom.
And again, to the extent it can be optimized, it can be done so most effectively in a VM layer, not by changes to Libbitcoin.
M
Mlmikael20:39
LibBitcoin having its own IO is not strange, OS IO is frequently archaic.
I agree that mmap() is a convenient interface though
evoskuil20:52
It would be a very poor architectural decision to write data access directly in the underlying API. Our interface is not mmap, it’s our abstraction over it.
(edited)
This is how we are able to incorporate a Win32 API implementation, as there is no mmap. The abstraction is thin, but it’s class-based (object oriented), unlike mmap.
(edited)
M
Mlmikael20:55
@evoskuil wait we checked what is done in Win32, and it's a wrapping over CreateFileMappingW() and MapViewOfFile(), which are just a flavor of mmap()?
(this logic is in mman-win32/mman.c)
i mean on Win32 all that is done is just bridge the Unix mmap() interface defintion through a thin wrapper to use CreateFileMappingW() and MapViewOfFile() instead?
evoskuil20:58
No, it is not a bridge to mmap. It is an implementation of data access that satisfies the interface above it using the Win32 APIs.
(edited)
1 reply · evoskuil · last Feb 02, 2021
evoskuil20:59
And this is what you must write if you hope to succeed.
M
Mlmikael20:59
> It would be a very poor architectural decision to write data access directly in the underlying API. Our interface is not mmap, it’s our abstraction over it. I mean in practice what you do is expect one continuous memory address range of couple hundred GB, to represent the LB blockchain file?
an implementation of data access that satisfies the interface above it using the Win32 APIs.
It's centered around using memory addresses, with .access() as offset, right?
evoskuil21:00
Do you really understand what is meant by virtual memory?
M
Mlmikael21:00
Yes, a memory range that is not backed by physical memory
or not necessarily
evoskuil21:01
I REALLY suggest you study VM in the context of your questions.
M
Mlmikael21:01
mmap() is such that it guarantees the memory from a void* and the subsequent size bytes, to be reserved -
no other malloc() or mmap() will make any claims to the memory address range -
evoskuil21:01
mmap is NOT relevant
This is going in circles. You need VM, look at it again.
M
Mlmikael21:01
I know what VM is
VM only means that logical memory and physical memory are segregated
evoskuil21:02
You may know what it is but your questions imply you don’t really understand it.
M
Mlmikael21:02
LibBitcoin is based on that LB's persistent memory is represented in whole by given logical memory address ranges
evoskuil21:03
LOGICAL memory yes, PHYSICAL is NOT contiguous.
M
Mlmikael21:03
You may know what it is but your questions imply you don’t really understand it.
I suggest I understand it but there are limits to what virtual memory operations Unix offers one to do in userland
evoskuil21:03
That’s the point of VM.
M
Mlmikael21:03
Yeah exactly. So now here I'm in userland, and have a direct hardware access based NVMe driver. It will give me load and store operations.
evoskuil21:04
Logical memory will always be contiguous.
Sorry, giving up for a while.
M
Mlmikael21:04
If I had something like "vmem_map(logical_address,physical address,len);" and "vmem_unmap(logical-address,len);"
Anyhow I think the answer is that a custom IO backend cannot be trivially implemented in LB, without changing to become a macro, every single memory read and store operation that LB does anywhere to persistent memory, as these are currently in the form of memory load/store ops (e.g. *v = 123; printf(*v); )
(edited)
Unix has a big problem in that it is essentially absent of userland virtual memory management ops. No idea why noone fixed this the last decades.
I see there's a MAP_ANON argument to mmap() to allocate virtual memory, but not sure if that vmam can then be used meaningfully
evoskuil21:19
It’s not a problem with or limitation of Libbitcoin.
But what is not clear to me is why you want to do this. If you cannot hold the blockchain until RAM you aren’t accomplishing anything that I can tell.
M
Mlmikael21:22
You are missing my point - so, in userland when you access hardware directly, then you're operating at the physical medium's sector size e.g. 4KB, so you do NVMe load and store ops, at a very very high speed.
To make LibBitcoin use direct hardware access storage, I run into a wall, which is that Unix' virtual memory handling operations don't exist, basically
Noone in the Unix community these past 50 years bothered to define and implement the fundamentals.
So I just wanted to ask you if there was any design thinking in LB to help work around it, and there is not, which is fine, it just means that a custom direct hw backend cannot be implemented unless one *also* implements userland virtual memory management extensions to the OS too
evoskuil21:25
And you are missing my point. I have been exceedingly explicit about it. You need to IMPLEMENT the VM layer.
M
Mlmikael21:25
the latter would be a PhD thesis or two or three in itself
right - implementing it would be really deep OS plumbing, it's be 1-3 PhD thesises
maybe in 10-20 years someone will have added it to Linux, i'm unsure why noone has expanded on that yet, it's really weird
evoskuil21:26
If you do not all you will end up doing is pushing that complexity into Libbitcoin, to no performance benefit.
M
Mlmikael21:26
it's barely clear how to implement custom page fault handlers in Linux
evoskuil21:26
But that is what you WANT!?
You want me to do it inside Libbitcoin database logic??
M
Mlmikael21:27
> If you do not all you will end up doing is pushing that complexity into Libbitcoin, to no performance benefit. My impression of the performance of userland direct NVMe hardware access is that it surprisingly fast
evoskuil21:27
Irrelevant
M
Mlmikael21:27
Ballpark 10-20x faster than when going through the host OS' IO subsystem
evoskuil21:27
Still irrelevant
M
Mlmikael21:28
It's not about pushing it into LibBitcoin, but to push it into userland from OS,
and have LibBitcoin support it
evoskuil21:28
And you have to compare apples to apples. It only matters to the extent you can USE it.
And that will definitely require a VM layer, not just for Libbitcoin.
(edited)
M
Mlmikael21:29
A friend told me that Google does all their persistent access through userland direct access
just because of the surprising speed advantages
evoskuil21:29
Great, so do it.
I’m telling you how.
M
Mlmikael21:29
however hardware operates in terms of load/store operations, not a memory abstraction
evoskuil21:30
And software does NOT
M
Mlmikael21:32
> I’m telling you how. How, without implementing vmem_map() and vmem_unmap() kind of virtual memory page allocate/deallocate virtual memory management support for my host OS?
> And software does NOT Software does! mmap() file as virtual memory is an abstraction on top of persistent storage HW load/store operation, not the other way around
evoskuil21:56
Exactly, software requires the abstraction.
You write the abstraction required by software in software.
Map and unmap do not have to move anything. They merely give the underlying code a signal.
(edited)
M
Mlmikael21:59
A competent friend suggests "I would argue that it's libbitcoin that lacks the hook you desire" (if he understood correctly)
maaybe through the SIGSEGV signal handler that i could somehow emulate virtual memory somehow
evoskuil22:00
The key phrase being “you desire” vs. “you require”.
There are no “hooks” in properly designed software.
(edited)
There are interfaces. And the interface you require is available.
M
Mlmikael22:01
points out "If you can map a page so that it's unreadable and unwritable and then trap on the sigsegv when someone tries to access it you could potentially remap they area"
evoskuil22:03
The memory class implements reads and writes. You simply implement it as part of the interface.
There is one class for managing the storage and one class for manage reads and writes, which is allocated off of the storage manager.
There are two Computer Science PhD level developers with extensive Libbitcoin experience. Phillip and Kulpreet. Kulpreet has done a lot of work in this area. I suggest you contact him.
(edited)
M
Mlmikael22:50
@evoskuil that sounds great! How do I reach Kulpreet?
evoskuil22:52
@jungly
✋🏾 1
evoskuil23:01
Point?

Wednesday, February 3, 2021

M
Mlmikael11:17
@evoskuil is Phillip Fil here on Slack?
Point?
Just saw you were the code committers to that part nvm
one class for manage reads and writes, which is allocated off of the storage manager.
@evoskuil Yey! - which class(es) is it that manages the reads and writes? Looking forward to dive further.
M
Mlmikael12:35
Oh A clarifies one C++ism that applies here, neat!: "You can replace that memory_ptr with an abstract interface that mimcs the mechanics of a character array So you don't need to change the actual code, and the memory_ptr is replaceable yep you might even have to get into templates" yey, hm
(edited)
"- memory_ptr is a typedef for char*, but actually this should probably become a template value MemoryPtr. So class implementations using memory_ptr might have to be moved into .ipp header files - Create an adapter class that has operator [] for accessing and writing the underlying data Since this is latency sensitive, I suggest using templates not polymorphism"
M
Mlmikael12:58
So, he suggests that LibBitcoin's memory_ptr can be abstracted upon. LB's memory_ptr is defined as memory::ptr . Exploring.
M
Mlmikael13:08
right so memory is defined here https://github.com/libbitcoin/libbitcoin-database/blob/master/include/bitcoin/database/memory/memory.hpp#L33-L43 /// This interface defines remap safe unrestricted access to a memory map. class BCD_API memory { public: typedef std::shared_ptr<memory> ptr; /// Get the address indicated by the pointer. virtual uint8_t* buffer() = 0; /// Increment the pointer the specified number of bytes within the record. virtual void increment(size_t value) = 0; };
He says: "you can abstract uint8_t* but you should replace it with a custom type I would talk with Eric about this because he will design how to do this"
M
Mlmikael14:00
@evoskuil he suggests i can do operator overloading on the uint8_t, however he suggested a couple times I should talk to you for the possibility of a LibBitcoin redesign in some part, to operate on whole blobs instead of individual uint8_t:s - though if I can pull off the uint8_t operator overloading thing fast then that might be totally fine as a start
evoskuil14:04
I did not suggest any change to Libbitcoin.
M
Mlmikael14:38
Very interesting, all places in LB where .buffer() is used are: .//libbitcoin-database/include/bitcoin/database/impl/slab_manager.ipp: auto deserial = system::make_unsafe_deserializer(memory->buffer()); .//libbitcoin-database/include/bitcoin/database/impl/slab_manager.ipp: auto serial = system::make_unsafe_serializer(memory->buffer()); .//libbitcoin-database/include/bitcoin/database/impl/record_manager.ipp: auto deserial = system::make_unsafe_deserializer(memory->buffer()); .//libbitcoin-database/include/bitcoin/database/impl/record_manager.ipp: auto serial = system::make_unsafe_serializer(memory->buffer()); .//libbitcoin-database/include/bitcoin/database/impl/list_element.ipp: auto serial = system::make_unsafe_serializer(memory->buffer()); .//libbitcoin-database/include/bitcoin/database/impl/list_element.ipp: auto serial = system::make_unsafe_serializer(memory->buffer()); .//libbitcoin-database/include/bitcoin/database/impl/list_element.ipp: auto serial = system::make_unsafe_serializer(memory->buffer()); .//libbitcoin-database/include/bitcoin/database/impl/list_element.ipp: auto deserial = system::make_unsafe_deserializer(memory->buffer()); .//libbitcoin-database/include/bitcoin/database/impl/list_element.ipp: return std::equal(key.begin(), key.end(), memory->buffer()); .//libbitcoin-database/include/bitcoin/database/impl/list_element.ipp: auto deserial = system::make_unsafe_deserializer(memory->buffer()); .//libbitcoin-database/include/bitcoin/database/impl/list_element.ipp: auto deserial = system::make_unsafe_deserializer(memory->buffer()); .//libbitcoin-database/include/bitcoin/database/impl/hash_table_header.ipp: memset(memory->buffer(), (uint8_t)empty, file_size); .//libbitcoin-database/include/bitcoin/database/impl/hash_table_header.ipp: auto serial = system::make_unsafe_serializer(memory->buffer()); .//libbitcoin-database/include/bitcoin/database/impl/hash_table_header.ipp: auto deserial = system::make_unsafe_deserializer(memory->buffer()); .//libbitcoin-database/include/bitcoin/database/impl/hash_table_header.ipp: auto deserial = system::make_unsafe_deserializer(memory->buffer()); .//libbitcoin-database/include/bitcoin/database/impl/hash_table_header.ipp: auto serial = system::make_unsafe_serializer(memory->buffer()); .//libbitcoin-database/src/result/transaction_iterator.cpp: auto deserial = make_unsafe_deserializer(memory->buffer());
So it's mostly make_unsafe_deserializer(). There's one memset() and one equal().
..and make_unsafe_serializer(), that should be the writer part together with memset().
evoskuil14:40
🤷🏻‍♂️
M
Mlmikael15:21
Wait what are the multiple OS threads in LibBitcoin doing again. So one thing you have parallellism for is to process input from different Bitcoin node peer connections in parallell.
Then of course, the program that uses LibBitcoin is free to have as many OS threads as it wants, to traverse the blockchain from those OS threads. However, those "give me a block please", "give me this transaction by id/index/etc. please" operations will never mean interaction with another OS thread right?
@evoskuil 😃
evoskuil15:28
Concurrent reads of the same segment are allowed.
Concurrent writes and read/write of different segments are allowed.
All threads are logical threads, and of course scheduling of those threads is controlled by the operating system. Because that’s what an operating system is.
M
Mlmikael15:33
@evoskuil is there any writeup anywhere of what different OS thread roles are involved in a full-fledged LibBitcoin node instance?
I remember LibBitcoin's separate OS threads for driving the peer to peer networking both outward and inward direction
evoskuil15:34
All threads are “OS threads”
M
Mlmikael15:35
I'm a program (= OS process) that runs my own LibBitcoin node, i just ->start():ed it and now i'm having my own N OS threads doing all kinds of blockchain traversal fun, and also broadcasting some txes
evoskuil15:35
The write up is the code
M
Mlmikael15:35
= typical block explorer usecase really heh
M
Mlmikael17:33
Neat we're reading. @evoskuil/@neillm I will comprehensively answer the following question myself by deep reading the code, however if you have any comment would be much appreciated: When in my application OS thread I make a call into LibBitcoin to do blockchain queries like retrieve a block, retrieve a transaction, and thus traverse all kinds of aspects of the blockchain, do there exist any points in LibBitcoin codes during the course of those operations that could be BLOCKING, EXCEPT for the disk IO itself (i.e.: that is in vanilla LibBitcoin the reads to the mmapped memory)? E.g. some point where some LibBitcoin logic would go into a blocking wait for some LibBitcoin node-internal worker thread, such
Any comment much appreciated. I think the answer will be "no" just because LibBitcoin is so clean - slim - neat - sensible in its design.
evoskuil17:38
There is no blocking in memory management except for allocation. Data access critical sections are in the store code, not at the lower level memory management. There is also a critical section in work queue/dequeue.
M
Mlmikael17:44
Great. So when you're an application thread doing blockchain explorer type work then the only thing that could block would be that the mmapped memory would need to read from disk. Great. Thanks for clarifying!
Ah and the block reading interface has a consistent view function so even if the node happens to make a reorg while in a blockchain explorer type job, that job will succeed even if the block was reorged away.
M
Mlmikael17:55
LibBitcoin does not use thread-local storage for anything does it?
I presume not (beyond if Boost for any reason would depend on it, but I don't think Boost does). Neat.
evoskuil18:06
It may in one case. Just search.
M
Mlmikael18:27
aha interesting, thanks for syaing

Thursday, February 4, 2021

M
Mlmikael08:38
@evoskuil just to save me from derpy mistakes, in this one optional case where it may, through which API does LB use the thread local storage then?
evoskuil17:24
Don’t recall, and it may just be a todo comment, but it would be through a boost threading class. Possibly in a base in -system. Should be an easy grep.

Friday, February 5, 2021

evan00:48
Why does it say “#1 new release” ?
Image from iOS
evoskuil00:50
🤷🏻‍♂️
Guess it is 😯
🎉 1
evan19:29
Image from iOS MPEG 4 Video · 3.6 MB
Too cool

Saturday, February 6, 2021

evoskuil17:16
Sweeeet!

Thursday, February 18, 2021

P
Phil17:47
Hi, I browsed the LB codebase for use of thread local storage, and i find one usage instance, which is that the Boost thread local storage abstraction is used to carry a PRNG, here: .//libbitcoin/src/utility/pseudo_random.cpp: static boost::thread_specific_ptr<std::mt19937> twister(deleter); Just curious why is this done, the OS for instance offers a random function?
Was there any particular reason?
neillm18:00
I believe the random engine used is not thread-safe, so each thread uses its own to avoid some complex locking around it
P
Phil18:13
Oh, I see. But why not just use OS random numbers facility?
pmienk18:14
Historically, not so random
neillm18:15
Like /dev/urandom? If so, I believe those wrappers also take contributions from OS random, but not 100%
(edited)
Also, none of the pseudo-random in libbitcoin is for anything security related
P
Phil18:19
No, I mean the C API! 🙂
neillm18:22
Because the code is C++? 🙂 And rand() has the same problem, and has different distribution properties than the one we're using
P
Phil18:22
linux.die.net
The arc4random() function uses the key stream generator employed by the arc4 cipher, which uses 8*8 8 bit S-Boxes. The S-Boxes can be in about (2**1700) ...
neillm18:23
Generally the idea is to stick to std C++, and then boost as needed.
P
Phil18:24
why is this any relevant? the thing with your current solution is that it “”taints”" :-) the thread local storage. i’m doing some deep plumbing where i’ll use thread local storage for myself, so something needs to move 🙂 random() looks like a nice substitute in my case
neillm18:27
I'm also not understanding the relevance here
P
Phil18:28
Neil, i have an idea: to clear up the TLS for me, actually this would work fine: locate the PRNG to a static variable (= one global) and put a mutex around its accesses. This would be the mildest code rewrite, preserve LB as-is the most, while freeing up the TLS for me. :-) Could this be relevant for a LibBitcoin ./configure optional? 🙂 ./configure --don’t-touch-tls-please
neillm18:31
I believe that's specifically what was trying to be avoided
👍 2
P
Phil18:36
Is that TLS only used in LB worker threads?
evoskuil18:37
🤷🏻‍♂️ what is the issue?
P
Phil18:41
I am making a C++ program which will contain an LB Node class instance i do all kinds of stuff in my program, and one of the exotic things i do is that i’m using boost thread local storage myself! how do I do this without tanking & corrupting LB + my program

Friday, February 19, 2021

evoskuil18:35
What is the issue with "corruption"?
Generally speaking, if your program cannot work with other code that properly uses TLS, there is a problem with your code.
evoskuil19:21
I'm considering dropping support for VS2013. Support for VC++11 in VS2013 is only available through a technology preview addition to VC++, and there are a couple things we have to do in the source to accommodate unsupported C++11 features. It's not a huge problem, but it would speed up development and testing, especially given the present need to add VS2019. Looking for anyone who has current hard dependencies on VS2013.
👍 2

Friday, February 26, 2021