4

This is little bit similar to this: Do i need to be synchronized to blockchain when creating DAG for mining on pool? and this https://github.com/ethereum/go-ethereum/issues/2333

Essentially pool sends seed hash used to generate the DAG, what I don't understand is how is this done without the block number.

In https://github.com/ethereum/wiki/wiki/Ethash the Hashimoto loop expects to know cache size which we can get only with block number.

So my question is: How is mining done with only the parameters from eth_getWork? And ideally keep it in terms of Ethash specification (https://github.com/ethereum/wiki/wiki/Ethash#defining-the-seed-hash)

6
+150

To recap, your mining pool will respond to the eth_getWork call with the following information:

DATA, 32 Bytes - current block header pow-hash
DATA, 32 Bytes - the seed hash used for the DAG.
DATA, 32 Bytes - the boundary condition ("target"), 2^256 / difficulty.

This information will be used to generate the DAG.

From here on I've referenced the Yellow Paper, rather than the Ethash wiki page. From the Yellow Paper, we can confirm the basis for your query:

J.3. Dataset generation. In order the generate the dataset we need the cache c, which is an array of bytes. It depends on the cache size csize and the seed hash s ∈ B32.

And:

J.2. Size of dataset and cache. The size for Ethash’s cache c ∈ B and dataset d ∈ B depend on the epoch, which in turn depends on the block number.

From section J.2. of the Yellow Paper you'll see that the size of the cache is constant for all mining performed during a given epoch, where an epoch is defined as 30,000 blocks (~100 hours). You therefore don't need to know the block number to calculate the cache size, only the current epoch number. The same holds true for the size of the DAG itself, which is also dependent on the current epoch number.

So... How do we locally find the epoch number?

We're told the seed hash by eth_getWork, but we can't reverse the hash to get the block number from which it was created. So we use trial and error. Starting with a block we know to be in epoch 0 - because we know epoch 0 is from block 0 to block 30,000 - we locally create a seed hash and check whether it matches the seed hash we've been sent.

An example in the code is as follows.

  • A call to getWork is made in MinerAux.h, and the 3 variables, including the seed hash, is returned
  • EthashAux::full() is called, and the seed hash from getWork is passed in
  • The action jumps to EthashAux.cpp, where the main functions are defined
  • In EthashAux::full() we call EthashAux::computeFull(), again passing through the seed hash
  • Here we calculate the (approximate) block number using blockNumber = EthashAux::number(_seedHash)

The pertinent part of EthashAux::number() is the following line:

for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = sha3(h), get()->m_epochs[h] = epoch) {}

... which loops through epoch numbers, hashes to create a seed hash, and checks it against the hash sent by getWork.

The function then returns an approximate block number: return epoch * ETHASH_EPOCH_LENGTH;.

We then later use this value to calculate the cache size, which in turn can be used in generating the cache.

Edit - Addendum:

Parity - the client written by Ethcore - does include the block number in its implementation of eth_getWork.

  • So are you saying that ethminer does a reverse lookup? Then why not just send the number instead of seed hash? – Visgean Skeloru Aug 29 '16 at 13:19
  • Correct - that's what I'm saying. I don't know why it wasn't originally implemented in the way you describe, but bugs 2333 and 2075 have/had been raised to address this. (2075 was closed with the reasoning that we'll be moving to PoS soon anyway... ) – Richard Horrocks Aug 29 '16 at 14:14
  • Updated the answer with a final note about Parity. – Richard Horrocks Aug 29 '16 at 15:10
  • Thanks, it sucks that geth did not change the parameters, it is still many years till POF comes.. – Visgean Skeloru Aug 29 '16 at 15:45
2

The DAG is generated from the seedhash itself and not the block number.

unsigned EthashAux::computeFull(h256 const& _seedHash, bool _createIfMissing)

While the seedhash is generated by taking the integer from the block number divided by the epoch length (30000 blocks)

defining-the-seed-hash

 def get_seedhash(block):
 s = '\x00' * 32
 for i in range(block.number // EPOCH_LENGTH):
     s = serialize_hash(sha3_256(s))
 return s

So a miner will compare it's current seedhash to the eth_getWork seedhash and if they differ, will generate a new DAG from the eth_getWork seedhash.

MinerAux.h#L553

if (current.seedHash != newSeedHash)
    minelog << "Grabbing DAG for" << newSeedHash;
if (!(dag = EthashAux::full(newSeedHash, true, [&](unsigned _pc){ cout << "\rCreating DAG. " << _pc << "% done..." << flush; return 0; })))
    BOOST_THROW_EXCEPTION(DAGCreationFailure());
if (m_precompute)
    EthashAux::computeFull(sha3(newSeedHash), true);
  • If the block number isn't needed, why the first thing computeFull() calculates blockNumber = EthashAux::number(_seedHash) ? – akvadrako Jun 20 '17 at 13:52
0

I think you might of meant block hash instead of number. I know block 1,000,000,000's block number, it's a billion. But we won't know it's block hash till it's mined!

Dags are generated from a seed hash that changes every epoch (30,000 blocks). Seed hashes are generated by the block numbers only with this function:

def get_seedhash(block):

s = '\x00' * 32
for i in range(block.number // EPOCH_LENGTH):
    s = serialize_hash(sha3_256(s))
return s

Because I know the block number of block a billion I could generate its dag now (but it would be huge)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.