¿Dónde se almacenan finalmente los registros de eventos de ethereum?

Hola, se ha escrito muchas veces, pero como observo, ninguna de las respuestas puede ser la indicada. Estoy interesado en lo siguiente. Si en un contrato inteligente realicé el evento, ¿dónde se almacenará? en blockchain, en caso afirmativo, ¿dónde exactamente? Si no está allí, ¿tal vez en los nodos? Estoy construyendo una aplicación seria y tengo que almacenar muchas cosas allí. así que finalmente tengo 2 preguntas.

1) ¿dónde se almacenan exactamente los registros? 2) si está en el encabezado del bloque, ¿puede describir cómo puedo confiar en que esos registros ingresen en el front-end de web3.js?

Respuestas (2)

Puede confiar en los registros porque están almacenados en su bloque respectivo. Si se cambia un registro, cambiaría el blockhash, la raíz del recibo, etc., al igual que con las transacciones. El árbol merkle ya no sería el mismo.

Entonces, si ejecuto una función a cierta altura de bloque, el recibo y, por lo tanto, el registro también serán parte del bloque a esa altura, así como mi transacción.

Para responder dónde se almacenan exactamente. En el recibo de transacciones prueba si no recuerdo mal.

crédito de imagen de https://medium.com/@preethikasireddy/how-does-ethereum-work-anyway-22d1df506369

Encabezado de bloque

Gracias por la buena explicación. Todo claro. Te haría una pregunta más si no te importa responder aquí. ¿De acuerdo? La cosa es que lo que hay que hacer es sobre big data. como saben, los contratos inteligentes no son para big data, debido al aumento del precio del gas. Entonces, una solución podría ser almacenar datos en IPFS o registros. Como se sabe, es mucho mejor almacenarlo en registros, porque todavía está en blockchain y eso es lo que le gusta a la gente. La pregunta es: cuando almaceno muchos datos allí, uno de los campos debe estar oculto y solo yo puedo obtener eso. nadie debería ir a blockchain y ver los registros. ¿Qué opinas?
Puede cifrar el campo con su clave pública para que solo su clave privada pueda descifrarlo. El problema es si el algoritmo de cifrado alguna vez choca y, por lo tanto, es posible adivinar las claves privadas, porque no podemos eliminar datos de un sistema como ethereum/ipfs/swarm. Aconsejaría usar Swarm (bzz en ethereum) sobre IPFS, ya que está más enredado con el ecosistema. Pero eso es solo preferencia personal después de usar ambos.

Cuando un contrato emite un evento, se almacena en la estructura StateDB:

// StateDBs within the ethereum protocol are used to store anything
// within the merkle trie. StateDBs take care of caching and storing
// nested states. It's the general query interface to retrieve:
// * Contracts
// * Accounts
type StateDB struct {
    db   Database
    trie Trie

    // This map holds 'live' objects, which will get modified while processing a state transition.
    stateObjects      map[common.Address]*stateObject
    stateObjectsDirty map[common.Address]struct{}

    // DB error.
    // State objects are used by the consensus core and VM which are
    // unable to deal with database-level errors. Any error that occurs
    // during a database read is memoized here and will eventually be returned
    // by StateDB.Commit.
    dbErr error

    // The refund counter, also used by state transitioning.
    refund uint64

    thash, bhash common.Hash
    txIndex      int
    logs         map[common.Hash][]*types.Log
    logSize      uint

    preimages map[common.Hash][]byte

    // Journal of state modifications. This is the backbone of
    // Snapshot and RevertToSnapshot.
    journal        *journal
    validRevisions []revision
    nextRevisionId int

    lock sync.Mutex
}

Este es el miembro que contiene los datos en la estructura anterior:

    logs         map[common.Hash][]*types.Log

Durante el procesamiento del bloque, cada transacción devuelve un recibo (con registros de eventos) y se almacenan en una matriz (desde core/state_processor.go):

// Iterate over and process the individual transactions
for i, tx := range block.Transactions() {
    statedb.Prepare(tx.Hash(), block.Hash(), i)
    receipt, _, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, usedGas, cfg)
    if err != nil {
        return nil, nil, 0, err
    }
    receipts = append(receipts, receipt)
    allLogs = append(allLogs, receipt.Logs...)
}

Una vez que se recopilan los recibos, el algoritmo de consenso calcula el hash en todos los recibos e incluye el hash resultante en el encabezado del bloque. Después de eso, el hash del bloque se genera y se distribuye a otros nodos.

El proceso para crear un nuevo bloque está aquí ( core/types/block.go)

func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt) *Block {
    b := &Block{header: CopyHeader(header), td: new(big.Int)}

    // TODO: panic if len(txs) != len(receipts)
    if len(txs) == 0 {
        b.header.TxHash = EmptyRootHash
    } else {
        b.header.TxHash = DeriveSha(Transactions(txs))
        b.transactions = make(Transactions, len(txs))
        copy(b.transactions, txs)
    }

    if len(receipts) == 0 {
        b.header.ReceiptHash = EmptyRootHash
    } else {
        b.header.ReceiptHash = DeriveSha(Receipts(receipts))
        b.header.Bloom = CreateBloom(receipts)
    }

    if len(uncles) == 0 {
        b.header.UncleHash = EmptyUncleHash
    } else {
        b.header.UncleHash = CalcUncleHash(uncles)
        b.uncles = make([]*Header, len(uncles))
        for i := range uncles {
            b.uncles[i] = CopyHeader(uncles[i])
        }
    }

    return b
}

Esta es la línea que calcula el hash en los recibos:

b.header.ReceiptHash = DeriveSha(Receipts(receipts))

Los eventos se almacenan como parte de la Receiptestructura:

// Receipt represents the results of a transaction.
type Receipt struct {
    // Consensus fields
    PostState         []byte `json:"root"`
    Status            uint64 `json:"status"`
    CumulativeGasUsed uint64 `json:"cumulativeGasUsed" gencodec:"required"`
    Bloom             Bloom  `json:"logsBloom"         gencodec:"required"`
    Logs              []*Log `json:"logs"              gencodec:"required"`

    // Implementation fields (don't reorder!)
    TxHash          common.Hash    `json:"transactionHash" gencodec:"required"`
    ContractAddress common.Address `json:"contractAddress"`
    GasUsed         uint64         `json:"gasUsed" gencodec:"required"`
}

esta es la matriz que contiene los eventos:

    Logs              []*Log `json:"logs"              gencodec:"required"`

Y el evento se define como:

// Log represents a contract log event. These events are generated by the LOG opcode and
// stored/indexed by the node.
type Log struct {
    // Consensus fields:
    // address of the contract that generated the event
    Address common.Address `json:"address" gencodec:"required"`
    // list of topics provided by the contract.
    Topics []common.Hash `json:"topics" gencodec:"required"`
    // supplied by the contract, usually ABI-encoded
    Data []byte `json:"data" gencodec:"required"`

    // Derived fields. These fields are filled in by the node
    // but not secured by consensus.
    // block in which the transaction was included
    BlockNumber uint64 `json:"blockNumber"`
    // hash of the transaction
    TxHash common.Hash `json:"transactionHash" gencodec:"required"`
    // index of the transaction in the block
    TxIndex uint `json:"transactionIndex" gencodec:"required"`
    // hash of the block in which the transaction was included
    BlockHash common.Hash `json:"blockHash"`
    // index of the log in the receipt
    Index uint `json:"logIndex" gencodec:"required"`

    // The Removed field is true if this log was reverted due to a chain reorganisation.
    // You must pay attention to this field if you receive logs through a filter query.
    Removed bool `json:"removed"`
}

Todos estos datos se tored en el --datadir``/geth/chaindatadirectorio

Puede confiar en los registros de eventos porque el hash de todos los recibos se incluye en el hash del Bloque, por lo que los datos están "sellados" criptográficamente.

En caso de que quieras validar los Tickets los obtienes con esta función:

receipts:=core.GetBlockReceipts(ethereum.ChainDb(), block.Hash(), block.NumberU64())`

Y recalcular el hash de nuevo. Debe coincidir con el ReceiptsHashdel encabezado del bloque.

Espero que ahora quede claro cómo funcionan los eventos internamente.

Gracias por la buena explicación. Todo claro. Te haría una pregunta más si no te importa responder aquí. ¿De acuerdo? La cosa es que lo que hay que hacer es sobre big data. como saben, los contratos inteligentes no son para big data, debido al aumento del precio del gas. Entonces, una solución podría ser almacenar datos en IPFS o registros. Como se sabe, es mucho mejor almacenarlo en registros, porque todavía está en blockchain y eso es lo que le gusta a la gente. La pregunta es: cuando almaceno muchos datos allí, uno de los campos debe estar oculto y solo yo puedo obtener eso. nadie debería ir a blockchain y ver los registros. ¿Qué opinas?
El concepto de la tecnología blockchain es que los datos sean públicos y accesibles para cualquier persona. Por lo tanto, la única forma de almacenar datos privados en la cadena de bloques es cifrarlos. Sin embargo, los algoritmos de cifrado se rompen constantemente debido a errores y hardware más avanzados, por lo que existe el riesgo de que sus datos se vuelvan públicos en el futuro. Puede almacenar sus datos privados en su propio centro de datos y publicar el hash de estos datos en blockhcain, esta es una forma de demostrar que no los alteró, mientras los almacena de forma privada.