summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorevoskuil <eric@voskuil.org>2014-04-08 06:38:42 (GMT)
committer evoskuil <eric@voskuil.org>2014-04-08 06:38:42 (GMT)
commit8e843a3e40ac94c71ec4b8a72c95ce036c104193 (patch)
tree3142604dca02d90f782ca96105028b80c257d7ed
parent8de24f9f66c8f922e8c1fcabcdfca6aae70983e7 (diff)
parent48fd8ffedf732c56a44e85d938e90c6a81a0b812 (diff)
Merge branch 'spesmilo/master'
-rw-r--r--AUTHORS6
-rw-r--r--README10
-rw-r--r--doc/sphinx/index.rst3
-rw-r--r--include/bitcoin/Makefile.am4
-rw-r--r--include/bitcoin/blockchain/blockchain.hpp10
-rw-r--r--include/bitcoin/impl/utility/serializer.ipp359
-rw-r--r--include/bitcoin/stealth.hpp4
-rw-r--r--include/bitcoin/utility/serializer.hpp324
-rw-r--r--src/block.cpp2
-rw-r--r--src/blockchain/leveldb/leveldb_blockchain.cpp8
-rw-r--r--src/blockchain/leveldb/leveldb_common.cpp21
-rw-r--r--src/blockchain/leveldb/leveldb_validate_block.cpp5
-rw-r--r--src/network/handshake.cpp8
-rw-r--r--src/network/protocol.cpp4
-rw-r--r--src/script.cpp5
-rw-r--r--src/utility/big_number.cpp81
-rw-r--r--src/utility/mmfile.cpp4
-rw-r--r--tools/verifier/README14
-rw-r--r--tools/verifier/check_binfo_json.py19
-rw-r--r--tools/verifier/cmp_binfo.py104
-rw-r--r--tools/verifier/cmp_history.py74
-rw-r--r--tools/verifier/fetch-history.cpp83
-rw-r--r--tools/verifier/final_check.py21
-rw-r--r--tools/verifier/query_binfo.py37
24 files changed, 919 insertions, 291 deletions
diff --git a/AUTHORS b/AUTHORS
index e9fa421..eae072d 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -3,6 +3,12 @@ Authors:
* Robert Williamson
Development. Added support for testnet.
+* Eric Voskuil
+ Windows port and code cleanup.
+
+* William Swanson
+ Android port and wallet code development.
+
* Amir Taaki (genjix)
Development
amir@unsystem.net
diff --git a/README b/README
index 5f80161..c0d340c 100644
--- a/README
+++ b/README
@@ -99,6 +99,16 @@ Arch Linux Instructions
Libbitcoin is available from the AUR: https://aur.archlinux.org/packages/libbitcoin-leveldb-git/
+=======================
+Gentoo Linux Instructions
+=======================
+
+Libbitcoin is available in the 'bitcoin' overlay. Use layman to fetch
+the overlay then emerge as usual.
+
+If you want to use live build, unlock the '**' keyword for net-p2p/libbitcoin
+in /etc/portage/package.accept_keywords
+
====================
Mac OSX Instructions
====================
diff --git a/doc/sphinx/index.rst b/doc/sphinx/index.rst
index 365fd5c..bfa06a6 100644
--- a/doc/sphinx/index.rst
+++ b/doc/sphinx/index.rst
@@ -21,7 +21,7 @@ Contents:
blockchain
network
fullnode
-.. offlinetx
+ offlinetx
.. toctree::
:hidden:
@@ -30,6 +30,7 @@ Contents:
examples/determ
examples/initchain
examples/display-last
+ examples/balance
examples/satoshiwords
examples/connect
examples/accept
diff --git a/include/bitcoin/Makefile.am b/include/bitcoin/Makefile.am
index 412fb41..fb10876 100644
--- a/include/bitcoin/Makefile.am
+++ b/include/bitcoin/Makefile.am
@@ -65,3 +65,7 @@ bitcoin_util_include_HEADERS = \
utility/weak_bind.hpp \
utility/timed_section.hpp
+bitcoin_impl_utility_includedir = $(includedir)/bitcoin/impl/utility
+bitcoin_impl_utility_include_HEADERS = \
+ impl/utility/serializer.ipp
+
diff --git a/include/bitcoin/blockchain/blockchain.hpp b/include/bitcoin/blockchain/blockchain.hpp
index 9769685..7df88ac 100644
--- a/include/bitcoin/blockchain/blockchain.hpp
+++ b/include/bitcoin/blockchain/blockchain.hpp
@@ -319,6 +319,16 @@ public:
* Fetch possible stealth results. These results can then be iterated
* to discover new payments belonging to a particular stealth address.
* This is for recipient privacy.
+ * The result is a list of rows with the following fields:
+ *
+ * @code
+ * struct stealth_row
+ * {
+ * data_chunk ephemkey;
+ * payment_address address;
+ * hash_digest transaction_hash;
+ * };
+ * @endcode
*
* The prefix is a special value that can be adjusted to provide
* greater precision at the expense of deniability.
diff --git a/include/bitcoin/impl/utility/serializer.ipp b/include/bitcoin/impl/utility/serializer.ipp
new file mode 100644
index 0000000..454e97d
--- /dev/null
+++ b/include/bitcoin/impl/utility/serializer.ipp
@@ -0,0 +1,359 @@
+/*
+ * Copyright (c) 2011-2013 libbitcoin developers (see AUTHORS)
+ *
+ * This file is part of libbitcoin.
+ *
+ * libbitcoin is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License with
+ * additional permissions to the one published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option)
+ * any later version. For more information see LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef LIBBITCOIN_SERIALIZER_IPP
+#define LIBBITCOIN_SERIALIZER_IPP
+
+namespace libbitcoin {
+
+template <typename Iterator>
+serializer<Iterator>::serializer(const Iterator begin)
+ : iter_(begin)
+{
+}
+
+template <typename Iterator>
+template <typename T>
+void serializer<Iterator>::write_uint_auto(T value)
+{
+ write_data(uncast_type(value));
+}
+
+template <typename Iterator>
+void serializer<Iterator>::write_byte(uint8_t value)
+{
+ *iter_ = value;
+ ++iter_;
+}
+template <typename Iterator>
+void serializer<Iterator>::write_2_bytes(uint16_t value)
+{
+ write_uint_auto(value);
+}
+template <typename Iterator>
+void serializer<Iterator>::write_4_bytes(uint32_t value)
+{
+ write_uint_auto(value);
+}
+template <typename Iterator>
+void serializer<Iterator>::write_8_bytes(uint64_t value)
+{
+ write_uint_auto(value);
+}
+
+template <typename Iterator>
+void serializer<Iterator>::write_variable_uint(uint64_t value)
+{
+ if (value < 0xfd)
+ {
+ write_byte((uint8_t)value);
+ }
+ else if (value <= 0xffff)
+ {
+ write_byte(0xfd);
+ write_2_bytes((uint16_t)value);
+ }
+ else if (value <= 0xffffffff)
+ {
+ write_byte(0xfe);
+ write_4_bytes((uint32_t)value);
+ }
+ else
+ {
+ write_byte(0xff);
+ write_8_bytes(value);
+ }
+}
+
+template <typename Iterator>
+template <typename T>
+void serializer<Iterator>::write_data(const T& data)
+{
+ internal_copy(data.begin(), data.end());
+}
+
+template <typename Iterator>
+void serializer<Iterator>::write_network_address(network_address_type addr)
+{
+ write_8_bytes(addr.services);
+ write_data(addr.ip);
+ write_data(uncast_type(addr.port, true));
+}
+
+template <typename Iterator>
+void serializer<Iterator>::write_hash(const hash_digest& hash)
+{
+ write_data_reverse(hash);
+}
+
+template <typename Iterator>
+void serializer<Iterator>::write_short_hash(const short_hash& hash)
+{
+ write_data_reverse(hash);
+}
+
+template <typename Iterator>
+void serializer<Iterator>::write_fixed_string(
+ const std::string& command, size_t string_size)
+{
+ BITCOIN_ASSERT(command.size() <= string_size);
+ data_chunk raw_string(string_size, 0);
+ std::copy(command.begin(), command.end(), raw_string.begin());
+ write_data(raw_string);
+}
+
+template <typename Iterator>
+void serializer<Iterator>::write_string(const std::string& str)
+{
+ write_variable_uint(str.size());
+ write_data(str);
+}
+
+/**
+ * Returns underlying iterator.
+ */
+template <typename Iterator>
+Iterator serializer<Iterator>::iterator()
+{
+ return iter_;
+}
+
+/**
+ * Useful if you want to serialize some data using another
+ * routine and then continue with this serializer.
+ */
+template <typename Iterator>
+void serializer<Iterator>::set_iterator(Iterator iter)
+{
+ iter_ = iter;
+}
+
+template <typename Iterator>
+template <typename T>
+void serializer<Iterator>::write_data_reverse(const T& data)
+{
+ internal_copy(data.rbegin(), data.rend());
+}
+
+// We need to advance the internal iterator.
+// std::copy gives no info on length of the data copied.
+template <typename Iterator>
+template <typename InputIterator>
+void serializer<Iterator>::internal_copy(
+ InputIterator first, InputIterator last)
+{
+ while (first != last)
+ {
+ *iter_ = *first;
+ ++first;
+ ++iter_;
+ }
+}
+
+template <typename Iterator>
+serializer<Iterator> make_serializer(Iterator begin)
+{
+ return serializer<Iterator>(begin);
+}
+
+template <typename Iterator>
+deserializer<Iterator>::deserializer(const Iterator begin, const Iterator end)
+ : iter_(begin), end_(end)
+{
+}
+
+template <typename Iterator>
+template <typename T>
+const T deserializer<Iterator>::read_uint_auto()
+{
+ return read_data_impl<T>(iter_, end_);
+}
+
+template <typename Iterator>
+uint8_t deserializer<Iterator>::read_byte()
+{
+ check_distance(iter_, end_, 1);
+ return *(iter_++);
+}
+template <typename Iterator>
+uint16_t deserializer<Iterator>::read_2_bytes()
+{
+ return read_uint_auto<uint16_t>();
+}
+template <typename Iterator>
+uint32_t deserializer<Iterator>::read_4_bytes()
+{
+ return read_uint_auto<uint32_t>();
+}
+template <typename Iterator>
+uint64_t deserializer<Iterator>::read_8_bytes()
+{
+ return read_uint_auto<uint64_t>();
+}
+
+template <typename Iterator>
+uint64_t deserializer<Iterator>::read_variable_uint()
+{
+ uint8_t length = read_byte();
+ if (length < 0xfd)
+ return length;
+ else if (length == 0xfd)
+ return read_2_bytes();
+ else if (length == 0xfe)
+ return read_4_bytes();
+ // length should be 0xff
+ return read_8_bytes();
+}
+
+// NOTE: n_bytes changed to uint32_t to prevent array overflow.
+template <typename Iterator>
+data_chunk deserializer<Iterator>::read_data(uint32_t n_bytes)
+{
+ check_distance(iter_, end_, n_bytes);
+ data_chunk raw_bytes(n_bytes);
+ for (uint32_t i = 0; i < n_bytes; ++i)
+ raw_bytes[i] = read_byte();
+ return raw_bytes;
+}
+
+template <typename Iterator>
+network_address_type deserializer<Iterator>::read_network_address()
+{
+ network_address_type addr;
+ addr.services = read_8_bytes();
+ // Read IP address
+ read_bytes<16>(iter_, end_, addr.ip);
+ addr.port = read_data_impl<uint16_t>(iter_, end_, true);
+ return addr;
+}
+
+template <typename Iterator>
+hash_digest deserializer<Iterator>::read_hash()
+{
+ hash_digest hash;
+ read_bytes<32>(iter_, end_, hash, true);
+ return hash;
+}
+
+template <typename Iterator>
+short_hash deserializer<Iterator>::read_short_hash()
+{
+ short_hash hash;
+ read_bytes<20>(iter_, end_, hash, true);
+ return hash;
+}
+
+template <typename Iterator>
+std::string deserializer<Iterator>::read_fixed_string(size_t len)
+{
+ data_chunk string_bytes = read_data(len);
+ std::string result(string_bytes.begin(), string_bytes.end());
+ // Removes trailing 0s... Needed for string comparisons
+ return result.c_str();
+}
+
+template <typename Iterator>
+std::string deserializer<Iterator>::read_string()
+{
+ uint64_t string_size = read_variable_uint();
+ // Warning: conversion from uint64_t to size_t, possible loss of data.
+ return read_fixed_string((size_t)string_size);
+}
+
+/**
+ * Returns underlying iterator.
+ */
+template <typename Iterator>
+Iterator deserializer<Iterator>::iterator() const
+{
+ return iter_;
+}
+
+/**
+ * Useful if you advance the iterator using other serialization
+ * methods or objects.
+ */
+template <typename Iterator>
+void deserializer<Iterator>::set_iterator(const Iterator iter)
+{
+ iter_ = iter;
+}
+
+// Try to advance iterator 'distance' incremenets forwards.
+// Throw if we prematurely reach the end.
+template <typename Iterator>
+void deserializer<Iterator>::check_distance(
+ Iterator it, const Iterator end, size_t distance)
+{
+ for (size_t i = 0; i < distance; ++i)
+ {
+ // Is this a valid byte?
+ if (it == end)
+ throw end_of_stream();
+ // If so move to next value.
+ ++it;
+ }
+}
+
+template <typename Iterator>
+template <typename T>
+T deserializer<Iterator>::read_data_impl(
+ Iterator& begin, const Iterator end, bool reverse)
+{
+ check_distance(begin, end, sizeof(T));
+ data_chunk chunk(begin, begin + sizeof(T));
+ T val = cast_chunk<T>(chunk, reverse);
+ begin += sizeof(T);
+ return val;
+}
+
+template <typename Iterator>
+template <unsigned int N>
+void deserializer<Iterator>::read_bytes(
+ Iterator& begin, const Iterator& end,
+ std::array<uint8_t, N>& byte_array, bool reverse)
+{
+ check_distance(begin, end, byte_array.size());
+#ifdef BOOST_LITTLE_ENDIAN
+ // do nothing
+#elif BOOST_BIG_ENDIAN
+ reverse = !reverse;
+#else
+ #error "Endian isn't defined!"
+#endif
+
+ if (reverse)
+ std::reverse_copy(
+ begin, begin + byte_array.size(), byte_array.begin());
+ else
+ std::copy(begin, begin + byte_array.size(), byte_array.begin());
+ begin += byte_array.size();
+}
+
+template <typename Iterator>
+deserializer<Iterator> make_deserializer(
+ const Iterator begin, const Iterator end)
+{
+ return deserializer<Iterator>(begin, end);
+}
+
+} // libbitcoin
+
+#endif
+
diff --git a/include/bitcoin/stealth.hpp b/include/bitcoin/stealth.hpp
index cf51ec9..b466efd 100644
--- a/include/bitcoin/stealth.hpp
+++ b/include/bitcoin/stealth.hpp
@@ -24,10 +24,12 @@
namespace libbitcoin {
+typedef uint32_t stealth_bitfield;
+
struct stealth_prefix
{
uint8_t number_bits;
- uint32_t bitfield;
+ stealth_bitfield bitfield;
};
bool stealth_match(const stealth_prefix& prefix, const uint8_t* raw_bitfield);
diff --git a/include/bitcoin/utility/serializer.hpp b/include/bitcoin/utility/serializer.hpp
index 3855f5d..bba340c 100644
--- a/include/bitcoin/utility/serializer.hpp
+++ b/include/bitcoin/utility/serializer.hpp
@@ -52,138 +52,69 @@ template <typename Iterator>
class serializer
{
public:
- serializer(const Iterator begin)
- : iter_(begin) {}
-
- void write_byte(uint8_t value)
- {
- *iter_ = value;
- ++iter_;
- }
- void write_2_bytes(uint16_t value)
- {
- write_uint(value);
- }
- void write_4_bytes(uint32_t value)
- {
- write_uint(value);
- }
- void write_8_bytes(uint64_t value)
- {
- write_uint(value);
- }
-
- void write_variable_uint(uint64_t value)
- {
- if (value < 0xfd)
- {
- write_byte((uint8_t)value);
- }
- else if (value <= 0xffff)
- {
- write_byte(0xfd);
- write_2_bytes((uint16_t)value);
- }
- else if (value <= 0xffffffff)
- {
- write_byte(0xfe);
- write_4_bytes((uint32_t)value);
- }
- else
- {
- write_byte(0xff);
- write_8_bytes(value);
- }
- }
+ serializer(const Iterator begin);
+ /**
+ * Equivalent to calling write_byte/write_N_bytes depending
+ * on the size of the passed value.
+ * For instance a uint32_t value is equivalent to write_4_bytes(value).
+ */
template <typename T>
- void write_data(const T& data)
- {
- internal_copy(data.begin(), data.end());
- }
-
- void write_network_address(network_address_type addr)
- {
- write_8_bytes(addr.services);
- write_data(addr.ip);
- write_data(uncast_type(addr.port, true));
- }
-
- void write_hash(const hash_digest& hash)
- {
- write_data_reverse(hash);
- }
-
- void write_short_hash(const short_hash& hash)
- {
- write_data_reverse(hash);
- }
-
- void write_fixed_string(const std::string& command, size_t string_size)
- {
- BITCOIN_ASSERT(command.size() <= string_size);
- data_chunk raw_string(string_size);
- std::copy(command.begin(), command.end(), raw_string.begin());
- write_data(raw_string);
- }
-
- void write_string(const std::string& str)
- {
- write_variable_uint(str.size());
- write_data(str);
- }
+ void write_uint_auto(T value);
+
+ void write_byte(uint8_t value);
+ void write_2_bytes(uint16_t value);
+ void write_4_bytes(uint32_t value);
+ void write_8_bytes(uint64_t value);
+
+ /**
+ * Variable uints are usually used for sizes.
+ * They're encoded using fewer bytes depending on the value itself.
+ */
+ void write_variable_uint(uint64_t value);
+
+ template <typename T>
+ void write_data(const T& data);
+ void write_hash(const hash_digest& hash);
+ void write_short_hash(const short_hash& hash);
+
+ void write_network_address(network_address_type addr);
+
+ /**
+ * Write a fixed size string padded with zeroes.
+ */
+ void write_fixed_string(const std::string& command, size_t string_size);
+
+ /**
+ * Write a variable length string.
+ */
+ void write_string(const std::string& str);
/**
* Returns underlying iterator.
*/
- Iterator iterator()
- {
- return iter_;
- }
+ Iterator iterator();
/**
* Useful if you want to serialize some data using another
* routine and then continue with this serializer.
*/
- void set_iterator(Iterator iter)
- {
- iter_ = iter;
- }
+ void set_iterator(Iterator iter);
private:
template <typename T>
- void write_uint(T value)
- {
- write_data(uncast_type(value));
- }
-
- template <typename T>
- void write_data_reverse(const T& data)
- {
- internal_copy(data.rbegin(), data.rend());
- }
+ void write_data_reverse(const T& data);
// We need to advance the internal iterator.
// std::copy gives no info on length of the data copied.
template <typename InputIterator>
- void internal_copy(InputIterator first, InputIterator last)
- {
- while (first != last)
- {
- *iter_ = *first;
- ++first;
- ++iter_;
- }
- }
+ void internal_copy(InputIterator first, InputIterator last);
Iterator iter_;
};
template <typename Iterator>
-serializer<Iterator> make_serializer(Iterator begin)
-{
- return serializer<Iterator>(begin);
-}
+serializer<Iterator> make_serializer(Iterator begin);
class end_of_stream
: std::exception {};
@@ -211,153 +142,67 @@ template <typename Iterator>
class deserializer
{
public:
- deserializer(const Iterator begin, const Iterator end)
- : iter_(begin), end_(end) {}
-
- uint8_t read_byte()
- {
- check_distance(iter_, end_, 1);
- return *(iter_++);
- }
- uint16_t read_2_bytes()
- {
- return read_data_impl<uint16_t>(iter_, end_);
- }
- uint32_t read_4_bytes()
- {
- return read_data_impl<uint32_t>(iter_, end_);
- }
- uint64_t read_8_bytes()
- {
- return read_data_impl<uint64_t>(iter_, end_);
- }
-
- uint64_t read_variable_uint()
- {
- uint8_t length = read_byte();
- if (length < 0xfd)
- return length;
- else if (length == 0xfd)
- return read_2_bytes();
- else if (length == 0xfe)
- return read_4_bytes();
- // length should be 0xff
- return read_8_bytes();
- }
-
- // NOTE: n_bytes changed to uint32_t to prevent array overflow.
- data_chunk read_data(uint32_t n_bytes)
- {
- check_distance(iter_, end_, n_bytes);
- data_chunk raw_bytes(n_bytes);
- for (uint32_t i = 0; i < n_bytes; ++i)
- raw_bytes[i] = read_byte();
- return raw_bytes;
- }
-
- network_address_type read_network_address()
- {
- network_address_type addr;
- addr.services = read_8_bytes();
- // Read IP address
- read_bytes<16>(iter_, end_, addr.ip);
- addr.port = read_data_impl<uint16_t>(iter_, end_, true);
- return addr;
- }
-
- hash_digest read_hash()
- {
- hash_digest hash;
- read_bytes<32>(iter_, end_, hash, true);
- return hash;
- }
-
- short_hash read_short_hash()
- {
- short_hash hash;
- read_bytes<20>(iter_, end_, hash, true);
- return hash;
- }
-
- std::string read_fixed_string(size_t len)
- {
- data_chunk string_bytes = read_data(len);
- std::string result(string_bytes.begin(), string_bytes.end());
- // Removes trailing 0s... Needed for string comparisons
- return result.c_str();
- }
-
- std::string read_string()
- {
- uint64_t string_size = read_variable_uint();
- // Warning: conversion from uint64_t to size_t, possible loss of data.
- return read_fixed_string((size_t)string_size);
- }
+ deserializer(const Iterator begin, const Iterator end);
+
+ /**
+ * Equivalent to calling read_byte/read_N_bytes depending
+ * on the size of the template parameter.
+ * For instance a uint32_t parameter is equivalent to read_4_bytes().
+ */
+ template <typename T>
+ const T read_uint_auto();
+
+ uint8_t read_byte();
+ uint16_t read_2_bytes();
+ uint32_t read_4_bytes();
+ uint64_t read_8_bytes();
+
+ /**
+ * Variable uints are usually used for sizes.
+ * They're encoded using fewer bytes depending on the value itself.
+ */
+ uint64_t read_variable_uint();
+
+ data_chunk read_data(uint32_t n_bytes);
+ hash_digest read_hash();
+ short_hash read_short_hash();
+
+ network_address_type read_network_address();
+
+ /**
+ * Read a fixed size string padded with zeroes.
+ */
+ std::string read_fixed_string(size_t len);
+
+ /**
+ * Read a variable length string.
+ */
+ std::string read_string();
/**
* Returns underlying iterator.
*/
- Iterator iterator() const
- {
- return iter_;
- }
+ Iterator iterator() const;
/**
* Useful if you advance the iterator using other serialization
* methods or objects.
*/
- void set_iterator(const Iterator iter)
- {
- iter_ = iter;
- }
+ void set_iterator(const Iterator iter);
private:
// Try to advance iterator 'distance' incremenets forwards.
// Throw if we prematurely reach the end.
static void check_distance(
- Iterator it, const Iterator end, size_t distance)
- {
- for (size_t i = 0; i < distance; ++i)
- {
- // Is this a valid byte?
- if (it == end)
- throw end_of_stream();
- // If so move to next value.
- ++it;
- }
- }
+ Iterator it, const Iterator end, size_t distance);
template <typename T>
static T read_data_impl(
- Iterator& begin, const Iterator end, bool reverse=false)
- {
- check_distance(begin, end, sizeof(T));
- data_chunk chunk(begin, begin + sizeof(T));
- T val = cast_chunk<T>(chunk, reverse);
- begin += sizeof(T);
- return val;
- }
+ Iterator& begin, const Iterator end, bool reverse=false);
template <unsigned int N>
static void read_bytes(Iterator& begin, const Iterator& end,
- std::array<uint8_t, N>& byte_array, bool reverse=false)
- {
- check_distance(begin, end, byte_array.size());
- #ifdef BOOST_LITTLE_ENDIAN
- // do nothing
- #elif BOOST_BIG_ENDIAN
- reverse = !reverse;
- #else
- #error "Endian isn't defined!"
- #endif
-
- if (reverse)
- std::reverse_copy(
- begin, begin + byte_array.size(), byte_array.begin());
- else
- std::copy(begin, begin + byte_array.size(), byte_array.begin());
- begin += byte_array.size();
- }
+ std::array<uint8_t, N>& byte_array, bool reverse=false);
Iterator iter_;
const Iterator end_;
@@ -365,12 +210,11 @@ private:
template <typename Iterator>
deserializer<Iterator> make_deserializer(
- const Iterator begin, const Iterator end)
-{
- return deserializer<Iterator>(begin, end);
-}
+ const Iterator begin, const Iterator end);
} // namespace libbitcoin
+#include <bitcoin/impl/utility/serializer.ipp>
+
#endif
diff --git a/src/block.cpp b/src/block.cpp
index ed4d582..40a705d 100644
--- a/src/block.cpp
+++ b/src/block.cpp
@@ -69,7 +69,7 @@ index_list block_locator_indexes(int top_height)
index_list indexes;
// Push last 10 indexes first
size_t step = 1, start = 0;
- for (size_t i = top_height; i > 0; i -= step, ++start)
+ for (int i = top_height; i > 0; i -= step, ++start)
{
if (start >= 10)
step *= 2;
diff --git a/src/blockchain/leveldb/leveldb_blockchain.cpp b/src/blockchain/leveldb/leveldb_blockchain.cpp
index d1f229b..30460ee 100644
--- a/src/blockchain/leveldb/leveldb_blockchain.cpp
+++ b/src/blockchain/leveldb/leveldb_blockchain.cpp
@@ -720,9 +720,10 @@ bool leveldb_blockchain::do_fetch_stealth(const stealth_prefix& prefix,
{
if (!stealth_match(prefix, it))
return;
- constexpr uint32_t row_size = 4 + 33 + 21 + 32;
+ constexpr size_t bitfield_size = sizeof(stealth_bitfield);
+ constexpr size_t row_size = bitfield_size + 33 + 21 + 32;
// Skip bitfield value since we don't need it.
- auto deserial = make_deserializer(it + 4, it + row_size);
+ auto deserial = make_deserializer(it + bitfield_size, it + row_size);
stealth_row row;
row.ephemkey = deserial.read_data(33);
uint8_t address_version = deserial.read_byte();
@@ -744,4 +745,5 @@ void leveldb_blockchain::subscribe_reorganize(
reorganize_subscriber_->subscribe(handle_reorganize);
}
-} // namespace libbitcoin \ No newline at end of file
+} // namespace libbitcoin
+
diff --git a/src/blockchain/leveldb/leveldb_common.cpp b/src/blockchain/leveldb/leveldb_common.cpp
index 7eac067..af0691e 100644
--- a/src/blockchain/leveldb/leveldb_common.cpp
+++ b/src/blockchain/leveldb/leveldb_common.cpp
@@ -24,6 +24,7 @@
#include <bitcoin/address.hpp>
#include <bitcoin/satoshi_serialize.hpp>
#include <bitcoin/transaction.hpp>
+#include <bitcoin/stealth.hpp>
#include <bitcoin/blockchain/database/stealth_database.hpp>
#include <bitcoin/utility/assert.hpp>
#include <bitcoin/utility/logger.hpp>
@@ -246,7 +247,7 @@ bool add_debit(leveldb::WriteBatch& batch,
bool process_stealth_output_info(const transaction_output_type& output,
data_chunk& stealth_data_store)
{
- // Return false when we want the main loop to skip pass this
+ // Return true when we want the main loop to skip past this
// output and not process it any further.
if (output.script.type() != payment_type::stealth_info)
return false;
@@ -255,12 +256,15 @@ bool process_stealth_output_info(const transaction_output_type& output,
return true;
}
-uint32_t calculate_bitfield(const data_chunk& stealth_data)
+constexpr size_t bitfield_size = sizeof(stealth_bitfield);
+
+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() + 4);
- uint32_t bitfield = deserial.read_4_bytes();
+ auto deserial = make_deserializer(
+ index.begin(), index.begin() + bitfield_size);
+ stealth_bitfield bitfield = deserial.read_uint_auto<stealth_bitfield>();
return bitfield;
}
data_chunk read_ephemkey(const data_chunk& stealth_data)
@@ -275,17 +279,17 @@ void add_stealth_info(const data_chunk& stealth_data,
const payment_address& address, const hash_digest& tx_hash,
stealth_database& db)
{
- const uint32_t bitfield = calculate_bitfield(stealth_data);
+ 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_4_bytes(bitfield);
+ 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 + 4 + 33 + 21 + 32);
+ BITCOIN_ASSERT(serial.iterator() == it + bitfield_size + 33 + 21 + 32);
};
db.store(write_func);
}
@@ -438,4 +442,5 @@ data_chunk create_address_key(
return result;
}
-} // namespace libbitcoin \ No newline at end of file
+} // namespace libbitcoin
+
diff --git a/src/blockchain/leveldb/leveldb_validate_block.cpp b/src/blockchain/leveldb/leveldb_validate_block.cpp
index 32cc217..0130318 100644
--- a/src/blockchain/leveldb/leveldb_validate_block.cpp
+++ b/src/blockchain/leveldb/leveldb_validate_block.cpp
@@ -73,7 +73,8 @@ uint64_t leveldb_validate_block::median_time_past()
{
// read last 11 block times into array and select median value
std::vector<uint64_t> times;
- for (int i = (int)(height_ - 1); i >= 0 && i >= (int)(height_ - 11); --i)
+ int first = static_cast<int>(height_) - 1, last = first - 10;
+ for (int i = first; i >= 0 && i >= last; --i)
times.push_back(fetch_block(i).timestamp);
BITCOIN_ASSERT(
(height_ < 11 && times.size() == height_) || times.size() == 11);
@@ -192,4 +193,4 @@ bool leveldb_validate_block::orphan_is_spent(
return false;
}
-} // namespace libbitcoin \ No newline at end of file
+} // namespace libbitcoin
diff --git a/src/network/handshake.cpp b/src/network/handshake.cpp
index d2dcf2d..2845db7 100644
--- a/src/network/handshake.cpp
+++ b/src/network/handshake.cpp
@@ -141,9 +141,9 @@ bool handshake::lookup_external(const std::string& website,
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_buffer);
curl_easy_setopt(curl, CURLOPT_URL, website.c_str());
// Everything fine. Do fetch
- CURLcode result = curl_easy_perform(curl);
+ CURLcode web_success = curl_easy_perform(curl);
curl_easy_cleanup(curl);
- if (result != CURLE_OK)
+ if (web_success != CURLE_OK)
return false;
// TODO use std::regex instead ... when it work >_>
boost::cmatch results;
@@ -153,8 +153,8 @@ bool handshake::lookup_external(const std::string& website,
return false;
}
ip = localhost_ip();
- for (int i = 0; i < 4; ++i)
- ip[i + 12] = boost::lexical_cast<unsigned>(results[i + 1]);
+ for (size_t i = 0; i < 4; ++i)
+ ip[i + 12] = boost::lexical_cast<unsigned int>(results[i + 1]);
return true;
}
diff --git a/src/network/protocol.cpp b/src/network/protocol.cpp
index 32374a6..6f4ddb6 100644
--- a/src/network/protocol.cpp
+++ b/src/network/protocol.cpp
@@ -30,8 +30,8 @@ using std::placeholders::_2;
using boost::posix_time::time_duration;
using boost::posix_time::seconds;
-constexpr size_t watermark_limit = 2000;
-const time_duration watermark_reset_interval = seconds(5);
+constexpr size_t watermark_limit = 10;
+const time_duration watermark_reset_interval = seconds(1);
static std::string pretty(const ip_address_type& ip)
{
diff --git a/src/script.cpp b/src/script.cpp
index 44e3d61..2d9aa3c 100644
--- a/src/script.cpp
+++ b/src/script.cpp
@@ -43,11 +43,6 @@ constexpr size_t op_counter_limit = 201;
// Convert opcode to its actual numeric value.
template<typename OpCode>
-#ifndef _MSC_VER
-// Workaround for CTP_Nov2013 compile error:
-// std::underlying_type<_Ty>::type': type not allowed for 'constexpr'
-constexpr
-#endif
auto base_value(OpCode code)
-> typename std::underlying_type<OpCode>::type
{
diff --git a/src/utility/big_number.cpp b/src/utility/big_number.cpp
index 76d86cf..d8d1f01 100644
--- a/src/utility/big_number.cpp
+++ b/src/utility/big_number.cpp
@@ -85,33 +85,68 @@ big_number& big_number::operator=(const big_number& other)
return *this;
}
+// The "compact" format is a representation of a whole
+// number N using an unsigned 32bit number similar to a
+// floating point format.
+// The most significant 8 bits are the unsigned exponent of base 256.
+// This exponent can be thought of as "number of bytes of N".
+// The lower 23 bits are the mantissa.
+// Bit number 24 (0x800000) represents the sign of N.
+// N = (-1^sign) * mantissa * 256^(exponent-3)
+//
+// Satoshi's original implementation used BN_bn2mpi() and BN_mpi2bn().
+// MPI uses the most significant bit of the first byte as sign.
+// Thus 0x1234560000 is compact (0x05123456)
+// and 0xc0de000000 is compact (0x0600c0de)
+// (0x05c0de00) would be -0x40de000000
+//
+// Bitcoin only uses this "compact" format for encoding difficulty
+// targets, which are unsigned 256bit quantities. Thus, all the
+// complexities of the sign bit and using base 256 are probably an
+// implementation accident.
+//
+// This implementation directly uses shifts instead of going
+// through an intermediate MPI representation.
+
void big_number::set_compact(uint32_t compact)
{
- uint8_t size = compact >> 24;
- data_chunk number_repr(4 + size);
- number_repr[3] = size;
- if (size >= 1)
- number_repr[4] = (compact >> 16) & 0xff;
- if (size >= 2)
- number_repr[5] = (compact >> 8) & 0xff;
- if (size >= 3)
- number_repr[6] = (compact >> 0) & 0xff;
- BN_mpi2bn(&number_repr[0], (int)number_repr.size(), &bignum_);
+ uint32_t size = compact >> 24;
+ bool is_negative = (compact & 0x00800000) != 0;
+ uint32_t word = compact & 0x007fffff;
+ if (size <= 3)
+ {
+ word >>= 8 * (3 - size);
+ BN_set_word(&bignum_, word);
+ }
+ else
+ {
+ BN_set_word(&bignum_, word);
+ BN_lshift(&bignum_, &bignum_, 8 * (size - 3));
+ }
+ BN_set_negative(&bignum_, is_negative);
}
-
uint32_t big_number::compact() const
{
- size_t size = BN_bn2mpi(&bignum_, NULL);
- data_chunk number_repr(size);
- size -= 4;
- BN_bn2mpi(&bignum_, &number_repr[0]);
- uint32_t compact = (uint32_t)(size << 24);
- if (size >= 1)
- compact |= (number_repr[4] << 16);
- if (size >= 2)
- compact |= (number_repr[5] << 8);
- if (size >= 3)
- compact |= (number_repr[6] << 0);
+ uint32_t size = BN_num_bytes(&bignum_);
+ uint32_t compact = 0;
+ if (size <= 3)
+ compact = BN_get_word(&bignum_) << 8 * (3 - size);
+ else
+ {
+ big_number tmp_number;
+ BN_rshift(&tmp_number.bignum_, &bignum_, 8 * (size - 3));
+ compact = BN_get_word(&tmp_number.bignum_);
+ }
+ // The 0x00800000 bit denotes the sign.
+ // Thus, if it is already set, divide the mantissa by 256
+ // and increase the exponent.
+ if (compact & 0x00800000)
+ {
+ compact >>= 8;
+ size++;
+ }
+ compact |= size << 24;
+ compact |= (BN_is_negative(&bignum_) ? 0x00800000 : 0);
return compact;
}
@@ -265,7 +300,7 @@ void big_number::set_uint64_impl(uint64_t value, bool is_negative)
*curr_byte = c;
++curr_byte;
}
- uint32_t size = (uint32_t)(curr_byte - (raw_mpi + 4));
+ uint32_t size = static_cast<uint32_t>(curr_byte - (raw_mpi + 4));
raw_mpi[0] = (size >> 24) & 0xff;
raw_mpi[1] = (size >> 16) & 0xff;
raw_mpi[2] = (size >> 8) & 0xff;
diff --git a/src/utility/mmfile.cpp b/src/utility/mmfile.cpp
index 1821609..267a473 100644
--- a/src/utility/mmfile.cpp
+++ b/src/utility/mmfile.cpp
@@ -28,6 +28,7 @@
#endif
#include <sys/types.h>
#include <sys/stat.h>
+#include <bitcoin/utility/assert.hpp>
namespace libbitcoin {
@@ -35,6 +36,7 @@ mmfile::mmfile(const std::string& filename)
{
#ifndef _MSC_VER
// Not yet MSVC portable (maybe windows).
+ BITCOIN_ASSERT_MSG(sizeof (void*) == 8, "Not a 64bit system!");
file_handle_ = open(filename.c_str(),
O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (file_handle_ == -1)
@@ -69,4 +71,4 @@ size_t mmfile::size() const
return size_;
}
-} // namespace libbitcoin \ No newline at end of file
+} // namespace libbitcoin
diff --git a/tools/verifier/README b/tools/verifier/README
new file mode 100644
index 0000000..30bc2c8
--- /dev/null
+++ b/tools/verifier/README
@@ -0,0 +1,14 @@
+# Download the blockchain.info history
+$ python query_binfo.py 1EXoDusjGwvnjZUyKkxZ4UHEf77z6A5S4P history.binfo.json
+$ python check_binfo_json.py history.binfo.json
+OK
+
+# Change constants at top of file first.
+$ g++ -o fetch-history fetch-history.cpp $(pkg-config --cflags --libs libbitcoin)
+
+# Stop anything using your blockchain.
+$ ./fetch-history > history.libbtc.json
+
+$ python cmp_history.py
+$ python final_check.py
+
diff --git a/tools/verifier/check_binfo_json.py b/tools/verifier/check_binfo_json.py
new file mode 100644
index 0000000..43492d8
--- /dev/null
+++ b/tools/verifier/check_binfo_json.py
@@ -0,0 +1,19 @@
+import json
+import sys
+
+def check(history):
+ assert len(history["txs"]) == history["n_tx"]
+ print "OK"
+
+def main(argv):
+ if len(argv) < 2:
+ print >> sys.stderr, "check_binfo_json FILENAME"
+ return -1
+ filename = argv[1]
+ with open(filename) as f:
+ history = json.loads(f.read())
+ check(history)
+
+if __name__ == "__main__":
+ sys.exit(main(sys.argv))
+
diff --git a/tools/verifier/cmp_binfo.py b/tools/verifier/cmp_binfo.py
new file mode 100644
index 0000000..7125c9b
--- /dev/null
+++ b/tools/verifier/cmp_binfo.py
@@ -0,0 +1,104 @@
+import sys
+import json
+import time
+import urllib2
+
+#address = "1Fufjpf9RM2aQsGedhSpbSCGRHrmLMJ7yY"
+address = "1EXoDusjGwvnjZUyKkxZ4UHEf77z6A5S4P"
+filename = "history.proc.json"
+limit = 292127;
+
+def read_binfo(address, offset=0, limit=50):
+ url = "https://blockchain.info/rawaddr/%s?offset=%s&limit=%s" % (
+ address, offset, limit)
+ f = urllib2.urlopen(url)
+ return f.read()
+
+def open_all(address, i):
+ with open(filename) as f:
+ history = json.loads(f.read())
+
+ binfo_history = json.loads(read_binfo(address, i * 50, 50))
+ if not binfo_history["txs"]:
+ print "Finished."
+ sys.exit(0)
+
+ return history, binfo_history
+
+def write_all(history):
+ with open(filename, "w") as f:
+ f.write(json.dumps(history, indent=2))
+
+def do_outpart(address, i):
+ history, binfo_history = open_all(address, i)
+
+ for bi_tx in binfo_history["txs"]:
+ if not "block_height" in bi_tx or bi_tx["block_height"] > limit:
+ continue
+ for bi_out in bi_tx["out"]:
+ if bi_out["addr"] != address:
+ continue
+ found = False
+ for row in history["history"]:
+ if (row["output"][0] == bi_tx["hash"] and
+ row["output"][1] == bi_out["n"]):
+ assert row["output_found"] == False
+ row["output_found"] = True
+ found = True
+ if not found:
+ print json.dumps(bi_tx, indent=2)
+ assert found
+
+ write_all(history)
+ print "Done:", i
+
+def do_inpart(address, i):
+ history, binfo_history = open_all(address, i)
+
+ for bi_tx in binfo_history["txs"]:
+ if not "block_height" in bi_tx or bi_tx["block_height"] > limit:
+ continue
+ for bi_idx, bi_inp in enumerate(bi_tx["inputs"]):
+ try:
+ prev = bi_inp["prev_out"]
+ except KeyError:
+ continue
+ if prev["addr"] != address:
+ continue
+ found = False
+ for row in history["history"]:
+ if row["spend"] is None:
+ continue
+ if (row["spend"][0] == bi_tx["hash"] and
+ row["spend"][1] == bi_idx):
+ assert row["spend_found"] == False
+ row["spend_found"] = True
+ found = True
+ if not found:
+ print json.dumps(bi_tx, indent=2)
+ assert found
+
+ write_all(history)
+ print "Done:", i
+
+def check():
+ with open(filename) as f:
+ history = json.loads(f.read())
+ uniq_txs = set()
+ for row in history["history"]:
+ if row["output_found"] == False:
+ print "Not found:", row
+ else:
+ uniq_txs.add(row["output"][0])
+ if row["spend_found"] == False:
+ print "Not found:", row
+ elif row["spend_found"] == True:
+ uniq_txs.add(row["spend"][0])
+ print "Txs:", len(uniq_txs)
+
+#for i in range(999):
+## do_outpart(address, i)
+# do_inpart(address, i)
+# time.sleep(0.5)
+check()
+
diff --git a/tools/verifier/cmp_history.py b/tools/verifier/cmp_history.py
new file mode 100644
index 0000000..9f11cbf
--- /dev/null
+++ b/tools/verifier/cmp_history.py
@@ -0,0 +1,74 @@
+import sys
+import json
+import time
+import urllib2
+
+address = "1EXoDusjGwvnjZUyKkxZ4UHEf77z6A5S4P"
+binfo_filename = "history.binfo.json"
+libbtc_filename = "history.libbtc.json"
+limit = 292127;
+output_filename = "history.proc.json"
+
+def json_load(filename):
+ with open(filename) as f:
+ return json.loads(f.read())
+ raise Exception("Problem loading JSON file.")
+
+def mark_output(history, outpoint):
+ found = False
+ for row in history["history"]:
+ if (row["output"][0] == outpoint[0] and
+ row["output"][1] == outpoint[1]):
+ assert row["output_found"] == False
+ row["output_found"] = True
+ found = True
+ if not found:
+ print json.dumps(bi_tx, indent=2)
+ assert found
+
+def mark_input(history, inpoint):
+ found = False
+ for row in history["history"]:
+ if row["spend"] is None:
+ continue
+ if (row["spend"][0] == inpoint[0] and
+ row["spend"][1] == inpoint[1]):
+ assert row["spend_found"] == False
+ row["spend_found"] = True
+ found = True
+ if not found:
+ print json.dumps(bi_tx, indent=2)
+ assert found
+
+def process(tx, history):
+ for output in tx["out"]:
+ if output["addr"] != address:
+ continue
+ outpoint = (tx["hash"], output["n"])
+ mark_output(history, outpoint)
+ for idx, input in enumerate(tx["inputs"]):
+ try:
+ prev = input["prev_out"]
+ except KeyError:
+ continue
+ if prev["addr"] != address:
+ continue
+ inpoint = (tx["hash"], idx)
+ mark_input(history, inpoint)
+
+def main():
+ bi_history = json_load(binfo_filename)
+ lb_history = json_load(libbtc_filename)
+
+ for tx in bi_history["txs"]:
+ if not "block_height" in tx or tx["block_height"] > limit:
+ continue
+ process(tx, lb_history)
+
+ with open(output_filename, "w") as f:
+ f.write(json.dumps(lb_history, indent=2))
+ return 0
+
+if __name__ == "__main__":
+ sys.exit(main())
+
diff --git a/tools/verifier/fetch-history.cpp b/tools/verifier/fetch-history.cpp
new file mode 100644
index 0000000..13a8caf
--- /dev/null
+++ b/tools/verifier/fetch-history.cpp
@@ -0,0 +1,83 @@
+#include <bitcoin/bitcoin.hpp>
+using namespace bc;
+
+blockchain* chain = nullptr;
+size_t limit = 292127;
+const std::string address = "1EXoDusjGwvnjZUyKkxZ4UHEf77z6A5S4P";
+// Make sure nothing else is using the blockchain.
+const std::string blockchain_path = "/mnt/disk1/blockchain/";
+
+void history_fetched(const std::error_code& ec,
+ const blockchain::history_list& history)
+{
+ if (ec)
+ {
+ log_error() << "Failed to fetch last height: " << ec.message();
+ return;
+ }
+ std::cout << "{\"history\": [" << std::endl;
+ bool first = true;
+ for (const auto& row: history)
+ {
+ if (row.output_height > limit)
+ continue;
+ if (first)
+ first = false;
+ else
+ std::cout << "," << std::endl;
+ std::cout << "{" << std::endl;
+ std::cout << " \"output\": [\"" << row.output.hash << "\", "
+ << row.output.index << "]," << std::endl;
+ std::cout << " \"output_height\": " << row.output_height
+ << "," << std::endl;
+ std::cout << " \"output_found\": false," << std::endl;
+ std::cout << " \"value\": " << row.value << "," << std::endl;
+ std::cout << " \"spend\": ";
+ if (row.spend.hash == null_hash)
+ std::cout << "null";
+ else
+ std::cout << "[\"" << row.spend.hash << "\", "
+ << row.spend.index << "]";
+ std::cout << "," << std::endl;
+ std::cout << " \"spend_height\": ";
+ if (row.spend.hash == null_hash || row.spend_height > limit)
+ {
+ std::cout << "null";
+ std::cout << "," << std::endl;
+ std::cout << " \"spend_found\": null" << std::endl;
+ }
+ else
+ {
+ std::cout << row.spend_height;
+ std::cout << "," << std::endl;
+ std::cout << " \"spend_found\": false" << std::endl;
+ }
+ std::cout << "}";
+ }
+ std::cout << std::endl << "]}" << std::endl;
+}
+
+void blockchain_started(const std::error_code& ec)
+{
+ if (ec)
+ {
+ log_error() << "Blockchain failed to start: " << ec.message();
+ return;
+ }
+ assert(chain);
+ chain->fetch_history(payment_address(address), history_fetched);
+}
+
+int main()
+{
+ threadpool pool(1);
+ leveldb_blockchain ldb_chain(pool);
+ chain = &ldb_chain;
+ ldb_chain.start(blockchain_path, blockchain_started);
+ pool.shutdown();
+ pool.join();
+ ldb_chain.stop();
+ return 0;
+}
+
+
diff --git a/tools/verifier/final_check.py b/tools/verifier/final_check.py
new file mode 100644
index 0000000..dd1efe5
--- /dev/null
+++ b/tools/verifier/final_check.py
@@ -0,0 +1,21 @@
+import json
+
+filename = "history.proc.json"
+
+def check():
+ with open(filename) as f:
+ history = json.loads(f.read())
+ uniq_txs = set()
+ for row in history["history"]:
+ if row["output_found"] == False:
+ print "Not found:", row
+ else:
+ uniq_txs.add(row["output"][0])
+ if row["spend_found"] == False:
+ print "Not found:", row
+ elif row["spend_found"] == True:
+ uniq_txs.add(row["spend"][0])
+ print "Txs:", len(uniq_txs)
+
+check()
+
diff --git a/tools/verifier/query_binfo.py b/tools/verifier/query_binfo.py
new file mode 100644
index 0000000..dc82d08
--- /dev/null
+++ b/tools/verifier/query_binfo.py
@@ -0,0 +1,37 @@
+import json
+import sys
+import time
+import urllib2
+
+def read_binfo(address, offset=0, limit=50):
+ url = "https://blockchain.info/rawaddr/%s?offset=%s&limit=%s" % (
+ address, offset, limit)
+ f = urllib2.urlopen(url)
+ return json.loads(f.read())
+
+def open_all(address):
+ limit = 50
+ history = read_binfo(address, 0, limit)
+ for i in range(1, 9999):
+ history_ext = read_binfo(address, i * limit, limit)
+ if not history_ext["txs"]:
+ return history
+ history["txs"].extend(history_ext["txs"])
+ print "Done:", i
+ time.sleep(0.5)
+ raise Exception("Never reach here.")
+
+def main(argv):
+ if len(argv) < 3:
+ print >> sys.stderr, "query_binfo ADDRESS FILENAME"
+ return -1
+ address = argv[1]
+ output_filename = argv[2]
+ history = open_all(address)
+ with open(output_filename, "w") as f:
+ f.write(json.dumps(history, indent=2))
+ return 0
+
+if __name__ == "__main__":
+ sys.exit(main(sys.argv))
+