bool State::sync(BlockChain const& _bc, h256 _block) { bool ret = false; // BLOCK BlockInfo bi; try { auto b = _bc.block(_block); bi.populate(b); bi.verifyInternals(_bc.block(_block)); } catch (...) { // TODO: Slightly nicer handling? :-) cerr << "ERROR: Corrupt block-chain! Delete your block-chain DB and restart." << endl; exit(1); } 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(); m_currentNumber++; 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.) std::vector<h256> chain; while (bi.stateRoot != BlockInfo::genesis().hash && 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. for (auto it = chain.rbegin(); it != chain.rend(); ++it) playback(_bc.block(*it), true); m_currentNumber = _bc.details(_block).number + 1; resetCurrent(); ret = true; } return ret; }
State::State(OverlayDB const& _db, BlockChain const& _bc, h256 _h): m_db(_db), m_state(&m_db), m_blockReward(c_blockReward) { // TODO THINK: is this necessary? m_state.init(); auto b = _bc.block(_h); BlockInfo bi; BlockInfo bip; if (_h) bi.populate(b); if (bi && bi.number) bip.populate(_bc.block(bi.parentHash)); if (!_h || !bip) return; m_ourAddress = bi.coinbaseAddress; sync(_bc, bi.parentHash, bip); enact(&b, _bc); }
u256 State::enactOn(bytesConstRef _block, BlockInfo const& _bi, BlockChain const& _bc) { // Check family: BlockInfo biParent(_bc.block(_bi.parentHash)); _bi.verifyParent(biParent); BlockInfo biGrandParent; if (biParent.number) biGrandParent.populate(_bc.block(biParent.parentHash)); sync(_bc, _bi.parentHash); resetCurrent(); m_previousBlock = biParent; return enact(_block, _bc); }
void BlockChain::import(bytes const& _block) { try { // VERIFY: populates from the block and checks the block is internally coherent. BlockInfo bi(&_block); bi.verifyInternals(&_block); auto newHash = eth::sha3(_block); // Check block doesn't already exist first! if (m_details.count(newHash)) return; // Work out its number as the parent's number + 1 auto it = m_details.find(bi.parentHash); if (it == m_details.end()) // We don't know the parent (yet) - discard for now. It'll get resent to us if we find out about its ancestry later on. return; // Check family: BlockInfo biParent(block(bi.parentHash)); bi.verifyParent(biParent); // Check transactions are valid and that they result in a state equivalent to our state_root. State s(bi.coinbaseAddress); s.sync(*this, bi.parentHash); // Get total difficulty increase and update state, checking it. BlockInfo biGrandParent; if (it->second.number) biGrandParent.populate(block(it->second.parent)); u256 td = it->second.totalDifficulty + s.playback(&_block, bi, biParent, biGrandParent); // All ok - insert into DB m_details[newHash] = BlockDetails{(uint)it->second.number + 1, bi.parentHash, td}; m_details[bi.parentHash].children.push_back(newHash); m_db->Put(m_writeOptions, ldb::Slice(toBigEndianString(newHash)), (ldb::Slice)ref(_block)); // This might be the new last block... if (td > m_details[m_lastBlockHash].totalDifficulty) m_lastBlockHash = newHash; } catch (...) { // Exit silently on exception(?) return; } }
PopulationStatistics State::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; return ret; } auto b = _bc.block(_h); BlockInfo bi(b); if (bi.number) { // Non-genesis: // 1. Start at parent's end state (state root). BlockInfo bip; bip.populate(_bc.block(bi.parentHash)); sync(_bc, bi.parentHash, bip, _ir); // 2. Enact the block's transactions onto this state. m_ourAddress = bi.coinbaseAddress; Timer t; auto vb = BlockChain::verifyBlock(b); ret.verify = t.elapsed(); t.restart(); enact(vb, _bc, _ir); ret.enact = t.elapsed(); } else { // Genesis required: // We know there are no transactions, so just populate directly. m_state.init(); sync(_bc, _h, bi, _ir); } return ret; }
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 {
void BlockChain::import(bytes const& _block, Overlay const& _db) { // VERIFY: populates from the block and checks the block is internally coherent. BlockInfo bi(&_block); bi.verifyInternals(&_block); auto newHash = eth::sha3(_block); // Check block doesn't already exist first! if (details(newHash)) { clog(BlockChainChat) << newHash << ": Not new."; throw AlreadyHaveBlock(); } // Work out its number as the parent's number + 1 auto pd = details(bi.parentHash); if (!pd) { clog(BlockChainChat) << newHash << ": Unknown parent " << bi.parentHash; // We don't know the parent (yet) - discard for now. It'll get resent to us if we find out about its ancestry later on. throw UnknownParent(); } clog(BlockChainNote) << "Attempting import of " << newHash << "..."; u256 td; try { // Check family: BlockInfo biParent(block(bi.parentHash)); bi.verifyParent(biParent); // Check transactions are valid and that they result in a state equivalent to our state_root. State s(bi.coinbaseAddress, _db); s.sync(*this, bi.parentHash); // Get total difficulty increase and update state, checking it. BlockInfo biGrandParent; if (pd.number) biGrandParent.populate(block(pd.parent)); auto tdIncrease = s.playback(&_block, bi, biParent, biGrandParent, true); td = pd.totalDifficulty + tdIncrease; #if !NDEBUG checkConsistency(); #endif // All ok - insert into DB m_details[newHash] = BlockDetails((uint)pd.number + 1, td, bi.parentHash, {}); m_detailsDB->Put(m_writeOptions, ldb::Slice((char const*)&newHash, 32), (ldb::Slice)eth::ref(m_details[newHash].rlp())); m_details[bi.parentHash].children.push_back(newHash); m_detailsDB->Put(m_writeOptions, ldb::Slice((char const*)&bi.parentHash, 32), (ldb::Slice)eth::ref(m_details[bi.parentHash].rlp())); m_db->Put(m_writeOptions, ldb::Slice((char const*)&newHash, 32), (ldb::Slice)ref(_block)); #if !NDEBUG checkConsistency(); #endif } catch (...) { clog(BlockChainNote) << " Malformed block."; throw; } // cnote << "Parent " << bi.parentHash << " has " << details(bi.parentHash).children.size() << " children."; // This might be the new last block... if (td > m_details[m_lastBlockHash].totalDifficulty) { m_lastBlockHash = newHash; m_detailsDB->Put(m_writeOptions, ldb::Slice("best"), ldb::Slice((char const*)&newHash, 32)); clog(BlockChainNote) << " Imported and best. Has" << details(bi.parentHash).children.size() << "siblings."; } else { clog(BlockChainNote) << " Imported but not best (oTD:" << m_details[m_lastBlockHash].totalDifficulty << ", TD:" << td << ")"; } }
ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc) { // Check if we already know this block. h256 h = BlockInfo::headerHash(_block); cblockq << "Queuing block" << h.abridged() << "for import..."; UpgradableGuard l(m_lock); if (m_readySet.count(h) || m_drainingSet.count(h) || m_unknownSet.count(h)) { // Already know about this one. cblockq << "Already known."; return ImportResult::AlreadyKnown; } // VERIFY: populates from the block and checks the block is internally coherent. BlockInfo bi; #if ETH_CATCH try #endif { bi.populate(_block); bi.verifyInternals(_block); } #if ETH_CATCH catch (Exception const& _e) { cwarn << "Ignoring malformed block: " << diagnostic_information(_e); return false; return ImportResult::Malformed; } #endif // Check block doesn't already exist first! if (_bc.details(h)) { cblockq << "Already known in chain."; return ImportResult::AlreadyInChain; } UpgradeGuard ul(l); // Check it's not in the future if (bi.timestamp > (u256)time(0)) { m_future.insert(make_pair((unsigned)bi.timestamp, _block.toBytes())); cblockq << "OK - queued for future."; return ImportResult::FutureTime; } else { // We now know it. if (!m_readySet.count(bi.parentHash) && !m_drainingSet.count(bi.parentHash) && !_bc.isKnown(bi.parentHash)) { // We don't know the parent (yet) - queue it up for later. It'll get resent to us if we find out about its ancestry later on. cblockq << "OK - queued as unknown parent:" << bi.parentHash.abridged(); m_unknown.insert(make_pair(bi.parentHash, make_pair(h, _block.toBytes()))); m_unknownSet.insert(h); return ImportResult::UnknownParent; } else { // If valid, append to blocks. cblockq << "OK - ready for chain insertion."; m_ready.push_back(_block.toBytes()); m_readySet.insert(h); noteReadyWithoutWriteGuard(h); return ImportResult::Success; } } }
bool State::sync(BlockChain const& _bc, h256 _block, BlockInfo const& _bi) { bool ret = false; // BLOCK BlockInfo bi = _bi; if (!bi) while (1) { try { auto b = _bc.block(_block); bi.populate(b); // bi.verifyInternals(_bc.block(_block)); // Unneeded - we already verify on import into the blockchain. 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; } } 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.) std::vector<h256> 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); 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; } return ret; }
ImportResult BlockQueue::import(bytesConstRef _block, BlockChain const& _bc, bool _isOurs) { clog(BlockQueueTraceChannel) << std::this_thread::get_id(); // Check if we already know this block. h256 h = BlockInfo::headerHash(_block); clog(BlockQueueTraceChannel) << "Queuing block" << h << "for import..."; UpgradableGuard l(m_lock); if (m_readySet.count(h) || m_drainingSet.count(h) || m_unknownSet.count(h) || m_knownBad.count(h)) { // Already know about this one. clog(BlockQueueTraceChannel) << "Already known."; return ImportResult::AlreadyKnown; } // VERIFY: populates from the block and checks the block is internally coherent. BlockInfo bi; try { // TODO: quick verify bi.populate(_block); bi.verifyInternals(_block); } catch (Exception const& _e) { cwarn << "Ignoring malformed block: " << diagnostic_information(_e); return ImportResult::Malformed; } clog(BlockQueueTraceChannel) << "Block" << h << "is" << bi.number << "parent is" << bi.parentHash; // Check block doesn't already exist first! if (_bc.isKnown(h)) { cblockq << "Already known in chain."; return ImportResult::AlreadyInChain; } UpgradeGuard ul(l); DEV_INVARIANT_CHECK; // Check it's not in the future (void)_isOurs; if (bi.timestamp > (u256)time(0)/* && !_isOurs*/) { m_future.insert(make_pair((unsigned)bi.timestamp, make_pair(h, _block.toBytes()))); char buf[24]; time_t bit = (unsigned)bi.timestamp; if (strftime(buf, 24, "%X", localtime(&bit)) == 0) buf[0] = '\0'; // empty if case strftime fails clog(BlockQueueTraceChannel) << "OK - queued for future [" << bi.timestamp << "vs" << time(0) << "] - will wait until" << buf; m_unknownSize += _block.size(); m_unknownCount++; m_difficulty += bi.difficulty; bool unknown = !m_readySet.count(bi.parentHash) && !m_drainingSet.count(bi.parentHash) && !_bc.isKnown(bi.parentHash); return unknown ? ImportResult::FutureTimeUnknown : ImportResult::FutureTimeKnown; } else { // We now know it. if (m_knownBad.count(bi.parentHash)) { m_knownBad.insert(bi.hash()); updateBad_WITH_LOCK(bi.hash()); // bad parent; this is bad too, note it as such return ImportResult::BadChain; } else if (!m_readySet.count(bi.parentHash) && !m_drainingSet.count(bi.parentHash) && !_bc.isKnown(bi.parentHash)) { // We don't know the parent (yet) - queue it up for later. It'll get resent to us if we find out about its ancestry later on. clog(BlockQueueTraceChannel) << "OK - queued as unknown parent:" << bi.parentHash; m_unknown.insert(make_pair(bi.parentHash, make_pair(h, _block.toBytes()))); m_unknownSet.insert(h); m_unknownSize += _block.size(); m_difficulty += bi.difficulty; m_unknownCount++; return ImportResult::UnknownParent; } else { // If valid, append to blocks. clog(BlockQueueTraceChannel) << "OK - ready for chain insertion."; DEV_GUARDED(m_verification) m_unverified.push_back(UnverifiedBlock { h, bi.parentHash, _block.toBytes() }); m_moreToVerify.notify_one(); m_readySet.insert(h); m_knownSize += _block.size(); m_difficulty += bi.difficulty; m_knownCount++; noteReady_WITH_LOCK(h); return ImportResult::Success; } } }