Hallo, es wurde schon oft geschrieben, aber wie ich beobachte, könnte keine der Antworten die richtige sein. Mich interessiert folgende Sache. Wenn ich das Ereignis in einem Smart Contract gemacht habe, wo wird es gespeichert? in Blockchain, wenn ja, wo genau? wenn nicht dort, vielleicht in Nodes? Ich baue eine seriöse App und muss dort viele Dinge speichern. also ich habe zum schluss noch 2 fragen.
1) Wo werden Protokolle genau gespeichert? 2) Wenn im Block-Header, können Sie beschreiben, wie ich diesen Protokollen vertrauen kann, die in das Front-End von web3.js gelangen?
Sie können den Protokollen vertrauen, da sie in ihrem jeweiligen Block gespeichert werden. Wenn ein Protokoll geändert wird, ändert es den Blockhash, die ReceiveRoot usw., genau wie bei Transaktionen. Der Merkle-Baum wäre nicht mehr derselbe.
Wenn ich also eine Funktion auf einer bestimmten Blockhöhe ausführe, ist die Quittung und damit das Protokoll auch Teil des Blocks auf dieser Höhe sowie meine Transaktion.
Um zu beantworten, wo sie genau gespeichert sind. In der Transaktionsquittung versuchen, wenn ich mich richtig erinnere.
Bildnachweis von https://medium.com/@preethikasireddy/how-does-ethereum-work-anyway-22d1df506369
Wenn ein Vertrag ein Ereignis ausgibt, wird es in der StateDB-Struktur gespeichert:
// 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
}
Dies ist das Mitglied, das die Daten in der obigen Struktur enthält:
logs map[common.Hash][]*types.Log
Während der Blockverarbeitung gibt jede Transaktion eine Quittung (mit Ereignisprotokollen) zurück und sie werden in einem Array gespeichert (von 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...)
}
Sobald die Quittungen gesammelt wurden, berechnet der Konsensalgorithmus den Hash für alle Quittungen und fügt den resultierenden Hash in den Header des Blocks ein. Danach wird der Hash des Blocks generiert und an andere Knoten verteilt.
Der Vorgang zum Erstellen eines neuen Blocks ist hier ( 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
}
Dies ist die Zeile, die den Hash auf den Quittungen berechnet:
b.header.ReceiptHash = DeriveSha(Receipts(receipts))
Ereignisse werden als Teil der Struktur gespeichert Receipt
:
// 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"`
}
Dies ist das Array mit den Ereignissen:
Logs []*Log `json:"logs" gencodec:"required"`
Und das Ereignis ist definiert als:
// 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"`
}
Alle diese Daten werden im --datadir``/geth/chaindata
Verzeichnis gespeichert
Sie können den Ereignisprotokollen vertrauen, da der Hash aller Quittungen im Hash des Blocks enthalten ist, sodass die Daten kryptografisch „versiegelt“ sind.
Falls Sie die Quittungen validieren möchten, erhalten Sie sie mit dieser Funktion:
receipts:=core.GetBlockReceipts(ethereum.ChainDb(), block.Hash(), block.NumberU64())`
Und den Hash erneut berechnen. Er muss mit dem ReceiptsHash
des Blockheaders übereinstimmen.
Ich hoffe, es ist jetzt klar, wie Events intern funktionieren.
Giorgi Lagidze
Nico Vergauwen