void bdb_blockchain::do_fetch_outputs(const payment_address& address, fetch_handler_outputs handle_fetch) { // Associated outputs message::output_point_list assoc_outs; txn_guard_ptr txn = std::make_shared<txn_guard>(env_); Dbc* cursor; db_address_->cursor(txn->get(), &cursor, 0); BITCOIN_ASSERT(cursor != nullptr); readable_data_type key; // version byte + hash for key serializer serial; serial.write_byte(address.version()); serial.write_short_hash(address.hash()); key.set(serial.data()); writable_data_type value; int ret = cursor->get(key.get(), value.get(), DB_SET); while (ret != DB_NOTFOUND) { message::output_point outpoint; // We need a copy not a temporary data_chunk raw_outpoint(value.data()); // Then read the value off deserializer deserial(raw_outpoint); outpoint.hash = deserial.read_hash(); outpoint.index = deserial.read_4_bytes(); assoc_outs.push_back(outpoint); ret = cursor->get(key.get(), value.get(), DB_NEXT_DUP); } cursor->close(); txn->commit(); handle_fetch(std::error_code(), assoc_outs); }
void subscribe_manager::post_updates(const payment_address& address, size_t height, const bc::hash_digest& block_hash, const bc::transaction_type& tx) { auto range = subs_.equal_range(address); // Avoid expensive serialization if not needed. if (range.first == range.second) return; // [ addr,version ] (1 byte) // [ addr.hash ] (20 bytes) // [ height ] (4 bytes) // [ block_hash ] (32 bytes) // [ tx ] constexpr size_t info_size = 1 + short_hash_size + 4 + hash_size; data_chunk data(info_size + satoshi_raw_size(tx)); auto serial = make_serializer(data.begin()); serial.write_byte(address.version()); serial.write_short_hash(address.hash()); serial.write_4_bytes(height); serial.write_hash(block_hash); BITCOIN_ASSERT(serial.iterator() == data.begin() + info_size); // Now write the tx part. auto rawtx_end_it = satoshi_save(tx, serial.iterator()); BITCOIN_ASSERT(rawtx_end_it == data.end()); // Send the result to everyone interested. for (auto it = range.first; it != range.second; ++it) { const subscription& sub_detail = it->second; outgoing_message update( sub_detail.client_origin, "address.update", data); sub_detail.queue_send(update); } }
void set_raw_address(const payment_address& address) { BITCOIN_ASSERT(raw_address_.size() == 1 + short_hash_size); auto serial = make_serializer(raw_address_.begin()); serial.write_byte(address.version()); serial.write_short_hash(address.hash()); BITCOIN_ASSERT( std::distance(raw_address_.begin(), serial.iterator()) == 1 + short_hash_size); }
void wrap_fetch_history_args(data_chunk& data, const payment_address& address, size_t from_height) { data.resize(1 + short_hash_size + 4); auto serial = make_serializer(data.begin()); serial.write_byte(address.version()); serial.write_short_hash(address.hash()); serial.write_4_bytes(from_height); BITCOIN_ASSERT(serial.iterator() == data.end()); }
data_chunk create_address_key( const payment_address& address, const output_point& outpoint) { data_chunk result(1 + short_hash_size + 8); auto serial = make_serializer(result.begin()); serial.write_byte(address.version()); serial.write_short_hash(address.hash()); serial.write_8_bytes(addr_key_checksum(outpoint)); BITCOIN_ASSERT(serial.iterator() == result.end()); return result; }
void address_subscriber::subscribe(const payment_address& address, update_handler handle_update, subscribe_handler handle_subscribe) { data_chunk data(1 + short_hash_size); auto serial = make_serializer(data.begin()); serial.write_byte(address.version()); serial.write_short_hash(address.hash()); BITCOIN_ASSERT(serial.iterator() == data.end()); backend_.request("address.subscribe", data, strand_.wrap(&address_subscriber::receive_subscribe_result, this, _1, _2, address, handle_update, handle_subscribe)); }
BC_API void obelisk_codec::address_fetch_history(error_handler&& on_error, fetch_history_handler&& on_reply, const payment_address& address, size_t from_height) { auto data = build_data({ to_byte(address.version()), reverse(address.hash()), to_little_endian<uint32_t>(from_height) }); send_request("address.fetch_history", data, std::move(on_error), std::bind(decode_fetch_history, _1, std::move(on_reply))); }
bool build_output_script( script_type& out_script, const payment_address& payaddr) { switch (payaddr.version()) { case payment_address::pubkey_version: out_script = build_pubkey_hash_script(payaddr.hash()); return true; case payment_address::script_version: out_script = build_script_hash_script(payaddr.hash()); return true; } return false; }
static void display_history(const std::error_code& code, const history_list& history, const payment_address& address, std::ostream& output) { const auto encoded_address = address.encoded(); if (code) { output << format(BN_FETCH_HISTORY_FAIL) % encoded_address % code.message(); return; } output << format(BN_FETCH_HISTORY_SUCCESS) % encoded_address; for (const auto& row: history) { const auto hash = bc::encode_hash(row.point.hash); if (row.id == point_ident::output) output << format(BN_FETCH_HISTORY_OUTPUT) % hash % row.point.index % row.height % row.value; else output << format(BN_FETCH_HISTORY_INPUT) % hash % row.point.index % row.height % row.value; } }
void add_stealth_info(const data_chunk& stealth_data, const payment_address& address, const hash_digest& tx_hash, stealth_database& db) { const stealth_bitfield bitfield = calculate_bitfield(stealth_data); const data_chunk ephemkey = read_ephemkey(stealth_data); auto write_func = [&](uint8_t *it) { auto serial = make_serializer(it); serial.write_uint_auto(bitfield); serial.write_data(ephemkey); serial.write_byte(address.version()); serial.write_short_hash(address.hash()); serial.write_hash(tx_hash); BITCOIN_ASSERT(serial.iterator() == it + bitfield_size + 33 + 21 + 32); }; db.store(write_func); }
bool payaddr_from_stdin(payment_address& payaddr) { if (!payaddr.set_encoded(read_stdin())) { std::cerr << "balance: Invalid address." << std::endl; return false; } return true; }
void bdb_blockchain::fetch_outputs(const payment_address& address, fetch_handler_outputs handle_fetch) { if (address.type() != payment_type::pubkey_hash) handle_fetch(error::unsupported_payment_type, message::output_point_list()); else queue( std::bind(&bdb_blockchain::do_fetch_outputs, this, address, handle_fetch)); }
void json_history_fetched(const payment_address& payaddr, const std::error_code& ec, const blockchain::history_list& history) { if (ec) { std::cerr << "history: Failed to fetch history: " << ec.message() << std::endl; return; } bool is_first = true; for (const auto& row: history) { // Put commas between each array item in json output. if (is_first) is_first = false; else std::cout << "," << std::endl; // Actual row data. std::cout << "{" << std::endl; std::cout << " \"address\": \"" << payaddr.encoded() << "\"," << std::endl; std::cout << " \"output\": \"" << row.output << "\"," << std::endl; std::cout << " \"output_height\": "; if (!row.output_height) std::cout << "\"Pending\""; else std::cout << row.output_height; std::cout << "," << std::endl; std::cout << " \"value\": \"" << row.value << "\"," << std::endl; if (row.spend.hash == null_hash) { std::cout << " \"spend\": \"Unspent\"," << std::endl; std::cout << " \"spend_height\": \"Unspent\"" << std::endl; } else { std::cout << " \"spend\": \"" << row.spend << "\"," << std::endl; std::cout << " \"spend_height\": "; if (!row.spend_height) std::cout << "\"Pending\""; else std::cout << "\"" << row.spend_height << "\""; } std::cout << "}"; } std::lock_guard<std::mutex> lock(mutex); BITCOIN_ASSERT(remaining_count != std::numeric_limits<int>::max()); --remaining_count; condition.notify_one(); if (remaining_count > 0) std::cout << ","; std::cout << std::endl; }
void blockchain_impl::fetch_history(const payment_address& address, fetch_handler_history handle_fetch, const uint64_t limit, const uint64_t from_height) { const auto do_fetch = [=](size_t slock) { const auto history = interface_.history.get( address.hash(), limit, from_height); return finish_fetch(slock, handle_fetch, std::error_code(), history); }; fetch(do_fetch); }
void json_history_fetched(const payment_address& payaddr, const std::error_code& ec, const blockchain::history_list& history) { if (ec) { std::cerr << "balance: Failed to fetch history: " << ec.message() << std::endl; return; } bool is_first = true; uint64_t total_recv = 0, balance = 0, pending_balance = 0; for (const auto& row: history) { uint64_t value = row.value; BITCOIN_ASSERT(value >= 0); total_recv += value; // Unconfirmed balance. if (row.spend.hash == null_hash) { pending_balance += value; } // Confirmed balance. if (row.output_height && (row.spend.hash == null_hash || !row.spend_height)) { balance += value; } BITCOIN_ASSERT(total_recv >= balance); BITCOIN_ASSERT(total_recv >= pending_balance); } // Put commas between each array item in json output. if (is_first) is_first = false; else std::cout << "," << std::endl; // Actual row data. std::cout << "{" << std::endl; std::cout << " \"address\": \"" << payaddr.encoded() << "\"," << std::endl; std::cout << " \"paid\": \"" << balance << "\"," << std::endl; std::cout << " \"pending\": \"" << pending_balance << "\"," << std::endl; std::cout << " \"received\": \"" << total_recv << "\"" << std::endl; std::cout << "}"; std::lock_guard<std::mutex> lock(mutex); BITCOIN_ASSERT(remaining_count != std::numeric_limits<int>::max()); --remaining_count; condition.notify_one(); if (remaining_count > 0) std::cout << ","; std::cout << std::endl; }
bool extract(payment_address& address, const script_type& script) { // Cast a data_chunk to a short_hash and set the address auto set_hash_data = [&address](uint8_t version, const data_chunk& raw_hash) { short_hash hash_data; BITCOIN_ASSERT(raw_hash.size() == hash_data.size()); std::copy(raw_hash.begin(), raw_hash.end(), hash_data.begin()); address.set(version, hash_data); }; const operation_stack& ops = script.operations(); payment_type pay_type = script.type(); switch (pay_type) { case payment_type::pubkey: BITCOIN_ASSERT(ops.size() == 2); set_public_key(address, ops[0].data); return true; case payment_type::pubkey_hash: BITCOIN_ASSERT(ops.size() == 5); set_hash_data(payment_address::pubkey_version, ops[2].data); return true; case payment_type::script_hash: BITCOIN_ASSERT(ops.size() == 3); set_hash_data(payment_address::script_version, ops[1].data); return true; case payment_type::multisig: // Unimplemented... return false; case payment_type::pubkey_hash_sig: BITCOIN_ASSERT(ops.size() == 2); set_public_key(address, ops[1].data); return true; case payment_type::script_code_sig: // Should have at least 1 sig and the script code. BITCOIN_ASSERT(ops.size() > 1); set_script_hash(address, bitcoin_short_hash(ops.back().data)); return true; default: return false; } // Should never happen! return false; }
bool deserialize_address(payment_address& addr, const data_chunk& data) { auto deserial = make_deserializer(data.begin(), data.end()); try { uint8_t version_byte = deserial.read_byte(); short_hash hash = deserial.read_short_hash(); addr.set(version_byte, hash); } catch (end_of_stream) { return false; } if (deserial.iterator() != data.end()) return false; return true; }
bool unwrap_fetch_history_args( payment_address& payaddr, uint32_t& from_height, const incoming_message& request) { const data_chunk& data = request.data(); if (data.size() != 1 + short_hash_size + 4) { log_error(LOG_WORKER) << "Incorrect data size for .fetch_history"; return false; } auto deserial = make_deserializer(data.begin(), data.end()); uint8_t version_byte = deserial.read_byte(); short_hash hash = deserial.read_short_hash(); from_height = deserial.read_4_bytes(); BITCOIN_ASSERT(deserial.iterator() == data.end()); payaddr.set(version_byte, hash); return true; }
void history_fetched(const payment_address& payaddr, const std::error_code& ec, const blockchain::history_list& history) { if (ec) { std::cerr << "history: Failed to fetch history: " << ec.message() << std::endl; return; } for (const auto& row: history) { std::cout << "Address: " << payaddr.encoded() << std::endl; std::cout << " output: " << row.output << std::endl; std::cout << " output_height: "; if (!row.output_height) std::cout << "Pending"; else std::cout << row.output_height; std::cout << std::endl; std::cout << " value: " << row.value << std::endl; if (row.spend.hash == null_hash) { std::cout << " spend: Unspent" << std::endl; std::cout << " spend_height: Unspent" << std::endl; } else { std::cout << " spend: " << row.spend << std::endl; std::cout << " spend_height: "; if (!row.spend_height) std::cout << "Pending"; else std::cout << row.spend_height; std::cout << std::endl; } std::cout << std::endl; } std::lock_guard<std::mutex> lock(mutex); BITCOIN_ASSERT(remaining_count != std::numeric_limits<int>::max()); --remaining_count; condition.notify_one(); }
void history_fetched(const payment_address& payaddr, const std::error_code& ec, const blockchain::history_list& history) { if (ec) { std::cerr << "history: Failed to fetch history: " << ec.message() << std::endl; return; } for (const auto& row: history) { std::cout << "Address: " << payaddr.encoded() << std::endl; std::cout << " output: " << row.output << std::endl; std::cout << " output_height: "; if (!row.output_height) std::cout << "Pending"; else std::cout << row.output_height; std::cout << std::endl; std::cout << " value: " << row.value << std::endl; if (row.spend.hash == null_hash) { std::cout << " spend: Unspent" << std::endl; std::cout << " spend_height: Unspent" << std::endl; } else { std::cout << " spend: " << row.spend << std::endl; std::cout << " spend_height: "; if (!row.spend_height) std::cout << "Pending"; else std::cout << row.spend_height; std::cout << std::endl; } std::cout << std::endl; } --remaining_count; }
void history_fetched(const payment_address& payaddr, const std::error_code& ec, const blockchain::history_list& history) { if (ec) { std::cerr << "balance: Failed to fetch history: " << ec.message() << std::endl; return; } uint64_t total_recv = 0, balance = 0, pending_balance = 0; for (const auto& row: history) { uint64_t value = row.value; BITCOIN_ASSERT(value >= 0); total_recv += value; // Unconfirmed balance. if (row.spend.hash == null_hash) { pending_balance += value; } // Confirmed balance. if (row.output_height && (row.spend.hash == null_hash || !row.spend_height)) { balance += value; } BITCOIN_ASSERT(total_recv >= balance); BITCOIN_ASSERT(total_recv >= pending_balance); } std::cout << "Address: " << payaddr.encoded() << std::endl; std::cout << " Paid balance: " << balance << std::endl; std::cout << " Pending balance: " << pending_balance << std::endl; std::cout << " Total received: " << total_recv << std::endl; std::cout << std::endl; std::lock_guard<std::mutex> lock(mutex); BITCOIN_ASSERT(remaining_count != std::numeric_limits<int>::max()); --remaining_count; condition.notify_one(); }
static hash_digest address_hash(const payment_address& address) { return bitcoin_hash(to_chunk(address.encoded())); }
bool set_public_key(payment_address& address, const data_chunk& public_key) { return address.set(payment_type::pubkey_hash, generate_ripemd_hash(public_key)); }
bool set_script_hash(payment_address& address, const short_hash& script_hash) { return address.set(payment_type::script_hash, script_hash); }
bool set_script(payment_address& address, const script& eval_script) { return address.set(payment_type::script_hash, generate_ripemd_hash(save_script(eval_script))); }
void set_script(payment_address& address, const script_type& eval_script) { address.set(payment_address::script_version, bitcoin_short_hash(save_script(eval_script))); }
void set_public_key(payment_address& address, const data_chunk& public_key) { address.set(payment_address::pubkey_version, bitcoin_short_hash(public_key)); }
void set_script_hash(payment_address& address, const short_hash& script_hash) { address.set(payment_address::script_version, script_hash); }
void set_public_key_hash(payment_address& address, const short_hash& pubkey_hash) { address.set(payment_address::pubkey_version, pubkey_hash); }
bool operator==(const payment_address& lhs, const payment_address& rhs) { return lhs.hash() == rhs.hash() && lhs.version() == rhs.version(); }