Skip to content

Instantly share code, notes, and snippets.

@mikers1
Last active August 1, 2017 16:50
Show Gist options
  • Save mikers1/cd4df87f8d811ef93f2f44bb2e2d1673 to your computer and use it in GitHub Desktop.
Save mikers1/cd4df87f8d811ef93f2f44bb2e2d1673 to your computer and use it in GitHub Desktop.
/** @file BlockChain.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include "Account.h"
#include "BlockDetails.h"
#include "BlockQueue.h"
#include "ChainParams.h"
#include "LastBlockHashesFace.h"
#include "State.h"
#include "Transaction.h"
#include "VerifiedBlock.h"
#include <libdevcore/db.h>
#include <libdevcore/Exceptions.h>
#include <libdevcore/Log.h>
#include <libdevcore/Guards.h>
#include <libethcore/BlockHeader.h>
#include <libethcore/Common.h>
#include <libethcore/SealEngine.h>
#include <libevm/ExtVMFace.h>
#include <chrono>
#include <deque>
#include <unordered_map>
#include <unordered_set>
namespace std
{
template <> struct hash<pair<dev::h256, unsigned>>
{
size_t operator()(pair<dev::h256, unsigned> const& _x) const { return hash<dev::h256>()(_x.first) ^ hash<unsigned>()(_x.second); }
};
}
namespace dev
{
class OverlayDB;
namespace eth
{
static const h256s NullH256s;
class State;
class Block;
DEV_SIMPLE_EXCEPTION(AlreadyHaveBlock);
DEV_SIMPLE_EXCEPTION(FutureTime);
DEV_SIMPLE_EXCEPTION(TransientError);
struct BlockChainChat: public LogChannel { static const char* name(); static const int verbosity = 5; };
struct BlockChainNote: public LogChannel { static const char* name(); static const int verbosity = 3; };
struct BlockChainWarn: public LogChannel { static const char* name(); static const int verbosity = 1; };
struct BlockChainDebug: public LogChannel { static const char* name(); static const int verbosity = 0; };
// TODO: Move all this Genesis stuff into Genesis.h/.cpp
std::unordered_map<Address, Account> const& genesisState();
ldb::Slice toSlice(h256 const& _h, unsigned _sub = 0);
ldb::Slice toSlice(uint64_t _n, unsigned _sub = 0);
using BlocksHash = std::unordered_map<h256, bytes>;
using TransactionHashes = h256s;
using UncleHashes = h256s;
enum {
ExtraDetails = 0,
ExtraBlockHash,
ExtraTransactionAddress,
ExtraLogBlooms,
ExtraReceipts,
ExtraBlocksBlooms
};
using ProgressCallback = std::function<void(unsigned, unsigned)>;
class VersionChecker
{
public:
VersionChecker(std::string const& _dbPath, h256 const& _genesisHash);
};
/**
* @brief Implements the blockchain database. All data this gives is disk-backed.
* @threadsafe
*/
class BlockChain
{
public:
/// Doesn't open the database - if you want it open it's up to you to subclass this and open it
/// in the constructor there.
BlockChain(ChainParams const& _p, std::string const& _path, WithExisting _we = WithExisting::Trust, ProgressCallback const& _pc = ProgressCallback());
~BlockChain();
/// Reopen everything.
void reopen(WithExisting _we = WithExisting::Trust, ProgressCallback const& _pc = ProgressCallback()) { reopen(m_params, _we, _pc); }
void reopen(ChainParams const& _p, WithExisting _we = WithExisting::Trust, ProgressCallback const& _pc = ProgressCallback());
/// (Potentially) renders invalid existing bytesConstRef returned by lastBlock.
/// To be called from main loop every 100ms or so.
void process();
/// Sync the chain with any incoming blocks. All blocks should, if processed in order.
/// @returns fresh blocks, dead blocks and true iff there are additional blocks to be processed waiting.
std::tuple<ImportRoute, bool, unsigned> sync(BlockQueue& _bq, OverlayDB const& _stateDB, unsigned _max);
/// Attempt to import the given block directly into the BlockChain and sync with the state DB.
/// @returns the block hashes of any blocks that came into/went out of the canonical block chain.
std::pair<ImportResult, ImportRoute> attemptImport(bytes const& _block, OverlayDB const& _stateDB, bool _mutBeNew = true) noexcept;
/// Import block into disk-backed DB.
/// @returns the block hashes of any blocks that came into/went out of the canonical block chain.
ImportRoute import(bytes const& _block, OverlayDB const& _stateDB, bool _mustBeNew = true);
ImportRoute import(VerifiedBlockRef const& _block, OverlayDB const& _db, bool _mustBeNew = true);
/// Import data into disk-backed DB.
/// This will not execute the block and populate the state trie, but rather will simply add the
/// block/header and receipts directly into the databases.
void insert(bytes const& _block, bytesConstRef _receipts, bool _mustBeNew = true);
void insert(VerifiedBlockRef _block, bytesConstRef _receipts, bool _mustBeNew = true);
/// Returns true if the given block is known (though not necessarily a part of the canon chain).
bool isKnown(h256 const& _hash, bool _isCurrent = true) const;
/// Get the partial-header of a block (or the most recent mined if none given). Thread-safe.
BlockHeader info(h256 const& _hash) const { return BlockHeader(headerData(_hash), HeaderData); }
BlockHeader info() const { return info(currentHash()); }
/// Get a block (RLP format) for the given hash (or the most recent mined if none given). Thread-safe.
bytes block(h256 const& _hash) const;
bytes block() const { return block(currentHash()); }
/// Get a block (RLP format) for the given hash (or the most recent mined if none given). Thread-safe.
bytes headerData(h256 const& _hash) const;
bytes headerData() const { return headerData(currentHash()); }
/// Get the familial details concerning a block (or the most recent mined if none given). Thread-safe.
BlockDetails details(h256 const& _hash) const { return queryExtras<BlockDetails, ExtraDetails>(_hash, m_details, x_details, NullBlockDetails); }
BlockDetails details() const { return details(currentHash()); }
/// Get the transactions' log blooms of a block (or the most recent mined if none given). Thread-safe.
BlockLogBlooms logBlooms(h256 const& _hash) const { return queryExtras<BlockLogBlooms, ExtraLogBlooms>(_hash, m_logBlooms, x_logBlooms, NullBlockLogBlooms); }
BlockLogBlooms logBlooms() const { return logBlooms(currentHash()); }
/// Get the transactions' receipts of a block (or the most recent mined if none given). Thread-safe.
/// receipts are given in the same order are in the same order as the transactions
BlockReceipts receipts(h256 const& _hash) const { return queryExtras<BlockReceipts, ExtraReceipts>(_hash, m_receipts, x_receipts, NullBlockReceipts); }
BlockReceipts receipts() const { return receipts(currentHash()); }
/// Get the transaction by block hash and index;
TransactionReceipt transactionReceipt(h256 const& _blockHash, unsigned _i) const { return receipts(_blockHash).receipts[_i]; }
/// Get the transaction receipt by transaction hash. Thread-safe.
TransactionReceipt transactionReceipt(h256 const& _transactionHash) const { TransactionAddress ta = queryExtras<TransactionAddress, ExtraTransactionAddress>(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return bytesConstRef(); return transactionReceipt(ta.blockHash, ta.index); }
/// Get a list of transaction hashes for a given block. Thread-safe.
TransactionHashes transactionHashes(h256 const& _hash) const { auto b = block(_hash); RLP rlp(b); h256s ret; for (auto t: rlp[1]) ret.push_back(sha3(t.data())); return ret; }
TransactionHashes transactionHashes() const { return transactionHashes(currentHash()); }
/// Get a list of uncle hashes for a given block. Thread-safe.
UncleHashes uncleHashes(h256 const& _hash) const { auto b = block(_hash); RLP rlp(b); h256s ret; for (auto t: rlp[2]) ret.push_back(sha3(t.data())); return ret; }
UncleHashes uncleHashes() const { return uncleHashes(currentHash()); }
/// Get the hash for a given block's number.
h256 numberHash(unsigned _i) const { if (!_i) return genesisHash(); return queryExtras<BlockHash, uint64_t, ExtraBlockHash>(_i, m_blockHashes, x_blockHashes, NullBlockHash).value; }
LastBlockHashesFace const& lastBlockHashes() const { return *m_lastBlockHashes; }
/** Get the block blooms for a number of blocks. Thread-safe.
* @returns the object pertaining to the blocks:
* level 0:
* 0x, 0x + 1, .. (1x - 1)
* 1x, 1x + 1, .. (2x - 1)
* ...
* (255x .. (256x - 1))
* level 1:
* 0x .. (1x - 1), 1x .. (2x - 1), ..., (255x .. (256x - 1))
* 256x .. (257x - 1), 257x .. (258x - 1), ..., (511x .. (512x - 1))
* ...
* level n, index i, offset o:
* i * (x ^ n) + o * x ^ (n - 1)
*/
BlocksBlooms blocksBlooms(unsigned _level, unsigned _index) const { return blocksBlooms(chunkId(_level, _index)); }
BlocksBlooms blocksBlooms(h256 const& _chunkId) const { return queryExtras<BlocksBlooms, ExtraBlocksBlooms>(_chunkId, m_blocksBlooms, x_blocksBlooms, NullBlocksBlooms); }
LogBloom blockBloom(unsigned _number) const { return blocksBlooms(chunkId(0, _number / c_bloomIndexSize)).blooms[_number % c_bloomIndexSize]; }
std::vector<unsigned> withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest) const;
std::vector<unsigned> withBlockBloom(LogBloom const& _b, unsigned _earliest, unsigned _latest, unsigned _topLevel, unsigned _index) const;
/// Returns true if transaction is known. Thread-safe
bool isKnownTransaction(h256 const& _transactionHash) const { TransactionAddress ta = queryExtras<TransactionAddress, ExtraTransactionAddress>(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); return !!ta; }
/// Get a transaction from its hash. Thread-safe.
bytes transaction(h256 const& _transactionHash) const { TransactionAddress ta = queryExtras<TransactionAddress, ExtraTransactionAddress>(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return bytes(); return transaction(ta.blockHash, ta.index); }
std::pair<h256, unsigned> transactionLocation(h256 const& _transactionHash) const { TransactionAddress ta = queryExtras<TransactionAddress, ExtraTransactionAddress>(_transactionHash, m_transactionAddresses, x_transactionAddresses, NullTransactionAddress); if (!ta) return std::pair<h256, unsigned>(h256(), 0); return std::make_pair(ta.blockHash, ta.index); }
/// Get a block's transaction (RLP format) for the given block hash (or the most recent mined if none given) & index. Thread-safe.
bytes transaction(h256 const& _blockHash, unsigned _i) const { bytes b = block(_blockHash); return RLP(b)[1][_i].data().toBytes(); }
bytes transaction(unsigned _i) const { return transaction(currentHash(), _i); }
/// Get all transactions from a block.
std::vector<bytes> transactions(h256 const& _blockHash) const { bytes b = block(_blockHash); std::vector<bytes> ret; for (auto const& i: RLP(b)[1]) ret.push_back(i.data().toBytes()); return ret; }
std::vector<bytes> transactions() const { return transactions(currentHash()); }
/// Get a number for the given hash (or the most recent mined if none given). Thread-safe.
unsigned number(h256 const& _hash) const { return details(_hash).number; }
unsigned number() const { return m_lastBlockNumber; }
/// Get a given block (RLP format). Thread-safe.
h256 currentHash() const { ReadGuard l(x_lastBlockHash); return m_lastBlockHash; }
/// Get the hash of the genesis block. Thread-safe.
h256 genesisHash() const { return m_genesisHash; }
/// Get all blocks not allowed as uncles given a parent (i.e. featured as uncles/main in parent, parent + 1, ... parent + @a _generations).
/// @returns set including the header-hash of every parent (including @a _parent) up to and including generation + @a _generations
/// togther with all their quoted uncles.
h256Hash allKinFrom(h256 const& _parent, unsigned _generations) const;
/// Run through database and verify all blocks by reevaluating.
/// Will call _progress with the progress in this operation first param done, second total.
void rebuild(std::string const& _path, ProgressCallback const& _progress = std::function<void(unsigned, unsigned)>());
/// Alter the head of the chain to some prior block along it.
void rewind(unsigned _newHead);
//snip - some functions deleted for brevity
struct Statistics
{
unsigned memBlocks;
unsigned memDetails;
unsigned memLogBlooms;
unsigned memReceipts;
unsigned memTransactionAddresses;
unsigned memBlockHashes;
unsigned memTotal() const { return memBlocks + memDetails + memLogBlooms + memReceipts + memTransactionAddresses + memBlockHashes; }
};
private:
static h256 chunkId(unsigned _level, unsigned _index) { return h256(_index * 0xff + _level); }
/// Initialise everything and ready for openning the database.
void init(ChainParams const& _p);
/// Open the database.
unsigned open(std::string const& _path, WithExisting _we);
/// Open the database, rebuilding if necessary.
void open(std::string const& _path, WithExisting _we, ProgressCallback const& _pc);
/// Finalise everything and close the database.
void close();
//snip a bunch more
/// The disk DBs. Thread-safe, so no need for locks.
ldb::DB* m_blocksDB;
ldb::DB* m_extrasDB;
/// Hash of the last (valid) block on the longest chain.
mutable boost::shared_mutex x_lastBlockHash;
h256 m_lastBlockHash;
unsigned m_lastBlockNumber = 0;
ldb::ReadOptions m_readOptions;
ldb::WriteOptions m_writeOptions;
ChainParams m_params;
std::shared_ptr<SealEngineFace> m_sealEngine; // consider shared_ptr.
mutable SharedMutex x_genesis;
mutable BlockHeader m_genesis; // mutable because they're effectively memos.
mutable bytes m_genesisHeaderBytes; // mutable because they're effectively memos.
mutable h256 m_genesisHash; // mutable because they're effectively memos.
std::function<void(Exception&)> m_onBad; ///< Called if we have a block that doesn't verify.
std::function<void(BlockHeader const&)> m_onBlockImport; ///< Called if we have imported a new block into the db
std::string m_dbPath;
friend std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc);
};
std::ostream& operator<<(std::ostream& _out, BlockChain const& _bc);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment