#include <boost/python.hpp>
namespace python = boost::python;
-#include "/home/genjix/python-bitcoin/src/primitive.h"
+#include "memory_buffer.hpp"
namespace ph = std::placeholders;
bool is_input;
// set to empty if unused
data_chunk raw_output_script;
+
+ // Convenient storage used by pool txs
+ message::output_point previous_output;
};
typedef std::shared_ptr<info_unit> info_unit_ptr;
+typedef std::vector<info_unit_ptr> info_unit_list;
bool inputs_all_loaded(info_unit::string_list inputs)
{
typedef std::vector<payment_entry_ptr> statement_entry;
typedef std::function<
- void (const std::error_code&, const statement_entry&)> finish_handler;
+ void (const std::error_code&, const statement_entry&,
+ const info_unit_list&)> finish_handler;
class query_history
: public std::enable_shared_from_this<query_history>
{
public:
- query_history(async_service& service,
- blockchain_ptr chain, transaction_pool_ptr txpool)
+ query_history(async_service& service, blockchain_ptr chain,
+ transaction_pool_ptr txpool, memory_buffer_ptr membuf)
: strand_(service.get_service()), chain_(chain),
- txpool_(txpool), stopped_(false)
+ txpool_(txpool), membuf_(membuf), stopped_(false)
{
}
void start(const std::string& address, finish_handler handle_finish)
{
- address_.set_encoded(address);
+ if (!address_.set_encoded(address))
+ {
+ handle_finish(make_error_code(std::errc::address_not_available),
+ statement_entry(), info_unit_list());
+ return;
+ }
handle_finish_ = handle_finish;
chain_->fetch_outputs(address_,
- strand_.wrap(std::bind(&query_history::start_loading,
+ strand_.wrap(std::bind(&query_history::check_membuf,
shared_from_this(), ph::_1, ph::_2)));
}
{
if (ec)
{
- handle_finish_(ec, statement_entry());
+ handle_finish_(ec, statement_entry(), info_unit_list());
stop();
}
return stopped_;
}
+ void check_membuf(const std::error_code& ec,
+ const message::output_point_list& outpoints)
+ {
+ if (stop_on_error(ec))
+ return;
+ membuf_->check(outpoints, address_,
+ strand_.wrap(std::bind(&query_history::start_loading,
+ shared_from_this(), ph::_1, ph::_2, outpoints)));
+ }
+
void start_loading(const std::error_code& ec,
+ const memory_buffer::check_result& membuf_result,
const message::output_point_list& outpoints)
{
if (stop_on_error(ec))
return;
+ else if (outpoints.empty() && membuf_result.empty())
+ {
+ handle_finish_(std::error_code(),
+ statement_entry(), info_unit_list());
+ stop();
+ return;
+ }
for (auto outpoint: outpoints)
{
auto entry = std::make_shared<payment_entry>();
shared_from_this(), ph::_1, ph::_2, entry)));
load_tx_info(outpoint, entry, false);
}
+ for (const auto& item: membuf_result)
+ {
+ auto info = std::make_shared<info_unit>();
+ info->tx_hash = std::move(item.tx_hash);
+ info->index = item.index;
+ info->is_input = item.is_input;
+ info->timestamp = item.timestamp;
+ membuf_result_.push_back(info);
+ txpool_->fetch(item.tx_hash,
+ strand_.wrap(std::bind(&query_history::load_pool_tx,
+ shared_from_this(), ph::_1, ph::_2, info)));
+ }
}
void load_spend(const std::error_code& ec,
}
}
+ bool find_set_value_from_prevout(info_unit_ptr info)
+ {
+ const hash_digest& prev_hash = info->previous_output.hash;
+ for (auto entry: statement_)
+ {
+ BITCOIN_ASSERT(entry->loaded_output);
+ if (entry->loaded_output->tx_hash == prev_hash)
+ {
+ info->value = entry->loaded_output->value;
+ return true;
+ }
+ else if (entry->input_exists &&
+ entry->loaded_input->tx_hash == prev_hash)
+ {
+ info->value = entry->loaded_output->value;
+ return true;
+ }
+ }
+ for (auto other_info: membuf_result_)
+ if (other_info->tx_hash == prev_hash)
+ {
+ info->value = other_info->value;
+ return true;
+ }
+ return false;
+ }
+
void finish_if_done()
{
for (auto entry: statement_)
BITCOIN_ASSERT(entry->loaded_input && entry->loaded_output);
// value of the input is simply the inverse of
// the corresponding output
- entry->loaded_input->value = -entry->loaded_output->value;
+ entry->loaded_input->value = entry->loaded_output->value;
// Unspent outputs have a raw_output_script field
// Blank this field as it isn't used
entry->loaded_output->raw_output_script.clear();
// Unspent outputs have a raw_output_script field
}
}
- handle_finish_(std::error_code(), statement_);
+ for (auto info: membuf_result_)
+ {
+ // Lookup prevout to set the value field
+ if (info->is_input)
+ {
+ bool set_prevout_value = find_set_value_from_prevout(info);
+ BITCOIN_ASSERT(set_prevout_value);
+ }
+ }
+ handle_finish_(std::error_code(), statement_, membuf_result_);
stop();
}
}
}
+ void load_pool_tx(const std::error_code& ec,
+ const message::transaction& tx, info_unit_ptr info)
+ {
+ if (stop_on_error(ec))
+ return;
+ // block_hash = mempool:5
+ // inputs (load from prevtx)
+ // outputs (load from tx)
+ // raw_output_script (load from tx)
+ // height is always None
+ // value (get from finish_if_done)
+ load_tx(tx, info);
+ if (info->is_input)
+ {
+ BITCOIN_ASSERT(info->index < tx.inputs.size());
+ info->previous_output = tx.inputs[info->index].previous_output;
+ }
+ else
+ {
+ const auto& our_output = tx.outputs[info->index];
+ info->value = our_output.value;
+ info->raw_output_script = save_script(our_output.output_script);
+ }
+ // If all the inputs are loaded
+ if (inputs_all_loaded(info->inputs))
+ {
+ // We are the sole input
+ BITCOIN_ASSERT(info->is_input);
+ // No more inputs left to load
+ // This info has finished loading
+ info->height = 0;
+ info->block_hash = null_hash;
+ finish_if_done();
+ }
+
+ // *********
+ // fetch_input_txs
+
+ // Load the previous_output for every input so we can get
+ // the output address
+ for (size_t input_index = 0;
+ input_index < tx.inputs.size(); ++input_index)
+ {
+ if (info->is_input && info->index == input_index)
+ continue;
+ const auto& prevout = tx.inputs[input_index].previous_output;
+ chain_->fetch_transaction(prevout.hash,
+ strand_.wrap(std::bind(&query_history::load_input_pool_tx,
+ shared_from_this(), ph::_1, ph::_2, prevout.index,
+ info, input_index)));
+ }
+ }
+
void load_input_tx(const message::transaction& tx, size_t output_index,
info_unit_ptr info, size_t input_index)
{
finish_if_done();
}
+ void load_input_pool_tx(const std::error_code& ec,
+ const message::transaction& tx, size_t output_index,
+ info_unit_ptr info, size_t input_index)
+ {
+ if (stop_on_error(ec))
+ return;
+ load_input_tx(tx, output_index, info, input_index);
+ if (inputs_all_loaded(info->inputs))
+ {
+ // No more inputs left to load
+ // This info has finished loading
+ info->height = 0;
+ info->block_hash = null_hash;
+ }
+ finish_if_done();
+ }
+
io_service::strand strand_;
blockchain_ptr chain_;
transaction_pool_ptr txpool_;
+ memory_buffer_ptr membuf_;
bool stopped_;
statement_entry statement_;
+ info_unit_list membuf_result_;
payment_address address_;
finish_handler handle_finish_;
};
std::stringstream ss;
ss << "{"
<< "\"tx_hash\": \"" << pretty_hex(info->tx_hash) << "\","
- << "\"block_hash\": \"" << pretty_hex(info->block_hash) << "\","
<< "\"index\": " << info->index << ","
- << "\"value\": " << info->value << ","
- << "\"height\": " << info->height << ","
+ // x for received, and -x for sent amounts
+ << "\"value\": " << (info->is_input ? "-" : "") << info->value << ","
<< "\"timestamp\": " << info->timestamp << ","
<< "\"is_input\": " << info->is_input << ",";
+ if (info->height == 0 && info->block_hash == null_hash)
+ {
+ ss << "\"block_hash\": \"mempool\","
+ << "\"height\": null,";
+ }
+ else
+ {
+ ss << "\"block_hash\": \"" << pretty_hex(info->block_hash) << "\","
+ << "\"height\": " << info->height << ",";
+ }
write_xputs_strings(ss, "inputs", info->inputs);
ss << ",";
write_xputs_strings(ss, "outputs", info->outputs);
}
void keep_query_alive_proxy(const std::error_code& ec,
- const statement_entry& statement,
+ const statement_entry& statement, const info_unit_list& membuf_result,
python::object handle_finish, query_history_ptr history)
{
std::string json = "[";
auto entry = *it;
BITCOIN_ASSERT(entry->loaded_output);
write_info(json, entry->loaded_output);
+ if (entry->input_exists)
+ {
+ BITCOIN_ASSERT(entry->loaded_input);
+ json += ",";
+ write_info(json, entry->loaded_input);
+ }
+ }
+ // A bit of super glue
+ if (!statement.empty() && !membuf_result.empty())
+ json += ",";
+ for (auto it = membuf_result.begin(); it != membuf_result.end(); ++it)
+ {
+ if (it != membuf_result.begin())
+ json += ",";
+ write_info(json, *it);
}
json += "]";
pyfunction<const std::error_code&, const std::string&> f(handle_finish);
}
void payment_history(async_service_ptr service, blockchain_ptr chain,
- transaction_pool_ptr txpool, const std::string& address,
- python::object handle_finish)
+ transaction_pool_ptr txpool, memory_buffer_ptr membuf,
+ const std::string& address, python::object handle_finish)
{
query_history_ptr history =
- std::make_shared<query_history>(*service, chain, txpool);
+ std::make_shared<query_history>(*service, chain, txpool, membuf);
history->start(address,
- std::bind(keep_query_alive_proxy, ph::_1, ph::_2,
+ std::bind(keep_query_alive_proxy, ph::_1, ph::_2, ph::_3,
handle_finish, history));
}