BCW_API bool hd_private_key::set_serialized(std::string encoded) { if (!is_base58(encoded)) return false; const data_chunk decoded = decode_base58(encoded); if (decoded.size() != serialized_length) return false; if (!verify_checksum(decoded)) return false; auto ds = make_deserializer(decoded.begin(), decoded.end()); auto prefix = ds.read_big_endian<uint32_t>(); if (prefix != mainnet_private_prefix && prefix != testnet_private_prefix) return false; valid_ = true; lineage_.testnet = prefix == testnet_private_prefix; lineage_.depth = ds.read_byte(); lineage_.parent_fingerprint = ds.read_little_endian<uint32_t>(); lineage_.child_number = ds.read_big_endian<uint32_t>(); c_ = ds.read_bytes<chain_code_size>(); ds.read_byte(); k_ = ds.read_bytes<ec_secret_size>(); K_ = secret_to_public_key(k_); return true; }
void receive_history_result(const data_chunk& data, blockchain::fetch_handler_history handle_fetch) { BITCOIN_ASSERT(data.size() >= 4); std::error_code ec; auto deserial = make_deserializer(data.begin(), data.end()); if (!read_error_code(deserial, data.size(), ec)) return; BITCOIN_ASSERT(deserial.iterator() == data.begin() + 4); size_t row_size = 36 + 4 + 8 + 36 + 4; if ((data.size() - 4) % row_size != 0) { log_error() << "Malformed response for *.fetch_history"; return; } size_t number_rows = (data.size() - 4) / row_size; blockchain::history_list history(number_rows); for (size_t i = 0; i < history.size(); ++i) { blockchain::history_row& row = history[i]; row.output.hash = deserial.read_hash(); row.output.index = deserial.read_4_bytes(); row.output_height = deserial.read_4_bytes(); row.value = deserial.read_8_bytes(); row.spend.hash = deserial.read_hash(); row.spend.index = deserial.read_4_bytes(); row.spend_height = deserial.read_4_bytes(); } BITCOIN_ASSERT(deserial.iterator() == data.end()); handle_fetch(ec, history); }
obj deserialize_as(const buf_t& buffer, obj result = obj()) { if (!buffer.empty()) make_deserializer(buffer)(result); return result; }
hashtable_database_writer::hashtable_database_writer(mmfile& file) : file_(file) { BITCOIN_ASSERT(file_.data() != nullptr); // [ version ] 8 // [ buckets ] 8 // [ values size ] 8 // ... // ... BITCOIN_ASSERT(file_.size() > 24); auto deserial = make_deserializer(file_.data(), file_.data() + 24); version_ = deserial.read_8_bytes(); buckets_ = deserial.read_8_bytes(); total_records_size_ = deserial.read_8_bytes(); BITCOIN_ASSERT(version_ == 1); const size_t header_size = 24 + buckets_ * 8; BITCOIN_ASSERT(file_.size() >= header_size + total_records_size_); // Advise the kernel that our access patterns for the tx records // will be random without pattern. //madvise(file_.data(), 24, POSIX_MADV_DONTNEED); //madvise(file_.data() + 24, buckets_ * 8, // POSIX_MADV_WILLNEED | POSIX_MADV_RANDOM); //madvise(file_.data() + header_size, file_.size() - header_size, // POSIX_MADV_DONTNEED | POSIX_MADV_RANDOM); }
stealth_database::stealth_database(mmfile& file) : file_(file) { BITCOIN_ASSERT(file_.data() != nullptr); // metadata: // [ version:4 ] // [ max_header_rows:4 ] // [ entries_count:4 ] // header: // [ ... ] // [ [ entry_index:4 ] ] // [ ... ] // entries: // [ ... ] // [ [ prefix_bitfield:4 ][ ephemkey:33 ][ address:21 ][ tx_id:32 ] ] // [ ... ] BITCOIN_ASSERT(file_.size() > metadata_size); // Read off metadata. auto deserial = make_deserializer( file_.data(), file_.data() + metadata_size); version_ = deserial.read_4_bytes(); max_header_rows_ = deserial.read_4_bytes(); entries_count_ = deserial.read_4_bytes(); BITCOIN_ASSERT(version_ == 1); // Calculate sector offsets. header_sector_ = metadata_size; entries_sector_ = header_sector_ + max_header_rows_ * 4; reset(); }
uint64_t hashtable_database_writer::read_bucket_value(uint64_t bucket_index) { BITCOIN_ASSERT(file_.size() > 24 + buckets_ * 8); BITCOIN_ASSERT(bucket_index < buckets_); uint8_t* bucket_begin = file_.data() + bucket_offset(bucket_index); // Read current record stored in the bucket. auto deserial = make_deserializer(bucket_begin, bucket_begin + 8); return deserial.read_8_bytes(); }
uint32_t stealth_database::read_start_entry_index(uint32_t from_height) { const uint32_t interval = from_height; BITCOIN_ASSERT(interval < max_header_rows_); uint64_t offset = header_sector_ + interval * 4; uint8_t* iter = file_.data() + offset; auto deserial = make_deserializer(iter, iter + 4); return deserial.read_4_bytes(); }
stealth_bitfield calculate_bitfield(const data_chunk& stealth_data) { // Calculate stealth bitfield const hash_digest index = generate_sha256_hash(stealth_data); auto deserial = make_deserializer( index.begin(), index.begin() + bitfield_size); stealth_bitfield bitfield = deserial.read_uint_auto<stealth_bitfield>(); return bitfield; }
void receive_transaction_result(const data_chunk& data, blockchain::fetch_handler_transaction handle_fetch) { std::error_code ec; auto deserial = make_deserializer(data.begin(), data.end()); if (!read_error_code(deserial, data.size(), ec)) return; BITCOIN_ASSERT(deserial.iterator() == data.begin() + 4); transaction_type tx; satoshi_load(deserial.iterator(), data.end(), tx); handle_fetch(ec, tx); }
big_number leveldb_chain_keeper::end_slice_difficulty(size_t slice_begin_index) { big_number total_work = 0; leveldb_iterator it(db_.block->NewIterator(leveldb::ReadOptions())); data_chunk raw_depth = uncast_type(slice_begin_index); for (it->Seek(slice(raw_depth)); it->Valid(); it->Next()) { constexpr size_t bits_offset = 4 + 2 * hash_digest_size + 4; BITCOIN_ASSERT(it->value().size() >= 84); // Deserialize only the bits field of block header. std::string raw_bits(it->value().data(), 4); auto deserial = make_deserializer(raw_bits.begin(), raw_bits.end()); uint32_t bits = deserial.read_4_bytes(); // Accumulate the total work. total_work += block_work(bits); } return total_work; }
bool unwrap(uint8_t& version, data_chunk& payload, uint32_t& checksum, data_slice wrapped) { constexpr size_t version_length = sizeof(version); constexpr size_t checksum_length = sizeof(checksum); // guard against insufficient buffer length if (wrapped.size() < version_length + checksum_length) return false; if (!verify_checksum(wrapped)) return false; // set return values version = wrapped.data()[0]; payload = data_chunk(wrapped.begin() + version_length, wrapped.end() - checksum_length); const auto checksum_start = wrapped.end() - checksum_length; auto deserial = make_deserializer(checksum_start, wrapped.end()); checksum = deserial.read_4_bytes(); return true; }
bool leveldb_common::fetch_spend(const output_point& spent_output, input_point& input_spend) { data_chunk spent_key = create_spent_key(spent_output); std::string raw_spend; leveldb::Status status = db_.spend->Get( leveldb::ReadOptions(), slice(spent_key), &raw_spend); if (status.IsNotFound()) return false; else if (!status.ok()) { log_fatal(LOG_BLOCKCHAIN) << "fetch_spend: " << status.ToString(); return false; } const data_chunk raw_spend_data(raw_spend.begin(), raw_spend.end()); auto deserial = make_deserializer( raw_spend_data.begin(), raw_spend_data.end()); input_spend.hash = deserial.read_hash(); input_spend.index = deserial.read_4_bytes(); return true; }
bool leveldb_common::deserialize_block(leveldb_block_info& blk_info, const std::string& raw_data, bool read_header, bool read_tx_hashes) { // Read the header (if neccessary). // There is always at least one tx in a block. BITCOIN_ASSERT(raw_data.size() >= 80 + 4 + hash_digest_size); BITCOIN_ASSERT((raw_data.size() - 84) % hash_digest_size == 0); if (read_header) satoshi_load(raw_data.begin(), raw_data.begin() + 80, blk_info.header); if (!read_tx_hashes) return true; // Read the tx hashes for this block (if neccessary). auto deserial = make_deserializer(raw_data.begin() + 80, raw_data.end()); uint32_t tx_count = deserial.read_4_bytes(); for (size_t i = 0; i < tx_count; ++i) { const hash_digest& tx_hash = deserial.read_hash(); blk_info.tx_hashes.push_back(tx_hash); } return true; }
bool leveldb_common::get_transaction(leveldb_tx_info& tx_info, const hash_digest& tx_hash, bool read_parent, bool read_tx) { // First we try to read the bytes from the database. std::string value; leveldb::Status status = db_.tx->Get( leveldb::ReadOptions(), slice(tx_hash), &value); if (status.IsNotFound()) return false; else if (!status.ok()) { log_fatal(LOG_BLOCKCHAIN) << "get_transaction(" << tx_hash << "): " << status.ToString(); return false; } // Read the parent block height and our index in that block (if neccessary). BITCOIN_ASSERT(value.size() > 8); if (read_parent) { auto deserial = make_deserializer(value.begin(), value.begin() + 8); tx_info.height = deserial.read_4_bytes(); tx_info.index = deserial.read_4_bytes(); } if (!read_tx) return true; // Read the actual transaction (if neccessary). try { BITCOIN_ASSERT(value.size() > 8); satoshi_load(value.begin() + 8, value.end(), tx_info.tx); } catch (end_of_stream) { return false; } BITCOIN_ASSERT(satoshi_raw_size(tx_info.tx) + 8 == value.size()); BITCOIN_ASSERT(hash_transaction(tx_info.tx) == tx_hash); return true; }
const hashtable_database_reader::get_result hashtable_database_reader::get( const hash_digest& key_hash) const { uint64_t bucket_index = remainder(key_hash.data(), writer_.buckets()); BITCOIN_ASSERT(bucket_index < writer_.buckets()); uint64_t record_offset = read_record_offset(file_.data(), bucket_index); const uint64_t header_size = 24 + writer_.buckets() * 8; const uint8_t* all_records_begin = file_.data() + header_size; const uint8_t* all_records_end = all_records_begin + writer_.records_size(); const uint8_t* record_begin = all_records_begin + record_offset; // We don't know the end of a record, so we use the end of all records // for the deserializer. // We will be jumping around the records since it's a chained // list per bucket. // Begin iterating the list. while (true) { auto deserial = make_deserializer(record_begin, all_records_end); const hash_digest current_hash = deserial.read_hash(); uint64_t value_size = deserial.read_variable_uint(); if (current_hash != key_hash) { // Move to next record in bucket. // Skip the transaction data. deserial.set_iterator(deserial.iterator() + value_size); uint64_t next_record = deserial.read_8_bytes(); if (next_record == record_doesnt_exist) return {nullptr, nullptr}; record_begin = all_records_begin + next_record; continue; } // We have the record! return {deserial.iterator(), deserial.iterator() + value_size}; } BITCOIN_ASSERT_MSG(false, "Broke out of unbreakable loop!"); return {nullptr, nullptr}; }
bool hd_public_key::set_encoded(const std::string& encoded) { data_chunk decoded; if (!decode_base58(decoded, encoded)) return false; if (decoded.size() != serialized_length) return false; if (!verify_checksum(decoded)) return false; auto ds = make_deserializer(decoded.begin(), decoded.end()); auto prefix = ds.read_big_endian<uint32_t>(); if (prefix != mainnet_public_prefix && prefix != testnet_public_prefix) return false; valid_ = true; lineage_.testnet = prefix == testnet_public_prefix; lineage_.depth = ds.read_byte(); lineage_.parent_fingerprint = ds.read_little_endian<uint32_t>(); lineage_.child_number = ds.read_big_endian<uint32_t>(); c_ = ds.read_bytes<chain_code_size>(); K_ = ds.read_data(33); return true; }
uint64_t read_record_offset(const uint8_t* data, uint64_t bucket_index) { const uint8_t* bucket_begin = data + bucket_offset(bucket_index); auto deserial = make_deserializer(bucket_begin, bucket_begin + 8); return deserial.read_8_bytes(); }