38 #include <boost/exception/errinfo_nested_exception.hpp>
39 #include <boost/filesystem.hpp>
44 namespace fs = boost::filesystem;
46 #define ETH_TIMED_IMPORTS 1
50 std::string
const c_chainStart{
"chainStart"};
51 db::Slice const c_sliceChainStart{c_chainStart};
57 _bc.m_blocksDB->forEach([&_out, &cmp](
db::Slice const& _key,
db::Slice const& _value) {
58 if (
string(_key.
data(), _key.
size()) !=
"best")
60 const string key(_key.data(), _key.size());
63 BlockHeader d(bytesConstRef{_value});
64 _out << toHex(key) <<
": " << d.number() <<
" @ " << d.parentHash()
65 << (cmp == key ?
" BEST" :
"") << std::endl;
69 cwarn <<
"Invalid DB entry:" <<
toHex(key) <<
" -> "
80 #if ALL_COMPILERS_ARE_CPP11_COMPLIANT
82 h[32] = (uint8_t)_sub;
85 static boost::thread_specific_ptr<FixedHash<33>> t_h;
89 (*t_h)[32] = (uint8_t)_sub;
91 #endif //ALL_COMPILERS_ARE_CPP11_COMPLIANT
96 #if ALL_COMPILERS_ARE_CPP11_COMPLIANT
99 h[32] = (uint8_t)_sub;
102 static boost::thread_specific_ptr<FixedHash<33>> t_h;
107 (*t_h)[32] = (uint8_t)_sub;
118 explicit LastBlockHashes(
BlockChain const& _bc): m_bc(_bc) {}
120 h256s precedingHashes(
h256 const& _mostRecentHash)
const override
122 Guard l(m_lastHashesMutex);
123 if (m_lastHashes.empty() || m_lastHashes.front() != _mostRecentHash)
125 m_lastHashes.resize(256);
126 m_lastHashes[0] = _mostRecentHash;
127 for (
unsigned i = 0; i < 255; ++i)
128 m_lastHashes[i + 1] = m_lastHashes[i] ? m_bc.info(m_lastHashes[i]).parentHash() :
h256();
133 void clear()
override
135 Guard l(m_lastHashesMutex);
136 m_lastHashes.clear();
142 mutable Mutex m_lastHashesMutex;
143 mutable h256s m_lastHashes;
160 static const chrono::system_clock::duration c_collectionDuration = chrono::seconds(60);
163 static const unsigned c_collectionQueueSize = 20;
166 static const unsigned c_maxCacheSize = 1024 * 1024 * 64;
169 static const unsigned c_minCacheSize = 1024 * 1024 * 32;
173 m_lastBlockHashes(new LastBlockHashes(*this)),
177 open(_dbPath, _we, _pc);
194 m_genesisHash = m_genesis.
hash();
202 m_cacheUsage.resize(c_collectionQueueSize);
203 m_lastCollection = chrono::system_clock::now();
212 unsigned BlockChain::open(fs::path
const& _path,
WithExisting _we)
215 fs::path chainPath = path / fs::path(
toHex(m_genesisHash.
ref().
cropped(0, 4)));
221 fs::create_directories(extrasPath);
231 fs::rename(extrasPath / fs::path(
"extras"), extrasPath / fs::path(
"extras.old"));
232 fs::remove_all(extrasPath / fs::path(
"state"));
234 lastMinor = (unsigned)
RLP(status);
239 cnote <<
"Killing blockchain & extras database (WithExisting::Kill).";
240 fs::remove_all(chainPath / fs::path(
"blocks"));
241 fs::remove_all(extrasPath / fs::path(
"extras"));
250 catch (db::DatabaseError
const& ex)
258 if (fs::space(chainPath / fs::path(
"blocks")).available < 1024)
260 cwarn <<
"Not enough available space found on hard drive. Please free some up and then re-run. Bailing.";
261 BOOST_THROW_EXCEPTION(NotEnoughAvailableSpace());
267 (chainPath / fs::path(
"blocks")) <<
269 (extrasPath / fs::path(
"extras")) <<
270 "already open. You appear to have another instance of ethereum running. Bailing.";
271 BOOST_THROW_EXCEPTION(DatabaseAlreadyOpen());
276 cwarn <<
"Unknown database error occurred during in-memory database creation";
286 auto r = m_details[m_genesisHash].rlp();
296 auto const l = m_extrasDB->lookup(
db::Slice(
"best"));
299 m_lastBlockNumber =
number(m_lastBlockHash);
315 open(m_dbPath, _we, _pc);
318 void BlockChain::close()
320 ctrace <<
"Closing blockchain DB";
326 m_lastBlockHash = m_genesisHash;
327 m_lastBlockNumber = 0;
333 m_transactionAddresses.clear();
334 m_blockHashes.clear();
335 m_blocksBlooms.clear();
336 m_cacheUsage.clear();
338 m_lastBlockHashes->clear();
345 cwarn <<
"In-memory database detected, skipping rebuild (since there's no existing database to rebuild)";
350 fs::path chainPath = path / fs::path(
toHex(m_genesisHash.
ref().
cropped(0, 4)));
353 unsigned originalNumber = m_lastBlockNumber;
363 fs::rename(extrasPath / fs::path(
"extras"), extrasPath / fs::path(
"extras.old"));
364 std::unique_ptr<db::DatabaseFace> oldExtrasDB(
db::DBFactory::create(extrasPath / fs::path(
"extras.old")));
374 m_transactionAddresses.clear();
375 m_blockHashes.clear();
376 m_blocksBlooms.clear();
377 m_lastBlockHashes->clear();
379 m_lastBlockNumber = 0;
381 m_details[m_lastBlockHash].totalDifficulty = s.
info().
difficulty();
386 h256 lastHash = m_lastBlockHash;
388 for (
unsigned d = 1; d <= originalNumber; ++d)
392 cerr <<
"\n1000 blocks in " << t.
elapsed() <<
"s = " << (1000.0 / t.
elapsed()) <<
"b/s" << endl;
397 bytes b =
block(queryExtras<BlockHash, uint64_t, ExtraBlockHash>(
398 d, m_blockHashes, x_blockHashes, NullBlockHash, oldExtrasDB.get())
405 cwarn <<
"DISJOINT CHAIN DETECTED; " << bi.
hash() <<
"#" << d <<
" -> parent is" << bi.
parentHash() <<
"; expected" << lastHash <<
"#" << (d - 1);
408 lastHash = bi.
hash();
409 import(b, s.
db(), 0);
418 _progress(d, originalNumber);
421 fs::remove_all(path / fs::path(
"extras.old"));
427 oss << m_lastBlockHash <<
'\n';
431 std::map<std::string, std::string> dbData;
437 for (
auto const& it : dbData)
438 oss <<
toHex(it.first) <<
"/" <<
toHex(it.second) <<
'\n';
448 _bq.
drain(blocks, _max);
466 goodTransactions.reserve(goodTransactions.size() + r.
goodTranactions.size());
470 catch (dev::eth::AlreadyHaveBlock
const&)
472 cwarn <<
"ODD: Import queue contains already imported block";
475 catch (dev::eth::UnknownParent
const&)
477 cwarn <<
"ODD: Import queue contains block with unknown parent.";
480 badBlocks.push_back(
block.verified.info.hash());
482 catch (dev::eth::FutureTime
const&)
484 cwarn <<
"ODD: Import queue contains a block with future time.";
485 this_thread::sleep_for(chrono::seconds(1));
488 catch (dev::eth::TransientError
const&)
490 this_thread::sleep_for(chrono::milliseconds(100));
500 badBlocks.push_back(
block.verified.info.hash());
517 catch (AlreadyHaveBlock&)
537 return import(
block, _db, _mustBeNew);
551 checkBlockIsNew(_block);
563 for (
auto i:
RLP(_receipts))
568 LOG(m_logger) << _block.
info.
hash() <<
" : Invalid receipts root "
571 BOOST_THROW_EXCEPTION(InvalidReceiptsStateRoot());
577 auto pdata = pd.rlp();
578 LOG(m_loggerError) <<
"Details is returning false despite block known: " <<
RLP(pdata);
581 LOG(m_loggerError) <<
"last/number: " << m_lastBlockNumber <<
" " << m_lastBlockHash <<
" "
584 LOG(m_loggerError) <<
"RLP: " <<
RLP(parentBlock);
585 LOG(m_loggerError) <<
"DATABASE CORRUPTION: CRITICAL FAILURE";
590 checkBlockTimestamp(_block.
info);
596 std::unique_ptr<db::WriteBatchFace> blocksWriteBatch = m_blocksDB->createWriteBatch();
597 std::unique_ptr<db::WriteBatchFace> extrasWriteBatch = m_extrasDB->createWriteBatch();
600 for (
auto i:
RLP(_receipts))
621 extrasWriteBatch->insert(
623 extrasWriteBatch->insert(
629 m_blocksDB->commit(std::move(blocksWriteBatch));
631 catch (boost::exception
const& ex)
633 cwarn <<
"Error writing to blockchain database: " << boost::diagnostic_information(ex);
634 cwarn <<
"Fail writing to blockchain database. Bombing out.";
640 m_extrasDB->commit(std::move(extrasWriteBatch));
642 catch (boost::exception
const& ex)
644 cwarn <<
"Error writing to extras database: " << boost::diagnostic_information(ex);
645 cwarn <<
"Fail writing to extras database. Bombing out.";
658 checkBlockIsNew(_block);
671 auto pdata = pd.rlp();
672 LOG(m_loggerError) <<
"Details is returning false despite block known: " <<
RLP(pdata);
675 LOG(m_loggerError) <<
"last/number: " << m_lastBlockNumber <<
" " << m_lastBlockHash <<
" "
678 LOG(m_loggerError) <<
"RLP: " <<
RLP(parentBlock);
679 LOG(m_loggerError) <<
"DATABASE CORRUPTION: CRITICAL FAILURE";
683 checkBlockTimestamp(_block.
info);
688 LOG(m_loggerDetail) <<
"Attempting import of " << _block.
info.
hash() <<
" ...";
699 auto tdIncrease = s.
enactOn(_block, *
this);
701 for (
unsigned i = 0; i < s.
pending().size(); ++i)
706 td = pd.totalDifficulty + tdIncrease;
712 #endif // ETH_PARANOIA
716 cwarn <<
"*** BadRoot error! Trying to import" << _block.
info.
hash() <<
"needed root"
717 << *boost::get_error_info<errinfo_hash256>(ex);
720 BOOST_THROW_EXCEPTION(TransientError());
730 return insertBlockAndExtras(_block,
ref(
receipts), td, performanceLogger);
738 checkBlockIsNew(
block);
740 checkBlockTimestamp(
block.info);
743 return insertBlockAndExtras(
block, _receipts, _totalDifficulty, performanceLogger);
750 LOG(m_logger) << _block.
info.
hash() <<
" : Not new.";
755 void BlockChain::checkBlockTimestamp(
BlockHeader const& _header)
const
760 LOG(m_loggerDetail) << _header.
hash() <<
" : Future time " << _header.
timestamp()
761 <<
" (now at " <<
utcTime() <<
")";
763 BOOST_THROW_EXCEPTION(FutureTime());
769 std::unique_ptr<db::WriteBatchFace> blocksWriteBatch = m_blocksDB->createWriteBatch();
770 std::unique_ptr<db::WriteBatchFace> extrasWriteBatch = m_extrasDB->createWriteBatch();
772 unsigned newLastBlockNumber =
number();
793 extrasWriteBatch->insert(
797 for (
auto i:
RLP(_receipts))
799 extrasWriteBatch->insert(
814 bool isImportedAndBest =
false;
817 if (_totalDifficulty >
details(last).totalDifficulty || (m_sealEngine->chainParams().tieBreakingGas &&
822 unsigned commonIndex;
824 route.push_back(_block.
info.
hash());
829 clearCachesDuringChainReversion(
number(common) + 1);
833 for (
auto i = route.rbegin(); i != route.rend() && *i != common; ++i)
848 for (
unsigned level = 0, index = (
unsigned)tbi.
number(); level < c_bloomIndexLevels; level++, index /= c_bloomIndexSize)
852 for (
unsigned level = 0, index = (
unsigned)tbi.
number(); level < c_bloomIndexLevels; level++, index /= c_bloomIndexSize)
854 unsigned i = index / c_bloomIndexSize;
855 unsigned o = index % c_bloomIndexSize;
856 alteredBlooms.push_back(chunkId(level, i));
857 m_blocksBlooms[alteredBlooms.back()].blooms[o] |=
blockBloom;
868 extrasWriteBatch->insert(
875 for (
auto const& h: alteredBlooms)
876 extrasWriteBatch->insert(
884 newLastBlockHash = _block.
info.
hash();
885 newLastBlockNumber = (unsigned)_block.
info.
number();
886 isImportedAndBest =
true;
889 LOG(m_logger) <<
" Imported and best " << _totalDifficulty <<
" (#"
892 <<
" siblings. Route: " << route;
897 <<
" > TD: " << _totalDifficulty <<
"; " <<
details(last).
number <<
".."
903 m_blocksDB->commit(std::move(blocksWriteBatch));
905 catch (boost::exception& ex)
907 cwarn <<
"Error writing to blockchain database: " << boost::diagnostic_information(ex);
908 cwarn <<
"Fail writing to blockchain database. Bombing out.";
914 m_extrasDB->commit(std::move(extrasWriteBatch));
916 catch (boost::exception& ex)
918 cwarn <<
"Error writing to extras database: " << boost::diagnostic_information(ex);
919 cwarn <<
"Fail writing to extras database. Bombing out.";
926 LOG(m_loggerError) <<
"Known block just inserted has no details.";
927 LOG(m_loggerError) <<
"Block: " << _block.
info;
928 LOG(m_loggerError) <<
"DATABASE CORRUPTION: CRITICAL FAILURE";
935 canary.populateFromChain(*
this, _block.
info.
hash());
939 LOG(m_loggerError) <<
"Failed to initialise State object form imported block.";
940 LOG(m_loggerError) <<
"Block: " << _block.
info;
941 LOG(m_loggerError) <<
"DATABASE CORRUPTION: CRITICAL FAILURE";
944 #endif // ETH_PARANOIA
946 if (m_lastBlockHash != newLastBlockHash)
949 m_lastBlockHash = newLastBlockHash;
950 m_lastBlockNumber = newLastBlockNumber;
955 catch (boost::exception
const& ex)
957 cwarn <<
"Error writing to extras database: " << boost::diagnostic_information(ex);
960 cwarn <<
"Fail writing to extras database. Bombing out.";
967 #endif // ETH_PARANOIA
971 unsigned const gasPerSecond =
static_cast<double>(_block.
info.
gasUsed()) / _performanceLogger.
stageDuration(
"enactment");
975 {
"gasPerSecond",
toString(gasPerSecond)},
983 if (isImportedAndBest && m_onBlockImport)
984 m_onBlockImport(_block.
info);
989 for (
auto const& h: route)
999 void BlockChain::clearBlockBlooms(
unsigned _begin,
unsigned _end)
1017 unsigned beginDirty = _begin;
1018 unsigned endDirty = _end;
1019 for (
unsigned level = 0; level < c_bloomIndexLevels; level++, beginDirty /= c_bloomIndexSize, endDirty = (endDirty - 1) / c_bloomIndexSize + 1)
1022 for (
unsigned item = beginDirty; item != endDirty; ++item)
1024 unsigned bunch = item / c_bloomIndexSize;
1025 unsigned offset = item % c_bloomIndexSize;
1026 auto id = chunkId(level, bunch);
1031 auto lowerChunkId = chunkId(level - 1, item);
1036 m_blocksBlooms[id].blooms[offset] = acc;
1043 cout <<
"Rescuing database..." << endl;
1060 cout <<
"Finding last likely block number..." << endl;
1063 unsigned m = (u + l) / 2;
1064 cout <<
" " << m << flush;
1070 cout <<
" lowest is " << l << endl;
1074 cout <<
"Checking validity of " << l <<
" (" << h <<
")..." << flush;
1077 cout <<
"block..." << flush;
1079 cout <<
"extras..." << flush;
1081 cout <<
"state..." << flush;
1087 cout <<
"OK." << endl;
1095 if (_newHead >= m_lastBlockNumber)
1097 clearCachesDuringChainReversion(_newHead + 1);
1099 m_lastBlockNumber = _newHead;
1104 catch (boost::exception
const& ex)
1106 cwarn <<
"Error writing to extras database: " << boost::diagnostic_information(ex);
1109 cwarn <<
"Fail writing to extras database. Bombing out.";
1127 unsigned fn = fromDetails.
number;
1128 unsigned tn = toDetails.
number;
1134 ret.push_back(from);
1150 if (_pre && (from != to || _common))
1151 ret.push_back(from);
1152 if (_post && (from != to || (!_pre && _common)))
1162 ret.reserve(ret.size() + back.size());
1163 unsigned i = ret.size() - (int)(_common && !ret.empty() && !back.empty());
1164 for (
auto it = back.rbegin(); it != back.rend(); ++it)
1166 return make_tuple(ret, from, i);
1169 void BlockChain::noteUsed(
h256 const& _h,
unsigned _extra)
const
1171 auto id = CacheID(_h, _extra);
1172 Guard l(x_cacheUsage);
1173 m_cacheUsage[0].insert(
id);
1174 if (m_cacheUsage[1].count(
id))
1175 m_cacheUsage[1].erase(
id);
1180 template <
class K,
class T>
static unsigned getHashSize(unordered_map<K, T>
const& _map)
1183 for (
auto const& i: _map)
1184 ret += i.second.size + 64;
1188 void BlockChain::updateStats()
const
1192 for (
auto const& i: m_blocks)
1193 m_lastStats.
memBlocks += i.second.size() + 64;
1195 m_lastStats.
memDetails = getHashSize(m_details);
1196 size_t logBloomsSize = 0;
1197 size_t blocksBloomsSize = 0;
1199 logBloomsSize = getHashSize(m_logBlooms);
1201 blocksBloomsSize = getHashSize(m_blocksBlooms);
1202 m_lastStats.
memLogBlooms = logBloomsSize + blocksBloomsSize;
1204 m_lastStats.
memReceipts = getHashSize(m_receipts);
1215 if (!_force && chrono::system_clock::now() < m_lastCollection + c_collectionDuration && m_lastStats.
memTotal() < c_maxCacheSize)
1217 if (m_lastStats.
memTotal() < c_minCacheSize)
1220 m_lastCollection = chrono::system_clock::now();
1222 Guard l(x_cacheUsage);
1223 for (CacheID
const&
id: m_cacheUsage.back())
1232 m_blocks.erase(
id.first);
1238 m_details.erase(
id.first);
1250 m_receipts.erase(
id.first);
1256 m_logBlooms.erase(
id.first);
1262 m_transactionAddresses.erase(
id.first);
1268 m_blocksBlooms.erase(
id.first);
1273 m_cacheUsage.pop_back();
1274 m_cacheUsage.push_front(std::unordered_set<CacheID>{});
1277 void BlockChain::checkConsistency()
1282 if (_key.
size() == 32)
1284 h256 h((byte const*)_key.data(), h256::ConstructFromPointer);
1285 auto dh = details(h);
1287 if (p != h256() && p != m_genesisHash)
1291 auto dp = details(p);
1292 if (asserts(contains(dp.children, h)))
1293 cnote <<
"Apparently the database is corrupt. Not much we can do at this "
1295 if (assertsEqual(dp.number, dh.number - 1))
1296 cnote <<
"Apparently the database is corrupt. Not much we can do at this "
1304 void BlockChain::clearCachesDuringChainReversion(
unsigned _firstInvalid)
1306 unsigned end = m_lastBlockNumber + 1;
1308 for (
auto i = _firstInvalid; i < end; ++i)
1309 m_blockHashes.erase(i);
1311 m_transactionAddresses.clear();
1315 clearBlockBlooms(_firstInvalid, end);
1318 static inline unsigned upow(
unsigned a,
unsigned b) {
if (!b)
return 1;
while (--b > 0) a *= a;
return a; }
1319 static inline unsigned ceilDiv(
unsigned n,
unsigned d) {
return (n + d - 1) / d; }
1334 vector<unsigned> BlockChain::withBlockBloom(
LogBloom const& _b,
unsigned _earliest,
unsigned _latest)
const
1336 vector<unsigned> ret;
1339 unsigned u = upow(c_bloomIndexSize, c_bloomIndexLevels);
1342 for (
unsigned index = _earliest / u; index <= ceilDiv(_latest, u); ++index)
1343 ret += withBlockBloom(_b, _earliest, _latest, c_bloomIndexLevels - 1, index);
1348 vector<unsigned> BlockChain::withBlockBloom(
LogBloom const& _b,
unsigned _earliest,
unsigned _latest,
unsigned _level,
unsigned _index)
const
1355 vector<unsigned> ret;
1357 unsigned uCourse = upow(c_bloomIndexSize, _level + 1);
1360 unsigned uFine = upow(c_bloomIndexSize, _level);
1364 unsigned obegin = _index == _earliest / uCourse ? _earliest / uFine % c_bloomIndexSize : 0;
1369 unsigned oend = _index == _latest / uCourse ? (_latest / uFine) % c_bloomIndexSize + 1 : c_bloomIndexSize;
1376 for (
unsigned o = obegin; o < oend; ++o)
1377 if (bb.blooms[o].contains(_b))
1381 ret += withBlockBloom(_b, _earliest, _latest, _level - 1, o + _index * c_bloomIndexSize);
1383 ret.push_back(o + _index * c_bloomIndexSize);
1388 h256Hash BlockChain::allKinFrom(
h256 const& _parent,
unsigned _generations)
const
1394 for (
unsigned i = 0; i < _generations && p != m_genesisHash; ++i, p = details(p).parent)
1396 ret.insert(details(p).parent);
1398 for (
auto i:
RLP(b)[2])
1399 ret.insert(
sha3(i.data()));
1404 bool BlockChain::isKnown(
h256 const& _hash,
bool _isCurrent)
const
1406 if (_hash == m_genesisHash)
1410 if (!m_blocks.count(_hash) && !m_blocksDB->exists(
toSlice(_hash)))
1420 return !_isCurrent || details(_hash).number <= m_lastBlockNumber;
1425 if (_hash == m_genesisHash)
1426 return m_params.genesisBlock();
1430 auto it = m_blocks.find(_hash);
1431 if (it != m_blocks.end())
1435 string const d = m_blocksDB->lookup(
toSlice(_hash));
1438 cwarn <<
"Couldn't find requested block:" << _hash;
1445 m_blocks[_hash].resize(d.size());
1446 memcpy(m_blocks[_hash].data(), d.data(), d.size());
1448 return m_blocks[_hash];
1453 if (_hash == m_genesisHash)
1454 return m_genesisHeaderBytes;
1458 auto it = m_blocks.find(_hash);
1459 if (it != m_blocks.end())
1460 return BlockHeader::extractHeader(&it->second).data().toBytes();
1463 string const d = m_blocksDB->lookup(
toSlice(_hash));
1466 cwarn <<
"Couldn't find requested block:" << _hash;
1473 m_blocks[_hash].resize(d.size());
1474 memcpy(m_blocks[_hash].data(), d.data(), d.size());
1476 return BlockHeader::extractHeader(&m_blocks[_hash]).data().toBytes();
1482 Block ret(*
this, _db, BaseState::Empty);
1490 cwarn <<
"Hinted genesis block's state root hash is incorrect!";
1496 ret.m_previousBlock =
BlockHeader(m_params.genesisBlock());
1508 if (!!(_ir & ImportRequirements::PostGenesis) && (!h.
parentHash() || h.
number() == 0))
1512 if (!!(_ir & ImportRequirements::Parent))
1515 if (parentHeader.empty())
1525 addBlockInfo(ex, h, _block.
toBytes());
1533 if (_ir & (ImportRequirements::UncleBasic | ImportRequirements::UncleParent | ImportRequirements::UncleSeals))
1534 for (
auto const& uncle: r[2])
1540 if (!!(_ir & ImportRequirements::UncleParent))
1543 if (parentHeader.empty())
1553 addBlockInfo(ex, uh, _block.
toBytes());
1561 if (_ir & (ImportRequirements::TransactionBasic | ImportRequirements::TransactionSignatures))
1562 for (
RLP const& tr: r[1])
1567 Transaction t(d, (_ir & ImportRequirements::TransactionSignatures) ? CheckTransaction::Everything : CheckTransaction::None);
1568 m_sealEngine->verifyTransaction(_ir, t, h, 0);
1576 addBlockInfo(ex, h, _block.
toBytes());
1587 void BlockChain::setChainStartBlockNumber(
unsigned _number)
1589 h256 const hash = numberHash(_number);
1591 BOOST_THROW_EXCEPTION(UnknownBlockNumber());
1598 catch (boost::exception
const& ex)
1600 BOOST_THROW_EXCEPTION(FailedToWriteChainStart()
1602 << boost::errinfo_nested_exception(boost::copy_exception(ex)));
1606 unsigned BlockChain::chainStartBlockNumber()
const
1608 auto const value = m_extrasDB->lookup(c_sliceChainStart);