HomeArticlesC++Now 2019: Arthur O’Dwyer “Boost.Blockchain: A new business model for open source”
C++Now 2019: Arthur O’Dwyer “Boost.Blockchain: A new business model for open source”
December 5, 2019
Hi folks. I’m Arthur O’Dwyer, and I’m here to tell you about a new business model for open source software. Monetizing application software is hard enough. You could make it software as a service. You could make people pay for support. You could make them pay for a “pro” version with extra features. But if you’re a library developer, like many people in this room, monetizing a C++ library is even harder. A lot of C++ libraries are header-only.
That means the source code has to be available. It’s difficult to tell who’s downloading and using your code, let alone how to get money out of them. Sure, you could put a big “Donate” button on your website. But there’s got to be a better way! I’m here to tell you about that better way. Enter Boost.Blockchain. Boost.Blockchain harnesses the power of upcoming C++26 and 29 features, such as constexpr, consteval, ranges, networking. Thanks to the pioneering efforts of some of the people in this very room, we can use compile-time networking to get you paid in Bitcoin when people compile your code. It’s simple to use. Here’s your header. We have a header guard, of course. Have to have a header guard. Your actual library functionality goes down there at the bottom. Above that, we include, which is where all the magic happens. We need to provide it with two config options.
BOOST_BLKCHN_FEE is how much we’d like to get paid,
on average, when someone compiles our header. I picked a value that’s about half a cent right now. Notice that we’re using Boost.Units, so it’s all type-safe. BOOST_BLKCHN_ADDRESS is the Bitcoin address where we want the fee to be sent. Notice that that’s actually a user-defined literal. How does that work? Short version is, a lot of macros. You don’t want to know. Let’s take a look at how Boost.Blockchain works on the inside. Insidewe spin up an io_service named ios. Notice that we’re using the io_service from constasio, not regular asio, because we want the networking to happen at compile-time. You might expect `constasio` to be a keyword, but it’s actually an identifier. Then we make our `static inline constexpr bool monetized`, and we set it to the result of this consteval function that creates a bitcoin miner initialized with BOOST_BLKCHN_ADDRESS, and we run it for a length of compile-time that’s calculated from our desired fee. And then we return true. You might wonder why we use right-shift instead of comma on that line there. It’s because right-shift looks cooler. blockchain::miner overloads the right-shift operator so that we’re able to do this. Finally, on the last line, we static_assert that we’ve successfully monetized our library. Let’s look at this time_to function that computes how long to mine for. It’s a consteval function, as you saw in the opening keynote. It takes in the desired fee; again we’re using Boost.Units here. So it’s all type-safe. First we make a couple of namespace aliases.
I highly recommend you do this any time you’re going to be dealing with ranges. (I actually recommend this, by the way.) And then we create a history object using our io_service and we fetch the blockchain into a local variable. Some compilers WILL run out of memory at this point. I think we’re working on it. By the way, why didn’t we say “co_await constexpr”? “co_await constexpr” being the same thing as regular “co_await” but at compile-time. It’s like “if constexpr”, “for constexpr”,— you can put “constexpr” in front of basically any keyword and it’ll do SOMETHING. Well, if we used “co_await”, then “time_to” would have to be a coroutine. The point of coroutines is that when you do a blocking operation — such as fetching the entire blockchain into memory in the compiler — the coroutine can suspend, and your thread can go
and do something else, such as compile the user’s actual code.
And we don’t want that. So at least for v1 it’s all synchronous code. This next bit — this is all bog-standard Ranges code. We get the last 100 transactions from the blockchain.
We extract the fees associated with those transactions. That’s a projection, there, with the member; we’re extracting just the “fee” member from each transaction. Gratuitous use of Ranges has the added benefit of decreasing performance to the point that the user may not even notice that we’re mining bitcoin in the background. Now this next part uses ranges::action::accumulate. That’s not in C++20. But it should be in C++29 if someone writes the paper. ranges::distance, on the other hand, is in C++20. That’s a real thing. It computes the distance of a range. If you’re familiar with some other Boost libraries, such as filesystem and datetime, you’re going to be confused by our use of forward-slash on that last line. The forward-slash here represents arithmetic division. Finally, we take that average transaction fee and divide our desired fee by it; then we multiply that ratio by the time it took to create those blocks. iter_distance is kind of like iter_swap. iter_swap says I don’t want to swap those two iterators, I want to swap what they point to. iter_distance says I don’t want to subtract those two iterators, I want to subtract what they point to. So we divide those two things and multiply by the time it took. Or something like that. The important thing is that it compiles, right? So let’s talk about some concerns you might have about using Boost.Blockchain in your own project. Could our entire business model be defeated by Modules? Could someone make a module out of your code and then use it in all their subsequent compiles without paying for those compiles? I’ll believe it when I see it. You might also ask, “What if the user just edits my header to remove the monetization code?” Well, first of all, if that’s happening, then man, you gotta get yourself a better class of users. The ones you have can’t be trusted. But there are technical ways to mitigate that. You could encrypt your code with a key stored in the blockchain. When the compiler provides sufficient proof-of-work, it gets access to the key; then it can use a consteval function to decrypt the source code and continue compilation. Reflection and metaclasses might also help with this. Also, if your users are doing something you don’t want them to do, that might be a problem that could be solved with Contracts. Future directions? I’ve only really thought of one. Running a bitcoin miner in the compiler is time-consuming and wasteful. Especially with distributed builds. We might consider moving the bitcoin miner into the build system. However, again, Modules might make this a moot point, as Modules effectively moves the build system back into the compiler. Boost.Blockchain.