Skip to content

Commit

Permalink
Python interface cleanups
Browse files Browse the repository at this point in the history
  • Loading branch information
hkalodner committed May 3, 2018
1 parent eafb0e9 commit 1b97e97
Show file tree
Hide file tree
Showing 23 changed files with 195 additions and 163 deletions.
2 changes: 1 addition & 1 deletion python/blocksci/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def mapreduce_block_ranges(chain, mapFunc, reduceFunc, init=missing_param, star
return mapFunc(chain[start:end])

raw_segments = chain._segment_indexes(start, end, cpu_count)
config = chain.config
config = chain._config

segments = [(raw_segment, config) for raw_segment in raw_segments]

Expand Down
2 changes: 1 addition & 1 deletion python/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def build_extension(self, ext):

setup(
name='blocksci',
version='0.3',
version='0.5.0',
author='Harry Kalodner',
author_email='[email protected]',
description='BlockSci: A high-performance tool for blockchain science and exploration',
Expand Down
3 changes: 3 additions & 0 deletions python/src/chain/block/block_py.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ struct AddBlockMethods {
func(property_tag, "weight", &Block::weight, "Three times the base size plus the total size");
func(property_tag, "input_value", totalInputValue<Block &>, "Returns the sum of the value of all of the inputs included in this block");
func(property_tag, "output_value", totalOutputValue<Block &>, "Returns the sum of the value of all of the outputs included in this block");
func(property_tag, "tx_count", [](const Block &block) -> int64_t {
return block.size();
}, "A range of all of the txes in the block");
func(property_tag, "input_count", inputCount<Block &>, "Returns total number of inputs included in this block");
func(property_tag, "output_count", outputCount<Block &>, "Returns total number of outputs included in this block");
;
Expand Down
18 changes: 4 additions & 14 deletions python/src/chain/blockchain_py.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ void init_blockchain(py::class_<Blockchain> &cl) {
cl
.def(py::init<std::string>())
.def(py::init<DataConfiguration>())
.def_property_readonly("config", [](Blockchain &chain) -> DataConfiguration { return chain.getAccess().config; }, "Returns the configuration settings for this blockchain")
.def_property_readonly("_config", [](Blockchain &chain) -> DataConfiguration { return chain.getAccess().config; }, "Returns the configuration settings for this blockchain")
.def("_segment", segmentChain, "Divide the blockchain into the given number of chunks with roughly the same number of transactions in each")
.def("_segment_indexes", segmentChainIndexes, "Return a list of [start, end] block height pairs representing chunks with roughly the same number of transactions in each")
.def("addresses", [](Blockchain &chain, AddressType::Enum type) {
return chain.scripts(type);
}, py::arg("address_type"))
}, py::arg("address_type"), "Return a range of all addresses of the given type")
.def("most_valuable_addresses", mostValuableAddresses, "Get a list of the top 100 most valuable addresses")
;

Expand All @@ -78,20 +78,10 @@ void init_data_access(py::module &m) {
py::class_<DataAccess> (m, "_DataAccess", "Private class for accessing blockchain data")
.def("tx_with_index", [](DataAccess &access, uint32_t index) {
return Transaction{index, access};
}, R"docstring(
This functions gets the transaction with given index.
:param int index: The index of the transation.
:returns: Tx
)docstring")
}, "This functions gets the transaction with given index.")
.def("tx_with_hash", [](DataAccess &access, const std::string &hash) {
return Transaction{hash, access};
}, R"docstring(
This functions gets the transaction with given hash.
:param string index: The hash of the transation.
:returns: Tx
)docstring")
}, "This functions gets the transaction with given hash.")
.def("address_from_index", [](DataAccess &access, uint32_t index, AddressType::Enum type) -> AnyScript {
return Address{index, type, access}.getScript().wrapped;
}, "Construct an address object from an address num and type")
Expand Down
16 changes: 2 additions & 14 deletions python/src/chain/blockchain_py.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,10 @@ struct AddBlockchainMethods {
func(property_tag, "blocks", &Blockchain::blocks, "Returns a range of all the blocks in the chain");
func(method_tag, "tx_with_index", [](Blockchain &chain, uint32_t index) {
return Transaction{index, chain.getAccess()};
},
R"docstring(
This functions gets the transaction with given index.
:param int index: The index of the transation.
:returns: Tx
)docstring", pybind11::arg("index"));
}, "This functions gets the transaction with given index.", pybind11::arg("index"));
func(method_tag, "tx_with_hash", [](Blockchain &chain, const std::string &hash) {
return Transaction{hash, chain.getAccess()};
},
R"docstring(
This functions gets the transaction with given hash.
:param string index: The hash of the transation.
:returns: Tx
)docstring", pybind11::arg("tx_hash"));
},"This functions gets the transaction with given hash.", pybind11::arg("tx_hash"));
func(method_tag, "address_from_index", [](Blockchain &chain, uint32_t index, AddressType::Enum type) {
return Address{index, type, chain.getAccess()};
}, "Construct an address object from an address num and type", pybind11::arg("index"), pybind11::arg("type"));
Expand Down
62 changes: 5 additions & 57 deletions python/src/chain/input/input_range_filters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,71 +23,19 @@ struct AddInputRangeMethods {

func(method_tag, "sent_before_height", [](Range &range, BlockHeight height) {
return inputsCreatedBeforeHeight(range, height);
},
R"docstring(sent_before_height(height: int) -> InputIterator
Filter the inputs to include only inputs which spent an output created before the given height
Args:
height (int): The height above which inputs are excluded.
Returns:
InputIterator: A iterator for the filtered range
)docstring", py::arg("height"));
}, "Filter the inputs to include only inputs which spent an output created before the given height", py::arg("height"));
func(method_tag, "sent_after_height", [](Range &range, BlockHeight height) {
return inputsCreatedAfterHeight(range, height);
},
R"docstring(sent_after_height(height: int) -> InputIterator
Filter the inputs to include only inputs which spent an output created after the given height
Args:
height (int): The height above which inputs are excluded.
Returns:
InputIterator: A iterator for the filtered range
)docstring", py::arg("height"));
}, "Filter the inputs to include only inputs which spent an output created after the given height", py::arg("height"));
func(method_tag, "with_age_less_than", [](Range &range, BlockHeight height) {
return inputsCreatedWithinRelativeHeight(range, height);
},
R"docstring(with_age_less_than(age: int) -> InputIterator
Filter the inputs to include only inputs with age less than the given value
Args:
age (int): The maximum age of the filtered inputs
Returns:
InputIterator: A iterator for the filtered range
)docstring", py::arg("age"));
}, "Filter the inputs to include only inputs with age less than the given value", py::arg("age"));
func(method_tag, "with_age_greater_than", [](Range &range, BlockHeight height) {
return inputsCreatedOutsideRelativeHeight(range, height);
},
R"docstring(with_age_greater_than(age: int) -> InputIterator
Filter the inputs to include only inputs with age more than the given value
Args:
age (int): The minimum age of the filtered inputs
Returns:
InputIterator: A iterator for the filtered range
)docstring", py::arg("age"));
}, "Filter the inputs to include only inputs with age more than the given value", py::arg("age"));
func(method_tag, "with_address_type", [](Range &range, AddressType::Enum type) {
return inputsOfType(range, type);
},
R"docstring(with_address_type(type: address_type) -> InputIterator
Filter the inputs to include only inputs that came from an address with the given type
Filter the inputs to include only inputs with age more than the given value
Args:
type (address_type): The address type of the filtered inputs
Returns:
InputIterator: A iterator for the filtered range
)docstring", py::arg("type"));
}, "Filter the inputs to include only inputs that came from an address with the given type", py::arg("type"));
}
};

Expand Down
14 changes: 2 additions & 12 deletions python/src/chain/tx/tx_py.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,9 @@ void init_tx(py::class_<Transaction> &cl) {
.def_property_readonly("_access", &Transaction::getAccess, py::return_value_policy::reference)
.def(py::init([](uint32_t index, blocksci::Blockchain &chain) {
return Transaction{index, chain.getAccess()};
}), R"docstring(
This functions gets the transaction with given index.
:param int index: The index of the transation.
:returns: Tx
)docstring")
}), "This functions gets the transaction with given index.")
.def(py::init([](const std::string hash, blocksci::Blockchain &chain) {
return Transaction{hash, chain.getAccess()};
}), R"docstring(
This functions gets the transaction with given hash.
:param string index: The hash of the transation.
:returns: Tx
)docstring")
}), "This functions gets the transaction with given hash.")
;
}
2 changes: 1 addition & 1 deletion python/src/method_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ struct PythonTypeName;
template <>
struct PythonTypeName<blocksci::AnyScript> {
static std::string name() {
return "AnyAddress";
return "Address";
}
};

Expand Down
9 changes: 8 additions & 1 deletion python/src/python_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@

#include "scripts/address_py.hpp"
#include "scripts/address_range_py.hpp"
#include "scripts/equiv_address/equiv_address_py.hpp"
#include "scripts/equiv_address/equiv_address_range_py.hpp"
#include "scripts/pubkey/pubkey_py.hpp"
#include "scripts/pubkey/pubkey_range_py.hpp"
#include "scripts/pubkey/pubkeyhash_range_py.hpp"
Expand Down Expand Up @@ -83,6 +85,7 @@ PYBIND11_MODULE(_blocksci, m) {
RangeClasses<Input> inputRangeCls(m);
RangeClasses<Output> outputRangeCls(m);
RangeClasses<AnyScript> addressRangeCls(m);
RangeClasses<EquivAddress> equivAddressRangeCls(m);
RangeClasses<script::Pubkey> pubkeyRangeCls(m);
RangeClasses<script::PubkeyHash> pubkeyHashRangeCls(m);
RangeClasses<script::WitnessPubkeyHash> witnessPubkeyHashRangeCls(m);
Expand All @@ -102,7 +105,11 @@ PYBIND11_MODULE(_blocksci, m) {
init_blockchain(blockchainCl);
init_uint160(uint160Cl);
init_uint256(uint256Cl);
init_equiv_address(equivAddressCl);
{
init_equiv_address(equivAddressCl);
addEquivAddressRangeMethods(equivAddressRangeCls);
applyMethodsToEquivAddressRange(equivAddressRangeCls);
}
{
init_block(blockCl);
addBlockRangeMethods(blockRangeCls);
Expand Down
7 changes: 4 additions & 3 deletions python/src/range_apply_py.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ struct ApplyMethodsToRangeImpl {
PYBIND11_DESCR returnTypeDescr = pybind11::detail::make_caster<typename wrapped_func_type::return_type>::name();

std::stringstream ss;
ss << "For each item " << description << "\n\n:type: :class:`" << getTypeName(returnTypeDescr.text(), returnTypeDescr.types()) << "`";
ss << "For each item: " << description << "\n\n:type: :class:`" << getTypeName(returnTypeDescr.text(), returnTypeDescr.types()) << "`";

cl.def_property_readonly(strdup(propertyName.c_str()), wrapped_func_type{func}, strdup(ss.str().c_str()));
}
Expand All @@ -137,9 +137,10 @@ struct ApplyMethodsToRangeImpl {
using traits = function_traits<F>;
using arg_sequence = std::make_index_sequence<traits::arity - 1>;
using wrapped_func_type = ApplyMethodToRange<Range, traits, F, arg_sequence>;


PYBIND11_DESCR returnTypeDescr = pybind11::detail::make_caster<typename wrapped_func_type::return_type>::name();
std::stringstream ss;
ss <<"For each item " << description;
ss << "For each item: " << description << "\n\n:rtype: :class:`" << getTypeName(returnTypeDescr.text(), returnTypeDescr.types()) << "`";

cl.def(strdup(propertyName.c_str()), wrapped_func_type{func}, std::forward<Args>(args)..., strdup(ss.str().c_str()));
}
Expand Down
25 changes: 11 additions & 14 deletions python/src/range_conversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ typename NumpyConverter<blocksci::uint256>::type NumpyConverter<blocksci::uint25
return ret;
}

typename NumpyConverter<blocksci::uint160>::type NumpyConverter<blocksci::uint160>::operator()(const blocksci::uint160 &val) {
auto hexStr = val.GetHex();
std::array<char, 40> ret;
std::copy_n(hexStr.begin(), 40, ret.begin());
return ret;
}

template <typename T>
pybind11::list convertRangeToPythonPy(T && t) {
if constexpr (ranges::RandomAccessRange<T>()) {
Expand Down Expand Up @@ -86,32 +93,22 @@ pybind11::array_t<typename NumpyConverter<ranges::range_value_type_t<T>>::type>
}

template <typename T>
using blocksci_range_type_t = ranges::any_view<ranges::range_value_type_t<T>, getBlockSciCategory(ranges::get_categories<T>())>;

template <typename T>
blocksci_range_type_t<T> convertRangeToPythonBlockSci(T && t) {
return {std::forward<T>(t)};
converted_range_impl_t<T> convertRangeToPythonBlockSci(T && t) {
return {std::forward<T>(t) | ranges::view::transform([](auto && x) { return BlockSciTypeConverter{}(std::forward<decltype(x)>(x)); })};
}

template <typename T>
converted_range_impl_t<T> convertRangeToPythonImpl(T && t) {
converted_range_t<T> convertAnyRangeToPython(T && t) {
using range_tag = typename type_tag<ranges::range_value_type_t<T>>::type;
if constexpr (std::is_same_v<range_tag, py_tag>) {
return convertRangeToPythonPy(std::forward<T>(t));
} else if constexpr (std::is_same_v<range_tag, numpy_tag>) {
return convertRangeToPythonNumpy(std::forward<T>(t));
} else if constexpr (std::is_same_v<range_tag, blocksci_tag>) {
auto converted = std::forward<T>(t) | ranges::view::transform([](auto && x) { return BlockSciTypeConverter{}(std::forward<decltype(x)>(x)); });
using range_tag = typename type_tag<ranges::range_value_type_t<decltype(converted)>>::type;
return convertRangeToPythonBlockSci(converted);
return convertRangeToPythonBlockSci(std::forward<T>(t));
}
}

template <typename T>
converted_range_t<T> convertAnyRangeToPython(T && t) {
return convertRangeToPythonImpl(std::forward<T>(t));
}

template <typename T>
using random_range = ranges::any_view<T, ranges::category::random_access>;

Expand Down
6 changes: 6 additions & 0 deletions python/src/range_conversion.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,12 @@ struct NumpyConverter<blocksci::uint256> {
type operator()(const blocksci::uint256 &val);
};

template<>
struct NumpyConverter<blocksci::uint160> {
using type = std::array<char, 40>;
type operator()(const blocksci::uint160 &val);
};

template<>
struct NumpyConverter<bool> {
using type = NumpyBool;
Expand Down
7 changes: 5 additions & 2 deletions python/src/range_filter_apply_py.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ struct ApplyRangeMethodsToRangeImpl {
using wrapped_func_type = ApplyRangeMethodToRange<Range, traits, F, arg_sequence>;

PYBIND11_DESCR returnTypeDescr = pybind11::detail::make_caster<typename wrapped_func_type::return_type>::name();

std::stringstream ss;
ss << description << "\n\n:type: :class:`" << getTypeName(returnTypeDescr.text(), returnTypeDescr.types()) << "`";

Expand All @@ -99,7 +98,11 @@ struct ApplyRangeMethodsToRangeImpl {
using arg_sequence = std::make_index_sequence<traits::arity - 1>;
using wrapped_func_type = ApplyRangeMethodToRange<Range, traits, F, arg_sequence>;

cl.def(strdup(propertyName.c_str()), wrapped_func_type{func}, std::forward<Args>(args)..., strdup(description.c_str()));
PYBIND11_DESCR returnTypeDescr = pybind11::detail::make_caster<typename wrapped_func_type::return_type>::name();
std::stringstream ss;
ss << description << "\n\n:rtype: :class:`" << getTypeName(returnTypeDescr.text(), returnTypeDescr.types()) << "`";

cl.def(strdup(propertyName.c_str()), wrapped_func_type{func}, std::forward<Args>(args)..., strdup(ss.str().c_str()));
}
};

Expand Down
4 changes: 2 additions & 2 deletions python/src/ranges_py.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ void init_ranges(py::module &m) {
addRangeMethods(cl);
}
{
py::class_<ranges::any_view<ranges::optional<uint256>>> cl(m, "Hash256OptionalIterator");
py::class_<ranges::any_view<ranges::optional<uint256>>> cl(m, "UInt256OptionalIterator");
addRangeMethods(cl);
}
{
py::class_<ranges::any_view<ranges::optional<uint256>, ranges::category::random_access>> cl(m, "Hash256OptionalRange");
py::class_<ranges::any_view<ranges::optional<uint256>, ranges::category::random_access>> cl(m, "UInt256OptionalRange");
addRangeMethods(cl);
}
{
Expand Down
29 changes: 0 additions & 29 deletions python/src/scripts/address_py.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,32 +77,3 @@ void init_address(py::class_<blocksci::ScriptBase> &cl) {

applyMethodsToSelf(cl, AddAddressMethods<ScriptBase>{});
}

void init_equiv_address(py::class_<EquivAddress> &cl) {
cl
.def(py::self == py::self)
.def(hash(py::self))
.def("__repr__", &EquivAddress::toString)
.def("__len__", [](const EquivAddress &address) { return address.size(); })
.def("__bool__", [](const EquivAddress &address) { return address.size() == 0; })
.def("__iter__", [](const EquivAddress &address) {
auto transformed = address | ranges::view::transform([](const Address &address) {
return address.getScript();
});
return py::make_iterator(transformed.begin(), transformed.end());
},py::keep_alive<0, 1>())
.def_property_readonly("is_script_equiv", &EquivAddress::isScriptEquiv, "Returns whether this equiv address is script equivalent or not")
.def("balance", &EquivAddress::calculateBalance, py::arg("height") = -1, "Calculates the balance held by these equivalent addresses at the height (Defaults to the full chain)")
.def("outs", &EquivAddress::getOutputs, "Returns a list of all outputs sent to these equivalent addresses")
.def("ins", &EquivAddress::getInputs, "Returns a list of all inputs spent from these equivalent addresses")
.def("txes", &EquivAddress::getTransactions, "Returns a list of all transactions involving these equivalent addresses")
.def("out_txes",&EquivAddress::getOutputTransactions, "Returns a range of all transaction where these equivalent addresses were an output")
.def("in_txes",&EquivAddress::getInputTransactions, "Returns a list of all transaction where these equivalent addresses were an input")
.def("out_txes_count", [](EquivAddress &address) -> int64_t {
return address.getOutputTransactions().size();
}, "Return the number of transactions where these equivalent addresses were an output")
.def("in_txes_count", [](EquivAddress &address) -> int64_t {
return address.getInputTransactions().size();
}, "Return the number of transactions where these equivalent addresses were an input")
;
}
Loading

0 comments on commit 1b97e97

Please sign in to comment.