Привет, это было написано много раз, но, как я вижу, ни один из ответов не может быть единственным. Меня интересует следующая вещь. Если в смарт-контракте я сделал событие, где оно будет храниться? в блокчейне, если да, то где? если не там, то в узлах? Я создаю серьезное приложение, и мне нужно хранить там много вещей. так что у меня есть, наконец, 2 вопроса.
1) где именно хранятся логи? 2) если в заголовке блока, можете ли вы описать, как я могу доверять этим журналам, попадающим в веб-интерфейс web3.js?
Вы можете доверять журналам, потому что они хранятся в соответствующем блоке. Если журнал будет изменен, он изменит хэш блока, квитанцию и т. Д., Так же, как и в случае с транзакциями. Дерево Меркле уже не будет прежним.
Поэтому, если я выполняю функцию на определенной высоте блока, квитанция и, следовательно, журнал также будут частью блока на этой высоте, как и моя транзакция.
Ответить, где именно они хранятся. В квитанции о транзакциях попробуйте, если я правильно помню.
изображение предоставлено https://medium.com/@preethikasireddy/how-does-ethereum-work-anyway-22d1df506369
Когда контракт генерирует событие, оно сохраняется в структуре 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
}
Это член, который содержит данные в приведенной выше структуре:
logs map[common.Hash][]*types.Log
Во время обработки блока каждая транзакция возвращает квитанцию (с журналами событий), и они сохраняются в массиве (от 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...)
}
После того, как квитанции собраны, алгоритм консенсуса вычисляет хэш для всех квитанций и включает полученный хэш в заголовок блока. После этого хэш блока генерируется и распределяется по другим узлам.
Процесс создания нового блока здесь ( 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
}
Это строка, которая вычисляет хэш на квитанциях:
b.header.ReceiptHash = DeriveSha(Receipts(receipts))
События хранятся как часть 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"`
}
это массив, содержащий события:
Logs []*Log `json:"logs" gencodec:"required"`
И событие определяется как:
// 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"`
}
Все эти данные разорваны в --datadir``/geth/chaindata
каталоге
Вы можете доверять журналам событий, потому что хэш всех квитанций включен в хэш блока, поэтому данные криптографически «запечатаны».
Если вы хотите проверить квитанции, вы можете получить их с помощью этой функции:
receipts:=core.GetBlockReceipts(ethereum.ChainDb(), block.Hash(), block.NumberU64())`
И снова пересчитать хэш. Он должен соответствовать ReceiptsHash
заголовку блока.
Я надеюсь, что теперь понятно, как события работают внутри.
Георгий Лагидзе
Нико Вергаувен