I understand that mining a block involves trying to generate a hash that is less than or equal to a target dictated by a required difficulty. Ethereum Wiki on Ethash:
def mine(full_size, dataset, header, difficulty):
# zero-pad target to compare with hash on the same digit
target = zpad(encode_int(2**256 // difficulty), 64)[::-1]
from random import randint
nonce = randint(0, 2**64)
while hashimoto_full(full_size, dataset, header, nonce) > target:
nonce = (nonce + 1) % 2**64
return nonce
I also learned from this SE topic that the block hash itself isn't checked against the target, but rather it's Ethash(hash(block header), nonce) < TARGET, which explains why Ethereum blocks don't have the leading zeros we're accustomed to with Bitcoin.
To calculate the difficulty, we'd presumably invert the operation that generated a target from difficulty to instead generate a difficulty from an Ethash? And is a block's difficulty that inversion applied to Ethash(hash(block header), nonce) or is it that inversion applied to the target to generate the original required difficulty?
If the former, shouldn't we be able to regain consensus immediately in forks without requiring additional blocks given that the consensus chain is that with highest total cumulative difficulty, and the probability that multiple prongs would share this would be effectively zero?
If the latter, and given this excellent explanation for how the difficulty adjustment algorithm works, and given that the difficulty bomb continues to be postponed, I'd expect consecutive blocks on Etherscan to share difficulty with some regularity, i.e. in cases when the block is mined within 10-20 seconds of its parent. But this is contrary to what I see glancing at recent blocks on Etherscan.
Returning to the central question: How can I calculate a given Ethereum block's difficulty?
block.difficultynumber is calculated. – Shane Mar 09 '22 at 14:55