Skip to content
This repository has been archived by the owner on Oct 28, 2021. It is now read-only.

Populate legacy block's state from database instead of executing transactions #5792

Merged
merged 6 commits into from
Nov 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

- Added: [#5699](https://github.com/ethereum/aleth/pull/5699) EIP 2046: Reduced gas cost for static calls made to precompiles.
- Added: [#5752](https://github.com/ethereum/aleth/pull/5752) [#5753](https://github.com/ethereum/aleth/pull/5753) Implement EIP1380 (reduced gas costs for call-to-self).
- Removed: [#5760](https://github.com/ethereum/aleth/pull/5760) Official support for Visual Studio 2015 has been dropped. Compilation with this compiler is expected to stop working after migration to C++14.
- Removed: [#5760](https://github.com/ethereum/aleth/pull/5760) Official support for Visual Studio 2015 has been dropped. Compilation with this compiler is expected to stop working after migration to C++14.
- Fixed: [#5792](https://github.com/ethereum/aleth/pull/5792) Faster and cheaper execution of RPC functions which query blockchain state (e.g. getBalance).

## [1.7.0] - Unreleased

Expand Down
67 changes: 38 additions & 29 deletions libethereum/Block.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,47 +136,56 @@ void Block::noteChain(BlockChain const& _bc)
}
}

PopulationStatistics Block::populateFromChain(BlockChain const& _bc, h256 const& _h, ImportRequirements::value _ir)
void Block::populateFromChain(BlockChain const& _bc, h256 const& _h)
{
noteChain(_bc);

PopulationStatistics ret { 0.0, 0.0 };

if (!_bc.isKnown(_h))
{
// Might be worth throwing here.
cwarn << "Invalid block given for state population: " << _h;
BOOST_THROW_EXCEPTION(BlockNotFound() << errinfo_target(_h));
}

auto b = _bc.block(_h);
BlockHeader bi(b); // No need to check - it's already in the DB.
if (bi.number())
auto const& blockBytes = _bc.block(_h);

// Set block headers
auto const blockHeader = BlockHeader{blockBytes};
m_currentBlock = blockHeader;

if (blockHeader.number())
m_previousBlock = _bc.info(blockHeader.parentHash());
else
m_previousBlock = m_currentBlock;

// Set state root and precommit state
//
// First check for database corruption by looking up the state root in the state database. Note
// that we don't technically need to do this since if the state DB is corrupt setting a new
// state root will throw anyway, but checking here enables us to log a user-friendly error
// message.
if (m_state.db().lookup(blockHeader.stateRoot()).empty())
{
// Non-genesis:

// 1. Start at parent's end state (state root).
BlockHeader bip(_bc.block(bi.parentHash()));
sync(_bc, bi.parentHash(), bip);

// 2. Enact the block's transactions onto this state.
m_author = bi.author();
Timer t;
auto vb = _bc.verifyBlock(&b, function<void(Exception&)>(), _ir | ImportRequirements::TransactionBasic);
ret.verify = t.elapsed();
t.restart();
enact(vb, _bc);
ret.enact = t.elapsed();
cerr << "Unable to populate block " << blockHeader.hash() << " - state root "
<< blockHeader.stateRoot() << " not found in database.";
cerr << "Database corrupt: contains block without state root: " << blockHeader;
cerr << "Try rescuing the database by running: eth --rescue";
BOOST_THROW_EXCEPTION(InvalidStateRoot() << errinfo_target(blockHeader.stateRoot()));
}
else

m_state.setRoot(blockHeader.stateRoot());
m_precommit = m_state;

RLP blockRLP{blockBytes};
auto const& txListRLP = blockRLP[1];
for (auto const& txRLP : txListRLP)
{
// Genesis required:
// We know there are no transactions, so just populate directly.
m_state = State(m_state.accountStartNonce(), m_state.db(), BaseState::Empty); // TODO: try with PreExisting.
sync(_bc, _h, bi);
m_transactions.push_back(Transaction{txRLP.data(), CheckTransaction::None});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did it fill m_transactions, m_transactionsSet, m_currentTxs, m_currentUncles before?

I suspect these are also useful only for mining, and RPC methods get them separately from BlockChain

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question 😀

  • m_transactions & m_transactionsSet: Yes
  • m_currentTxs & m_currentUncles: No

So you raise a good question - can we assume that this function will only be called via RPC? I populated everything in the block because populateFromChain is a public function so it can be called by anyone...unless these fields are only usable in very specific situations (e.g. mining), perhaps we should fork the populateFromChain into two functions - 1 which populates the fields which are useful in most cases and another which populates the rest?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think ideally these different cases should be handled somehow by different classes.

But let's not complicate it here, I think I'm fine with leaving like this.
But m_currentTxs really looks to be specific to only commitToSeal and sealBlock methods, and I think it would be fine to not copy it here. m_currentUncles probably too.

m_transactionSet.insert(m_transactions.back().sha3());
}
m_receipts = _bc.receipts(_h).receipts;

return ret;
m_author = blockHeader.author();

m_committedToSeal = false;
m_sealEngine = _bc.sealEngine();
}

bool Block::sync(BlockChain const& _bc)
Expand Down
8 changes: 1 addition & 7 deletions libethereum/Block.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,6 @@ class TransactionQueue;
struct VerifiedBlockRef;
class LastBlockHashesFace;

struct PopulationStatistics
{
double verify;
double enact;
};

DEV_SIMPLE_EXCEPTION(ChainOperationWithUnknownBlockChain);
DEV_SIMPLE_EXCEPTION(InvalidOperationOnSealedBlock);

Expand Down Expand Up @@ -190,7 +184,7 @@ class Block
// State-change operations

/// Construct state object from arbitrary point in blockchain.
PopulationStatistics populateFromChain(BlockChain const& _bc, h256 const& _hash, ImportRequirements::value _ir = ImportRequirements::None);
void populateFromChain(BlockChain const& _bc, h256 const& _hash);

/// Execute a given transaction.
/// This will append @a _t to the transaction list and change the state accordingly.
Expand Down
26 changes: 4 additions & 22 deletions libethereum/Client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -759,37 +759,19 @@ void Client::prepareForTransaction()
startWorking();
}

Block Client::block(h256 const& _block) const
Block Client::block(h256 const& _blockHash) const
{
try
{
Block ret(bc(), m_stateDB);
ret.populateFromChain(bc(), _block);
return ret;
}
catch (Exception& ex)
{
ex << errinfo_block(bc().block(_block));
onBadBlock(ex);
return Block(bc());
}
}

Block Client::block(h256 const& _blockHash, PopulationStatistics* o_stats) const
{
try
{
Block ret(bc(), m_stateDB);
PopulationStatistics s = ret.populateFromChain(bc(), _blockHash);
if (o_stats)
swap(s, *o_stats);
Block ret{bc(), m_stateDB};
ret.populateFromChain(bc(), _blockHash);
return ret;
}
catch (Exception& ex)
{
ex << errinfo_block(bc().block(_blockHash));
onBadBlock(ex);
return Block(bc());
return Block{bc()};
}
}

Expand Down
4 changes: 0 additions & 4 deletions libethereum/Client.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,6 @@ class Client: public ClientBase, protected Worker
/// Get the gas bid price
u256 gasBidPrice() const override { return m_gp->bid(); }

// [PRIVATE API - only relevant for base clients, not available in general]
/// Get the block.
dev::eth::Block block(h256 const& _blockHash, PopulationStatistics* o_stats) const;

/// Get the object representing the current state of Ethereum.
dev::eth::Block postState() const { ReadGuard l(x_postSeal); return m_postSeal; }
/// Get the object representing the current canonical blockchain.
Expand Down
12 changes: 0 additions & 12 deletions libweb3jsonrpc/AdminEth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,18 +179,6 @@ h256 AdminEth::blockHash(string const& _blockNumberOrHash) const
}
}

Json::Value AdminEth::admin_eth_reprocess(string const& _blockNumberOrHash, string const& _session)
{
RPC_ADMIN;
Json::Value ret;
PopulationStatistics ps;
m_eth.block(blockHash(_blockNumberOrHash), &ps);
ret["enact"] = ps.enact;
ret["verify"] = ps.verify;
ret["total"] = ps.verify + ps.enact;
return ret;
}

Json::Value AdminEth::admin_eth_vmTrace(string const& _blockNumberOrHash, int _txIndex, string const& _session)
{
RPC_ADMIN;
Expand Down
1 change: 0 additions & 1 deletion libweb3jsonrpc/AdminEth.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ class AdminEth: public AdminEthFace
virtual Json::Value admin_eth_newAccount(const Json::Value& _info, std::string const& _session) override;
virtual bool admin_eth_setMiningBenefactor(std::string const& _uuidOrAddress, std::string const& _session) override;
virtual Json::Value admin_eth_inspect(std::string const& _address, std::string const& _session) override;
virtual Json::Value admin_eth_reprocess(std::string const& _blockNumberOrHash, std::string const& _session) override;
virtual Json::Value admin_eth_vmTrace(std::string const& _blockNumberOrHash, int _txIndex, std::string const& _session) override;
virtual Json::Value admin_eth_getReceiptByHashAndIndex(std::string const& _blockNumberOrHash, int _txIndex, std::string const& _session) override;
virtual bool miner_start(int _threads) override;
Expand Down
6 changes: 0 additions & 6 deletions libweb3jsonrpc/AdminEthFace.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ namespace dev {
this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_newAccount", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_OBJECT,"param2",jsonrpc::JSON_STRING, NULL), &dev::rpc::AdminEthFace::admin_eth_newAccountI);
this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_setMiningBenefactor", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &dev::rpc::AdminEthFace::admin_eth_setMiningBenefactorI);
this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_inspect", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &dev::rpc::AdminEthFace::admin_eth_inspectI);
this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_reprocess", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_STRING, NULL), &dev::rpc::AdminEthFace::admin_eth_reprocessI);
this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_vmTrace", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_INTEGER,"param3",jsonrpc::JSON_STRING, NULL), &dev::rpc::AdminEthFace::admin_eth_vmTraceI);
this->bindAndAddMethod(jsonrpc::Procedure("admin_eth_getReceiptByHashAndIndex", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_OBJECT, "param1",jsonrpc::JSON_STRING,"param2",jsonrpc::JSON_INTEGER,"param3",jsonrpc::JSON_STRING, NULL), &dev::rpc::AdminEthFace::admin_eth_getReceiptByHashAndIndexI);
this->bindAndAddMethod(jsonrpc::Procedure("miner_start", jsonrpc::PARAMS_BY_POSITION, jsonrpc::JSON_BOOLEAN, "param1",jsonrpc::JSON_INTEGER, NULL), &dev::rpc::AdminEthFace::miner_startI);
Expand Down Expand Up @@ -80,10 +79,6 @@ namespace dev {
{
response = this->admin_eth_inspect(request[0u].asString(), request[1u].asString());
}
inline virtual void admin_eth_reprocessI(const Json::Value &request, Json::Value &response)
{
response = this->admin_eth_reprocess(request[0u].asString(), request[1u].asString());
}
inline virtual void admin_eth_vmTraceI(const Json::Value &request, Json::Value &response)
{
response = this->admin_eth_vmTrace(request[0u].asString(), request[1u].asInt(), request[2u].asString());
Expand Down Expand Up @@ -129,7 +124,6 @@ namespace dev {
virtual Json::Value admin_eth_newAccount(const Json::Value& param1, const std::string& param2) = 0;
virtual bool admin_eth_setMiningBenefactor(const std::string& param1, const std::string& param2) = 0;
virtual Json::Value admin_eth_inspect(const std::string& param1, const std::string& param2) = 0;
virtual Json::Value admin_eth_reprocess(const std::string& param1, const std::string& param2) = 0;
virtual Json::Value admin_eth_vmTrace(const std::string& param1, int param2, const std::string& param3) = 0;
virtual Json::Value admin_eth_getReceiptByHashAndIndex(const std::string& param1, int param2, const std::string& param3) = 0;
virtual bool miner_start(int param1) = 0;
Expand Down
1 change: 0 additions & 1 deletion libweb3jsonrpc/admin_eth.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
{ "name": "admin_eth_newAccount", "params": [{}, ""], "returns": {} },
{ "name": "admin_eth_setMiningBenefactor", "params": ["", ""], "returns": true },
{ "name": "admin_eth_inspect", "params": ["", ""], "returns": {} },
{ "name": "admin_eth_reprocess", "params": ["", ""], "returns": {} },
{ "name": "admin_eth_vmTrace", "params": ["", 0, ""], "returns": {} },
{ "name": "admin_eth_getReceiptByHashAndIndex", "params": ["", 0, ""], "returns": {} },
{ "name": "miner_start", "params": [0], "returns": true },
Expand Down
11 changes: 0 additions & 11 deletions test/unittests/libweb3jsonrpc/WebThreeStubClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -924,17 +924,6 @@ class WebThreeStubClient : public jsonrpc::Client
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
Json::Value admin_eth_reprocess(const std::string& param1, const std::string& param2) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
p.append(param1);
p.append(param2);
Json::Value result = this->CallMethod("admin_eth_reprocess",p);
if (result.isObject())
return result;
else
throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, result.toStyledString());
}
Json::Value admin_eth_vmTrace(const std::string& param1, int param2, const std::string& param3) throw (jsonrpc::JsonRpcException)
{
Json::Value p;
Expand Down