¿Cómo haría frente un minero a un gran bloque de tiempo?

Dado que:

  1. De acuerdo con el papel blanco y amarillo , la marca de tiempo de un bloque debe ser más grande que la del padre .
  2. De acuerdo con el libro blanco , la marca de tiempo de un bloque puede estar dentro de los 15 minutos del tiempo de bloqueo principal .

¿Eso implicaría que si ocurre un tiempo de bloqueo diferencial de 14 min,

  1. el próximo minero ganador tiene que falsificar una marca de tiempo, y esto podría hacer que la cadena de bloques se ejecute cada vez más rápido en el futuro,
  2. o espere hasta que su tiempo de Unix se ponga al día.

¿Es esto cierto? ¿Hay alguna razón por la que empíricamente nunca sucedió ?

Para mayor claridad, no hay una regla relacionada con los 15 minutos (el libro blanco es antiguo y no está actualizado) ethereum.stackexchange.com/questions/5924/… . Aún así, esta es una buena pregunta.

Respuestas (2)

Resumen

Un bloque extraído por un minero que establece el tiempo de su computadora por delante del tiempo "real" actual tendrá su bloque ganador rechazado por otros nodos de Ethereum. Otros mineros en la red Ethereum continuarán extrayendo en el último bloque válido.


Detalles

Según el código, de Github - Go Ethereum - consenso/ethash/consensus.go, líneas 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
}

Las siguientes validaciones de tiempo se realizan cuando un nodo Ethereum recibe un nuevo bloque:

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

y

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

Los mensajes de error son de Github - Go Ethereum - consenso/errors.go, línea 28 y Github - Go Ethereum - consenso/ethash/consensus.go, línea 50 .



Comprobación de marca de tiempo de paridad

De Github - Parity - ethcore/src/verification/verification.rs, líneas 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(())
}

Por lo tanto, Parity verifica que la marca de tiempo del bloque sea > la marca de tiempo del bloque principal, pero no verifica si la marca de tiempo del bloque está en el futuro.

Desde Github - Parity - ethcore/src/verification/verification.rs, líneas 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));

Hay una TODO pendiente para verificar la marca de tiempo.

Eso significa que Geth es más estricto que la especificación. asumen que el nodo tiene servidores de tiempo sincronizados correctamente y ni siquiera piensan que hay una latencia de más de un segundo. Interesante. ¿Y si Parity lo ve diferente?
@Roland Kofler, agregué el código relevante para Parity al final de mi respuesta.
La ubicación real en código geth está aquí: github.com/ethereum/go-ethereum/blob/885c13c/consensus/ethash/…
@RolandKofler, la marca de tiempo simplemente tiene que ser menor que la marca de tiempo actual del nodo y mayor que la marca de tiempo del último bloque. Por lo tanto, se tiene en cuenta la latencia, ya que la latencia solo va en una dirección: los bloques llegan lentamente, no que los bloques lleguen en el futuro;)

Si un minero M ve un bloque B con una marca de tiempo en un futuro lejano, esto es lo que probablemente haría: en lugar de construir sobre B, publicaría su propio bloque C con una marca de tiempo más precisa. Al hacer esto, es probable que M sea recompensado, ya que es probable que otros se basen en C en lugar de B.

Como se menciona en la pregunta, para construir sobre el bloque B, un minero, como M, tendría que usar una marca de tiempo falsa (ya que la marca de tiempo de un bloque debe ser más grande que la del padre). ¿Por qué M construiría sobre B, cuando hacerlo requerirá que otros mineros usen una marca de tiempo falsa para construir sobre el bloque de M? Hay más beneficios para M si publica un bloque C con una marca de tiempo más precisa.

La estrategia de M se ve favorecida por el hecho de que, de forma predeterminada, todos los nodos de Ethereum se coordinan en torno a la hora correcta, por lo que M tiene una idea de si debe construir sobre B o publicar su propio C.


Para conectarse con la respuesta de @BokkyPooBah, cuando un nodo ve B, el nodo rechaza B con BlockFutureErr para que no produzca un bloque no válido X (donde B.timestamp >= X.timestamp) por protocolo . Pero tenga en cuenta que no hay nada en el protocolo que diga algo sobre la invalidez de que un bloque tenga una marca de tiempo en un futuro lejano.

¡Esa es una razón increíble por la cual!