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 @@
+
+
+

+
+
+
[](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