Где, наконец, хранятся журналы событий ethereum?

Привет, это было написано много раз, но, как я вижу, ни один из ответов не может быть единственным. Меня интересует следующая вещь. Если в смарт-контракте я сделал событие, где оно будет храниться? в блокчейне, если да, то где? если не там, то в узлах? Я создаю серьезное приложение, и мне нужно хранить там много вещей. так что у меня есть, наконец, 2 вопроса.

1) где именно хранятся логи? 2) если в заголовке блока, можете ли вы описать, как я могу доверять этим журналам, попадающим в веб-интерфейс web3.js?

Ответы (2)

Вы можете доверять журналам, потому что они хранятся в соответствующем блоке. Если журнал будет изменен, он изменит хэш блока, квитанцию ​​и т. Д., Так же, как и в случае с транзакциями. Дерево Меркле уже не будет прежним.

Поэтому, если я выполняю функцию на определенной высоте блока, квитанция и, следовательно, журнал также будут частью блока на этой высоте, как и моя транзакция.

Ответить, где именно они хранятся. В квитанции о транзакциях попробуйте, если я правильно помню.

изображение предоставлено https://medium.com/@preethikasireddy/how-does-ethereum-work-anyway-22d1df506369

Заголовок блока

Спасибо за хорошее объяснение. Все чисто. Я хотел бы задать вам еще один вопрос, если вы не против ответить здесь. Хорошо? Дело в том, что нужно делать с большими данными. как вы знаете, смарт-контракты не для больших данных из-за повышения цен на газ. Таким образом, одним из решений может быть хранение данных в IPFS или журналах. Как известно, гораздо лучше хранить его в журналах, потому что он все еще находится в блокчейне, и это то, что нравится людям. Вопрос: когда я храню там много данных, одно из полей должно быть скрыто, и только я мог это получить. никто не должен заходить в блокчейн и смотреть логи. Что вы думаете?
Вы можете зашифровать поле своим открытым ключом, чтобы только ваш закрытый ключ мог его расшифровать. Проблема в том, что если алгоритм шифрования когда-нибудь столкнется и, таким образом, станет возможным угадать закрытые ключи, потому что мы не можем удалить данные из такой системы, как ethereum/ipfs/swarm. Я бы посоветовал использовать Swarm (bzz в эфириуме) вместо IPFS, так как он более тесно связан с экосистемой. Но это только личные предпочтения после использования обоих.

Когда контракт генерирует событие, оно сохраняется в структуре 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заголовку блока.

Я надеюсь, что теперь понятно, как события работают внутри.

Спасибо за хорошее объяснение. Все чисто. Я хотел бы задать вам еще один вопрос, если вы не против ответить здесь. Хорошо? Дело в том, что нужно делать с большими данными. как вы знаете, смарт-контракты не для больших данных из-за повышения цен на газ. Таким образом, одним из решений может быть хранение данных в IPFS или журналах. Как известно, гораздо лучше хранить его в журналах, потому что он все еще находится в блокчейне, и это то, что нравится людям. Вопрос: когда я храню там много данных, одно из полей должно быть скрыто, и только я мог это получить. никто не должен заходить в блокчейн и смотреть логи. Что вы думаете?
Концепция технологии блокчейна заключается в том, чтобы данные были общедоступными и доступными для всех. Таким образом, единственный способ хранить приватные данные в блокчейне — это зашифровать их. Однако алгоритмы шифрования постоянно ломаются из-за более совершенного оборудования и ошибок, поэтому есть риск, что ваши данные могут стать общедоступными в будущем. Вы можете хранить свои личные данные в своем собственном центре обработки данных и публиковать хэш этих данных в блокчейне, это один из способов доказать, что вы не изменяли их, сохраняя их в частном порядке.