// Copyright 2018 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // The go-ethereum library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . package rawdb import ( "bytes" "encoding/binary" "errors" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/params" "github.com/XinFinOrg/XDPoSChain/rlp" ) // ReadHeaderNumber returns the header number assigned to a hash. func ReadHeaderNumber(db ethdb.KeyValueReader, hash common.Hash) *uint64 { data, _ := db.Get(headerNumberKey(hash)) if len(data) != 8 { return nil } number := binary.BigEndian.Uint64(data) return &number } // ReadBodyRLP retrieves the block body (transactions and uncles) in RLP encoding. func ReadBodyRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue { // First try to look up the data in ancient database. Extra hash // comparison is necessary since ancient database only maintains // the canonical data. data, _ := db.Ancient(freezerBodiesTable, number) if len(data) > 0 { h, _ := db.Ancient(freezerHashTable, number) if common.BytesToHash(h) == hash { return data } } // Then try to look up the data in leveldb. data, _ = db.Get(blockBodyKey(number, hash)) if len(data) > 0 { return data } // In the background freezer is moving data from leveldb to flatten files. // So during the first check for ancient db, the data is not yet in there, // but when we reach into leveldb, the data was already moved. That would // result in a not found error. data, _ = db.Ancient(freezerBodiesTable, number) if len(data) > 0 { h, _ := db.Ancient(freezerHashTable, number) if common.BytesToHash(h) == hash { return data } } return nil // Can't find the data anywhere. } // WriteBodyRLP stores an RLP encoded block body into the database. func WriteBodyRLP(db ethdb.KeyValueWriter, hash common.Hash, number uint64, rlp rlp.RawValue) { if err := db.Put(blockBodyKey(number, hash), rlp); err != nil { log.Crit("Failed to store block body", "err", err) } } // ReadBody retrieves the block body corresponding to the hash. func ReadBody(db ethdb.Reader, hash common.Hash, number uint64) *types.Body { data := ReadBodyRLP(db, hash, number) if len(data) == 0 { return nil } body := new(types.Body) if err := rlp.Decode(bytes.NewReader(data), body); err != nil { log.Error("Invalid block body RLP", "hash", hash, "err", err) return nil } return body } // WriteBody stores a block body into the database. func WriteBody(db ethdb.KeyValueWriter, hash common.Hash, number uint64, body *types.Body) { data, err := rlp.EncodeToBytes(body) if err != nil { log.Crit("Failed to RLP encode body", "err", err) } WriteBodyRLP(db, hash, number, data) } // ReadReceiptsRLP retrieves all the transaction receipts belonging to a block in RLP encoding. func ReadReceiptsRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue { // First try to look up the data in ancient database. Extra hash // comparison is necessary since ancient database only maintains // the canonical data. data, _ := db.Ancient(freezerReceiptTable, number) if len(data) > 0 { h, _ := db.Ancient(freezerHashTable, number) if common.BytesToHash(h) == hash { return data } } // Then try to look up the data in leveldb. data, _ = db.Get(blockReceiptsKey(number, hash)) if len(data) > 0 { return data } // In the background freezer is moving data from leveldb to flatten files. // So during the first check for ancient db, the data is not yet in there, // but when we reach into leveldb, the data was already moved. That would // result in a not found error. data, _ = db.Ancient(freezerReceiptTable, number) if len(data) > 0 { h, _ := db.Ancient(freezerHashTable, number) if common.BytesToHash(h) == hash { return data } } return nil // Can't find the data anywhere. } // ReadRawReceipts retrieves all the transaction receipts belonging to a block. // The receipt metadata fields are not guaranteed to be populated, so they // should not be used. Use ReadReceipts instead if the metadata is needed. func ReadRawReceipts(db ethdb.Reader, hash common.Hash, number uint64) types.Receipts { // Retrieve the flattened receipt slice data := ReadReceiptsRLP(db, hash, number) if len(data) == 0 { return nil } // Convert the receipts from their storage form to their internal representation storageReceipts := []*types.ReceiptForStorage{} if err := rlp.DecodeBytes(data, &storageReceipts); err != nil { log.Error("Invalid receipt array RLP", "hash", hash, "err", err) return nil } receipts := make(types.Receipts, len(storageReceipts)) for i, storageReceipt := range storageReceipts { receipts[i] = (*types.Receipt)(storageReceipt) } return receipts } // ReadReceipts retrieves all the transaction receipts belonging to a block, including // its correspoinding metadata fields. If it is unable to populate these metadata // fields then nil is returned. // // The current implementation populates these metadata fields by reading the receipts' // corresponding block body, so if the block body is not found it will return nil even // if the receipt itself is stored. func ReadReceipts(db ethdb.Reader, hash common.Hash, number uint64, config *params.ChainConfig) types.Receipts { // We're deriving many fields from the block body, retrieve beside the receipt receipts := ReadRawReceipts(db, hash, number) if receipts == nil { return nil } body := ReadBody(db, hash, number) if body == nil { log.Error("Missing body but have receipt", "hash", hash, "number", number) return nil } if err := receipts.DeriveFields(config, hash, number, body.Transactions); err != nil { log.Error("Failed to derive block receipts fields", "hash", hash, "number", number, "err", err) return nil } return receipts } // WriteReceipts stores all the transaction receipts belonging to a block. func WriteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64, receipts types.Receipts) { // Convert the receipts into their storage form and serialize them storageReceipts := make([]*types.ReceiptForStorage, len(receipts)) for i, receipt := range receipts { storageReceipts[i] = (*types.ReceiptForStorage)(receipt) } bytes, err := rlp.EncodeToBytes(storageReceipts) if err != nil { log.Crit("Failed to encode block receipts", "err", err) } // Store the flattened receipt slice if err := db.Put(blockReceiptsKey(number, hash), bytes); err != nil { log.Crit("Failed to store block receipts", "err", err) } } // storedReceiptRLP is the storage encoding of a receipt. // Re-definition in core/types/receipt.go. type storedReceiptRLP struct { PostStateOrStatus []byte CumulativeGasUsed uint64 Bloom types.Bloom TxHash common.Hash ContractAddress common.Address Logs []*types.LogForStorage GasUsed uint64 } // ReceiptLogs is a barebone version of ReceiptForStorage which only keeps // the list of logs. When decoding a stored receipt into this object we // avoid creating the bloom filter. type receiptLogs struct { Logs []*types.Log } // DecodeRLP implements rlp.Decoder. func (r *receiptLogs) DecodeRLP(s *rlp.Stream) error { var stored storedReceiptRLP if err := s.Decode(&stored); err != nil { return err } r.Logs = make([]*types.Log, len(stored.Logs)) for i, log := range stored.Logs { r.Logs[i] = (*types.Log)(log) } return nil } // DeriveLogFields fills the logs in receiptLogs with information such as block number, txhash, etc. func deriveLogFields(receipts []*receiptLogs, hash common.Hash, number uint64, txs types.Transactions) error { logIndex := uint(0) if len(txs) != len(receipts) { return errors.New("transaction and receipt count mismatch") } for i := 0; i < len(receipts); i++ { txHash := txs[i].Hash() // The derived log fields can simply be set from the block and transaction for j := 0; j < len(receipts[i].Logs); j++ { receipts[i].Logs[j].BlockNumber = number receipts[i].Logs[j].BlockHash = hash receipts[i].Logs[j].TxHash = txHash receipts[i].Logs[j].TxIndex = uint(i) receipts[i].Logs[j].Index = logIndex logIndex++ } } return nil } // ReadLogs retrieves the logs for all transactions in a block. The log fields // are populated with metadata. In case the receipts or the block body // are not found, a nil is returned. func ReadLogs(db ethdb.Reader, hash common.Hash, number uint64) [][]*types.Log { // Retrieve the flattened receipt slice data := ReadReceiptsRLP(db, hash, number) if len(data) == 0 { return nil } receipts := []*receiptLogs{} if err := rlp.DecodeBytes(data, &receipts); err != nil { log.Error("Invalid receipt array RLP", "hash", hash, "err", err) return nil } body := ReadBody(db, hash, number) if body == nil { log.Error("Missing body but have receipt", "hash", hash, "number", number) return nil } if err := deriveLogFields(receipts, hash, number, body.Transactions); err != nil { log.Error("Failed to derive block receipts fields", "hash", hash, "number", number, "err", err) return nil } logs := make([][]*types.Log, len(receipts)) for i, receipt := range receipts { logs[i] = receipt.Logs } return logs }