Merge master into ublocks (#714)

* Fix typos (#683)

* Move to C++14

Replaces a single reset with make_unique to see if things compile in CI. Also reverts a CI-specific RPATH change which prevented the wallet from running on macOS dev machines

* Update CMakeLists.txt to support gtest linking for Windows

* Fix test for wallet_create_max to allow for utx database addition (#691)

* Remove unused code (#686)

* Fix docker boost url

* Revert nonWalletDbs 

Revert to fix failing tests

* Improved error handling in message parser (#704)

*  Replacing bool get_optional with get default values (#670)

* Revert "Watch only accounts"

This reverts commit 1b0809a3af.

* Replacing bool get_optional with get default values

* RPC tests for optional bool values

* RPC tests for wallet_ledger & wallet_add_watch

* Improve account history RPC endpoint (#668)

* Checking ledger::forked_block preconditions which were asserting.

* Update README (#709)

* update docs to add guides and update "what is nano?" system

* add whitepaper to guides and docs

* update features list

* update what is nano header level

* Build of Boost Cleanup (#707)

* Updated to check the boost SHA256 when downloading

* Fixed invalid shell option

* Allow the user to request a minimal build of Boost

* Ensure boost build script exits on error

* Removed apparently unused "mkdir"

* Docker altnets (#708)

* Updated to check the boost SHA256 when downloading

* Fixed invalid shell option

* Allow the user to request a minimal build of Boost

* Ensure boost build script exits on error

* Removed apparently unused "mkdir"

* Updated Docker build for node to support altnets

* Allow user-supplied arguments to "docker build"

* Updated Docker auto deploy to build a live and beta image for each tag

* Print the correct image name when building the docker image

* Corrected typo for the test network

* Broadcasting active elections blocks to discovered representatives (#711)

* Broadcasting active elections blocks

* Improve formatting for clang

* Connecting to rai-beta for beta network.

* Specify beta network representatives (#712)

* Update the beta network configuration (#713)

* Specify beta network representatives

* Update beta network genesis block

* Use port 54000 for beta nodes

* Add history RPC raw support to ublocks

* Fix test for wallet_create_max to allow for utx database addition (#691)
This commit is contained in:
Lee Bousfield 2018-03-08 16:50:48 -07:00 committed by clemahieu
commit 755dae8612
36 changed files with 1006 additions and 455 deletions

View file

@ -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")

View file

@ -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`.

View file

@ -1,33 +1,53 @@
<hr />
<div align="center">
<img src="images/logo.svg" alt="Logo" width='300px' height='auto'/>
</div>
<hr />
[![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

View file

@ -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}"

View file

@ -1,8 +1,17 @@
#!/bin/bash
set -eu
if [ "$#" -lt 2 ]; then
echo 'Usage: build-docker-image.sh <dockerFile> <dockerImageTag> [<dockerBuildArgs>...]' >&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}" .

View file

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

View file

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

View file

@ -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"]

View file

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

View file

@ -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"
}
}

View file

@ -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"
}
}

View file

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

1
images/logo.svg Normal file
View file

@ -0,0 +1 @@
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 216 29.4"><style>.st0{fill:#4a90e2}.st1{fill:#000034}</style><circle class="st0" cx="4.8" cy="24.4" r="4.8"/><path class="st0" d="M62 .6c-2.6 0-4.8 2.1-4.8 4.8 0 3.8-.6 4.8-4.8 4.8H52c-2.4.2-4.3 2.2-4.3 4.7v.1c0 3.7-.7 4.6-4.8 4.6-.2 0-.4 0-.5.1-2.4.3-4.3 2.3-4.3 4.7 0 2.6 2.1 4.8 4.8 4.8 2.5 0 4.6-2 4.7-4.4v-.4c0-3.4 1.1-4.7 4.7-4.8h.1c2.5 0 4.6-2 4.7-4.5v-.3c0-3.5 1.1-4.8 4.8-4.8 2.6 0 4.8-2.1 4.8-4.8 0-2.5-2.1-4.6-4.7-4.6zM33.8 10.2h-.4c-4.2 0-4.8-1-4.8-4.8 0-2.6-2.1-4.8-4.8-4.8C21.2.6 19 2.7 19 5.4c0 3.8-.6 4.7-4.8 4.7h-.4c-2.4.2-4.3 2.2-4.3 4.7 0 2.6 2.1 4.8 4.8 4.8 2.5 0 4.6-2 4.7-4.4v-.3c0-3.5 1.1-4.8 4.8-4.8 3.7 0 4.8 1.3 4.8 4.7 0 2.6 2.1 4.8 4.8 4.8s4.8-2.1 4.8-4.8c-.1-2.4-2-4.4-4.4-4.6z"/><g><path class="st1" d="M109.3.8c-.3 0-.6.1-.8.4-.2.2-.3.5-.3.9V25L90.7 1.4c-.3-.4-.6-.6-1-.6s-.7.1-.9.4c-.2.2-.3.5-.3.9v26c0 .4.1.7.3.9.2.2.5.3.9.3.3 0 .6-.1.8-.4.2-.2.3-.5.3-.9V5.2l17.5 23.6c.3.4.7.6 1.1.6.4 0 .7-.1.9-.4.2-.2.3-.5.3-.9v-26c0-.4-.1-.7-.3-.9-.3-.3-.6-.4-1-.4zm92-.8c-8.1 0-14.7 6.6-14.7 14.7s6.6 14.7 14.7 14.7S216 22.8 216 14.7 209.4 0 201.3 0zm0 27c-6.8 0-12.3-5.5-12.3-12.3s5.5-12.3 12.3-12.3 12.3 5.5 12.3 12.3S208.1 27 201.3 27zM177 .8c-.3 0-.6.1-.8.4-.2.2-.3.5-.3.9V25L158.4 1.4c-.3-.4-.6-.6-1-.6s-.7.1-.9.4c-.2.2-.3.5-.3.9v26c0 .4.1.7.3.9.2.2.5.3.9.3.3 0 .6-.1.8-.4.2-.2.3-.5.3-.9V5.2L176 28.8c.3.4.7.6 1.1.6.4 0 .7-.1.9-.4.2-.2.3-.5.3-.9v-26c0-.4-.1-.7-.3-.9-.3-.3-.6-.4-1-.4zm-42.3.8c-.1-.3-.3-.5-.5-.6-.3-.2-.5-.2-.8-.2-.6 0-1.1.3-1.3.9l-11.7 26.2c-.1.1-.1.3-.1.4 0 .3.1.6.3.8.2.2.5.3.8.3.5 0 .9-.3 1.1-.8l3.1-7h15.5l3 7c.1.2.3.4.5.6.2.1.4.2.6.2.3 0 .6-.1.8-.3.2-.2.4-.4.4-.7 0-.2 0-.3-.1-.5L134.7 1.6zm-8.2 17.9L133.3 4l6.8 15.5h-13.6z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -827,35 +827,6 @@ rai::block_counts rai::block_store::block_count (MDB_txn * transaction_a)
return result;
}
std::unordered_multimap<rai::block_hash, rai::block_hash> rai::block_store::block_dependencies (MDB_txn * transaction_a)
{
std::unordered_multimap<rai::block_hash, rai::block_hash> 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<std::mutex> lock (cache_mutex);

View file

@ -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<rai::block_hash, rai::block_hash> 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 &);

View file

@ -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<rai::send_block *> (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)

View file

@ -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);

View file

@ -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);
}

View file

@ -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));
}

View file

@ -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<std::string> ("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<std::string> (""));
blocks[hash] = amount;
boost::optional<std::string> source (i->second.get_optional<std::string> ("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<std::string> (""));
blocks[hash] = amount;
boost::optional<std::string> source (i->second.get_optional<std::string> ("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<std::string> (""));
blocks[hash] = amount;
boost::optional<std::string> source (i->second.get_optional<std::string> ("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<std::string> ("block_count"));
ASSERT_EQ ("2", block_count);
boost::optional<std::string> weight (response.json.get_optional<std::string> ("weight"));
ASSERT_FALSE (weight.is_initialized ());
boost::optional<std::string> pending (response.json.get_optional<std::string> ("pending"));
ASSERT_FALSE (pending.is_initialized ());
boost::optional<std::string> representative (response.json.get_optional<std::string> ("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<std::string> ("weight"));
ASSERT_EQ ("100", weight2);
std::string pending2 (response2.json.get<std::string> ("pending"));
ASSERT_EQ ("0", pending2);
std::string representative2 (response2.json.get<std::string> ("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<std::string> (), amount_text);
std::string blocks_text (blocks.second.get<std::string> ("contents"));
ASSERT_FALSE (blocks_text.empty ());
boost::optional<std::string> pending (blocks.second.get_optional<std::string> ("pending"));
ASSERT_FALSE (pending.is_initialized ());
boost::optional<std::string> source (blocks.second.get_optional<std::string> ("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<std::string> ("source_account"));
ASSERT_EQ ("0", source);
std::string pending (blocks.second.get<std::string> ("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<std::string> ("block_count"));
ASSERT_EQ ("1", block_count);
boost::optional<std::string> weight (accounts.second.get_optional<std::string> ("weight"));
ASSERT_FALSE (weight.is_initialized ());
boost::optional<std::string> pending (accounts.second.get_optional<std::string> ("pending"));
ASSERT_FALSE (pending.is_initialized ());
boost::optional<std::string> representative (accounts.second.get_optional<std::string> ("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<std::string> weight (accounts.second.get_optional<std::string> ("weight"));
ASSERT_TRUE (weight.is_initialized ());
ASSERT_EQ ("0", weight.get ());
boost::optional<std::string> pending (accounts.second.get_optional<std::string> ("pending"));
ASSERT_TRUE (pending.is_initialized ());
ASSERT_EQ ("0", pending.get ());
boost::optional<std::string> representative (accounts.second.get_optional<std::string> ("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<std::string> ("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<std::string> ("frontier"));
ASSERT_EQ (open.hash ().to_string (), frontier);
std::string open_block (accounts.second.get<std::string> ("open_block"));
ASSERT_EQ (open.hash ().to_string (), open_block);
std::string representative_block (accounts.second.get<std::string> ("representative_block"));
ASSERT_EQ (open.hash ().to_string (), representative_block);
std::string balance_text (accounts.second.get<std::string> ("balance"));
ASSERT_EQ ("340282366920938463463374607431768211355", balance_text);
std::string modified_timestamp (accounts.second.get<std::string> ("modified_timestamp"));
ASSERT_EQ (std::to_string (time), modified_timestamp);
std::string block_count (accounts.second.get<std::string> ("block_count"));
ASSERT_EQ ("1", block_count);
boost::optional<std::string> weight (accounts.second.get_optional<std::string> ("weight"));
ASSERT_FALSE (weight.is_initialized ());
boost::optional<std::string> pending (accounts.second.get_optional<std::string> ("pending"));
ASSERT_FALSE (pending.is_initialized ());
boost::optional<std::string> representative (accounts.second.get_optional<std::string> ("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<std::string> weight (accounts.second.get_optional<std::string> ("weight"));
ASSERT_TRUE (weight.is_initialized ());
ASSERT_EQ ("0", weight.get ());
boost::optional<std::string> pending (accounts.second.get_optional<std::string> ("pending"));
ASSERT_TRUE (pending.is_initialized ());
ASSERT_EQ ("0", pending.get ());
boost::optional<std::string> representative (accounts.second.get_optional<std::string> ("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<std::string> ("success"));
ASSERT_TRUE (success.empty ());
ASSERT_TRUE (system.wallet (0)->exists (rai::test_genesis_key.pub));
}

View file

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

View file

@ -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);

View file

@ -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<rai::block> block_a, bool from_processor)
{
std::weak_ptr<rai::bootstrap_attempt> this_w (shared_from_this ());
std::shared_ptr<rai::block> 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<rai::block>, bool resolved) {
if (auto this_l = this_w.lock ())
{
if (resolved)
std::shared_ptr<rai::block> 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<rai::block>, bool resolved) {
if (auto this_l = this_w.lock ())
{
if (resolved)
{
std::unique_lock<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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);
}
}
}

View file

@ -1,3 +1,4 @@
#include <rai/node/common.hpp>
#include <rai/lib/work.hpp>
@ -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<uint8_t, 16> 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

View file

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

View file

@ -254,7 +254,7 @@ void rai::network::broadcast_confirm_req (std::shared_ptr<rai::block> 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<std::mutex> 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<std::mutex> 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<std::shared_ptr<rai::block>> rai::active_transactions::list_blocks ()
{
std::deque<std::shared_ptr<rai::block>> result;
std::lock_guard<std::mutex> 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)
{

View file

@ -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<std::shared_ptr<rai::block>> 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::vote>, 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:

View file

@ -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<bool> work (request.get_optional<bool> ("work"));
if (work.is_initialized ())
{
generate_work = work.get ();
}
const bool generate_work = request.get<bool> ("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<bool> representative_optional (request.get_optional<bool> ("representative"));
if (representative_optional.is_initialized ())
{
representative = representative_optional.get ();
}
bool weight (false);
boost::optional<bool> weight_optional (request.get_optional<bool> ("weight"));
if (weight_optional.is_initialized ())
{
weight = weight_optional.get ();
}
bool pending (false);
boost::optional<bool> pending_optional (request.get_optional<bool> ("pending"));
if (pending_optional.is_initialized ())
{
pending = pending_optional.get ();
}
const bool representative = request.get<bool> ("representative", false);
const bool weight = request.get<bool> ("weight", false);
const bool pending = request.get<bool> ("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<bool> work (request.get_optional<bool> ("work"));
if (work.is_initialized ())
{
generate_work = work.get ();
}
const bool generate_work = request.get<bool> ("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<uint64_t>::max ());
rai::uint128_union threshold (0);
bool source (false);
boost::optional<std::string> count_text (request.get_optional<std::string> ("count"));
if (count_text.is_initialized ())
{
@ -834,11 +808,7 @@ void rai::rpc_handler::accounts_pending ()
error_response (response, "Bad threshold number");
}
}
boost::optional<bool> source_optional (request.get_optional<bool> ("source"));
if (source_optional.is_initialized ())
{
source = source_optional.get ();
}
const bool source = request.get<bool> ("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<bool> pending_optional (request.get_optional<bool> ("pending"));
if (pending_optional.is_initialized ())
{
pending = pending_optional.get ();
}
bool source (false);
boost::optional<bool> source_optional (request.get_optional<bool> ("source"));
if (source_optional.is_initialized ())
{
source = source_optional.get ();
}
const bool pending = request.get<bool> ("pending", false);
const bool source = request.get<bool> ("source", false);
std::vector<std::string> 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<std::string> ());
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<std::string> ());
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<std::string> ());
}
}
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<std::string> ());
}
@ -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<std::string> ());
}
}
}
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<std::string> ("hash"));
std::string count_text (request.get<std::string> ("count"));
bool output_raw (request.get_optional<bool> ("raw") == true);
auto error (false);
rai::block_hash hash;
if (!hash.decode_hex (hash_text))
auto head_str (request.get_optional<std::string> ("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<std::string> ("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<std::string> ("account"));
std::string count_text (request.get<std::string> ("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<std::string> ("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<uint64_t>::max ());
bool sorting (false);
boost::optional<std::string> account_text (request.get_optional<std::string> ("account"));
if (account_text.is_initialized ())
{
@ -1862,29 +1872,16 @@ void rai::rpc_handler::ledger ()
error_response (response, "Invalid count limit");
}
}
boost::optional<bool> sorting_optional (request.get_optional<bool> ("sorting"));
if (sorting_optional.is_initialized ())
uint64_t modified_since (0);
boost::optional<std::string> modified_since_text (request.get_optional<std::string> ("modified_since"));
if (modified_since_text.is_initialized ())
{
sorting = sorting_optional.get ();
}
bool representative (false);
boost::optional<bool> representative_optional (request.get_optional<bool> ("representative"));
if (representative_optional.is_initialized ())
{
representative = representative_optional.get ();
}
bool weight (false);
boost::optional<bool> weight_optional (request.get_optional<bool> ("weight"));
if (weight_optional.is_initialized ())
{
weight = weight_optional.get ();
}
bool pending (false);
boost::optional<bool> pending_optional (request.get_optional<bool> ("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<bool> ("sorting", false);
const bool representative = request.get<bool> ("representative", false);
const bool weight = request.get<bool> ("weight", false);
const bool pending = request.get<bool> ("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<std::string> ());
}
if (pending)
{
auto account_pending (node.ledger.account_pending (transaction, account));
response_l.put ("pending", account_pending.convert_to<std::string> ());
}
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<std::string> ());
}
if (pending)
{
auto account_pending (node.ledger.account_pending (transaction, account));
response_l.put ("pending", account_pending.convert_to<std::string> ());
}
accounts.push_back (std::make_pair (account.to_account (), response_l));
}
}
else // Sorting
@ -1928,8 +1928,12 @@ void rai::rpc_handler::ledger ()
std::vector<std::pair<rai::uint128_union, rai::account>> 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<uint64_t>::max ());
rai::uint128_union threshold (0);
bool source (false);
boost::optional<std::string> count_text (request.get_optional<std::string> ("count"));
if (count_text.is_initialized ())
{
@ -2194,11 +2197,7 @@ void rai::rpc_handler::pending ()
error_response (response, "Bad threshold number");
}
}
boost::optional<bool> source_optional (request.get_optional<bool> ("source"));
if (source_optional.is_initialized ())
{
source = source_optional.get ();
}
const bool source = request.get<bool> ("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<uint64_t>::max ());
bool sorting (false);
boost::optional<std::string> count_text (request.get_optional<std::string> ("count"));
if (count_text.is_initialized ())
{
@ -2771,11 +2769,7 @@ void rai::rpc_handler::representatives ()
error_response (response, "Invalid count limit");
}
}
boost::optional<bool> sorting_optional (request.get_optional<bool> ("sorting"));
if (sorting_optional.is_initialized ())
{
sorting = sorting_optional.get ();
}
const bool sorting = request.get<bool> ("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<bool> work (request.get_optional<bool> ("work"));
if (work.is_initialized ())
{
generate_work = work.get ();
}
const bool generate_work = request.get<bool> ("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<std::string> ("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<std::string> ("wallet"));
@ -3636,6 +3679,79 @@ void rai::rpc_handler::wallet_key_valid ()
}
}
void rai::rpc_handler::wallet_ledger ()
{
const bool representative = request.get<bool> ("representative", false);
const bool weight = request.get<bool> ("weight", false);
const bool pending = request.get<bool> ("pending", false);
uint64_t modified_since (0);
boost::optional<std::string> modified_since_text (request.get_optional<std::string> ("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<std::string> ("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<std::string> ());
}
if (pending)
{
auto account_pending (node.ledger.account_pending (transaction, account));
entry.put ("pending", account_pending.convert_to<std::string> ());
}
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<uint64_t>::max ());
rai::uint128_union threshold (0);
bool source (false);
boost::optional<std::string> count_text (request.get_optional<std::string> ("count"));
if (count_text.is_initialized ())
{
@ -3702,11 +3817,7 @@ void rai::rpc_handler::wallet_pending ()
error_response (response, "Bad threshold number");
}
}
boost::optional<bool> source_optional (request.get_optional<bool> ("source"));
if (source_optional.is_initialized ())
{
source = source_optional.get ();
}
const bool source = request.get<bool> ("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<std::string> ("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 ();

View file

@ -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 ();

View file

@ -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%
}

View file

@ -26,7 +26,7 @@ using vectorstream = boost::iostreams::stream_buffer<boost::iostreams::back_inse
boost::filesystem::path working_path ();
// Get a unique path within the home directory, used for testing
boost::filesystem::path unique_path ();
// C++ stream are absolutely horrible so I need this helper function to do the most basic operation of creating a file if it doesn't exist or truntacing it.
// C++ stream are absolutely horrible so I need this helper function to do the most basic operation of creating a file if it doesn't exist or truncating it.
void open_or_create (std::fstream &, std::string const &);
// Reads a json object from the stream and if was changed, write the object back to the stream
template <typename T>

View file

@ -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 ()

View file

@ -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 &);

View file

@ -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]() {

View file

@ -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<rai::thread_runner> (service, node->config.io_threads);
runner->join ();
}
else