/* This file is part of cpp-ethereum. cpp-ethereum is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. cpp-ethereum 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 General Public License for more details. You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ /** @file Block.cpp * @author Gav Wood * @date 2014 */ #include "Block.h" #include #include #include #include #include #include #include #include #include #include #include #include "BlockChain.h" #include "Defaults.h" #include "ExtVM.h" #include "Executive.h" #include "CachedAddressState.h" #include "CanonBlockChain.h" #include "TransactionQueue.h" using namespace std; using namespace dev; using namespace dev::eth; namespace fs = boost::filesystem; #define ctrace clog(BlockTrace) #define ETH_TIMED_ENACTMENTS 0 static const unsigned c_maxSyncTransactions = 256; const char* BlockSafeExceptions::name() { return EthViolet "⚙" EthBlue " ℹ"; } const char* BlockDetail::name() { return EthViolet "⚙" EthWhite " ◌"; } const char* BlockTrace::name() { return EthViolet "⚙" EthGray " ◎"; } const char* BlockChat::name() { return EthViolet "⚙" EthWhite " ◌"; } Block::Block(OverlayDB const& _db, BaseState _bs, Address _coinbaseAddress): m_state(_db, _bs), m_beneficiary(_coinbaseAddress), m_blockReward(c_blockReward) { m_previousBlock.clear(); m_currentBlock.clear(); // assert(m_state.root() == m_previousBlock.stateRoot()); } Block::Block(Block const& _s): m_state(_s.m_state), m_transactions(_s.m_transactions), m_receipts(_s.m_receipts), m_transactionSet(_s.m_transactionSet), m_previousBlock(_s.m_previousBlock), m_currentBlock(_s.m_currentBlock), m_beneficiary(_s.m_beneficiary), m_blockReward(_s.m_blockReward) { m_precommit = m_state; m_committedToMine = false; } Block& Block::operator=(Block const& _s) { m_state = _s.m_state; m_transactions = _s.m_transactions; m_receipts = _s.m_receipts; m_transactionSet = _s.m_transactionSet; m_previousBlock = _s.m_previousBlock; m_currentBlock = _s.m_currentBlock; m_beneficiary = _s.m_beneficiary; m_blockReward = _s.m_blockReward; m_precommit = m_state; m_committedToMine = false; return *this; } void Block::resetCurrent() { m_transactions.clear(); m_receipts.clear(); m_transactionSet.clear(); m_currentBlock = BlockInfo(); m_currentBlock.setCoinbaseAddress(m_beneficiary); m_currentBlock.setTimestamp(max(m_previousBlock.timestamp() + 1, (u256)time(0))); m_currentBlock.populateFromParent(m_previousBlock); // TODO: check. m_state.setRoot(m_previousBlock.stateRoot()); m_precommit = m_state; m_committedToMine = false; } PopulationStatistics Block::populateFromChain(BlockChain const& _bc, h256 const& _h, ImportRequirements::value _ir) { 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); BlockInfo bi(b); if (bi.number()) { // Non-genesis: // 1. Start at parent's end state (state root). BlockInfo bip(_bc.block(bi.parentHash())); sync(_bc, bi.parentHash(), bip); // 2. Enact the block's transactions onto this state. m_beneficiary = bi.beneficiary(); Timer t; auto vb = _bc.verifyBlock(&b, function(), _ir | ImportRequirements::TransactionBasic); ret.verify = t.elapsed(); t.restart(); enact(vb, _bc); ret.enact = t.elapsed(); } else { // Genesis required: // We know there are no transactions, so just populate directly. m_state = State(m_state.db(), BaseState::Empty); // TODO: try with PreExisting. sync(_bc, _h, bi); } return ret; } bool Block::sync(BlockChain const& _bc) { return sync(_bc, _bc.currentHash()); } bool Block::sync(BlockChain const& _bc, h256 const& _block, BlockInfo const& _bi) { bool ret = false; // BLOCK BlockInfo bi = _bi ? _bi : _bc.info(_block); #if ETH_PARANOIA if (!bi) while (1) { try { auto b = _bc.block(_block); bi.populate(b); break; } catch (Exception const& _e) { // TODO: Slightly nicer handling? :-) cerr << "ERROR: Corrupt block-chain! Delete your block-chain DB and restart." << endl; cerr << diagnostic_information(_e) << endl; } catch (std::exception const& _e) { // TODO: Slightly nicer handling? :-) cerr << "ERROR: Corrupt block-chain! Delete your block-chain DB and restart." << endl; cerr << _e.what() << endl; } } #endif if (bi == m_currentBlock) { // We mined the last block. // Our state is good - we just need to move on to next. m_previousBlock = m_currentBlock; resetCurrent(); ret = true; } else if (bi == m_previousBlock) { // No change since last sync. // Carry on as we were. } else { // New blocks available, or we've switched to a different branch. All change. // Find most recent state dump and replay what's left. // (Most recent state dump might end up being genesis.) if (m_state.db().lookup(bi.stateRoot()).empty()) // TODO: API in State for this? { cwarn << "Unable to sync to" << bi.hash() << "; state root" << bi.stateRoot() << "not found in database."; cwarn << "Database corrupt: contains block without stateRoot:" << bi; cwarn << "Try rescuing the database by running: eth --rescue"; BOOST_THROW_EXCEPTION(InvalidStateRoot() << errinfo_target(bi.stateRoot())); } m_previousBlock = bi; resetCurrent(); ret = true; } #if ALLOW_REBUILD else { // New blocks available, or we've switched to a different branch. All change. // Find most recent state dump and replay what's left. // (Most recent state dump might end up being genesis.) std::vector chain; while (bi.number() != 0 && m_db.lookup(bi.stateRoot()).empty()) // while we don't have the state root of the latest block... { chain.push_back(bi.hash()); // push back for later replay. bi.populate(_bc.block(bi.parentHash())); // move to parent. } m_previousBlock = bi; resetCurrent(); // Iterate through in reverse, playing back each of the blocks. try { for (auto it = chain.rbegin(); it != chain.rend(); ++it) { auto b = _bc.block(*it); enact(&b, _bc, _ir); cleanup(true); } } catch (...) { // TODO: Slightly nicer handling? :-) cerr << "ERROR: Corrupt block-chain! Delete your block-chain DB and restart." << endl; cerr << boost::current_exception_diagnostic_information() << endl; exit(1); } resetCurrent(); ret = true; } #endif return ret; } pair Block::sync(BlockChain const& _bc, TransactionQueue& _tq, GasPricer const& _gp, unsigned msTimeout) { // TRANSACTIONS pair ret; ret.second = false; auto ts = _tq.topTransactions(c_maxSyncTransactions); LastHashes lh; auto deadline = chrono::steady_clock::now() + chrono::milliseconds(msTimeout); for (int goodTxs = 1; goodTxs; ) { goodTxs = 0; for (auto const& t: ts) if (!m_transactionSet.count(t.sha3())) { try { if (t.gasPrice() >= _gp.ask(*this)) { // Timer t; if (lh.empty()) lh = _bc.lastHashes(); execute(lh, t); ret.first.push_back(m_receipts.back()); ++goodTxs; // cnote << "TX took:" << t.elapsed() * 1000; } else if (t.gasPrice() < _gp.ask(*this) * 9 / 10) { clog(StateTrace) << t.sha3() << "Dropping El Cheapo transaction (<90% of ask price)"; _tq.drop(t.sha3()); } } catch (InvalidNonce const& in) { bigint const& req = *boost::get_error_info(in); bigint const& got = *boost::get_error_info(in); if (req > got) { // too old clog(StateTrace) << t.sha3() << "Dropping old transaction (nonce too low)"; _tq.drop(t.sha3()); } else if (got > req + _tq.waiting(t.sender())) { // too new clog(StateTrace) << t.sha3() << "Dropping new transaction (too many nonces ahead)"; _tq.drop(t.sha3()); } else _tq.setFuture(t.sha3()); } catch (BlockGasLimitReached const& e) { bigint const& got = *boost::get_error_info(e); if (got > m_currentBlock.gasLimit()) { clog(StateTrace) << t.sha3() << "Dropping over-gassy transaction (gas > block's gas limit)"; _tq.drop(t.sha3()); } else { // Temporarily no gas left in current block. // OPTIMISE: could note this and then we don't evaluate until a block that does have the gas left. // for now, just leave alone. } } catch (Exception const& _e) { // Something else went wrong - drop it. clog(StateTrace) << t.sha3() << "Dropping invalid transaction:" << diagnostic_information(_e); _tq.drop(t.sha3()); } catch (std::exception const&) { // Something else went wrong - drop it. _tq.drop(t.sha3()); cwarn << t.sha3() << "Transaction caused low-level exception :("; } } if (chrono::steady_clock::now() > deadline) { ret.second = true; break; } } return ret; } u256 Block::enactOn(VerifiedBlockRef const& _block, BlockChain const& _bc) { #if ETH_TIMED_ENACTMENTS Timer t; double populateVerify; double populateGrand; double syncReset; double enactment; #endif // Check family: BlockInfo biParent = _bc.info(_block.info.parentHash()); _block.info.verifyParent(biParent); #if ETH_TIMED_ENACTMENTS populateVerify = t.elapsed(); t.restart(); #endif BlockInfo biGrandParent; if (biParent.number()) biGrandParent = _bc.info(biParent.parentHash()); #if ETH_TIMED_ENACTMENTS populateGrand = t.elapsed(); t.restart(); #endif sync(_bc, _block.info.parentHash(), BlockInfo()); resetCurrent(); #if ETH_TIMED_ENACTMENTS syncReset = t.elapsed(); t.restart(); #endif m_previousBlock = biParent; auto ret = enact(_block, _bc); #if ETH_TIMED_ENACTMENTS enactment = t.elapsed(); if (populateVerify + populateGrand + syncReset + enactment > 0.5) clog(StateChat) << "popVer/popGrand/syncReset/enactment = " << populateVerify << "/" << populateGrand << "/" << syncReset << "/" << enactment; #endif return ret; } u256 Block::enact(VerifiedBlockRef const& _block, BlockChain const& _bc) { DEV_TIMED_FUNCTION_ABOVE(500); // m_currentBlock is assumed to be prepopulated and reset. #if !ETH_RELEASE assert(m_previousBlock.hash() == _block.info.parentHash()); assert(m_currentBlock.parentHash() == _block.info.parentHash()); assert(rootHash() == m_previousBlock.stateRoot()); #endif if (m_currentBlock.parentHash() != m_previousBlock.hash()) // Internal client error. BOOST_THROW_EXCEPTION(InvalidParentHash()); // Populate m_currentBlock with the correct values. m_currentBlock.noteDirty(); m_currentBlock = _block.info; // cnote << "playback begins:" << m_state.root(); // cnote << m_state; LastHashes lh; DEV_TIMED_ABOVE("lastHashes", 500) lh = _bc.lastHashes((unsigned)m_previousBlock.number()); RLP rlp(_block.block); vector receipts; // All ok with the block generally. Play back the transactions now... unsigned i = 0; DEV_TIMED_ABOVE("txExec", 500) for (auto const& tr: _block.transactions) { try { LogOverride o(false); execute(lh, tr); } catch (Exception& ex) { ex << errinfo_transactionIndex(i); throw; } RLPStream receiptRLP; m_receipts.back().streamRLP(receiptRLP); receipts.push_back(receiptRLP.out()); ++i; } h256 receiptsRoot; DEV_TIMED_ABOVE(".receiptsRoot()", 500) receiptsRoot = orderedTrieRoot(receipts); if (receiptsRoot != m_currentBlock.receiptsRoot()) { InvalidReceiptsStateRoot ex; ex << Hash256RequirementError(receiptsRoot, m_currentBlock.receiptsRoot()); ex << errinfo_receipts(receipts); // ex << errinfo_vmtrace(vmTrace(_block.block, _bc, ImportRequirements::None)); BOOST_THROW_EXCEPTION(ex); } if (m_currentBlock.logBloom() != logBloom()) { InvalidLogBloom ex; ex << LogBloomRequirementError(logBloom(), m_currentBlock.logBloom()); ex << errinfo_receipts(receipts); BOOST_THROW_EXCEPTION(ex); } // Initialise total difficulty calculation. u256 tdIncrease = m_currentBlock.difficulty(); // Check uncles & apply their rewards to state. if (rlp[2].itemCount() > 2) { TooManyUncles ex; ex << errinfo_max(2); ex << errinfo_got(rlp[2].itemCount()); BOOST_THROW_EXCEPTION(ex); } vector rewarded; h256Hash excluded; DEV_TIMED_ABOVE("allKin", 500) excluded = _bc.allKinFrom(m_currentBlock.parentHash(), 6); excluded.insert(m_currentBlock.hash()); unsigned ii = 0; DEV_TIMED_ABOVE("uncleCheck", 500) for (auto const& i: rlp[2]) { try { auto h = sha3(i.data()); if (excluded.count(h)) { UncleInChain ex; ex << errinfo_comment("Uncle in block already mentioned"); ex << errinfo_unclesExcluded(excluded); ex << errinfo_hash256(sha3(i.data())); BOOST_THROW_EXCEPTION(ex); } excluded.insert(h); // IgnoreSeal since it's a VerifiedBlock. BlockInfo uncle(i.data(), IgnoreSeal, h, HeaderData); BlockInfo uncleParent; if (!_bc.isKnown(uncle.parentHash())) BOOST_THROW_EXCEPTION(UnknownParent()); uncleParent = BlockInfo(_bc.block(uncle.parentHash())); if ((bigint)uncleParent.number() < (bigint)m_currentBlock.number() - 7) { UncleTooOld ex; ex << errinfo_uncleNumber(uncle.number()); ex << errinfo_currentNumber(m_currentBlock.number()); BOOST_THROW_EXCEPTION(ex); } else if (uncle.number() == m_currentBlock.number()) { UncleIsBrother ex; ex << errinfo_uncleNumber(uncle.number()); ex << errinfo_currentNumber(m_currentBlock.number()); BOOST_THROW_EXCEPTION(ex); } uncle.verifyParent(uncleParent); rewarded.push_back(uncle); ++ii; } catch (Exception& ex) { ex << errinfo_uncleIndex(ii); throw; } } DEV_TIMED_ABOVE("applyRewards", 500) applyRewards(rewarded); // Commit all cached state changes to the state trie. DEV_TIMED_ABOVE("commit", 500) m_state.commit(); // Hash the state trie and check against the state_root hash in m_currentBlock. if (m_currentBlock.stateRoot() != m_previousBlock.stateRoot() && m_currentBlock.stateRoot() != rootHash()) { auto r = rootHash(); m_state.db().rollback(); // TODO: API in State for this? BOOST_THROW_EXCEPTION(InvalidStateRoot() << Hash256RequirementError(r, m_currentBlock.stateRoot())); } if (m_currentBlock.gasUsed() != gasUsed()) { // Rollback the trie. m_state.db().rollback(); // TODO: API in State for this? BOOST_THROW_EXCEPTION(InvalidGasUsed() << RequirementError(bigint(gasUsed()), bigint(m_currentBlock.gasUsed()))); } return tdIncrease; } ExecutionResult Block::execute(LastHashes const& _lh, Transaction const& _t, Permanence _p, OnOpFunc const& _onOp) { // Uncommitting is a non-trivial operation - only do it once we've verified as much of the // transaction as possible. uncommitToMine(); std::pair resultReceipt = m_state.execute(EnvInfo(info(), _lh, gasUsed()), _t, _p, _onOp); if (_p == Permanence::Committed) { // Add to the user-originated transactions that we've executed. m_transactions.push_back(_t); m_receipts.push_back(resultReceipt.second); m_transactionSet.insert(_t.sha3()); } return resultReceipt.first; } void Block::applyRewards(vector const& _uncleBlockHeaders) { u256 r = m_blockReward; for (auto const& i: _uncleBlockHeaders) { m_state.addBalance(i.beneficiary(), m_blockReward * (8 + i.number() - m_currentBlock.number()) / 8); r += m_blockReward / 32; } m_state.addBalance(m_currentBlock.beneficiary(), r); } void Block::commitToSeal(BlockChain const& _bc, bytes const& _extraData) { if (m_committedToMine) uncommitToMine(); else m_precommit = m_state; vector uncleBlockHeaders; RLPStream unclesData; unsigned unclesCount = 0; if (m_previousBlock.number() != 0) { // Find great-uncles (or second-cousins or whatever they are) - children of great-grandparents, great-great-grandparents... that were not already uncles in previous generations. clog(StateDetail) << "Checking " << m_previousBlock.hash() << ", parent=" << m_previousBlock.parentHash(); h256Hash excluded = _bc.allKinFrom(m_currentBlock.parentHash(), 6); auto p = m_previousBlock.parentHash(); for (unsigned gen = 0; gen < 6 && p != _bc.genesisHash() && unclesCount < 2; ++gen, p = _bc.details(p).parent) { auto us = _bc.details(p).children; assert(us.size() >= 1); // must be at least 1 child of our grandparent - it's our own parent! for (auto const& u: us) if (!excluded.count(u)) // ignore any uncles/mainline blocks that we know about. { uncleBlockHeaders.push_back(_bc.info(u)); unclesData.appendRaw(_bc.headerData(u)); ++unclesCount; if (unclesCount == 2) break; } } } BytesMap transactionsMap; BytesMap receiptsMap; RLPStream txs; txs.appendList(m_transactions.size()); for (unsigned i = 0; i < m_transactions.size(); ++i) { RLPStream k; k << i; RLPStream receiptrlp; m_receipts[i].streamRLP(receiptrlp); receiptsMap.insert(std::make_pair(k.out(), receiptrlp.out())); RLPStream txrlp; m_transactions[i].streamRLP(txrlp); transactionsMap.insert(std::make_pair(k.out(), txrlp.out())); txs.appendRaw(txrlp.out()); } txs.swapOut(m_currentTxs); RLPStream(unclesCount).appendRaw(unclesData.out(), unclesCount).swapOut(m_currentUncles); // Apply rewards last of all. applyRewards(uncleBlockHeaders); // Commit any and all changes to the trie that are in the cache, then update the state root accordingly. m_state.commit(); clog(StateDetail) << "Post-reward stateRoot:" << m_state.rootHash(); clog(StateDetail) << m_state; clog(StateDetail) << *this; m_currentBlock.setLogBloom(logBloom()); m_currentBlock.setGasUsed(gasUsed()); m_currentBlock.setRoots(hash256(transactionsMap), hash256(receiptsMap), sha3(m_currentUncles), m_state.rootHash()); m_currentBlock.setParentHash(m_previousBlock.hash()); m_currentBlock.setExtraData(_extraData); if (m_currentBlock.extraData().size() > 32) { auto ed = m_currentBlock.extraData(); ed.resize(32); m_currentBlock.setExtraData(ed); } m_committedToMine = true; } void Block::uncommitToMine() { if (m_committedToMine) { m_state = m_precommit; m_committedToMine = false; } } bool Block::sealBlock(bytesConstRef _header) { if (!m_committedToMine) return false; if (BlockInfo(_header, CheckNothing, h256{}, HeaderData).hashWithout() != m_currentBlock.hashWithout()) return false; clog(StateDetail) << "Sealing block!"; // Compile block: RLPStream ret; ret.appendList(3); ret.appendRaw(_header); ret.appendRaw(m_currentTxs); ret.appendRaw(m_currentUncles); ret.swapOut(m_currentBytes); m_currentBlock = BlockInfo(_header, CheckNothing, h256(), HeaderData); cnote << "Mined " << m_currentBlock.hash() << "(parent: " << m_currentBlock.parentHash() << ")"; // TODO: move into Sealer StructuredLogger::minedNewBlock( m_currentBlock.hash().abridged(), "", // Can't give the nonce here. "", //TODO: chain head hash here ?? m_currentBlock.parentHash().abridged() ); // Quickly reset the transactions. // TODO: Leave this in a better state than this limbo, or at least record that it's in limbo. m_transactions.clear(); m_receipts.clear(); m_transactionSet.clear(); m_precommit = m_state; return true; } State Block::fromPending(unsigned _i) const { State ret = m_state; _i = min(_i, m_transactions.size()); if (!_i) ret.setRoot(m_previousBlock.stateRoot()); else ret.setRoot(m_receipts[_i - 1].stateRoot()); return ret; } LogBloom Block::logBloom() const { LogBloom ret; for (TransactionReceipt const& i: m_receipts) ret |= i.bloom(); return ret; } void Block::cleanup(bool _fullCommit) { if (_fullCommit) { // Commit the new trie to disk. if (isChannelVisible()) // Avoid calling toHex if not needed clog(StateTrace) << "Committing to disk: stateRoot" << m_currentBlock.stateRoot() << "=" << rootHash() << "=" << toHex(asBytes(db().lookup(rootHash()))); try { EnforceRefs er(db(), true); rootHash(); } catch (BadRoot const&) { clog(StateChat) << "Trie corrupt! :-("; throw; } m_state.db().commit(); // TODO: State API for this? if (isChannelVisible()) // Avoid calling toHex if not needed clog(StateTrace) << "Committed: stateRoot" << m_currentBlock.stateRoot() << "=" << rootHash() << "=" << toHex(asBytes(db().lookup(rootHash()))); m_previousBlock = m_currentBlock; m_currentBlock.populateFromParent(m_previousBlock); clog(StateTrace) << "finalising enactment. current -> previous, hash is" << m_previousBlock.hash(); } else m_state.db().rollback(); // TODO: State API for this? resetCurrent(); } string Block::vmTrace(bytesConstRef _block, BlockChain const& _bc, ImportRequirements::value _ir) { RLP rlp(_block); cleanup(false); BlockInfo bi(_block, (_ir & ImportRequirements::ValidSeal) ? CheckEverything : IgnoreSeal); m_currentBlock = bi; m_currentBlock.verifyInternals(_block); m_currentBlock.noteDirty(); LastHashes lh = _bc.lastHashes((unsigned)m_previousBlock.number()); string ret; unsigned i = 0; for (auto const& tr: rlp[1]) { StandardTrace st; st.setShowMnemonics(); execute(lh, Transaction(tr.data(), CheckTransaction::Everything), Permanence::Committed, st.onOp()); ret += (ret.empty() ? "[" : ",") + st.json(); ++i; } return ret.empty() ? "[]" : (ret + "]"); } std::ostream& dev::eth::operator<<(std::ostream& _out, Block const& _s) { (void)_s; return _out; }