aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.dockerignore1
-rw-r--r--CONTRIBUTING.md11
-rw-r--r--Dockerfile67
-rw-r--r--README.md7
-rw-r--r--cmake/FindReadline.cmake4
-rw-r--r--contrib/epee/include/console_handler.h24
-rw-r--r--contrib/epee/include/misc_log_ex.h2
-rw-r--r--contrib/epee/include/net/http_protocol_handler.inl3
-rw-r--r--contrib/epee/include/net/net_utils_base.h28
-rw-r--r--contrib/epee/include/serialization/keyvalue_serialization_overloads.h4
-rw-r--r--contrib/snap/snapcraft.yaml6
-rw-r--r--external/db_drivers/liblmdb/Makefile5
-rw-r--r--external/db_drivers/liblmdb/mdb_drop.140
-rw-r--r--external/db_drivers/liblmdb/mdb_drop.c135
-rw-r--r--external/db_drivers/liblmdb/tooltag5
-rw-r--r--external/easylogging++/easylogging++.cc1
-rw-r--r--external/easylogging++/easylogging++.h11
-rw-r--r--src/blockchain_db/blockchain_db.cpp4
-rw-r--r--src/common/perf_timer.h3
-rw-r--r--src/common/util.cpp2
-rw-r--r--src/crypto/crypto-ops.c663
-rw-r--r--src/crypto/crypto-ops.h3
-rw-r--r--src/cryptonote_basic/cryptonote_boost_serialization.h29
-rw-r--r--src/cryptonote_core/blockchain.cpp35
-rw-r--r--src/cryptonote_core/cryptonote_core.cpp19
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.cpp8
-rw-r--r--src/cryptonote_core/cryptonote_tx_utils.h2
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_defs.h4
-rw-r--r--src/cryptonote_protocol/cryptonote_protocol_handler.inl6
-rw-r--r--src/daemon/command_server.cpp136
-rw-r--r--src/daemon/command_server.h1
-rw-r--r--src/daemon/daemon.cpp40
-rw-r--r--src/daemon/rpc.h28
-rw-r--r--src/ringct/CMakeLists.txt7
-rw-r--r--src/ringct/bulletproofs.cc761
-rw-r--r--src/ringct/bulletproofs.h47
-rw-r--r--src/ringct/rctOps.cpp14
-rw-r--r--src/ringct/rctOps.h2
-rw-r--r--src/ringct/rctSigs.cpp129
-rw-r--r--src/ringct/rctSigs.h4
-rw-r--r--src/ringct/rctTypes.h87
-rw-r--r--src/rpc/core_rpc_server.cpp33
-rw-r--r--src/rpc/core_rpc_server.h7
-rw-r--r--src/rpc/core_rpc_server_commands_defs.h4
-rw-r--r--src/serialization/json_object.cpp41
-rw-r--r--src/serialization/json_object.h3
-rw-r--r--src/simplewallet/simplewallet.cpp390
-rw-r--r--src/simplewallet/simplewallet.h4
-rw-r--r--src/wallet/wallet2.cpp122
-rw-r--r--src/wallet/wallet2.h3
-rw-r--r--tests/CMakeLists.txt4
-rw-r--r--tests/core_tests/rct.cpp2
-rw-r--r--tests/performance_tests/check_tx_signature.h2
-rw-r--r--tests/unit_tests/CMakeLists.txt1
-rw-r--r--tests/unit_tests/bulletproofs.cpp71
-rw-r--r--utils/build_scripts/android32.Dockerfile15
-rw-r--r--utils/build_scripts/android64.Dockerfile14
-rw-r--r--utils/systemd/monerod.service3
58 files changed, 2782 insertions, 325 deletions
diff --git a/.dockerignore b/.dockerignore
deleted file mode 100644
index 72e8ffc0d..000000000
--- a/.dockerignore
+++ /dev/null
@@ -1 +0,0 @@
-*
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 86eb45191..72571920a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -25,6 +25,17 @@ modifying is encourgaged. Proper squashing should be done (eg, if
you're making a buggy patch, then a later patch to fix the bug,
both patches should be merged).
+If you've made random unrelated changes (either because your editor
+is annoying or you made them for other reasons), you can select
+what changes go into the coming commit using git add -p, which
+walks you through all the changes and asks whether or not to
+include this particular change. This helps create clean patches
+without any irrelevant changes. git diff will show you the changes
+in your tree. git diff --cached will show what is currently staged
+for commit. As you add hunks with git add -p, those hunks will
+"move" from the git diff output to the git diff --cached output,
+so you can see clearly what your commit is going to look like.
+
## Commits and Pull Requests
Commit messages should be sensible. That means a subject line that
diff --git a/Dockerfile b/Dockerfile
index 37f212b73..71b0658a0 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,31 +1,38 @@
+# Multistage docker build, requires docker 17.05
+
+# builder stage
+FROM ubuntu:16.04 as builder
+
+RUN apt-get update && \
+ apt-get --no-install-recommends --yes install \
+ ca-certificates \
+ cmake \
+ g++ \
+ libboost1.58-all-dev \
+ libssl-dev \
+ libzmq3-dev \
+ libreadline-dev \
+ libsodium-dev \
+ make \
+ pkg-config \
+ graphviz \
+ doxygen \
+ git
+
+WORKDIR /src
+COPY . .
+RUN rm -rf build && \
+ make -j$(nproc) release-static
+
+# runtime stage
FROM ubuntu:16.04
-ENV SRC_DIR /usr/local/src/monero
-
-RUN set -x \
- && buildDeps=' \
- ca-certificates \
- cmake \
- g++ \
- git \
- libboost1.58-all-dev \
- libssl-dev \
- libzmq3-dev \
- libsodium-dev \
- make \
- pkg-config \
- ' \
- && apt-get -qq update \
- && apt-get -qq --no-install-recommends install $buildDeps
-
-RUN git clone https://github.com/monero-project/monero.git $SRC_DIR
-WORKDIR $SRC_DIR
-RUN make -j$(nproc) release-static
-
-RUN cp build/release/bin/* /usr/local/bin/ \
- \
- && rm -r $SRC_DIR \
- && apt-get -qq --auto-remove purge $buildDeps
+RUN apt-get update && \
+ apt-get --no-install-recommends --yes install ca-certificates && \
+ apt-get clean && \
+ rm -rf /var/lib/apt
+
+COPY --from=builder /src/build/release/bin/* /usr/local/bin/
# Contains the blockchain
VOLUME /root/.bitmonero
@@ -35,13 +42,7 @@ VOLUME /root/.bitmonero
# monero-wallet-cli
VOLUME /wallet
-ENV LOG_LEVEL 0
-ENV P2P_BIND_IP 0.0.0.0
-ENV P2P_BIND_PORT 18080
-ENV RPC_BIND_IP 127.0.0.1
-ENV RPC_BIND_PORT 18081
-
EXPOSE 18080
EXPOSE 18081
-CMD monerod --log-level=$LOG_LEVEL --p2p-bind-ip=$P2P_BIND_IP --p2p-bind-port=$P2P_BIND_PORT --rpc-bind-ip=$RPC_BIND_IP --rpc-bind-port=$RPC_BIND_PORT
+ENTRYPOINT ["monerod", "--p2p-bind-ip=0.0.0.0", "--p2p-bind-port=18080", "--rpc-bind-ip=127.0.0.1", "--rpc-bind-port=18081", "--non-interactive"]
diff --git a/README.md b/README.md
index 2dec65465..4386aacaa 100644
--- a/README.md
+++ b/README.md
@@ -111,6 +111,10 @@ Dates are provided in the format YYYY-MM-DD.
X's indicate that these details have not been determined as of commit date, 2017-09-20.
+## Monero release staging schedule and protocol
+
+Approximately 3 months prior to a Network Consensus Protocol Upgrade, a branch from Master will be created with the new release version tag. Pull requests that address bugs should then be made to both Master and the new release branch. Pull requests that require extensive review and testing (generally, optmizations and new features) should *not* be made to the release branch.
+
## Installing Monero from a Package
Packages are available for
@@ -409,6 +413,9 @@ Build the cppzmq bindings.
We assume you are compiling with a non-root user and you have `doas` enabled.
```
+# Create a library link so cmake is able to find it
+doas ln -s /usr/local/lib/libzmq.so.4.1 /usr/local/lib/libzmq.so
+
# Create cppzmq building directory
mkdir ~/cppzmq
cd ~/cppzmq
diff --git a/cmake/FindReadline.cmake b/cmake/FindReadline.cmake
index 9ccef7ad8..5a8aa7b0a 100644
--- a/cmake/FindReadline.cmake
+++ b/cmake/FindReadline.cmake
@@ -64,3 +64,7 @@ main()
char * s = rl_copy_text(0, 0);
}
" GNU_READLINE_FOUND)
+
+if(NOT Readline_LIBRARY)
+ set(Readline_LIBRARY "")
+endif()
diff --git a/contrib/epee/include/console_handler.h b/contrib/epee/include/console_handler.h
index b8336b270..6d369d4d8 100644
--- a/contrib/epee/include/console_handler.h
+++ b/contrib/epee/include/console_handler.h
@@ -456,29 +456,35 @@ eof:
class command_handler {
public:
typedef boost::function<bool (const std::vector<std::string> &)> callback;
- typedef std::map<std::string, std::pair<callback, std::string> > lookup;
+ typedef std::map<std::string, std::pair<callback, std::pair<std::string, std::string>>> lookup;
std::string get_usage()
{
std::stringstream ss;
- size_t max_command_len = 0;
- for(auto& x:m_command_handlers)
- if(x.first.size() > max_command_len)
- max_command_len = x.first.size();
for(auto& x:m_command_handlers)
{
- ss.width(max_command_len + 3);
- ss << std::left << x.first << x.second.second << ENDL;
+ ss << x.second.second.first << ENDL;
}
return ss.str();
}
- void set_handler(const std::string& cmd, const callback& hndlr, const std::string& usage = "")
+ std::pair<std::string, std::string> get_documentation(const std::vector<std::string>& cmd)
+ {
+ if(cmd.empty())
+ return std::make_pair("", "");
+ auto it = m_command_handlers.find(cmd.front());
+ if(it == m_command_handlers.end())
+ return std::make_pair("", "");
+ return it->second.second;
+ }
+
+ void set_handler(const std::string& cmd, const callback& hndlr, const std::string& usage = "", const std::string& description = "")
{
lookup::mapped_type & vt = m_command_handlers[cmd];
vt.first = hndlr;
- vt.second = usage;
+ vt.second.first = description.empty() ? cmd : usage;
+ vt.second.second = description.empty() ? usage : description;
#ifdef HAVE_READLINE
rdln::readline_buffer::add_completion(cmd);
#endif
diff --git a/contrib/epee/include/misc_log_ex.h b/contrib/epee/include/misc_log_ex.h
index 7ac07d112..67fd93206 100644
--- a/contrib/epee/include/misc_log_ex.h
+++ b/contrib/epee/include/misc_log_ex.h
@@ -169,7 +169,7 @@ namespace debug
#define ASSERT_MES_AND_THROW(message) {LOG_ERROR(message); std::stringstream ss; ss << message; throw std::runtime_error(ss.str());}
-#define CHECK_AND_ASSERT_THROW_MES(expr, message) {if(!(expr)) ASSERT_MES_AND_THROW(message);}
+#define CHECK_AND_ASSERT_THROW_MES(expr, message) do {if(!(expr)) ASSERT_MES_AND_THROW(message);} while(0)
#ifndef CHECK_AND_ASSERT
diff --git a/contrib/epee/include/net/http_protocol_handler.inl b/contrib/epee/include/net/http_protocol_handler.inl
index c3350bf73..c555707ac 100644
--- a/contrib/epee/include/net/http_protocol_handler.inl
+++ b/contrib/epee/include/net/http_protocol_handler.inl
@@ -639,6 +639,9 @@ namespace net_utils
buf += "Access-Control-Allow-Origin: ";
buf += m_query_info.m_header_info.m_origin;
buf += "\r\n";
+ buf += "Access-Control-Expose-Headers: www-authenticate\r\n";
+ if (m_query_info.m_http_method == http::http_method_options)
+ buf += "Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With\r\n";
buf += "Access-Control-Allow-Methods: POST, PUT, GET, OPTIONS\r\n";
}
}
diff --git a/contrib/epee/include/net/net_utils_base.h b/contrib/epee/include/net/net_utils_base.h
index 0e31ee86f..04e3fe6a4 100644
--- a/contrib/epee/include/net/net_utils_base.h
+++ b/contrib/epee/include/net/net_utils_base.h
@@ -166,15 +166,37 @@ namespace net_utils
BEGIN_KV_SERIALIZE_MAP()
uint8_t type = is_store ? this_ref.get_type_id() : 0;
- epee::serialization::selector<is_store>::serialize(type, stg, hparent_section, "type");
+ if (!epee::serialization::selector<is_store>::serialize(type, stg, hparent_section, "type"))
+ return false;
switch (type)
{
case ipv4_network_address::ID:
+ {
if (!is_store)
+ {
const_cast<network_address&>(this_ref) = ipv4_network_address{0, 0};
- KV_SERIALIZE(template as_mutable<ipv4_network_address>());
+ auto &addr = this_ref.template as_mutable<ipv4_network_address>();
+ if (epee::serialization::selector<is_store>::serialize(addr, stg, hparent_section, "addr"))
+ MDEBUG("Found as addr: " << this_ref.str());
+ else if (epee::serialization::selector<is_store>::serialize(addr, stg, hparent_section, "template as<ipv4_network_address>()"))
+ MDEBUG("Found as template as<ipv4_network_address>(): " << this_ref.str());
+ else if (epee::serialization::selector<is_store>::serialize(addr, stg, hparent_section, "template as_mutable<ipv4_network_address>()"))
+ MDEBUG("Found as template as_mutable<ipv4_network_address>(): " << this_ref.str());
+ else
+ {
+ MWARNING("Address not found");
+ return false;
+ }
+ }
+ else
+ {
+ auto &addr = this_ref.template as_mutable<ipv4_network_address>();
+ if (!epee::serialization::selector<is_store>::serialize(addr, stg, hparent_section, "addr"))
+ return false;
+ }
break;
- default: MERROR("Unsupported network address type: " << type); return false;
+ }
+ default: MERROR("Unsupported network address type: " << (unsigned)type); return false;
}
END_KV_SERIALIZE_MAP()
};
diff --git a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h
index a94ecacc5..2e020b136 100644
--- a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h
+++ b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h
@@ -73,7 +73,7 @@ namespace epee
template<class serializible_type, class t_storage>
static bool unserialize_t_obj(serializible_type& obj, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
- typename t_storage::hsection hchild_section = stg.open_section(pname, hparent_section, true);
+ typename t_storage::hsection hchild_section = stg.open_section(pname, hparent_section, false);
if(!hchild_section) return false;
return obj._load(stg, hchild_section);
}
@@ -90,7 +90,7 @@ namespace epee
static bool unserialize_t_obj(enableable<serializible_type>& obj, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname)
{
obj.enabled = false;
- typename t_storage::hsection hchild_section = stg.open_section(pname, hparent_section, true);
+ typename t_storage::hsection hchild_section = stg.open_section(pname, hparent_section, false);
if(!hchild_section) return false;
obj.enabled = true;
return obj.v._load(stg, hchild_section);
diff --git a/contrib/snap/snapcraft.yaml b/contrib/snap/snapcraft.yaml
index 11d480d10..49e305243 100644
--- a/contrib/snap/snapcraft.yaml
+++ b/contrib/snap/snapcraft.yaml
@@ -1,5 +1,5 @@
name: monero
-version: 0.10.2-1
+version: 0.11.1.0-1
summary: "Monero: the secure, private, untraceable cryptocurrency https://getmonero.org"
description: |
Monero is a private, secure, untraceable, decentralised digital currency.
@@ -46,11 +46,15 @@ parts:
- libunbound-dev
- libevent-dev
- libboost-all-dev
+ - libzmqpp-dev
+ - libzmq3-dev
+ - libsodium-dev
- libdb-dev
- libunwind-dev
- libminiupnpc-dev
- libldns-dev
- libexpat1-dev
+ - libreadline6-dev
- bison
- doxygen
- graphviz
diff --git a/external/db_drivers/liblmdb/Makefile b/external/db_drivers/liblmdb/Makefile
index f3c93a2ff..42a5f034a 100644
--- a/external/db_drivers/liblmdb/Makefile
+++ b/external/db_drivers/liblmdb/Makefile
@@ -38,8 +38,8 @@ mandir = $(datarootdir)/man
IHDRS = lmdb.h
ILIBS = liblmdb.a liblmdb.so
-IPROGS = mdb_stat mdb_copy mdb_dump mdb_load
-IDOCS = mdb_stat.1 mdb_copy.1 mdb_dump.1 mdb_load.1
+IPROGS = mdb_stat mdb_copy mdb_dump mdb_load mdb_drop
+IDOCS = mdb_stat.1 mdb_copy.1 mdb_dump.1 mdb_load.1 mdb_drop.1
PROGS = $(IPROGS) mtest mtest2 mtest3 mtest4 mtest5
all: $(ILIBS) $(PROGS)
@@ -71,6 +71,7 @@ mdb_stat: mdb_stat.o liblmdb.a
mdb_copy: mdb_copy.o liblmdb.a
mdb_dump: mdb_dump.o liblmdb.a
mdb_load: mdb_load.o liblmdb.a
+mdb_drop: mdb_drop.o liblmdb.a
mtest: mtest.o liblmdb.a
mtest2: mtest2.o liblmdb.a
mtest3: mtest3.o liblmdb.a
diff --git a/external/db_drivers/liblmdb/mdb_drop.1 b/external/db_drivers/liblmdb/mdb_drop.1
new file mode 100644
index 000000000..997e29118
--- /dev/null
+++ b/external/db_drivers/liblmdb/mdb_drop.1
@@ -0,0 +1,40 @@
+.TH MDB_DROP 1 "2017/11/19" "LMDB 0.9.70"
+.\" Copyright 2014-2017 Howard Chu, Symas Corp. All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+mdb_drop \- LMDB database delete tool
+.SH SYNOPSIS
+.B mdb_drop
+[\c
+.BR \-V ]
+[\c
+.BR \-n ]
+[\c
+.BR \-d ]
+[\c
+.BI \-s \ subdb\fR]
+.BR \ envpath
+.SH DESCRIPTION
+The
+.B mdb_drop
+utility empties or deletes a database in the specified
+environment.
+.SH OPTIONS
+.TP
+.BR \-V
+Write the library version number to the standard output, and exit.
+.TP
+.BR \-n
+Operate on an LMDB database which does not use subdirectories.
+.TP
+.BR \-d
+Delete the specified database, don't just empty it.
+.TP
+.BR \-s \ subdb
+Operate on a specific subdatabase. If no database is specified, only the main database is dropped.
+.SH DIAGNOSTICS
+Exit status is zero if no errors occur.
+Errors result in a non-zero exit status and
+a diagnostic message being written to standard error.
+.SH AUTHOR
+Howard Chu of Symas Corporation <http://www.symas.com>
diff --git a/external/db_drivers/liblmdb/mdb_drop.c b/external/db_drivers/liblmdb/mdb_drop.c
new file mode 100644
index 000000000..725891685
--- /dev/null
+++ b/external/db_drivers/liblmdb/mdb_drop.c
@@ -0,0 +1,135 @@
+/* mdb_drop.c - memory-mapped database delete tool */
+/*
+ * Copyright 2016-2017 Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <signal.h>
+#include "lmdb.h"
+
+static volatile sig_atomic_t gotsig;
+
+static void dumpsig( int sig )
+{
+ gotsig=1;
+}
+
+static void usage(char *prog)
+{
+ fprintf(stderr, "usage: %s [-V] [-n] [-d] [-s subdb] dbpath\n", prog);
+ exit(EXIT_FAILURE);
+}
+
+int main(int argc, char *argv[])
+{
+ int i, rc;
+ MDB_env *env;
+ MDB_txn *txn;
+ MDB_dbi dbi;
+ char *prog = argv[0];
+ char *envname;
+ char *subname = NULL;
+ int envflags = 0, delete = 0;
+
+ if (argc < 2) {
+ usage(prog);
+ }
+
+ /* -d: delete the db, don't just empty it
+ * -s: drop the named subDB
+ * -n: use NOSUBDIR flag on env_open
+ * -V: print version and exit
+ * (default) empty the main DB
+ */
+ while ((i = getopt(argc, argv, "dns:V")) != EOF) {
+ switch(i) {
+ case 'V':
+ printf("%s\n", MDB_VERSION_STRING);
+ exit(0);
+ break;
+ case 'd':
+ delete = 1;
+ break;
+ case 'n':
+ envflags |= MDB_NOSUBDIR;
+ break;
+ case 's':
+ subname = optarg;
+ break;
+ default:
+ usage(prog);
+ }
+ }
+
+ if (optind != argc - 1)
+ usage(prog);
+
+#ifdef SIGPIPE
+ signal(SIGPIPE, dumpsig);
+#endif
+#ifdef SIGHUP
+ signal(SIGHUP, dumpsig);
+#endif
+ signal(SIGINT, dumpsig);
+ signal(SIGTERM, dumpsig);
+
+ envname = argv[optind];
+ rc = mdb_env_create(&env);
+ if (rc) {
+ fprintf(stderr, "mdb_env_create failed, error %d %s\n", rc, mdb_strerror(rc));
+ return EXIT_FAILURE;
+ }
+
+ mdb_env_set_maxdbs(env, 2);
+
+ rc = mdb_env_open(env, envname, envflags, 0664);
+ if (rc) {
+ fprintf(stderr, "mdb_env_open failed, error %d %s\n", rc, mdb_strerror(rc));
+ goto env_close;
+ }
+
+ rc = mdb_txn_begin(env, NULL, 0, &txn);
+ if (rc) {
+ fprintf(stderr, "mdb_txn_begin failed, error %d %s\n", rc, mdb_strerror(rc));
+ goto env_close;
+ }
+
+ rc = mdb_open(txn, subname, 0, &dbi);
+ if (rc) {
+ fprintf(stderr, "mdb_open failed, error %d %s\n", rc, mdb_strerror(rc));
+ goto txn_abort;
+ }
+
+ rc = mdb_drop(txn, dbi, delete);
+ if (rc) {
+ fprintf(stderr, "mdb_drop failed, error %d %s\n", rc, mdb_strerror(rc));
+ goto txn_abort;
+ }
+ rc = mdb_txn_commit(txn);
+ if (rc) {
+ fprintf(stderr, "mdb_txn_commit failed, error %d %s\n", rc, mdb_strerror(rc));
+ goto txn_abort;
+ }
+ txn = NULL;
+
+txn_abort:
+ if (txn)
+ mdb_txn_abort(txn);
+env_close:
+ mdb_env_close(env);
+
+ return rc ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/external/db_drivers/liblmdb/tooltag b/external/db_drivers/liblmdb/tooltag
index 229bf16ba..879c14d22 100644
--- a/external/db_drivers/liblmdb/tooltag
+++ b/external/db_drivers/liblmdb/tooltag
@@ -5,6 +5,11 @@
<filename>mdb_copy.1</filename>
</compound>
<compound kind="page">
+ <name>mdb_drop_1</name>
+ <title>mdb_drop - database delete tool</title>
+ <filename>mdb_drop.1</filename>
+ </compound>
+ <compound kind="page">
<name>mdb_dump_1</name>
<title>mdb_dump - environment export tool</title>
<filename>mdb_dump.1</filename>
diff --git a/external/easylogging++/easylogging++.cc b/external/easylogging++/easylogging++.cc
index 6bc6b2619..31b201897 100644
--- a/external/easylogging++/easylogging++.cc
+++ b/external/easylogging++/easylogging++.cc
@@ -14,6 +14,7 @@
// http://muflihun.com
//
+#define EASYLOGGING_CC
#include "easylogging++.h"
#if defined(AUTO_INITIALIZE_EASYLOGGINGPP)
diff --git a/external/easylogging++/easylogging++.h b/external/easylogging++/easylogging++.h
index 469cf9eec..3270bd607 100644
--- a/external/easylogging++/easylogging++.h
+++ b/external/easylogging++/easylogging++.h
@@ -203,16 +203,17 @@ ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStre
# if (ELPP_COMPILER_GCC && !ELPP_MINGW && !ELPP_OS_OPENBSD)
# define ELPP_STACKTRACE 1
# else
-# define ELPP_STACKTRACE 0
+# define ELPP_STACKTRACE 0
+# ifdef EASYLOGGING_CC
# if ELPP_COMPILER_MSVC
-# pragma message("Stack trace not available for this compiler")
+# pragma message("Stack trace not available for this compiler")
# else
-# warning "Stack trace not available for this compiler";
+# warning "Stack trace not available for this compiler";
# endif // ELPP_COMPILER_MSVC
-# define ELPP_STACKTRACE 0
+# endif
# endif // ELPP_COMPILER_GCC
#else
-# define ELPP_STACKTRACE 0
+# define ELPP_STACKTRACE 0
#endif // (defined(ELPP_FEATURE_ALL)) || (defined(ELPP_FEATURE_CRASH_LOG))
// Miscellaneous macros
#define ELPP_UNUSED(x) (void)x
diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp
index c3f6e3d87..2fb43a4ba 100644
--- a/src/blockchain_db/blockchain_db.cpp
+++ b/src/blockchain_db/blockchain_db.cpp
@@ -194,6 +194,10 @@ uint64_t BlockchainDB::add_block( const block& blk
, const std::vector<transaction>& txs
)
{
+ // sanity
+ if (blk.tx_hashes.size() != txs.size())
+ throw new std::runtime_error("Inconsistent tx/hashes sizes");
+
block_txn_start(false);
TIME_MEASURE_START(time1);
diff --git a/src/common/perf_timer.h b/src/common/perf_timer.h
index bc8e05800..4d7d99afb 100644
--- a/src/common/perf_timer.h
+++ b/src/common/perf_timer.h
@@ -94,5 +94,8 @@ void set_performance_timer_log_level(el::Level level);
#define PERF_TIMER_UNIT_L(name, unit, l) tools::PerformanceTimer pt_##name(#name, unit, l)
#define PERF_TIMER(name) PERF_TIMER_UNIT(name, 1000)
#define PERF_TIMER_L(name, l) PERF_TIMER_UNIT_L(name, 1000, l)
+#define PERF_TIMER_START_UNIT(name, unit) tools::PerformanceTimer *pt_##name = new tools::PerformanceTimer(#name, unit, el::Level::Info)
+#define PERF_TIMER_START(name) PERF_TIMER_START_UNIT(name, 1000)
+#define PERF_TIMER_STOP(name) do { delete pt_##name; pt_##name = NULL; } while(0)
}
diff --git a/src/common/util.cpp b/src/common/util.cpp
index eac5a67cf..de19fec81 100644
--- a/src/common/util.cpp
+++ b/src/common/util.cpp
@@ -556,7 +556,7 @@ std::string get_nix_version_display_string()
MCLOG_RED(el::Level::Warning, "global", "Running with glibc " << ver << ", hangs may occur - change glibc version if possible");
#endif
-#if OPENSSL_VERSION_NUMBER < 0x10100000
+#if OPENSSL_VERSION_NUMBER < 0x10100000 || defined(LIBRESSL_VERSION_TEXT)
SSL_library_init();
#else
OPENSSL_init_ssl(0, NULL);
diff --git a/src/crypto/crypto-ops.c b/src/crypto/crypto-ops.c
index 4edfee0ce..b5c62bce4 100644
--- a/src/crypto/crypto-ops.c
+++ b/src/crypto/crypto-ops.c
@@ -2000,17 +2000,15 @@ void ge_scalarmult(ge_p2 *r, const unsigned char *a, const ge_p3 *A) {
}
}
-void ge_double_scalarmult_precomp_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b, const ge_dsmp Bi) {
+void ge_double_scalarmult_precomp_vartime2(ge_p2 *r, const unsigned char *a, const ge_dsmp Ai, const unsigned char *b, const ge_dsmp Bi) {
signed char aslide[256];
signed char bslide[256];
- ge_dsmp Ai; /* A, 3A, 5A, 7A, 9A, 11A, 13A, 15A */
ge_p1p1 t;
ge_p3 u;
int i;
slide(aslide, a);
slide(bslide, b);
- ge_dsm_precomp(Ai, A);
ge_p2_0(r);
@@ -2041,6 +2039,13 @@ void ge_double_scalarmult_precomp_vartime(ge_p2 *r, const unsigned char *a, cons
}
}
+void ge_double_scalarmult_precomp_vartime(ge_p2 *r, const unsigned char *a, const ge_p3 *A, const unsigned char *b, const ge_dsmp Bi) {
+ ge_dsmp Ai; /* A, 3A, 5A, 7A, 9A, 11A, 13A, 15A */
+
+ ge_dsm_precomp(Ai, A);
+ ge_double_scalarmult_precomp_vartime2(r, a, Ai, b, Bi);
+}
+
void ge_mul8(ge_p1p1 *r, const ge_p2 *t) {
ge_p2 u;
ge_p2_dbl(r, t);
@@ -2898,6 +2903,658 @@ void sc_mulsub(unsigned char *s, const unsigned char *a, const unsigned char *b,
s[31] = s11 >> 17;
}
+//copied from above and modified
+/*
+Input:
+ a[0]+256*a[1]+...+256^31*a[31] = a
+ b[0]+256*b[1]+...+256^31*b[31] = b
+
+Output:
+ s[0]+256*s[1]+...+256^31*s[31] = (ab) mod l
+ where l = 2^252 + 27742317777372353535851937790883648493.
+*/
+void sc_mul(unsigned char *s, const unsigned char *a, const unsigned char *b) {
+ int64_t a0 = 2097151 & load_3(a);
+ int64_t a1 = 2097151 & (load_4(a + 2) >> 5);
+ int64_t a2 = 2097151 & (load_3(a + 5) >> 2);
+ int64_t a3 = 2097151 & (load_4(a + 7) >> 7);
+ int64_t a4 = 2097151 & (load_4(a + 10) >> 4);
+ int64_t a5 = 2097151 & (load_3(a + 13) >> 1);
+ int64_t a6 = 2097151 & (load_4(a + 15) >> 6);
+ int64_t a7 = 2097151 & (load_3(a + 18) >> 3);
+ int64_t a8 = 2097151 & load_3(a + 21);
+ int64_t a9 = 2097151 & (load_4(a + 23) >> 5);
+ int64_t a10 = 2097151 & (load_3(a + 26) >> 2);
+ int64_t a11 = (load_4(a + 28) >> 7);
+ int64_t b0 = 2097151 & load_3(b);
+ int64_t b1 = 2097151 & (load_4(b + 2) >> 5);
+ int64_t b2 = 2097151 & (load_3(b + 5) >> 2);
+ int64_t b3 = 2097151 & (load_4(b + 7) >> 7);
+ int64_t b4 = 2097151 & (load_4(b + 10) >> 4);
+ int64_t b5 = 2097151 & (load_3(b + 13) >> 1);
+ int64_t b6 = 2097151 & (load_4(b + 15) >> 6);
+ int64_t b7 = 2097151 & (load_3(b + 18) >> 3);
+ int64_t b8 = 2097151 & load_3(b + 21);
+ int64_t b9 = 2097151 & (load_4(b + 23) >> 5);
+ int64_t b10 = 2097151 & (load_3(b + 26) >> 2);
+ int64_t b11 = (load_4(b + 28) >> 7);
+ int64_t s0;
+ int64_t s1;
+ int64_t s2;
+ int64_t s3;
+ int64_t s4;
+ int64_t s5;
+ int64_t s6;
+ int64_t s7;
+ int64_t s8;
+ int64_t s9;
+ int64_t s10;
+ int64_t s11;
+ int64_t s12;
+ int64_t s13;
+ int64_t s14;
+ int64_t s15;
+ int64_t s16;
+ int64_t s17;
+ int64_t s18;
+ int64_t s19;
+ int64_t s20;
+ int64_t s21;
+ int64_t s22;
+ int64_t s23;
+ int64_t carry0;
+ int64_t carry1;
+ int64_t carry2;
+ int64_t carry3;
+ int64_t carry4;
+ int64_t carry5;
+ int64_t carry6;
+ int64_t carry7;
+ int64_t carry8;
+ int64_t carry9;
+ int64_t carry10;
+ int64_t carry11;
+ int64_t carry12;
+ int64_t carry13;
+ int64_t carry14;
+ int64_t carry15;
+ int64_t carry16;
+ int64_t carry17;
+ int64_t carry18;
+ int64_t carry19;
+ int64_t carry20;
+ int64_t carry21;
+ int64_t carry22;
+
+ s0 = a0*b0;
+ s1 = (a0*b1 + a1*b0);
+ s2 = (a0*b2 + a1*b1 + a2*b0);
+ s3 = (a0*b3 + a1*b2 + a2*b1 + a3*b0);
+ s4 = (a0*b4 + a1*b3 + a2*b2 + a3*b1 + a4*b0);
+ s5 = (a0*b5 + a1*b4 + a2*b3 + a3*b2 + a4*b1 + a5*b0);
+ s6 = (a0*b6 + a1*b5 + a2*b4 + a3*b3 + a4*b2 + a5*b1 + a6*b0);
+ s7 = (a0*b7 + a1*b6 + a2*b5 + a3*b4 + a4*b3 + a5*b2 + a6*b1 + a7*b0);
+ s8 = (a0*b8 + a1*b7 + a2*b6 + a3*b5 + a4*b4 + a5*b3 + a6*b2 + a7*b1 + a8*b0);
+ s9 = (a0*b9 + a1*b8 + a2*b7 + a3*b6 + a4*b5 + a5*b4 + a6*b3 + a7*b2 + a8*b1 + a9*b0);
+ s10 = (a0*b10 + a1*b9 + a2*b8 + a3*b7 + a4*b6 + a5*b5 + a6*b4 + a7*b3 + a8*b2 + a9*b1 + a10*b0);
+ s11 = (a0*b11 + a1*b10 + a2*b9 + a3*b8 + a4*b7 + a5*b6 + a6*b5 + a7*b4 + a8*b3 + a9*b2 + a10*b1 + a11*b0);
+ s12 = (a1*b11 + a2*b10 + a3*b9 + a4*b8 + a5*b7 + a6*b6 + a7*b5 + a8*b4 + a9*b3 + a10*b2 + a11*b1);
+ s13 = (a2*b11 + a3*b10 + a4*b9 + a5*b8 + a6*b7 + a7*b6 + a8*b5 + a9*b4 + a10*b3 + a11*b2);
+ s14 = (a3*b11 + a4*b10 + a5*b9 + a6*b8 + a7*b7 + a8*b6 + a9*b5 + a10*b4 + a11*b3);
+ s15 = (a4*b11 + a5*b10 + a6*b9 + a7*b8 + a8*b7 + a9*b6 + a10*b5 + a11*b4);
+ s16 = (a5*b11 + a6*b10 + a7*b9 + a8*b8 + a9*b7 + a10*b6 + a11*b5);
+ s17 = (a6*b11 + a7*b10 + a8*b9 + a9*b8 + a10*b7 + a11*b6);
+ s18 = (a7*b11 + a8*b10 + a9*b9 + a10*b8 + a11*b7);
+ s19 = (a8*b11 + a9*b10 + a10*b9 + a11*b8);
+ s20 = (a9*b11 + a10*b10 + a11*b9);
+ s21 = (a10*b11 + a11*b10);
+ s22 = a11*b11;
+ s23 = 0;
+
+ carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21;
+ carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21;
+ carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21;
+ carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21;
+ carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21;
+ carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21;
+ carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21;
+ carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21;
+ carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21;
+ carry18 = (s18 + (1<<20)) >> 21; s19 += carry18; s18 -= carry18 << 21;
+ carry20 = (s20 + (1<<20)) >> 21; s21 += carry20; s20 -= carry20 << 21;
+ carry22 = (s22 + (1<<20)) >> 21; s23 += carry22; s22 -= carry22 << 21;
+
+ carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21;
+ carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21;
+ carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21;
+ carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21;
+ carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21;
+ carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21;
+ carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21;
+ carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21;
+ carry17 = (s17 + (1<<20)) >> 21; s18 += carry17; s17 -= carry17 << 21;
+ carry19 = (s19 + (1<<20)) >> 21; s20 += carry19; s19 -= carry19 << 21;
+ carry21 = (s21 + (1<<20)) >> 21; s22 += carry21; s21 -= carry21 << 21;
+
+ s11 += s23 * 666643;
+ s12 += s23 * 470296;
+ s13 += s23 * 654183;
+ s14 -= s23 * 997805;
+ s15 += s23 * 136657;
+ s16 -= s23 * 683901;
+
+ s10 += s22 * 666643;
+ s11 += s22 * 470296;
+ s12 += s22 * 654183;
+ s13 -= s22 * 997805;
+ s14 += s22 * 136657;
+ s15 -= s22 * 683901;
+
+ s9 += s21 * 666643;
+ s10 += s21 * 470296;
+ s11 += s21 * 654183;
+ s12 -= s21 * 997805;
+ s13 += s21 * 136657;
+ s14 -= s21 * 683901;
+
+ s8 += s20 * 666643;
+ s9 += s20 * 470296;
+ s10 += s20 * 654183;
+ s11 -= s20 * 997805;
+ s12 += s20 * 136657;
+ s13 -= s20 * 683901;
+
+ s7 += s19 * 666643;
+ s8 += s19 * 470296;
+ s9 += s19 * 654183;
+ s10 -= s19 * 997805;
+ s11 += s19 * 136657;
+ s12 -= s19 * 683901;
+
+ s6 += s18 * 666643;
+ s7 += s18 * 470296;
+ s8 += s18 * 654183;
+ s9 -= s18 * 997805;
+ s10 += s18 * 136657;
+ s11 -= s18 * 683901;
+
+ carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21;
+ carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21;
+ carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21;
+ carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21;
+ carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21;
+ carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21;
+
+ carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21;
+ carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21;
+ carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21;
+ carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21;
+ carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21;
+
+ s5 += s17 * 666643;
+ s6 += s17 * 470296;
+ s7 += s17 * 654183;
+ s8 -= s17 * 997805;
+ s9 += s17 * 136657;
+ s10 -= s17 * 683901;
+
+ s4 += s16 * 666643;
+ s5 += s16 * 470296;
+ s6 += s16 * 654183;
+ s7 -= s16 * 997805;
+ s8 += s16 * 136657;
+ s9 -= s16 * 683901;
+
+ s3 += s15 * 666643;
+ s4 += s15 * 470296;
+ s5 += s15 * 654183;
+ s6 -= s15 * 997805;
+ s7 += s15 * 136657;
+ s8 -= s15 * 683901;
+
+ s2 += s14 * 666643;
+ s3 += s14 * 470296;
+ s4 += s14 * 654183;
+ s5 -= s14 * 997805;
+ s6 += s14 * 136657;
+ s7 -= s14 * 683901;
+
+ s1 += s13 * 666643;
+ s2 += s13 * 470296;
+ s3 += s13 * 654183;
+ s4 -= s13 * 997805;
+ s5 += s13 * 136657;
+ s6 -= s13 * 683901;
+
+ s0 += s12 * 666643;
+ s1 += s12 * 470296;
+ s2 += s12 * 654183;
+ s3 -= s12 * 997805;
+ s4 += s12 * 136657;
+ s5 -= s12 * 683901;
+ s12 = 0;
+
+ carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21;
+ carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21;
+ carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21;
+ carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21;
+ carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21;
+ carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21;
+
+ carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21;
+ carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21;
+ carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21;
+ carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21;
+ carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21;
+ carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21;
+
+ s0 += s12 * 666643;
+ s1 += s12 * 470296;
+ s2 += s12 * 654183;
+ s3 -= s12 * 997805;
+ s4 += s12 * 136657;
+ s5 -= s12 * 683901;
+ s12 = 0;
+
+ carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21;
+ carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21;
+ carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21;
+ carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21;
+ carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21;
+ carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21;
+ carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21;
+ carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21;
+ carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21;
+ carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21;
+ carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21;
+ carry11 = s11 >> 21; s12 += carry11; s11 -= carry11 << 21;
+
+ s0 += s12 * 666643;
+ s1 += s12 * 470296;
+ s2 += s12 * 654183;
+ s3 -= s12 * 997805;
+ s4 += s12 * 136657;
+ s5 -= s12 * 683901;
+
+ carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21;
+ carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21;
+ carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21;
+ carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21;
+ carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21;
+ carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21;
+ carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21;
+ carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21;
+ carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21;
+ carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21;
+ carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21;
+
+ s[0] = s0 >> 0;
+ s[1] = s0 >> 8;
+ s[2] = (s0 >> 16) | (s1 << 5);
+ s[3] = s1 >> 3;
+ s[4] = s1 >> 11;
+ s[5] = (s1 >> 19) | (s2 << 2);
+ s[6] = s2 >> 6;
+ s[7] = (s2 >> 14) | (s3 << 7);
+ s[8] = s3 >> 1;
+ s[9] = s3 >> 9;
+ s[10] = (s3 >> 17) | (s4 << 4);
+ s[11] = s4 >> 4;
+ s[12] = s4 >> 12;
+ s[13] = (s4 >> 20) | (s5 << 1);
+ s[14] = s5 >> 7;
+ s[15] = (s5 >> 15) | (s6 << 6);
+ s[16] = s6 >> 2;
+ s[17] = s6 >> 10;
+ s[18] = (s6 >> 18) | (s7 << 3);
+ s[19] = s7 >> 5;
+ s[20] = s7 >> 13;
+ s[21] = s8 >> 0;
+ s[22] = s8 >> 8;
+ s[23] = (s8 >> 16) | (s9 << 5);
+ s[24] = s9 >> 3;
+ s[25] = s9 >> 11;
+ s[26] = (s9 >> 19) | (s10 << 2);
+ s[27] = s10 >> 6;
+ s[28] = (s10 >> 14) | (s11 << 7);
+ s[29] = s11 >> 1;
+ s[30] = s11 >> 9;
+ s[31] = s11 >> 17;
+}
+
+//copied from above and modified
+/*
+Input:
+ a[0]+256*a[1]+...+256^31*a[31] = a
+ b[0]+256*b[1]+...+256^31*b[31] = b
+ c[0]+256*c[1]+...+256^31*c[31] = c
+
+Output:
+ s[0]+256*s[1]+...+256^31*s[31] = (c+ab) mod l
+ where l = 2^252 + 27742317777372353535851937790883648493.
+*/
+
+void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c) {
+ int64_t a0 = 2097151 & load_3(a);
+ int64_t a1 = 2097151 & (load_4(a + 2) >> 5);
+ int64_t a2 = 2097151 & (load_3(a + 5) >> 2);
+ int64_t a3 = 2097151 & (load_4(a + 7) >> 7);
+ int64_t a4 = 2097151 & (load_4(a + 10) >> 4);
+ int64_t a5 = 2097151 & (load_3(a + 13) >> 1);
+ int64_t a6 = 2097151 & (load_4(a + 15) >> 6);
+ int64_t a7 = 2097151 & (load_3(a + 18) >> 3);
+ int64_t a8 = 2097151 & load_3(a + 21);
+ int64_t a9 = 2097151 & (load_4(a + 23) >> 5);
+ int64_t a10 = 2097151 & (load_3(a + 26) >> 2);
+ int64_t a11 = (load_4(a + 28) >> 7);
+ int64_t b0 = 2097151 & load_3(b);
+ int64_t b1 = 2097151 & (load_4(b + 2) >> 5);
+ int64_t b2 = 2097151 & (load_3(b + 5) >> 2);
+ int64_t b3 = 2097151 & (load_4(b + 7) >> 7);
+ int64_t b4 = 2097151 & (load_4(b + 10) >> 4);
+ int64_t b5 = 2097151 & (load_3(b + 13) >> 1);
+ int64_t b6 = 2097151 & (load_4(b + 15) >> 6);
+ int64_t b7 = 2097151 & (load_3(b + 18) >> 3);
+ int64_t b8 = 2097151 & load_3(b + 21);
+ int64_t b9 = 2097151 & (load_4(b + 23) >> 5);
+ int64_t b10 = 2097151 & (load_3(b + 26) >> 2);
+ int64_t b11 = (load_4(b + 28) >> 7);
+ int64_t c0 = 2097151 & load_3(c);
+ int64_t c1 = 2097151 & (load_4(c + 2) >> 5);
+ int64_t c2 = 2097151 & (load_3(c + 5) >> 2);
+ int64_t c3 = 2097151 & (load_4(c + 7) >> 7);
+ int64_t c4 = 2097151 & (load_4(c + 10) >> 4);
+ int64_t c5 = 2097151 & (load_3(c + 13) >> 1);
+ int64_t c6 = 2097151 & (load_4(c + 15) >> 6);
+ int64_t c7 = 2097151 & (load_3(c + 18) >> 3);
+ int64_t c8 = 2097151 & load_3(c + 21);
+ int64_t c9 = 2097151 & (load_4(c + 23) >> 5);
+ int64_t c10 = 2097151 & (load_3(c + 26) >> 2);
+ int64_t c11 = (load_4(c + 28) >> 7);
+ int64_t s0;
+ int64_t s1;
+ int64_t s2;
+ int64_t s3;
+ int64_t s4;
+ int64_t s5;
+ int64_t s6;
+ int64_t s7;
+ int64_t s8;
+ int64_t s9;
+ int64_t s10;
+ int64_t s11;
+ int64_t s12;
+ int64_t s13;
+ int64_t s14;
+ int64_t s15;
+ int64_t s16;
+ int64_t s17;
+ int64_t s18;
+ int64_t s19;
+ int64_t s20;
+ int64_t s21;
+ int64_t s22;
+ int64_t s23;
+ int64_t carry0;
+ int64_t carry1;
+ int64_t carry2;
+ int64_t carry3;
+ int64_t carry4;
+ int64_t carry5;
+ int64_t carry6;
+ int64_t carry7;
+ int64_t carry8;
+ int64_t carry9;
+ int64_t carry10;
+ int64_t carry11;
+ int64_t carry12;
+ int64_t carry13;
+ int64_t carry14;
+ int64_t carry15;
+ int64_t carry16;
+ int64_t carry17;
+ int64_t carry18;
+ int64_t carry19;
+ int64_t carry20;
+ int64_t carry21;
+ int64_t carry22;
+
+ s0 = c0 + a0*b0;
+ s1 = c1 + (a0*b1 + a1*b0);
+ s2 = c2 + (a0*b2 + a1*b1 + a2*b0);
+ s3 = c3 + (a0*b3 + a1*b2 + a2*b1 + a3*b0);
+ s4 = c4 + (a0*b4 + a1*b3 + a2*b2 + a3*b1 + a4*b0);
+ s5 = c5 + (a0*b5 + a1*b4 + a2*b3 + a3*b2 + a4*b1 + a5*b0);
+ s6 = c6 + (a0*b6 + a1*b5 + a2*b4 + a3*b3 + a4*b2 + a5*b1 + a6*b0);
+ s7 = c7 + (a0*b7 + a1*b6 + a2*b5 + a3*b4 + a4*b3 + a5*b2 + a6*b1 + a7*b0);
+ s8 = c8 + (a0*b8 + a1*b7 + a2*b6 + a3*b5 + a4*b4 + a5*b3 + a6*b2 + a7*b1 + a8*b0);
+ s9 = c9 + (a0*b9 + a1*b8 + a2*b7 + a3*b6 + a4*b5 + a5*b4 + a6*b3 + a7*b2 + a8*b1 + a9*b0);
+ s10 = c10 + (a0*b10 + a1*b9 + a2*b8 + a3*b7 + a4*b6 + a5*b5 + a6*b4 + a7*b3 + a8*b2 + a9*b1 + a10*b0);
+ s11 = c11 + (a0*b11 + a1*b10 + a2*b9 + a3*b8 + a4*b7 + a5*b6 + a6*b5 + a7*b4 + a8*b3 + a9*b2 + a10*b1 + a11*b0);
+ s12 = (a1*b11 + a2*b10 + a3*b9 + a4*b8 + a5*b7 + a6*b6 + a7*b5 + a8*b4 + a9*b3 + a10*b2 + a11*b1);
+ s13 = (a2*b11 + a3*b10 + a4*b9 + a5*b8 + a6*b7 + a7*b6 + a8*b5 + a9*b4 + a10*b3 + a11*b2);
+ s14 = (a3*b11 + a4*b10 + a5*b9 + a6*b8 + a7*b7 + a8*b6 + a9*b5 + a10*b4 + a11*b3);
+ s15 = (a4*b11 + a5*b10 + a6*b9 + a7*b8 + a8*b7 + a9*b6 + a10*b5 + a11*b4);
+ s16 = (a5*b11 + a6*b10 + a7*b9 + a8*b8 + a9*b7 + a10*b6 + a11*b5);
+ s17 = (a6*b11 + a7*b10 + a8*b9 + a9*b8 + a10*b7 + a11*b6);
+ s18 = (a7*b11 + a8*b10 + a9*b9 + a10*b8 + a11*b7);
+ s19 = (a8*b11 + a9*b10 + a10*b9 + a11*b8);
+ s20 = (a9*b11 + a10*b10 + a11*b9);
+ s21 = (a10*b11 + a11*b10);
+ s22 = a11*b11;
+ s23 = 0;
+
+ carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21;
+ carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21;
+ carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21;
+ carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21;
+ carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21;
+ carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21;
+ carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21;
+ carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21;
+ carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21;
+ carry18 = (s18 + (1<<20)) >> 21; s19 += carry18; s18 -= carry18 << 21;
+ carry20 = (s20 + (1<<20)) >> 21; s21 += carry20; s20 -= carry20 << 21;
+ carry22 = (s22 + (1<<20)) >> 21; s23 += carry22; s22 -= carry22 << 21;
+
+ carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21;
+ carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21;
+ carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21;
+ carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21;
+ carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21;
+ carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21;
+ carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21;
+ carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21;
+ carry17 = (s17 + (1<<20)) >> 21; s18 += carry17; s17 -= carry17 << 21;
+ carry19 = (s19 + (1<<20)) >> 21; s20 += carry19; s19 -= carry19 << 21;
+ carry21 = (s21 + (1<<20)) >> 21; s22 += carry21; s21 -= carry21 << 21;
+
+ s11 += s23 * 666643;
+ s12 += s23 * 470296;
+ s13 += s23 * 654183;
+ s14 -= s23 * 997805;
+ s15 += s23 * 136657;
+ s16 -= s23 * 683901;
+
+ s10 += s22 * 666643;
+ s11 += s22 * 470296;
+ s12 += s22 * 654183;
+ s13 -= s22 * 997805;
+ s14 += s22 * 136657;
+ s15 -= s22 * 683901;
+
+ s9 += s21 * 666643;
+ s10 += s21 * 470296;
+ s11 += s21 * 654183;
+ s12 -= s21 * 997805;
+ s13 += s21 * 136657;
+ s14 -= s21 * 683901;
+
+ s8 += s20 * 666643;
+ s9 += s20 * 470296;
+ s10 += s20 * 654183;
+ s11 -= s20 * 997805;
+ s12 += s20 * 136657;
+ s13 -= s20 * 683901;
+
+ s7 += s19 * 666643;
+ s8 += s19 * 470296;
+ s9 += s19 * 654183;
+ s10 -= s19 * 997805;
+ s11 += s19 * 136657;
+ s12 -= s19 * 683901;
+
+ s6 += s18 * 666643;
+ s7 += s18 * 470296;
+ s8 += s18 * 654183;
+ s9 -= s18 * 997805;
+ s10 += s18 * 136657;
+ s11 -= s18 * 683901;
+
+ carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21;
+ carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21;
+ carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21;
+ carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21;
+ carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21;
+ carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21;
+
+ carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21;
+ carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21;
+ carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21;
+ carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21;
+ carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21;
+
+ s5 += s17 * 666643;
+ s6 += s17 * 470296;
+ s7 += s17 * 654183;
+ s8 -= s17 * 997805;
+ s9 += s17 * 136657;
+ s10 -= s17 * 683901;
+
+ s4 += s16 * 666643;
+ s5 += s16 * 470296;
+ s6 += s16 * 654183;
+ s7 -= s16 * 997805;
+ s8 += s16 * 136657;
+ s9 -= s16 * 683901;
+
+ s3 += s15 * 666643;
+ s4 += s15 * 470296;
+ s5 += s15 * 654183;
+ s6 -= s15 * 997805;
+ s7 += s15 * 136657;
+ s8 -= s15 * 683901;
+
+ s2 += s14 * 666643;
+ s3 += s14 * 470296;
+ s4 += s14 * 654183;
+ s5 -= s14 * 997805;
+ s6 += s14 * 136657;
+ s7 -= s14 * 683901;
+
+ s1 += s13 * 666643;
+ s2 += s13 * 470296;
+ s3 += s13 * 654183;
+ s4 -= s13 * 997805;
+ s5 += s13 * 136657;
+ s6 -= s13 * 683901;
+
+ s0 += s12 * 666643;
+ s1 += s12 * 470296;
+ s2 += s12 * 654183;
+ s3 -= s12 * 997805;
+ s4 += s12 * 136657;
+ s5 -= s12 * 683901;
+ s12 = 0;
+
+ carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21;
+ carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21;
+ carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21;
+ carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21;
+ carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21;
+ carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21;
+
+ carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21;
+ carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21;
+ carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21;
+ carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21;
+ carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21;
+ carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21;
+
+ s0 += s12 * 666643;
+ s1 += s12 * 470296;
+ s2 += s12 * 654183;
+ s3 -= s12 * 997805;
+ s4 += s12 * 136657;
+ s5 -= s12 * 683901;
+ s12 = 0;
+
+ carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21;
+ carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21;
+ carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21;
+ carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21;
+ carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21;
+ carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21;
+ carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21;
+ carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21;
+ carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21;
+ carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21;
+ carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21;
+ carry11 = s11 >> 21; s12 += carry11; s11 -= carry11 << 21;
+
+ s0 += s12 * 666643;
+ s1 += s12 * 470296;
+ s2 += s12 * 654183;
+ s3 -= s12 * 997805;
+ s4 += s12 * 136657;
+ s5 -= s12 * 683901;
+
+ carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21;
+ carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21;
+ carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21;
+ carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21;
+ carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21;
+ carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21;
+ carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21;
+ carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21;
+ carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21;
+ carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21;
+ carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21;
+
+ s[0] = s0 >> 0;
+ s[1] = s0 >> 8;
+ s[2] = (s0 >> 16) | (s1 << 5);
+ s[3] = s1 >> 3;
+ s[4] = s1 >> 11;
+ s[5] = (s1 >> 19) | (s2 << 2);
+ s[6] = s2 >> 6;
+ s[7] = (s2 >> 14) | (s3 << 7);
+ s[8] = s3 >> 1;
+ s[9] = s3 >> 9;
+ s[10] = (s3 >> 17) | (s4 << 4);
+ s[11] = s4 >> 4;
+ s[12] = s4 >> 12;
+ s[13] = (s4 >> 20) | (s5 << 1);
+ s[14] = s5 >> 7;
+ s[15] = (s5 >> 15) | (s6 << 6);
+ s[16] = s6 >> 2;
+ s[17] = s6 >> 10;
+ s[18] = (s6 >> 18) | (s7 << 3);
+ s[19] = s7 >> 5;
+ s[20] = s7 >> 13;
+ s[21] = s8 >> 0;
+ s[22] = s8 >> 8;
+ s[23] = (s8 >> 16) | (s9 << 5);
+ s[24] = s9 >> 3;
+ s[25] = s9 >> 11;
+ s[26] = (s9 >> 19) | (s10 << 2);
+ s[27] = s10 >> 6;
+ s[28] = (s10 >> 14) | (s11 << 7);
+ s[29] = s11 >> 1;
+ s[30] = s11 >> 9;
+ s[31] = s11 >> 17;
+}
+
/* Assumes that a != INT64_MIN */
static int64_t signum(int64_t a) {
return (a >> 63) - ((-a) >> 63);
diff --git a/src/crypto/crypto-ops.h b/src/crypto/crypto-ops.h
index 37edf5b6d..c76455551 100644
--- a/src/crypto/crypto-ops.h
+++ b/src/crypto/crypto-ops.h
@@ -128,6 +128,7 @@ void sc_reduce(unsigned char *);
void ge_scalarmult(ge_p2 *, const unsigned char *, const ge_p3 *);
void ge_double_scalarmult_precomp_vartime(ge_p2 *, const unsigned char *, const ge_p3 *, const unsigned char *, const ge_dsmp);
+void ge_double_scalarmult_precomp_vartime2(ge_p2 *, const unsigned char *, const ge_dsmp, const unsigned char *, const ge_dsmp);
void ge_mul8(ge_p1p1 *, const ge_p2 *);
extern const fe fe_ma2;
extern const fe fe_ma;
@@ -141,6 +142,8 @@ void sc_reduce32(unsigned char *);
void sc_add(unsigned char *, const unsigned char *, const unsigned char *);
void sc_sub(unsigned char *, const unsigned char *, const unsigned char *);
void sc_mulsub(unsigned char *, const unsigned char *, const unsigned char *, const unsigned char *);
+void sc_mul(unsigned char *, const unsigned char *, const unsigned char *);
+void sc_muladd(unsigned char *s, const unsigned char *a, const unsigned char *b, const unsigned char *c);
int sc_check(const unsigned char *);
int sc_isnonzero(const unsigned char *); /* Doesn't normalize */
diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h
index a67fa0ae7..760edf9b9 100644
--- a/src/cryptonote_basic/cryptonote_boost_serialization.h
+++ b/src/cryptonote_basic/cryptonote_boost_serialization.h
@@ -212,6 +212,23 @@ namespace boost
}
template <class Archive>
+ inline void serialize(Archive &a, rct::Bulletproof &x, const boost::serialization::version_type ver)
+ {
+ a & x.V;
+ a & x.A;
+ a & x.S;
+ a & x.T1;
+ a & x.T2;
+ a & x.taux;
+ a & x.mu;
+ a & x.L;
+ a & x.R;
+ a & x.a;
+ a & x.b;
+ a & x.t;
+ }
+
+ template <class Archive>
inline void serialize(Archive &a, rct::boroSig &x, const boost::serialization::version_type ver)
{
a & x.s0;
@@ -263,11 +280,11 @@ namespace boost
a & x.type;
if (x.type == rct::RCTTypeNull)
return;
- if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple)
+ if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeFullBulletproof && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeSimpleBulletproof)
throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type");
// a & x.message; message is not serialized, as it can be reconstructed from the tx data
// a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets
- if (x.type == rct::RCTTypeSimple)
+ if (x.type == rct::RCTTypeSimple || x.type == rct::RCTTypeSimpleBulletproof)
a & x.pseudoOuts;
a & x.ecdhInfo;
serializeOutPk(a, x.outPk, ver);
@@ -278,6 +295,8 @@ namespace boost
inline void serialize(Archive &a, rct::rctSigPrunable &x, const boost::serialization::version_type ver)
{
a & x.rangeSigs;
+ if (x.rangeSigs.empty())
+ a & x.bulletproofs;
a & x.MGs;
}
@@ -287,17 +306,19 @@ namespace boost
a & x.type;
if (x.type == rct::RCTTypeNull)
return;
- if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple)
+ if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeFullBulletproof && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeSimpleBulletproof)
throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type");
// a & x.message; message is not serialized, as it can be reconstructed from the tx data
// a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets
- if (x.type == rct::RCTTypeSimple)
+ if (x.type == rct::RCTTypeSimple || x.type == rct::RCTTypeSimpleBulletproof)
a & x.pseudoOuts;
a & x.ecdhInfo;
serializeOutPk(a, x.outPk, ver);
a & x.txnFee;
//--------------
a & x.p.rangeSigs;
+ if (x.p.rangeSigs.empty())
+ a & x.p.bulletproofs;
a & x.p.MGs;
}
}
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 3d586a704..836856bae 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -127,6 +127,7 @@ static const struct {
{ 5, 802660, 0, 1472415036 + 86400*180 }, // add 5 months on testnet to shut the update warning up since there's a large gap to v6
{ 6, 971400, 0, 1501709789 },
+ { 7, 1057028, 0, 1512211236 },
};
static const uint64_t testnet_hard_fork_version_1_till = 624633;
@@ -2387,8 +2388,10 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
+ const uint8_t hf_version = m_hardfork->get_current_version();
+
// from hard fork 2, we forbid dust and compound outputs
- if (m_hardfork->get_current_version() >= 2) {
+ if (hf_version >= 2) {
for (auto &o: tx.vout) {
if (tx.version == 1)
{
@@ -2401,7 +2404,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
}
// in a v2 tx, all outputs must have 0 amount
- if (m_hardfork->get_current_version() >= 3) {
+ if (hf_version >= 3) {
if (tx.version >= 2) {
for (auto &o: tx.vout) {
if (o.amount != 0) {
@@ -2413,7 +2416,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
}
// from v4, forbid invalid pubkeys
- if (m_hardfork->get_current_version() >= 4) {
+ if (hf_version >= 4) {
for (const auto &o: tx.vout) {
if (o.target.type() == typeid(txout_to_key)) {
const txout_to_key& out_to_key = boost::get<txout_to_key>(o.target);
@@ -2425,6 +2428,16 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
}
}
+ // from v7, allow bulletproofs
+ if (hf_version < 7 || !m_testnet) {
+ if (!tx.rct_signatures.p.bulletproofs.empty())
+ {
+ MERROR("Bulletproofs are not allowed before v7 or on mainnet");
+ tvc.m_invalid_output = true;
+ return false;
+ }
+ }
+
return true;
}
//------------------------------------------------------------------
@@ -2450,7 +2463,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
rv.message = rct::hash2rct(tx_prefix_hash);
// mixRing - full and simple store it in opposite ways
- if (rv.type == rct::RCTTypeFull)
+ if (rv.type == rct::RCTTypeFull || rv.type == rct::RCTTypeFullBulletproof)
{
rv.mixRing.resize(pubkeys[0].size());
for (size_t m = 0; m < pubkeys[0].size(); ++m)
@@ -2464,7 +2477,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
}
}
}
- else if (rv.type == rct::RCTTypeSimple)
+ else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeSimpleBulletproof)
{
rv.mixRing.resize(pubkeys.size());
for (size_t n = 0; n < pubkeys.size(); ++n)
@@ -2482,14 +2495,14 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr
}
// II
- if (rv.type == rct::RCTTypeFull)
+ if (rv.type == rct::RCTTypeFull || rv.type == rct::RCTTypeFullBulletproof)
{
rv.p.MGs.resize(1);
rv.p.MGs[0].II.resize(tx.vin.size());
for (size_t n = 0; n < tx.vin.size(); ++n)
rv.p.MGs[0].II[n] = rct::ki2rct(boost::get<txin_to_key>(tx.vin[n]).k_image);
}
- else if (rv.type == rct::RCTTypeSimple)
+ else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeSimpleBulletproof)
{
CHECK_AND_ASSERT_MES(rv.p.MGs.size() == tx.vin.size(), false, "Bad MGs size");
for (size_t n = 0; n < tx.vin.size(); ++n)
@@ -2753,7 +2766,9 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
MERROR_VER("Null rct signature on non-coinbase tx");
return false;
}
- case rct::RCTTypeSimple: {
+ case rct::RCTTypeSimple:
+ case rct::RCTTypeSimpleBulletproof:
+ {
// check all this, either recontructed (so should really pass), or not
{
if (pubkeys.size() != rv.mixRing.size())
@@ -2809,7 +2824,9 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
}
break;
}
- case rct::RCTTypeFull: {
+ case rct::RCTTypeFull:
+ case rct::RCTTypeFullBulletproof:
+ {
// check all this, either recontructed (so should really pass), or not
{
bool size_matches = true;
diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp
index 5c181208f..5cfa4b3e9 100644
--- a/src/cryptonote_core/cryptonote_core.cpp
+++ b/src/cryptonote_core/cryptonote_core.cpp
@@ -625,6 +625,22 @@ namespace cryptonote
}
for (size_t n = 0; n < tx.rct_signatures.outPk.size(); ++n)
rv.outPk[n].dest = rct::pk2rct(boost::get<txout_to_key>(tx.vout[n].target).key);
+
+ const bool bulletproof = rv.type == rct::RCTTypeFullBulletproof || rv.type == rct::RCTTypeSimpleBulletproof;
+ if (bulletproof)
+ {
+ if (rv.p.bulletproofs.size() != tx.vout.size())
+ {
+ LOG_PRINT_L1("WRONG TRANSACTION BLOB, Bad bulletproofs size in tx " << tx_hash << ", rejected");
+ tvc.m_verifivation_failed = true;
+ return false;
+ }
+ for (size_t n = 0; n < rv.outPk.size(); ++n)
+ {
+ rv.p.bulletproofs[n].V.resize(1);
+ rv.p.bulletproofs[n].V[0] = rv.outPk[n].mask;
+ }
+ }
}
if (keeped_by_block && get_blockchain_storage().is_within_compiled_block_hash_area())
@@ -828,6 +844,7 @@ namespace cryptonote
MERROR_VER("Unexpected Null rctSig type");
return false;
case rct::RCTTypeSimple:
+ case rct::RCTTypeSimpleBulletproof:
if (!rct::verRctSimple(rv, true))
{
MERROR_VER("rct signature semantics check failed");
@@ -835,6 +852,7 @@ namespace cryptonote
}
break;
case rct::RCTTypeFull:
+ case rct::RCTTypeFullBulletproof:
if (!rct::verRct(rv, true))
{
MERROR_VER("rct signature semantics check failed");
@@ -1329,6 +1347,7 @@ namespace cryptonote
<< "where <level> is between 0 (no details) and 4 (very verbose), or custom category based levels (eg, *:WARNING)" << ENDL
<< ENDL
<< "Use the \"help\" command to see the list of available commands." << ENDL
+ << "Use \"help <command>\" to see a command's documentation." << ENDL
<< "**********************************************************************" << ENDL);
m_starter_message_showed = true;
}
diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp
index feefc1592..4afa669fd 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.cpp
+++ b/src/cryptonote_core/cryptonote_tx_utils.cpp
@@ -160,7 +160,7 @@ namespace cryptonote
return destinations[0].addr.m_view_public_key;
}
//---------------------------------------------------------------
- bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct)
+ bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct, bool bulletproof)
{
std::vector<rct::key> amount_keys;
tx.set_null();
@@ -281,7 +281,7 @@ namespace cryptonote
std::sort(ins_order.begin(), ins_order.end(), [&](const size_t i0, const size_t i1) {
const txin_to_key &tk0 = boost::get<txin_to_key>(tx.vin[i0]);
const txin_to_key &tk1 = boost::get<txin_to_key>(tx.vin[i1]);
- return memcmp(&tk0.k_image, &tk1.k_image, sizeof(tk0.k_image)) < 0;
+ return memcmp(&tk0.k_image, &tk1.k_image, sizeof(tk0.k_image)) > 0;
});
tools::apply_permutation(ins_order, [&] (size_t i0, size_t i1) {
std::swap(tx.vin[i0], tx.vin[i1]);
@@ -552,9 +552,9 @@ namespace cryptonote
get_transaction_prefix_hash(tx, tx_prefix_hash);
rct::ctkeyV outSk;
if (use_simple_rct)
- tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, index, outSk);
+ tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, index, outSk, bulletproof);
else
- tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, sources[0].real_output, outSk); // same index assumption
+ tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, sources[0].real_output, outSk, bulletproof); // same index assumption
CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout");
diff --git a/src/cryptonote_core/cryptonote_tx_utils.h b/src/cryptonote_core/cryptonote_tx_utils.h
index 8d9a1e332..d72f5d13b 100644
--- a/src/cryptonote_core/cryptonote_tx_utils.h
+++ b/src/cryptonote_core/cryptonote_tx_utils.h
@@ -88,7 +88,7 @@ namespace cryptonote
//---------------------------------------------------------------
crypto::public_key get_destination_view_key_pub(const std::vector<tx_destination_entry> &destinations, const account_keys &sender_keys);
bool construct_tx(const account_keys& sender_account_keys, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time);
- bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false);
+ bool construct_tx_and_get_tx_key(const account_keys& sender_account_keys, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, std::vector<tx_source_entry>& sources, const std::vector<tx_destination_entry>& destinations, const boost::optional<cryptonote::account_public_address>& change_addr, std::vector<uint8_t> extra, transaction& tx, uint64_t unlock_time, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys, bool rct = false, bool bulletproof = false);
bool generate_genesis_block(
block& bl
diff --git a/src/cryptonote_protocol/cryptonote_protocol_defs.h b/src/cryptonote_protocol/cryptonote_protocol_defs.h
index 1804cc101..7cf7e4a4d 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_defs.h
+++ b/src/cryptonote_protocol/cryptonote_protocol_defs.h
@@ -74,7 +74,7 @@ namespace cryptonote
uint32_t support_flags;
- boost::uuids::uuid connection_id;
+ std::string connection_id;
uint64_t height;
@@ -98,7 +98,7 @@ namespace cryptonote
KV_SERIALIZE(avg_upload)
KV_SERIALIZE(current_upload)
KV_SERIALIZE(support_flags)
- KV_SERIALIZE_VAL_POD_AS_BLOB(connection_id)
+ KV_SERIALIZE(connection_id)
KV_SERIALIZE(height)
END_KV_SERIALIZE_MAP()
};
diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
index 73433a8d8..48ab1eadf 100644
--- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl
+++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl
@@ -244,7 +244,7 @@ namespace cryptonote
cnx.current_download = cntxt.m_current_speed_down / 1024;
cnx.current_upload = cntxt.m_current_speed_up / 1024;
- cnx.connection_id = cntxt.m_connection_id;
+ cnx.connection_id = epee::string_tools::pod_to_hex(cntxt.m_connection_id);
cnx.height = cntxt.m_remote_blockchain_height;
@@ -1414,6 +1414,10 @@ skip:
// take out blocks we already have
while (!context.m_needed_objects.empty() && m_core.have_block(context.m_needed_objects.front()))
{
+ // if we're popping the last hash, record it so we can ask again from that hash,
+ // this prevents never being able to progress on peers we get old hash lists from
+ if (context.m_needed_objects.size() == 1)
+ context.m_last_known_hash = context.m_needed_objects.front();
context.m_needed_objects.pop_front();
}
const uint64_t first_block_height = context.m_last_response_height - context.m_needed_objects.size() + 1;
diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp
index b134aee61..7ff6b2bf3 100644
--- a/src/daemon/command_server.cpp
+++ b/src/daemon/command_server.cpp
@@ -49,214 +49,228 @@ t_command_server::t_command_server(
, m_is_rpc(is_rpc)
{
m_command_lookup.set_handler(
- "q"
- , [] (const std::vector<std::string>& args) {return true;}
- , "ignored"
- );
- m_command_lookup.set_handler(
"help"
, std::bind(&t_command_server::help, this, p::_1)
- , "Show this help"
+ , "help [<command>]"
+ , "Show the help section or the documentation about a <command>."
);
m_command_lookup.set_handler(
"print_height"
, std::bind(&t_command_parser_executor::print_height, &m_parser, p::_1)
- , "Print local blockchain height"
+ , "Print the local blockchain height."
);
m_command_lookup.set_handler(
"print_pl"
, std::bind(&t_command_parser_executor::print_peer_list, &m_parser, p::_1)
- , "Print peer list"
+ , "Print the current peer list."
);
m_command_lookup.set_handler(
"print_pl_stats"
, std::bind(&t_command_parser_executor::print_peer_list_stats, &m_parser, p::_1)
- , "Print peer list stats"
+ , "Print the peer list statistics."
);
m_command_lookup.set_handler(
"print_cn"
, std::bind(&t_command_parser_executor::print_connections, &m_parser, p::_1)
- , "Print connections"
+ , "Print the current connections."
);
m_command_lookup.set_handler(
"print_bc"
, std::bind(&t_command_parser_executor::print_blockchain_info, &m_parser, p::_1)
- , "Print blockchain info in a given blocks range, print_bc <begin_height> [<end_height>]"
+ , "print_bc <begin_height> [<end_height>]"
+ , "Print the blockchain info in a given blocks range."
);
m_command_lookup.set_handler(
"print_block"
, std::bind(&t_command_parser_executor::print_block, &m_parser, p::_1)
- , "Print block, print_block <block_hash> | <block_height>"
+ , "print_block <block_hash> | <block_height>"
+ , "Print a given block."
);
m_command_lookup.set_handler(
"print_tx"
, std::bind(&t_command_parser_executor::print_transaction, &m_parser, p::_1)
- , "Print transaction, print_tx <transaction_hash> [+hex] [+json]"
+ , "print_tx <transaction_hash> [+hex] [+json]"
+ , "Print a given transaction."
);
m_command_lookup.set_handler(
"is_key_image_spent"
, std::bind(&t_command_parser_executor::is_key_image_spent, &m_parser, p::_1)
- , "Prints whether a given key image is in the spent key images set, is_key_image_spent <key_image>"
+ , "is_key_image_spent <key_image>"
+ , "Print whether a given key image is in the spent key images set."
);
m_command_lookup.set_handler(
"start_mining"
, std::bind(&t_command_parser_executor::start_mining, &m_parser, p::_1)
- , "Start mining for specified address, start_mining <addr> [<threads>] [do_background_mining] [ignore_battery], default 1 thread, no background mining"
+ , "start_mining <addr> [<threads>] [do_background_mining] [ignore_battery]"
+ , "Start mining for specified address. Defaults to 1 thread and no background mining."
);
m_command_lookup.set_handler(
"stop_mining"
, std::bind(&t_command_parser_executor::stop_mining, &m_parser, p::_1)
- , "Stop mining"
+ , "Stop mining."
);
m_command_lookup.set_handler(
"print_pool"
, std::bind(&t_command_parser_executor::print_transaction_pool_long, &m_parser, p::_1)
- , "Print transaction pool (long format)"
+ , "Print the transaction pool using a long format."
);
m_command_lookup.set_handler(
"print_pool_sh"
, std::bind(&t_command_parser_executor::print_transaction_pool_short, &m_parser, p::_1)
- , "Print transaction pool (short format)"
+ , "Print transaction pool using a short format."
);
m_command_lookup.set_handler(
"print_pool_stats"
, std::bind(&t_command_parser_executor::print_transaction_pool_stats, &m_parser, p::_1)
- , "Print transaction pool statistics"
+ , "Print the transaction pool's statistics."
);
m_command_lookup.set_handler(
"show_hr"
, std::bind(&t_command_parser_executor::show_hash_rate, &m_parser, p::_1)
- , "Start showing hash rate"
+ , "Start showing the current hash rate."
);
m_command_lookup.set_handler(
"hide_hr"
, std::bind(&t_command_parser_executor::hide_hash_rate, &m_parser, p::_1)
- , "Stop showing hash rate"
+ , "Stop showing the hash rate."
);
m_command_lookup.set_handler(
"save"
, std::bind(&t_command_parser_executor::save_blockchain, &m_parser, p::_1)
- , "Save blockchain"
+ , "Save the blockchain."
);
m_command_lookup.set_handler(
"set_log"
, std::bind(&t_command_parser_executor::set_log_level, &m_parser, p::_1)
- , "set_log <level>|<{+,-,}categories> - Change current log level/categories, <level> is a number 0-4"
+ , "set_log <level>|<{+,-,}categories>"
+ , "Change the current log level/categories where <level> is a number 0-4."
);
m_command_lookup.set_handler(
"diff"
, std::bind(&t_command_parser_executor::show_difficulty, &m_parser, p::_1)
- , "Show difficulty"
+ , "Show the current difficulty."
);
m_command_lookup.set_handler(
"status"
, std::bind(&t_command_parser_executor::show_status, &m_parser, p::_1)
- , "Show status"
+ , "Show the current status."
);
m_command_lookup.set_handler(
"stop_daemon"
, std::bind(&t_command_parser_executor::stop_daemon, &m_parser, p::_1)
- , "Stop the daemon"
+ , "Stop the daemon."
);
m_command_lookup.set_handler(
"exit"
, std::bind(&t_command_parser_executor::stop_daemon, &m_parser, p::_1)
- , "Stop the daemon"
+ , "Stop the daemon."
);
m_command_lookup.set_handler(
"print_status"
, std::bind(&t_command_parser_executor::print_status, &m_parser, p::_1)
- , "Prints daemon status"
+ , "Print the current daemon status."
);
m_command_lookup.set_handler(
"limit"
, std::bind(&t_command_parser_executor::set_limit, &m_parser, p::_1)
- , "limit <kB/s> - Set download and upload limit"
+ , "limit [<kB/s>]"
+ , "Get or set the download and upload limit."
);
m_command_lookup.set_handler(
"limit_up"
, std::bind(&t_command_parser_executor::set_limit_up, &m_parser, p::_1)
- , "limit <kB/s> - Set upload limit"
+ , "limit_up [<kB/s>]"
+ , "Get or set the upload limit."
);
m_command_lookup.set_handler(
"limit_down"
, std::bind(&t_command_parser_executor::set_limit_down, &m_parser, p::_1)
- , "limit <kB/s> - Set download limit"
+ , "limit_down [<kB/s>]"
+ , "Get or set the download limit."
);
m_command_lookup.set_handler(
"out_peers"
, std::bind(&t_command_parser_executor::out_peers, &m_parser, p::_1)
- , "Set max number of out peers"
+ , "out_peers <max_number>"
+ , "Set the <max_number> of out peers."
);
m_command_lookup.set_handler(
"start_save_graph"
, std::bind(&t_command_parser_executor::start_save_graph, &m_parser, p::_1)
- , "Start save data for dr monero"
+ , "Start saving data for dr monero."
);
m_command_lookup.set_handler(
"stop_save_graph"
, std::bind(&t_command_parser_executor::stop_save_graph, &m_parser, p::_1)
- , "Stop save data for dr monero"
+ , "Stop saving data for dr monero."
);
m_command_lookup.set_handler(
"hard_fork_info"
, std::bind(&t_command_parser_executor::hard_fork_info, &m_parser, p::_1)
- , "Print hard fork voting information"
+ , "Print the hard fork voting information."
);
m_command_lookup.set_handler(
"bans"
, std::bind(&t_command_parser_executor::show_bans, &m_parser, p::_1)
- , "Show the currently banned IPs"
+ , "Show the currently banned IPs."
);
m_command_lookup.set_handler(
"ban"
, std::bind(&t_command_parser_executor::ban, &m_parser, p::_1)
- , "Ban a given IP for a time"
+ , "ban <IP> [<seconds>]"
+ , "Ban a given <IP> for a given amount of <seconds>."
);
m_command_lookup.set_handler(
"unban"
, std::bind(&t_command_parser_executor::unban, &m_parser, p::_1)
- , "Unban a given IP"
+ , "unban <IP>"
+ , "Unban a given <IP>."
);
m_command_lookup.set_handler(
"flush_txpool"
, std::bind(&t_command_parser_executor::flush_txpool, &m_parser, p::_1)
- , "Flush a transaction from the tx pool by its txid, or the whole tx pool"
+ , "flush_txpool [<txid>]"
+ , "Flush a transaction from the tx pool by its <txid>, or the whole tx pool."
);
m_command_lookup.set_handler(
"output_histogram"
, std::bind(&t_command_parser_executor::output_histogram, &m_parser, p::_1)
- , "Print output histogram (amount, instances)"
+ , "output_histogram [@<amount>] <min_count> [<max_count>]"
+ , "Print the output histogram of outputs."
);
m_command_lookup.set_handler(
"print_coinbase_tx_sum"
, std::bind(&t_command_parser_executor::print_coinbase_tx_sum, &m_parser, p::_1)
- , "Print sum of coinbase transactions <start height> [block count]"
+ , "print_coinbase_tx_sum <start_height> [<block_count>]"
+ , "Print the sum of coinbase transactions."
);
m_command_lookup.set_handler(
"alt_chain_info"
, std::bind(&t_command_parser_executor::alt_chain_info, &m_parser, p::_1)
- , "Print information about alternative chains"
+ , "Print the information about alternative chains."
);
m_command_lookup.set_handler(
"bc_dyn_stats"
, std::bind(&t_command_parser_executor::print_blockchain_dynamic_stats, &m_parser, p::_1)
- , "Print information about current blockchain dynamic state, bc_dyn_stats <last n blocks>"
+ , "bc_dyn_stats <last_block_count>"
+ , "Print the information about current blockchain dynamic state."
);
m_command_lookup.set_handler(
"update"
, std::bind(&t_command_parser_executor::update, &m_parser, p::_1)
- , "subcommands: check (check if an update is available), download (download it if there is), update (not implemented)"
+ , "update (check|download)"
+ , "Check if an update is available, optionally downloads it if there is. Updating is not yet implemented."
);
m_command_lookup.set_handler(
"relay_tx"
, std::bind(&t_command_parser_executor::relay_tx, &m_parser, p::_1)
- , "Relay a given transaction by its txid"
+ , "relay_tx <txid>"
+ , "Relay a given transaction by its <txid>."
);
m_command_lookup.set_handler(
"sync_info"
, std::bind(&t_command_parser_executor::sync_info, &m_parser, p::_1)
- , "Print information about blockchain sync state"
+ , "Print information about the blockchain sync state."
);
}
@@ -293,7 +307,14 @@ void t_command_server::stop_handling()
bool t_command_server::help(const std::vector<std::string>& args)
{
- std::cout << get_commands_str() << std::endl;
+ if(args.empty())
+ {
+ std::cout << get_commands_str() << std::endl;
+ }
+ else
+ {
+ std::cout << get_command_usage(args) << std::endl;
+ }
return true;
}
@@ -309,4 +330,25 @@ std::string t_command_server::get_commands_str()
return ss.str();
}
+ std::string t_command_server::get_command_usage(const std::vector<std::string> &args)
+ {
+ std::pair<std::string, std::string> documentation = m_command_lookup.get_documentation(args);
+ std::stringstream ss;
+ if(documentation.first.empty())
+ {
+ ss << "Unknown command: " << args.front() << std::endl;
+ }
+ else
+ {
+ std::string usage = documentation.second.empty() ? args.front() : documentation.first;
+ std::string description = documentation.second.empty() ? documentation.first : documentation.second;
+ usage.insert(0, " ");
+ ss << "Command usage: " << std::endl << usage << std::endl << std::endl;
+ boost::replace_all(description, "\n", "\n ");
+ description.insert(0, " ");
+ ss << "Command description: " << std::endl << description << std::endl;
+ }
+ return ss.str();
+ }
+
} // namespace daemonize
diff --git a/src/daemon/command_server.h b/src/daemon/command_server.h
index 476b75141..2ad277f4a 100644
--- a/src/daemon/command_server.h
+++ b/src/daemon/command_server.h
@@ -73,6 +73,7 @@ private:
bool help(const std::vector<std::string>& args);
std::string get_commands_str();
+ std::string get_command_usage(const std::vector<std::string> &args);
};
} // namespace daemonize
diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp
index cf7d5f8ab..f8acf1357 100644
--- a/src/daemon/daemon.cpp
+++ b/src/daemon/daemon.cpp
@@ -60,7 +60,7 @@ private:
public:
t_core core;
t_p2p p2p;
- t_rpc rpc;
+ std::vector<std::unique_ptr<t_rpc>> rpcs;
t_internals(
boost::program_options::variables_map const & vm
@@ -68,11 +68,22 @@ public:
: core{vm}
, protocol{vm, core}
, p2p{vm, protocol}
- , rpc{vm, core, p2p}
{
// Handle circular dependencies
protocol.set_p2p_endpoint(p2p.get());
core.set_protocol(protocol.get());
+
+ const auto testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
+ const auto restricted = command_line::get_arg(vm, cryptonote::core_rpc_server::arg_restricted_rpc);
+ const auto main_rpc_port = command_line::get_arg(vm, testnet ? cryptonote::core_rpc_server::arg_testnet_rpc_bind_port : cryptonote::core_rpc_server::arg_rpc_bind_port);
+ rpcs.emplace_back(new t_rpc{vm, core, p2p, restricted, testnet, main_rpc_port, "core"});
+
+ auto restricted_rpc_port_arg = testnet ? cryptonote::core_rpc_server::arg_testnet_rpc_restricted_bind_port : cryptonote::core_rpc_server::arg_rpc_restricted_bind_port;
+ if(!command_line::is_arg_defaulted(vm, restricted_rpc_port_arg))
+ {
+ auto restricted_rpc_port = command_line::get_arg(vm, restricted_rpc_port_arg);
+ rpcs.emplace_back(new t_rpc{vm, core, p2p, true, testnet, restricted_rpc_port, "restricted"});
+ }
}
};
@@ -135,14 +146,15 @@ bool t_daemon::run(bool interactive)
{
if (!mp_internals->core.run())
return false;
- mp_internals->rpc.run();
- std::unique_ptr<daemonize::t_command_server> rpc_commands;
+ for(auto& rpc: mp_internals->rpcs)
+ rpc->run();
- if (interactive)
+ std::unique_ptr<daemonize::t_command_server> rpc_commands;
+ if (interactive && mp_internals->rpcs.size())
{
// The first three variables are not used when the fourth is false
- rpc_commands.reset(new daemonize::t_command_server(0, 0, boost::none, false, mp_internals->rpc.get_server()));
+ rpc_commands.reset(new daemonize::t_command_server(0, 0, boost::none, false, mp_internals->rpcs.front()->get_server()));
rpc_commands->start_handling(std::bind(&daemonize::t_daemon::stop_p2p, this));
}
@@ -154,12 +166,11 @@ bool t_daemon::run(bool interactive)
LOG_ERROR(std::string("Failed to add TCP Socket (") + zmq_rpc_bind_address
+ ":" + zmq_rpc_bind_port + ") to ZMQ RPC Server");
- if (interactive)
- {
+ if (rpc_commands)
rpc_commands->stop_handling();
- }
- mp_internals->rpc.stop();
+ for(auto& rpc : mp_internals->rpcs)
+ rpc->stop();
return false;
}
@@ -173,13 +184,12 @@ bool t_daemon::run(bool interactive)
mp_internals->p2p.run(); // blocks until p2p goes down
if (rpc_commands)
- {
rpc_commands->stop_handling();
- }
zmq_server.stop();
- mp_internals->rpc.stop();
+ for(auto& rpc : mp_internals->rpcs)
+ rpc->stop();
mp_internals->core.get().get_miner().stop();
MGINFO("Node stopped.");
return true;
@@ -204,7 +214,9 @@ void t_daemon::stop()
}
mp_internals->core.get().get_miner().stop();
mp_internals->p2p.stop();
- mp_internals->rpc.stop();
+ for(auto& rpc : mp_internals->rpcs)
+ rpc->stop();
+
mp_internals.reset(nullptr); // Ensure resources are cleaned up before we return
}
diff --git a/src/daemon/rpc.h b/src/daemon/rpc.h
index 0ecfdd120..9b7438053 100644
--- a/src/daemon/rpc.h
+++ b/src/daemon/rpc.h
@@ -47,35 +47,41 @@ public:
}
private:
cryptonote::core_rpc_server m_server;
+ const std::string m_description;
public:
t_rpc(
boost::program_options::variables_map const & vm
, t_core & core
, t_p2p & p2p
+ , const bool restricted
+ , const bool testnet
+ , const std::string & port
+ , const std::string & description
)
- : m_server{core.get(), p2p.get()}
+ : m_server{core.get(), p2p.get()}, m_description{description}
{
- MGINFO("Initializing core rpc server...");
- if (!m_server.init(vm))
+ MGINFO("Initializing " << m_description << " rpc server...");
+
+ if (!m_server.init(vm, restricted, testnet, port))
{
- throw std::runtime_error("Failed to initialize core rpc server.");
+ throw std::runtime_error("Failed to initialize " + m_description + " rpc server.");
}
- MGINFO("Core rpc server initialized OK on port: " << m_server.get_binded_port());
+ MGINFO(m_description << " rpc server initialized OK on port: " << m_server.get_binded_port());
}
void run()
{
- MGINFO("Starting core rpc server...");
+ MGINFO("Starting " << m_description << " rpc server...");
if (!m_server.run(2, false))
{
- throw std::runtime_error("Failed to start core rpc server.");
+ throw std::runtime_error("Failed to start " + m_description + " rpc server.");
}
- MGINFO("Core rpc server started ok");
+ MGINFO(m_description << " rpc server started ok");
}
void stop()
{
- MGINFO("Stopping core rpc server...");
+ MGINFO("Stopping " << m_description << " rpc server...");
m_server.send_stop_signal();
m_server.timed_wait_server_stop(5000);
}
@@ -87,11 +93,11 @@ public:
~t_rpc()
{
- MGINFO("Deinitializing rpc server...");
+ MGINFO("Deinitializing " << m_description << " rpc server...");
try {
m_server.deinit();
} catch (...) {
- MERROR("Failed to deinitialize rpc server...");
+ MERROR("Failed to deinitialize " << m_description << " rpc server...");
}
}
};
diff --git a/src/ringct/CMakeLists.txt b/src/ringct/CMakeLists.txt
index f9862ac80..1452e5367 100644
--- a/src/ringct/CMakeLists.txt
+++ b/src/ringct/CMakeLists.txt
@@ -30,14 +30,16 @@ set(ringct_sources
rctOps.cpp
rctSigs.cpp
rctTypes.cpp
- rctCryptoOps.c)
+ rctCryptoOps.c
+ bulletproofs.cc)
set(ringct_headers)
set(ringct_private_headers
rctOps.h
rctSigs.h
- rctTypes.h)
+ rctTypes.h
+ bulletproofs.h)
monero_private_headers(ringct
${crypto_private_headers})
@@ -51,4 +53,5 @@ target_link_libraries(ringct
cncrypto
cryptonote_basic
PRIVATE
+ ${OPENSSL_LIBRARIES}
${EXTRA_LIBRARIES})
diff --git a/src/ringct/bulletproofs.cc b/src/ringct/bulletproofs.cc
new file mode 100644
index 000000000..51cf9e3be
--- /dev/null
+++ b/src/ringct/bulletproofs.cc
@@ -0,0 +1,761 @@
+// Copyright (c) 2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Adapted from Java code by Sarang Noether
+
+#include <stdlib.h>
+#include <openssl/ssl.h>
+#include <boost/thread/mutex.hpp>
+#include "misc_log_ex.h"
+#include "common/perf_timer.h"
+extern "C"
+{
+#include "crypto/crypto-ops.h"
+}
+#include "rctOps.h"
+#include "bulletproofs.h"
+
+#undef MONERO_DEFAULT_LOG_CATEGORY
+#define MONERO_DEFAULT_LOG_CATEGORY "bulletproofs"
+
+//#define DEBUG_BP
+
+#define PERF_TIMER_START_BP(x) PERF_TIMER_START_UNIT(x, 1000000)
+
+namespace rct
+{
+
+static rct::key vector_exponent(const rct::keyV &a, const rct::keyV &b);
+static rct::keyV vector_powers(rct::key x, size_t n);
+static rct::key inner_product(const rct::keyV &a, const rct::keyV &b);
+
+static constexpr size_t maxN = 64;
+static rct::key Hi[maxN], Gi[maxN];
+static ge_dsmp Gprecomp[64], Hprecomp[64];
+static const rct::key TWO = { {0x02, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } };
+static const rct::keyV oneN = vector_powers(rct::identity(), maxN);
+static const rct::keyV twoN = vector_powers(TWO, maxN);
+static const rct::key ip12 = inner_product(oneN, twoN);
+static boost::mutex init_mutex;
+
+static rct::key get_exponent(const rct::key &base, size_t idx)
+{
+ static const std::string salt("bulletproof");
+ std::string hashed = std::string((const char*)base.bytes, sizeof(base)) + salt + tools::get_varint_data(idx);
+ return rct::hashToPoint(rct::hash2rct(crypto::cn_fast_hash(hashed.data(), hashed.size())));
+}
+
+static void init_exponents()
+{
+ boost::lock_guard<boost::mutex> lock(init_mutex);
+
+ static bool init_done = false;
+ if (init_done)
+ return;
+ for (size_t i = 0; i < maxN; ++i)
+ {
+ Hi[i] = get_exponent(rct::H, i * 2);
+ rct::precomp(Hprecomp[i], Hi[i]);
+ Gi[i] = get_exponent(rct::H, i * 2 + 1);
+ rct::precomp(Gprecomp[i], Gi[i]);
+ }
+ init_done = true;
+}
+
+/* Given two scalar arrays, construct a vector commitment */
+static rct::key vector_exponent(const rct::keyV &a, const rct::keyV &b)
+{
+ CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b");
+ CHECK_AND_ASSERT_THROW_MES(a.size() <= maxN, "Incompatible sizes of a and maxN");
+ rct::key res = rct::identity();
+ for (size_t i = 0; i < a.size(); ++i)
+ {
+ rct::key term;
+ rct::addKeys3(term, a[i], Gprecomp[i], b[i], Hprecomp[i]);
+ rct::addKeys(res, res, term);
+ }
+ return res;
+}
+
+/* Compute a custom vector-scalar commitment */
+static rct::key vector_exponent_custom(const rct::keyV &A, const rct::keyV &B, const rct::keyV &a, const rct::keyV &b)
+{
+ CHECK_AND_ASSERT_THROW_MES(A.size() == B.size(), "Incompatible sizes of A and B");
+ CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b");
+ CHECK_AND_ASSERT_THROW_MES(a.size() == A.size(), "Incompatible sizes of a and A");
+ CHECK_AND_ASSERT_THROW_MES(a.size() <= maxN, "Incompatible sizes of a and maxN");
+ rct::key res = rct::identity();
+ for (size_t i = 0; i < a.size(); ++i)
+ {
+ rct::key term;
+#if 0
+ // we happen to know where A and B might fall, so don't bother checking the rest
+ ge_dsmp *Acache = NULL, *Bcache = NULL;
+ ge_dsmp Acache_custom[1], Bcache_custom[1];
+ if (Gi[i] == A[i])
+ Acache = Gprecomp + i;
+ else if (i<32 && Gi[i+32] == A[i])
+ Acache = Gprecomp + i + 32;
+ else
+ {
+ rct::precomp(Acache_custom[0], A[i]);
+ Acache = Acache_custom;
+ }
+ if (i == 0 && B[i] == Hi[0])
+ Bcache = Hprecomp;
+ else
+ {
+ rct::precomp(Bcache_custom[0], B[i]);
+ Bcache = Bcache_custom;
+ }
+ rct::addKeys3(term, a[i], *Acache, b[i], *Bcache);
+#else
+ ge_dsmp Acache, Bcache;
+ rct::precomp(Bcache, B[i]);
+ rct::addKeys3(term, a[i], A[i], b[i], Bcache);
+#endif
+ rct::addKeys(res, res, term);
+ }
+ return res;
+}
+
+/* Given a scalar, construct a vector of powers */
+static rct::keyV vector_powers(rct::key x, size_t n)
+{
+ rct::keyV res(n);
+ if (n == 0)
+ return res;
+ res[0] = rct::identity();
+ if (n == 1)
+ return res;
+ res[1] = x;
+ for (size_t i = 2; i < n; ++i)
+ {
+ sc_mul(res[i].bytes, res[i-1].bytes, x.bytes);
+ }
+ return res;
+}
+
+/* Given two scalar arrays, construct the inner product */
+static rct::key inner_product(const rct::keyV &a, const rct::keyV &b)
+{
+ CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b");
+ rct::key res = rct::zero();
+ for (size_t i = 0; i < a.size(); ++i)
+ {
+ sc_muladd(res.bytes, a[i].bytes, b[i].bytes, res.bytes);
+ }
+ return res;
+}
+
+/* Given two scalar arrays, construct the Hadamard product */
+static rct::keyV hadamard(const rct::keyV &a, const rct::keyV &b)
+{
+ CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b");
+ rct::keyV res(a.size());
+ for (size_t i = 0; i < a.size(); ++i)
+ {
+ sc_mul(res[i].bytes, a[i].bytes, b[i].bytes);
+ }
+ return res;
+}
+
+/* Given two curvepoint arrays, construct the Hadamard product */
+static rct::keyV hadamard2(const rct::keyV &a, const rct::keyV &b)
+{
+ CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b");
+ rct::keyV res(a.size());
+ for (size_t i = 0; i < a.size(); ++i)
+ {
+ rct::addKeys(res[i], a[i], b[i]);
+ }
+ return res;
+}
+
+/* Add two vectors */
+static rct::keyV vector_add(const rct::keyV &a, const rct::keyV &b)
+{
+ CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b");
+ rct::keyV res(a.size());
+ for (size_t i = 0; i < a.size(); ++i)
+ {
+ sc_add(res[i].bytes, a[i].bytes, b[i].bytes);
+ }
+ return res;
+}
+
+/* Subtract two vectors */
+static rct::keyV vector_subtract(const rct::keyV &a, const rct::keyV &b)
+{
+ CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b");
+ rct::keyV res(a.size());
+ for (size_t i = 0; i < a.size(); ++i)
+ {
+ sc_sub(res[i].bytes, a[i].bytes, b[i].bytes);
+ }
+ return res;
+}
+
+/* Multiply a scalar and a vector */
+static rct::keyV vector_scalar(const rct::keyV &a, const rct::key &x)
+{
+ rct::keyV res(a.size());
+ for (size_t i = 0; i < a.size(); ++i)
+ {
+ sc_mul(res[i].bytes, a[i].bytes, x.bytes);
+ }
+ return res;
+}
+
+/* Exponentiate a curve vector by a scalar */
+static rct::keyV vector_scalar2(const rct::keyV &a, const rct::key &x)
+{
+ rct::keyV res(a.size());
+ for (size_t i = 0; i < a.size(); ++i)
+ {
+ rct::scalarmultKey(res[i], a[i], x);
+ }
+ return res;
+}
+
+static rct::key switch_endianness(rct::key k)
+{
+ std::reverse(k.bytes, k.bytes + sizeof(k));
+ return k;
+}
+
+/* Compute the inverse of a scalar, the stupid way */
+static rct::key invert(const rct::key &x)
+{
+ rct::key inv;
+
+ BN_CTX *ctx = BN_CTX_new();
+ BIGNUM *X = BN_new();
+ BIGNUM *L = BN_new();
+ BIGNUM *I = BN_new();
+
+ BN_bin2bn(switch_endianness(x).bytes, sizeof(rct::key), X);
+ BN_bin2bn(switch_endianness(rct::curveOrder()).bytes, sizeof(rct::key), L);
+
+ CHECK_AND_ASSERT_THROW_MES(BN_mod_inverse(I, X, L, ctx), "Failed to invert");
+
+ const int len = BN_num_bytes(I);
+ CHECK_AND_ASSERT_THROW_MES((size_t)len <= sizeof(rct::key), "Invalid number length");
+ inv = rct::zero();
+ BN_bn2bin(I, inv.bytes);
+ std::reverse(inv.bytes, inv.bytes + len);
+
+ BN_free(I);
+ BN_free(L);
+ BN_free(X);
+ BN_CTX_free(ctx);
+
+#ifdef DEBUG_BP
+ rct::key tmp;
+ sc_mul(tmp.bytes, inv.bytes, x.bytes);
+ CHECK_AND_ASSERT_THROW_MES(tmp == rct::identity(), "invert failed");
+#endif
+ return inv;
+}
+
+/* Compute the slice of a vector */
+static rct::keyV slice(const rct::keyV &a, size_t start, size_t stop)
+{
+ CHECK_AND_ASSERT_THROW_MES(start < a.size(), "Invalid start index");
+ CHECK_AND_ASSERT_THROW_MES(stop <= a.size(), "Invalid stop index");
+ CHECK_AND_ASSERT_THROW_MES(start < stop, "Invalid start/stop indices");
+ rct::keyV res(stop - start);
+ for (size_t i = start; i < stop; ++i)
+ {
+ res[i - start] = a[i];
+ }
+ return res;
+}
+
+/* Given a value v (0..2^N-1) and a mask gamma, construct a range proof */
+Bulletproof bulletproof_PROVE(const rct::key &sv, const rct::key &gamma)
+{
+ init_exponents();
+
+ PERF_TIMER_UNIT(PROVE, 1000000);
+
+ constexpr size_t logN = 6; // log2(64)
+ constexpr size_t N = 1<<logN;
+
+ rct::key V;
+ rct::keyV aL(N), aR(N);
+
+ PERF_TIMER_START_BP(PROVE_v);
+ rct::addKeys2(V, gamma, sv, rct::H);
+ PERF_TIMER_STOP(PROVE_v);
+
+ PERF_TIMER_START_BP(PROVE_aLaR);
+ for (size_t i = N; i-- > 0; )
+ {
+ if (sv[i/8] & (((uint64_t)1)<<(i%8)))
+ {
+ aL[i] = rct::identity();
+ }
+ else
+ {
+ aL[i] = rct::zero();
+ }
+ sc_sub(aR[i].bytes, aL[i].bytes, rct::identity().bytes);
+ }
+ PERF_TIMER_STOP(PROVE_aLaR);
+
+
+ // DEBUG: Test to ensure this recovers the value
+#ifdef DEBUG_BP
+ uint64_t test_aL = 0, test_aR = 0;
+ for (size_t i = 0; i < N; ++i)
+ {
+ if (aL[i] == rct::identity())
+ test_aL += ((uint64_t)1)<<i;
+ if (aR[i] == rct::zero())
+ test_aR += ((uint64_t)1)<<i;
+ }
+ uint64_t v_test = 0;
+ for (int n = 0; n < 8; ++n) v_test |= (((uint64_t)sv[n]) << (8*n));
+ CHECK_AND_ASSERT_THROW_MES(test_aL == v_test, "test_aL failed");
+ CHECK_AND_ASSERT_THROW_MES(test_aR == v_test, "test_aR failed");
+#endif
+
+ PERF_TIMER_START_BP(PROVE_step1);
+ // PAPER LINES 38-39
+ rct::key alpha = rct::skGen();
+ rct::key ve = vector_exponent(aL, aR);
+ rct::key A;
+ rct::addKeys(A, ve, rct::scalarmultBase(alpha));
+
+ // PAPER LINES 40-42
+ rct::keyV sL = rct::skvGen(N), sR = rct::skvGen(N);
+ rct::key rho = rct::skGen();
+ ve = vector_exponent(sL, sR);
+ rct::key S;
+ rct::addKeys(S, ve, rct::scalarmultBase(rho));
+
+ // PAPER LINES 43-45
+ rct::keyV hashed;
+ hashed.push_back(A);
+ hashed.push_back(S);
+ rct::key y = rct::hash_to_scalar(hashed);
+ rct::key z = rct::hash_to_scalar(y);
+
+ // Polynomial construction before PAPER LINE 46
+ rct::key t0 = rct::zero();
+ rct::key t1 = rct::zero();
+ rct::key t2 = rct::zero();
+
+ const auto yN = vector_powers(y, N);
+
+ rct::key ip1y = inner_product(oneN, yN);
+ rct::key tmp;
+ sc_muladd(t0.bytes, z.bytes, ip1y.bytes, t0.bytes);
+
+ rct::key zsq;
+ sc_mul(zsq.bytes, z.bytes, z.bytes);
+ sc_muladd(t0.bytes, zsq.bytes, sv.bytes, t0.bytes);
+
+ rct::key k = rct::zero();
+ sc_mulsub(k.bytes, zsq.bytes, ip1y.bytes, k.bytes);
+
+ rct::key zcu;
+ sc_mul(zcu.bytes, zsq.bytes, z.bytes);
+ sc_mulsub(k.bytes, zcu.bytes, ip12.bytes, k.bytes);
+ sc_add(t0.bytes, t0.bytes, k.bytes);
+
+ // DEBUG: Test the value of t0 has the correct form
+#ifdef DEBUG_BP
+ rct::key test_t0 = rct::zero();
+ rct::key iph = inner_product(aL, hadamard(aR, yN));
+ sc_add(test_t0.bytes, test_t0.bytes, iph.bytes);
+ rct::key ips = inner_product(vector_subtract(aL, aR), yN);
+ sc_muladd(test_t0.bytes, z.bytes, ips.bytes, test_t0.bytes);
+ rct::key ipt = inner_product(twoN, aL);
+ sc_muladd(test_t0.bytes, zsq.bytes, ipt.bytes, test_t0.bytes);
+ sc_add(test_t0.bytes, test_t0.bytes, k.bytes);
+ CHECK_AND_ASSERT_THROW_MES(t0 == test_t0, "t0 check failed");
+#endif
+ PERF_TIMER_STOP(PROVE_step1);
+
+ PERF_TIMER_START_BP(PROVE_step2);
+ const auto HyNsR = hadamard(yN, sR);
+ const auto vpIz = vector_scalar(oneN, z);
+ const auto vp2zsq = vector_scalar(twoN, zsq);
+ const auto aL_vpIz = vector_subtract(aL, vpIz);
+ const auto aR_vpIz = vector_add(aR, vpIz);
+
+ rct::key ip1 = inner_product(aL_vpIz, HyNsR);
+ sc_add(t1.bytes, t1.bytes, ip1.bytes);
+
+ rct::key ip2 = inner_product(sL, vector_add(hadamard(yN, aR_vpIz), vp2zsq));
+ sc_add(t1.bytes, t1.bytes, ip2.bytes);
+
+ rct::key ip3 = inner_product(sL, HyNsR);
+ sc_add(t2.bytes, t2.bytes, ip3.bytes);
+
+ // PAPER LINES 47-48
+ rct::key tau1 = rct::skGen(), tau2 = rct::skGen();
+
+ rct::key T1 = rct::addKeys(rct::scalarmultKey(rct::H, t1), rct::scalarmultBase(tau1));
+ rct::key T2 = rct::addKeys(rct::scalarmultKey(rct::H, t2), rct::scalarmultBase(tau2));
+
+ // PAPER LINES 49-51
+ hashed.clear();
+ hashed.push_back(z);
+ hashed.push_back(T1);
+ hashed.push_back(T2);
+ rct::key x = rct::hash_to_scalar(hashed);
+
+ // PAPER LINES 52-53
+ rct::key taux = rct::zero();
+ sc_mul(taux.bytes, tau1.bytes, x.bytes);
+ rct::key xsq;
+ sc_mul(xsq.bytes, x.bytes, x.bytes);
+ sc_muladd(taux.bytes, tau2.bytes, xsq.bytes, taux.bytes);
+ sc_muladd(taux.bytes, gamma.bytes, zsq.bytes, taux.bytes);
+ rct::key mu;
+ sc_muladd(mu.bytes, x.bytes, rho.bytes, alpha.bytes);
+
+ // PAPER LINES 54-57
+ rct::keyV l = vector_add(aL_vpIz, vector_scalar(sL, x));
+ rct::keyV r = vector_add(hadamard(yN, vector_add(aR_vpIz, vector_scalar(sR, x))), vp2zsq);
+ PERF_TIMER_STOP(PROVE_step2);
+
+ PERF_TIMER_START_BP(PROVE_step3);
+ rct::key t = inner_product(l, r);
+
+ // DEBUG: Test if the l and r vectors match the polynomial forms
+#ifdef DEBUG_BP
+ rct::key test_t;
+ sc_muladd(test_t.bytes, t1.bytes, x.bytes, t0.bytes);
+ sc_muladd(test_t.bytes, t2.bytes, xsq.bytes, test_t.bytes);
+ CHECK_AND_ASSERT_THROW_MES(test_t == t, "test_t check failed");
+#endif
+
+ // PAPER LINES 32-33
+ hashed.clear();
+ hashed.push_back(x);
+ hashed.push_back(taux);
+ hashed.push_back(mu);
+ hashed.push_back(t);
+ rct::key x_ip = rct::hash_to_scalar(hashed);
+
+ // These are used in the inner product rounds
+ size_t nprime = N;
+ rct::keyV Gprime(N);
+ rct::keyV Hprime(N);
+ rct::keyV aprime(N);
+ rct::keyV bprime(N);
+ const rct::key yinv = invert(y);
+ rct::key yinvpow = rct::identity();
+ for (size_t i = 0; i < N; ++i)
+ {
+ Gprime[i] = Gi[i];
+ Hprime[i] = scalarmultKey(Hi[i], yinvpow);
+ sc_mul(yinvpow.bytes, yinvpow.bytes, yinv.bytes);
+ aprime[i] = l[i];
+ bprime[i] = r[i];
+ }
+ rct::keyV L(logN);
+ rct::keyV R(logN);
+ int round = 0;
+ rct::keyV w(logN); // this is the challenge x in the inner product protocol
+ PERF_TIMER_STOP(PROVE_step3);
+
+ PERF_TIMER_START_BP(PROVE_step4);
+ // PAPER LINE 13
+ while (nprime > 1)
+ {
+ // PAPER LINE 15
+ nprime /= 2;
+
+ // PAPER LINES 16-17
+ rct::key cL = inner_product(slice(aprime, 0, nprime), slice(bprime, nprime, bprime.size()));
+ rct::key cR = inner_product(slice(aprime, nprime, aprime.size()), slice(bprime, 0, nprime));
+
+ // PAPER LINES 18-19
+ L[round] = vector_exponent_custom(slice(Gprime, nprime, Gprime.size()), slice(Hprime, 0, nprime), slice(aprime, 0, nprime), slice(bprime, nprime, bprime.size()));
+ sc_mul(tmp.bytes, cL.bytes, x_ip.bytes);
+ rct::addKeys(L[round], L[round], rct::scalarmultKey(rct::H, tmp));
+ R[round] = vector_exponent_custom(slice(Gprime, 0, nprime), slice(Hprime, nprime, Hprime.size()), slice(aprime, nprime, aprime.size()), slice(bprime, 0, nprime));
+ sc_mul(tmp.bytes, cR.bytes, x_ip.bytes);
+ rct::addKeys(R[round], R[round], rct::scalarmultKey(rct::H, tmp));
+
+ // PAPER LINES 21-22
+ hashed.clear();
+ if (round == 0)
+ {
+ hashed.push_back(L[0]);
+ hashed.push_back(R[0]);
+ w[0] = rct::hash_to_scalar(hashed);
+ }
+ else
+ {
+ hashed.push_back(w[round - 1]);
+ hashed.push_back(L[round]);
+ hashed.push_back(R[round]);
+ w[round] = rct::hash_to_scalar(hashed);
+ }
+
+ // PAPER LINES 24-25
+ const rct::key winv = invert(w[round]);
+ Gprime = hadamard2(vector_scalar2(slice(Gprime, 0, nprime), winv), vector_scalar2(slice(Gprime, nprime, Gprime.size()), w[round]));
+ Hprime = hadamard2(vector_scalar2(slice(Hprime, 0, nprime), w[round]), vector_scalar2(slice(Hprime, nprime, Hprime.size()), winv));
+
+ // PAPER LINES 28-29
+ aprime = vector_add(vector_scalar(slice(aprime, 0, nprime), w[round]), vector_scalar(slice(aprime, nprime, aprime.size()), winv));
+ bprime = vector_add(vector_scalar(slice(bprime, 0, nprime), winv), vector_scalar(slice(bprime, nprime, bprime.size()), w[round]));
+
+ ++round;
+ }
+ PERF_TIMER_STOP(PROVE_step4);
+
+ // PAPER LINE 58 (with inclusions from PAPER LINE 8 and PAPER LINE 20)
+ return Bulletproof(V, A, S, T1, T2, taux, mu, L, R, aprime[0], bprime[0], t);
+}
+
+Bulletproof bulletproof_PROVE(uint64_t v, const rct::key &gamma)
+{
+ // vG + gammaH
+ PERF_TIMER_START_BP(PROVE_v);
+ rct::key sv = rct::zero();
+ sv.bytes[0] = v & 255;
+ sv.bytes[1] = (v >> 8) & 255;
+ sv.bytes[2] = (v >> 16) & 255;
+ sv.bytes[3] = (v >> 24) & 255;
+ sv.bytes[4] = (v >> 32) & 255;
+ sv.bytes[5] = (v >> 40) & 255;
+ sv.bytes[6] = (v >> 48) & 255;
+ sv.bytes[7] = (v >> 56) & 255;
+ PERF_TIMER_STOP(PROVE_v);
+ return bulletproof_PROVE(sv, gamma);
+}
+
+/* Given a range proof, determine if it is valid */
+bool bulletproof_VERIFY(const Bulletproof &proof)
+{
+ init_exponents();
+
+ CHECK_AND_ASSERT_MES(proof.L.size() == proof.R.size(), false, "Mismatched L and R sizes");
+ CHECK_AND_ASSERT_MES(proof.L.size() > 0, false, "Empty proof");
+ CHECK_AND_ASSERT_MES(proof.L.size() == 6, false, "Proof is not for 64 bits");
+
+ const size_t logN = proof.L.size();
+ const size_t N = 1 << logN;
+
+ // Reconstruct the challenges
+ PERF_TIMER_START_BP(VERIFY);
+ PERF_TIMER_START_BP(VERIFY_start);
+ rct::keyV hashed;
+ hashed.push_back(proof.A);
+ hashed.push_back(proof.S);
+ rct::key y = rct::hash_to_scalar(hashed);
+ rct::key z = rct::hash_to_scalar(y);
+ hashed.clear();
+ hashed.push_back(z);
+ hashed.push_back(proof.T1);
+ hashed.push_back(proof.T2);
+ rct::key x = rct::hash_to_scalar(hashed);
+ PERF_TIMER_STOP(VERIFY_start);
+
+ PERF_TIMER_START_BP(VERIFY_line_60);
+ // Reconstruct the challenges
+ hashed.clear();
+ hashed.push_back(x);
+ hashed.push_back(proof.taux);
+ hashed.push_back(proof.mu);
+ hashed.push_back(proof.t);
+ rct::key x_ip = hash_to_scalar(hashed);
+ PERF_TIMER_STOP(VERIFY_line_60);
+
+ PERF_TIMER_START_BP(VERIFY_line_61);
+ // PAPER LINE 61
+ rct::key L61Left = rct::addKeys(rct::scalarmultBase(proof.taux), rct::scalarmultKey(rct::H, proof.t));
+
+ rct::key k = rct::zero();
+ const auto yN = vector_powers(y, N);
+ rct::key ip1y = inner_product(oneN, yN);
+ rct::key zsq;
+ sc_mul(zsq.bytes, z.bytes, z.bytes);
+ rct::key tmp, tmp2;
+ sc_mulsub(k.bytes, zsq.bytes, ip1y.bytes, k.bytes);
+ rct::key zcu;
+ sc_mul(zcu.bytes, zsq.bytes, z.bytes);
+ sc_mulsub(k.bytes, zcu.bytes, ip12.bytes, k.bytes);
+ PERF_TIMER_STOP(VERIFY_line_61);
+
+ PERF_TIMER_START_BP(VERIFY_line_61rl);
+ sc_muladd(tmp.bytes, z.bytes, ip1y.bytes, k.bytes);
+ rct::key L61Right = rct::scalarmultKey(rct::H, tmp);
+
+ CHECK_AND_ASSERT_MES(proof.V.size() == 1, false, "proof.V does not have exactly one element");
+ tmp = rct::scalarmultKey(proof.V[0], zsq);
+ rct::addKeys(L61Right, L61Right, tmp);
+
+ tmp = rct::scalarmultKey(proof.T1, x);
+ rct::addKeys(L61Right, L61Right, tmp);
+
+ rct::key xsq;
+ sc_mul(xsq.bytes, x.bytes, x.bytes);
+ tmp = rct::scalarmultKey(proof.T2, xsq);
+ rct::addKeys(L61Right, L61Right, tmp);
+ PERF_TIMER_STOP(VERIFY_line_61rl);
+
+ if (!(L61Right == L61Left))
+ {
+ MERROR("Verification failure at step 1");
+ return false;
+ }
+
+ PERF_TIMER_START_BP(VERIFY_line_62);
+ // PAPER LINE 62
+ rct::key P = rct::addKeys(proof.A, rct::scalarmultKey(proof.S, x));
+ PERF_TIMER_STOP(VERIFY_line_62);
+
+ // Compute the number of rounds for the inner product
+ const size_t rounds = proof.L.size();
+ CHECK_AND_ASSERT_MES(rounds > 0, false, "Zero rounds");
+
+ PERF_TIMER_START_BP(VERIFY_line_21_22);
+ // PAPER LINES 21-22
+ // The inner product challenges are computed per round
+ rct::keyV w(rounds);
+ hashed.clear();
+ hashed.push_back(proof.L[0]);
+ hashed.push_back(proof.R[0]);
+ w[0] = rct::hash_to_scalar(hashed);
+ for (size_t i = 1; i < rounds; ++i)
+ {
+ hashed.clear();
+ hashed.push_back(w[i-1]);
+ hashed.push_back(proof.L[i]);
+ hashed.push_back(proof.R[i]);
+ w[i] = rct::hash_to_scalar(hashed);
+ }
+ PERF_TIMER_STOP(VERIFY_line_21_22);
+
+ PERF_TIMER_START_BP(VERIFY_line_24_25);
+ // Basically PAPER LINES 24-25
+ // Compute the curvepoints from G[i] and H[i]
+ rct::key inner_prod = rct::identity();
+ rct::key yinvpow = rct::identity();
+ rct::key ypow = rct::identity();
+
+ PERF_TIMER_START_BP(VERIFY_line_24_25_invert);
+ const rct::key yinv = invert(y);
+ rct::keyV winv(rounds);
+ for (size_t i = 0; i < rounds; ++i)
+ winv[i] = invert(w[i]);
+ PERF_TIMER_STOP(VERIFY_line_24_25_invert);
+
+ for (size_t i = 0; i < N; ++i)
+ {
+ // Convert the index to binary IN REVERSE and construct the scalar exponent
+ rct::key g_scalar = proof.a;
+ rct::key h_scalar;
+ sc_mul(h_scalar.bytes, proof.b.bytes, yinvpow.bytes);
+
+ for (size_t j = rounds; j-- > 0; )
+ {
+ size_t J = w.size() - j - 1;
+
+ if ((i & (((size_t)1)<<j)) == 0)
+ {
+ sc_mul(g_scalar.bytes, g_scalar.bytes, winv[J].bytes);
+ sc_mul(h_scalar.bytes, h_scalar.bytes, w[J].bytes);
+ }
+ else
+ {
+ sc_mul(g_scalar.bytes, g_scalar.bytes, w[J].bytes);
+ sc_mul(h_scalar.bytes, h_scalar.bytes, winv[J].bytes);
+ }
+ }
+
+ // Adjust the scalars using the exponents from PAPER LINE 62
+ sc_add(g_scalar.bytes, g_scalar.bytes, z.bytes);
+ sc_mul(tmp.bytes, zsq.bytes, twoN[i].bytes);
+ sc_muladd(tmp.bytes, z.bytes, ypow.bytes, tmp.bytes);
+ sc_mulsub(h_scalar.bytes, tmp.bytes, yinvpow.bytes, h_scalar.bytes);
+
+ // Now compute the basepoint's scalar multiplication
+ // Each of these could be written as a multiexp operation instead
+ rct::addKeys3(tmp, g_scalar, Gprecomp[i], h_scalar, Hprecomp[i]);
+ rct::addKeys(inner_prod, inner_prod, tmp);
+
+ if (i != N-1)
+ {
+ sc_mul(yinvpow.bytes, yinvpow.bytes, yinv.bytes);
+ sc_mul(ypow.bytes, ypow.bytes, y.bytes);
+ }
+ }
+ PERF_TIMER_STOP(VERIFY_line_24_25);
+
+ PERF_TIMER_START_BP(VERIFY_line_26);
+ // PAPER LINE 26
+ rct::key pprime;
+ sc_sub(tmp.bytes, rct::zero().bytes, proof.mu.bytes);
+ rct::addKeys(pprime, P, rct::scalarmultBase(tmp));
+
+ for (size_t i = 0; i < rounds; ++i)
+ {
+ sc_mul(tmp.bytes, w[i].bytes, w[i].bytes);
+ sc_mul(tmp2.bytes, winv[i].bytes, winv[i].bytes);
+#if 1
+ ge_dsmp cacheL, cacheR;
+ rct::precomp(cacheL, proof.L[i]);
+ rct::precomp(cacheR, proof.R[i]);
+ rct::addKeys3(tmp, tmp, cacheL, tmp2, cacheR);
+ rct::addKeys(pprime, pprime, tmp);
+#else
+ rct::addKeys(pprime, pprime, rct::scalarmultKey(proof.L[i], tmp));
+ rct::addKeys(pprime, pprime, rct::scalarmultKey(proof.R[i], tmp2));
+#endif
+ }
+ sc_mul(tmp.bytes, proof.t.bytes, x_ip.bytes);
+ rct::addKeys(pprime, pprime, rct::scalarmultKey(rct::H, tmp));
+ PERF_TIMER_STOP(VERIFY_line_26);
+
+ PERF_TIMER_START_BP(VERIFY_step2_check);
+ sc_mul(tmp.bytes, proof.a.bytes, proof.b.bytes);
+ sc_mul(tmp.bytes, tmp.bytes, x_ip.bytes);
+ tmp = rct::scalarmultKey(rct::H, tmp);
+ rct::addKeys(tmp, tmp, inner_prod);
+ PERF_TIMER_STOP(VERIFY_step2_check);
+ if (!(pprime == tmp))
+ {
+ MERROR("Verification failure at step 2");
+ return false;
+ }
+
+ PERF_TIMER_STOP(VERIFY);
+ return true;
+}
+
+}
diff --git a/src/ringct/bulletproofs.h b/src/ringct/bulletproofs.h
new file mode 100644
index 000000000..aca470f47
--- /dev/null
+++ b/src/ringct/bulletproofs.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Adapted from Java code by Sarang Noether
+
+#pragma once
+
+#ifndef BULLETPROOFS_H
+#define BULLETPROOFS_H
+
+#include "rctTypes.h"
+
+namespace rct
+{
+
+Bulletproof bulletproof_PROVE(const rct::key &v, const rct::key &gamma);
+Bulletproof bulletproof_PROVE(uint64_t v, const rct::key &gamma);
+bool bulletproof_VERIFY(const Bulletproof &proof);
+
+}
+
+#endif
diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp
index d0e0964b6..8e94b52b3 100644
--- a/src/ringct/rctOps.cpp
+++ b/src/ringct/rctOps.cpp
@@ -220,6 +220,11 @@ namespace rct {
ge_p3_tobytes(AB.bytes, &A2);
}
+ rct::key addKeys(const key &A, const key &B) {
+ key k;
+ addKeys(k, A, B);
+ return k;
+ }
//addKeys1
//aGB = aG + B where a is a scalar, G is the basepoint, and B is a point
@@ -257,6 +262,15 @@ namespace rct {
ge_tobytes(aAbB.bytes, &rv);
}
+ //addKeys3
+ //aAbB = a*A + b*B where a, b are scalars, A, B are curve points
+ //A and B must be input after applying "precomp"
+ void addKeys3(key &aAbB, const key &a, const ge_dsmp A, const key &b, const ge_dsmp B) {
+ ge_p2 rv;
+ ge_double_scalarmult_precomp_vartime2(&rv, a.bytes, A, b.bytes, B);
+ ge_tobytes(aAbB.bytes, &rv);
+ }
+
//subtract Keys (subtracts curve points)
//AB = A - B where A, B are curve points
diff --git a/src/ringct/rctOps.h b/src/ringct/rctOps.h
index 412450c18..3f8f6955c 100644
--- a/src/ringct/rctOps.h
+++ b/src/ringct/rctOps.h
@@ -123,6 +123,7 @@ namespace rct {
//for curve points: AB = A + B
void addKeys(key &AB, const key &A, const key &B);
+ rct::key addKeys(const key &A, const key &B);
//aGB = aG + B where a is a scalar, G is the basepoint, and B is a point
void addKeys1(key &aGB, const key &a, const key & B);
//aGbB = aG + bB where a, b are scalars, G is the basepoint and B is a point
@@ -133,6 +134,7 @@ namespace rct {
//aAbB = a*A + b*B where a, b are scalars, A, B are curve points
//B must be input after applying "precomp"
void addKeys3(key &aAbB, const key &a, const key &A, const key &b, const ge_dsmp B);
+ void addKeys3(key &aAbB, const key &a, const ge_dsmp A, const key &b, const ge_dsmp B);
//AB = A - B where A, B are curve points
void subKeys(key &AB, const key &A, const key &B);
//checks if A, B are equal as curve points
diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp
index 946325367..38b213e8b 100644
--- a/src/ringct/rctSigs.cpp
+++ b/src/ringct/rctSigs.cpp
@@ -33,6 +33,7 @@
#include "common/threadpool.h"
#include "common/util.h"
#include "rctSigs.h"
+#include "bulletproofs.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
using namespace crypto;
@@ -42,6 +43,15 @@ using namespace std;
#define MONERO_DEFAULT_LOG_CATEGORY "ringct"
namespace rct {
+ Bulletproof proveRangeBulletproof(key &C, key &mask, uint64_t amount)
+ {
+ mask = rct::skGen();
+ Bulletproof proof = bulletproof_PROVE(amount, mask);
+ CHECK_AND_ASSERT_THROW_MES(proof.V.size() == 1, "V has not exactly one element");
+ C = proof.V[0];
+ return proof;
+ }
+
//Borromean (c.f. gmax/andytoshi's paper)
boroSig genBorromean(const key64 x, const key64 P1, const key64 P2, const bits indices) {
key64 L[2], alpha;
@@ -335,16 +345,41 @@ namespace rct {
hashes.push_back(hash2rct(h));
keyV kv;
- kv.reserve((64*3+1) * rv.p.rangeSigs.size());
- for (auto r: rv.p.rangeSigs)
+ if (rv.type == RCTTypeSimpleBulletproof || rv.type == RCTTypeFullBulletproof)
+ {
+ kv.reserve((6*2+9) * rv.p.bulletproofs.size());
+ for (const auto &p: rv.p.bulletproofs)
+ {
+ // V are not hashed as they're expanded from outPk.mask
+ // (and thus hashed as part of rctSigBase above)
+ kv.push_back(p.A);
+ kv.push_back(p.S);
+ kv.push_back(p.T1);
+ kv.push_back(p.T2);
+ kv.push_back(p.taux);
+ kv.push_back(p.mu);
+ for (size_t n = 0; n < p.L.size(); ++n)
+ kv.push_back(p.L[n]);
+ for (size_t n = 0; n < p.R.size(); ++n)
+ kv.push_back(p.R[n]);
+ kv.push_back(p.a);
+ kv.push_back(p.b);
+ kv.push_back(p.t);
+ }
+ }
+ else
{
- for (size_t n = 0; n < 64; ++n)
- kv.push_back(r.asig.s0[n]);
- for (size_t n = 0; n < 64; ++n)
- kv.push_back(r.asig.s1[n]);
- kv.push_back(r.asig.ee);
- for (size_t n = 0; n < 64; ++n)
- kv.push_back(r.Ci[n]);
+ kv.reserve((64*3+1) * rv.p.rangeSigs.size());
+ for (const auto &r: rv.p.rangeSigs)
+ {
+ for (size_t n = 0; n < 64; ++n)
+ kv.push_back(r.asig.s0[n]);
+ for (size_t n = 0; n < 64; ++n)
+ kv.push_back(r.asig.s1[n]);
+ kv.push_back(r.asig.ee);
+ for (size_t n = 0; n < 64; ++n)
+ kv.push_back(r.Ci[n]);
+ }
}
hashes.push_back(cn_fast_hash(kv));
return cn_fast_hash(hashes);
@@ -563,7 +598,7 @@ namespace rct {
// must know the destination private key to find the correct amount, else will return a random number
// Note: For txn fees, the last index in the amounts vector should contain that
// Thus the amounts vector will be "one" longer than the destinations vectort
- rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, unsigned int index, ctkeyV &outSk) {
+ rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, unsigned int index, ctkeyV &outSk, bool bulletproof) {
CHECK_AND_ASSERT_THROW_MES(amounts.size() == destinations.size() || amounts.size() == destinations.size() + 1, "Different number of amounts/destinations");
CHECK_AND_ASSERT_THROW_MES(amount_keys.size() == destinations.size(), "Different number of amount_keys/destinations");
CHECK_AND_ASSERT_THROW_MES(index < mixRing.size(), "Bad index into mixRing");
@@ -572,10 +607,13 @@ namespace rct {
}
rctSig rv;
- rv.type = RCTTypeFull;
+ rv.type = bulletproof ? RCTTypeFullBulletproof : RCTTypeFull;
rv.message = message;
rv.outPk.resize(destinations.size());
- rv.p.rangeSigs.resize(destinations.size());
+ if (bulletproof)
+ rv.p.bulletproofs.resize(destinations.size());
+ else
+ rv.p.rangeSigs.resize(destinations.size());
rv.ecdhInfo.resize(destinations.size());
size_t i = 0;
@@ -585,8 +623,14 @@ namespace rct {
//add destination to sig
rv.outPk[i].dest = copy(destinations[i]);
//compute range proof
- rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, amounts[i]);
+ if (bulletproof)
+ rv.p.bulletproofs[i] = proveRangeBulletproof(rv.outPk[i].mask, outSk[i].mask, amounts[i]);
+ else
+ rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, amounts[i]);
#ifdef DBG
+ if (bulletproof)
+ CHECK_AND_ASSERT_THROW_MES(bulletproof_VERIFY(rv.p.bulletproofs[i]), "bulletproof_VERIFY failed on newly created proof");
+ else
CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof");
#endif
@@ -618,12 +662,12 @@ namespace rct {
ctkeyM mixRing;
ctkeyV outSk;
tie(mixRing, index) = populateFromBlockchain(inPk, mixin);
- return genRct(message, inSk, destinations, amounts, mixRing, amount_keys, index, outSk);
+ return genRct(message, inSk, destinations, amounts, mixRing, amount_keys, index, outSk, false);
}
//RCT simple
//for post-rct only
- rctSig genRctSimple(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> &inamounts, const vector<xmr_amount> &outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<unsigned int> & index, ctkeyV &outSk) {
+ rctSig genRctSimple(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> &inamounts, const vector<xmr_amount> &outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<unsigned int> & index, ctkeyV &outSk, bool bulletproof) {
CHECK_AND_ASSERT_THROW_MES(inamounts.size() > 0, "Empty inamounts");
CHECK_AND_ASSERT_THROW_MES(inamounts.size() == inSk.size(), "Different number of inamounts/inSk");
CHECK_AND_ASSERT_THROW_MES(outamounts.size() == destinations.size(), "Different number of amounts/destinations");
@@ -635,10 +679,13 @@ namespace rct {
}
rctSig rv;
- rv.type = RCTTypeSimple;
+ rv.type = bulletproof ? RCTTypeSimpleBulletproof : RCTTypeSimple;
rv.message = message;
rv.outPk.resize(destinations.size());
- rv.p.rangeSigs.resize(destinations.size());
+ if (bulletproof)
+ rv.p.bulletproofs.resize(destinations.size());
+ else
+ rv.p.rangeSigs.resize(destinations.size());
rv.ecdhInfo.resize(destinations.size());
size_t i;
@@ -650,10 +697,16 @@ namespace rct {
//add destination to sig
rv.outPk[i].dest = copy(destinations[i]);
//compute range proof
- rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, outamounts[i]);
- #ifdef DBG
- verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]);
- #endif
+ if (bulletproof)
+ rv.p.bulletproofs[i] = proveRangeBulletproof(rv.outPk[i].mask, outSk[i].mask, outamounts[i]);
+ else
+ rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, outamounts[i]);
+ #ifdef DBG
+ if (bulletproof)
+ CHECK_AND_ASSERT_THROW_MES(bulletproof_VERIFY(rv.p.bulletproofs[i]), "bulletproof_VERIFY failed on newly created proof");
+ else
+ CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof");
+ #endif
sc_add(sumout.bytes, outSk[i].mask.bytes, sumout.bytes);
@@ -699,7 +752,7 @@ namespace rct {
mixRing[i].resize(mixin+1);
index[i] = populateFromBlockchainSimple(mixRing[i], inPk[i], mixin);
}
- return genRctSimple(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, amount_keys, index, outSk);
+ return genRctSimple(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, amount_keys, index, outSk, false);
}
//RingCT protocol
@@ -714,10 +767,13 @@ namespace rct {
// must know the destination private key to find the correct amount, else will return a random number
bool verRct(const rctSig & rv, bool semantics) {
PERF_TIMER(verRct);
- CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "verRct called on non-full rctSig");
+ CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeFullBulletproof, false, "verRct called on non-full rctSig");
if (semantics)
{
- CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs");
+ if (rv.type == RCTTypeFullBulletproof)
+ CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.bulletproofs.size(), false, "Mismatched sizes of outPk and rv.p.bulletproofs");
+ else
+ CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs");
CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo");
CHECK_AND_ASSERT_MES(rv.p.MGs.size() == 1, false, "full rctSig has not one MG");
}
@@ -736,7 +792,10 @@ namespace rct {
DP("range proofs verified?");
for (size_t i = 0; i < rv.outPk.size(); i++) {
tpool.submit(&waiter, [&, i] {
- results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]);
+ if (rv.p.rangeSigs.empty())
+ results[i] = bulletproof_VERIFY(rv.p.bulletproofs[i]);
+ else
+ results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]);
});
}
waiter.wait();
@@ -776,10 +835,13 @@ namespace rct {
{
PERF_TIMER(verRctSimple);
- CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple, false, "verRctSimple called on non simple rctSig");
+ CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeSimpleBulletproof, false, "verRctSimple called on non simple rctSig");
if (semantics)
{
- CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs");
+ if (rv.type == RCTTypeSimpleBulletproof)
+ CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.bulletproofs.size(), false, "Mismatched sizes of outPk and rv.p.bulletproofs");
+ else
+ CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs");
CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo");
CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.pseudoOuts and rv.p.MGs");
}
@@ -820,7 +882,10 @@ namespace rct {
results.resize(rv.outPk.size());
for (size_t i = 0; i < rv.outPk.size(); i++) {
tpool.submit(&waiter, [&, i] {
- results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]);
+ if (rv.p.rangeSigs.empty())
+ results[i] = bulletproof_VERIFY(rv.p.bulletproofs[i]);
+ else
+ results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]);
});
}
waiter.wait();
@@ -869,9 +934,9 @@ namespace rct {
// uses the attached ecdh info to find the amounts represented by each output commitment
// must know the destination private key to find the correct amount, else will return a random number
xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask) {
- CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "decodeRct called on non-full rctSig");
- CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo");
+ CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeFullBulletproof, false, "decodeRct called on non-full rctSig");
CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index");
+ CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo");
//mask amount and mask
ecdhTuple ecdh_info = rv.ecdhInfo[i];
@@ -897,9 +962,9 @@ namespace rct {
}
xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key &mask) {
- CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple, false, "decodeRct called on non simple rctSig");
- CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo");
+ CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeSimpleBulletproof, false, "decodeRct called on non simple rctSig");
CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index");
+ CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo");
//mask amount and mask
ecdhTuple ecdh_info = rv.ecdhInfo[i];
diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h
index d158f06f0..46c9cb2df 100644
--- a/src/ringct/rctSigs.h
+++ b/src/ringct/rctSigs.h
@@ -118,10 +118,10 @@ namespace rct {
//decodeRct: (c.f. http://eprint.iacr.org/2015/1098 section 5.1.1)
// uses the attached ecdh info to find the amounts represented by each output commitment
// must know the destination private key to find the correct amount, else will return a random number
- rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, unsigned int index, ctkeyV &outSk);
+ rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & amounts, const ctkeyM &mixRing, const keyV &amount_keys, unsigned int index, ctkeyV &outSk, bool bulletproof);
rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector<xmr_amount> & amounts, const keyV &amount_keys, const int mixin);
rctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, const keyV &amount_keys, xmr_amount txnFee, unsigned int mixin);
- rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<unsigned int> & index, ctkeyV &outSk);
+ rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const std::vector<xmr_amount> & inamounts, const std::vector<xmr_amount> & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector<unsigned int> & index, ctkeyV &outSk, bool bulletproof);
bool verRct(const rctSig & rv, bool semantics);
static inline bool verRct(const rctSig & rv) { return verRct(rv, true) && verRct(rv, false); }
bool verRctSimple(const rctSig & rv, bool semantics);
diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h
index 8147cb602..50dfdb432 100644
--- a/src/ringct/rctTypes.h
+++ b/src/ringct/rctTypes.h
@@ -161,6 +161,39 @@ namespace rct {
FIELD(Ci)
END_SERIALIZE()
};
+
+ struct Bulletproof
+ {
+ rct::keyV V;
+ rct::key A, S, T1, T2;
+ rct::key taux, mu;
+ rct::keyV L, R;
+ rct::key a, b, t;
+
+ Bulletproof() {}
+ Bulletproof(const rct::key &V, const rct::key &A, const rct::key &S, const rct::key &T1, const rct::key &T2, const rct::key &taux, const rct::key &mu, const rct::keyV &L, const rct::keyV &R, const rct::key &a, const rct::key &b, const rct::key &t):
+ V({V}), A(A), S(S), T1(T1), T2(T2), taux(taux), mu(mu), L(L), R(R), a(a), b(b), t(t) {}
+
+ BEGIN_SERIALIZE_OBJECT()
+ // Commitments aren't saved, they're restored via outPk
+ // FIELD(V)
+ FIELD(A)
+ FIELD(S)
+ FIELD(T1)
+ FIELD(T2)
+ FIELD(taux)
+ FIELD(mu)
+ FIELD(L)
+ FIELD(R)
+ FIELD(a)
+ FIELD(b)
+ FIELD(t)
+
+ if (L.empty() || L.size() != R.size())
+ return false;
+ END_SERIALIZE()
+ };
+
//A container to hold all signatures necessary for RingCT
// rangeSigs holds all the rangeproof data of a transaction
// MG holds the MLSAG signature of a transaction
@@ -172,6 +205,8 @@ namespace rct {
RCTTypeNull = 0,
RCTTypeFull = 1,
RCTTypeSimple = 2,
+ RCTTypeFullBulletproof = 3,
+ RCTTypeSimpleBulletproof = 4,
};
struct rctSigBase {
uint8_t type;
@@ -189,13 +224,13 @@ namespace rct {
FIELD(type)
if (type == RCTTypeNull)
return true;
- if (type != RCTTypeFull && type != RCTTypeSimple)
+ if (type != RCTTypeFull && type != RCTTypeFullBulletproof && type != RCTTypeSimple && type != RCTTypeSimpleBulletproof)
return false;
VARINT_FIELD(txnFee)
// inputs/outputs not saved, only here for serialization help
// FIELD(message) - not serialized, it can be reconstructed
// FIELD(mixRing) - not serialized, it can be reconstructed
- if (type == RCTTypeSimple)
+ if (type == RCTTypeSimple || type == RCTTypeSimpleBulletproof)
{
ar.tag("pseudoOuts");
ar.begin_array();
@@ -241,6 +276,7 @@ namespace rct {
};
struct rctSigPrunable {
std::vector<rangeSig> rangeSigs;
+ std::vector<Bulletproof> bulletproofs;
std::vector<mgSig> MGs; // simple rct has N, full has 1
template<bool W, template <bool> class Archive>
@@ -248,26 +284,44 @@ namespace rct {
{
if (type == RCTTypeNull)
return true;
- if (type != RCTTypeFull && type != RCTTypeSimple)
+ if (type != RCTTypeFull && type != RCTTypeFullBulletproof && type != RCTTypeSimple && type != RCTTypeSimpleBulletproof)
return false;
- ar.tag("rangeSigs");
- ar.begin_array();
- PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, rangeSigs);
- if (rangeSigs.size() != outputs)
- return false;
- for (size_t i = 0; i < outputs; ++i)
+ if (type == RCTTypeSimpleBulletproof || type == RCTTypeFullBulletproof)
{
- FIELDS(rangeSigs[i])
- if (outputs - i > 1)
- ar.delimit_array();
+ ar.tag("bp");
+ ar.begin_array();
+ PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, bulletproofs);
+ if (bulletproofs.size() != outputs)
+ return false;
+ for (size_t i = 0; i < outputs; ++i)
+ {
+ FIELDS(bulletproofs[i])
+ if (outputs - i > 1)
+ ar.delimit_array();
+ }
+ ar.end_array();
+ }
+ else
+ {
+ ar.tag("rangeSigs");
+ ar.begin_array();
+ PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, rangeSigs);
+ if (rangeSigs.size() != outputs)
+ return false;
+ for (size_t i = 0; i < outputs; ++i)
+ {
+ FIELDS(rangeSigs[i])
+ if (outputs - i > 1)
+ ar.delimit_array();
+ }
+ ar.end_array();
}
- ar.end_array();
ar.tag("MGs");
ar.begin_array();
// we keep a byte for size of MGs, because we don't know whether this is
// a simple or full rct signature, and it's starting to annoy the hell out of me
- size_t mg_elements = type == RCTTypeSimple ? inputs : 1;
+ size_t mg_elements = (type == RCTTypeSimple || type == RCTTypeSimpleBulletproof) ? inputs : 1;
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_elements, MGs);
if (MGs.size() != mg_elements)
return false;
@@ -285,7 +339,7 @@ namespace rct {
for (size_t j = 0; j < mixin + 1; ++j)
{
ar.begin_array();
- size_t mg_ss2_elements = (type == RCTTypeSimple ? 1 : inputs) + 1;
+ size_t mg_ss2_elements = ((type == RCTTypeSimple || type == RCTTypeSimpleBulletproof) ? 1 : inputs) + 1;
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_ss2_elements, MGs[i].ss[j]);
if (MGs[i].ss[j].size() != mg_ss2_elements)
return false;
@@ -464,6 +518,7 @@ VARIANT_TAG(debug_archive, rct::mgSig, "rct::mgSig");
VARIANT_TAG(debug_archive, rct::rangeSig, "rct::rangeSig");
VARIANT_TAG(debug_archive, rct::boroSig, "rct::boroSig");
VARIANT_TAG(debug_archive, rct::rctSig, "rct::rctSig");
+VARIANT_TAG(debug_archive, rct::Bulletproof, "rct::bulletproof");
VARIANT_TAG(binary_archive, rct::key, 0x90);
VARIANT_TAG(binary_archive, rct::key64, 0x91);
@@ -477,6 +532,7 @@ VARIANT_TAG(binary_archive, rct::mgSig, 0x98);
VARIANT_TAG(binary_archive, rct::rangeSig, 0x99);
VARIANT_TAG(binary_archive, rct::boroSig, 0x9a);
VARIANT_TAG(binary_archive, rct::rctSig, 0x9b);
+VARIANT_TAG(binary_archive, rct::Bulletproof, 0x9c);
VARIANT_TAG(json_archive, rct::key, "rct_key");
VARIANT_TAG(json_archive, rct::key64, "rct_key64");
@@ -490,5 +546,6 @@ VARIANT_TAG(json_archive, rct::mgSig, "rct_mgSig");
VARIANT_TAG(json_archive, rct::rangeSig, "rct_rangeSig");
VARIANT_TAG(json_archive, rct::boroSig, "rct_boroSig");
VARIANT_TAG(json_archive, rct::rctSig, "rct_rctSig");
+VARIANT_TAG(json_archive, rct::Bulletproof, "rct_bulletproof");
#endif /* RCTTYPES_H */
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index e9a6a18aa..3a3d9f664 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -49,7 +49,7 @@ using namespace epee;
#define MONERO_DEFAULT_LOG_CATEGORY "daemon.rpc"
#define MAX_RESTRICTED_FAKE_OUTS_COUNT 40
-#define MAX_RESTRICTED_GLOBAL_FAKE_OUTS_COUNT 500
+#define MAX_RESTRICTED_GLOBAL_FAKE_OUTS_COUNT 5000
namespace
{
@@ -68,7 +68,9 @@ namespace cryptonote
void core_rpc_server::init_options(boost::program_options::options_description& desc)
{
command_line::add_arg(desc, arg_rpc_bind_port);
+ command_line::add_arg(desc, arg_rpc_restricted_bind_port);
command_line::add_arg(desc, arg_testnet_rpc_bind_port);
+ command_line::add_arg(desc, arg_testnet_rpc_restricted_bind_port);
command_line::add_arg(desc, arg_restricted_rpc);
cryptonote::rpc_args::init_options(desc);
}
@@ -83,21 +85,21 @@ namespace cryptonote
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::init(
const boost::program_options::variables_map& vm
+ , const bool restricted
+ , const bool testnet
+ , const std::string& port
)
{
- m_testnet = command_line::get_arg(vm, cryptonote::arg_testnet_on);
+ m_restricted = restricted;
+ m_testnet = testnet;
m_net_server.set_threads_prefix("RPC");
- auto p2p_bind_arg = m_testnet ? arg_testnet_rpc_bind_port : arg_rpc_bind_port;
-
auto rpc_config = cryptonote::rpc_args::process(vm);
if (!rpc_config)
return false;
- m_restricted = command_line::get_arg(vm, arg_restricted_rpc);
-
boost::optional<epee::net_utils::http::login> http_login{};
- std::string port = command_line::get_arg(vm, p2p_bind_arg);
+
if (rpc_config->login)
http_login.emplace(std::move(rpc_config->login->username), std::move(rpc_config->login->password).password());
@@ -1747,12 +1749,13 @@ namespace cryptonote
res.peers.push_back({c});
const cryptonote::block_queue &block_queue = m_p2p.get_payload_object().get_block_queue();
block_queue.foreach([&](const cryptonote::block_queue::span &span) {
+ const std::string span_connection_id = epee::string_tools::pod_to_hex(span.connection_id);
uint32_t speed = (uint32_t)(100.0f * block_queue.get_speed(span.connection_id) + 0.5f);
std::string address = "";
for (const auto &c: m_p2p.get_payload_object().get_connections())
- if (c.connection_id == span.connection_id)
+ if (c.connection_id == span_connection_id)
address = c.address;
- res.spans.push_back({span.start_block_height, span.nblocks, span.connection_id, (uint32_t)(span.rate + 0.5f), speed, span.size, address});
+ res.spans.push_back({span.start_block_height, span.nblocks, span_connection_id, (uint32_t)(span.rate + 0.5f), speed, span.size, address});
return true;
});
@@ -1782,12 +1785,24 @@ namespace cryptonote
, std::to_string(config::RPC_DEFAULT_PORT)
};
+ const command_line::arg_descriptor<std::string> core_rpc_server::arg_rpc_restricted_bind_port = {
+ "rpc-restricted-bind-port"
+ , "Port for restricted RPC server"
+ , ""
+ };
+
const command_line::arg_descriptor<std::string> core_rpc_server::arg_testnet_rpc_bind_port = {
"testnet-rpc-bind-port"
, "Port for testnet RPC server"
, std::to_string(config::testnet::RPC_DEFAULT_PORT)
};
+ const command_line::arg_descriptor<std::string> core_rpc_server::arg_testnet_rpc_restricted_bind_port = {
+ "testnet-rpc-restricted-bind-port"
+ , "Port for testnet restricted RPC server"
+ , ""
+ };
+
const command_line::arg_descriptor<bool> core_rpc_server::arg_restricted_rpc = {
"restricted-rpc"
, "Restrict RPC to view only commands and do not return privacy sensitive data in RPC calls"
diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h
index 7f252258c..bf4371a4e 100644
--- a/src/rpc/core_rpc_server.h
+++ b/src/rpc/core_rpc_server.h
@@ -53,7 +53,9 @@ namespace cryptonote
public:
static const command_line::arg_descriptor<std::string> arg_rpc_bind_port;
+ static const command_line::arg_descriptor<std::string> arg_rpc_restricted_bind_port;
static const command_line::arg_descriptor<std::string> arg_testnet_rpc_bind_port;
+ static const command_line::arg_descriptor<std::string> arg_testnet_rpc_restricted_bind_port;
static const command_line::arg_descriptor<bool> arg_restricted_rpc;
typedef epee::net_utils::connection_context_base connection_context;
@@ -65,7 +67,10 @@ namespace cryptonote
static void init_options(boost::program_options::options_description& desc);
bool init(
- const boost::program_options::variables_map& vm
+ const boost::program_options::variables_map& vm,
+ const bool restricted,
+ const bool testnet,
+ const std::string& port
);
bool is_testnet() const { return m_testnet; }
diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h
index d27d5611e..58a6ce9e1 100644
--- a/src/rpc/core_rpc_server_commands_defs.h
+++ b/src/rpc/core_rpc_server_commands_defs.h
@@ -2077,7 +2077,7 @@ namespace cryptonote
{
uint64_t start_block_height;
uint64_t nblocks;
- boost::uuids::uuid connection_id;
+ std::string connection_id;
uint32_t rate;
uint32_t speed;
uint64_t size;
@@ -2086,7 +2086,7 @@ namespace cryptonote
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(start_block_height)
KV_SERIALIZE(nblocks)
- KV_SERIALIZE_VAL_POD_AS_BLOB(connection_id)
+ KV_SERIALIZE(connection_id)
KV_SERIALIZE(rate)
KV_SERIALIZE(speed)
KV_SERIALIZE(size)
diff --git a/src/serialization/json_object.cpp b/src/serialization/json_object.cpp
index 6e6e51528..2c86d4054 100644
--- a/src/serialization/json_object.cpp
+++ b/src/serialization/json_object.cpp
@@ -1007,6 +1007,7 @@ void toJsonValue(rapidjson::Document& doc, const rct::rctSigPrunable& sig, rapid
val.SetObject();
INSERT_INTO_JSON_OBJECT(val, doc, rangeSigs, sig.rangeSigs);
+ INSERT_INTO_JSON_OBJECT(val, doc, bulletproofs, sig.bulletproofs);
INSERT_INTO_JSON_OBJECT(val, doc, MGs, sig.MGs);
}
@@ -1018,6 +1019,7 @@ void fromJsonValue(const rapidjson::Value& val, rct::rctSigPrunable& sig)
}
GET_FROM_JSON_OBJECT(val, sig.rangeSigs, rangeSigs);
+ GET_FROM_JSON_OBJECT(val, sig.bulletproofs, bulletproofs);
GET_FROM_JSON_OBJECT(val, sig.MGs, MGs);
}
@@ -1052,6 +1054,45 @@ void fromJsonValue(const rapidjson::Value& val, rct::rangeSig& sig)
}
}
+void toJsonValue(rapidjson::Document& doc, const rct::Bulletproof& p, rapidjson::Value& val)
+{
+ val.SetObject();
+
+ INSERT_INTO_JSON_OBJECT(val, doc, V, p.V);
+ INSERT_INTO_JSON_OBJECT(val, doc, A, p.A);
+ INSERT_INTO_JSON_OBJECT(val, doc, S, p.S);
+ INSERT_INTO_JSON_OBJECT(val, doc, T1, p.T1);
+ INSERT_INTO_JSON_OBJECT(val, doc, T2, p.T2);
+ INSERT_INTO_JSON_OBJECT(val, doc, taux, p.taux);
+ INSERT_INTO_JSON_OBJECT(val, doc, mu, p.mu);
+ INSERT_INTO_JSON_OBJECT(val, doc, L, p.L);
+ INSERT_INTO_JSON_OBJECT(val, doc, R, p.R);
+ INSERT_INTO_JSON_OBJECT(val, doc, a, p.a);
+ INSERT_INTO_JSON_OBJECT(val, doc, b, p.b);
+ INSERT_INTO_JSON_OBJECT(val, doc, t, p.t);
+}
+
+void fromJsonValue(const rapidjson::Value& val, rct::Bulletproof& p)
+{
+ if (!val.IsObject())
+ {
+ throw WRONG_TYPE("json object");
+ }
+
+ GET_FROM_JSON_OBJECT(val, p.V, V);
+ GET_FROM_JSON_OBJECT(val, p.A, A);
+ GET_FROM_JSON_OBJECT(val, p.S, S);
+ GET_FROM_JSON_OBJECT(val, p.T1, T1);
+ GET_FROM_JSON_OBJECT(val, p.T2, T2);
+ GET_FROM_JSON_OBJECT(val, p.taux, taux);
+ GET_FROM_JSON_OBJECT(val, p.mu, mu);
+ GET_FROM_JSON_OBJECT(val, p.L, L);
+ GET_FROM_JSON_OBJECT(val, p.R, R);
+ GET_FROM_JSON_OBJECT(val, p.a, a);
+ GET_FROM_JSON_OBJECT(val, p.b, b);
+ GET_FROM_JSON_OBJECT(val, p.t, t);
+}
+
void toJsonValue(rapidjson::Document& doc, const rct::boroSig& sig, rapidjson::Value& val)
{
val.SetObject();
diff --git a/src/serialization/json_object.h b/src/serialization/json_object.h
index 7b9519c48..5dca7b249 100644
--- a/src/serialization/json_object.h
+++ b/src/serialization/json_object.h
@@ -274,6 +274,9 @@ void fromJsonValue(const rapidjson::Value& val, rct::rctSigPrunable& sig);
void toJsonValue(rapidjson::Document& doc, const rct::rangeSig& sig, rapidjson::Value& val);
void fromJsonValue(const rapidjson::Value& val, rct::rangeSig& sig);
+void toJsonValue(rapidjson::Document& doc, const rct::Bulletproof& p, rapidjson::Value& val);
+void fromJsonValue(const rapidjson::Value& val, rct::Bulletproof& p);
+
void toJsonValue(rapidjson::Document& doc, const rct::boroSig& sig, rapidjson::Value& val);
void fromJsonValue(const rapidjson::Value& val, rct::boroSig& sig);
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index a20bbd336..489bbfb4b 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -42,6 +42,7 @@
#include <boost/program_options.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/format.hpp>
+#include <boost/regex.hpp>
#include "include_base_utils.h"
#include "common/i18n.h"
#include "common/command_line.h"
@@ -365,6 +366,27 @@ std::string simple_wallet::get_commands_str()
return ss.str();
}
+std::string simple_wallet::get_command_usage(const std::vector<std::string> &args)
+{
+ std::pair<std::string, std::string> documentation = m_cmd_binder.get_documentation(args);
+ std::stringstream ss;
+ if(documentation.first.empty())
+ {
+ ss << tr("Unknown command: ") << args.front();
+ }
+ else
+ {
+ std::string usage = documentation.second.empty() ? args.front() : documentation.first;
+ std::string description = documentation.second.empty() ? documentation.first : documentation.second;
+ usage.insert(0, " ");
+ ss << tr("Command usage: ") << ENDL << usage << ENDL << ENDL;
+ boost::replace_all(description, "\n", "\n ");
+ description.insert(0, " ");
+ ss << tr("Command description: ") << ENDL << description << ENDL;
+ }
+ return ss.str();
+}
+
bool simple_wallet::viewkey(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
if (m_wallet->ask_password() && !get_and_verify_password()) { return true; }
@@ -885,7 +907,14 @@ bool simple_wallet::set_refresh_from_block_height(const std::vector<std::string>
bool simple_wallet::help(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
{
- success_msg_writer() << get_commands_str();
+ if(args.empty())
+ {
+ success_msg_writer() << get_commands_str();
+ }
+ else
+ {
+ success_msg_writer() << get_command_usage(args);
+ }
return true;
}
@@ -898,63 +927,248 @@ simple_wallet::simple_wallet()
, m_in_manual_refresh(false)
, m_current_subaddress_account(0)
{
- m_cmd_binder.set_handler("start_mining", boost::bind(&simple_wallet::start_mining, this, _1), tr("start_mining [<number_of_threads>] [bg_mining] [ignore_battery] - Start mining in daemon (bg_mining and ignore_battery are optional booleans)"));
- m_cmd_binder.set_handler("stop_mining", boost::bind(&simple_wallet::stop_mining, this, _1), tr("Stop mining in daemon"));
- m_cmd_binder.set_handler("save_bc", boost::bind(&simple_wallet::save_bc, this, _1), tr("Save current blockchain data"));
- m_cmd_binder.set_handler("refresh", boost::bind(&simple_wallet::refresh, this, _1), tr("Synchronize transactions and balance"));
- m_cmd_binder.set_handler("balance", boost::bind(&simple_wallet::show_balance, this, _1), tr("balance [detail] - Show wallet balance of currently selected account"));
- m_cmd_binder.set_handler("incoming_transfers", boost::bind(&simple_wallet::show_incoming_transfers, this, _1), tr("incoming_transfers [available|unavailable] [verbose] [index=<N1>[,<N2>,...]] - Show incoming transfers, all or filtered by availability and address index"));
- m_cmd_binder.set_handler("payments", boost::bind(&simple_wallet::show_payments, this, _1), tr("payments <PID_1> [<PID_2> ... <PID_N>] - Show payments for given payment ID[s]"));
- m_cmd_binder.set_handler("bc_height", boost::bind(&simple_wallet::show_blockchain_height, this, _1), tr("Show blockchain height"));
- m_cmd_binder.set_handler("transfer_original", boost::bind(&simple_wallet::transfer, this, _1), tr("Same as transfer, but using an older transaction building algorithm"));
- m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer_new, this, _1), tr("transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <amount> [<payment_id>] - Transfer <amount> to <address>. If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the fee of the transaction. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included)"));
- m_cmd_binder.set_handler("locked_transfer", boost::bind(&simple_wallet::locked_transfer, this, _1), tr("locked_transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <addr> <amount> <lockblocks> [<payment_id>] - Same as transfer, but with number of blocks to lock the transaction for, max 1000000"));
- m_cmd_binder.set_handler("sweep_unmixable", boost::bind(&simple_wallet::sweep_unmixable, this, _1), tr("Send all unmixable outputs to yourself with ring_size 1"));
- m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1), tr("sweep_all [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id>] - Send all unlocked balance to an address. If the parameter \"index<N1>[,<N2>,...]\" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used."));
- m_cmd_binder.set_handler("sweep_below", boost::bind(&simple_wallet::sweep_below, this, _1), tr("sweep_below <amount_threshold> [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id>] - Send all unlocked outputs below the threshold to an address"));
- m_cmd_binder.set_handler("sweep_single", boost::bind(&simple_wallet::sweep_single, this, _1), tr("sweep_single [<priority>] [<ring_size>] <key_image> <address> [<payment_id>] - Send a single output of the given key image to an address without change"));
- m_cmd_binder.set_handler("donate", boost::bind(&simple_wallet::donate, this, _1), tr("donate [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <amount> [<payment_id>] - Donate <amount> to the development team (donate.getmonero.org)"));
- m_cmd_binder.set_handler("sign_transfer", boost::bind(&simple_wallet::sign_transfer, this, _1), tr("Sign a transaction from a file"));
- m_cmd_binder.set_handler("submit_transfer", boost::bind(&simple_wallet::submit_transfer, this, _1), tr("Submit a signed transaction from a file"));
- m_cmd_binder.set_handler("set_log", boost::bind(&simple_wallet::set_log, this, _1), tr("set_log <level>|{+,-,}<categories> - Change current log detail (level must be <0-4>)"));
- m_cmd_binder.set_handler("account", boost::bind(&simple_wallet::account, this, _1), tr("account [new <label text with white spaces allowed> | switch <index> | label <index> <label text with white spaces allowed>] - If no argments are specified, the wallet shows all the existing accounts along with their balances. If the \"new\" argument is specified, the wallet creates a new account with its label initialized by the provided label text (which can be empty). If the \"switch\" argument is specified, the wallet switches to the account specified by <index>. If the \"label\" argument is specified, the wallet sets the label of the account specified by <index> to the provided label text."));
- m_cmd_binder.set_handler("address", boost::bind(&simple_wallet::print_address, this, _1), tr("address [ new <label text with white spaces allowed> | all | <index_min> [<index_max>] | label <index> <label text with white spaces allowed> ] - If no arguments are specified or <index> is specified, the wallet shows the default or specified address. If \"all\" is specified, the walllet shows all the existing addresses in the currently selected account. If \"new \" is specified, the wallet creates a new address with the provided label text (which can be empty). If \"label\" is specified, the wallet sets the label of the address specified by <index> to the provided label text."));
- m_cmd_binder.set_handler("integrated_address", boost::bind(&simple_wallet::print_integrated_address, this, _1), tr("integrated_address [PID] - Encode a payment ID into an integrated address for the current wallet public address (no argument uses a random payment ID), or decode an integrated address to standard address and payment ID"));
- m_cmd_binder.set_handler("address_book", boost::bind(&simple_wallet::address_book, this, _1), tr("address_book [(add (<address> [pid <long or short payment id>])|<integrated address> [<description possibly with whitespaces>])|(delete <index>)] - Print all entries in the address book, optionally adding/deleting an entry to/from it"));
- m_cmd_binder.set_handler("save", boost::bind(&simple_wallet::save, this, _1), tr("Save wallet data"));
- m_cmd_binder.set_handler("save_watch_only", boost::bind(&simple_wallet::save_watch_only, this, _1), tr("Save a watch-only keys file"));
- m_cmd_binder.set_handler("viewkey", boost::bind(&simple_wallet::viewkey, this, _1), tr("Display private view key"));
- m_cmd_binder.set_handler("spendkey", boost::bind(&simple_wallet::spendkey, this, _1), tr("Display private spend key"));
- m_cmd_binder.set_handler("seed", boost::bind(&simple_wallet::seed, this, _1), tr("Display Electrum-style mnemonic seed"));
- m_cmd_binder.set_handler("set", boost::bind(&simple_wallet::set_variable, this, _1), tr("Available options: seed language - set wallet seed language; always-confirm-transfers <1|0> - whether to confirm unsplit txes; print-ring-members <1|0> - whether to print detailed information about ring members during confirmation; store-tx-info <1|0> - whether to store outgoing tx info (destination address, payment ID, tx secret key) for future reference; default-ring-size <n> - set default ring size (default is 5); auto-refresh <1|0> - whether to automatically sync new blocks from the daemon; refresh-type <full|optimize-coinbase|no-coinbase|default> - set wallet refresh behaviour; priority [0|1|2|3|4] - default/unimportant/normal/elevated/priority fee; confirm-missing-payment-id <1|0>; ask-password <1|0>; unit <monero|millinero|micronero|nanonero|piconero> - set default monero (sub-)unit; min-outputs-count [n] - try to keep at least that many outputs of value at least min-outputs-value; min-outputs-value [n] - try to keep at least min-outputs-count outputs of at least that value; merge-destinations <1|0> - whether to merge multiple payments to the same destination address; confirm-backlog <1|0> - whether to warn if there is transaction backlog; confirm-backlog-threshold [n] - sets a threshold for confirm-backlog to only warn if the transaction backlog is greater than n blocks; refresh-from-block-height [n] - set height before which to ignore blocks"));
- m_cmd_binder.set_handler("encrypted_seed", boost::bind(&simple_wallet::encrypted_seed, this, _1), tr("Display encrypted Electrum-style mnemonic seed"));
- m_cmd_binder.set_handler("rescan_spent", boost::bind(&simple_wallet::rescan_spent, this, _1), tr("Rescan blockchain for spent outputs"));
- m_cmd_binder.set_handler("get_tx_key", boost::bind(&simple_wallet::get_tx_key, this, _1), tr("Get transaction key (r) for a given <txid>"));
- m_cmd_binder.set_handler("check_tx_key", boost::bind(&simple_wallet::check_tx_key, this, _1), tr("Check amount going to <address> in <txid>"));
- m_cmd_binder.set_handler("get_tx_proof", boost::bind(&simple_wallet::get_tx_proof, this, _1), tr("Generate a signature proving payment/receipt of money to/by <address> in <txid> using the transaction/view secret key"));
- m_cmd_binder.set_handler("check_tx_proof", boost::bind(&simple_wallet::check_tx_proof, this, _1), tr("Check tx proof for payment going to <address> in <txid>"));
- m_cmd_binder.set_handler("get_spend_proof", boost::bind(&simple_wallet::get_spend_proof, this, _1), tr("Generate a signature proving that you generated <txid> using the spend secret key"));
- m_cmd_binder.set_handler("check_spend_proof", boost::bind(&simple_wallet::check_spend_proof, this, _1), tr("Check a signature proving that the signer generated <txid>"));
- m_cmd_binder.set_handler("show_transfers", boost::bind(&simple_wallet::show_transfers, this, _1), tr("show_transfers [in|out|pending|failed|pool] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]] - Show incoming/outgoing transfers within an optional height range"));
- m_cmd_binder.set_handler("unspent_outputs", boost::bind(&simple_wallet::unspent_outputs, this, _1), tr("unspent_outputs [index=<N1>[,<N2>,...]] [<min_amount> [<max_amount>]] - Show unspent outputs of a specified address within an optional amount range"));
- m_cmd_binder.set_handler("rescan_bc", boost::bind(&simple_wallet::rescan_blockchain, this, _1), tr("Rescan blockchain from scratch"));
- m_cmd_binder.set_handler("set_tx_note", boost::bind(&simple_wallet::set_tx_note, this, _1), tr("Set an arbitrary string note for a txid"));
- m_cmd_binder.set_handler("get_tx_note", boost::bind(&simple_wallet::get_tx_note, this, _1), tr("Get a string note for a txid"));
- m_cmd_binder.set_handler("set_description", boost::bind(&simple_wallet::set_description, this, _1), tr("Set an arbitrary description for the wallet"));
- m_cmd_binder.set_handler("get_description", boost::bind(&simple_wallet::get_description, this, _1), tr("Get the description of the wallet "));
- m_cmd_binder.set_handler("status", boost::bind(&simple_wallet::status, this, _1), tr("Show wallet status information"));
- m_cmd_binder.set_handler("wallet_info", boost::bind(&simple_wallet::wallet_info, this, _1), tr("Show wallet information"));
- m_cmd_binder.set_handler("sign", boost::bind(&simple_wallet::sign, this, _1), tr("Sign the contents of a file"));
- m_cmd_binder.set_handler("verify", boost::bind(&simple_wallet::verify, this, _1), tr("Verify a signature on the contents of a file"));
- m_cmd_binder.set_handler("export_key_images", boost::bind(&simple_wallet::export_key_images, this, _1), tr("Export a signed set of key images"));
- m_cmd_binder.set_handler("import_key_images", boost::bind(&simple_wallet::import_key_images, this, _1), tr("Import signed key images list and verify their spent status"));
- m_cmd_binder.set_handler("export_outputs", boost::bind(&simple_wallet::export_outputs, this, _1), tr("Export a set of outputs owned by this wallet"));
- m_cmd_binder.set_handler("import_outputs", boost::bind(&simple_wallet::import_outputs, this, _1), tr("Import set of outputs owned by this wallet"));
- m_cmd_binder.set_handler("show_transfer", boost::bind(&simple_wallet::show_transfer, this, _1), tr("Show information about a transfer to/from this address"));
- m_cmd_binder.set_handler("password", boost::bind(&simple_wallet::change_password, this, _1), tr("Change wallet password"));
- m_cmd_binder.set_handler("payment_id", boost::bind(&simple_wallet::payment_id, this, _1), tr("Generate a new random full size payment id - these will be unencrypted on the blockchain, see integrated_address for encrypted short payment ids"));
- m_cmd_binder.set_handler("fee", boost::bind(&simple_wallet::print_fee_info, this, _1), tr("Print information about fee and current transaction backlog"));
- m_cmd_binder.set_handler("help", boost::bind(&simple_wallet::help, this, _1), tr("Show this help"));
+ m_cmd_binder.set_handler("start_mining",
+ boost::bind(&simple_wallet::start_mining, this, _1),
+ tr("start_mining [<number_of_threads>] [bg_mining] [ignore_battery]"),
+ tr("Start mining in the daemon (bg_mining and ignore_battery are optional booleans)."));
+ m_cmd_binder.set_handler("stop_mining",
+ boost::bind(&simple_wallet::stop_mining, this, _1),
+ tr("Stop mining in the daemon."));
+ m_cmd_binder.set_handler("set_daemon",
+ boost::bind(&simple_wallet::set_daemon, this, _1),
+ tr("set_daemon <host>[:<port>]"),
+ tr("Set another daemon to connect to."));
+ m_cmd_binder.set_handler("save_bc",
+ boost::bind(&simple_wallet::save_bc, this, _1),
+ tr("Save the current blockchain data."));
+ m_cmd_binder.set_handler("refresh",
+ boost::bind(&simple_wallet::refresh, this, _1),
+ tr("Synchronize the transactions and balance."));
+ m_cmd_binder.set_handler("balance",
+ boost::bind(&simple_wallet::show_balance, this, _1),
+ tr("balance [detail]"),
+ tr("Show the wallet's balance of the currently selected account."));
+ m_cmd_binder.set_handler("incoming_transfers",
+ boost::bind(&simple_wallet::show_incoming_transfers, this, _1),
+ tr("incoming_transfers [available|unavailable] [verbose] [index=<N1>[,<N2>[,...]]]"),
+ tr("Show the incoming transfers, all or filtered by availability and address index."));
+ m_cmd_binder.set_handler("payments",
+ boost::bind(&simple_wallet::show_payments, this, _1),
+ tr("payments <PID_1> [<PID_2> ... <PID_N>]"),
+ tr("Show the payments for the given payment IDs."));
+ m_cmd_binder.set_handler("bc_height",
+ boost::bind(&simple_wallet::show_blockchain_height, this, _1),
+ tr("Show the blockchain height."));
+ m_cmd_binder.set_handler("transfer_original",
+ boost::bind(&simple_wallet::transfer, this, _1),
+ tr("transfer_original [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <amount> [<payment_id>]"),
+ tr("Transfer <amount> to <address> using an older transaction building algorithm. If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the fee of the transaction. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included)"));
+ m_cmd_binder.set_handler("transfer", boost::bind(&simple_wallet::transfer_new, this, _1),
+ tr("transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> <amount> [<payment_id>]"),
+ tr("Transfer <amount> to <address>. If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the fee of the transaction. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included)"));
+ m_cmd_binder.set_handler("locked_transfer",
+ boost::bind(&simple_wallet::locked_transfer, this, _1),
+ tr("locked_transfer [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <addr> <amount> <lockblocks> [<payment_id>]"),
+ tr("Transfer <amount> to <address> and lock it for <lockblocks> (max. 1000000). If the parameter \"index=<N1>[,<N2>,...]\" is specified, the wallet uses outputs received by addresses of those indices. If omitted, the wallet randomly chooses address indices to be used. In any case, it tries its best not to combine outputs across multiple addresses. <priority> is the priority of the transaction. The higher the priority, the higher the fee of the transaction. Valid values in priority order (from lowest to highest) are: unimportant, normal, elevated, priority. If omitted, the default value (see the command \"set priority\") is used. <ring_size> is the number of inputs to include for untraceability. Multiple payments can be made at once by adding <address_2> <amount_2> etcetera (before the payment ID, if it's included)"));
+ m_cmd_binder.set_handler("sweep_unmixable",
+ boost::bind(&simple_wallet::sweep_unmixable, this, _1),
+ tr("Send all unmixable outputs to yourself with ring_size 1"));
+ m_cmd_binder.set_handler("sweep_all", boost::bind(&simple_wallet::sweep_all, this, _1),
+ tr("sweep_all [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id>]"),
+ tr("Send all unlocked balance to an address. If the parameter \"index<N1>[,<N2>,...]\" is specified, the wallet sweeps outputs received by those address indices. If omitted, the wallet randomly chooses an address index to be used."));
+ m_cmd_binder.set_handler("sweep_below",
+ boost::bind(&simple_wallet::sweep_below, this, _1),
+ tr("sweep_below <amount_threshold> [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <address> [<payment_id>]"),
+ tr("Send all unlocked outputs below the threshold to an address."));
+ m_cmd_binder.set_handler("sweep_single",
+ boost::bind(&simple_wallet::sweep_single, this, _1),
+ tr("sweep_single [<priority>] [<ring_size>] <key_image> <address> [<payment_id>]"),
+ tr("Send a single output of the given key image to an address without change."));
+ m_cmd_binder.set_handler("donate",
+ boost::bind(&simple_wallet::donate, this, _1),
+ tr("donate [index=<N1>[,<N2>,...]] [<priority>] [<ring_size>] <amount> [<payment_id>]"),
+ tr("Donate <amount> to the development team (donate.getmonero.org)."));
+ m_cmd_binder.set_handler("sign_transfer",
+ boost::bind(&simple_wallet::sign_transfer, this, _1),
+ tr("sign_transfer <file>"),
+ tr("Sign a transaction from a <file>."));
+ m_cmd_binder.set_handler("submit_transfer",
+ boost::bind(&simple_wallet::submit_transfer, this, _1),
+ tr("Submit a signed transaction from a file."));
+ m_cmd_binder.set_handler("set_log",
+ boost::bind(&simple_wallet::set_log, this, _1),
+ tr("set_log <level>|{+,-,}<categories>"),
+ tr("Change the current log detail (level must be <0-4>)."));
+ m_cmd_binder.set_handler("account",
+ boost::bind(&simple_wallet::account, this, _1),
+ tr("account [new <label text with white spaces allowed> | switch <index> | label <index> <label text with white spaces allowed>]"),
+ tr("If no arguments are specified, the wallet shows all the existing accounts along with their balances. If the \"new\" argument is specified, the wallet creates a new account with its label initialized by the provided label text (which can be empty). If the \"switch\" argument is specified, the wallet switches to the account specified by <index>. If the \"label\" argument is specified, the wallet sets the label of the account specified by <index> to the provided label text."));
+ m_cmd_binder.set_handler("address",
+ boost::bind(&simple_wallet::print_address, this, _1),
+ tr("address [ new <label text with white spaces allowed> | all | <index_min> [<index_max>] | label <index> <label text with white spaces allowed>]"),
+ tr("If no arguments are specified or <index> is specified, the wallet shows the default or specified address. If \"all\" is specified, the walllet shows all the existing addresses in the currently selected account. If \"new \" is specified, the wallet creates a new address with the provided label text (which can be empty). If \"label\" is specified, the wallet sets the label of the address specified by <index> to the provided label text."));
+ m_cmd_binder.set_handler("integrated_address",
+ boost::bind(&simple_wallet::print_integrated_address, this, _1),
+ tr("integrated_address [<payment_id> | <address>]"),
+ tr("Encode a payment ID into an integrated address for the current wallet public address (no argument uses a random payment ID), or decode an integrated address to standard address and payment ID"));
+ m_cmd_binder.set_handler("address_book",
+ boost::bind(&simple_wallet::address_book, this, _1),
+ tr("address_book [(add ((<address> [pid <id>])|<integrated address>) [<description possibly with whitespaces>])|(delete <index>)]"),
+ tr("Print all entries in the address book, optionally adding/deleting an entry to/from it."));
+ m_cmd_binder.set_handler("save",
+ boost::bind(&simple_wallet::save, this, _1),
+ tr("Save the wallet data."));
+ m_cmd_binder.set_handler("save_watch_only",
+ boost::bind(&simple_wallet::save_watch_only, this, _1),
+ tr("Save a watch-only keys file."));
+ m_cmd_binder.set_handler("viewkey",
+ boost::bind(&simple_wallet::viewkey, this, _1),
+ tr("Display the private view key."));
+ m_cmd_binder.set_handler("spendkey",
+ boost::bind(&simple_wallet::spendkey, this, _1),
+ tr("Display the private spend key."));
+ m_cmd_binder.set_handler("seed",
+ boost::bind(&simple_wallet::seed, this, _1),
+ tr("Display the Electrum-style mnemonic seed"));
+ m_cmd_binder.set_handler("set",
+ boost::bind(&simple_wallet::set_variable, this, _1),
+ tr("set <option> [<value>]"),
+ tr("Available options:\n "
+ "seed language\n "
+ " Set the wallet's seed language.\n "
+ "always-confirm-transfers <1|0>\n "
+ " Whether to confirm unsplit txes.\n "
+ "print-ring-members <1|0>\n "
+ " Whether to print detailed information about ring members during confirmation.\n "
+ "store-tx-info <1|0>\n "
+ " Whether to store outgoing tx info (destination address, payment ID, tx secret key) for future reference.\n "
+ "default-ring-size <n>\n "
+ " Set the default ring size (default and minimum is 5).\n "
+ "auto-refresh <1|0>\n "
+ " Whether to automatically synchronize new blocks from the daemon.\n "
+ "refresh-type <full|optimize-coinbase|no-coinbase|default>\n "
+ " Set the wallet's refresh behaviour.\n "
+ "priority [0|1|2|3|4]\n "
+ " Set the fee too default/unimportant/normal/elevated/priority.\n "
+ "confirm-missing-payment-id <1|0>\n "
+ "ask-password <1|0>\n "
+ "unit <monero|millinero|micronero|nanonero|piconero>\n "
+ " Set the default monero (sub-)unit.\n "
+ "min-outputs-count [n]\n "
+ " Try to keep at least that many outputs of value at least min-outputs-value.\n "
+ "min-outputs-value [n]\n "
+ " Try to keep at least min-outputs-count outputs of at least that value.\n "
+ "merge-destinations <1|0>\n "
+ " Whether to merge multiple payments to the same destination address.\n "
+ "confirm-backlog <1|0>\n "
+ " Whether to warn if there is transaction backlog.\n "
+ "confirm-backlog-threshold [n]\n "
+ " Set a threshold for confirm-backlog to only warn if the transaction backlog is greater than n blocks.\n "
+ "refresh-from-block-height [n]\n "
+ " Set the height before which to ignore blocks."));
+ m_cmd_binder.set_handler("encrypted_seed",
+ boost::bind(&simple_wallet::encrypted_seed, this, _1),
+ tr("Display the encrypted Electrum-style mnemonic seed."));
+ m_cmd_binder.set_handler("rescan_spent",
+ boost::bind(&simple_wallet::rescan_spent, this, _1),
+ tr("Rescan the blockchain for spent outputs."));
+ m_cmd_binder.set_handler("get_tx_key",
+ boost::bind(&simple_wallet::get_tx_key, this, _1),
+ tr("get_tx_key <txid>"),
+ tr("Get the transaction key (r) for a given <txid>."));
+ m_cmd_binder.set_handler("check_tx_key",
+ boost::bind(&simple_wallet::check_tx_key, this, _1),
+ tr("check_tx_key <txid> <txkey> <address>"),
+ tr("Check the amount going to <address> in <txid>."));
+ m_cmd_binder.set_handler("get_tx_proof_out",
+ boost::bind(&simple_wallet::get_tx_proof, this, _1),
+ tr("get_tx_proof_out <txid> <address> [<message>]"),
+ tr("Generate a signature proving funds sent to <address> in <txid>, optionally with a challenge string <message>, using either the transaction secret key (when <address> is not your wallet's address) or the view secret key (otherwise), which does not disclose the secret key."));
+ m_cmd_binder.set_handler("check_tx_proof",
+ boost::bind(&simple_wallet::check_tx_proof, this, _1),
+ tr("check_tx_proof <txid> <address> <signature_file> [<message>]"),
+ tr("Check the proof for funds going to <address> in <txid> with the challenge string <message> if any."));
+ m_cmd_binder.set_handler("get_spend_proof",
+ boost::bind(&simple_wallet::get_spend_proof, this, _1),
+ tr("get_spend_proof <txid> [<message>]"),
+ tr("Generate a signature proving that you generated <txid> using the spend secret key, optionally with a challenge string <message>."));
+ m_cmd_binder.set_handler("check_spend_proof",
+ boost::bind(&simple_wallet::check_spend_proof, this, _1),
+ tr("check_spend_proof <txid> <signature_file> [<message>]"),
+ tr("Check a signature proving that the signer generated <txid>, optionally with a challenge string <message>."));
+ m_cmd_binder.set_handler("show_transfers",
+ boost::bind(&simple_wallet::show_transfers, this, _1),
+ tr("show_transfers [in|out|pending|failed|pool] [index=<N1>[,<N2>,...]] [<min_height> [<max_height>]]"),
+ tr("Show the incoming/outgoing transfers within an optional height range."));
+ m_cmd_binder.set_handler("unspent_outputs",
+ boost::bind(&simple_wallet::unspent_outputs, this, _1),
+ tr("unspent_outputs [index=<N1>[,<N2>,...]] [<min_amount> [<max_amount>]]"),
+ tr("Show the unspent outputs of a specified address within an optional amount range."));
+ m_cmd_binder.set_handler("rescan_bc",
+ boost::bind(&simple_wallet::rescan_blockchain, this, _1),
+ tr("Rescan the blockchain from scratch."));
+ m_cmd_binder.set_handler("set_tx_note",
+ boost::bind(&simple_wallet::set_tx_note, this, _1),
+ tr("set_tx_note <txid> [free text note]"),
+ tr("Set an arbitrary string note for a <txid>."));
+ m_cmd_binder.set_handler("get_tx_note",
+ boost::bind(&simple_wallet::get_tx_note, this, _1),
+ tr("get_tx_note <txid>"),
+ tr("Get a string note for a txid."));
+ m_cmd_binder.set_handler("set_description",
+ boost::bind(&simple_wallet::set_description, this, _1),
+ tr("set_description [free text note]"),
+ tr("Set an arbitrary description for the wallet."));
+ m_cmd_binder.set_handler("get_description",
+ boost::bind(&simple_wallet::get_description, this, _1),
+ tr("Get the description of the wallet."));
+ m_cmd_binder.set_handler("status",
+ boost::bind(&simple_wallet::status, this, _1),
+ tr("Show the wallet's status."));
+ m_cmd_binder.set_handler("wallet_info",
+ boost::bind(&simple_wallet::wallet_info, this, _1),
+ tr("Show the wallet's information."));
+ m_cmd_binder.set_handler("sign",
+ boost::bind(&simple_wallet::sign, this, _1),
+ tr("sign <file>"),
+ tr("Sign the contents of a file."));
+ m_cmd_binder.set_handler("verify",
+ boost::bind(&simple_wallet::verify, this, _1),
+ tr("verify <filename> <address> <signature>"),
+ tr("Verify a signature on the contents of a file."));
+ m_cmd_binder.set_handler("export_key_images",
+ boost::bind(&simple_wallet::export_key_images, this, _1),
+ tr("export_key_images <file>"),
+ tr("Export a signed set of key images to a <file>."));
+ m_cmd_binder.set_handler("import_key_images",
+ boost::bind(&simple_wallet::import_key_images, this, _1),
+ tr("import_key_images <file>"),
+ tr("Import a signed key images list and verify their spent status."));
+ m_cmd_binder.set_handler("export_outputs",
+ boost::bind(&simple_wallet::export_outputs, this, _1),
+ tr("export_outputs <file>"),
+ tr("Export a set of outputs owned by this wallet."));
+ m_cmd_binder.set_handler("import_outputs",
+ boost::bind(&simple_wallet::import_outputs, this, _1),
+ tr("import_outputs <file>"),
+ tr("Import a set of outputs owned by this wallet."));
+ m_cmd_binder.set_handler("show_transfer",
+ boost::bind(&simple_wallet::show_transfer, this, _1),
+ tr("show_transfer <txid>"),
+ tr("Show information about a transfer to/from this address."));
+ m_cmd_binder.set_handler("password",
+ boost::bind(&simple_wallet::change_password, this, _1),
+ tr("Change the wallet's password."));
+ m_cmd_binder.set_handler("payment_id",
+ boost::bind(&simple_wallet::payment_id, this, _1),
+ tr("Generate a new random full size payment id. These will be unencrypted on the blockchain, see integrated_address for encrypted short payment ids."));
+ m_cmd_binder.set_handler("fee",
+ boost::bind(&simple_wallet::print_fee_info, this, _1),
+ tr("Print the information about the current fee and transaction backlog."));
+ m_cmd_binder.set_handler("help",
+ boost::bind(&simple_wallet::help, this, _1),
+ tr("help [<command>]"),
+ tr("Show the help section or the documentation about a <command>."));
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::set_variable(const std::vector<std::string> &args)
@@ -1592,7 +1806,17 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
}
}
if (m_restoring)
+ {
+ uint64_t estimate_height = m_wallet->estimate_blockchain_height();
+ if (m_restore_height >= estimate_height)
+ {
+ success_msg_writer() << tr("Restore height ") << m_restore_height << (" is not yet reached. The current estimated height is ") << estimate_height;
+ std::string confirm = input_line(tr("Still apply restore height? (Y/Yes/N/No): "));
+ if (std::cin.eof() || command_line::is_no(confirm))
+ m_restore_height = 0;
+ }
m_wallet->set_refresh_from_block_height(m_restore_height);
+ }
}
else
{
@@ -1671,7 +1895,7 @@ bool simple_wallet::try_connect_to_daemon(bool silent, uint32_t* version)
if (!silent)
fail_msg_writer() << tr("wallet failed to connect to daemon: ") << m_wallet->get_daemon_address() << ". " <<
tr("Daemon either is not started or wrong port was passed. "
- "Please make sure daemon is running or restart the wallet with the correct daemon address.");
+ "Please make sure daemon is running or change the daemon address using the 'set_daemon' command.");
return false;
}
if (!m_allow_mismatched_daemon_version && ((*version >> 16) != CORE_RPC_VERSION_MAJOR))
@@ -1805,6 +2029,7 @@ bool simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
tr("Your wallet has been generated!\n"
"To start synchronizing with the daemon, use \"refresh\" command.\n"
"Use \"help\" command to see the list of available commands.\n"
+ "Use \"help <command>\" to see a command's documentation.\n"
"Always use \"exit\" command when closing monero-wallet-cli to save your\n"
"current session's state. Otherwise, you might need to synchronize \n"
"your wallet again (your wallet keys are NOT at risk in any case).\n")
@@ -1924,6 +2149,7 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm)
success_msg_writer() <<
"**********************************************************************\n" <<
tr("Use \"help\" command to see the list of available commands.\n") <<
+ tr("Use \"help <command>\" to see a command's documentation.\n") <<
"**********************************************************************";
return true;
}
@@ -2074,6 +2300,42 @@ bool simple_wallet::stop_mining(const std::vector<std::string>& args)
return true;
}
//----------------------------------------------------------------------------------------------------
+bool simple_wallet::set_daemon(const std::vector<std::string>& args)
+{
+ std::string daemon_url;
+
+ if (args.size() < 1)
+ {
+ fail_msg_writer() << tr("missing daemon URL argument");
+ return true;
+ }
+
+ boost::regex rgx("^(.*://)?([A-Za-z0-9\\-\\.]+)(:[0-9]+)?");
+ boost::cmatch match;
+ // If user input matches URL regex
+ if (boost::regex_match(args[0].c_str(), match, rgx))
+ {
+ if (match.length() < 4)
+ {
+ fail_msg_writer() << tr("Unexpected array length - Exited simple_wallet::set_daemon()");
+ return true;
+ }
+ // If no port has been provided, use the default from config
+ if (!match[3].length())
+ {
+ int daemon_port = m_wallet->testnet() ? config::testnet::RPC_DEFAULT_PORT : config::RPC_DEFAULT_PORT;
+ daemon_url = match[1] + match[2] + std::string(":") + std::to_string(daemon_port);
+ } else {
+ daemon_url = args[0];
+ }
+ LOCK_IDLE_SCOPE();
+ m_wallet->init(daemon_url);
+ } else {
+ fail_msg_writer() << tr("This does not seem to be a valid daemon URL.");
+ }
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
bool simple_wallet::save_bc(const std::vector<std::string>& args)
{
if (!try_connect_to_daemon())
@@ -4866,6 +5128,11 @@ bool simple_wallet::print_integrated_address(const std::vector<std::string> &arg
}
if (args.size() == 0)
{
+ if (m_current_subaddress_account != 0)
+ {
+ fail_msg_writer() << tr("Integrated addresses can only be created for account 0");
+ return true;
+ }
payment_id = crypto::rand<crypto::hash8>();
success_msg_writer() << tr("Random payment ID: ") << payment_id;
success_msg_writer() << tr("Matching integrated address: ") << m_wallet->get_account().get_public_integrated_address_str(payment_id, m_wallet->testnet());
@@ -4873,6 +5140,11 @@ bool simple_wallet::print_integrated_address(const std::vector<std::string> &arg
}
if(tools::wallet2::parse_short_payment_id(args.back(), payment_id))
{
+ if (m_current_subaddress_account != 0)
+ {
+ fail_msg_writer() << tr("Integrated addresses can only be created for account 0");
+ return true;
+ }
success_msg_writer() << m_wallet->get_account().get_public_integrated_address_str(payment_id, m_wallet->testnet());
return true;
}
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index d6dde3ea7..ad174a636 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -77,6 +77,7 @@ namespace cryptonote
//wallet *create_wallet();
bool process_command(const std::vector<std::string> &args);
std::string get_commands_str();
+ std::string get_command_usage(const std::vector<std::string> &args);
private:
bool handle_command_line(const boost::program_options::variables_map& vm);
@@ -127,7 +128,8 @@ namespace cryptonote
bool help(const std::vector<std::string> &args = std::vector<std::string>());
bool start_mining(const std::vector<std::string> &args);
bool stop_mining(const std::vector<std::string> &args);
- bool save_bc(const std::vector<std::string>& args);
+ bool set_daemon(const std::vector<std::string> &args);
+ bool save_bc(const std::vector<std::string> &args);
bool refresh(const std::vector<std::string> &args);
bool show_balance_unlocked(bool detailed = false);
bool show_balance(const std::vector<std::string> &args = std::vector<std::string>());
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 13bf54212..fba1ef534 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -434,7 +434,7 @@ static void emplace_or_replace(std::unordered_multimap<crypto::hash, tools::wall
auto range = container.equal_range(key);
for (auto i = range.first; i != range.second; ++i)
{
- if (i->second.m_pd.m_tx_hash == pd.m_pd.m_tx_hash)
+ if (i->second.m_pd.m_tx_hash == pd.m_pd.m_tx_hash && i->second.m_pd.m_subaddr_index == pd.m_pd.m_subaddr_index)
{
i->second = pd;
return;
@@ -456,7 +456,7 @@ void drop_from_short_history(std::list<crypto::hash> &short_chain_history, size_
}
}
-size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra_size)
+size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof)
{
size_t size = 0;
@@ -480,7 +480,10 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra
size += 1;
// rangeSigs
- size += (2*64*32+32+64*32) * n_outputs;
+ if (bulletproof)
+ size += ((2*6 + 4 + 5)*32 + 3) * n_outputs;
+ else
+ size += (2*64*32+32+64*32) * n_outputs;
// MGs
size += n_inputs * (64 * (mixin+1) + 32);
@@ -501,14 +504,22 @@ size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs, size_t extra
return size;
}
-size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size)
+size_t estimate_tx_size(bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof)
{
if (use_rct)
- return estimate_rct_tx_size(n_inputs, mixin, n_outputs + 1, extra_size);
+ return estimate_rct_tx_size(n_inputs, mixin, n_outputs + 1, extra_size, bulletproof);
else
return n_inputs * (mixin+1) * APPROXIMATE_INPUT_BYTES + extra_size;
}
+uint8_t get_bulletproof_fork(bool testnet)
+{
+ if (testnet)
+ return 7;
+ else
+ return 255; // TODO
+}
+
} //namespace
namespace tools
@@ -812,8 +823,10 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &
switch (rv.type)
{
case rct::RCTTypeSimple:
+ case rct::RCTTypeSimpleBulletproof:
return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask);
case rct::RCTTypeFull:
+ case rct::RCTTypeFullBulletproof:
return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask);
default:
LOG_ERROR("Unsupported rct type: " << rv.type);
@@ -886,7 +899,12 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
tools::threadpool::waiter waiter;
const cryptonote::account_keys& keys = m_account.get_keys();
crypto::key_derivation derivation;
- generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation);
+ if (!generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation))
+ {
+ MWARNING("Failed to generate key derivation from tx pubkey, skipping");
+ static_assert(sizeof(derivation) == sizeof(rct::key), "Mismatched sizes of key_derivation and rct::key");
+ memcpy(&derivation, rct::identity().bytes, sizeof(derivation));
+ }
// additional tx pubkeys and derivations for multi-destination transfers involving one or more subaddresses
std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(tx);
@@ -894,7 +912,11 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i)
{
additional_derivations.push_back({});
- generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back());
+ if (!generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back()))
+ {
+ MWARNING("Failed to generate key derivation from tx pubkey, skipping");
+ additional_derivations.pop_back();
+ }
}
if (miner_tx && m_refresh_type == RefreshNoCoinbase)
@@ -2467,26 +2489,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip
// try asking the daemon first
if(m_refresh_from_block_height == 0 && !recover){
- std::string err;
- uint64_t height = 0;
-
- // we get the max of approximated height and known height
- // approximated height is the least of daemon target height
- // (the max of what the other daemons are claiming is their
- // height) and the theoretical height based on the local
- // clock. This will be wrong only if both the local clock
- // is bad *and* a peer daemon claims a highest height than
- // the real chain.
- // known height is the height the local daemon is currently
- // synced to, it will be lower than the real chain height if
- // the daemon is currently syncing.
- height = get_approximate_blockchain_height();
- uint64_t target_height = get_daemon_blockchain_target_height(err);
- if (err.empty() && target_height < height)
- height = target_height;
- uint64_t local_height = get_daemon_blockchain_height(err);
- if (err.empty() && local_height > height)
- height = local_height;
+ uint64_t height = estimate_blockchain_height();
m_refresh_from_block_height = height >= blocks_per_month ? height - blocks_per_month : 0;
}
@@ -2505,6 +2508,38 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip
return retval;
}
+ uint64_t wallet2::estimate_blockchain_height()
+ {
+ // -1 month for fluctuations in block time and machine date/time setup.
+ // avg seconds per block
+ const int seconds_per_block = DIFFICULTY_TARGET_V2;
+ // ~num blocks per month
+ const uint64_t blocks_per_month = 60*60*24*30/seconds_per_block;
+
+ // try asking the daemon first
+ std::string err;
+ uint64_t height = 0;
+
+ // we get the max of approximated height and known height
+ // approximated height is the least of daemon target height
+ // (the max of what the other daemons are claiming is their
+ // height) and the theoretical height based on the local
+ // clock. This will be wrong only if both the local clock
+ // is bad *and* a peer daemon claims a highest height than
+ // the real chain.
+ // known height is the height the local daemon is currently
+ // synced to, it will be lower than the real chain height if
+ // the daemon is currently syncing.
+ height = get_approximate_blockchain_height();
+ uint64_t target_height = get_daemon_blockchain_target_height(err);
+ if (err.empty() && target_height < height)
+ height = target_height;
+ uint64_t local_height = get_daemon_blockchain_height(err);
+ if (err.empty() && local_height > height)
+ height = local_height;
+ return height;
+ }
+
/*!
* \brief Creates a watch only wallet from a public address and a view secret key.
* \param wallet_ Name of wallet file
@@ -3741,9 +3776,10 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, const std::string &signed_f
LOG_PRINT_L1(" " << (n+1) << ": " << sd.sources.size() << " inputs, ring size " << sd.sources[0].outputs.size());
signed_txes.ptx.push_back(pending_tx());
tools::wallet2::pending_tx &ptx = signed_txes.ptx.back();
+ bool bulletproof = sd.use_rct && !ptx.tx.rct_signatures.p.bulletproofs.empty();
crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys;
- bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, sd.change_dts.addr, sd.extra, ptx.tx, sd.unlock_time, tx_key, additional_tx_keys, sd.use_rct);
+ bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sd.sources, sd.splitted_dsts, sd.change_dts.addr, sd.extra, ptx.tx, sd.unlock_time, tx_key, additional_tx_keys, sd.use_rct, bulletproof);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_testnet);
// we don't test tx size, because we don't know the current limit, due to not having a blockchain,
// and it's a bit pointless to fail there anyway, since it'd be a (good) guess only. We sign anyway,
@@ -4044,7 +4080,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions(std::vector<crypto
pending_tx ptx;
// loop until fee is met without increasing tx size to next KB boundary.
- const size_t estimated_tx_size = estimate_tx_size(false, unused_transfers_indices.size(), fake_outs_count, dst_vector.size(), extra.size());
+ const size_t estimated_tx_size = estimate_tx_size(false, unused_transfers_indices.size(), fake_outs_count, dst_vector.size(), extra.size(), false);
uint64_t needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
do
{
@@ -4626,7 +4662,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
- uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx)
+ uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, bool bulletproof)
{
using namespace cryptonote;
// throw if attempting a transaction with no destinations
@@ -4742,7 +4778,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
crypto::secret_key tx_key;
std::vector<crypto::secret_key> additional_tx_keys;
LOG_PRINT_L2("constructing tx");
- bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true);
+ bool r = cryptonote::construct_tx_and_get_tx_key(m_account.get_keys(), m_subaddresses, sources, splitted_dsts, change_dts.addr, extra, tx, unlock_time, tx_key, additional_tx_keys, true, bulletproof);
LOG_PRINT_L2("constructed tx, r="<<r);
THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sources, dsts, unlock_time, m_testnet);
THROW_WALLET_EXCEPTION_IF(upper_transaction_size_limit <= get_object_blobsize(tx), error::tx_too_big, tx, upper_transaction_size_limit);
@@ -5410,6 +5446,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
uint64_t needed_fee, available_for_fee = 0;
uint64_t upper_transaction_size_limit = get_upper_transaction_size_limit();
const bool use_rct = use_fork_rules(4, 0);
+ const bool bulletproof = use_fork_rules(get_bulletproof_fork(m_testnet), 0);
const uint64_t fee_per_kb = get_per_kb_fee();
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
@@ -5545,7 +5582,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
{
// this is used to build a tx that's 1 or 2 inputs, and 2 outputs, which
// will get us a known fee.
- uint64_t estimated_fee = calculate_fee(fee_per_kb, estimate_rct_tx_size(2, fake_outs_count, 2, extra.size()), fee_multiplier);
+ uint64_t estimated_fee = calculate_fee(fee_per_kb, estimate_rct_tx_size(2, fake_outs_count, 2, extra.size(), bulletproof), fee_multiplier);
preferred_inputs = pick_preferred_rct_inputs(needed_money + estimated_fee, subaddr_account, subaddr_indices);
if (!preferred_inputs.empty())
{
@@ -5648,7 +5685,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
else
{
- while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size()) < TX_SIZE_TARGET(upper_transaction_size_limit))
+ while (!dsts.empty() && dsts[0].amount <= available_amount && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof) < TX_SIZE_TARGET(upper_transaction_size_limit))
{
// we can fully pay that destination
LOG_PRINT_L2("We can fully pay " << get_account_address_as_str(m_testnet, dsts[0].is_subaddress, dsts[0].addr) <<
@@ -5660,7 +5697,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
++original_output_index;
}
- if (available_amount > 0 && !dsts.empty() && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size()) < TX_SIZE_TARGET(upper_transaction_size_limit)) {
+ if (available_amount > 0 && !dsts.empty() && estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof) < TX_SIZE_TARGET(upper_transaction_size_limit)) {
// we can partially fill that destination
LOG_PRINT_L2("We can partially pay " << get_account_address_as_str(m_testnet, dsts[0].is_subaddress, dsts[0].addr) <<
" for " << print_money(available_amount) << "/" << print_money(dsts[0].amount));
@@ -5684,7 +5721,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
}
else
{
- const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size());
+ const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof);
try_tx = dsts.empty() || (estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit));
}
}
@@ -5693,14 +5730,14 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
cryptonote::transaction test_tx;
pending_tx test_ptx;
- const size_t estimated_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size());
+ const size_t estimated_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof);
needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " outputs and " <<
tx.selected_transfers.size() << " inputs");
if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
- test_tx, test_ptx);
+ test_tx, test_ptx, bulletproof);
else
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
@@ -5743,7 +5780,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
while (needed_fee > test_ptx.fee) {
if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
- test_tx, test_ptx);
+ test_tx, test_ptx, bulletproof);
else
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
@@ -5895,6 +5932,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
std::vector<std::vector<get_outs_entry>> outs;
const bool use_rct = fake_outs_count > 0 && use_fork_rules(4, 0);
+ const bool bulletproof = use_fork_rules(get_bulletproof_fork(m_testnet), 0);
const uint64_t fee_per_kb = get_per_kb_fee();
const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm());
@@ -5933,14 +5971,14 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
// here, check if we need to sent tx and start a new one
LOG_PRINT_L2("Considering whether to create a tx now, " << tx.selected_transfers.size() << " inputs, tx limit "
<< upper_transaction_size_limit);
- const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1, extra.size());
+ const size_t estimated_rct_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size() + 1, extra.size(), bulletproof);
bool try_tx = (unused_dust_indices.empty() && unused_transfers_indices.empty()) || ( estimated_rct_tx_size >= TX_SIZE_TARGET(upper_transaction_size_limit));
if (try_tx) {
cryptonote::transaction test_tx;
pending_tx test_ptx;
- const size_t estimated_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size());
+ const size_t estimated_tx_size = estimate_tx_size(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size(), extra.size(), bulletproof);
needed_fee = calculate_fee(fee_per_kb, estimated_tx_size, fee_multiplier);
tx.dsts.push_back(tx_destination_entry(1, address, is_subaddress));
@@ -5949,7 +5987,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
tx.selected_transfers.size() << " outputs");
if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
- test_tx, test_ptx);
+ test_tx, test_ptx, bulletproof);
else
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
@@ -5966,7 +6004,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
tx.dsts[0].amount = available_for_fee - needed_fee;
if (use_rct)
transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
- test_tx, test_ptx);
+ test_tx, test_ptx, bulletproof);
else
transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx);
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 9308aadeb..de1bed90c 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -536,7 +536,7 @@ namespace tools
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx);
void transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count,
std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs,
- uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx);
+ uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, bool bulletproof);
void commit_tx(pending_tx& ptx_vector);
void commit_tx(std::vector<pending_tx>& ptx_vector);
@@ -742,6 +742,7 @@ namespace tools
* \brief Calculates the approximate blockchain height from current date/time.
*/
uint64_t get_approximate_blockchain_height() const;
+ uint64_t estimate_blockchain_height();
std::vector<size_t> select_available_outputs_from_histogram(uint64_t count, bool atleast, bool unlocked, bool allow_rct, bool trusted_daemon);
std::vector<size_t> select_available_outputs(const std::function<bool(const transfer_details &td)> &f);
std::vector<size_t> select_available_unmixable_outputs(bool trusted_daemon);
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 762eee776..d0c89db46 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -87,7 +87,9 @@ add_subdirectory(unit_tests)
add_subdirectory(difficulty)
add_subdirectory(hash)
add_subdirectory(net_load_tests)
-add_subdirectory(libwallet_api_tests)
+if (BUILD_GUI_DEPS)
+ add_subdirectory(libwallet_api_tests)
+endif()
# add_subdirectory(daemon_tests)
diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp
index 50f65cc67..e5047baf2 100644
--- a/tests/core_tests/rct.cpp
+++ b/tests/core_tests/rct.cpp
@@ -132,7 +132,7 @@ bool gen_rct_tx_validation_base::generate_with(std::vector<test_event_entry>& ev
CHECK_AND_ASSERT_MES(r, false, "Failed to generate key derivation");
crypto::secret_key amount_key;
crypto::derivation_to_scalar(derivation, o, amount_key);
- if (rct_txes[n].rct_signatures.type == rct::RCTTypeSimple)
+ if (rct_txes[n].rct_signatures.type == rct::RCTTypeSimple || rct_txes[n].rct_signatures.type == rct::RCTTypeSimpleBulletproof)
rct::decodeRctSimple(rct_txes[n].rct_signatures, rct::sk2rct(amount_key), o, rct_tx_masks[o+n*4]);
else
rct::decodeRct(rct_txes[n].rct_signatures, rct::sk2rct(amount_key), o, rct_tx_masks[o+n*4]);
diff --git a/tests/performance_tests/check_tx_signature.h b/tests/performance_tests/check_tx_signature.h
index 02555fae8..afc2bdc45 100644
--- a/tests/performance_tests/check_tx_signature.h
+++ b/tests/performance_tests/check_tx_signature.h
@@ -80,7 +80,7 @@ public:
{
if (rct)
{
- if (m_tx.rct_signatures.type == rct::RCTTypeFull)
+ if (m_tx.rct_signatures.type == rct::RCTTypeFull || m_tx.rct_signatures.type == rct::RCTTypeFullBulletproof)
return rct::verRct(m_tx.rct_signatures);
else
return rct::verRctSimple(m_tx.rct_signatures);
diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt
index 3aa35aab7..e37d34063 100644
--- a/tests/unit_tests/CMakeLists.txt
+++ b/tests/unit_tests/CMakeLists.txt
@@ -34,6 +34,7 @@ set(unit_tests_sources
blockchain_db.cpp
block_queue.cpp
block_reward.cpp
+ bulletproofs.cpp
canonical_amounts.cpp
chacha8.cpp
checkpoints.cpp
diff --git a/tests/unit_tests/bulletproofs.cpp b/tests/unit_tests/bulletproofs.cpp
new file mode 100644
index 000000000..3d3dba5e6
--- /dev/null
+++ b/tests/unit_tests/bulletproofs.cpp
@@ -0,0 +1,71 @@
+// Copyright (c) 2017, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
+
+#include "gtest/gtest.h"
+
+#include "ringct/rctOps.h"
+#include "ringct/bulletproofs.h"
+
+TEST(bulletproofs, valid_zero)
+{
+ rct::Bulletproof proof = bulletproof_PROVE(0, rct::skGen());
+ ASSERT_TRUE(rct::bulletproof_VERIFY(proof));
+}
+
+TEST(bulletproofs, valid_max)
+{
+ rct::Bulletproof proof = bulletproof_PROVE(0xffffffffffffffff, rct::skGen());
+ ASSERT_TRUE(rct::bulletproof_VERIFY(proof));
+}
+
+TEST(bulletproofs, valid_random)
+{
+ for (int n = 0; n < 8; ++n)
+ {
+ rct::Bulletproof proof = bulletproof_PROVE(crypto::rand<uint64_t>(), rct::skGen());
+ ASSERT_TRUE(rct::bulletproof_VERIFY(proof));
+ }
+}
+
+TEST(bulletproofs, invalid_8)
+{
+ rct::key invalid_amount = rct::zero();
+ invalid_amount[8] = 1;
+ rct::Bulletproof proof = bulletproof_PROVE(invalid_amount, rct::skGen());
+ ASSERT_FALSE(rct::bulletproof_VERIFY(proof));
+}
+
+TEST(bulletproofs, invalid_31)
+{
+ rct::key invalid_amount = rct::zero();
+ invalid_amount[31] = 1;
+ rct::Bulletproof proof = bulletproof_PROVE(invalid_amount, rct::skGen());
+ ASSERT_FALSE(rct::bulletproof_VERIFY(proof));
+}
diff --git a/utils/build_scripts/android32.Dockerfile b/utils/build_scripts/android32.Dockerfile
index da81b6435..37d012202 100644
--- a/utils/build_scripts/android32.Dockerfile
+++ b/utils/build_scripts/android32.Dockerfile
@@ -1,6 +1,6 @@
FROM debian:jessie
-RUN apt-get update && apt-get install -y unzip automake build-essential curl file pkg-config git python
+RUN apt-get update && apt-get install -y unzip automake build-essential curl file pkg-config git python libtool
WORKDIR /opt/android
## INSTALL ANDROID SDK
@@ -72,10 +72,23 @@ RUN curl -s -O https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz
&& make build_crypto build_ssl \
&& cd .. && mv openssl-${OPENSSL_VERSION} openssl
+# ZMQ
+RUN git clone https://github.com/zeromq/zeromq4-1.git \
+ && git clone https://github.com/zeromq/cppzmq.git \
+ && cd zeromq4-1 \
+ && ./autogen.sh \
+ && CC=clang CXX=clang++ ./configure --host=arm-none-linux-gnueabi \
+ && make
+
+RUN ln -s /opt/android/openssl/libcrypto.a /opt/android/openssl/libssl.a /opt/android/toolchain-arm/arm-linux-androideabi/lib/armv7-a
+
RUN git clone https://github.com/monero-project/monero.git \
&& cd monero \
&& mkdir -p build/release \
&& CC=clang CXX=clang++ \
BOOST_ROOT=${WORKDIR}/boost_${BOOST_VERSION} BOOST_LIBRARYDIR=${WORKDIR}/boost_${BOOST_VERSION}/android32/lib/ \
OPENSSL_ROOT_DIR=${WORKDIR}/openssl/ \
+ CMAKE_INCLUDE_PATH=${WORKDIR}/cppzmq/ \
+ CMAKE_LIBRARY_PATH=${WORKDIR}/zeromq4-1/.libs \
+ CXXFLAGS="-I ${WORKDIR}/zeromq4-1/include/" \
make release-static-android
diff --git a/utils/build_scripts/android64.Dockerfile b/utils/build_scripts/android64.Dockerfile
index 5e6bc05af..70c3c2b41 100644
--- a/utils/build_scripts/android64.Dockerfile
+++ b/utils/build_scripts/android64.Dockerfile
@@ -1,6 +1,6 @@
FROM debian:jessie
-RUN apt-get update && apt-get install -y unzip automake build-essential curl file pkg-config git python
+RUN apt-get update && apt-get install -y unzip automake build-essential curl file pkg-config git python libtool
WORKDIR /opt/android
## INSTALL ANDROID SDK
@@ -72,6 +72,15 @@ RUN curl -s -O https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz
&& make build_crypto build_ssl \
&& cd .. && mv openssl-${OPENSSL_VERSION} openssl
+RUN git clone https://github.com/zeromq/zeromq4-1.git \
+ && git clone https://github.com/zeromq/cppzmq.git \
+ && cd zeromq4-1 \
+ && ./autogen.sh \
+ && CC=clang CXX=clang++ ./configure --host=aarch64-linux-android \
+ && make
+
+RUN ln -s /opt/android/openssl/libcrypto.a /opt/android/openssl/libssl.a /opt/android/toolchain-arm/aarch64-linux-android/lib
+
RUN git clone https://github.com/monero-project/monero.git \
&& cd monero \
&& mkdir -p build/release \
@@ -79,5 +88,8 @@ RUN git clone https://github.com/monero-project/monero.git \
&& CC=clang CXX=clang++ \
BOOST_ROOT=${WORKDIR}/boost_${BOOST_VERSION} BOOST_LIBRARYDIR=${WORKDIR}/boost_${BOOST_VERSION}/android64/lib/ \
OPENSSL_ROOT_DIR=${WORKDIR}/openssl/ \
+ CMAKE_INCLUDE_PATH=${WORKDIR}/cppzmq/ \
+ CMAKE_LIBRARY_PATH=${WORKDIR}/zeromq4-1/.libs \
+ CXXFLAGS="-I ${WORKDIR}/zeromq4-1/include/" \
cmake -D BUILD_TESTS=OFF -D ARCH="armv8-a" -D STATIC=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D INSTALL_VENDORED_LIBUNBOUND=ON -D BUILD_TAG="android" ../.. \
&& make -j3
diff --git a/utils/systemd/monerod.service b/utils/systemd/monerod.service
index 96e88a2d3..b6b6b6ce6 100644
--- a/utils/systemd/monerod.service
+++ b/utils/systemd/monerod.service
@@ -14,5 +14,8 @@ PIDFile=/run/monero/monerod.pid
ExecStart=/usr/bin/monerod --config-file /etc/monerod.conf \
--detach --pidfile /run/monero/monerod.pid
+Restart=always
+PrivateTmp=true
+
[Install]
WantedBy=multi-user.target