In Anbetracht dessen:
Würde das bedeuten, dass bei einer differenziellen Sperrzeit von 14 min
Ist das wahr? Gibt es einen Grund, warum es empirisch nie passiert ist ?
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.
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 .
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.
@Roland Kofler
, ich habe das relevante Code-Bit für Parität am Ende meiner Antwort hinzugefügt.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.
eth