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?
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
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 Receipt
estructura:
// 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/chaindata
directorio
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 ReceiptsHash
del encabezado del bloque.
Espero que ahora quede claro cómo funcionan los eventos internamente.
Giorgi Lagidze
Nico Vergauwen