Hey guys,
Long time lurker, first time posting. Let me get right to it.
I have been going through nearly every algorithms whitepaper, or implementation and ingesting all i can, because why the hell not.
Now i can not only appreciate the level of hard work that went into these algorithms, and then the optimizations that happen on some after. Some of the source for some algorithms took me weeks to fully ingest and understand on a programming and mathematical level. Now the ones that always interested me the most was CryptoNight and Blake2. I quite enjoy the characteristics of how they come up with different ways to favor the CPU. However i have been experimenting with my own modified version of these algorithms and one of them is a extremely simple idea that was so easy to implement and test that it seems i have to be missing something.
This algorithm would also favor CPUs, would have ridiculously fast validation time, and should be ASIC unfavorable, as well as not much increase on GPUs if possible at all.
PoW Algorithm
There would be a fixed length Merkel Tree of 8 or 13 gigs (relatively high), and the tree would be made of 32 byte Blake2 leafs. So lets say for the sake of the post we decided on a ~8G Tree. For a ~8G tree consisting of 32 byte leafs we would need to add
134217728 (2^27) "Base Leafs" to the tree in a loop, example:
(Pseudo code to save time)
MerkelTree MT = new MerkelTree();
for (long long i = 0; i < (2^27); i++)
{
MT.addLeaf(Blake2.hasher(i));
}
When complete we would have a ~8G MerkelTree in the memory which are simply hashes of the index of the loop. The exact size of the tree after branches combine is
(2**28 - 1) * 32 (8589934560 bytes). Now, this is a PoW post after all, so we need to include the
Block Header and Nonce. In this implementation, it would literally be up to client miner where in the
BASE of the MerkelTree the
Block Header and Nonce would go, the algorithm would not care, as long as its in the base. So lets update our example client code to the following:
MerkelTree MT = new MerkelTree();
char* blockHeader = ...;
long long headerPOS = getRandomLong() % (2^27);
long long Nonce = getRandomLong();
for (long long i = 0; i < (2^27); i++)
{
if(headerPOS == i)
MT.addLeaf(Blake2.hasher(blockHeader + Nonce));
MT.addLeaf(Blake2.hasher(i));
}
Now its also important to know that the other leafs beside the
Block Header and Nonce hash can be a hash of anything the client also wants and doesn't have to be the index hashed, allowing this to be dynamic as possible, and allow a infinitely larger possible outputs.
So now at this point the machine as the "Base Leafs" entirely completed. Now we need to complete the MerkelTree Computation, and get our
Root and
Proofs.
The proof would simply be a simple json array or binary Struct of the siblings and siblings positions needed to achieve the
Root.
An example of a proof item, which the
Proofs would consist of a array of these items :
{'right': '19581e27de7ced00ff1ce50b2047e7a567c76b1cbaebabe5ef03f7c3017bb5b7'}
Which would now bring our code to:
MerkelTree MT = new MerkelTree();
char* blockHeader = ...;
long long headerPOS = getRandomLong() % (2^27);
long long Nonce = getRandomLong();
for (long long i = 0; i < (2^27); i++)
{
if(headerPOS == i)
MT.addLeaf(Blake2.hasher(blockHeader + Nonce));
MT.addLeaf(Blake2.hasher(i));
}
MT.computeTree();
proofs = MT.getProof(Blake2.hasher(blockHeader + Nonce));
rootHash = MT.root;
The miner now hash attempted his first solution. If the
Root, hash is lower than the set target then the Mining client can now send the following to the network:
- The Nonce used
- The Root Hash
- The Proofs
Optimization (IMPORTANT!):
Let it be known that because most of the 8GB MerkelTree is random hashes based on the clients wishes, it is most likely a fact that instead of newly allocating 8GB of RAM each loop cycle, the client will most likely allocate once, and only slightly modify on each attempt to get a solution. This is 100% ok in my eyes, and doesn't break the PoW more than it compliments it in my opinion. There are such a vast varieties of ways to produce a different solution with this algorithm like changing the position of the Block Header, the Nonce, each and every leaf in the base, timestamps, and more.Notes: - The amount of RAM needed is arbitrary right now and would most likely need to be agreed upon in a best case manner, with keeping in mind that we would want to favor
- CPU/MEM combo over GPUs, and try to completely eliminate ASICs.
- The base hashing algorithm Blake2 is insanely fast on CPUs, and secure with the right implementation.
- The algorithm above can also use CryptoNight instead if needed to hit its favorable CPU goal, however i would hate to introduce the slowdown.
Validation:
- First, the network would examine the Root, and Proofs hashes length to ensure they are 32 bytes in length, to migrate any attempts to use less RAM.
- Second, the network would examine the Levels of the Proofs array. If a client tried to use less than the 8GB Tree, then the levels would be less and invalid.
- Third, with all preconditions checked, the network then combines the Block Header and Nonce the same way the client did, and hashes them to get the Hash Target.
- Fourth, the network starts with the Hash Target found and combines each Proof Level in the Proof Array. If the remaining last hash is equal to the Root, then the PoW is accepted and completed.
Notes: - The validation is one of the best things i love about this algorithm. The work sent up by the miner to the network is less than 1kb! and the only thing the network needs to do to verify is simply make sure the Block Header and Nonce hash is in the base, and that the proofs match up with the root. This is such a small computation, with only validating a MerkelTree and Blake2 hash speed, that it should in theory be among some of the fastest out there now.
- The idea is that when validating, its not just the Root that matters, but the Block Header being in the base and matching has just as strong validation as that root, and when trying to game a MerkelTree, you change the whole thing. So why use anything more complicated than that.
Conclusion:
What i have purposed is a PoW based algorithm that doesn't use 2 through 10 algorithms in one to make it secure, but rather kept it simple with using the MerkelTree in a unique way. These trees provide a amazing level of security and flexibility by themselves, that when combined with a fast algorithm, seems to be up to snuff, if not better than most PoW's out there. Despite my long and poorly written post, this algorithm on paper is actually really simple compared to most of its bothern out there, and the idea of mining with a MerkelTree but in a different way than MTP, interests me for its ridiculously fast validation techniques.
I also enjoy the "Less constants, more dynamic" approach to these algorithms, where the client can choose many of the inputs for themselves, and the network doesn't care as long as the X set rules are used in those choices.
Now that i have laid my thought process and small PoC out there, i would love for some of the brilliant minds here to poke some holes in this, and attempt to break the system. If breaking it is not possible, then is there a reason why a experimental coin shouldn't be created to test this out further?Thank you for your time.