Quick project info
This software is a "mining" software for a family of cryptocurrencies based on so-called Proof-of-Capacity. The best information about that concept is available in info materials from the BURST coin. You can learn some technical bits from resources on their home page, for example this whitepaper.
This submission is related to my work on this project between Jul 15, 2019 - Jul 06, 2019. I've made several improvements and added a special mining mode for a new coin type, and released it all as a minor feature release v2.300002.0. (If you'd like to know why the version number looks so strange, I wrote a bit here about the reasons for that)
1. Faster Round Interrupt
Feature branch for this change is feature/fast-round-interrupt.
Some users, including me, observed that sometimes Blagominer acts as if it didn't notice that the current round has ended, and keeps further scanning plot files, instead of interrupting and starting over with new block height.
After analyzing log files, it turned out that in fact the "interrupt" signal was issued to worker threads, but was ignored.
In short, it turned out that the "interrupt" mechanism in
worker.cpp : work_i() was too focused. In a multiply-netested loop that is responsible for visiting all plot directories and all plot files, then reading plot files in chunks, and so on - each worker thread only checked the interrupt flag in one specific moment at the end of a phase. If the worker were busy in preceding parts of its job, it could very well fail to notice the flag for a prolonged time. Also, for reasons yet unknown, for some users the worker threads seemed to simply skip that flag check and keep mining till end of input files. This seemed to happen especially on installations with many small plot files, or with very slow plot reading speeds.
Fortunatelly, a simple change of adding more interrupt points at various places in the workflow seems to have resolved these issues for now. Users reported that their miners react to round change much faster and no longer mine at wrong block heights for up to few minutes, losing chances of sending DLs on the new round.
2. Faster Exit
Feature branch for this change is feature/fast-exit.
This is a minor improvement for rarely-used feature, but nevertheless it was irritating. Blagominer supports a few hotkeys, including 'Q' which was meant to simply close the application. However, in many cases, the user had to wait a long time before it actually happened and before the app decided to actually quit.
This was caused by an overlook - the application could be closed in several ways ('q', ctrl+c, close the window, etc), but only one of them actually was setting the
exit_flag flag that causes all threads to quit. Fix was obvious: just set the flag in all exit handlers.
3. Fixed Memory Leak
The problem is described the best by that linked issue on GitHub. By observing the behavior and memory growth, I came to a conclusion that most probably the leak is located in networking operations, most probably in the
updater thread, since the leak seemed to grow regardless of mining operations.
Anyways, since the whole code often used
HeapFree() directly with zero exception safely, I decided to review everything and make it safer, hence the name of the branch. I wrote a
heap_allocator and changed all places using
HeapAlloc/Free to use
heap_allocator instead. This guaranteed that no buffer can get away without being released, and in fact it fixed the leak.
While at it, I also found and corrected few other issues, like wrong initial size for
MemoryStruct buffer for reading responses via CURL, or a hideous bug in the call back handling that response-reading. This one was nasty and hard to notice, since good old
memcpy, as a
void*-based function, gladly accepted a pointer to vector, instead of a pointer to the contents of that vector. I have no idea how lucky it was it didn't immediately crash. Magic and pure luck, seriously.
4. Fixed Configuration Reading Bug
Feature branch for this change is feature/fix-odd-config-reading-issues.
That was strange! One user reported on Discord that his
miner.conf file, while looking totally fine, was rejected by the Blagominer on startup with it stating something about "JSON parse errors".
We've analyzed the config file thoroughly and found no issues, so we started randomly chaning the config file and testing out when the issue occurs and when it does not. It turned out that the problem is related to the filesize being higher/lower than certain threshold. However, other users were successfully using config files several times longer without any problems.
Suspecting issues in the JSON parsing library, I started live debugging - and it actually turned out to be two issues, not one.
First of all, the code opens the config tile in
"rt" mode, well known for MS Windows developers for its tricky new line translation. Developer who wrote this code had good intentions, but failed to notice that preallocation buffer equal to filesize will not match the textsize when the newline translation kicks in. For example, file of 15200 bytes were read as 15030 characters, because 170 two-byte new-line markers were translated into 170 one-byte new-line markers.. However, this alone would not be an issue, since the developer used
HeapAlloc'ed buffer with
HEAP_ZERO_MEMORY flag, so any left over space should be zero'ed out
...or at least, theoretically, because it turned out that the API function used to read the file,
fread_s, didn't care about exact memory transfers. In
"rt" mode, it actually transferred more bytes than it should. Having read a chunk of a file, it squashed newline markers in-place, and then transferred the chole chunk, with no respect of the fact that the data was now shorter due to the transformation. To be honest, that's in fact not really relevant, as next chunks of data were written at correct places, and only the final chunk of data could habe any chance leaving any trailing trash. And that was exactly what was happening. Comparing original file and contents read by the
...and again, that alone wouldn't be a problem, if only the implementor took some care and had checked what was the return value form
fread_s. While that function indeed were dumping too many data to the output buffer, it actually was returning the correct number of actual data bytes written, different from the actual number of bytes written to the buffer. Taking that returned number and trimming the data in the output buffer at that point fixed the issue.
5. Added Support for DiskCoin Testnet
DiskCoin is a new type of Proof-of-Capacity coin that is currently being actively developed and we might expect its release in upcoming months. For now what's available, is a closed testnet (you have to register and apply for participating), and it's already known that the basic miner application is a modded Blagominer, probably from JohnnyFFM, as it lacks even the dual-mining capabilities from andzno1 version.
Needless to say, some of the users tried to mine DiskCoin on their testnet using recent versions of Blagominer and .. failed. It turned out that DiskCoin's Blagominer produced different dealines than the normal Blagominers, old or new versions alike.
Apparently, authors of DiskCoin tampered with the deadline calculation algorithms, and the challenge was accepted :D
How their "algorithm" was discovered is a different story, and it's a longer one, since reverse-engineering takes time, but the main points and obvservations can be summed up as:
- input data from the wallet/pool was totally ordinary
DISKCOIN%-08dwas visible in the EXE file, pretty suspicious considering it shows up neither in the UI nor log files
- their miner contains AES128 code that is not present in "original blago"
- their shabal routines and DL formulas seemed unchanged
- DiskCoin miner gets different deadlines because the miner silently alters the gen-sig received from the wallet/pool, it takes the gen-sig from the pool, builds a 'key' of
DISKCOIN%-08dsubstituting %d for current blockheight, then it decrypts the orignal gen-sig with this key with AES128, and obtains a "corrected gen-sig" in this way, which is then used as if it were the round's gen-sig.
- Yes. Seriously. It decrypts it, not encrypts it. It doesn't matter anyway, since the gen-sig is NOT an encrypted message, and AES128 couldn't care less, it just spits transformed bytes out, but nevertheless I find it hilarious to decrypt the gensig that way :D
I have no idea if this "corrected gensig" algorithm is just temporary precaution so the testnet is "safer" from outsiders, and no idea if this scheme will be repeated in the main network. We will see.
Since the gen-sig transformation is clearly dedicated to DiskCoin only, you can enable it on per-coin basis via
enableDiskcoinGensigs setting. It's OFF by default.
Also note that since the secret gen-sig transformation, once known, is pretty simple, your pool may automatically apply that transformation to enable users to use ANY classic miner application. If that's the case for your pool, keep
enableDiskcoinGensigs turned OFF.