This post was a very interesting read for me too.
I think, but I'm not sure, that I am somewhat familiar with the basic concepts of PoW in solo mining:
- Your mempool contains unconfirmed transactions, that makes the content for the next block being "generated"
- In order to have this block accepted as the next one in the chain, you need to find an SHA256d hash for this content + a nonce
- The hash is used in the header, and it needs to be below a defined target, i.e. difficulty, for the pushed block to be accepted by other nodes
Now I'm more confused about pooled mining. I admit that I never took the time to read the Stratum protocol RFC, spec or whatever.
What I'm not understanding here is how the pool manages to hide block content from pool miner (that's something I'm used to read when starting the miner, i.e. something like "pool is hiding block contents from us"). I understand that the hiding is necessary to prevent the pool miner from pushing the block solo, bypassing the pool and getting the block reward for himself instead of the pool. But then what exactly is the (partial?) content that is used to hash, and more importantly, how can it be useful to the pool, since the hash sent by the miner will not be block_content + nonce but other_content + nonce (+ extra nonce etc?).
What you're missing is that the hash used for proof of work is *not* a hash of the block contents, only a hash of the block header. The block header contains:
1) Block Version (version 2 currently)
2) Hash of the previous block
3) MerkleRoot hash of the transactions in the block
4) Timestamp
5) Bits (difficulty)
6) Nonce
#3 is the key part. A MerkleRoot is essentially a hash of hashes. So lets say you have 4 transactions, A, B, C, D. You take the hash of them, and call them A1, B1, C1, and D1. You would hash A1 and B1 together to get A2. You would hash C1 and D1 together to get B2. You then hash A2 and B2 together. You're now out of transactions to hash together, which means you have the MerkleRoot. It creates a tree/pyramid shape as you go. A1 would be your Coinbase transaction, which is why each pool/miner has a different MerkleRoot/transaction list.
The key for Stratum is that to rebuild a MerkleRoot, you only need one side of the branches in this tree of hashes. You can change A1 (coinbase), rehash it with B1 for a new A2, and hash it with the same B2, without redoing the C1+D1 hash. As such, the miner can constantly alter a field in the Coinbase transaction (ExtraNonce), to get a new A1, rehash it with B2, B3, B4, B5, B6, etc., until it is done and has a fresh MerkleRoot, without knowing the actual transactions.