/** * class CoinQBlockTreeSqlite3 */ void CoinQBlockTreeSqlite3::open(const std::string& filename, const Coin::CoinBlockHeader& header, bool bCreateIfNotExists) { uchar_vector genesisHeaderHash = header.getHashLittleEndian(); std::stringstream sql; db.open(filename, bCreateIfNotExists); sql << "CREATE TABLE IF NOT EXISTS `block_headers` (" << "`hash` TEXT UNIQUE NOT NULL," << "`in_best_chain` INTEGER NOT NULL," << "`height` INTEGER NOT NULL," << "`chain_work` TEXT NOT NULL," << "`version` INTEGER NOT NULL," << "`prev_block_hash` TEXT NOT NULL," << "`merkle_root` TEXT NOT NULL," << "`timestamp` INTEGER NOT NULL," << "`bits` INTEGER NOT NULL," << "`nonce` INTEGER NOT NULL," << "`time_inserted` INTEGER DEFAULT (strftime('%s', 'now')))"; db.query(sql.str()); sql.str(""); SQLite3Tx sqliteTx(db, SQLite3Tx::Type::EXCLUSIVE); SQLite3Stmt stmt; sql << "SELECT `hash` FROM `block_headers` WHERE `height` = 0"; stmt.prepare(db, sql.str()); int count = 0; while (stmt.step() == SQLITE_ROW) { if (count > 0) { throw std::runtime_error("File contains more than one genesis block."); } uchar_vector hash; hash.setHex((char*)stmt.getText(0)); if (genesisHeaderHash != hash) { throw std::runtime_error("Genesis block mismatch."); } count++; } if (count == 0) { ChainHeader genesisHeader(header, true, 0, header.getWork()); sql.str(""); sql << "INSERT INTO `block_headers` (`hash`, `in_best_chain`, `height`, `chain_work`, `version`, `prev_block_hash`, `merkle_root`, `timestamp`, `bits`, `nonce`)" << " VALUES ('" << genesisHeaderHash.getHex() << "',1,0,'" << genesisHeader.chainWork.getDec() << "'," << genesisHeader.version << ",'" << genesisHeader.prevBlockHash.getHex() << "','" << genesisHeader.merkleRoot.getHex() << "'," << genesisHeader.timestamp << "," << genesisHeader.bits << "," << genesisHeader.nonce << ")"; db.query(sql.str()); sqliteTx.commit(); notifyInsert(genesisHeader); notifyAddBestChain(genesisHeader); } }
void BlockHeader::fromCoinClasses(const Coin::CoinBlockHeader& blockheader, uint32_t height) { hash_ = blockheader.getHashLittleEndian(); height_ = height; version_ = blockheader.version; prevhash_ = blockheader.prevBlockHash; merkleroot_ = blockheader.merkleRoot; timestamp_ = blockheader.timestamp; bits_ = blockheader.bits; nonce_ = blockheader.nonce; }
void BlockHeader::fromCoinCore(const Coin::CoinBlockHeader& blockheader, uint32_t height) { hash_ = blockheader.hash(); height_ = height; version_ = blockheader.version(); prevhash_ = blockheader.prevBlockHash(); merkleroot_ = blockheader.merkleRoot(); timestamp_ = blockheader.timestamp(); bits_ = blockheader.bits(); nonce_ = blockheader.nonce(); }
bool CoinQBlockTreeMem::insertHeader(const Coin::CoinBlockHeader& header, bool bCheckProofOfWork, bool bReplaceTip) { if (mHeaderHashMap.size() == 0) throw std::runtime_error("No genesis block."); uchar_vector headerHash = header.hash(); if (hasHeader(headerHash)) return false; header_hash_map_t::iterator it = mHeaderHashMap.find(header.prevBlockHash()); if (it == mHeaderHashMap.end()) throw std::runtime_error("Parent not found."); ChainHeader& parent = it->second; // TODO: Check version, compute work required. // Check timestamp /* if (bCheckTimestamp && header.timestamp > time(NULL) + 2 * 60 * 60) { throw std::runtime_error("Timestamp too far in the future."); }*/ // Check proof of work if (bCheckProofOfWork && BigInt(header.getPOWHashLittleEndian()) > header.getTarget()) throw std::runtime_error("Header hash is too big."); ChainHeader& chainHeader = mHeaderHashMap[headerHash] = header; chainHeader.height = parent.height + 1; chainHeader.chainWork = parent.chainWork + chainHeader.getWork(); parent.childHashes.insert(headerHash); notifyInsert(chainHeader); if ((bReplaceTip && chainHeader.chainWork >= mTotalWork) || chainHeader.chainWork > mTotalWork) { setBestChain(chainHeader); } bFlushed = false; return true; }
void CoinQBlockTreeMem::setGenesisBlock(const Coin::CoinBlockHeader& header) { if (mHeaderHashMap.size() != 0) throw std::runtime_error("Tree is not empty."); bFlushed = false; uchar_vector hash = header.hash(); ChainHeader& genesisHeader = mHeaderHashMap[hash] = header; mHeaderHeightMap[0] = &genesisHeader; genesisHeader.height = 0; genesisHeader.inBestChain = true; genesisHeader.chainWork = genesisHeader.getWork(); mBestHeight = 0; mTotalWork = genesisHeader.chainWork; pHead = &genesisHeader; notifyInsert(header); notifyAddBestChain(header); }
void CoinQBlockTreeMem::loadFromFile(const std::string& filename, bool bCheckProofOfWork, CoinQBlockTreeMem::callback_t callback) { boost::filesystem::path p(filename); if (!boost::filesystem::exists(p)) throw BlockTreeFileNotFoundException(); if (!boost::filesystem::is_regular_file(p)) throw BlockTreeInvalidFileTypeException(); const unsigned int RECORD_SIZE = MIN_COIN_BLOCK_HEADER_SIZE + 4; if (boost::filesystem::file_size(p) % RECORD_SIZE != 0) throw BlockTreeInvalidFileLengthException(); #ifndef _WIN32 std::ifstream fs(p.native(), std::ios::binary); #else std::ifstream fs(filename, std::ios::binary); #endif if (!fs.good()) throw BlockTreeFailedToOpenFileForReadException(); clear(); uchar_vector headerBytes; uchar_vector hash; Coin::CoinBlockHeader header; unsigned int count = 0; char buf[RECORD_SIZE * 64]; while (fs) { fs.read(buf, RECORD_SIZE * 64); if (fs.bad()) throw BlockTreeFileReadFailureException(); unsigned int nbytesread = fs.gcount(); unsigned int pos = 0; for (; pos <= nbytesread - RECORD_SIZE; pos += RECORD_SIZE) { headerBytes.assign((unsigned char*)&buf[pos], (unsigned char*)&buf[pos + MIN_COIN_BLOCK_HEADER_SIZE]); header.setSerialized(headerBytes); hash = header.hash(); if (memcmp(&buf[pos + MIN_COIN_BLOCK_HEADER_SIZE], &hash[0], 4)) throw BlockTreeChecksumErrorException(); try { if (mBestHeight >= 0) { insertHeader(header, bCheckProofOfWork); if (count % 10000 == 0) { if (callback && !callback(*this)) throw BlockTreeLoadInterruptedException(); LOGGER(debug) << "CoinQBlockTreeMem::loadFromFile() - header hash: " << header.hash().getHex() << " height: " << count << std::endl; } count++; } else { setGenesisBlock(header); if (callback && !callback(*this)) throw BlockTreeLoadInterruptedException(); LOGGER(debug) << "CoinQBlockTreeMem::loadFromFile() - genesis hash: " << header.hash().getHex() << std::endl; count++; } } catch (const BlockTreeException& e) { throw e; } catch (const std::exception& e) { throw std::runtime_error(std::string("Block ") + hash.getHex() + ": " + e.what()); } } if (pos != nbytesread) throw BlockTreeUnexpectedEndOfFileException(); } if (callback) callback(*this); // No need to interrupt since we're done. }