diff --git a/CMakeLists.txt b/CMakeLists.txt index 2727b92e..27d83ec4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,12 +7,8 @@ set (CPACK_PACKAGE_VERSION_PATCH "1") set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks") -else() - set(CMAKE_INSTALL_RPATH "\$ORIGIN/../lib:\$ORIGIN/") - set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) -endif() +set(CMAKE_INSTALL_RPATH "\$ORIGIN/../lib:\$ORIGIN/") +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) set (RAIBLOCKS_GUI OFF CACHE BOOL "") set (RAIBLOCKS_TEST OFF CACHE BOOL "") @@ -70,7 +66,7 @@ endif (WIN32) if (WIN32) set (PLATFORM_CXX_FLAGS "/bigobj") else (WIN32) - set (PLATFORM_CXX_FLAGS "-std=c++11") + set (PLATFORM_CXX_FLAGS "-std=c++14") endif (WIN32) if (WIN32) @@ -146,6 +142,11 @@ else () set (ARGON_CORE phc-winner-argon2/src/ref.c) endif () +if (WIN32) + set (gtest_force_shared_crt ON) +else () + set (gtest_force_shared_crt OFF) +endif() add_subdirectory (gtest) include_directories ("gtest/include") diff --git a/FindBoost.cmake b/FindBoost.cmake index e9677074..224934b5 100644 --- a/FindBoost.cmake +++ b/FindBoost.cmake @@ -925,7 +925,7 @@ function(_Boost_MISSING_DEPENDENCIES componentvar extravar) endfunction() # -# Some boost libraries may require particular set of compler features. +# Some boost libraries may require particular set of compiler features. # The very first one was `boost::fiber` introduced in Boost 1.62. # One can check required compiler features of it in # `${Boost_ROOT}/libs/fiber/build/Jamfile.v2`. diff --git a/README.md b/README.md index c51abd7a..af73e99b 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,53 @@ +
+
+ Logo +
+
+ [![Build Status](https://travis-ci.org/nanocurrency/raiblocks.svg?branch=master)](https://travis-ci.org/nanocurrency/raiblocks) -# What is Nano? -Nano is designed to be a feeless, instant, high throughput cryptocurrency. +### What is Nano? -We've applied the philosophy of "do one thing and do it well", giving you performance and scalability unmatched by any other platform. +--- -### Key features -* Nano utilizes a novel block-lattice architecture, unlike conventional blockchains used in many other cryptocurrencies. +Nano's goal is to become _"a global currency with instantaneous transactions and zero fees over a secure, decentralized network."_ + +We've applied the philosophy of _"do one thing and do it well"_, we are focused on building the best medium for value exchange in the world. + +--- + +### Key Features + +* Nano utilizes a novel [block-lattice](https://github.com/nanocurrency/raiblocks/wiki/Block-lattice) architecture, unlike conventional blockchains used in many other cryptocurrencies. * The network requires minimal resources, no high-power mining hardware, and can process high transaction throughput. -* Offers feeless, instantaneous transactions, as well as unlimited scalability, making Nano ideal for peer-to-peer transactions. +* Offers instantaneous transactions with zero fees and unlimited scalability, making Nano an ideal solution for peer-to-peer transactions. * As of December 2017, the Nano network has processed over four million transactions with an unpruned ledger size of only 1.7GB. For more information, see [Nano.org](https://nano.org/) or read the [whitepaper](https://nano.org/en/whitepaper). -### Resources -- [Nano website](https://nano.org) -- [Whitepaper](https://nano.org/en/whitepaper) -- [Roadmap](https://raiblocks.net/media/raiblocks-roadmap-v2-en.png) -- [Discord chat](https://chat.nano.org/) -- [Reddit](https://reddit.com/r/nanocurrency) -- [Medium](https://medium.com/@nanocurrency) -- [Twitter](https://twitter.com/nanocurrency) -- [Forum](https://forum.raiblocks.net/) -- [GitHub wiki](https://github.com/nanocurrency/raiblocks/wiki) +### Guides & Documentation -### Build instructions -https://github.com/nanocurrency/raiblocks/wiki/Build-Instructions +* [White Paper](https://nano.org/en/whitepaper) +* [Build Instructions](https://github.com/nanocurrency/raiblocks/wiki/Build-Instructions) +* [Command Line Interface](https://github.com/nanocurrency/raiblocks/wiki/Command-line-interface) +* [RPC Protocol](https://github.com/nanocurrency/raiblocks/wiki/RPC-protocol) +* [Wallet Design](https://github.com/nanocurrency/raiblocks/wiki/Wallet-design) +* [Block Lattice](https://github.com/nanocurrency/raiblocks/wiki/Block-lattice) +* [Design Features](https://github.com/nanocurrency/raiblocks/wiki/Design-features) + +### Links & Resources + +* [Nano Website](https://nano.org) +* [Nano Roadmap](https://raiblocks.net/media/raiblocks-roadmap-v2-en.png) +* [Discord Chat](https://chat.nano.org/) +* [Reddit](https://reddit.com/r/nanocurrency) +* [Medium](https://medium.com/@nanocurrency) +* [Twitter](https://twitter.com/nanocurrency) +* [Forum](https://forum.raiblocks.net/) +* [GitHub wiki](https://github.com/nanocurrency/raiblocks/wiki) + +### Want to Contribute? -### Want to contribute? Please see the [contributors guide](https://github.com/nanocurrency/raiblocks/wiki/Contributing). ### Contact us diff --git a/ci/bootstrap_boost.sh b/ci/bootstrap_boost.sh index 90424cd8..f866f872 100755 --- a/ci/bootstrap_boost.sh +++ b/ci/bootstrap_boost.sh @@ -1,18 +1,37 @@ #!/usr/bin/env bash -set -o unset +set -o nounset +set -o errexit set -o xtrace +bootstrapArgs=() +while getopts 'm' OPT; do + case "${OPT}" in + m) + bootstrapArgs+=('--with-libraries=atomic,chrono,thread,log,date_time,filesystem,program_options,regex') + ;; + esac +done + BOOST_BASENAME=boost_1_66_0 BOOST_ROOT=${BOOST_ROOT-/usr/local/boost} BOOST_URL=https://downloads.sourceforge.net/project/boost/boost/1.66.0/${BOOST_BASENAME}.tar.bz2 +BOOST_ARCHIVE="${BOOST_BASENAME}.tar.bz2" +BOOST_ARCHIVE_SHA256='5721818253e6a0989583192f96782c4a98eb6204965316df9f5ad75819225ca9' -wget --quiet -O ${BOOST_BASENAME}.tar.gz "${BOOST_URL}" -tar xf ${BOOST_BASENAME}.tar.gz +wget --quiet -O "${BOOST_ARCHIVE}.new" "${BOOST_URL}" +checkHash="$(openssl dgst -sha256 "${BOOST_ARCHIVE}.new" | sed 's@^.*= *@@')" +if [ "${checkHash}" != "${BOOST_ARCHIVE_SHA256}" ]; then + echo "Checksum mismatch. Expected ${BOOST_ARCHIVE_SHA256}, got ${checkHash}" >&2 + + exit 1 +fi +mv "${BOOST_ARCHIVE}.new" "${BOOST_ARCHIVE}" + +tar xf "${BOOST_ARCHIVE}" cd ${BOOST_BASENAME} -./bootstrap.sh +./bootstrap.sh "${bootstrapArgs[@]}" ./b2 -d0 --prefix=${BOOST_ROOT} link=static install cd .. rm -rf ${BOOST_BASENAME} -rm -f ${BOOST_BASENAME}.tar.gz -mkdir -p app +rm -f "${BOOST_ARCHIVE}" diff --git a/ci/build-docker-image.sh b/ci/build-docker-image.sh index e5e34488..6268adb3 100755 --- a/ci/build-docker-image.sh +++ b/ci/build-docker-image.sh @@ -1,8 +1,17 @@ #!/bin/bash set -eu +if [ "$#" -lt 2 ]; then + echo 'Usage: build-docker-image.sh [...]' >&2 + exit 1 +fi + +dockerFile="$1" +dockerTag="$2" +shift; shift + scripts="$(dirname "$0")" -"$scripts"/custom-timeout.sh 20 docker pull "$2" || true -echo "Building $2" -"$scripts"/custom-timeout.sh 30 docker build -f "$1" -t "$2" --cache-from "$2" . +"$scripts"/custom-timeout.sh 20 docker pull "${dockerTag}" || true +echo "Building $dockerTag" +"$scripts"/custom-timeout.sh 30 docker build "$@" -f "${dockerFile}" -t "${dockerTag}" --cache-from "${dockerTag}" . diff --git a/ci/deploy-docker.sh b/ci/deploy-docker.sh index 7669e33c..d82a3984 100755 --- a/ci/deploy-docker.sh +++ b/ci/deploy-docker.sh @@ -15,13 +15,23 @@ elif [ -n "$TRAVIS_TAG" ]; then tags+=("$TRAVIS_TAG" latest) fi -ci/build-docker-image.sh docker/node/Dockerfile nanocurrency/nano -for tag in "${tags[@]}"; do - # Sanitize docker tag - # https://docs.docker.com/engine/reference/commandline/tag/ - tag="$(printf '%s' "$tag" | tr -c '[a-z][A-Z][0-9]_.-' -)" - if [ "$tag" != "latest" ]; then - docker tag nanocurrency/nano nanocurrency/nano:"$tag" +for network in live beta; do + if [ "${network}" = 'live' ]; then + network_tag_suffix='' + else + network_tag_suffix="-${network}" fi - "$scripts"/custom-timeout.sh 30 docker push nanocurrency/nano:"$tag" + + docker_image_name="nanocurrency/nano${network_tag_suffix}" + + ci/build-docker-image.sh docker/node/Dockerfile "$docker_image_name" --build-arg NETWORK="${network}" + for tag in "${tags[@]}"; do + # Sanitize docker tag + # https://docs.docker.com/engine/reference/commandline/tag/ + tag="$(printf '%s' "$tag" | tr -c '[a-z][A-Z][0-9]_.-' -)" + if [ "$tag" != "latest" ]; then + docker tag "$docker_image_name" "${docker_image_name}:$tag" + fi + "$scripts"/custom-timeout.sh 30 docker push "${docker_image_name}:$tag" + done done diff --git a/docker/ci/Dockerfile b/docker/ci/Dockerfile index 4bd6065a..2e9c112a 100644 --- a/docker/ci/Dockerfile +++ b/docker/ci/Dockerfile @@ -1,7 +1,7 @@ FROM ubuntu:16.04 ENV BOOST_BASENAME=boost_1_66_0 \ - BOOST_URL=https://sourceforge.net/projects/boost/files/boost/1.66.0/boost_1_66_0.tar.gz/download + BOOST_URL=https://netix.dl.sourceforge.net/project/boost/boost/1.66.0/boost_1_66_0.tar.gz RUN apt-get update -qq && apt-get install -yqq \ build-essential \ diff --git a/docker/node/Dockerfile b/docker/node/Dockerfile index ff7467f4..ece9f9a4 100644 --- a/docker/node/Dockerfile +++ b/docker/node/Dockerfile @@ -1,35 +1,31 @@ FROM ubuntu:16.04 -ENV BOOST_BASENAME=boost_1_66_0 \ - BOOST_ROOT=/tmp/boost_install \ - BOOST_URL=https://sourceforge.net/projects/boost/files/boost/1.66.0/boost_1_66_0.tar.gz/download +ARG NETWORK=live + +ENV BOOST_ROOT=/tmp/boost_install + +ADD ci /tmp/ci RUN apt-get update -qq && apt-get install -yqq \ build-essential \ cmake \ g++ \ wget && \ - wget -qO ${BOOST_BASENAME}.tar.gz ${BOOST_URL} && \ - tar xzf ${BOOST_BASENAME}.tar.gz && \ - cd ${BOOST_BASENAME} && \ - ./bootstrap.sh && \ - ./b2 -d0 --prefix=${BOOST_ROOT} link=static install && \ - rm -rf ${BOOST_BASENAME} && \ - rm -f ${BOOST_BASENAME}.tar.gz && \ - cd .. && \ - mkdir /usr/share/raiblocks/ + /tmp/ci/bootstrap_boost.sh -m ADD ./ /tmp/src RUN mkdir /tmp/build && \ cd /tmp/build && \ - cmake /tmp/src -DBOOST_ROOT=${BOOST_ROOT} && \ + cmake /tmp/src -DBOOST_ROOT=${BOOST_ROOT} -DACTIVE_NETWORK=rai_${NETWORK}_network && \ make rai_node && \ - cd .. + cd .. && \ + echo ${NETWORK} > /etc/nano-network FROM ubuntu:16.04 COPY --from=0 /tmp/build/rai_node /usr/bin +COPY --from=0 /etc/nano-network /etc COPY docker/node/entry.sh /entry.sh -COPY docker/node/config.json /usr/share/raiblocks/config.json +COPY docker/node/config /usr/share/raiblocks/config RUN chmod +x /entry.sh CMD ["/bin/bash", "/entry.sh"] diff --git a/docker/node/build.sh b/docker/node/build.sh index c873913b..7885e5eb 100755 --- a/docker/node/build.sh +++ b/docker/node/build.sh @@ -1,6 +1,42 @@ #!/bin/bash + +network='live' + +print_usage() { + echo 'build.sh [-h] [-n {live|beta|test}]' +} + +while getopts 'hn:' OPT; do + case "${OPT}" in + h) + print_usage + exit 0 + ;; + n) + network="${OPTARG}" + ;; + *) + print_usage >&2 + exit 1 + ;; + esac +done + +case "${network}" in + live) + network_tag='' + ;; + test|beta) + network_tag="-${network}" + ;; + *) + echo "Invalid network: ${network}" >&2 + exit 1 + ;; +esac + REPO_ROOT=`git rev-parse --show-toplevel` COMMIT_SHA=`git rev-parse --short HEAD` pushd $REPO_ROOT -docker build -f docker/node/Dockerfile -t raiblocks-node:latest . +docker build --build-arg NETWORK="${network}" -f docker/node/Dockerfile -t raiblocks-node${network_tag}:latest . popd diff --git a/docker/node/config/beta.json b/docker/node/config/beta.json new file mode 100644 index 00000000..f7bc0221 --- /dev/null +++ b/docker/node/config/beta.json @@ -0,0 +1,60 @@ +{ + "version": "2", + "rpc_enable": "true", + "rpc": { + "address": "::ffff:0.0.0.0", + "port": "7076", + "enable_control": "true", + "frontier_request_limit": "16384", + "chain_request_limit": "16384" + }, + "node": { + "version": "8", + "peering_port": "54000", + "bootstrap_fraction_numerator": "1", + "receive_minimum": "1000000000000000000000000", + "logging": { + "version": "2", + "ledger": "false", + "ledger_duplicate": "false", + "vote": "false", + "network": "true", + "network_message": "false", + "network_publish": "false", + "network_packet": "false", + "network_keepalive": "false", + "node_lifetime_tracing": "false", + "insufficient_work": "true", + "log_rpc": "true", + "bulk_pull": "false", + "work_generation_time": "true", + "log_to_cerr": "false", + "max_size": "16777216", + "rotation_size": "4194304", + "flush": "false" + }, + "work_peers": "", + "preconfigured_peers": [ + "rai-beta.raiblocks.net" + ], + "preconfigured_representatives": [ + "xrb_3kbzg73bjsi85scbwnouj44iinrsqtdqphzay1x3pwmgmhkdwg8yjntxff33" + ], + "inactive_supply": "0", + "password_fanout": "1024", + "io_threads": "4", + "work_threads": "4", + "enable_voting": "true", + "bootstrap_connections": "16", + "callback_address": "", + "callback_port": "0", + "callback_target": "", + "lmdb_max_dbs": "128" + }, + "opencl_enable": "false", + "opencl": { + "platform": "0", + "device": "0", + "threads": "1048576" + } +} diff --git a/docker/node/config.json b/docker/node/config/live.json similarity index 100% rename from docker/node/config.json rename to docker/node/config/live.json diff --git a/docker/node/config/test.json b/docker/node/config/test.json new file mode 100644 index 00000000..bdd6c191 --- /dev/null +++ b/docker/node/config/test.json @@ -0,0 +1,67 @@ +{ + "version": "2", + "rpc_enable": "true", + "rpc": { + "address": "::ffff:0.0.0.0", + "port": "7076", + "enable_control": "true", + "frontier_request_limit": "16384", + "chain_request_limit": "16384" + }, + "node": { + "version": "8", + "peering_port": "7075", + "bootstrap_fraction_numerator": "1", + "receive_minimum": "1000000000000000000000000", + "logging": { + "version": "2", + "ledger": "false", + "ledger_duplicate": "false", + "vote": "false", + "network": "true", + "network_message": "false", + "network_publish": "false", + "network_packet": "false", + "network_keepalive": "false", + "node_lifetime_tracing": "false", + "insufficient_work": "true", + "log_rpc": "true", + "bulk_pull": "false", + "work_generation_time": "true", + "log_to_cerr": "false", + "max_size": "16777216", + "rotation_size": "4194304", + "flush": "false" + }, + "work_peers": "", + "preconfigured_peers": [ + "rai-test.raiblocks.net" + ], + "preconfigured_representatives": [ + "xrb_3arg3asgtigae3xckabaaewkx3bzsh7nwz7jkmjos79ihyaxwphhm6qgjps4", + "xrb_1stofnrxuz3cai7ze75o174bpm7scwj9jn3nxsn8ntzg784jf1gzn1jjdkou", + "xrb_1q3hqecaw15cjt7thbtxu3pbzr1eihtzzpzxguoc37bj1wc5ffoh7w74gi6p", + "xrb_3dmtrrws3pocycmbqwawk6xs7446qxa36fcncush4s1pejk16ksbmakis78m", + "xrb_3hd4ezdgsp15iemx7h81in7xz5tpxi43b6b41zn3qmwiuypankocw3awes5k", + "xrb_1awsn43we17c1oshdru4azeqjz9wii41dy8npubm4rg11so7dx3jtqgoeahy", + "xrb_1anrzcuwe64rwxzcco8dkhpyxpi8kd7zsjc1oeimpc3ppca4mrjtwnqposrs", + "xrb_1hza3f7wiiqa7ig3jczyxj5yo86yegcmqk3criaz838j91sxcckpfhbhhra1" + ], + "inactive_supply": "0", + "password_fanout": "1024", + "io_threads": "4", + "work_threads": "4", + "enable_voting": "true", + "bootstrap_connections": "16", + "callback_address": "", + "callback_port": "0", + "callback_target": "", + "lmdb_max_dbs": "128" + }, + "opencl_enable": "false", + "opencl": { + "platform": "0", + "device": "0", + "threads": "1048576" + } +} diff --git a/docker/node/entry.sh b/docker/node/entry.sh index a38dc03d..02d38d09 100644 --- a/docker/node/entry.sh +++ b/docker/node/entry.sh @@ -1,10 +1,27 @@ #!/bin/bash + set -euo pipefail IFS=$'\n\t' -mkdir -p ~/RaiBlocks -if [ ! -f ~/RaiBlocks/config.json ]; then - echo "Config File not found, adding default." - cp /usr/share/raiblocks/config.json ~/RaiBlocks/ +network="$(cat /etc/nano-network)" +case "${network}" in + live|'') + network='live' + dirSuffix='' + ;; + beta) + dirSuffix='Beta' + ;; + test) + dirSuffix='Test' + ;; +esac + +nanodir="${HOME}/RaiBlocks${dirSuffix}" +mkdir -p "${nanodir}" +if [ ! -f "${nanodir}/config.json" ]; then + echo "Config File not found, adding default." + cp "/usr/share/raiblocks/config/${network}.json" "${nanodir}/config.json" fi + /usr/bin/rai_node --daemon diff --git a/images/logo.svg b/images/logo.svg new file mode 100644 index 00000000..95d96fb9 --- /dev/null +++ b/images/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/rai/blockstore.cpp b/rai/blockstore.cpp index 88b01b6a..c83ebee6 100644 --- a/rai/blockstore.cpp +++ b/rai/blockstore.cpp @@ -827,35 +827,6 @@ rai::block_counts rai::block_store::block_count (MDB_txn * transaction_a) return result; } -std::unordered_multimap rai::block_store::block_dependencies (MDB_txn * transaction_a) -{ - std::unordered_multimap result; - // For every block type - for (auto type : { rai::block_type::send, rai::block_type::receive, rai::block_type::open, rai::block_type::change }) - { - auto db (block_database (type)); - // For every block in that type's table - for (auto i (rai::store_iterator (transaction_a, db)), n (rai::store_iterator (nullptr)); i != n; ++i) - { - rai::block_hash hash (i->first.uint256 ()); - auto block (block_get (transaction_a, hash)); - if (type != rai::block_type::open) - { - auto previous (block->previous ()); - assert (!previous.is_zero ()); - result.insert (std::make_pair (previous, hash)); - } - if (type == rai::block_type::open || type == rai::block_type::receive) - { - auto source (block->source ()); - assert (!source.is_zero ()); - result.insert (std::make_pair (source, hash)); - } - } - } - return result; -} - void rai::block_store::account_del (MDB_txn * transaction_a, rai::account const & account_a) { auto status (mdb_del (transaction_a, accounts, rai::mdb_val (account_a), nullptr)); @@ -1074,7 +1045,7 @@ void rai::block_store::unchecked_put (MDB_txn * transaction_a, rai::block_hash c exists = true; } } - // Insering block if it wasn't found in database + // Inserting block if it wasn't found in database if (!exists) { std::lock_guard lock (cache_mutex); diff --git a/rai/blockstore.hpp b/rai/blockstore.hpp index 248c3567..77ecce89 100644 --- a/rai/blockstore.hpp +++ b/rai/blockstore.hpp @@ -60,7 +60,6 @@ public: void block_del (MDB_txn *, rai::block_hash const &); bool block_exists (MDB_txn *, rai::block_hash const &); rai::block_counts block_count (MDB_txn *); - std::unordered_multimap block_dependencies (MDB_txn *); void frontier_put (MDB_txn *, rai::block_hash const &, rai::account const &); rai::account frontier_get (MDB_txn *, rai::block_hash const &); diff --git a/rai/common.cpp b/rai/common.cpp index bc3acd11..a6d32f83 100644 --- a/rai/common.cpp +++ b/rai/common.cpp @@ -16,7 +16,7 @@ namespace { char const * test_private_key_data = "34F0A37AAD20F4A260F0A5B3CB3D7FB50673212263E58A380BC10474BB039CE4"; char const * test_public_key_data = "B0311EA55708D6A53C75CDBF88300259C6D018522FE3D4D0A242E431F9E8B6D0"; // xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpiij4txtdo -char const * beta_public_key_data = "9D3A5B66B478670455B241D6BAC3D3FE1CBB7E7B7EAA429FA036C2704C3DC0A4"; // xrb_39btdfmday591jcu6igpqd3x9ziwqfz9pzocacht1fp4g385ui76a87x6phk +char const * beta_public_key_data = "0311B25E0D1E1D7724BBA5BD523954F1DBCFC01CB8671D55ED2D32C7549FB252"; // xrb_11rjpbh1t9ixgwkdqbfxcawobwgusz13sg595ocytdbkrxcbzekkcqkc3dn1 char const * live_public_key_data = "E89208DD038FBB269987689621D52292AE9C35941A7484756ECCED92A65093BA"; // xrb_3t6k35gi95xu6tergt6p69ck76ogmitsa8mnijtpxm9fkcm736xtoncuohr3 char const * test_genesis_data = R"%%%({ "type": "open", @@ -29,11 +29,11 @@ char const * test_genesis_data = R"%%%({ char const * beta_genesis_data = R"%%%({ "type": "open", - "source": "9D3A5B66B478670455B241D6BAC3D3FE1CBB7E7B7EAA429FA036C2704C3DC0A4", - "representative": "xrb_39btdfmday591jcu6igpqd3x9ziwqfz9pzocacht1fp4g385ui76a87x6phk", - "account": "xrb_39btdfmday591jcu6igpqd3x9ziwqfz9pzocacht1fp4g385ui76a87x6phk", - "work": "6eb12d4c42dba31e", - "signature": "BD0D374FCEB33EAABDF728E9B4DCDBF3B226DA97EEAB8EA5B7EDE286B1282C24D6EB544644FE871235E4F58CD94DF66D9C555309895F67A7D1F922AAC12CE907" + "source": "0311B25E0D1E1D7724BBA5BD523954F1DBCFC01CB8671D55ED2D32C7549FB252", + "representative": "xrb_11rjpbh1t9ixgwkdqbfxcawobwgusz13sg595ocytdbkrxcbzekkcqkc3dn1", + "account": "xrb_11rjpbh1t9ixgwkdqbfxcawobwgusz13sg595ocytdbkrxcbzekkcqkc3dn1", + "work": "869e17b2bfa36639", + "signature": "34DF447C7F185673128C3516A657DFEC7906F16C68FB5A8879432E2E4FB908C8ED0DD24BBECFAB3C7852898231544A421DC8CB636EF66C82E1245083EB08EA0F" })%%%"; char const * live_genesis_data = R"%%%({ @@ -63,7 +63,7 @@ public: burn_account (0) { CryptoPP::AutoSeededRandomPool random_pool; - // Randomly generating these mean no two nodes will ever have the same sentinal values which protects against some insecure algorithms + // Randomly generating these mean no two nodes will ever have the same sentinel values which protects against some insecure algorithms random_pool.GenerateBlock (not_a_block.bytes.data (), not_a_block.bytes.size ()); random_pool.GenerateBlock (not_an_account.bytes.data (), not_an_account.bytes.size ()); } @@ -410,56 +410,6 @@ std::string rai::vote::to_json () const return stream.str (); } -namespace -{ -class root_visitor : public rai::block_visitor -{ -public: - root_visitor (rai::block_store & store_a) : - store (store_a) - { - } - virtual ~root_visitor () = default; - void send_block (rai::send_block const & block_a) override - { - result = block_a.previous (); - } - void receive_block (rai::receive_block const & block_a) override - { - result = block_a.previous (); - } - // Open blocks have no previous () so we use the account number - void open_block (rai::open_block const & block_a) override - { - rai::transaction transaction (store.environment, nullptr, false); - auto hash (block_a.source ()); - auto source (store.block_get (transaction, hash)); - if (source != nullptr) - { - auto send (dynamic_cast (source.get ())); - if (send != nullptr) - { - result = send->hashables.destination; - } - else - { - result.clear (); - } - } - else - { - result.clear (); - } - } - void change_block (rai::change_block const & block_a) override - { - result = block_a.previous (); - } - rai::block_store & store; - rai::block_hash result; -}; -} // namespace - rai::amount_visitor::amount_visitor (MDB_txn * transaction_a, rai::block_store & store_a) : transaction (transaction_a), store (store_a) diff --git a/rai/core_test/ledger.cpp b/rai/core_test/ledger.cpp index f7dcf36a..e76f579c 100644 --- a/rai/core_test/ledger.cpp +++ b/rai/core_test/ledger.cpp @@ -12,7 +12,7 @@ TEST (ledger, store_error) rai::ledger ledger (store); } -// Ledger can be initialized and retuns a basic query for an empty account +// Ledger can be initialized and returns a basic query for an empty account TEST (ledger, empty) { bool init (false); diff --git a/rai/core_test/message_parser.cpp b/rai/core_test/message_parser.cpp index 6fdd1f69..937fc407 100644 --- a/rai/core_test/message_parser.cpp +++ b/rai/core_test/message_parser.cpp @@ -74,14 +74,14 @@ TEST (message_parser, exact_confirm_ack_size) message.serialize (stream); } ASSERT_EQ (0, visitor.confirm_ack_count); - ASSERT_FALSE (parser.error); + ASSERT_EQ (parser.status, rai::message_parser::parse_status::success); parser.deserialize_confirm_ack (bytes.data (), bytes.size ()); ASSERT_EQ (1, visitor.confirm_ack_count); - ASSERT_FALSE (parser.error); + ASSERT_EQ (parser.status, rai::message_parser::parse_status::success); bytes.push_back (0); parser.deserialize_confirm_ack (bytes.data (), bytes.size ()); ASSERT_EQ (1, visitor.confirm_ack_count); - ASSERT_TRUE (parser.error); + ASSERT_NE (parser.status, rai::message_parser::parse_status::success); } TEST (message_parser, exact_confirm_req_size) @@ -97,14 +97,14 @@ TEST (message_parser, exact_confirm_req_size) message.serialize (stream); } ASSERT_EQ (0, visitor.confirm_req_count); - ASSERT_FALSE (parser.error); + ASSERT_EQ (parser.status, rai::message_parser::parse_status::success); parser.deserialize_confirm_req (bytes.data (), bytes.size ()); ASSERT_EQ (1, visitor.confirm_req_count); - ASSERT_FALSE (parser.error); + ASSERT_EQ (parser.status, rai::message_parser::parse_status::success); bytes.push_back (0); parser.deserialize_confirm_req (bytes.data (), bytes.size ()); ASSERT_EQ (1, visitor.confirm_req_count); - ASSERT_TRUE (parser.error); + ASSERT_NE (parser.status, rai::message_parser::parse_status::success); } TEST (message_parser, exact_publish_size) @@ -120,14 +120,14 @@ TEST (message_parser, exact_publish_size) message.serialize (stream); } ASSERT_EQ (0, visitor.publish_count); - ASSERT_FALSE (parser.error); + ASSERT_EQ (parser.status, rai::message_parser::parse_status::success); parser.deserialize_publish (bytes.data (), bytes.size ()); ASSERT_EQ (1, visitor.publish_count); - ASSERT_FALSE (parser.error); + ASSERT_EQ (parser.status, rai::message_parser::parse_status::success); bytes.push_back (0); parser.deserialize_publish (bytes.data (), bytes.size ()); ASSERT_EQ (1, visitor.publish_count); - ASSERT_TRUE (parser.error); + ASSERT_NE (parser.status, rai::message_parser::parse_status::success); } TEST (message_parser, exact_keepalive_size) @@ -142,12 +142,12 @@ TEST (message_parser, exact_keepalive_size) message.serialize (stream); } ASSERT_EQ (0, visitor.keepalive_count); - ASSERT_FALSE (parser.error); + ASSERT_EQ (parser.status, rai::message_parser::parse_status::success); parser.deserialize_keepalive (bytes.data (), bytes.size ()); ASSERT_EQ (1, visitor.keepalive_count); - ASSERT_FALSE (parser.error); + ASSERT_EQ (parser.status, rai::message_parser::parse_status::success); bytes.push_back (0); parser.deserialize_keepalive (bytes.data (), bytes.size ()); ASSERT_EQ (1, visitor.keepalive_count); - ASSERT_TRUE (parser.error); + ASSERT_NE (parser.status, rai::message_parser::parse_status::success); } diff --git a/rai/core_test/network.cpp b/rai/core_test/network.cpp index 14d221f4..dcab2e67 100644 --- a/rai/core_test/network.cpp +++ b/rai/core_test/network.cpp @@ -206,7 +206,7 @@ TEST (network, send_valid_confirm_ack) ++iterations; ASSERT_LT (iterations, 200); } - // Make sure the balance has decreased after procssing the block. + // Make sure the balance has decreased after processing the block. ASSERT_EQ (50, system.nodes[1]->balance (rai::test_genesis_key.pub)); } diff --git a/rai/core_test/rpc.cpp b/rai/core_test/rpc.cpp index 02845c4c..584fef99 100644 --- a/rai/core_test/rpc.cpp +++ b/rai/core_test/rpc.cpp @@ -379,7 +379,6 @@ TEST (rpc, wallet_add) rai::keypair key1; std::string key_text; key1.prv.data.encode_hex (key_text); - system.wallet (0)->insert_adhoc (key1.prv); boost::property_tree::ptree request; std::string wallet; system.nodes[0]->wallets.items.begin ()->first.encode_hex (wallet); @@ -394,6 +393,7 @@ TEST (rpc, wallet_add) ASSERT_EQ (200, response.status); std::string account_text1 (response.json.get ("account")); ASSERT_EQ (account_text1, key1.pub.to_account ()); + ASSERT_TRUE (system.wallet (0)->exists (key1.pub)); } TEST (rpc, wallet_password_valid) @@ -1337,6 +1337,8 @@ TEST (rpc, pending) rai::uint128_union amount; amount.decode_dec (i->second.get ("")); blocks[hash] = amount; + boost::optional source (i->second.get_optional ("source")); + ASSERT_FALSE (source.is_initialized ()); } ASSERT_EQ (blocks[block1->hash ()], 100); request.put ("threshold", "101"); @@ -2265,6 +2267,8 @@ TEST (rpc, accounts_pending) rai::uint128_union amount; amount.decode_dec (i->second.get ("")); blocks[hash] = amount; + boost::optional source (i->second.get_optional ("source")); + ASSERT_FALSE (source.is_initialized ()); } } ASSERT_EQ (blocks[block1->hash ()], 100); @@ -2465,6 +2469,8 @@ TEST (rpc, wallet_pending) rai::uint128_union amount; amount.decode_dec (i->second.get ("")); blocks[hash] = amount; + boost::optional source (i->second.get_optional ("source")); + ASSERT_FALSE (source.is_initialized ()); } } ASSERT_EQ (blocks[block1->hash ()], 100); @@ -2780,6 +2786,27 @@ TEST (rpc, account_info) ASSERT_TRUE (time - stol (modified_timestamp) < 5); std::string block_count (response.json.get ("block_count")); ASSERT_EQ ("2", block_count); + boost::optional weight (response.json.get_optional ("weight")); + ASSERT_FALSE (weight.is_initialized ()); + boost::optional pending (response.json.get_optional ("pending")); + ASSERT_FALSE (pending.is_initialized ()); + boost::optional representative (response.json.get_optional ("representative")); + ASSERT_FALSE (representative.is_initialized ()); + // Test for optional values + request.put ("weight", "true"); + request.put ("pending", "1"); + request.put ("representative", "1"); + test_response response2 (request, rpc, system.service); + while (response2.status == 0) + { + system.poll (); + } + std::string weight2 (response2.json.get ("weight")); + ASSERT_EQ ("100", weight2); + std::string pending2 (response2.json.get ("pending")); + ASSERT_EQ ("0", pending2); + std::string representative2 (response2.json.get ("representative")); + ASSERT_EQ (rai::test_genesis_key.pub.to_account (), representative2); } TEST (rpc, blocks_info) @@ -2810,6 +2837,26 @@ TEST (rpc, blocks_info) ASSERT_EQ (rai::genesis_amount.convert_to (), amount_text); std::string blocks_text (blocks.second.get ("contents")); ASSERT_FALSE (blocks_text.empty ()); + boost::optional pending (blocks.second.get_optional ("pending")); + ASSERT_FALSE (pending.is_initialized ()); + boost::optional source (blocks.second.get_optional ("source_account")); + ASSERT_FALSE (source.is_initialized ()); + } + // Test for optional values + request.put ("source", "true"); + request.put ("pending", "1"); + test_response response2 (request, rpc, system.service); + while (response2.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response2.status); + for (auto & blocks : response2.json.get_child ("blocks")) + { + std::string source (blocks.second.get ("source_account")); + ASSERT_EQ ("0", source); + std::string pending (blocks.second.get ("pending")); + ASSERT_EQ ("0", pending); } } @@ -2940,6 +2987,33 @@ TEST (rpc, ledger) ASSERT_EQ (std::to_string (time), modified_timestamp); std::string block_count (accounts.second.get ("block_count")); ASSERT_EQ ("1", block_count); + boost::optional weight (accounts.second.get_optional ("weight")); + ASSERT_FALSE (weight.is_initialized ()); + boost::optional pending (accounts.second.get_optional ("pending")); + ASSERT_FALSE (pending.is_initialized ()); + boost::optional representative (accounts.second.get_optional ("representative")); + ASSERT_FALSE (representative.is_initialized ()); + } + // Test for optional values + request.put ("weight", "1"); + request.put ("pending", "1"); + request.put ("representative", "true"); + test_response response2 (request, rpc, system.service); + while (response2.status == 0) + { + system.poll (); + } + for (auto & accounts : response2.json.get_child ("accounts")) + { + boost::optional weight (accounts.second.get_optional ("weight")); + ASSERT_TRUE (weight.is_initialized ()); + ASSERT_EQ ("0", weight.get ()); + boost::optional pending (accounts.second.get_optional ("pending")); + ASSERT_TRUE (pending.is_initialized ()); + ASSERT_EQ ("0", pending.get ()); + boost::optional representative (accounts.second.get_optional ("representative")); + ASSERT_TRUE (representative.is_initialized ()); + ASSERT_EQ (rai::test_genesis_key.pub.to_account (), representative.get ()); } } @@ -3193,3 +3267,99 @@ TEST (rpc, wallet_create_fail) } ASSERT_EQ ("Failed to create wallet. Increase lmdb_max_dbs in node config.", response.json.get ("error")); } + +TEST (rpc, wallet_ledger) +{ + rai::system system (24000, 1); + rai::keypair key; + rai::genesis genesis; + system.wallet (0)->insert_adhoc (key.prv); + auto & node1 (*system.nodes[0]); + auto latest (system.nodes[0]->latest (rai::test_genesis_key.pub)); + rai::send_block send (latest, key.pub, 100, rai::test_genesis_key.prv, rai::test_genesis_key.pub, node1.generate_work (latest)); + system.nodes[0]->process (send); + rai::open_block open (send.hash (), rai::test_genesis_key.pub, key.pub, key.prv, key.pub, node1.generate_work (key.pub)); + ASSERT_EQ (rai::process_result::progress, system.nodes[0]->process (open).code); + auto time (rai::seconds_since_epoch ()); + rai::rpc rpc (system.service, *system.nodes[0], rai::rpc_config (true)); + rpc.start (); + boost::property_tree::ptree request; + request.put ("action", "wallet_ledger"); + request.put ("wallet", system.nodes[0]->wallets.items.begin ()->first.to_string ()); + request.put ("sorting", "1"); + request.put ("count", "1"); + test_response response (request, rpc, system.service); + while (response.status == 0) + { + system.poll (); + } + for (auto & accounts : response.json.get_child ("accounts")) + { + std::string account_text (accounts.first); + ASSERT_EQ (key.pub.to_account (), account_text); + std::string frontier (accounts.second.get ("frontier")); + ASSERT_EQ (open.hash ().to_string (), frontier); + std::string open_block (accounts.second.get ("open_block")); + ASSERT_EQ (open.hash ().to_string (), open_block); + std::string representative_block (accounts.second.get ("representative_block")); + ASSERT_EQ (open.hash ().to_string (), representative_block); + std::string balance_text (accounts.second.get ("balance")); + ASSERT_EQ ("340282366920938463463374607431768211355", balance_text); + std::string modified_timestamp (accounts.second.get ("modified_timestamp")); + ASSERT_EQ (std::to_string (time), modified_timestamp); + std::string block_count (accounts.second.get ("block_count")); + ASSERT_EQ ("1", block_count); + boost::optional weight (accounts.second.get_optional ("weight")); + ASSERT_FALSE (weight.is_initialized ()); + boost::optional pending (accounts.second.get_optional ("pending")); + ASSERT_FALSE (pending.is_initialized ()); + boost::optional representative (accounts.second.get_optional ("representative")); + ASSERT_FALSE (representative.is_initialized ()); + } + // Test for optional values + request.put ("weight", "true"); + request.put ("pending", "1"); + request.put ("representative", "false"); + test_response response2 (request, rpc, system.service); + while (response2.status == 0) + { + system.poll (); + } + for (auto & accounts : response2.json.get_child ("accounts")) + { + boost::optional weight (accounts.second.get_optional ("weight")); + ASSERT_TRUE (weight.is_initialized ()); + ASSERT_EQ ("0", weight.get ()); + boost::optional pending (accounts.second.get_optional ("pending")); + ASSERT_TRUE (pending.is_initialized ()); + ASSERT_EQ ("0", pending.get ()); + boost::optional representative (accounts.second.get_optional ("representative")); + ASSERT_FALSE (representative.is_initialized ()); + } +} + +TEST (rpc, wallet_add_watch) +{ + rai::system system (24000, 1); + rai::rpc rpc (system.service, *system.nodes[0], rai::rpc_config (true)); + rpc.start (); + boost::property_tree::ptree request; + std::string wallet; + system.nodes[0]->wallets.items.begin ()->first.encode_hex (wallet); + request.put ("wallet", wallet); + request.put ("action", "wallet_add_watch"); + boost::property_tree::ptree entry; + boost::property_tree::ptree peers_l; + entry.put ("", rai::test_genesis_key.pub.to_account ()); + peers_l.push_back (std::make_pair ("", entry)); + request.add_child ("accounts", peers_l); + test_response response (request, rpc, system.service); + while (response.status == 0) + { + system.poll (); + } + ASSERT_EQ (200, response.status); + std::string success (response.json.get ("success")); + ASSERT_TRUE (success.empty ()); + ASSERT_TRUE (system.wallet (0)->exists (rai::test_genesis_key.pub)); +} diff --git a/rai/core_test/wallets.cpp b/rai/core_test/wallets.cpp index 8f08492b..f8ede908 100644 --- a/rai/core_test/wallets.cpp +++ b/rai/core_test/wallets.cpp @@ -76,7 +76,7 @@ TEST (wallets, wallet_create_max) rai::system system (24000, 1); bool error (false); rai::wallets wallets (error, *system.nodes[0]); - const int nonWalletDbs = 16; + const int nonWalletDbs = 17; for (int i = 0; i < system.nodes[0]->config.lmdb_max_dbs - nonWalletDbs; i++) { rai::keypair key; diff --git a/rai/lib/interface.h b/rai/lib/interface.h index 283454ce..d16db966 100644 --- a/rai/lib/interface.h +++ b/rai/lib/interface.h @@ -29,7 +29,7 @@ int xrb_valid_address (const char * account); // Create a new random number in to 'destination' void xrb_generate_random (xrb_uint256 destination); -// Retrieve the detereministic private key for 'seed' at 'index' +// Retrieve the deterministic private key for 'seed' at 'index' void xrb_seed_key (const xrb_uint256 seed, int index, xrb_uint256); // Derive the public key 'pub' from 'key' void xrb_key_account (xrb_uint256 key, xrb_uint256 pub); diff --git a/rai/node/bootstrap.cpp b/rai/node/bootstrap.cpp index 07e61611..98a7bac3 100644 --- a/rai/node/bootstrap.cpp +++ b/rai/node/bootstrap.cpp @@ -988,55 +988,58 @@ void rai::bootstrap_attempt::process_fork (MDB_txn * transaction_a, std::shared_ void rai::bootstrap_attempt::try_resolve_fork (MDB_txn * transaction_a, std::shared_ptr block_a, bool from_processor) { std::weak_ptr this_w (shared_from_this ()); - std::shared_ptr ledger_block (node->ledger.forked_block (transaction_a, *block_a)); - if (ledger_block) + if (!node->store.block_exists (transaction_a, block_a->hash ()) && node->store.block_exists (transaction_a, block_a->root ())) { - node->active.start (transaction_a, ledger_block, [this_w, block_a](std::shared_ptr, bool resolved) { - if (auto this_l = this_w.lock ()) - { - if (resolved) + std::shared_ptr ledger_block (node->ledger.forked_block (transaction_a, *block_a)); + if (ledger_block) + { + node->active.start (transaction_a, ledger_block, [this_w, block_a](std::shared_ptr, bool resolved) { + if (auto this_l = this_w.lock ()) { + if (resolved) { - std::unique_lock lock (this_l->mutex); - this_l->unresolved_forks.erase (block_a->hash ()); - this_l->condition.notify_all (); - } - rai::transaction transaction (this_l->node->store.environment, nullptr, false); - auto account (this_l->node->ledger.store.frontier_get (transaction, block_a->root ())); - if (!account.is_zero ()) - { - this_l->requeue_pull (rai::pull_info (account, block_a->root (), block_a->root ())); - } - else if (this_l->node->ledger.store.account_exists (transaction, block_a->root ())) - { - this_l->requeue_pull (rai::pull_info (block_a->root (), rai::block_hash (0), rai::block_hash (0))); + { + std::unique_lock lock (this_l->mutex); + this_l->unresolved_forks.erase (block_a->hash ()); + this_l->condition.notify_all (); + } + rai::transaction transaction (this_l->node->store.environment, nullptr, false); + auto account (this_l->node->ledger.store.frontier_get (transaction, block_a->root ())); + if (!account.is_zero ()) + { + this_l->requeue_pull (rai::pull_info (account, block_a->root (), block_a->root ())); + } + else if (this_l->node->ledger.store.account_exists (transaction, block_a->root ())) + { + this_l->requeue_pull (rai::pull_info (block_a->root (), rai::block_hash (0), rai::block_hash (0))); + } } } - } - }); + }); + + auto hash = block_a->hash (); + bool exists = true; + if (from_processor) + { + // Only add the block to the unresolved fork tracker if it's the first time we've seen it (i.e. this call came from the block processor). + std::unique_lock lock (mutex); + exists = unresolved_forks.find (hash) != unresolved_forks.end (); + if (!exists) + { + unresolved_forks[hash] = block_a; + } + } - auto hash = block_a->hash (); - bool exists = true; - if (from_processor) - { - // Only add the block to the unresolved fork tracker if it's the first time we've seen it (i.e. this call came from the block processor). - std::unique_lock lock (mutex); - exists = unresolved_forks.find (hash) != unresolved_forks.end (); if (!exists) { - unresolved_forks[hash] = block_a; + BOOST_LOG (node->log) << boost::str (boost::format ("While bootstrappping, fork between our block: %1% and block %2% both with root %3%") % ledger_block->hash ().to_string () % hash.to_string () % block_a->root ().to_string ()); + } + if (!exists || !from_processor) + { + // Only broadcast if it's a new fork, or if the request is coming from the retry loop. + node->network.broadcast_confirm_req (ledger_block); + node->network.broadcast_confirm_req (block_a); } - } - - if (!exists) - { - BOOST_LOG (node->log) << boost::str (boost::format ("While bootstrappping, fork between our block: %1% and block %2% both with root %3%") % ledger_block->hash ().to_string () % hash.to_string () % block_a->root ().to_string ()); - } - if (!exists || !from_processor) - { - // Only broadcast if it's a new fork, or if the request is coming from the retry loop. - node->network.broadcast_confirm_req (ledger_block); - node->network.broadcast_confirm_req (block_a); } } } diff --git a/rai/node/common.cpp b/rai/node/common.cpp index dd535002..67365575 100644 --- a/rai/node/common.cpp +++ b/rai/node/common.cpp @@ -1,3 +1,4 @@ + #include #include @@ -73,14 +74,13 @@ bool rai::message::read_header (rai::stream & stream_a, uint8_t & version_max_a, rai::message_parser::message_parser (rai::message_visitor & visitor_a, rai::work_pool & pool_a) : visitor (visitor_a), pool (pool_a), -error (false), -insufficient_work (false) +status (parse_status::success) { } void rai::message_parser::deserialize_buffer (uint8_t const * buffer_a, size_t size_a) { - error = false; + status = parse_status::success; rai::bufferstream header_stream (buffer_a, size_a); uint8_t version_max; uint8_t version_using; @@ -113,14 +113,14 @@ void rai::message_parser::deserialize_buffer (uint8_t const * buffer_a, size_t s } default: { - error = true; + status = parse_status::invalid_message_type; break; } } } else { - error = true; + status = parse_status::invalid_header; } } @@ -135,7 +135,7 @@ void rai::message_parser::deserialize_keepalive (uint8_t const * buffer_a, size_ } else { - error = true; + status = parse_status::invalid_keepalive_message; } } @@ -152,12 +152,12 @@ void rai::message_parser::deserialize_publish (uint8_t const * buffer_a, size_t } else { - insufficient_work = true; + status = parse_status::insufficient_work; } } else { - error = true; + status = parse_status::invalid_publish_message; } } @@ -174,12 +174,12 @@ void rai::message_parser::deserialize_confirm_req (uint8_t const * buffer_a, siz } else { - insufficient_work = true; + status = parse_status::insufficient_work; } } else { - error = true; + status = parse_status::invalid_confirm_req_message; } } @@ -196,12 +196,12 @@ void rai::message_parser::deserialize_confirm_ack (uint8_t const * buffer_a, siz } else { - insufficient_work = true; + status = parse_status::insufficient_work; } } else { - error = true; + status = parse_status::invalid_confirm_ack_message; } } @@ -241,18 +241,23 @@ void rai::keepalive::serialize (rai::stream & stream_a) bool rai::keepalive::deserialize (rai::stream & stream_a) { - auto result (read_header (stream_a, version_max, version_using, version_min, type, extensions)); - assert (!result); + auto error (read_header (stream_a, version_max, version_using, version_min, type, extensions)); + assert (!error); assert (type == rai::message_type::keepalive); - for (auto i (peers.begin ()), j (peers.end ()); i != j; ++i) + for (auto i (peers.begin ()), j (peers.end ()); i != j && !error; ++i) { std::array address; uint16_t port; - read (stream_a, address); - read (stream_a, port); - *i = rai::endpoint (boost::asio::ip::address_v6 (address), port); + if (!read (stream_a, address) && !read (stream_a, port)) + { + *i = rai::endpoint (boost::asio::ip::address_v6 (address), port); + } + else + { + error = true; + } } - return result; + return error; } bool rai::keepalive::operator== (rai::keepalive const & other_a) const diff --git a/rai/node/common.hpp b/rai/node/common.hpp index fb94c9c9..5bc6fc03 100644 --- a/rai/node/common.hpp +++ b/rai/node/common.hpp @@ -130,6 +130,17 @@ class work_pool; class message_parser { public: + enum class parse_status + { + success, + insufficient_work, + invalid_header, + invalid_message_type, + invalid_keepalive_message, + invalid_publish_message, + invalid_confirm_req_message, + invalid_confirm_ack_message + }; message_parser (rai::message_visitor &, rai::work_pool &); void deserialize_buffer (uint8_t const *, size_t); void deserialize_keepalive (uint8_t const *, size_t); @@ -139,8 +150,7 @@ public: bool at_end (rai::bufferstream &); rai::message_visitor & visitor; rai::work_pool & pool; - bool error; - bool insufficient_work; + parse_status status; }; class keepalive : public message { diff --git a/rai/node/node.cpp b/rai/node/node.cpp index 64a26f6d..13b24c94 100644 --- a/rai/node/node.cpp +++ b/rai/node/node.cpp @@ -254,7 +254,7 @@ void rai::network::broadcast_confirm_req (std::shared_ptr block_a) } if (node.config.logging.network_logging ()) { - BOOST_LOG (node.log) << boost::str (boost::format ("Broadcasted confirm req to %1% representatives") % list.size ()); + BOOST_LOG (node.log) << boost::str (boost::format ("Broadcasted confirm req for block %1% to %2% representatives") % block_a->hash ().to_string () % list.size ()); } } @@ -419,17 +419,65 @@ void rai::network::receive_action (boost::system::error_code const & error, size network_message_visitor visitor (node, remote); rai::message_parser parser (visitor, node.work); parser.deserialize_buffer (buffer.data (), size_a); - if (parser.error) + if (parser.status != rai::message_parser::parse_status::success) { ++error_count; - } - else if (parser.insufficient_work) - { - if (node.config.logging.insufficient_work_logging ()) + + if (parser.status == rai::message_parser::parse_status::insufficient_work) { - BOOST_LOG (node.log) << "Insufficient work in message"; + if (node.config.logging.insufficient_work_logging ()) + { + BOOST_LOG (node.log) << "Insufficient work in message"; + } + + ++insufficient_work_count; + } + else if (parser.status == rai::message_parser::parse_status::invalid_message_type) + { + if (node.config.logging.network_logging ()) + { + BOOST_LOG (node.log) << "Invalid message type in message"; + } + } + else if (parser.status == rai::message_parser::parse_status::invalid_header) + { + if (node.config.logging.network_logging ()) + { + BOOST_LOG (node.log) << "Invalid header in message"; + } + } + else if (parser.status == rai::message_parser::parse_status::invalid_keepalive_message) + { + if (node.config.logging.network_logging ()) + { + BOOST_LOG (node.log) << "Invalid keepalive message"; + } + } + else if (parser.status == rai::message_parser::parse_status::invalid_publish_message) + { + if (node.config.logging.network_logging ()) + { + BOOST_LOG (node.log) << "Invalid publish message"; + } + } + else if (parser.status == rai::message_parser::parse_status::invalid_confirm_req_message) + { + if (node.config.logging.network_logging ()) + { + BOOST_LOG (node.log) << "Invalid confirm_req message"; + } + } + else if (parser.status == rai::message_parser::parse_status::invalid_confirm_ack_message) + { + if (node.config.logging.network_logging ()) + { + BOOST_LOG (node.log) << "Invalid confirm_ack message"; + } + } + else + { + BOOST_LOG (node.log) << "Could not deserialize buffer"; } - ++insufficient_work_count; } } else @@ -763,11 +811,8 @@ lmdb_max_dbs (128) preconfigured_representatives.push_back (rai::genesis_account); break; case rai::rai_networks::rai_beta_network: - preconfigured_peers.push_back ("rai.raiblocks.net"); - preconfigured_representatives.push_back (rai::account ("59750C057F42806F40C5D9EAA1E0263E9DB48FE385BD0172BFC573BD37EEC4A7")); - preconfigured_representatives.push_back (rai::account ("8B05C9B160DE9B006FA27DD6A368D7CA122A2EE7537C308CF22EFD3ABF5B36C3")); - preconfigured_representatives.push_back (rai::account ("91D51BF05F02698EBB4649FB06D1BBFD2E4AE2579660E8D784A002D9C0CB1BD2")); - preconfigured_representatives.push_back (rai::account ("CB35ED23D47E1A16667EDE415CD4CD05961481D7D23A43958FAE81FC12FA49FF")); + preconfigured_peers.push_back ("rai-beta.raiblocks.net"); + preconfigured_representatives.push_back (rai::account ("5DF352F3E7367A17F2ADB52B8123959602F8D94C2F295B23F6BDFFFC5FEFCA5E")); break; case rai::rai_networks::rai_live_network: preconfigured_peers.push_back ("rai.raiblocks.net"); @@ -1518,6 +1563,15 @@ block_processor_thread ([this]() { this->block_processor.process_blocks (); }) if (peers.rep_response (endpoint_a, weight_l)) { BOOST_LOG (log) << boost::str (boost::format ("Found a representative at %1%") % endpoint_a); + // Rebroadcasting all active votes to new representative + auto blocks (active.list_blocks ()); + for (auto i (blocks.begin ()), n (blocks.end ()); i != n; ++i) + { + if (*i != nullptr) + { + this->network.send_confirm_req (endpoint_a, *i); + } + } } } }); @@ -2534,16 +2588,18 @@ void rai::peer_container::rep_request (rai::endpoint const & endpoint_a) bool rai::peer_container::reachout (rai::endpoint const & endpoint_a) { - auto result (false); // Don't contact invalid IPs - result |= not_a_peer (endpoint_a); - // Don't keepalive to nodes that already sent us something - result |= known_peer (endpoint_a); - std::lock_guard lock (mutex); - auto existing (attempts.find (endpoint_a)); - result |= existing != attempts.end (); - attempts.insert ({ endpoint_a, std::chrono::steady_clock::now () }); - return result; + bool error = not_a_peer (endpoint_a); + if (!error) + { + // Don't keepalive to nodes that already sent us something + error |= known_peer (endpoint_a); + std::lock_guard lock (mutex); + auto existing (attempts.find (endpoint_a)); + error |= existing != attempts.end (); + attempts.insert ({ endpoint_a, std::chrono::steady_clock::now () }); + } + return error; } bool rai::peer_container::insert (rai::endpoint const & endpoint_a, unsigned version_a) @@ -2761,7 +2817,7 @@ rai::uint128_t rai::election::quorum_threshold (MDB_txn * transaction_a, rai::le rai::uint128_t rai::election::minimum_threshold (MDB_txn * transaction_a, rai::ledger & ledger_a) { - // Minimum number of votes needed to change our ledger, underwhich we're probably disconnected + // Minimum number of votes needed to change our ledger, under which we're probably disconnected return ledger_a.supply (transaction_a) / 16; } @@ -2854,7 +2910,7 @@ void rai::active_transactions::announce_votes () { auto election_l (i->election); node.background ([election_l]() { election_l->broadcast_winner (); }); - if (i->announcements >= contigious_announcements - 1) + if (i->announcements >= contiguous_announcements - 1) { // These blocks have reached the confirmation interval for forks i->election->confirm_cutoff (transaction); @@ -2939,6 +2995,18 @@ bool rai::active_transactions::active (rai::block const & block_a) return roots.find (block_a.root ()) != roots.end (); } +// List of active blocks in elections +std::deque> rai::active_transactions::list_blocks () +{ + std::deque> result; + std::lock_guard lock (mutex); + for (auto i (roots.begin ()), n (roots.end ()); i != n; ++i) + { + result.push_back (i->election->last_winner); + } + return result; +} + rai::active_transactions::active_transactions (rai::node & node_a) : node (node_a) { diff --git a/rai/node/node.hpp b/rai/node/node.hpp index 125000b5..4b6792f6 100644 --- a/rai/node/node.hpp +++ b/rai/node/node.hpp @@ -70,7 +70,7 @@ public: // Number of announcements in a row for this fork unsigned announcements; }; -// Core class for determining concensus +// Core class for determining consensus // Holds all active blocks i.e. recently added blocks that need confirmation class active_transactions { @@ -83,6 +83,7 @@ public: // Is the root of this block in the roots container bool active (rai::block const &); void announce_votes (); + std::deque> list_blocks (); void stop (); boost::multi_index_container< rai::conflict_info, @@ -94,7 +95,7 @@ public: // Maximum number of conflicts to vote on per interval, lowest root hash first static unsigned constexpr announcements_per_interval = 32; // After this many successive vote announcements, block is confirmed - static unsigned constexpr contigious_announcements = 4; + static unsigned constexpr contiguous_announcements = 4; static unsigned constexpr announce_interval_ms = (rai::rai_network == rai::rai_networks::rai_test_network) ? 10 : 16000; }; class operation @@ -247,7 +248,7 @@ public: void refresh_devices (); // Refresh when the lease ends void refresh_mapping (); - // Refresh ocassionally in case router loses mapping + // Refresh occasionally in case router loses mapping void check_mapping_loop (); int check_mapping (); bool has_address (); @@ -428,7 +429,7 @@ public: rai::vote_result vote (std::shared_ptr, rai::endpoint); rai::node & node; }; -// The network is crawled for representatives by ocassionally sending a unicast confirm_req for a specific block and watching to see if it's acknowledged with a vote. +// The network is crawled for representatives by occasionally sending a unicast confirm_req for a specific block and watching to see if it's acknowledged with a vote. class rep_crawler { public: diff --git a/rai/node/rpc.cpp b/rai/node/rpc.cpp index 285bc657..47366e9c 100644 --- a/rai/node/rpc.cpp +++ b/rai/node/rpc.cpp @@ -282,12 +282,7 @@ void rai::rpc_handler::account_create () auto existing (node.wallets.items.find (wallet)); if (existing != node.wallets.items.end ()) { - bool generate_work (true); - boost::optional work (request.get_optional ("work")); - if (work.is_initialized ()) - { - generate_work = work.get (); - } + const bool generate_work = request.get ("work", true); rai::account new_key (existing->second->deterministic_insert (generate_work)); if (!new_key.is_zero ()) { @@ -340,24 +335,9 @@ void rai::rpc_handler::account_info () auto error (account.decode_account (account_text)); if (!error) { - bool representative (false); - boost::optional representative_optional (request.get_optional ("representative")); - if (representative_optional.is_initialized ()) - { - representative = representative_optional.get (); - } - bool weight (false); - boost::optional weight_optional (request.get_optional ("weight")); - if (weight_optional.is_initialized ()) - { - weight = weight_optional.get (); - } - bool pending (false); - boost::optional pending_optional (request.get_optional ("pending")); - if (pending_optional.is_initialized ()) - { - pending = pending_optional.get (); - } + const bool representative = request.get ("representative", false); + const bool weight = request.get ("weight", false); + const bool pending = request.get ("pending", false); rai::transaction transaction (node.store.environment, nullptr, false); rai::account_info info; if (!node.store.account_get (transaction, account, info)) @@ -742,12 +722,7 @@ void rai::rpc_handler::accounts_create () auto existing (node.wallets.items.find (wallet)); if (existing != node.wallets.items.end ()) { - bool generate_work (true); - boost::optional work (request.get_optional ("work")); - if (work.is_initialized ()) - { - generate_work = work.get (); - } + const bool generate_work = request.get ("work", true); boost::property_tree::ptree response_l; boost::property_tree::ptree accounts; for (auto i (0); accounts.size () < count; ++i) @@ -815,7 +790,6 @@ void rai::rpc_handler::accounts_pending () { uint64_t count (std::numeric_limits::max ()); rai::uint128_union threshold (0); - bool source (false); boost::optional count_text (request.get_optional ("count")); if (count_text.is_initialized ()) { @@ -834,11 +808,7 @@ void rai::rpc_handler::accounts_pending () error_response (response, "Bad threshold number"); } } - boost::optional source_optional (request.get_optional ("source")); - if (source_optional.is_initialized ()) - { - source = source_optional.get (); - } + const bool source = request.get ("source", false); boost::property_tree::ptree response_l; boost::property_tree::ptree pending; rai::transaction transaction (node.store.environment, nullptr, false); @@ -965,18 +935,8 @@ void rai::rpc_handler::blocks () void rai::rpc_handler::blocks_info () { - bool pending (false); - boost::optional pending_optional (request.get_optional ("pending")); - if (pending_optional.is_initialized ()) - { - pending = pending_optional.get (); - } - bool source (false); - boost::optional source_optional (request.get_optional ("source")); - if (source_optional.is_initialized ()) - { - source = source_optional.get (); - } + const bool pending = request.get ("pending", false); + const bool source = request.get ("source", false); std::vector hashes; boost::property_tree::ptree response_l; boost::property_tree::ptree blocks; @@ -1622,7 +1582,8 @@ namespace class history_visitor : public rai::block_visitor { public: - history_visitor (rai::rpc_handler & handler_a, rai::transaction & transaction_a, boost::property_tree::ptree & tree_a, rai::block_hash const & hash_a) : + history_visitor (rai::rpc_handler & handler_a, bool raw_a, rai::transaction & transaction_a, boost::property_tree::ptree & tree_a, rai::block_hash const & hash_a) : + raw (raw_a), handler (handler_a), transaction (transaction_a), tree (tree_a), @@ -1637,6 +1598,11 @@ public: tree.put ("account", account); auto amount (handler.node.ledger.amount (transaction, hash).convert_to ()); tree.put ("amount", amount); + if (raw) + { + tree.put ("destination", account); + tree.put ("balance", block_a.hashables.balance.to_string_dec ()); + } } void receive_block (rai::receive_block const & block_a) { @@ -1645,11 +1611,25 @@ public: tree.put ("account", account); auto amount (handler.node.ledger.amount (transaction, hash).convert_to ()); tree.put ("amount", amount); + if (raw) + { + tree.put ("source", block_a.hashables.source.to_string ()); + } } void open_block (rai::open_block const & block_a) { - // Report opens as a receive - tree.put ("type", "receive"); + if (raw) + { + tree.put ("type", "open"); + tree.put ("representative", block_a.hashables.representative.to_account ()); + tree.put ("source", block_a.hashables.source.to_string ()); + tree.put ("opened", block_a.hashables.account.to_account ()); + } + else + { + // Report opens as a receive + tree.put ("type", "receive"); + } if (block_a.hashables.source != rai::genesis_account) { tree.put ("account", handler.node.ledger.account (transaction, block_a.hashables.source).to_account ()); @@ -1661,17 +1641,34 @@ public: tree.put ("amount", rai::genesis_amount.convert_to ()); } } - void change_block (rai::change_block const &) + void change_block (rai::change_block const & block_a) { - // Don't report change blocks + if (raw) + { + tree.put ("type", "change"); + tree.put ("representative", block_a.hashables.representative.to_account ()); + } } void utx_block (rai::utx_block const & block_a) { + if (raw) + { + tree.put ("type", "utx"); + tree.put ("representative", block_a.hashables.representative.to_account ()); + tree.put ("link", block_a.hashables.link.to_string ()); + } auto balance (block_a.hashables.balance.number ()); auto previous_balance (handler.node.ledger.balance (transaction, block_a.hashables.previous)); if (balance < previous_balance) { - tree.put ("type", "send"); + if (raw) + { + tree.put ("subtype", "send"); + } + else + { + tree.put ("type", "send"); + } tree.put ("account", block_a.hashables.account.to_account ()); tree.put ("amount", (previous_balance - balance).convert_to ()); } @@ -1679,107 +1676,121 @@ public: { if (block_a.hashables.link.is_zero ()) { - // Don't report change blocks + if (raw) + { + tree.put ("subtype", "change"); + } } else { - tree.put ("type", "receive"); + if (raw) + { + tree.put ("subtype", "receive"); + } + else + { + tree.put ("type", "receive"); + } tree.put ("account", block_a.hashables.account.to_account ()); tree.put ("amount", (balance - previous_balance).convert_to ()); } } } rai::rpc_handler & handler; + bool raw; rai::transaction & transaction; boost::property_tree::ptree & tree; rai::block_hash const & hash; }; } -void rai::rpc_handler::history () +void rai::rpc_handler::account_history () { - std::string hash_text (request.get ("hash")); std::string count_text (request.get ("count")); + bool output_raw (request.get_optional ("raw") == true); + auto error (false); rai::block_hash hash; - if (!hash.decode_hex (hash_text)) + auto head_str (request.get_optional ("head")); + rai::transaction transaction (node.store.environment, nullptr, false); + if (head_str) { - uint64_t count; - if (!decode_unsigned (count_text, count)) + error = hash.decode_hex (*head_str); + if (error) { - boost::property_tree::ptree response_l; - boost::property_tree::ptree history; - rai::transaction transaction (node.store.environment, nullptr, false); - auto block (node.store.block_get (transaction, hash)); - while (block != nullptr && count > 0) - { - boost::property_tree::ptree entry; - history_visitor visitor (*this, transaction, entry, hash); - block->visit (visitor); - if (!entry.empty ()) - { - entry.put ("hash", hash.to_string ()); - history.push_back (std::make_pair ("", entry)); - } - hash = block->previous (); - block = node.store.block_get (transaction, hash); - --count; - } - response_l.add_child ("history", history); - response (response_l); - } - else - { - error_response (response, "Invalid count limit"); + error_response (response, "Invalid block hash"); } } else { - error_response (response, "Invalid block hash"); + std::string account_text (request.get ("account")); + rai::uint256_union account; + error = account.decode_account (account_text); + if (!error) + { + hash = node.ledger.latest (transaction, account); + } + else + { + error_response (response, "Bad account number"); + } } -} - -void rai::rpc_handler::account_history () -{ - std::string account_text (request.get ("account")); - std::string count_text (request.get ("count")); - rai::uint256_union account; - auto error (account.decode_account (account_text)); if (!error) { uint64_t count; if (!decode_unsigned (count_text, count)) { - boost::property_tree::ptree response_l; - boost::property_tree::ptree history; - rai::transaction transaction (node.store.environment, nullptr, false); - auto hash (node.ledger.latest (transaction, account)); - auto block (node.store.block_get (transaction, hash)); - while (block != nullptr && count > 0) + uint64_t offset = 0; + auto offset_text (request.get_optional ("offset")); + if (!offset_text || !decode_unsigned (*offset_text, offset)) { - boost::property_tree::ptree entry; - history_visitor visitor (*this, transaction, entry, hash); - block->visit (visitor); - if (!entry.empty ()) + boost::property_tree::ptree response_l; + boost::property_tree::ptree history; + if (!error) { - entry.put ("hash", hash.to_string ()); - history.push_back (std::make_pair ("", entry)); + auto block (node.store.block_get (transaction, hash)); + while (block != nullptr && count > 0) + { + if (offset > 0) + { + --offset; + } + else + { + boost::property_tree::ptree entry; + history_visitor visitor (*this, output_raw, transaction, entry, hash); + block->visit (visitor); + if (!entry.empty ()) + { + entry.put ("hash", hash.to_string ()); + history.push_back (std::make_pair ("", entry)); + } + --count; + } + hash = block->previous (); + block = node.store.block_get (transaction, hash); + } + response_l.add_child ("history", history); + if (!hash.is_zero ()) + { + response_l.put ("previous", hash.to_string ()); + } + response (response_l); + } + else + { + error_response (response, "Failed to decode head block hash"); } - hash = block->previous (); - block = node.store.block_get (transaction, hash); - --count; } - response_l.add_child ("history", history); - response (response_l); + else + { + error_response (response, "Invalid offset"); + } } else { error_response (response, "Invalid count limit"); } } - else - { - error_response (response, "Bad account number"); - } } void rai::rpc_handler::keepalive () @@ -1843,7 +1854,6 @@ void rai::rpc_handler::ledger () { rai::account start (0); uint64_t count (std::numeric_limits::max ()); - bool sorting (false); boost::optional account_text (request.get_optional ("account")); if (account_text.is_initialized ()) { @@ -1862,29 +1872,16 @@ void rai::rpc_handler::ledger () error_response (response, "Invalid count limit"); } } - boost::optional sorting_optional (request.get_optional ("sorting")); - if (sorting_optional.is_initialized ()) + uint64_t modified_since (0); + boost::optional modified_since_text (request.get_optional ("modified_since")); + if (modified_since_text.is_initialized ()) { - sorting = sorting_optional.get (); - } - bool representative (false); - boost::optional representative_optional (request.get_optional ("representative")); - if (representative_optional.is_initialized ()) - { - representative = representative_optional.get (); - } - bool weight (false); - boost::optional weight_optional (request.get_optional ("weight")); - if (weight_optional.is_initialized ()) - { - weight = weight_optional.get (); - } - bool pending (false); - boost::optional pending_optional (request.get_optional ("pending")); - if (pending_optional.is_initialized ()) - { - pending = pending_optional.get (); + modified_since = strtoul (modified_since_text.get ().c_str (), NULL, 10); } + const bool sorting = request.get ("sorting", false); + const bool representative = request.get ("representative", false); + const bool weight = request.get ("weight", false); + const bool pending = request.get ("pending", false); boost::property_tree::ptree response_a; boost::property_tree::ptree response_l; boost::property_tree::ptree accounts; @@ -1894,33 +1891,36 @@ void rai::rpc_handler::ledger () for (auto i (node.store.latest_begin (transaction, start)), n (node.store.latest_end ()); i != n && accounts.size () < count; ++i) { rai::account_info info (i->second); - rai::account account (i->first.uint256 ()); - boost::property_tree::ptree response_l; - response_l.put ("frontier", info.head.to_string ()); - response_l.put ("open_block", info.open_block.to_string ()); - response_l.put ("representative_block", info.rep_block.to_string ()); - std::string balance; - rai::uint128_union (info.balance).encode_dec (balance); - response_l.put ("balance", balance); - response_l.put ("modified_timestamp", std::to_string (info.modified)); - response_l.put ("block_count", std::to_string (info.block_count)); - if (representative) + if (info.modified >= modified_since) { - auto block (node.store.block_get (transaction, info.rep_block)); - assert (block != nullptr); - response_l.put ("representative", block->representative ().to_account ()); + rai::account account (i->first.uint256 ()); + boost::property_tree::ptree response_l; + response_l.put ("frontier", info.head.to_string ()); + response_l.put ("open_block", info.open_block.to_string ()); + response_l.put ("representative_block", info.rep_block.to_string ()); + std::string balance; + rai::uint128_union (info.balance).encode_dec (balance); + response_l.put ("balance", balance); + response_l.put ("modified_timestamp", std::to_string (info.modified)); + response_l.put ("block_count", std::to_string (info.block_count)); + if (representative) + { + auto block (node.store.block_get (transaction, info.rep_block)); + assert (block != nullptr); + response_l.put ("representative", block->representative ().to_account ()); + } + if (weight) + { + auto account_weight (node.ledger.weight (transaction, account)); + response_l.put ("weight", account_weight.convert_to ()); + } + if (pending) + { + auto account_pending (node.ledger.account_pending (transaction, account)); + response_l.put ("pending", account_pending.convert_to ()); + } + accounts.push_back (std::make_pair (account.to_account (), response_l)); } - if (weight) - { - auto account_weight (node.ledger.weight (transaction, account)); - response_l.put ("weight", account_weight.convert_to ()); - } - if (pending) - { - auto account_pending (node.ledger.account_pending (transaction, account)); - response_l.put ("pending", account_pending.convert_to ()); - } - accounts.push_back (std::make_pair (account.to_account (), response_l)); } } else // Sorting @@ -1928,8 +1928,12 @@ void rai::rpc_handler::ledger () std::vector> ledger_l; for (auto i (node.store.latest_begin (transaction, start)), n (node.store.latest_end ()); i != n; ++i) { - rai::uint128_union balance (rai::account_info (i->second).balance); - ledger_l.push_back (std::make_pair (balance, rai::account (i->first.uint256 ()))); + rai::account_info info (i->second); + rai::uint128_union balance (info.balance); + if (info.modified >= modified_since) + { + ledger_l.push_back (std::make_pair (balance, rai::account (i->first.uint256 ()))); + } } std::sort (ledger_l.begin (), ledger_l.end ()); std::reverse (ledger_l.begin (), ledger_l.end ()); @@ -2175,7 +2179,6 @@ void rai::rpc_handler::pending () { uint64_t count (std::numeric_limits::max ()); rai::uint128_union threshold (0); - bool source (false); boost::optional count_text (request.get_optional ("count")); if (count_text.is_initialized ()) { @@ -2194,11 +2197,7 @@ void rai::rpc_handler::pending () error_response (response, "Bad threshold number"); } } - boost::optional source_optional (request.get_optional ("source")); - if (source_optional.is_initialized ()) - { - source = source_optional.get (); - } + const bool source = request.get ("source", false); boost::property_tree::ptree response_l; boost::property_tree::ptree peers_l; { @@ -2761,7 +2760,6 @@ void rai::rpc_handler::receive_minimum_set () void rai::rpc_handler::representatives () { uint64_t count (std::numeric_limits::max ()); - bool sorting (false); boost::optional count_text (request.get_optional ("count")); if (count_text.is_initialized ()) { @@ -2771,11 +2769,7 @@ void rai::rpc_handler::representatives () error_response (response, "Invalid count limit"); } } - boost::optional sorting_optional (request.get_optional ("sorting")); - if (sorting_optional.is_initialized ()) - { - sorting = sorting_optional.get (); - } + const bool sorting = request.get ("sorting", false); boost::property_tree::ptree response_l; boost::property_tree::ptree representatives; rai::transaction transaction (node.store.environment, nullptr, false); @@ -3265,12 +3259,7 @@ void rai::rpc_handler::wallet_add () auto existing (node.wallets.items.find (wallet)); if (existing != node.wallets.items.end ()) { - bool generate_work (true); - boost::optional work (request.get_optional ("work")); - if (work.is_initialized ()) - { - generate_work = work.get (); - } + const bool generate_work = request.get ("work", true); auto pub (existing->second->insert_adhoc (key, generate_work)); if (!pub.is_zero ()) { @@ -3304,6 +3293,60 @@ void rai::rpc_handler::wallet_add () } } +void rai::rpc_handler::wallet_add_watch () +{ + if (rpc.config.enable_control) + { + std::string wallet_text (request.get ("wallet")); + rai::uint256_union wallet; + auto error (wallet.decode_hex (wallet_text)); + if (!error) + { + auto existing (node.wallets.items.find (wallet)); + if (existing != node.wallets.items.end ()) + { + rai::transaction transaction (node.store.environment, nullptr, true); + if (existing->second->store.valid_password (transaction)) + { + for (auto & accounts : request.get_child ("accounts")) + { + std::string account_text = accounts.second.data (); + rai::uint256_union account; + auto error (account.decode_account (account_text)); + if (!error) + { + existing->second->insert_watch (transaction, account); + } + else + { + error_response (response, "Bad account number"); + } + } + boost::property_tree::ptree response_l; + response_l.put ("success", ""); + response (response_l); + } + else + { + error_response (response, "Wallet locked"); + } + } + else + { + error_response (response, "Wallet not found"); + } + } + else + { + error_response (response, "Bad wallet number"); + } + } + else + { + error_response (response, "RPC control is disabled"); + } +} + void rai::rpc_handler::wallet_balance_total () { std::string wallet_text (request.get ("wallet")); @@ -3636,6 +3679,79 @@ void rai::rpc_handler::wallet_key_valid () } } +void rai::rpc_handler::wallet_ledger () +{ + const bool representative = request.get ("representative", false); + const bool weight = request.get ("weight", false); + const bool pending = request.get ("pending", false); + uint64_t modified_since (0); + boost::optional modified_since_text (request.get_optional ("modified_since")); + if (modified_since_text.is_initialized ()) + { + modified_since = strtoul (modified_since_text.get ().c_str (), NULL, 10); + } + std::string wallet_text (request.get ("wallet")); + rai::uint256_union wallet; + auto error (wallet.decode_hex (wallet_text)); + if (!error) + { + auto existing (node.wallets.items.find (wallet)); + if (existing != node.wallets.items.end ()) + { + boost::property_tree::ptree response_l; + boost::property_tree::ptree accounts; + rai::transaction transaction (node.store.environment, nullptr, false); + for (auto i (existing->second->store.begin (transaction)), n (existing->second->store.end ()); i != n; ++i) + { + rai::account account (i->first.uint256 ()); + rai::account_info info; + if (!node.store.account_get (transaction, account, info)) + { + if (info.modified >= modified_since) + { + boost::property_tree::ptree entry; + entry.put ("frontier", info.head.to_string ()); + entry.put ("open_block", info.open_block.to_string ()); + entry.put ("representative_block", info.rep_block.to_string ()); + std::string balance; + rai::uint128_union (info.balance).encode_dec (balance); + entry.put ("balance", balance); + entry.put ("modified_timestamp", std::to_string (info.modified)); + entry.put ("block_count", std::to_string (info.block_count)); + if (representative) + { + auto block (node.store.block_get (transaction, info.rep_block)); + assert (block != nullptr); + entry.put ("representative", block->representative ().to_account ()); + } + if (weight) + { + auto account_weight (node.ledger.weight (transaction, account)); + entry.put ("weight", account_weight.convert_to ()); + } + if (pending) + { + auto account_pending (node.ledger.account_pending (transaction, account)); + entry.put ("pending", account_pending.convert_to ()); + } + accounts.push_back (std::make_pair (account.to_account (), entry)); + } + } + } + response_l.add_child ("accounts", accounts); + response (response_l); + } + else + { + error_response (response, "Wallet not found"); + } + } + else + { + error_response (response, "Bad wallet number"); + } +} + void rai::rpc_handler::wallet_lock () { if (rpc.config.enable_control) @@ -3683,7 +3799,6 @@ void rai::rpc_handler::wallet_pending () { uint64_t count (std::numeric_limits::max ()); rai::uint128_union threshold (0); - bool source (false); boost::optional count_text (request.get_optional ("count")); if (count_text.is_initialized ()) { @@ -3702,11 +3817,7 @@ void rai::rpc_handler::wallet_pending () error_response (response, "Bad threshold number"); } } - boost::optional source_optional (request.get_optional ("source")); - if (source_optional.is_initialized ()) - { - source = source_optional.get (); - } + const bool source = request.get ("source", false); boost::property_tree::ptree response_l; boost::property_tree::ptree pending; rai::transaction transaction (node.store.environment, nullptr, false); @@ -4453,7 +4564,8 @@ void rai::rpc_handler::process_request () } else if (action == "history") { - history (); + request.put ("head", request.get ("hash")); + account_history (); } else if (action == "keepalive") { @@ -4603,6 +4715,10 @@ void rai::rpc_handler::process_request () { wallet_add (); } + else if (action == "wallet_add_watch") + { + wallet_add_watch (); + } else if (action == "wallet_balance_total") { wallet_balance_total (); @@ -4639,6 +4755,10 @@ void rai::rpc_handler::process_request () { wallet_key_valid (); } + else if (action == "wallet_ledger") + { + wallet_ledger (); + } else if (action == "wallet_lock") { wallet_lock (); diff --git a/rai/node/rpc.hpp b/rai/node/rpc.hpp index 2bf0b6f9..91ca8a39 100644 --- a/rai/node/rpc.hpp +++ b/rai/node/rpc.hpp @@ -185,6 +185,7 @@ public: void validate_account_number (); void version (); void wallet_add (); + void wallet_add_watch (); void wallet_balance_total (); void wallet_balances (); void wallet_change_seed (); @@ -194,6 +195,7 @@ public: void wallet_export (); void wallet_frontiers (); void wallet_key_valid (); + void wallet_ledger (); void wallet_lock (); void wallet_pending (); void wallet_representative (); diff --git a/rai/node/testing.cpp b/rai/node/testing.cpp index a8f0d1dd..ff3f0021 100644 --- a/rai/node/testing.cpp +++ b/rai/node/testing.cpp @@ -410,13 +410,13 @@ void rai::landing::write_store () rai::uint128_t rai::landing::distribution_amount (uint64_t interval) { - // Halfing period ~= Exponent of 2 in secounds approixmately 1 year = 2^25 = 33554432 + // Halving period ~= Exponent of 2 in seconds approximately 1 year = 2^25 = 33554432 // Interval = Exponent of 2 in seconds approximately 1 minute = 2^10 = 64 uint64_t intervals_per_period (1 << (25 - interval_exponent)); rai::uint128_t result; if (interval < intervals_per_period * 1) { - // Total supply / 2^halfing period / intervals per period + // Total supply / 2^halving period / intervals per period // 2^128 / 2^1 / (2^25 / 2^10) result = rai::uint128_t (1) << (127 - (25 - interval_exponent)); // 50% } diff --git a/rai/node/utility.hpp b/rai/node/utility.hpp index c6b55955..d5fb7c87 100644 --- a/rai/node/utility.hpp +++ b/rai/node/utility.hpp @@ -26,7 +26,7 @@ using vectorstream = boost::iostreams::stream_buffer diff --git a/rai/node/wallet.cpp b/rai/node/wallet.cpp index 27347a6d..d0297d1e 100644 --- a/rai/node/wallet.cpp +++ b/rai/node/wallet.cpp @@ -252,7 +252,7 @@ unsigned const rai::wallet_store::version_3 (3); unsigned const rai::wallet_store::version_current (version_3); // Wallet version number rai::uint256_union const rai::wallet_store::version_special (0); -// Random number used to salt private key encription +// Random number used to salt private key encryption rai::uint256_union const rai::wallet_store::salt_special (1); // Key used to encrypt wallet keys, encrypted itself by the user password rai::uint256_union const rai::wallet_store::wallet_key_special (2); @@ -420,6 +420,11 @@ rai::public_key rai::wallet_store::insert_adhoc (MDB_txn * transaction_a, rai::r return pub; } +void rai::wallet_store::insert_watch (MDB_txn * transaction_a, rai::public_key const & pub) +{ + entry_put_raw (transaction_a, pub, rai::wallet_value (rai::uint256_union (0), 0)); +} + void rai::wallet_store::erase (MDB_txn * transaction_a, rai::public_key const & pub) { auto status (mdb_del (transaction_a, handle, rai::mdb_val (pub), nullptr)); @@ -790,6 +795,11 @@ rai::public_key rai::wallet::insert_adhoc (rai::raw_key const & account_a, bool return result; } +void rai::wallet::insert_watch (MDB_txn * transaction_a, rai::public_key const & pub_a) +{ + store.insert_watch (transaction_a, pub_a); +} + bool rai::wallet::exists (rai::public_key const & account_a) { rai::transaction transaction (store.environment, nullptr, false); @@ -1159,7 +1169,11 @@ public: { for (auto i (wallet_a->store.begin (transaction_a)), n (wallet_a->store.end ()); i != n; ++i) { - keys.insert (i->first.uint256 ()); + // Don't search pending for watch-only accounts + if (!rai::wallet_value (i->second).key.is_zero ()) + { + keys.insert (i->first.uint256 ()); + } } } void run () diff --git a/rai/node/wallet.hpp b/rai/node/wallet.hpp index b354b95b..836aedfa 100644 --- a/rai/node/wallet.hpp +++ b/rai/node/wallet.hpp @@ -74,6 +74,7 @@ public: rai::account representative (MDB_txn *); void representative_set (MDB_txn *, rai::account const &); rai::public_key insert_adhoc (MDB_txn *, rai::raw_key const &); + void insert_watch (MDB_txn *, rai::public_key const &); void erase (MDB_txn *, rai::public_key const &); rai::wallet_value entry_get_raw (MDB_txn *, rai::public_key const &); void entry_put_raw (MDB_txn *, rai::public_key const &, rai::wallet_value const &); @@ -132,6 +133,7 @@ public: bool enter_password (std::string const &); rai::public_key insert_adhoc (rai::raw_key const &, bool = true); rai::public_key insert_adhoc (MDB_txn *, rai::raw_key const &, bool = true); + void insert_watch (MDB_txn *, rai::public_key const &); rai::public_key deterministic_insert (MDB_txn *, bool = true); rai::public_key deterministic_insert (bool = true); bool exists (rai::public_key const &); diff --git a/rai/qt/qt.cpp b/rai/qt/qt.cpp index 5db861be..42dd4f4a 100644 --- a/rai/qt/qt.cpp +++ b/rai/qt/qt.cpp @@ -1394,7 +1394,7 @@ wallet (wallet_a) auto block (this->wallet.wallet_m->change_sync (this->wallet.account, representative_l)); change_rep->setEnabled (true); show_button_success (*change_rep); - change_rep->setText ("Represenative was changed"); + change_rep->setText ("Representative was changed"); current_representative->setText (QString (representative_l.to_account_split ().c_str ())); new_representative->clear (); this->wallet.node.alarm.add (std::chrono::steady_clock::now () + std::chrono::seconds (5), [this]() { diff --git a/rai/rai_node/daemon.cpp b/rai/rai_node/daemon.cpp index 6c84bc3c..181aac00 100644 --- a/rai/rai_node/daemon.cpp +++ b/rai/rai_node/daemon.cpp @@ -124,7 +124,7 @@ void rai_daemon::daemon::run (boost::filesystem::path const & data_path) { rpc->start (); } - runner.reset (new rai::thread_runner (service, node->config.io_threads)); + runner = std::make_unique (service, node->config.io_threads); runner->join (); } else