Wie würde ein Miner mit einer riesigen Blockzeit fertig werden?

In Anbetracht dessen:

  1. Gemäß dem Weiß-Gelb-Papier muss der Zeitstempel eines Blocks größer sein als der des Elternteils .
  2. Gemäß dem Whitepaper kann der Zeitstempel eines Blocks innerhalb von 15 Minuten von der übergeordneten Blockzeit liegen .

Würde das bedeuten, dass bei einer differenziellen Sperrzeit von 14 min

  1. Der nächste gewinnende Miner muss entweder einen Zeitstempel fälschen, und dies könnte möglicherweise dazu führen, dass die Blockchain in Zukunft immer schneller läuft,
  2. oder warten, bis seine Unix-Zeit aufholt.

Ist das wahr? Gibt es einen Grund, warum es empirisch nie passiert ist ?

Aus Gründen der Klarheit gibt es keine Regel in Bezug auf 15 Minuten (das Whitepaper ist alt und nicht aktualisiert) ethereum.stackexchange.com/questions/5924/… . Trotzdem ist das eine gute Frage.

Antworten (2)

Zusammenfassung

Bei einem Block, der von einem Miner abgebaut wird, der seine Computerzeit vor die aktuelle „Echtzeit“ setzt, wird sein Gewinnerblock von anderen Ethereum-Knoten abgelehnt. Andere Bergleute im Ethereum-Netzwerk werden den Bergbau auf dem letzten gültigen Block fortsetzen.


Einzelheiten

Gemäß dem Code von Github – Go Ethereum – Consensus/ethash/consensus.go, Zeilen 220-284 :

// verifyHeader checks whether a header conforms to the consensus rules of the
// stock Ethereum ethash engine.
// See YP section 4.3.4. "Block Header Validity"
func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent *types.Header, uncle bool, seal bool) error {
  // Ensure that the header's extra-data section is of a reasonable size
  if uint64(len(header.Extra)) > params.MaximumExtraDataSize {
    return fmt.Errorf("extra-data too long: %d > %d", len(header.Extra), params.MaximumExtraDataSize)
  }
  // Verify the header's timestamp
  if uncle {
    if header.Time.Cmp(math.MaxBig256) > 0 {
      return errLargeBlockTime
    }
  } else {
    if header.Time.Cmp(big.NewInt(time.Now().Unix())) > 0 {
      return consensus.ErrFutureBlock
    }
  }
  if header.Time.Cmp(parent.Time) <= 0 {
    return errZeroBlockTime
  }
  // Verify the block's difficulty based in it's timestamp and parent's difficulty
  expected := CalcDifficulty(chain.Config(), header.Time.Uint64(), parent)
  if expected.Cmp(header.Difficulty) != 0 {
    return fmt.Errorf("invalid difficulty: have %v, want %v", header.Difficulty, expected)
  }
  // Verify that the gas limit is <= 2^63-1
  if header.GasLimit.Cmp(math.MaxBig63) > 0 {
    return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, math.MaxBig63)
  }
  // Verify that the gasUsed is <= gasLimit
  if header.GasUsed.Cmp(header.GasLimit) > 0 {
    return fmt.Errorf("invalid gasUsed: have %v, gasLimit %v", header.GasUsed, header.GasLimit)
  }

  // Verify that the gas limit remains within allowed bounds
  diff := new(big.Int).Set(parent.GasLimit)
  diff = diff.Sub(diff, header.GasLimit)
  diff.Abs(diff)

  limit := new(big.Int).Set(parent.GasLimit)
  limit = limit.Div(limit, params.GasLimitBoundDivisor)

  if diff.Cmp(limit) >= 0 || header.GasLimit.Cmp(params.MinGasLimit) < 0 {
    return fmt.Errorf("invalid gas limit: have %v, want %v += %v", header.GasLimit, parent.GasLimit, limit)
  }
  // Verify that the block number is parent's +1
  if diff := new(big.Int).Sub(header.Number, parent.Number); diff.Cmp(big.NewInt(1)) != 0 {
    return consensus.ErrInvalidNumber
  }
  // Verify the engine specific seal securing the block
  if seal {
    if err := ethash.VerifySeal(chain, header); err != nil {
      return err
    }
  }
  // If all checks passed, validate any special fields for hard forks
  if err := misc.VerifyDAOHeaderExtraData(chain.Config(), header); err != nil {
    return err
  }
  if err := misc.VerifyForkHashes(chain.Config(), header, uncle); err != nil {
    return err
  }
  return nil
}

Die folgenden Zeitvalidierungen werden durchgeführt, wenn ein neuer Block von einem Ethereum-Knoten empfangen wird:

if (newblock.header.time > thiscomputer.time) {
    error "block in the future"
}

und

if (newblock.header.time <= newblock.parent.header.time) {
    error "timestamp equals parent's"  
}

Die Fehlermeldungen stammen von Github – Go Ethereum – Consensus/errors.go, Zeile 28 und Github – Go Ethereum – Consensus/ethash/consensus.go, Zeile 50 .



Prüfung des Paritätszeitstempels

Von Github - Parity - ethcore/src/verification/verification.rs, Zeilen 197-209 :

/// Check header parameters agains parent header.
fn verify_parent(header: &Header, parent: &Header) -> Result<(), Error> {
    if !header.parent_hash.is_zero() && parent.hash() != header.parent_hash {
        return Err(From::from(BlockError::InvalidParentHash(Mismatch { expected: parent.hash(), found: header.parent_hash.clone() })))
    }
    if header.timestamp <= parent.timestamp {
        return Err(From::from(BlockError::InvalidTimestamp(OutOfBounds { max: None, min: Some(parent.timestamp + 1), found: header.timestamp })))
    }
    if header.number != parent.number + 1 {
        return Err(From::from(BlockError::InvalidNumber(Mismatch { expected: parent.number + 1, found: header.number })));
    }
    Ok(())
}

Die Parität prüft also, ob der Zeitstempel des Blocks > Zeitstempel des übergeordneten Blocks ist, prüft jedoch nicht, ob der Zeitstempel des Blocks in der Zukunft liegt.

Von Github - Parity - ethcore/src/verification/verification.rs, Zeilen 80-84 :

/// Phase 3 verification. Check block information against parent and uncles.
pub fn verify_block_family(header: &Header, bytes: &[u8], engine: &Engine, bc: &BlockProvider) -> Result<(), Error> {
    // TODO: verify timestamp
    let parent = try!(bc.block_header(&header.parent_hash).ok_or_else(|| Error::from(BlockError::UnknownParent(header.parent_hash.clone()))));
    try!(verify_parent(&header, &parent));

Es gibt ein ausstehendes TODO, um den Zeitstempel zu überprüfen.

Das bedeutet also, dass Geth strenger ist als die Spezifikation. Sie gehen davon aus, dass die Knoten ordnungsgemäß synchronisierte Zeitserver haben, und glauben nicht einmal, dass es eine Latenz von mehr als einer Sekunde gibt. Interessant. Was, wenn Parity das anders sieht?
@Roland Kofler, ich habe das relevante Code-Bit für Parität am Ende meiner Antwort hinzugefügt.
Der tatsächliche Speicherort im Geth-Code ist hier: github.com/ethereum/go-ethereum/blob/885c13c/consensus/ethash/…
@RolandKofler Der Zeitstempel muss einfach kleiner als der aktuelle Zeitstempel des Knotens und größer als der Zeitstempel des letzten Blocks sein. Daher wird die Latenz berücksichtigt, da die Latenz nur in eine Richtung geht – Blöcke kommen langsam an, nicht dass Blöcke in der Zukunft ankommen;)

Wenn ein Miner M einen Block B mit einem Zeitstempel weit in der Zukunft sieht, würde er wahrscheinlich Folgendes tun: Anstatt auf B aufzubauen, würde er seinen eigenen Block C mit einem genaueren Zeitstempel veröffentlichen. Dadurch wird M wahrscheinlich belohnt, da andere wahrscheinlich auf C statt auf B aufbauen werden.

Wie in der Frage erwähnt, müsste ein Miner wie M einen gefälschten Zeitstempel verwenden, um auf Block B aufzubauen (da der Zeitstempel eines Blocks größer sein muss als der des übergeordneten Blocks). Kaufen Sie, warum sollte M auf B aufbauen, wenn dies andere Miner erfordern würde, um einen gefälschten Zeitstempel zu verwenden, um auf dem Block von M aufzubauen? Es gibt einen größeren Vorteil für M, einen Block C mit einem genaueren Zeitstempel zu veröffentlichen.

Die Strategie von M wird durch die Tatsache unterstützt, dass sich alle Ethereum-Knoten standardmäßig um die richtige Zeit herum koordinieren, sodass M eine Vorstellung davon hat, ob es auf B aufbauen oder sein eigenes C veröffentlichen soll.


Um eine Verbindung mit der Antwort von @BokkyPooBah herzustellen: Wenn ein Knoten B sieht, lehnt der Knoten B mit BlockFutureErr ab, damit er keinen ungültigen Block X (wobei B.timestamp >= X.timestamp) pro Protokoll erzeugt . Beachten Sie jedoch, dass das Protokoll nichts über die Ungültigkeit eines Blocks mit einem weit in der Zukunft liegenden Zeitstempel aussagt.

Das ist ein toller Grund, warum!