aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt6
-rw-r--r--Doxyfile2
-rw-r--r--LICENSE2
-rw-r--r--Makefile2
-rw-r--r--README.md53
-rw-r--r--contrib/depends/packages/unbound.mk4
-rw-r--r--contrib/epee/include/gzip_encoding.h227
-rw-r--r--contrib/epee/include/hmac-md5.h93
-rw-r--r--contrib/epee/include/md5_l.h1
-rw-r--r--contrib/epee/include/md5_l.inl208
-rw-r--r--contrib/epee/include/net/http_client.h11
-rw-r--r--contrib/epee/include/net/net_ssl.h2
-rw-r--r--contrib/epee/include/storages/portable_storage_base.h2
-rw-r--r--contrib/epee/include/storages/portable_storage_from_bin.h4
-rw-r--r--contrib/epee/include/storages/portable_storage_to_bin.h4
-rw-r--r--contrib/gitian/DOCKRUN.md2
-rw-r--r--contrib/gitian/README.md2
-rw-r--r--docs/CONTRIBUTING.md4
-rw-r--r--docs/PORTABLE_STORAGE.md198
-rw-r--r--docs/images/storage_binary_example.pngbin0 -> 538682 bytes
-rw-r--r--src/blockchain_db/blockchain_db.cpp9
-rw-r--r--src/crypto/keccak.c95
-rw-r--r--src/cryptonote_basic/cryptonote_format_utils.cpp2
-rw-r--r--src/hardforks/hardforks.cpp2
-rw-r--r--src/multisig/multisig_account.cpp49
-rw-r--r--src/multisig/multisig_account.h26
-rw-r--r--src/multisig/multisig_account_kex_impl.cpp333
-rw-r--r--src/multisig/multisig_kex_msg.cpp2
-rw-r--r--src/multisig/multisig_kex_msg.h2
-rw-r--r--src/multisig/multisig_kex_msg_serialization.h2
-rw-r--r--src/net/zmq.cpp2
-rw-r--r--src/ringct/bulletproofs_plus.cc8
-rw-r--r--src/ringct/bulletproofs_plus.h2
-rw-r--r--src/ringct/rctSigs.cpp32
-rw-r--r--src/serialization/list.h58
-rw-r--r--src/serialization/serialization.h7
-rw-r--r--src/simplewallet/simplewallet.cpp10
-rw-r--r--src/wallet/ringdb.cpp39
-rw-r--r--src/wallet/ringdb.h2
-rw-r--r--src/wallet/wallet2.cpp137
-rw-r--r--src/wallet/wallet2.h12
-rw-r--r--src/wallet/wallet_rpc_server.cpp3
-rw-r--r--tests/core_tests/bulletproof_plus.cpp2
-rw-r--r--tests/core_tests/bulletproof_plus.h2
-rw-r--r--tests/core_tests/multisig.cpp10
-rw-r--r--tests/crypto/tests.txt14
-rwxr-xr-xtests/functional_tests/multisig.py85
-rw-r--r--tests/performance_tests/bulletproof_plus.h2
-rw-r--r--tests/performance_tests/derive_view_tag.h2
-rw-r--r--tests/performance_tests/out_can_be_to_acc.h2
-rw-r--r--tests/unit_tests/bulletproofs_plus.cpp2
-rw-r--r--tests/unit_tests/multisig.cpp15
-rw-r--r--tests/unit_tests/scaling_2021.cpp2
53 files changed, 838 insertions, 961 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a0f11608c..3abd0722a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -472,9 +472,11 @@ endif()
option(STATIC "Link libraries statically" ${DEFAULT_STATIC})
# This is a CMake built-in switch that concerns internal libraries
-if (NOT DEFINED BUILD_SHARED_LIBS AND NOT STATIC AND CMAKE_BUILD_TYPE_LOWER STREQUAL "debug")
- set(BUILD_SHARED_LIBS ON)
+set(BUILD_SHARED_LIBS_DEFAULT OFF)
+if (NOT STATIC AND CMAKE_BUILD_TYPE_LOWER STREQUAL "debug")
+ set(BUILD_SHARED_LIBS_DEFAULT ON)
endif()
+option(BUILD_SHARED_LIBS "Build internal libraries as shared" ${BUILD_SHARED_LIBS_DEFAULT})
if (BUILD_SHARED_LIBS)
message(STATUS "Building internal libraries with position independent code")
diff --git a/Doxyfile b/Doxyfile
index aa3b6fe65..81b200986 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -452,7 +452,7 @@ EXTRACT_LOCAL_METHODS = NO
# are hidden.
# The default value is: NO.
-EXTRACT_ANON_NSPACES = YES
+EXTRACT_ANON_NSPACES = NO
# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
# undocumented members inside documented classes or files. If set to NO these
diff --git a/LICENSE b/LICENSE
index 72da9414e..c55c48771 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2014-2021, The Monero Project
+Copyright (c) 2014-2022, The Monero Project
All rights reserved.
diff --git a/Makefile b/Makefile
index 8a804c39a..a07ac77a1 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2020, The Monero Project
+# Copyright (c) 2014-2022, The Monero Project
#
# All rights reserved.
#
diff --git a/README.md b/README.md
index ad31b127a..b3422280d 100644
--- a/README.md
+++ b/README.md
@@ -81,7 +81,7 @@ Monero is a private, secure, untraceable, decentralised digital currency. You ar
This is the core implementation of Monero. It is open source and completely free to use without restrictions, except for those specified in the license agreement below. There are no restrictions on anyone creating an alternative implementation of Monero that uses the protocol and network in a compatible manner.
-As with many development projects, the repository on Github is considered to be the "staging" area for the latest changes. Before changes are merged into that branch on the main repository, they are tested by individual developers in their own branches, submitted as a pull request, and then subsequently tested by contributors who focus on testing and code reviews. That having been said, the repository should be carefully considered before using it in a production environment, unless there is a patch in the repository for a particular show-stopping issue you are experiencing. It is generally a better idea to use a tagged release for stability.
+As with many development projects, the repository on GitHub is considered to be the "staging" area for the latest changes. Before changes are merged into that branch on the main repository, they are tested by individual developers in their own branches, submitted as a pull request, and then subsequently tested by contributors who focus on testing and code reviews. That having been said, the repository should be carefully considered before using it in a production environment, unless there is a patch in the repository for a particular show-stopping issue you are experiencing. It is generally a better idea to use a tagged release for stability.
**Anyone is welcome to contribute to Monero's codebase!** If you have a fix or code change, feel free to submit it as a pull request directly to the "master" branch. In cases where the change is relatively small or does not affect other parts of the codebase, it may be merged in immediately by any one of the collaborators. On the other hand, if the change is particularly large or complex, it is expected that it will be discussed at length either well in advance of the pull request being submitted, or even directly on the pull request.
@@ -89,9 +89,15 @@ As with many development projects, the repository on Github is considered to be
Monero is a 100% community-sponsored endeavor. If you want to join our efforts, the easiest thing you can do is support the project financially. Both Monero and Bitcoin donations can be made to **donate.getmonero.org** if using a client that supports the [OpenAlias](https://openalias.org) standard. Alternatively, you can send XMR to the Monero donation address via the `donate` command (type `help` in the command-line wallet for details).
-The Monero donation address is: `888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H` (viewkey: `f359631075708155cc3d92a32b75a7d02a5dcf27756707b47a2b31b21c389501`; base address for restoring with address and viewkey: `44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A`)
+The Monero donation address is:
+`888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H`
+Viewkey:
+`f359631075708155cc3d92a32b75a7d02a5dcf27756707b47a2b31b21c389501`
+Base address for restoring with address and viewkey:
+`44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A`
-The Bitcoin donation address is: `1KTexdemPdxSBcG55heUuTjDRYqbC5ZL8H`
+The Bitcoin donation address is:
+`1KTexdemPdxSBcG55heUuTjDRYqbC5ZL8H`
Core development funding and/or some supporting services are also graciously provided by [sponsors](https://www.getmonero.org/community/sponsorships/):
@@ -130,8 +136,8 @@ Dates are provided in the format YYYY-MM-DD.
| 1788000 | 2019-03-09 | v10 | v0.14.0.0 | v0.14.1.2 | New PoW based on Cryptonight-R, new block weight algorithm, slightly more efficient RingCT format
| 1788720 | 2019-03-10 | v11 | v0.14.0.0 | v0.14.1.2 | forbid old RingCT transaction format
| 1978433 | 2019-11-30 | v12 | v0.15.0.0 | v0.16.0.0 | New PoW based on RandomX, only allow >= 2 outputs, change to the block median used to calculate penalty, v1 coinbases are forbidden, rct sigs in coinbase forbidden, 10 block lock time for incoming outputs
-| 2210000 | 2020-10-17 | v13 | v0.17.0.0 | v0.17.1.1 | New CLSAG transaction format
-| 2210720 | 2020-10-18 | v14 | v0.17.1.1 | v0.17.1.7 | forbid old MLSAG transaction format
+| 2210000 | 2020-10-17 | v13 | v0.17.0.0 | v0.17.3.2 | New CLSAG transaction format
+| 2210720 | 2020-10-18 | v14 | v0.17.1.1 | v0.17.3.2 | forbid old MLSAG transaction format
| XXXXXXX | XXX-XX-XX | XXX | vX.XX.X.X | vX.XX.X.X | XXX |
X's indicate that these details have not been determined as of commit date.
@@ -181,7 +187,7 @@ library archives (`.a`).
| libusb | ? | NO | `libusb-1.0-0-dev` | `libusb` | `libusb-devel` | `libusbx-devel` | YES | Hardware wallet |
| libprotobuf | ? | NO | `libprotobuf-dev` | `protobuf` | `protobuf-devel` | `protobuf-devel` | YES | Hardware wallet |
| protoc | ? | NO | `protobuf-compiler` | `protobuf` | `protobuf` | `protobuf-compiler` | YES | Hardware wallet |
-| libudev | ? | No | `libudev-dev` | `systemd` | `eudev-libudev-devel` | `systemd-devel` | YES | Hardware wallet |
+| libudev | ? | NO | `libudev-dev` | `systemd` | `eudev-libudev-devel` | `systemd-devel` | YES | Hardware wallet |
[1] On Debian/Ubuntu `libgtest-dev` only includes sources and headers. You must
build the library binary manually. This can be done with the following command `sudo apt-get install libgtest-dev && cd /usr/src/gtest && sudo cmake . && sudo make`
@@ -197,7 +203,17 @@ then:
Install all dependencies at once on Debian/Ubuntu:
```
-sudo apt update && sudo apt install build-essential cmake pkg-config libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev libreadline6-dev libldns-dev libexpat1-dev libpgm-dev qttools5-dev-tools libhidapi-dev libusb-1.0-0-dev libprotobuf-dev protobuf-compiler libudev-dev libboost-chrono-dev libboost-date-time-dev libboost-filesystem-dev libboost-locale-dev libboost-program-options-dev libboost-regex-dev libboost-serialization-dev libboost-system-dev libboost-thread-dev ccache doxygen graphviz
+sudo apt update && sudo apt install build-essential cmake pkg-config libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev libreadline6-dev libldns-dev libexpat1-dev libpgm-dev qttools5-dev-tools libhidapi-dev libusb-1.0-0-dev libprotobuf-dev protobuf-compiler libudev-dev libboost-chrono-dev libboost-date-time-dev libboost-filesystem-dev libboost-locale-dev libboost-program-options-dev libboost-regex-dev libboost-serialization-dev libboost-system-dev libboost-thread-dev python3 ccache doxygen graphviz
+```
+
+Install all dependencies at once on Arch:
+```
+sudo pacman -Syu --needed base-devel cmake boost openssl zeromq libpgm unbound libsodium libunwind xz readline ldns expat gtest python3 ccache doxygen graphviz qt5-tools hidapi libusb protobuf systemd
+```
+
+Install all dependencies at once on Fedora:
+```
+sudo dnf install gcc gcc-c++ cmake pkgconf boost-devel openssl-devel zeromq-devel openpgm-devel unbound-devel libsodium-devel libunwind-devel xz-devel readline-devel ldns-devel expat-devel gtest-devel ccache doxygen graphviz qt5-linguist hidapi-devel libusbx-devel protobuf-devel protobuf-compiler systemd-devel
```
Install all dependencies at once on openSUSE:
@@ -259,7 +275,7 @@ invokes cmake commands as needed.
*Note*: The instructions above will compile the most stable release of the
Monero software. If you would like to use and test the most recent software,
- use ```git checkout master```. The master branch may contain updates that are
+ use `git checkout master`. The master branch may contain updates that are
both unstable and incompatible with release software, though testing is always
encouraged.
@@ -327,7 +343,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch (
```bash
git clone https://github.com/monero-project/monero.git
cd monero
- git checkout tags/v0.17.1.0
+ git checkout v0.17.3.2
```
* Build:
@@ -446,10 +462,10 @@ application.
cd monero
```
-* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.17.1.0'. If you don't care about the version and just want binaries from master, skip this step:
+* If you would like a specific [version/tag](https://github.com/monero-project/monero/tags), do a git checkout for that version. eg. 'v0.17.3.2'. If you don't care about the version and just want binaries from master, skip this step:
```bash
- git checkout v0.17.1.0
+ git checkout v0.17.3.2
```
* If you are on a 64-bit system, run:
@@ -586,8 +602,11 @@ Packages are available for
```
More info and versions in the [Debian package tracker](https://tracker.debian.org/pkg/monero).
-* Arch Linux (via Community packages):
- [`monero`](https://www.archlinux.org/packages/community/x86_64/monero/)
+* Arch Linux [(via Community packages)](https://www.archlinux.org/packages/community/x86_64/monero/):
+
+ ```bash
+ sudo pacman -S monero
+ ```
* Void Linux:
@@ -611,7 +630,7 @@ More info and versions in the [Debian package tracker](https://tracker.debian.or
emerge net-p2p/monero
```
-* macOS (homebrew)
+* macOS [(homebrew)](https://brew.sh/)
```bash
brew install monero
```
@@ -633,7 +652,7 @@ More info and versions in the [Debian package tracker](https://tracker.debian.or
```
* The build needs 3 GB space.
-* Wait one hour or more
+* Wait one hour or more
Packaging for your favorite distribution would be a welcome contribution!
@@ -726,7 +745,7 @@ DNS_PUBLIC=tcp torsocks ./monerod --p2p-bind-ip 127.0.0.1 --no-igd --rpc-bind-ip
## Pruning
-As of May 2020, the full Monero blockchain file is about 100 GB. One can store a pruned blockchain, which is about 30 GB.
+As of April 2022, the full Monero blockchain file is about 130 GB. One can store a pruned blockchain, which is about 45 GB.
A pruned blockchain can only serve part of the historical chain data to other peers, but is otherwise identical in
functionality to the full blockchain.
To use a pruned blockchain, it is best to start the initial sync with `--prune-blockchain`. However, it is also possible
@@ -738,7 +757,7 @@ For more detailed information see the ['Pruning' entry in the Moneropedia](https
## Debugging
-This section contains general instructions for debugging failed installs or problems encountered with Monero. First, ensure you are running the latest version built from the Github repo.
+This section contains general instructions for debugging failed installs or problems encountered with Monero. First, ensure you are running the latest version built from the GitHub repo.
### Obtaining stack traces and core dumps on Unix systems
diff --git a/contrib/depends/packages/unbound.mk b/contrib/depends/packages/unbound.mk
index 06504a7cb..a85c47e4e 100644
--- a/contrib/depends/packages/unbound.mk
+++ b/contrib/depends/packages/unbound.mk
@@ -1,8 +1,8 @@
package=unbound
-$(package)_version=1.13.2
+$(package)_version=1.15.0
$(package)_download_path=https://www.nlnetlabs.nl/downloads/$(package)/
$(package)_file_name=$(package)-$($(package)_version).tar.gz
-$(package)_sha256_hash=0a13b547f3b92a026b5ebd0423f54c991e5718037fd9f72445817f6a040e1a83
+$(package)_sha256_hash=a480dc6c8937447b98d161fe911ffc76cfaffa2da18788781314e81339f1126f
$(package)_dependencies=openssl expat ldns
define $(package)_set_vars
diff --git a/contrib/epee/include/gzip_encoding.h b/contrib/epee/include/gzip_encoding.h
deleted file mode 100644
index 2be51e77d..000000000
--- a/contrib/epee/include/gzip_encoding.h
+++ /dev/null
@@ -1,227 +0,0 @@
-// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * 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.
-// * Neither the name of the Andrey N. Sabelnikov 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 OWNER 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.
-//
-
-
-
-
-#ifndef _GZIP_ENCODING_H_
-#define _GZIP_ENCODING_H_
-#include "net/http_client_base.h"
-#include "zlib/zlib.h"
-//#include "http.h"
-
-
-namespace epee
-{
-namespace net_utils
-{
-
-
-
- class content_encoding_gzip: public i_sub_handler
- {
- public:
- /*! \brief
- * Function content_encoding_gzip : Constructor
- *
- */
- inline
- content_encoding_gzip(i_target_handler* powner_filter, bool is_deflate_mode = false):m_powner_filter(powner_filter),
- m_is_stream_ended(false),
- m_is_deflate_mode(is_deflate_mode),
- m_is_first_update_in(true)
- {
- memset(&m_zstream_in, 0, sizeof(m_zstream_in));
- memset(&m_zstream_out, 0, sizeof(m_zstream_out));
- int ret = 0;
- if(is_deflate_mode)
- {
- ret = inflateInit(&m_zstream_in);
- ret = deflateInit(&m_zstream_out, Z_DEFAULT_COMPRESSION);
- }else
- {
- ret = inflateInit2(&m_zstream_in, 0x1F);
- ret = deflateInit2(&m_zstream_out, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 0x1F, 8, Z_DEFAULT_STRATEGY);
- }
- }
- /*! \brief
- * Function content_encoding_gzip : Destructor
- *
- */
- inline
- ~content_encoding_gzip()
- {
- inflateEnd(& m_zstream_in );
- deflateEnd(& m_zstream_out );
- }
- /*! \brief
- * Function update_in : Entry point for income data
- *
- */
- inline
- virtual bool update_in( std::string& piece_of_transfer)
- {
-
- bool is_first_time_here = m_is_first_update_in;
- m_is_first_update_in = false;
-
- if(m_pre_decode.size())
- m_pre_decode += piece_of_transfer;
- else
- m_pre_decode.swap(piece_of_transfer);
- piece_of_transfer.clear();
-
- std::string decode_summary_buff;
-
- size_t ungzip_size = m_pre_decode.size() * 0x30;
- std::string current_decode_buff(ungzip_size, 'X');
-
- //Here the cycle is introduced where we unpack the buffer, the cycle is required
- //because of the case where if after unpacking the data will exceed the awaited size, we will not halt with error
- bool continue_unpacking = true;
- bool first_step = true;
- while(m_pre_decode.size() && continue_unpacking)
- {
-
- //fill buffers
- m_zstream_in.next_in = (Bytef*)m_pre_decode.data();
- m_zstream_in.avail_in = (uInt)m_pre_decode.size();
- m_zstream_in.next_out = (Bytef*)current_decode_buff.data();
- m_zstream_in.avail_out = (uInt)ungzip_size;
-
- int flag = Z_SYNC_FLUSH;
- int ret = inflate(&m_zstream_in, flag);
- CHECK_AND_ASSERT_MES(ret>=0 || m_zstream_in.avail_out ||m_is_deflate_mode, false, "content_encoding_gzip::update_in() Failed to inflate. err = " << ret);
-
- if(Z_STREAM_END == ret)
- m_is_stream_ended = true;
- else if(Z_DATA_ERROR == ret && is_first_time_here && m_is_deflate_mode&& first_step)
- {
- // some servers (notably Apache with mod_deflate) don't generate zlib headers
- // insert a dummy header and try again
- static char dummy_head[2] =
- {
- 0x8 + 0x7 * 0x10,
- (((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF,
- };
- inflateReset(&m_zstream_in);
- m_zstream_in.next_in = (Bytef*) dummy_head;
- m_zstream_in.avail_in = sizeof(dummy_head);
-
- ret = inflate(&m_zstream_in, Z_NO_FLUSH);
- if (ret != Z_OK)
- {
- LOCAL_ASSERT(0);
- m_pre_decode.swap(piece_of_transfer);
- return false;
- }
- m_zstream_in.next_in = (Bytef*)m_pre_decode.data();
- m_zstream_in.avail_in = (uInt)m_pre_decode.size();
-
- ret = inflate(&m_zstream_in, Z_NO_FLUSH);
- if (ret != Z_OK)
- {
- LOCAL_ASSERT(0);
- m_pre_decode.swap(piece_of_transfer);
- return false;
- }
- }
-
-
- //leave only unpacked part in the output buffer to start with it the next time
- m_pre_decode.erase(0, m_pre_decode.size()-m_zstream_in.avail_in);
- //if decoder gave nothing to return, then everything is ahead, now simply break
- if(ungzip_size == m_zstream_in.avail_out)
- break;
-
- //decode_buff currently stores data parts that were unpacked, fix this size
- current_decode_buff.resize(ungzip_size - m_zstream_in.avail_out);
- if(decode_summary_buff.size())
- decode_summary_buff += current_decode_buff;
- else
- current_decode_buff.swap(decode_summary_buff);
-
- current_decode_buff.resize(ungzip_size);
- first_step = false;
- }
-
- //Process these data if required
- bool res = true;
-
- res = m_powner_filter->handle_target_data(decode_summary_buff);
-
- return true;
-
- }
- /*! \brief
- * Function stop : Entry point for stop signal and flushing cached data buffer.
- *
- */
- inline
- virtual void stop(std::string& OUT collect_remains)
- {
- }
- protected:
- private:
- /*! \brief
- * Pointer to parent HTTP-parser
- */
- i_target_handler* m_powner_filter;
- /*! \brief
- * ZLIB object for income stream
- */
- z_stream m_zstream_in;
- /*! \brief
- * ZLIB object for outcome stream
- */
- z_stream m_zstream_out;
- /*! \brief
- * Data that could not be unpacked immediately, left to wait for the next packet of data
- */
- std::string m_pre_decode;
- /*! \brief
- * The data are accumulated for a package in the buffer to send the web client
- */
- std::string m_pre_encode;
- /*! \brief
- * Signals that stream looks like ended
- */
- bool m_is_stream_ended;
- /*! \brief
- * If this flag is set, income data is in HTTP-deflate mode
- */
- bool m_is_deflate_mode;
- /*! \brief
- * Marks that it is a first data packet
- */
- bool m_is_first_update_in;
- };
-}
-}
-
-
-
-#endif //_GZIP_ENCODING_H_
diff --git a/contrib/epee/include/hmac-md5.h b/contrib/epee/include/hmac-md5.h
deleted file mode 100644
index 2a4e0d401..000000000
--- a/contrib/epee/include/hmac-md5.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * libEtPan! -- a mail stuff library
- *
- * Copyright (C) 2001, 2005 - DINH Viet Hoa
- * 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 libEtPan! project 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 AUTHORS 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 AUTHORS 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.
- */
-
-/* hmac-md5.h -- HMAC_MD5 functions
- */
-
-/*
- * $Id: hmac-md5.h,v 1.1.1.1 2005/03/18 20:17:28 zautrix Exp $
- */
-
-#ifndef HMAC_MD5_H
-#define HMAC_MD5_H 1
-
-namespace md5
-{
-
-
-
-#define HMAC_MD5_SIZE 16
-
- /* intermediate MD5 context */
- typedef struct HMAC_MD5_CTX_s {
- MD5_CTX ictx, octx;
- } HMAC_MD5_CTX;
-
- /* intermediate HMAC state
- * values stored in network byte order (Big Endian)
- */
- typedef struct HMAC_MD5_STATE_s {
- UINT4 istate[4];
- UINT4 ostate[4];
- } HMAC_MD5_STATE;
-
- /* One step hmac computation
- *
- * digest may be same as text or key
- */
- void hmac_md5(const unsigned char *text, int text_len,
- const unsigned char *key, int key_len,
- unsigned char digest[HMAC_MD5_SIZE]);
-
- /* create context from key
- */
- void hmac_md5_init(HMAC_MD5_CTX *hmac,
- const unsigned char *key, int key_len);
-
- /* precalculate intermediate state from key
- */
- void hmac_md5_precalc(HMAC_MD5_STATE *hmac,
- const unsigned char *key, int key_len);
-
- /* initialize context from intermediate state
- */
- void hmac_md5_import(HMAC_MD5_CTX *hmac, HMAC_MD5_STATE *state);
-
-#define hmac_md5_update(hmac, text, text_len) MD5Update(&(hmac)->ictx, (text), (text_len))
-
- /* finish hmac from intermediate result. Intermediate result is zeroed.
- */
- void hmac_md5_final(unsigned char digest[HMAC_MD5_SIZE],
- HMAC_MD5_CTX *hmac);
-
-}
-
-#endif /* HMAC_MD5_H */
diff --git a/contrib/epee/include/md5_l.h b/contrib/epee/include/md5_l.h
index bc7122650..ffb8c5944 100644
--- a/contrib/epee/include/md5_l.h
+++ b/contrib/epee/include/md5_l.h
@@ -74,7 +74,6 @@ namespace md5
static void MD5Init(MD5_CTX * context);
static void MD5Update( MD5_CTX *context, const unsigned char *input, unsigned int inputLen );
static void MD5Final ( unsigned char digest[16], MD5_CTX *context );
- static inline void hmac_md5(const unsigned char* text, int text_len, const unsigned char* key, int key_len, unsigned char *digest);
inline bool md5( unsigned char *input, int ilen, unsigned char output[16] )
diff --git a/contrib/epee/include/md5_l.inl b/contrib/epee/include/md5_l.inl
index cb2bd54f9..28d52c23c 100644
--- a/contrib/epee/include/md5_l.inl
+++ b/contrib/epee/include/md5_l.inl
@@ -65,7 +65,6 @@ documentation and/or software.
#endif
#include "md5global.h"
#include "md5_l.h"
-#include "hmac-md5.h"
namespace md5
{
@@ -89,16 +88,6 @@ namespace md5
#define S43 15
#define S44 21
- /*
- static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64]));
- static void Encode PROTO_LIST
- ((unsigned char *, UINT4 *, unsigned int));
- static void Decode PROTO_LIST
- ((UINT4 *, unsigned char *, unsigned int));
- static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int));
- static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int));
- */
-
static void MD5_memcpy (POINTER output, POINTER input, unsigned int len)
{
unsigned int i;
@@ -107,17 +96,6 @@ namespace md5
output[i] = input[i];
}
- /* Note: Replace "for loop" with standard memset if possible.
- */
-
- static void MD5_memset (POINTER output, int value, unsigned int len)
- {
- unsigned int i;
-
- for (i = 0; i < len; i++)
- ((char *)output)[i] = (char)value;
- }
-
static void MD5Transform (UINT4 state[4], unsigned char block[64]);
static unsigned char* PADDING()
@@ -371,190 +349,4 @@ namespace md5
*/
memwipe ((POINTER)x, sizeof (x));
}
-
- /* Note: Replace "for loop" with standard memcpy if possible.
-
- */
- inline
- void hmac_md5_init(HMAC_MD5_CTX *hmac,
- const unsigned char *key,
- int key_len)
- {
- unsigned char k_ipad[65]; /* inner padding -
- * key XORd with ipad
- */
- unsigned char k_opad[65]; /* outer padding -
- * key XORd with opad
- */
- unsigned char tk[16];
- int i;
- /* if key is longer than 64 bytes reset it to key=MD5(key) */
- if (key_len > 64) {
-
- MD5_CTX tctx;
-
- MD5Init(&tctx);
- MD5Update(&tctx, key, key_len);
- MD5Final(tk, &tctx);
-
- key = tk;
- key_len = 16;
- }
-
- /*
- * the HMAC_MD5 transform looks like:
- *
- * MD5(K XOR opad, MD5(K XOR ipad, text))
- *
- * where K is an n byte key
- * ipad is the byte 0x36 repeated 64 times
- * opad is the byte 0x5c repeated 64 times
- * and text is the data being protected
- */
-
- /* start out by storing key in pads */
- MD5_memset(k_ipad, '\0', sizeof k_ipad);
- MD5_memset(k_opad, '\0', sizeof k_opad);
- MD5_memcpy( k_ipad, (POINTER)key, key_len);
- MD5_memcpy( k_opad, (POINTER)key, key_len);
-
- /* XOR key with ipad and opad values */
- for (i=0; i<64; i++) {
- k_ipad[i] ^= 0x36;
- k_opad[i] ^= 0x5c;
- }
-
- MD5Init(&hmac->ictx); /* init inner context */
- MD5Update(&hmac->ictx, k_ipad, 64); /* apply inner pad */
-
- MD5Init(&hmac->octx); /* init outer context */
- MD5Update(&hmac->octx, k_opad, 64); /* apply outer pad */
-
- /* scrub the pads and key context (if used) */
- memwipe( (POINTER)&k_ipad, sizeof(k_ipad));
- memwipe( (POINTER)&k_opad, sizeof(k_opad));
- memwipe( (POINTER)&tk, sizeof(tk));
-
- /* and we're done. */
- }
-
- /* The precalc and import routines here rely on the fact that we pad
- * the key out to 64 bytes and use that to initialize the md5
- * contexts, and that updating an md5 context with 64 bytes of data
- * leaves nothing left over; all of the interesting state is contained
- * in the state field, and none of it is left over in the count and
- * buffer fields. So all we have to do is save the state field; we
- * can zero the others when we reload it. Which is why the decision
- * was made to pad the key out to 64 bytes in the first place. */
- inline
- void hmac_md5_precalc(HMAC_MD5_STATE *state,
- const unsigned char *key,
- int key_len)
- {
- HMAC_MD5_CTX hmac;
- unsigned lupe;
-
- hmac_md5_init(&hmac, key, key_len);
- for (lupe = 0; lupe < 4; lupe++) {
- state->istate[lupe] = htonl(hmac.ictx.state[lupe]);
- state->ostate[lupe] = htonl(hmac.octx.state[lupe]);
- }
- memwipe( (POINTER)&hmac, sizeof(hmac));
- }
-
-
- inline
- void hmac_md5_import(HMAC_MD5_CTX *hmac,
- HMAC_MD5_STATE *state)
- {
- unsigned lupe;
- MD5_memset( (POINTER)hmac, 0, sizeof(HMAC_MD5_CTX));
- for (lupe = 0; lupe < 4; lupe++) {
- hmac->ictx.state[lupe] = ntohl(state->istate[lupe]);
- hmac->octx.state[lupe] = ntohl(state->ostate[lupe]);
- }
- /* Init the counts to account for our having applied
- * 64 bytes of key; this works out to 0x200 (64 << 3; see
- * MD5Update above...) */
- hmac->ictx.count[0] = hmac->octx.count[0] = 0x200;
- }
-
- inline
- void hmac_md5_final(unsigned char digest[HMAC_MD5_SIZE],
- HMAC_MD5_CTX *hmac)
- {
- MD5Final(digest, &hmac->ictx); /* Finalize inner md5 */
- MD5Update(&hmac->octx, digest, 16); /* Update outer ctx */
- MD5Final(digest, &hmac->octx); /* Finalize outer md5 */
- }
-
-
- void hmac_md5(const unsigned char* text, int text_len, const unsigned char* key, int key_len, unsigned char *digest)
- {
- MD5_CTX context;
-
- unsigned char k_ipad[65]; /* inner padding -
- * key XORd with ipad
- */
- unsigned char k_opad[65]; /* outer padding -
- * key XORd with opad
- */
- unsigned char tk[16];
- int i;
- /* if key is longer than 64 bytes reset it to key=MD5(key) */
- if (key_len > 64) {
-
- MD5_CTX tctx;
-
- MD5Init(&tctx);
- MD5Update(&tctx, key, key_len);
- MD5Final(tk, &tctx);
-
- key = tk;
- key_len = 16;
- }
-
- /*
- * the HMAC_MD5 transform looks like:
- *
- * MD5(K XOR opad, MD5(K XOR ipad, text))
- *
- * where K is an n byte key
- * ipad is the byte 0x36 repeated 64 times
- * opad is the byte 0x5c repeated 64 times
- * and text is the data being protected
- */
-
- /* start out by storing key in pads */
- MD5_memset(k_ipad, '\0', sizeof k_ipad);
- MD5_memset(k_opad, '\0', sizeof k_opad);
- MD5_memcpy( k_ipad, (POINTER)key, key_len);
- MD5_memcpy( k_opad, (POINTER)key, key_len);
-
- /* XOR key with ipad and opad values */
- for (i=0; i<64; i++) {
- k_ipad[i] ^= 0x36;
- k_opad[i] ^= 0x5c;
- }
- /*
- * perform inner MD5
- */
-
- MD5Init(&context); /* init context for 1st
- * pass */
- MD5Update(&context, k_ipad, 64); /* start with inner pad */
- MD5Update(&context, text, text_len); /* then text of datagram */
- MD5Final(digest, &context); /* finish up 1st pass */
-
- /*
- * perform outer MD5
- */
- MD5Init(&context); /* init context for 2nd
- * pass */
- MD5Update(&context, k_opad, 64); /* start with outer pad */
- MD5Update(&context, digest, 16); /* then results of 1st
- * hash */
- MD5Final(digest, &context); /* finish up 2nd pass */
-
- }
}
diff --git a/contrib/epee/include/net/http_client.h b/contrib/epee/include/net/http_client.h
index 056b50fe6..9ce30b620 100644
--- a/contrib/epee/include/net/http_client.h
+++ b/contrib/epee/include/net/http_client.h
@@ -39,11 +39,6 @@
#include "net_helper.h"
#include "http_client_base.h"
-
-#ifdef HTTP_ENABLE_GZIP
-#include "gzip_encoding.h"
-#endif
-
#include "string_tools.h"
#include "string_tools_lexical.h"
#include "reg_exp_definer.h"
@@ -757,13 +752,9 @@ namespace net_utils
boost::smatch result; // 12 3
if(boost::regex_search( m_response_info.m_header_info.m_content_encoding, result, rexp_match_gzip, boost::match_default) && result[0].matched)
{
-#ifdef HTTP_ENABLE_GZIP
- m_pcontent_encoding_handler.reset(new content_encoding_gzip(this, result[3].matched));
-#else
m_pcontent_encoding_handler.reset(new do_nothing_sub_handler(this));
- LOG_ERROR("GZIP encoding not supported in this build, please add zlib to your project and define HTTP_ENABLE_GZIP");
+ LOG_ERROR("GZIP encoding not supported");
return false;
-#endif
}
else
{
diff --git a/contrib/epee/include/net/net_ssl.h b/contrib/epee/include/net/net_ssl.h
index 58cd7e45f..108e6771b 100644
--- a/contrib/epee/include/net/net_ssl.h
+++ b/contrib/epee/include/net/net_ssl.h
@@ -104,7 +104,7 @@ namespace net_utils
//! \return False iff ssl is disabled, otherwise true.
explicit operator bool() const noexcept { return support != ssl_support_t::e_ssl_support_disabled; }
- //! \retrurn True if `host` can be verified using `this` configuration WITHOUT system "root" CAs.
+ //! \return True if `host` can be verified using `this` configuration WITHOUT system "root" CAs.
bool has_strong_verification(boost::string_ref host) const noexcept;
//! Search against internal fingerprints. Always false if `behavior() != user_certificate_check`.
diff --git a/contrib/epee/include/storages/portable_storage_base.h b/contrib/epee/include/storages/portable_storage_base.h
index ae0be6a34..c15c9b826 100644
--- a/contrib/epee/include/storages/portable_storage_base.h
+++ b/contrib/epee/include/storages/portable_storage_base.h
@@ -57,7 +57,7 @@
#define SERIALIZE_TYPE_UINT32 6
#define SERIALIZE_TYPE_UINT16 7
#define SERIALIZE_TYPE_UINT8 8
-#define SERIALIZE_TYPE_DUOBLE 9
+#define SERIALIZE_TYPE_DOUBLE 9
#define SERIALIZE_TYPE_STRING 10
#define SERIALIZE_TYPE_BOOL 11
#define SERIALIZE_TYPE_OBJECT 12
diff --git a/contrib/epee/include/storages/portable_storage_from_bin.h b/contrib/epee/include/storages/portable_storage_from_bin.h
index 6f081dbc7..d8a8a4a49 100644
--- a/contrib/epee/include/storages/portable_storage_from_bin.h
+++ b/contrib/epee/include/storages/portable_storage_from_bin.h
@@ -220,7 +220,7 @@ namespace epee
case SERIALIZE_TYPE_UINT32: return read_ae<uint32_t>();
case SERIALIZE_TYPE_UINT16: return read_ae<uint16_t>();
case SERIALIZE_TYPE_UINT8: return read_ae<uint8_t>();
- case SERIALIZE_TYPE_DUOBLE: return read_ae<double>();
+ case SERIALIZE_TYPE_DOUBLE: return read_ae<double>();
case SERIALIZE_TYPE_BOOL: return read_ae<bool>();
case SERIALIZE_TYPE_STRING: return read_ae<std::string>();
case SERIALIZE_TYPE_OBJECT: return read_ae<section>();
@@ -311,7 +311,7 @@ namespace epee
case SERIALIZE_TYPE_UINT32: return read_se<uint32_t>();
case SERIALIZE_TYPE_UINT16: return read_se<uint16_t>();
case SERIALIZE_TYPE_UINT8: return read_se<uint8_t>();
- case SERIALIZE_TYPE_DUOBLE: return read_se<double>();
+ case SERIALIZE_TYPE_DOUBLE: return read_se<double>();
case SERIALIZE_TYPE_BOOL: return read_se<bool>();
case SERIALIZE_TYPE_STRING: return read_se<std::string>();
case SERIALIZE_TYPE_OBJECT: return read_se<section>();
diff --git a/contrib/epee/include/storages/portable_storage_to_bin.h b/contrib/epee/include/storages/portable_storage_to_bin.h
index be4033dd8..70757607e 100644
--- a/contrib/epee/include/storages/portable_storage_to_bin.h
+++ b/contrib/epee/include/storages/portable_storage_to_bin.h
@@ -107,7 +107,7 @@ namespace epee
bool operator()(const array_entry_t<int32_t>& v) { return pack_pod_array_type(SERIALIZE_TYPE_INT32, v);}
bool operator()(const array_entry_t<int16_t>& v) { return pack_pod_array_type(SERIALIZE_TYPE_INT16, v);}
bool operator()(const array_entry_t<int8_t>& v) { return pack_pod_array_type(SERIALIZE_TYPE_INT8, v);}
- bool operator()(const array_entry_t<double>& v) { return pack_pod_array_type(SERIALIZE_TYPE_DUOBLE, v);}
+ bool operator()(const array_entry_t<double>& v) { return pack_pod_array_type(SERIALIZE_TYPE_DOUBLE, v);}
bool operator()(const array_entry_t<bool>& v) { return pack_pod_array_type(SERIALIZE_TYPE_BOOL, v);}
bool operator()(const array_entry_t<std::string>& arr_str)
{
@@ -160,7 +160,7 @@ namespace epee
bool operator()(const int32_t& v) { return pack_pod_type(SERIALIZE_TYPE_INT32, v);}
bool operator()(const int16_t& v) { return pack_pod_type(SERIALIZE_TYPE_INT16, v);}
bool operator()(const int8_t& v) { return pack_pod_type(SERIALIZE_TYPE_INT8, v);}
- bool operator()(const double& v) { return pack_pod_type(SERIALIZE_TYPE_DUOBLE, v);}
+ bool operator()(const double& v) { return pack_pod_type(SERIALIZE_TYPE_DOUBLE, v);}
bool operator()(const bool& v) { return pack_pod_type(SERIALIZE_TYPE_BOOL, v);}
bool operator()(const std::string& v)
{
diff --git a/contrib/gitian/DOCKRUN.md b/contrib/gitian/DOCKRUN.md
index dabf090c2..96998b1fe 100644
--- a/contrib/gitian/DOCKRUN.md
+++ b/contrib/gitian/DOCKRUN.md
@@ -64,7 +64,7 @@ The build should run to completion with no errors, and will display the SHA256 c
of the resulting binaries. You'll be prompted to check if the sums look good, and if so
then the results will be signed, and the signatures will be pushed to GitHub.
-***Note: In order to publish the signed assertions via this script, you need to have your SSH key uploaded to Github beforehand. See https://docs.github.com/articles/generating-an-ssh-key/ for more info.***
+***Note: In order to publish the signed assertions via this script, you need to have your SSH key uploaded to GitHub beforehand. See https://docs.github.com/articles/generating-an-ssh-key/ for more info.***
You can also look in the [gitian.sigs](https://github.com/monero-project/gitian.sigs/) repo and / or [getmonero.org release checksums](https://web.getmonero.org/downloads/hashes.txt) to see if others got the same checksum for the same version tag. If there is ever a mismatch -- **STOP! Something is wrong**. Contact others on IRC / GitHub to figure out what is going on.
diff --git a/contrib/gitian/README.md b/contrib/gitian/README.md
index 5590acf17..9852b07ba 100644
--- a/contrib/gitian/README.md
+++ b/contrib/gitian/README.md
@@ -136,7 +136,7 @@ GH_USER=YOUR_GITHUB_USER_NAME
VERSION=v0.17.2.0
```
-Where `GH_USER` is your Github user name and `VERSION` is the version tag you want to build.
+Where `GH_USER` is your GitHub user name and `VERSION` is the version tag you want to build.
Setup for LXC:
diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md
index 14b52a2af..a50114d1a 100644
--- a/docs/CONTRIBUTING.md
+++ b/docs/CONTRIBUTING.md
@@ -12,7 +12,7 @@ of software solid and usable.
* If modifying code for which Doxygen headers exist, that header must be modified to match.
* Tests would be nice to have if you're adding functionality.
-Patches are preferably to be sent via a Github pull request. If that
+Patches are preferably to be sent via a GitHub pull request. If that
can't be done, patches in "git format-patch" format can be sent
(eg, posted to fpaste.org with a long enough timeout and a link
posted to #monero-dev on irc.libera.chat).
@@ -43,7 +43,7 @@ Commit messages should be sensible. That means a subject line that
describes the patch, with an optional longer body that gives details,
documentation, etc.
-When submitting a pull request on Github, make sure your branch is
+When submitting a pull request on GitHub, make sure your branch is
rebased. No merge commits nor stray commits from other people in
your submitted branch, please. You may be asked to rebase if there
are conflicts (even trivially resolvable ones).
diff --git a/docs/PORTABLE_STORAGE.md b/docs/PORTABLE_STORAGE.md
new file mode 100644
index 000000000..675ca818c
--- /dev/null
+++ b/docs/PORTABLE_STORAGE.md
@@ -0,0 +1,198 @@
+# Portable Storage Format
+
+## Background
+
+Monero makes use of a set of helper classes from a small library named
+[epee](https://github.com/monero-project/monero/tree/master/contrib/epee). Part
+of this library implements a networking protocol called
+[Levin](https://github.com/monero-project/monero/blob/master/contrib/epee/include/net/levin_base.h),
+which internally uses a storage format called [Portable
+Storage](https://github.com/monero-project/monero/tree/master/contrib/epee/include/storages).
+This format (amongst the rest of the
+[epee](https://github.com/monero-project/monero/tree/master/contrib/epee)
+library), is undocumented - or rather relies on the code itself to serve as the
+documentation. Unfortunately, whilst the rest of the library is fairly
+straightforward to decipher, the Portable Storage is less-so. Hence this
+document.
+
+## String and Integer Encoding
+
+### Integers
+
+With few exceptions, integers serialized in epee portable storage format are serialized
+as little-endian.
+
+### Varints
+
+Varints are used to pack integers in an portable and space optimized way. Varints are stored as little-endian integers, with the lowest 2 bits storing the amount of bytes required, which means the largest value integer that can be packed into 1 byte is 63
+(6 bits).
+
+#### Byte Sizes
+
+| Lowest 2 bits | Size value | Value range |
+|---------------|---------------|-----------------------------------|
+| b00 | 1 byte | 0 to 63 |
+| b01 | 2 bytes | 64 to 16383 |
+| b10 | 4 bytes | 16384 to 1073741823 |
+| b11 | 8 bytes | 1073741824 to 4611686018427387903 |
+
+#### Represenations of Example Values
+| Value | Byte Representation (hex) |
+|----------------------|---------------------------|
+| 0 | 00 |
+| 7 | 1c |
+| 101 | 95 01 |
+| 17,000 | A2 09 01 00 |
+| 7,942,319,744 | 03 BA 98 65 07 00 00 00 |
+
+### Strings
+
+These are simply length (varint) prefixed char strings without a null
+terminator (though one can always add one if desired). There is no
+specific encoding enforced, and in fact, many times binary blobs are
+stored as these strings. This type should not be confused with the keys
+in sections, as those are restricted to a maximum length of 255 and
+do not use varints to encode the length.
+
+ "Howdy" => 14 48 6F 77 64 79
+
+### Section Keys
+
+These are similar to strings except that they are length limited to 255
+bytes, and use a single byte at the front of the string to describe the
+length (as opposed to a varint).
+
+ "Howdy" => 05 48 6F 77 64 79
+
+## Binary Format Specification
+
+### Header
+
+The format must always start with the following header:
+
+| Field | Type | Value |
+|------------------|----------|------------|
+| Signature Part A | UInt32 | 0x01011101 |
+| Signature Part B | UInt32 | 0x01020101 |
+| Version | UInt8 | 0x01 |
+
+In total, the 9 byte header will look like this (in hex): `01 11 01 01 01 01 02 01 01`
+
+### Section
+
+Next we have a root object (or section as the library calls it). This is a map
+of name-value pairs called [entries](#Entry). It starts with a count:
+
+| Section | Type |
+|---------------|-----------|
+| Entry count | varint |
+
+
+Which is followed by the section's name-value [entries](#Entry) sequentially:
+
+### Entry
+
+| Entry | Type |
+|-------------------|-----------------------|
+| Name | section key |
+| Type | byte |
+| Count<sup>1</sup> | varint |
+| Value(s) | (type dependant data) |
+
+<sup>1</sup> Note, this is only present if the entry type has the array flag
+(see below).
+
+#### Entry types
+
+The types defined are:
+
+```cpp
+#define SERIALIZE_TYPE_INT64 1
+#define SERIALIZE_TYPE_INT32 2
+#define SERIALIZE_TYPE_INT16 3
+#define SERIALIZE_TYPE_INT8 4
+#define SERIALIZE_TYPE_UINT64 5
+#define SERIALIZE_TYPE_UINT32 6
+#define SERIALIZE_TYPE_UINT16 7
+#define SERIALIZE_TYPE_UINT8 8
+#define SERIALIZE_TYPE_DOUBLE 9
+#define SERIALIZE_TYPE_STRING 10
+#define SERIALIZE_TYPE_BOOL 11
+#define SERIALIZE_TYPE_OBJECT 12
+#define SERIALIZE_TYPE_ARRAY 13
+```
+
+The entry type can be bitwise OR'ed with a flag:
+
+```cpp
+#define SERIALIZE_FLAG_ARRAY 0x80
+```
+
+This signals there are multiple *values* for the entry. Since only one bit is
+reserved for specifying an array, we can not directly represent nested arrays.
+However, you can place each of the inner arrays inside of a section, and make
+the outer array type `SERIALIZE_TYPE_OBJECT | SERIALIZE_FLAG_ARRAY`. Immediately following the type code byte is a varint specifying the length of the array.
+Finally, the all the elements are serialized in sequence with no padding and
+without any type information. For example:
+
+<p style="padding-left:1em; font:italic larger serif">type, count,
+value<sub>1</sub>, value<sub>2</sub>,..., value<sub>n</sub></p>
+
+#### Entry values
+
+It's important to understand that entry *values* can be encoded any way in which
+an implementation chooses. For example, the integers can be in either big or
+little endian byte order.
+
+Entry values which are objects (i.e. `SERIALIZE_TYPE_OBJECT`), are stored as
+[sections](#Section).
+
+Note, I have not yet seen the type `SERIALIZE_TYPE_ARRAY` in use. My assumption
+is this would be used for *untyped* arrays and so subsequent entries could be of
+any type.
+
+### Overall example
+
+Let's put it all together and see what an entire object would look like serialized. To represent our data, let's create a JSON object (since it's a format
+that most will be familiar with):
+
+```json
+{
+ "short_quote": "Give me liberty or give me death!",
+ "long_quote": "Monero is more than just a technology. It's also what the technology stands for.",
+ "signed_32bit_int": 20140418,
+ "array_of_bools": [true, false, true, true],
+ "nested_section": {
+ "double": -6.9,
+ "unsigned_64bit_int": 11111111111111111111
+ }
+}
+```
+
+This would translate to:
+
+![Epee binary storage format example](/docs/images/storage_binary_example.png)
+
+## Monero specifics
+
+### Entry values
+
+#### Hashes, Keys, Blobs
+
+These are stored as strings, `SERIALIZE_TYPE_STRING`.
+
+#### STL containers (vector, list)
+
+These can be arrays of standard integer types, strings or
+`SERIALIZE_TYPE_OBJECT`'s for structs.
+
+#### Links to some Monero struct definitions
+
+- [Core RPC
+ definitions](https://github.com/monero-project/monero/blob/master/src/rpc/core_rpc_server_commands_defs.h)
+- [CryptoNote protocol
+ definitions](https://github.com/monero-project/monero/blob/master/src/cryptonote_protocol/cryptonote_protocol_defs.h)
+
+
+
+[//]: # ( vim: set tw=80: )
diff --git a/docs/images/storage_binary_example.png b/docs/images/storage_binary_example.png
new file mode 100644
index 000000000..1baa20cc1
--- /dev/null
+++ b/docs/images/storage_binary_example.png
Binary files differ
diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp
index 44be08003..ab73e255c 100644
--- a/src/blockchain_db/blockchain_db.cpp
+++ b/src/blockchain_db/blockchain_db.cpp
@@ -241,15 +241,8 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const std::pair
}
else
{
- rct::key commitment;
- if (tx.version > 1)
- {
- commitment = tx.rct_signatures.outPk[i].mask;
- if (rct::is_rct_bulletproof_plus(tx.rct_signatures.type))
- commitment = rct::scalarmult8(commitment);
- }
amount_output_indices[i] = add_output(tx_hash, tx.vout[i], i, tx.unlock_time,
- tx.version > 1 ? &commitment : NULL);
+ tx.version > 1 ? &tx.rct_signatures.outPk[i].mask : NULL);
}
}
add_tx_amount_output_indices(tx_id, amount_output_indices);
diff --git a/src/crypto/keccak.c b/src/crypto/keccak.c
index 72d472d8a..f098cbdf0 100644
--- a/src/crypto/keccak.c
+++ b/src/crypto/keccak.c
@@ -31,54 +31,83 @@ const uint64_t keccakf_rndc[24] =
0x8000000000008080, 0x0000000080000001, 0x8000000080008008
};
-const int keccakf_rotc[24] =
-{
- 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14,
- 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44
-};
-
-const int keccakf_piln[24] =
-{
- 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4,
- 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1
-};
-
// update the state with given number of rounds
void keccakf(uint64_t st[25], int rounds)
{
- int i, j, round;
+ int round;
uint64_t t, bc[5];
- for (round = 0; round < rounds; round++) {
-
+ for (round = 0; round < rounds; ++round) {
// Theta
- for (i = 0; i < 5; i++)
- bc[i] = st[i] ^ st[i + 5] ^ st[i + 10] ^ st[i + 15] ^ st[i + 20];
-
- for (i = 0; i < 5; i++) {
- t = bc[(i + 4) % 5] ^ ROTL64(bc[(i + 1) % 5], 1);
- for (j = 0; j < 25; j += 5)
- st[j + i] ^= t;
+ bc[0] = st[0] ^ st[5] ^ st[10] ^ st[15] ^ st[20];
+ bc[1] = st[1] ^ st[6] ^ st[11] ^ st[16] ^ st[21];
+ bc[2] = st[2] ^ st[7] ^ st[12] ^ st[17] ^ st[22];
+ bc[3] = st[3] ^ st[8] ^ st[13] ^ st[18] ^ st[23];
+ bc[4] = st[4] ^ st[9] ^ st[14] ^ st[19] ^ st[24];
+
+#define THETA(i) { \
+ t = bc[(i + 4) % 5] ^ ROTL64(bc[(i + 1) % 5], 1); \
+ st[i ] ^= t; \
+ st[i + 5] ^= t; \
+ st[i + 10] ^= t; \
+ st[i + 15] ^= t; \
+ st[i + 20] ^= t; \
}
+ THETA(0);
+ THETA(1);
+ THETA(2);
+ THETA(3);
+ THETA(4);
+
// Rho Pi
t = st[1];
- for (i = 0; i < 24; i++) {
- j = keccakf_piln[i];
- bc[0] = st[j];
- st[j] = ROTL64(t, keccakf_rotc[i]);
- t = bc[0];
- }
+ st[ 1] = ROTL64(st[ 6], 44);
+ st[ 6] = ROTL64(st[ 9], 20);
+ st[ 9] = ROTL64(st[22], 61);
+ st[22] = ROTL64(st[14], 39);
+ st[14] = ROTL64(st[20], 18);
+ st[20] = ROTL64(st[ 2], 62);
+ st[ 2] = ROTL64(st[12], 43);
+ st[12] = ROTL64(st[13], 25);
+ st[13] = ROTL64(st[19], 8);
+ st[19] = ROTL64(st[23], 56);
+ st[23] = ROTL64(st[15], 41);
+ st[15] = ROTL64(st[ 4], 27);
+ st[ 4] = ROTL64(st[24], 14);
+ st[24] = ROTL64(st[21], 2);
+ st[21] = ROTL64(st[ 8], 55);
+ st[ 8] = ROTL64(st[16], 45);
+ st[16] = ROTL64(st[ 5], 36);
+ st[ 5] = ROTL64(st[ 3], 28);
+ st[ 3] = ROTL64(st[18], 21);
+ st[18] = ROTL64(st[17], 15);
+ st[17] = ROTL64(st[11], 10);
+ st[11] = ROTL64(st[ 7], 6);
+ st[ 7] = ROTL64(st[10], 3);
+ st[10] = ROTL64(t, 1);
// Chi
- for (j = 0; j < 25; j += 5) {
- for (i = 0; i < 5; i++)
- bc[i] = st[j + i];
- for (i = 0; i < 5; i++)
- st[j + i] ^= (~bc[(i + 1) % 5]) & bc[(i + 2) % 5];
+#define CHI(j) { \
+ const uint64_t st0 = st[j ]; \
+ const uint64_t st1 = st[j + 1]; \
+ const uint64_t st2 = st[j + 2]; \
+ const uint64_t st3 = st[j + 3]; \
+ const uint64_t st4 = st[j + 4]; \
+ st[j ] ^= ~st1 & st2; \
+ st[j + 1] ^= ~st2 & st3; \
+ st[j + 2] ^= ~st3 & st4; \
+ st[j + 3] ^= ~st4 & st0; \
+ st[j + 4] ^= ~st0 & st1; \
}
+ CHI( 0);
+ CHI( 5);
+ CHI(10);
+ CHI(15);
+ CHI(20);
+
// Iota
st[0] ^= keccakf_rndc[round];
}
diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp
index 432617a4f..f101f10c5 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.cpp
+++ b/src/cryptonote_basic/cryptonote_format_utils.cpp
@@ -190,7 +190,7 @@ namespace cryptonote
CHECK_AND_ASSERT_MES(n_amounts == rv.outPk.size(), false, "Internal error filling out V");
rv.p.bulletproofs_plus[0].V.resize(n_amounts);
for (size_t i = 0; i < n_amounts; ++i)
- rv.p.bulletproofs_plus[0].V[i] = rv.outPk[i].mask;
+ rv.p.bulletproofs_plus[0].V[i] = rct::scalarmultKey(rv.outPk[i].mask, rct::INV_EIGHT);
}
else if (bulletproof)
{
diff --git a/src/hardforks/hardforks.cpp b/src/hardforks/hardforks.cpp
index c055f49d2..0dd0cf5f0 100644
--- a/src/hardforks/hardforks.cpp
+++ b/src/hardforks/hardforks.cpp
@@ -98,6 +98,8 @@ const hardfork_t testnet_hard_forks[] = {
{ 12, 1308737, 0, 1569582000 },
{ 13, 1543939, 0, 1599069376 },
{ 14, 1544659, 0, 1599069377 },
+ { 15, 1982800, 0, 1652727000 },
+ { 16, 1983520, 0, 1652813400 },
};
const size_t num_testnet_hard_forks = sizeof(testnet_hard_forks) / sizeof(testnet_hard_forks[0]);
const uint64_t testnet_hard_fork_version_1_till = 624633;
diff --git a/src/multisig/multisig_account.cpp b/src/multisig/multisig_account.cpp
index b7298c4b6..9bdcf2dbc 100644
--- a/src/multisig/multisig_account.cpp
+++ b/src/multisig/multisig_account.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2021, The Monero Project
+// Copyright (c) 2021-2022, The Monero Project
//
// All rights reserved.
//
@@ -73,7 +73,7 @@ namespace multisig
const crypto::public_key &multisig_pubkey,
const crypto::public_key &common_pubkey,
const std::uint32_t kex_rounds_complete,
- kex_origins_map_t kex_origins_map,
+ multisig_keyset_map_memsafe_t kex_origins_map,
std::string next_round_kex_message) :
m_base_privkey{base_privkey},
m_base_common_privkey{base_common_privkey},
@@ -89,6 +89,20 @@ namespace multisig
CHECK_AND_ASSERT_THROW_MES(crypto::secret_key_to_public_key(m_base_privkey, m_base_pubkey),
"Failed to derive public key");
set_multisig_config(threshold, std::move(signers));
+
+ // kex rounds should not exceed post-kex verification round
+ const std::uint32_t kex_rounds_required{multisig_kex_rounds_required(m_signers.size(), m_threshold)};
+ CHECK_AND_ASSERT_THROW_MES(m_kex_rounds_complete <= kex_rounds_required + 1,
+ "multisig account: tried to reconstruct account, but kex rounds complete counter is invalid.");
+
+ // once an account is done with kex, the 'next kex msg' is always the post-kex verification message
+ // i.e. the multisig account pubkey signed by the signer's privkey AND the common pubkey
+ if (main_kex_rounds_done())
+ {
+ m_next_round_kex_message = multisig_kex_msg{kex_rounds_required + 1,
+ m_base_privkey,
+ std::vector<crypto::public_key>{m_multisig_pubkey, m_common_pubkey}}.get_msg();
+ }
}
//----------------------------------------------------------------------------------------------------------------------
// multisig_account: EXTERNAL
@@ -100,14 +114,24 @@ namespace multisig
//----------------------------------------------------------------------------------------------------------------------
// multisig_account: EXTERNAL
//----------------------------------------------------------------------------------------------------------------------
- bool multisig_account::multisig_is_ready() const
+ bool multisig_account::main_kex_rounds_done() const
{
if (account_is_active())
- return multisig_kex_rounds_required(m_signers.size(), m_threshold) == m_kex_rounds_complete;
+ return m_kex_rounds_complete >= multisig_kex_rounds_required(m_signers.size(), m_threshold);
+ else
+ return false;
+ }
+ //----------------------------------------------------------------------------------------------------------------------
+ // multisig_account: EXTERNAL
+ //----------------------------------------------------------------------------------------------------------------------
+ bool multisig_account::multisig_is_ready() const
+ {
+ if (main_kex_rounds_done())
+ return m_kex_rounds_complete >= multisig_kex_rounds_required(m_signers.size(), m_threshold) + 1;
else
return false;
}
- //----------------------------------------------------------------------------------------------------------------------
+ //----------------------------------------------------------------------------------------------------------------------
// multisig_account: INTERNAL
//----------------------------------------------------------------------------------------------------------------------
void multisig_account::set_multisig_config(const std::size_t threshold, std::vector<crypto::public_key> signers)
@@ -119,10 +143,6 @@ namespace multisig
for (auto signer_it = signers.begin(); signer_it != signers.end(); ++signer_it)
{
- // signers should all be unique
- CHECK_AND_ASSERT_THROW_MES(std::find(signers.begin(), signer_it, *signer_it) == signer_it,
- "multisig account: tried to set signers, but found a duplicate signer unexpectedly.");
-
// signer pubkeys must be in main subgroup, and not identity
CHECK_AND_ASSERT_THROW_MES(rct::isInMainSubgroup(rct::pk2rct(*signer_it)) && !(*signer_it == rct::rct2pk(rct::identity())),
"multisig account: tried to set signers, but a signer pubkey is invalid.");
@@ -133,12 +153,11 @@ namespace multisig
"multisig account: tried to set signers, but did not find the account's base pubkey in signer list.");
// sort signers
- std::sort(signers.begin(), signers.end(),
- [](const crypto::public_key &key1, const crypto::public_key &key2) -> bool
- {
- return memcmp(&key1, &key2, sizeof(crypto::public_key)) < 0;
- }
- );
+ std::sort(signers.begin(), signers.end());
+
+ // signers should all be unique
+ CHECK_AND_ASSERT_THROW_MES(std::adjacent_find(signers.begin(), signers.end()) == signers.end(),
+ "multisig account: tried to set signers, but there are duplicate signers unexpectedly.");
// set
m_threshold = threshold;
diff --git a/src/multisig/multisig_account.h b/src/multisig/multisig_account.h
index b01ae6c88..7b372bbff 100644
--- a/src/multisig/multisig_account.h
+++ b/src/multisig/multisig_account.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2021, The Monero Project
+// Copyright (c) 2021-2022, The Monero Project
//
// All rights reserved.
//
@@ -75,12 +75,12 @@ namespace multisig
* - ZtM2: https://web.getmonero.org/library/Zero-to-Monero-2-0-0.pdf Ch. 9, especially Section 9.6.3
* - FROST: https://eprint.iacr.org/2018/417
*/
+ using multisig_keyset_map_memsafe_t =
+ std::unordered_map<crypto::public_key_memsafe, std::unordered_set<crypto::public_key>>;
+
class multisig_account final
{
public:
- //member types
- using kex_origins_map_t = std::unordered_map<crypto::public_key_memsafe, std::unordered_set<crypto::public_key>>;
-
//constructors
// default constructor
multisig_account() = default;
@@ -105,7 +105,7 @@ namespace multisig
const crypto::public_key &multisig_pubkey,
const crypto::public_key &common_pubkey,
const std::uint32_t kex_rounds_complete,
- kex_origins_map_t kex_origins_map,
+ multisig_keyset_map_memsafe_t kex_origins_map,
std::string next_round_kex_message);
// copy constructor: default
@@ -137,13 +137,15 @@ namespace multisig
// get kex rounds complete
std::uint32_t get_kex_rounds_complete() const { return m_kex_rounds_complete; }
// get kex keys to origins map
- const kex_origins_map_t& get_kex_keys_to_origins_map() const { return m_kex_keys_to_origins_map; }
+ const multisig_keyset_map_memsafe_t& get_kex_keys_to_origins_map() const { return m_kex_keys_to_origins_map; }
// get the kex msg for the next round
const std::string& get_next_kex_round_msg() const { return m_next_round_kex_message; }
//account status functions
// account has been intialized, and the account holder can use the 'common' key
bool account_is_active() const;
+ // account has gone through main kex rounds, only remaining step is to verify all other participants are ready
+ bool main_kex_rounds_done() const;
// account is ready to make multisig signatures
bool multisig_is_ready() const;
@@ -178,21 +180,21 @@ namespace multisig
* - Collect the local signer's shared keys to ignore in incoming messages, build the aggregate ancillary key
* if appropriate.
* param: expanded_msgs - set of multisig kex messages to process
- * param: rounds_required - number of rounds required for kex
+ * param: kex_rounds_required - number of rounds required for kex (not including post-kex verification round)
* outparam: exclude_pubkeys_out - keys held by the local account corresponding to round 'current_round'
* - If 'current_round' is the final round, these are the local account's shares of the final aggregate key.
*/
void initialize_kex_update(const std::vector<multisig_kex_msg> &expanded_msgs,
- const std::uint32_t rounds_required,
+ const std::uint32_t kex_rounds_required,
std::vector<crypto::public_key> &exclude_pubkeys_out);
/**
* brief: finalize_kex_update - Helper for kex_update_impl()
- * param: rounds_required - number of rounds required for kex
+ * param: kex_rounds_required - number of rounds required for kex (not including post-kex verification round)
* param: result_keys_to_origins_map - map between keys for the next round and the other participants they correspond to
* inoutparam: temp_account_inout - account to perform last update steps on
*/
- void finalize_kex_update(const std::uint32_t rounds_required,
- kex_origins_map_t result_keys_to_origins_map);
+ void finalize_kex_update(const std::uint32_t kex_rounds_required,
+ multisig_keyset_map_memsafe_t result_keys_to_origins_map);
//member variables
private:
@@ -226,7 +228,7 @@ namespace multisig
std::uint32_t m_kex_rounds_complete{0};
// this account's pubkeys for the in-progress key exchange round
// - either DH derivations (intermediate rounds), H(derivation)*G (final round), empty (when kex is done)
- kex_origins_map_t m_kex_keys_to_origins_map;
+ multisig_keyset_map_memsafe_t m_kex_keys_to_origins_map;
// the account's message for the in-progress key exchange round
std::string m_next_round_kex_message;
};
diff --git a/src/multisig/multisig_account_kex_impl.cpp b/src/multisig/multisig_account_kex_impl.cpp
index 0a0ca7bdc..be9ed9cb2 100644
--- a/src/multisig/multisig_account_kex_impl.cpp
+++ b/src/multisig/multisig_account_kex_impl.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2021, The Monero Project
+// Copyright (c) 2021-2022, The Monero Project
//
// All rights reserved.
//
@@ -57,6 +57,30 @@ namespace multisig
/**
* INTERNAL
*
+ * brief: check_multisig_config - validate multisig configuration details
+ * param: round - the round of the message that should be produced
+ * param: threshold - threshold for multisig (M in M-of-N)
+ * param: num_signers - number of participants in multisig (N)
+ */
+ //----------------------------------------------------------------------------------------------------------------------
+ static void check_multisig_config(const std::uint32_t round,
+ const std::uint32_t threshold,
+ const std::uint32_t num_signers)
+ {
+ CHECK_AND_ASSERT_THROW_MES(num_signers > 1, "Must be at least one other multisig signer.");
+ CHECK_AND_ASSERT_THROW_MES(num_signers <= config::MULTISIG_MAX_SIGNERS,
+ "Too many multisig signers specified (limit = 16 to prevent dangerous combinatorial explosion during key exchange).");
+ CHECK_AND_ASSERT_THROW_MES(num_signers >= threshold,
+ "Multisig threshold may not be larger than number of signers.");
+ CHECK_AND_ASSERT_THROW_MES(threshold > 0, "Multisig threshold must be > 0.");
+ CHECK_AND_ASSERT_THROW_MES(round > 0, "Multisig kex round must be > 0.");
+ CHECK_AND_ASSERT_THROW_MES(round <= multisig_kex_rounds_required(num_signers, threshold) + 1,
+ "Trying to process multisig kex for an invalid round.");
+ }
+ //----------------------------------------------------------------------------------------------------------------------
+ /**
+ * INTERNAL
+ *
* brief: calculate_multisig_keypair_from_derivation - wrapper on calculate_multisig_keypair() for an input public key
* Converts an input public key into a crypto private key (type cast, does not change serialization),
* then passes it to get_multisig_blinded_secret_key().
@@ -224,50 +248,23 @@ namespace multisig
/**
* INTERNAL
*
- * brief: multisig_kex_make_next_msg - Construct a kex msg for any round > 1 of multisig key construction.
+ * brief: multisig_kex_make_round_keys - Makes a kex round's keys.
* - Involves DH exchanges with pubkeys provided by other participants.
* - Conserves mapping [pubkey -> DH derivation] : [origin keys of participants that share this secret with you].
* param: base_privkey - account's base private key, for performing DH exchanges and signing messages
- * param: round - the round of the message that should be produced
- * param: threshold - threshold for multisig (M in M-of-N)
- * param: num_signers - number of participants in multisig (N)
* param: pubkey_origins_map - map between pubkeys to produce DH derivations with and identity keys of
* participants who will share each derivation with you
* outparam: derivation_origins_map_out - map between DH derivations (shared secrets) and identity keys
- * - If msg is not for the last round, then these derivations are also stored in the output message
- * so they can be sent to other participants, who will make more DH derivations for the next kex round.
- * - If msg is for the last round, then these derivations won't be sent to other participants.
- * Instead, they are converted to share secrets (i.e. s = H(derivation)) and multiplied by G.
- * The keys s*G are sent to other participants in the message, so they can be used to produce the final
- * multisig key via generate_multisig_spend_public_key().
- * - The values s are the local account's shares of the final multisig key's private key. The caller can
- * compute those values with calculate_multisig_keypair_from_derivation() (or compute them directly).
- * return: multisig kex message for the specified round
*/
//----------------------------------------------------------------------------------------------------------------------
- static multisig_kex_msg multisig_kex_make_next_msg(const crypto::secret_key &base_privkey,
- const std::uint32_t round,
- const std::uint32_t threshold,
- const std::uint32_t num_signers,
- const std::unordered_map<crypto::public_key_memsafe, std::unordered_set<crypto::public_key>> &pubkey_origins_map,
- std::unordered_map<crypto::public_key_memsafe, std::unordered_set<crypto::public_key>> &derivation_origins_map_out)
+ static void multisig_kex_make_round_keys(const crypto::secret_key &base_privkey,
+ multisig_keyset_map_memsafe_t pubkey_origins_map,
+ multisig_keyset_map_memsafe_t &derivation_origins_map_out)
{
- CHECK_AND_ASSERT_THROW_MES(num_signers > 1, "Must be at least one other multisig signer.");
- CHECK_AND_ASSERT_THROW_MES(num_signers <= config::MULTISIG_MAX_SIGNERS,
- "Too many multisig signers specified (limit = 16 to prevent dangerous combinatorial explosion during key exchange).");
- CHECK_AND_ASSERT_THROW_MES(num_signers >= threshold,
- "Multisig threshold may not be larger than number of signers.");
- CHECK_AND_ASSERT_THROW_MES(threshold > 0, "Multisig threshold must be > 0.");
- CHECK_AND_ASSERT_THROW_MES(round > 1, "Round for next msg must be > 1.");
- CHECK_AND_ASSERT_THROW_MES(round <= multisig_kex_rounds_required(num_signers, threshold),
- "Trying to make key exchange message for an invalid round.");
-
// make shared secrets with input pubkeys
- std::vector<crypto::public_key> msg_pubkeys;
- msg_pubkeys.reserve(pubkey_origins_map.size());
derivation_origins_map_out.clear();
- for (const auto &pubkey_and_origins : pubkey_origins_map)
+ for (auto &pubkey_and_origins : pubkey_origins_map)
{
// D = 8 * k_base * K_pubkey
// note: must be mul8 (cofactor), otherwise it is possible to leak to a malicious participant if the local
@@ -281,27 +278,29 @@ namespace multisig
rct::scalarmultKey(derivation_rct, rct::pk2rct(pubkey_and_origins.first), rct::sk2rct(base_privkey));
rct::scalarmultKey(derivation_rct, derivation_rct, rct::EIGHT);
- crypto::public_key_memsafe derivation{rct::rct2pk(derivation_rct)};
-
// retain mapping between pubkey's origins and the DH derivation
- // note: if msg for last round, then caller must know how to handle these derivations properly
- derivation_origins_map_out[derivation] = pubkey_and_origins.second;
-
- // if the last round, convert derivations to public keys for the output message
- if (round == multisig_kex_rounds_required(num_signers, threshold))
- {
- // derived_pubkey = H(derivation)*G
- crypto::public_key derived_pubkey;
- calculate_multisig_keypair_from_derivation(derivation, derived_pubkey);
- msg_pubkeys.push_back(derived_pubkey);
- }
- // otherwise, put derivations in message directly, so other signers can in turn create derivations (shared secrets)
- // with them for the next round
- else
- msg_pubkeys.push_back(derivation);
+ // note: if working on last kex round, then caller must know how to handle these derivations properly
+ derivation_origins_map_out[rct::rct2pk(derivation_rct)] = std::move(pubkey_and_origins.second);
}
+ }
+ //----------------------------------------------------------------------------------------------------------------------
+ /**
+ * INTERNAL
+ *
+ * brief: check_messages_round - Check that a set of messages have an expected round number.
+ * param: expanded_msgs - set of multisig kex messages to process
+ * param: expected_round - round number the kex messages should have
+ */
+ //----------------------------------------------------------------------------------------------------------------------
+ static void check_messages_round(const std::vector<multisig_kex_msg> &expanded_msgs,
+ const std::uint32_t expected_round)
+ {
+ CHECK_AND_ASSERT_THROW_MES(expanded_msgs.size() > 0, "At least one input message expected.");
+ const std::uint32_t round{expanded_msgs[0].get_round()};
+ CHECK_AND_ASSERT_THROW_MES(round == expected_round, "Messages don't have the expected kex round number.");
- return multisig_kex_msg{round, base_privkey, std::move(msg_pubkeys)};
+ for (const auto &expanded_msg : expanded_msgs)
+ CHECK_AND_ASSERT_THROW_MES(expanded_msg.get_round() == round, "All messages must have the same kex round number.");
}
//----------------------------------------------------------------------------------------------------------------------
/**
@@ -327,19 +326,19 @@ namespace multisig
static std::uint32_t multisig_kex_msgs_sanitize_pubkeys(const crypto::public_key &own_pubkey,
const std::vector<multisig_kex_msg> &expanded_msgs,
const std::vector<crypto::public_key> &exclude_pubkeys,
- std::unordered_map<crypto::public_key_memsafe, std::unordered_set<crypto::public_key>> &sanitized_pubkeys_out)
+ multisig_keyset_map_memsafe_t &sanitized_pubkeys_out)
{
+ // all messages should have the same round (redundant sanity check)
CHECK_AND_ASSERT_THROW_MES(expanded_msgs.size() > 0, "At least one input message expected.");
+ const std::uint32_t round{expanded_msgs[0].get_round()};
+ check_messages_round(expanded_msgs, round);
- std::uint32_t round = expanded_msgs[0].get_round();
sanitized_pubkeys_out.clear();
// get all pubkeys from input messages, add them to pubkey:origins map
// - origins = all the signing pubkeys that recommended a given msg pubkey
for (const auto &expanded_msg : expanded_msgs)
{
- CHECK_AND_ASSERT_THROW_MES(expanded_msg.get_round() == round, "All messages must have the same kex round number.");
-
// ignore messages from self
if (expanded_msg.get_signing_pubkey() == own_pubkey)
continue;
@@ -378,7 +377,7 @@ namespace multisig
*
* brief: evaluate_multisig_kex_round_msgs - Evaluate pubkeys from a kex round in order to prepare for the next round.
* - Sanitizes input msgs.
- * - Require uniqueness in: 'signers', 'exclude_pubkeys'.
+ * - Require uniqueness in: 'exclude_pubkeys'.
* - Requires each input pubkey be recommended by 'num_recommendations = expected_round' msg signers.
* - For a final multisig key to be truly 'M-of-N', each of the the private key's components must be
* shared by (N - M + 1) signers.
@@ -388,39 +387,21 @@ namespace multisig
* with the local account.
* - Requires that 'exclude_pubkeys' has [num_signers - 1 CHOOSE (expected_round - 1)] pubkeys.
* - These should be derivations the local account has corresponding to round 'expected_round'.
- * param: base_privkey - multisig account's base private key
+ * param: base_pubkey - multisig account's base public key
* param: expected_round - expected kex round of input messages
- * param: threshold - threshold for multisig (M in M-of-N)
* param: signers - expected participants in multisig kex
* param: expanded_msgs - set of multisig kex messages to process
* param: exclude_pubkeys - derivations held by the local account corresponding to round 'expected_round'
* return: fully sanitized and validated pubkey:origins map for building the account's next kex round message
*/
//----------------------------------------------------------------------------------------------------------------------
- static std::unordered_map<crypto::public_key_memsafe, std::unordered_set<crypto::public_key>> evaluate_multisig_kex_round_msgs(
+ static multisig_keyset_map_memsafe_t evaluate_multisig_kex_round_msgs(
const crypto::public_key &base_pubkey,
const std::uint32_t expected_round,
- const std::uint32_t threshold,
const std::vector<crypto::public_key> &signers,
const std::vector<multisig_kex_msg> &expanded_msgs,
const std::vector<crypto::public_key> &exclude_pubkeys)
{
- CHECK_AND_ASSERT_THROW_MES(signers.size() > 1, "Must be at least one other multisig signer.");
- CHECK_AND_ASSERT_THROW_MES(signers.size() <= config::MULTISIG_MAX_SIGNERS,
- "Too many multisig signers specified (limit = 16 to prevent dangerous combinatorial explosion during key exchange).");
- CHECK_AND_ASSERT_THROW_MES(signers.size() >= threshold, "Multisig threshold may not be larger than number of signers.");
- CHECK_AND_ASSERT_THROW_MES(threshold > 0, "Multisig threshold must be > 0.");
- CHECK_AND_ASSERT_THROW_MES(expected_round > 0, "Expected round must be > 0.");
- CHECK_AND_ASSERT_THROW_MES(expected_round <= multisig_kex_rounds_required(signers.size(), threshold),
- "Expecting key exchange messages for an invalid round.");
-
- std::unordered_map<crypto::public_key_memsafe, std::unordered_set<crypto::public_key>> pubkey_origins_map;
-
- // leave early in the last round of 1-of-N, where all signers share a key so the local signer doesn't care about
- // recommendations from other signers
- if (threshold == 1 && expected_round == multisig_kex_rounds_required(signers.size(), threshold))
- return pubkey_origins_map;
-
// exclude_pubkeys should all be unique
for (auto it = exclude_pubkeys.begin(); it != exclude_pubkeys.end(); ++it)
{
@@ -429,7 +410,8 @@ namespace multisig
}
// sanitize input messages
- std::uint32_t round = multisig_kex_msgs_sanitize_pubkeys(base_pubkey, expanded_msgs, exclude_pubkeys, pubkey_origins_map);
+ multisig_keyset_map_memsafe_t pubkey_origins_map;
+ const std::uint32_t round = multisig_kex_msgs_sanitize_pubkeys(base_pubkey, expanded_msgs, exclude_pubkeys, pubkey_origins_map);
CHECK_AND_ASSERT_THROW_MES(round == expected_round,
"Kex messages were for round [" << round << "], but expected round is [" << expected_round << "]");
@@ -486,10 +468,10 @@ namespace multisig
// - Each origin should have a shared key with each group of size 'round - 1'.
// Note: Keys shared with local are ignored to facilitate kex round boosting, where one or more signers may
// have boosted the local signer (implying they didn't have access to the local signer's previous round msg).
- std::uint32_t expected_recommendations_others = n_choose_k_f(signers.size() - 2, round - 1);
+ const std::uint32_t expected_recommendations_others = n_choose_k_f(signers.size() - 2, round - 1);
// local: (N - 1) choose (msg_round_num - 1)
- std::uint32_t expected_recommendations_self = n_choose_k_f(signers.size() - 1, round - 1);
+ const std::uint32_t expected_recommendations_self = n_choose_k_f(signers.size() - 1, round - 1);
// note: expected_recommendations_others would be 0 in the last round of 1-of-N, but we return early for that case
CHECK_AND_ASSERT_THROW_MES(expected_recommendations_self > 0 && expected_recommendations_others > 0,
@@ -517,7 +499,60 @@ namespace multisig
/**
* INTERNAL
*
- * brief: multisig_kex_process_round - Process kex messages for the active kex round.
+ * brief: evaluate_multisig_post_kex_round_msgs - Evaluate messages for the post-kex verification round.
+ * - Sanitizes input msgs.
+ * - Requires that only one pubkey is recommended.
+ * - Requires that all signers (other than self) recommend that one pubkey.
+ * param: base_pubkey - multisig account's base public key
+ * param: expected_round - expected kex round of input messages
+ * param: signers - expected participants in multisig kex
+ * param: expanded_msgs - set of multisig kex messages to process
+ * return: sanitized and validated pubkey:origins map
+ */
+ //----------------------------------------------------------------------------------------------------------------------
+ static multisig_keyset_map_memsafe_t evaluate_multisig_post_kex_round_msgs(
+ const crypto::public_key &base_pubkey,
+ const std::uint32_t expected_round,
+ const std::vector<crypto::public_key> &signers,
+ const std::vector<multisig_kex_msg> &expanded_msgs)
+ {
+ // sanitize input messages
+ const std::vector<crypto::public_key> dummy;
+ multisig_keyset_map_memsafe_t pubkey_origins_map;
+ const std::uint32_t round = multisig_kex_msgs_sanitize_pubkeys(base_pubkey, expanded_msgs, dummy, pubkey_origins_map);
+ CHECK_AND_ASSERT_THROW_MES(round == expected_round,
+ "Kex messages were for round [" << round << "], but expected round is [" << expected_round << "]");
+
+ // evaluate pubkeys collected
+
+ // 1) there should only be two pubkeys
+ CHECK_AND_ASSERT_THROW_MES(pubkey_origins_map.size() == 2,
+ "Multisig post-kex round messages from other signers did not all contain two pubkeys.");
+
+ // 2) both keys should be recommended by the same set of signers
+ CHECK_AND_ASSERT_THROW_MES(pubkey_origins_map.begin()->second == (++(pubkey_origins_map.begin()))->second,
+ "Multisig post-kex round messages from other signers did not all recommend the same pubkey pair.");
+
+ // 3) all signers should be present in the recommendation list
+ auto origins = pubkey_origins_map.begin()->second;
+ origins.insert(base_pubkey); //add self
+
+ CHECK_AND_ASSERT_THROW_MES(origins.size() == signers.size(),
+ "Multisig post-kex round message origins don't line up with multisig signer set.");
+
+ for (const crypto::public_key &signer : signers)
+ {
+ CHECK_AND_ASSERT_THROW_MES(origins.find(signer) != origins.end(),
+ "Could not find an expected signer in multisig post-kex round messages (all signers expected).");
+ }
+
+ return pubkey_origins_map;
+ }
+ //----------------------------------------------------------------------------------------------------------------------
+ /**
+ * INTERNAL
+ *
+ * brief: multisig_kex_process_round_msgs - Process kex messages for the active kex round.
* - A wrapper around evaluate_multisig_kex_round_msgs() -> multisig_kex_make_next_msg().
* - In other words, evaluate the input messages and try to make a message for the next round.
* - Note: Must be called on the final round's msgs to evaluate the final key components
@@ -536,43 +571,62 @@ namespace multisig
* return: multisig kex message for next round, or empty message if 'current_round' is the final round
*/
//----------------------------------------------------------------------------------------------------------------------
- static multisig_kex_msg multisig_kex_process_round(const crypto::secret_key &base_privkey,
+ static void multisig_kex_process_round_msgs(const crypto::secret_key &base_privkey,
const crypto::public_key &base_pubkey,
const std::uint32_t current_round,
const std::uint32_t threshold,
const std::vector<crypto::public_key> &signers,
const std::vector<multisig_kex_msg> &expanded_msgs,
const std::vector<crypto::public_key> &exclude_pubkeys,
- std::unordered_map<crypto::public_key_memsafe, std::unordered_set<crypto::public_key>> &keys_to_origins_map_out)
+ multisig_keyset_map_memsafe_t &keys_to_origins_map_out)
{
- // evaluate messages
- std::unordered_map<crypto::public_key_memsafe, std::unordered_set<crypto::public_key>> evaluated_pubkeys =
- evaluate_multisig_kex_round_msgs(base_pubkey, current_round, threshold, signers, expanded_msgs, exclude_pubkeys);
+ check_multisig_config(current_round, threshold, signers.size());
+ const std::uint32_t kex_rounds_required{multisig_kex_rounds_required(signers.size(), threshold)};
- // produce message for next round (if there is one)
- if (current_round < multisig_kex_rounds_required(signers.size(), threshold))
+ // process messages into a [pubkey : {origins}] map
+ multisig_keyset_map_memsafe_t evaluated_pubkeys;
+
+ if (threshold == 1 && current_round == kex_rounds_required)
{
- return multisig_kex_make_next_msg(base_privkey,
- current_round + 1,
- threshold,
- signers.size(),
- evaluated_pubkeys,
- keys_to_origins_map_out);
+ // in the last main kex round of 1-of-N, all signers share a key so the local signer doesn't care about evaluating
+ // recommendations from other signers
}
- else
+ else if (current_round <= kex_rounds_required)
{
- // no more rounds, so collect the key shares recommended by other signers for the final aggregate key
- keys_to_origins_map_out.clear();
- keys_to_origins_map_out = std::move(evaluated_pubkeys);
+ // for normal kex rounds, fully evaluate kex round messages
+ evaluated_pubkeys = evaluate_multisig_kex_round_msgs(base_pubkey,
+ current_round,
+ signers,
+ expanded_msgs,
+ exclude_pubkeys);
+ }
+ else //(current_round == kex_rounds_required + 1)
+ {
+ // for the post-kex verification round, validate the last kex round's messages
+ evaluated_pubkeys = evaluate_multisig_post_kex_round_msgs(base_pubkey,
+ current_round,
+ signers,
+ expanded_msgs);
+ }
- return multisig_kex_msg{};
+ // prepare keys-to-origins map for updating the multisig account
+ if (current_round < kex_rounds_required)
+ {
+ // normal kex round: make new keys
+ multisig_kex_make_round_keys(base_privkey, std::move(evaluated_pubkeys), keys_to_origins_map_out);
+ }
+ else if (current_round >= kex_rounds_required)
+ {
+ // last kex round: collect the key shares recommended by other signers for the final aggregate key
+ // post-kex verification round: save the keys found in input messages
+ keys_to_origins_map_out = std::move(evaluated_pubkeys);
}
}
//----------------------------------------------------------------------------------------------------------------------
// multisig_account: INTERNAL
//----------------------------------------------------------------------------------------------------------------------
void multisig_account::initialize_kex_update(const std::vector<multisig_kex_msg> &expanded_msgs,
- const std::uint32_t rounds_required,
+ const std::uint32_t kex_rounds_required,
std::vector<crypto::public_key> &exclude_pubkeys_out)
{
if (m_kex_rounds_complete == 0)
@@ -605,7 +659,7 @@ namespace multisig
"Failed to derive public key");
// if N-of-N, then the base privkey will be used directly to make the account's share of the final key
- if (rounds_required == 1)
+ if (kex_rounds_required == 1)
{
m_multisig_privkeys.clear();
m_multisig_privkeys.emplace_back(m_base_privkey);
@@ -629,13 +683,29 @@ namespace multisig
//----------------------------------------------------------------------------------------------------------------------
// multisig_account: INTERNAL
//----------------------------------------------------------------------------------------------------------------------
- void multisig_account::finalize_kex_update(const std::uint32_t rounds_required,
- std::unordered_map<crypto::public_key_memsafe, std::unordered_set<crypto::public_key>> result_keys_to_origins_map)
+ void multisig_account::finalize_kex_update(const std::uint32_t kex_rounds_required,
+ multisig_keyset_map_memsafe_t result_keys_to_origins_map)
{
+ std::vector<crypto::public_key> next_msg_keys;
+
// prepare for next round (or complete the multisig account fully)
- if (rounds_required == m_kex_rounds_complete + 1)
+ if (m_kex_rounds_complete == kex_rounds_required)
+ {
+ // post-kex verification round: check that the multisig pubkey and common pubkey were recommended by other signers
+ CHECK_AND_ASSERT_THROW_MES(result_keys_to_origins_map.count(m_multisig_pubkey) > 0,
+ "Multisig post-kex round: expected multisig pubkey wasn't found in other signers' messages.");
+ CHECK_AND_ASSERT_THROW_MES(result_keys_to_origins_map.count(m_common_pubkey) > 0,
+ "Multisig post-kex round: expected common pubkey wasn't found in other signers' messages.");
+
+ // save keys that should be recommended to other signers
+ // - for convenience, re-recommend the post-kex verification message once an account is complete
+ next_msg_keys.reserve(2);
+ next_msg_keys.push_back(m_multisig_pubkey);
+ next_msg_keys.push_back(m_common_pubkey);
+ }
+ else if (m_kex_rounds_complete + 1 == kex_rounds_required)
{
- // finished (have set of msgs to complete address)
+ // finished with main kex rounds (have set of msgs to complete address)
// when 'completing the final round', result keys are other signers' shares of the final key
std::vector<crypto::public_key> result_keys;
@@ -652,8 +722,14 @@ namespace multisig
// no longer need the account's pubkeys saved for this round (they were only used to build exclude_pubkeys)
// TODO: record [pre-aggregation pubkeys : origins] map for aggregation-style signing
m_kex_keys_to_origins_map.clear();
+
+ // save keys that should be recommended to other signers
+ // - for post-kex verification, recommend the multisig pubkeys to notify other signers that the local signer is done
+ next_msg_keys.reserve(2);
+ next_msg_keys.push_back(m_multisig_pubkey);
+ next_msg_keys.push_back(m_common_pubkey);
}
- else if (rounds_required == m_kex_rounds_complete + 2)
+ else if (m_kex_rounds_complete + 2 == kex_rounds_required)
{
// one more round (must send/receive one more set of kex msgs)
// - at this point, have local signer's pre-aggregation private key shares of the final address
@@ -668,6 +744,7 @@ namespace multisig
m_multisig_privkeys.reserve(result_keys_to_origins_map.size());
m_kex_keys_to_origins_map.clear();
+ next_msg_keys.reserve(result_keys_to_origins_map.size());
for (const auto &derivation_and_origins : result_keys_to_origins_map)
{
@@ -679,37 +756,59 @@ namespace multisig
// save the account's kex key mappings for this round [derived pubkey : other signers who will have the same key]
m_kex_keys_to_origins_map[derived_pubkey] = std::move(derivation_and_origins.second);
+
+ // save keys that should be recommended to other signers
+ // - The keys multisig_key*G are sent to other participants in the message, so they can be used to produce the final
+ // multisig key via generate_multisig_spend_public_key().
+ next_msg_keys.push_back(derived_pubkey);
}
}
- else
+ else //(m_kex_rounds_complete + 3 <= kex_rounds_required)
{
// next round is an 'intermediate' key exchange round, so there is nothing special to do here
- // save the account's kex keys for this round [DH derivation : other signers who will have the same derivation]
+ // save keys that should be recommended to other signers
+ // - Send this round's DH derivations to other participants, who will make more DH derivations for the following round.
+ next_msg_keys.reserve(result_keys_to_origins_map.size());
+
+ for (const auto &derivation_and_origins : result_keys_to_origins_map)
+ next_msg_keys.push_back(derivation_and_origins.first);
+
+ // save the account's kex keys for this round [DH derivation : other signers who should have the same derivation]
m_kex_keys_to_origins_map = std::move(result_keys_to_origins_map);
}
// a full set of msgs has been collected and processed, so the 'round is complete'
++m_kex_rounds_complete;
+
+ // make next round's message (or reproduce the post-kex verification round if kex is complete)
+ m_next_round_kex_message = multisig_kex_msg{
+ (m_kex_rounds_complete > kex_rounds_required ? kex_rounds_required : m_kex_rounds_complete) + 1,
+ m_base_privkey,
+ std::move(next_msg_keys)}.get_msg();
}
//----------------------------------------------------------------------------------------------------------------------
// multisig_account: INTERNAL
//----------------------------------------------------------------------------------------------------------------------
void multisig_account::kex_update_impl(const std::vector<multisig_kex_msg> &expanded_msgs)
{
- CHECK_AND_ASSERT_THROW_MES(expanded_msgs.size() > 0, "No key exchange messages passed in.");
+ // check messages are for the expected kex round
+ check_messages_round(expanded_msgs, m_kex_rounds_complete + 1);
+
+ // check kex round count
+ const std::uint32_t kex_rounds_required{multisig_kex_rounds_required(m_signers.size(), m_threshold)};
- const std::uint32_t rounds_required = multisig_kex_rounds_required(m_signers.size(), m_threshold);
- CHECK_AND_ASSERT_THROW_MES(rounds_required > 0, "Multisig kex rounds required unexpectedly 0.");
+ CHECK_AND_ASSERT_THROW_MES(kex_rounds_required > 0, "Multisig kex rounds required unexpectedly 0.");
+ CHECK_AND_ASSERT_THROW_MES(m_kex_rounds_complete < kex_rounds_required + 1,
+ "Multisig kex has already completed all required rounds (including post-kex verification).");
// initialize account update
std::vector<crypto::public_key> exclude_pubkeys;
- initialize_kex_update(expanded_msgs, rounds_required, exclude_pubkeys);
-
- // evaluate messages and get this account's kex msg for the next round
- std::unordered_map<crypto::public_key_memsafe, std::unordered_set<crypto::public_key>> result_keys_to_origins_map;
+ initialize_kex_update(expanded_msgs, kex_rounds_required, exclude_pubkeys);
- m_next_round_kex_message = multisig_kex_process_round(
+ // process messages into a [pubkey : {origins}] map
+ multisig_keyset_map_memsafe_t result_keys_to_origins_map;
+ multisig_kex_process_round_msgs(
m_base_privkey,
m_base_pubkey,
m_kex_rounds_complete + 1,
@@ -717,10 +816,10 @@ namespace multisig
m_signers,
expanded_msgs,
exclude_pubkeys,
- result_keys_to_origins_map).get_msg();
+ result_keys_to_origins_map);
// finish account update
- finalize_kex_update(rounds_required, std::move(result_keys_to_origins_map));
+ finalize_kex_update(kex_rounds_required, std::move(result_keys_to_origins_map));
}
//----------------------------------------------------------------------------------------------------------------------
} //namespace multisig
diff --git a/src/multisig/multisig_kex_msg.cpp b/src/multisig/multisig_kex_msg.cpp
index 2bbceb19d..c717e23ad 100644
--- a/src/multisig/multisig_kex_msg.cpp
+++ b/src/multisig/multisig_kex_msg.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2021, The Monero Project
+// Copyright (c) 2021-2022, The Monero Project
//
// All rights reserved.
//
diff --git a/src/multisig/multisig_kex_msg.h b/src/multisig/multisig_kex_msg.h
index 23e3042f2..833c7c8f6 100644
--- a/src/multisig/multisig_kex_msg.h
+++ b/src/multisig/multisig_kex_msg.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2021, The Monero Project
+// Copyright (c) 2021-2022, The Monero Project
//
// All rights reserved.
//
diff --git a/src/multisig/multisig_kex_msg_serialization.h b/src/multisig/multisig_kex_msg_serialization.h
index 9c7b993a7..e5558cdff 100644
--- a/src/multisig/multisig_kex_msg_serialization.h
+++ b/src/multisig/multisig_kex_msg_serialization.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2021, The Monero Project
+// Copyright (c) 2021-2022, The Monero Project
//
// All rights reserved.
//
diff --git a/src/net/zmq.cpp b/src/net/zmq.cpp
index bd7855f21..2b3ca8376 100644
--- a/src/net/zmq.cpp
+++ b/src/net/zmq.cpp
@@ -134,7 +134,7 @@ namespace zmq
{
/* ZMQ documentation states that message parts are atomic - either
all are received or none are. Looking through ZMQ code and
- Github discussions indicates that after part 1 is returned,
+ GitHub discussions indicates that after part 1 is returned,
`EAGAIN` cannot be returned to meet these guarantees. Unit tests
verify (for the `inproc://` case) that this is the behavior.
Therefore, read errors after the first part are treated as a
diff --git a/src/ringct/bulletproofs_plus.cc b/src/ringct/bulletproofs_plus.cc
index 3d27849c1..77b800064 100644
--- a/src/ringct/bulletproofs_plus.cc
+++ b/src/ringct/bulletproofs_plus.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, The Monero Project
+// Copyright (c) 2017-2022, The Monero Project
//
// All rights reserved.
//
@@ -644,8 +644,7 @@ try_again:
{
sc_mul(temp.bytes, temp.bytes, z_squared.bytes);
sc_mul(temp2.bytes, y_powers[MN+1].bytes, temp.bytes);
- sc_mul(temp2.bytes, temp2.bytes, gamma[j].bytes);
- sc_add(alpha1.bytes, alpha1.bytes, temp2.bytes);
+ sc_muladd(alpha1.bytes, temp2.bytes, gamma[j].bytes, alpha1.bytes);
}
// These are used in the inner product rounds
@@ -706,7 +705,8 @@ try_again:
rct::key challenge_squared;
sc_mul(challenge_squared.bytes, challenge.bytes, challenge.bytes);
- rct::key challenge_squared_inv = invert(challenge_squared);
+ rct::key challenge_squared_inv;
+ sc_mul(challenge_squared_inv.bytes, challenge_inv.bytes, challenge_inv.bytes);
sc_muladd(alpha1.bytes, dL.bytes, challenge_squared.bytes, alpha1.bytes);
sc_muladd(alpha1.bytes, dR.bytes, challenge_squared_inv.bytes, alpha1.bytes);
diff --git a/src/ringct/bulletproofs_plus.h b/src/ringct/bulletproofs_plus.h
index d9084075a..861c54f4f 100644
--- a/src/ringct/bulletproofs_plus.h
+++ b/src/ringct/bulletproofs_plus.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, The Monero Project
+// Copyright (c) 2017-2022, The Monero Project
//
// All rights reserved.
//
diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp
index d7883baac..bd67778ec 100644
--- a/src/ringct/rctSigs.cpp
+++ b/src/ringct/rctSigs.cpp
@@ -79,6 +79,7 @@ namespace
return rct::Bulletproof{rct::keyV(n_outs, I), I, I, I, I, I, I, rct::keyV(nrl, I), rct::keyV(nrl, I), I, I, I};
}
+
rct::BulletproofPlus make_dummy_bulletproof_plus(const std::vector<uint64_t> &outamounts, rct::keyV &C, rct::keyV &masks)
{
const size_t n_outs = outamounts.size();
@@ -109,6 +110,13 @@ namespace
return rct::BulletproofPlus{rct::keyV(n_outs, I), I, I, I, I, I, I, rct::keyV(nrl, I), rct::keyV(nrl, I)};
}
+
+ rct::clsag make_dummy_clsag(size_t ring_size)
+ {
+ const rct::key I = rct::identity();
+ const size_t n_scalars = ring_size;
+ return rct::clsag{rct::keyV(n_scalars, I), I, I, I};
+ }
}
namespace rct {
@@ -1235,10 +1243,7 @@ namespace rct {
}
for (i = 0; i < outamounts.size(); ++i)
{
- if (plus)
- rv.outPk[i].mask = C[i];
- else
- rv.outPk[i].mask = rct::scalarmult8(C[i]);
+ rv.outPk[i].mask = rct::scalarmult8(C[i]);
outSk[i].mask = masks[i];
}
}
@@ -1276,10 +1281,7 @@ namespace rct {
}
for (i = 0; i < batch_size; ++i)
{
- if (plus)
- rv.outPk[i + amounts_proved].mask = C[i];
- else
- rv.outPk[i + amounts_proved].mask = rct::scalarmult8(C[i]);
+ rv.outPk[i + amounts_proved].mask = rct::scalarmult8(C[i]);
outSk[i + amounts_proved].mask = masks[i];
}
amounts_proved += batch_size;
@@ -1329,7 +1331,10 @@ namespace rct {
{
if (is_rct_clsag(rv.type))
{
- rv.p.CLSAGs[i] = proveRctCLSAGSimple(full_message, rv.mixRing[i], inSk[i], a[i], pseudoOuts[i], kLRki ? &(*kLRki)[i]: NULL, msout ? &msout->c[i] : NULL, msout ? &msout->mu_p[i] : NULL, index[i], hwdev);
+ if (hwdev.get_mode() == hw::device::TRANSACTION_CREATE_FAKE)
+ rv.p.CLSAGs[i] = make_dummy_clsag(rv.mixRing[i].size());
+ else
+ rv.p.CLSAGs[i] = proveRctCLSAGSimple(full_message, rv.mixRing[i], inSk[i], a[i], pseudoOuts[i], kLRki ? &(*kLRki)[i]: NULL, msout ? &msout->c[i] : NULL, msout ? &msout->mu_p[i] : NULL, index[i], hwdev);
}
else
{
@@ -1486,10 +1491,7 @@ namespace rct {
rct::keyV masks(rv.outPk.size());
for (size_t i = 0; i < rv.outPk.size(); i++) {
- if (bulletproof_plus)
- masks[i] = rct::scalarmult8(rv.outPk[i].mask);
- else
- masks[i] = rv.outPk[i].mask;
+ masks[i] = rv.outPk[i].mask;
}
key sumOutpks = addKeys(masks);
DP(sumOutpks);
@@ -1649,8 +1651,6 @@ namespace rct {
mask = ecdh_info.mask;
key amount = ecdh_info.amount;
key C = rv.outPk[i].mask;
- if (is_rct_bulletproof_plus(rv.type))
- C = scalarmult8(C);
DP("C");
DP(C);
key Ctmp;
@@ -1682,8 +1682,6 @@ namespace rct {
mask = ecdh_info.mask;
key amount = ecdh_info.amount;
key C = rv.outPk[i].mask;
- if (is_rct_bulletproof_plus(rv.type))
- C = scalarmult8(C);
DP("C");
DP(C);
key Ctmp;
diff --git a/src/serialization/list.h b/src/serialization/list.h
deleted file mode 100644
index 16ee1b034..000000000
--- a/src/serialization/list.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (c) 2014-2022, 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
-
-#pragma once
-
-#include <list>
-
-template <template <bool> class Archive, class T>
-bool do_serialize(Archive<false> &ar, std::list<T> &v);
-template <template <bool> class Archive, class T>
-bool do_serialize(Archive<true> &ar, std::list<T> &v);
-
-namespace serialization
-{
- namespace detail
- {
- template <typename T>
- void do_add(std::list<T> &c, T &&e)
- {
- c.emplace_back(std::forward<T>(e));
- }
- }
-}
-
-#include "serialization.h"
-
-template <template <bool> class Archive, class T>
-bool do_serialize(Archive<false> &ar, std::list<T> &v) { return do_serialize_container(ar, v); }
-template <template <bool> class Archive, class T>
-bool do_serialize(Archive<true> &ar, std::list<T> &v) { return do_serialize_container(ar, v); }
-
diff --git a/src/serialization/serialization.h b/src/serialization/serialization.h
index 590b84428..381d29cfc 100644
--- a/src/serialization/serialization.h
+++ b/src/serialization/serialization.h
@@ -132,13 +132,6 @@ inline bool do_serialize(Archive &ar, bool &v)
return true;
}
-// Never used in the code base
-// #ifndef __GNUC__
-// #ifndef constexpr
-// #define constexpr
-// #endif
-// #endif
-
/* the following add a trait to a set and define the serialization DSL*/
/*! \macro BLOB_SERIALIZER
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index f30225e1d..d3e40ab74 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -1087,7 +1087,9 @@ bool simple_wallet::make_multisig_main(const std::vector<std::string> &args, boo
auto local_args = args;
local_args.erase(local_args.begin());
std::string multisig_extra_info = m_wallet->make_multisig(orig_pwd_container->password(), local_args, threshold);
- if (!multisig_extra_info.empty())
+ bool ready;
+ m_wallet->multisig(&ready);
+ if (!ready)
{
success_msg_writer() << tr("Another step is needed");
success_msg_writer() << multisig_extra_info;
@@ -1148,7 +1150,7 @@ bool simple_wallet::exchange_multisig_keys_main(const std::vector<std::string> &
return false;
}
- if (args.size() < 2)
+ if (args.size() < 1)
{
PRINT_USAGE(USAGE_EXCHANGE_MULTISIG_KEYS);
return false;
@@ -1157,7 +1159,9 @@ bool simple_wallet::exchange_multisig_keys_main(const std::vector<std::string> &
try
{
std::string multisig_extra_info = m_wallet->exchange_multisig_keys(orig_pwd_container->password(), args);
- if (!multisig_extra_info.empty())
+ bool ready;
+ m_wallet->multisig(&ready);
+ if (!ready)
{
message_writer() << tr("Another step is needed");
message_writer() << multisig_extra_info;
diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp
index bfbbbaeb7..7e4f12f5b 100644
--- a/src/wallet/ringdb.cpp
+++ b/src/wallet/ringdb.cpp
@@ -344,12 +344,15 @@ bool ringdb::remove_rings(const crypto::chacha_key &chacha_key, const cryptonote
return remove_rings(chacha_key, key_images);
}
-bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, std::vector<uint64_t> &outs)
+bool ringdb::get_rings(const crypto::chacha_key &chacha_key, const std::vector<crypto::key_image> &key_images, std::vector<std::vector<uint64_t>> &all_outs)
{
MDB_txn *txn;
int dbr;
bool tx_active = false;
+ all_outs.clear();
+ all_outs.reserve(key_images.size());
+
dbr = resize_env(env, filename.c_str(), 0);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to set env map size: " + std::string(mdb_strerror(dbr)));
dbr = mdb_txn_begin(env, NULL, 0, &txn);
@@ -357,6 +360,10 @@ bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_im
epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){if (tx_active) mdb_txn_abort(txn);});
tx_active = true;
+ for (size_t i = 0; i < key_images.size(); ++i)
+ {
+ const crypto::key_image &key_image = key_images[i];
+
MDB_val key, data;
std::string key_ciphertext = encrypt(key_image, chacha_key, 0);
key.mv_data = (void*)key_ciphertext.data();
@@ -367,6 +374,7 @@ bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_im
return false;
THROW_WALLET_EXCEPTION_IF(data.mv_size <= 0, tools::error::wallet_internal_error, "Invalid ring data size");
+ std::vector<uint64_t> outs;
bool try_v0 = false;
std::string data_plaintext = decrypt(std::string((const char*)data.mv_data, data.mv_size), key_image, chacha_key, 1);
try { outs = decompress_ring(data_plaintext, V1TAG); if (outs.empty()) try_v0 = true; }
@@ -380,6 +388,9 @@ bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_im
MDEBUG("Relative: " << boost::join(outs | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " "));
outs = cryptonote::relative_output_offsets_to_absolute(outs);
MDEBUG("Absolute: " << boost::join(outs | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " "));
+ all_outs.push_back(std::move(outs));
+
+ }
dbr = mdb_txn_commit(txn);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to commit txn getting ring from database: " + std::string(mdb_strerror(dbr)));
@@ -387,20 +398,33 @@ bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_im
return true;
}
-bool ringdb::set_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, const std::vector<uint64_t> &outs, bool relative)
+bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, std::vector<uint64_t> &outs)
+{
+ std::vector<std::vector<uint64_t>> all_outs;
+ if (!get_rings(chacha_key, std::vector<crypto::key_image>(1, key_image), all_outs))
+ return false;
+ outs = std::move(all_outs.front());
+ return true;
+}
+
+bool ringdb::set_rings(const crypto::chacha_key &chacha_key, const std::vector<std::pair<crypto::key_image, std::vector<uint64_t>>> &rings, bool relative)
{
MDB_txn *txn;
int dbr;
bool tx_active = false;
- dbr = resize_env(env, filename.c_str(), outs.size() * 64);
+ size_t n_outs = 0;
+ for (const auto &e: rings)
+ n_outs += e.second.size();
+ dbr = resize_env(env, filename.c_str(), n_outs * 64);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to set env map size: " + std::string(mdb_strerror(dbr)));
dbr = mdb_txn_begin(env, NULL, 0, &txn);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr)));
epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){if (tx_active) mdb_txn_abort(txn);});
tx_active = true;
- store_relative_ring(txn, dbi_rings, key_image, relative ? outs : cryptonote::absolute_output_offsets_to_relative(outs), chacha_key);
+ for (const auto &e: rings)
+ store_relative_ring(txn, dbi_rings, e.first, relative ? e.second : cryptonote::absolute_output_offsets_to_relative(e.second), chacha_key);
dbr = mdb_txn_commit(txn);
THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to commit txn setting ring to database: " + std::string(mdb_strerror(dbr)));
@@ -408,6 +432,13 @@ bool ringdb::set_ring(const crypto::chacha_key &chacha_key, const crypto::key_im
return true;
}
+bool ringdb::set_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, const std::vector<uint64_t> &outs, bool relative)
+{
+ std::vector<std::pair<crypto::key_image, std::vector<uint64_t>>> rings;
+ rings.push_back(std::make_pair(key_image, outs));
+ return set_rings(chacha_key, rings, relative);
+}
+
bool ringdb::blackball_worker(const std::vector<std::pair<uint64_t, uint64_t>> &outputs, int op)
{
MDB_txn *txn;
diff --git a/src/wallet/ringdb.h b/src/wallet/ringdb.h
index e9941bf94..bdecdba37 100644
--- a/src/wallet/ringdb.h
+++ b/src/wallet/ringdb.h
@@ -49,7 +49,9 @@ namespace tools
bool remove_rings(const crypto::chacha_key &chacha_key, const std::vector<crypto::key_image> &key_images);
bool remove_rings(const crypto::chacha_key &chacha_key, const cryptonote::transaction_prefix &tx);
bool get_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, std::vector<uint64_t> &outs);
+ bool get_rings(const crypto::chacha_key &chacha_key, const std::vector<crypto::key_image> &key_images, std::vector<std::vector<uint64_t>> &all_outs);
bool set_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, const std::vector<uint64_t> &outs, bool relative);
+ bool set_rings(const crypto::chacha_key &chacha_key, const std::vector<std::pair<crypto::key_image, std::vector<uint64_t>>> &rings, bool relative);
bool blackball(const std::pair<uint64_t, uint64_t> &output);
bool blackball(const std::vector<std::pair<uint64_t, uint64_t>> &outputs);
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index f0b29b9a2..1eeb893c0 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -5125,7 +5125,7 @@ std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &passwor
// reconstruct multisig account
crypto::public_key dummy;
- multisig::multisig_account::kex_origins_map_t kex_origins_map;
+ multisig::multisig_keyset_map_memsafe_t kex_origins_map;
for (const auto &derivation : m_multisig_derivations)
kex_origins_map[derivation];
@@ -5138,7 +5138,7 @@ std::string wallet2::exchange_multisig_keys(const epee::wipeable_string &passwor
get_account().get_keys().m_multisig_keys,
get_account().get_keys().m_view_secret_key,
m_account_public_address.m_spend_public_key,
- dummy, //common pubkey: not used
+ m_account_public_address.m_view_public_key,
m_multisig_rounds_passed,
std::move(kex_origins_map),
""
@@ -5225,7 +5225,10 @@ bool wallet2::multisig(bool *ready, uint32_t *threshold, uint32_t *total) const
if (total)
*total = m_multisig_signers.size();
if (ready)
- *ready = !(get_account().get_keys().m_account_address.m_spend_public_key == rct::rct2pk(rct::identity()));
+ {
+ *ready = !(get_account().get_keys().m_account_address.m_spend_public_key == rct::rct2pk(rct::identity())) &&
+ (m_multisig_rounds_passed == multisig::multisig_kex_rounds_required(m_multisig_signers.size(), m_multisig_threshold) + 1);
+ }
return true;
}
//----------------------------------------------------------------------------------------------------
@@ -7603,6 +7606,14 @@ bool wallet2::get_ring(const crypto::chacha_key &key, const crypto::key_image &k
catch (const std::exception &e) { return false; }
}
+bool wallet2::get_rings(const crypto::chacha_key &key, const std::vector<crypto::key_image> &key_images, std::vector<std::vector<uint64_t>> &outs)
+{
+ if (!m_ringdb)
+ return false;
+ try { return m_ringdb->get_rings(key, key_images, outs); }
+ catch (const std::exception &e) { return false; }
+}
+
bool wallet2::get_rings(const crypto::hash &txid, std::vector<std::pair<crypto::key_image, std::vector<uint64_t>>> &outs)
{
for (auto i: m_confirmed_txs)
@@ -7641,6 +7652,15 @@ bool wallet2::set_ring(const crypto::key_image &key_image, const std::vector<uin
catch (const std::exception &e) { return false; }
}
+bool wallet2::set_rings(const std::vector<std::pair<crypto::key_image, std::vector<uint64_t>>> &rings, bool relative)
+{
+ if (!m_ringdb)
+ return false;
+
+ try { return m_ringdb->set_rings(get_ringdb_key(), rings, relative); }
+ catch (const std::exception &e) { return false; }
+}
+
bool wallet2::unset_ring(const std::vector<crypto::key_image> &key_images)
{
if (!m_ringdb)
@@ -7815,7 +7835,7 @@ bool wallet2::is_keys_file_locked() const
return m_keys_file_locker->locked();
}
-bool wallet2::tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& output_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const
+bool wallet2::tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& output_public_key, const rct::key& mask, uint64_t real_index, bool unlocked, std::unordered_set<crypto::public_key> &valid_public_keys_cache) const
{
if (!unlocked) // don't add locked outs
return false;
@@ -7826,16 +7846,18 @@ bool wallet2::tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_out
if (std::find(outs.back().begin(), outs.back().end(), item) != outs.back().end()) // don't add duplicates
return false;
// check the keys are valid
- if (!rct::isInMainSubgroup(rct::pk2rct(output_public_key)))
+ if (valid_public_keys_cache.find(output_public_key) == valid_public_keys_cache.end() && !rct::isInMainSubgroup(rct::pk2rct(output_public_key)))
{
MWARNING("Key " << output_public_key << " at index " << global_index << " is not in the main subgroup");
return false;
}
- if (!rct::isInMainSubgroup(mask))
+ valid_public_keys_cache.insert(output_public_key);
+ if (valid_public_keys_cache.find(rct::rct2pk(mask)) == valid_public_keys_cache.end() && !rct::isInMainSubgroup(mask))
{
MWARNING("Commitment " << mask << " at index " << global_index << " is not in the main subgroup");
return false;
}
+ valid_public_keys_cache.insert(rct::rct2pk(mask));
// if (is_output_blackballed(output_public_key)) // don't add blackballed outputs
// return false;
outs.back().push_back(item);
@@ -7868,7 +7890,6 @@ void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_
m_daemon_rpc_mutex.unlock();
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_random_outs");
THROW_WALLET_EXCEPTION_IF(ores.amount_outs.empty() , error::wallet_internal_error, "No outputs received from light wallet node. Error: " + ores.Error);
- size_t n_outs = 0; for (const auto &e: ores.amount_outs) n_outs += e.outputs.size();
}
// Check if we got enough outputs for each amount
@@ -7879,6 +7900,7 @@ void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_
MDEBUG("selected transfers size: " << selected_transfers.size());
+ std::unordered_set<crypto::public_key> valid_public_keys_cache;
for(size_t idx: selected_transfers)
{
// Create new index
@@ -7930,7 +7952,7 @@ void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_
if(!light_wallet_parse_rct_str(ores.amount_outs[amount_key].outputs[i].rct, tx_public_key, 0, mask, rct_commit, false))
rct_commit = rct::zeroCommit(td.amount());
- if (tx_add_fake_output(outs, global_index, tx_public_key, rct_commit, td.m_global_output_index, true)) {
+ if (tx_add_fake_output(outs, global_index, tx_public_key, rct_commit, td.m_global_output_index, true, valid_public_keys_cache)) {
MDEBUG("added fake output " << ores.amount_outs[amount_key].outputs[i].public_key);
MDEBUG("index " << global_index);
}
@@ -7967,12 +7989,12 @@ std::pair<std::set<uint64_t>, size_t> outs_unique(const std::vector<std::vector<
return std::make_pair(std::move(unique), total);
}
-void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, bool rct)
+void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, bool rct, std::unordered_set<crypto::public_key> &valid_public_keys_cache)
{
std::vector<uint64_t> rct_offsets;
for (size_t attempts = 3; attempts > 0; --attempts)
{
- get_outs(outs, selected_transfers, fake_outputs_count, rct_offsets);
+ get_outs(outs, selected_transfers, fake_outputs_count, rct_offsets, valid_public_keys_cache);
if (!rct)
return;
@@ -7994,7 +8016,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
THROW_WALLET_EXCEPTION(error::wallet_internal_error, tr("Transaction sanity check failed"));
}
-void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, std::vector<uint64_t> &rct_offsets)
+void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, std::vector<uint64_t> &rct_offsets, std::unordered_set<crypto::public_key> &valid_public_keys_cache)
{
LOG_PRINT_L2("fake_outputs_count: " << fake_outputs_count);
outs.clear();
@@ -8038,6 +8060,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request req_t = AUTO_VAL_INIT(req_t);
cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response resp_t = AUTO_VAL_INIT(resp_t);
// request histogram for all outputs, except 0 if we have the rct distribution
+ req_t.amounts.reserve(selected_transfers.size());
for(size_t idx: selected_transfers)
if (!m_transfers[idx].is_rct() || !has_rct_distribution)
req_t.amounts.push_back(m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount());
@@ -8065,6 +8088,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
{
cryptonote::COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request req_t = AUTO_VAL_INIT(req_t);
cryptonote::COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response resp_t = AUTO_VAL_INIT(resp_t);
+ req_t.amounts.reserve(req_t.amounts.size() + selected_transfers.size());
for(size_t idx: selected_transfers)
req_t.amounts.push_back(m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount());
std::sort(req_t.amounts.begin(), req_t.amounts.end());
@@ -8111,6 +8135,25 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
}
}
+ std::vector<crypto::key_image> ring_key_images;
+ ring_key_images.reserve(selected_transfers.size());
+ std::unordered_map<crypto::key_image, std::vector<uint64_t>> existing_rings;
+ for(size_t idx: selected_transfers)
+ {
+ const transfer_details &td = m_transfers[idx];
+ if (td.m_key_image_known && !td.m_key_image_partial)
+ ring_key_images.push_back(td.m_key_image);
+ }
+ if (!ring_key_images.empty())
+ {
+ std::vector<std::vector<uint64_t>> all_outs;
+ if (get_rings(get_ringdb_key(), ring_key_images, all_outs))
+ {
+ for (size_t i = 0; i < ring_key_images.size(); ++i)
+ existing_rings[ring_key_images[i]] = std::move(all_outs[i]);
+ }
+ }
+
// we ask for more, to have spares if some outputs are still locked
size_t base_requested_outputs_count = (size_t)((fake_outputs_count + 1) * 1.5 + 1);
LOG_PRINT_L2("base_requested_outputs_count: " << base_requested_outputs_count);
@@ -8124,6 +8167,8 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
gamma.reset(new gamma_picker(rct_offsets));
size_t num_selected_transfers = 0;
+ req.outputs.reserve(selected_transfers.size() * (base_requested_outputs_count + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW));
+ daemon_resp.outs.reserve(selected_transfers.size() * (base_requested_outputs_count + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW));
for(size_t idx: selected_transfers)
{
++num_selected_transfers;
@@ -8233,9 +8278,12 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
// if we have a known ring, use it
if (td.m_key_image_known && !td.m_key_image_partial)
{
- std::vector<uint64_t> ring;
- if (get_ring(get_ringdb_key(), td.m_key_image, ring))
+
+ const auto it = existing_rings.find(td.m_key_image);
+ const bool has_ring = it != existing_rings.end();
+ if (has_ring)
{
+ const std::vector<uint64_t> &ring = it->second;
MINFO("This output has a known ring, reusing (size " << ring.size() << ")");
THROW_WALLET_EXCEPTION_IF(ring.size() > fake_outputs_count + 1, error::wallet_internal_error,
"An output in this transaction was previously spent on another chain with ring size " +
@@ -8435,7 +8483,9 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
COMMAND_RPC_GET_OUTPUTS_BIN::request chunk_req = AUTO_VAL_INIT(chunk_req);
COMMAND_RPC_GET_OUTPUTS_BIN::response chunk_daemon_resp = AUTO_VAL_INIT(chunk_daemon_resp);
chunk_req.get_txid = false;
- for (size_t i = 0; i < std::min<size_t>(req.outputs.size() - offset, chunk_size); ++i)
+ const size_t this_chunk_size = std::min<size_t>(req.outputs.size() - offset, chunk_size);
+ chunk_req.outputs.reserve(this_chunk_size);
+ for (size_t i = 0; i < this_chunk_size; ++i)
chunk_req.outputs.push_back(req.outputs[offset + i]);
const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex};
@@ -8505,9 +8555,10 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
// then pick outs from an existing ring, if any
if (td.m_key_image_known && !td.m_key_image_partial)
{
- std::vector<uint64_t> ring;
- if (get_ring(get_ringdb_key(), td.m_key_image, ring))
+ const auto it = existing_rings.find(td.m_key_image);
+ if (it != existing_rings.end())
{
+ const std::vector<uint64_t> &ring = it->second;
for (uint64_t out: ring)
{
if (out < num_outs)
@@ -8521,7 +8572,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
if (req.outputs[i].index == out)
{
LOG_PRINT_L2("Index " << i << "/" << requested_outputs_count << ": idx " << req.outputs[i].index << " (real " << td.m_global_output_index << "), unlocked " << daemon_resp.outs[i].unlocked << ", key " << daemon_resp.outs[i].key << " (from existing ring)");
- tx_add_fake_output(outs, req.outputs[i].index, daemon_resp.outs[i].key, daemon_resp.outs[i].mask, td.m_global_output_index, daemon_resp.outs[i].unlocked);
+ tx_add_fake_output(outs, req.outputs[i].index, daemon_resp.outs[i].key, daemon_resp.outs[i].mask, td.m_global_output_index, daemon_resp.outs[i].unlocked, valid_public_keys_cache);
found = true;
break;
}
@@ -8546,7 +8597,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
{
size_t i = base + order[o];
LOG_PRINT_L2("Index " << i << "/" << requested_outputs_count << ": idx " << req.outputs[i].index << " (real " << td.m_global_output_index << "), unlocked " << daemon_resp.outs[i].unlocked << ", key " << daemon_resp.outs[i].key);
- tx_add_fake_output(outs, req.outputs[i].index, daemon_resp.outs[i].key, daemon_resp.outs[i].mask, td.m_global_output_index, daemon_resp.outs[i].unlocked);
+ tx_add_fake_output(outs, req.outputs[i].index, daemon_resp.outs[i].key, daemon_resp.outs[i].mask, td.m_global_output_index, daemon_resp.outs[i].unlocked, valid_public_keys_cache);
}
if (outs.back().size() < fake_outputs_count + 1)
{
@@ -8574,6 +8625,8 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
}
// save those outs in the ringdb for reuse
+ std::vector<std::pair<crypto::key_image, std::vector<uint64_t>>> rings;
+ rings.reserve(selected_transfers.size());
for (size_t i = 0; i < selected_transfers.size(); ++i)
{
const size_t idx = selected_transfers[i];
@@ -8583,14 +8636,15 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
ring.reserve(outs[i].size());
for (const auto &e: outs[i])
ring.push_back(std::get<0>(e));
- if (!set_ring(td.m_key_image, ring, false))
- MERROR("Failed to set ring for " << td.m_key_image);
+ rings.push_back(std::make_pair(td.m_key_image, std::move(ring)));
}
+ if (!set_rings(rings, false))
+ MERROR("Failed to set rings");
}
template<typename T>
void wallet2::transfer_selected(const 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,
+ std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, std::unordered_set<crypto::public_key> &valid_public_keys_cache,
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,
bool use_view_tags)
{
@@ -8628,7 +8682,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent
THROW_WALLET_EXCEPTION_IF(subaddr_account != m_transfers[*i].m_subaddr_index.major, error::wallet_internal_error, "the tx uses funds from multiple accounts");
if (outs.empty())
- get_outs(outs, selected_transfers, fake_outputs_count, false); // may throw
+ get_outs(outs, selected_transfers, fake_outputs_count, false, valid_public_keys_cache); // may throw
//prepare inputs
LOG_PRINT_L2("preparing outputs");
@@ -8752,7 +8806,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,
+ std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, std::unordered_set<crypto::public_key> &valid_public_keys_cache,
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, const rct::RCTConfig &rct_config, bool use_view_tags)
{
using namespace cryptonote;
@@ -8846,7 +8900,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry
THROW_WALLET_EXCEPTION_IF(subaddr_account != m_transfers[*i].m_subaddr_index.major, error::wallet_internal_error, "the tx uses funds from multiple accounts");
if (outs.empty())
- get_outs(outs, selected_transfers, fake_outputs_count, all_rct); // may throw
+ get_outs(outs, selected_transfers, fake_outputs_count, all_rct, valid_public_keys_cache); // may throw
//prepare inputs
LOG_PRINT_L2("preparing outputs");
@@ -9671,7 +9725,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
std::vector<std::pair<uint32_t, std::vector<size_t>>> unused_transfers_indices_per_subaddr;
std::vector<std::pair<uint32_t, std::vector<size_t>>> unused_dust_indices_per_subaddr;
uint64_t needed_money;
- uint64_t accumulated_fee, accumulated_outputs, accumulated_change;
+ uint64_t accumulated_fee, accumulated_change;
struct TX {
std::vector<size_t> selected_transfers;
std::vector<cryptonote::tx_destination_entry> dsts;
@@ -9732,6 +9786,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
bulletproof_plus ? 4 : 3
};
const bool use_view_tags = use_fork_rules(get_view_tag_fork(), 0);
+ std::unordered_set<crypto::public_key> valid_public_keys_cache;
const uint64_t base_fee = get_base_fee(priority);
const uint64_t fee_quantization_mask = get_fee_quantization_mask();
@@ -9862,7 +9917,6 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// start with an empty tx
txes.push_back(TX());
accumulated_fee = 0;
- accumulated_outputs = 0;
accumulated_change = 0;
adding_fee = false;
needed_fee = 0;
@@ -9875,8 +9929,6 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// the destination, and one for change.
LOG_PRINT_L2("checking preferred");
std::vector<size_t> preferred_inputs;
- uint64_t rct_outs_needed = 2 * (fake_outs_count + 1);
- rct_outs_needed += 100; // some fudge factor since we don't know how many are locked
if (use_rct)
{
// this is used to build a tx that's 1 or 2 inputs, and 2 outputs, which
@@ -9981,7 +10033,6 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
// add this output to the list to spend
tx.selected_transfers.push_back(idx);
uint64_t available_amount = td.amount();
- accumulated_outputs += available_amount;
// clear any fake outs we'd already gathered, since we'll need a new set
outs.clear();
@@ -10109,10 +10160,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
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,
+ transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra,
test_tx, test_ptx, rct_config, use_view_tags);
else
- transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
+ transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra,
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx, use_view_tags);
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_quantization_mask);
@@ -10134,10 +10185,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
LOG_PRINT_L2("We made a tx, adjusting fee and saving it, we need " << print_money(needed_fee) << " and we have " << print_money(test_ptx.fee));
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,
+ transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra,
test_tx, test_ptx, rct_config, use_view_tags);
else
- transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
+ transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra,
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx, use_view_tags);
txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_quantization_mask);
@@ -10203,6 +10254,7 @@ skip_tx:
tx.selected_transfers, /* const std::list<size_t> selected_transfers */
fake_outs_count, /* CONST size_t fake_outputs_count, */
tx.outs, /* MOD std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, */
+ valid_public_keys_cache,
unlock_time, /* CONST uint64_t unlock_time, */
tx.needed_fee, /* CONST uint64_t fee, */
extra, /* const std::vector<uint8_t>& extra, */
@@ -10215,6 +10267,7 @@ skip_tx:
tx.selected_transfers,
fake_outs_count,
tx.outs,
+ valid_public_keys_cache,
unlock_time,
tx.needed_fee,
extra,
@@ -10332,6 +10385,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
THROW_WALLET_EXCEPTION_IF(tx_weight_one_ring > tx_weight_two_rings, error::wallet_internal_error, "Estimated tx weight with 1 input is larger than with 2 inputs!");
const size_t tx_weight_per_ring = tx_weight_two_rings - tx_weight_one_ring;
const uint64_t fractional_threshold = (base_fee * tx_weight_per_ring) / (use_per_byte_fee ? 1 : 1024);
+ std::unordered_set<crypto::public_key> valid_public_keys_cache;
THROW_WALLET_EXCEPTION_IF(unlocked_balance(subaddr_account, false) == 0, error::wallet_internal_error, "No unlocked balance in the specified account");
@@ -10413,6 +10467,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
hw::device &hwdev = m_account.get_device();
boost::unique_lock<hw::device> hwdev_lock (hwdev);
hw::reset_mode rst(hwdev);
+ std::unordered_set<crypto::public_key> valid_public_keys_cache;
uint64_t accumulated_fee, accumulated_outputs, accumulated_change;
struct TX {
@@ -10515,10 +10570,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " destinations and " <<
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,
+ transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra,
test_tx, test_ptx, rct_config, use_view_tags);
else
- transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
+ transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra,
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx, use_view_tags);
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_quantization_mask);
@@ -10552,10 +10607,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
dt.amount = dt_amount + dt_residue;
}
if (use_rct)
- transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
+ transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra,
test_tx, test_ptx, rct_config, use_view_tags);
else
- transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra,
+ transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra,
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx, use_view_tags);
txBlob = t_serializable_object_to_blob(test_ptx.tx);
needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_quantization_mask);
@@ -10591,10 +10646,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton
cryptonote::transaction test_tx;
pending_tx test_ptx;
if (use_rct) {
- transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra,
+ transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, valid_public_keys_cache, unlock_time, tx.needed_fee, extra,
test_tx, test_ptx, rct_config, use_view_tags);
} else {
- transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra,
+ transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, valid_public_keys_cache, unlock_time, tx.needed_fee, extra,
detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx, use_view_tags);
}
auto txBlob = t_serializable_object_to_blob(test_ptx.tx);
@@ -11356,9 +11411,7 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt
crypto::derivation_to_scalar(found_derivation, n, scalar1);
rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n];
rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG || tx.rct_signatures.type == rct::RCTTypeBulletproofPlus);
- rct::key C = tx.rct_signatures.outPk[n].mask;
- if (rct::is_rct_bulletproof_plus(tx.rct_signatures.type))
- C = rct::scalarmult8(C);
+ const rct::key C = tx.rct_signatures.outPk[n].mask;
rct::key Ctmp;
THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.mask.bytes) != 0, error::wallet_internal_error, "Bad ECDH input mask");
THROW_WALLET_EXCEPTION_IF(sc_check(ecdh_info.amount.bytes) != 0, error::wallet_internal_error, "Bad ECDH input amount");
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 660e6a14b..e9e5bc95e 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -1001,10 +1001,10 @@ private:
uint64_t unlocked_balance_all(bool strict, uint64_t *blocks_to_unlock = NULL, uint64_t *time_to_unlock = NULL);
template<typename T>
void transfer_selected(const 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,
+ std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, std::unordered_set<crypto::public_key> &valid_public_keys_cache,
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, const bool use_view_tags);
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,
+ std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, std::unordered_set<crypto::public_key> &valid_public_keys_cache,
uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, const rct::RCTConfig &rct_config, const bool use_view_tags);
void commit_tx(pending_tx& ptx_vector);
@@ -1558,7 +1558,9 @@ private:
const std::string get_ring_database() const { return m_ring_database; }
bool get_ring(const crypto::key_image &key_image, std::vector<uint64_t> &outs);
bool get_rings(const crypto::hash &txid, std::vector<std::pair<crypto::key_image, std::vector<uint64_t>>> &outs);
+ bool get_rings(const crypto::chacha_key &key, const std::vector<crypto::key_image> &key_images, std::vector<std::vector<uint64_t>> &outs);
bool set_ring(const crypto::key_image &key_image, const std::vector<uint64_t> &outs, bool relative);
+ bool set_rings(const std::vector<std::pair<crypto::key_image, std::vector<uint64_t>>> &rings, bool relative);
bool unset_ring(const std::vector<crypto::key_image> &key_images);
bool unset_ring(const crypto::hash &txid);
bool find_and_save_rings(bool force = true);
@@ -1665,9 +1667,9 @@ private:
void set_unspent(size_t idx);
bool is_spent(const transfer_details &td, bool strict = true) const;
bool is_spent(size_t idx, bool strict = true) const;
- void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, bool rct);
- void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, std::vector<uint64_t> &rct_offsets);
- bool tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& tx_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const;
+ void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, bool rct, std::unordered_set<crypto::public_key> &valid_public_keys_cache);
+ void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, std::vector<uint64_t> &rct_offsets, std::unordered_set<crypto::public_key> &valid_public_keys_cache);
+ bool tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& tx_public_key, const rct::key& mask, uint64_t real_index, bool unlocked, std::unordered_set<crypto::public_key> &valid_public_keys_cache) const;
bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const;
std::vector<size_t> get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const;
void scan_output(const cryptonote::transaction &tx, bool miner_tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs, bool pool);
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 543caac1b..57baf428f 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -4134,7 +4134,8 @@ namespace tools
try
{
res.multisig_info = m_wallet->exchange_multisig_keys(req.password, req.multisig_info);
- if (res.multisig_info.empty())
+ m_wallet->multisig(&ready);
+ if (ready)
{
res.address = m_wallet->get_account().get_public_address_str(m_wallet->nettype());
}
diff --git a/tests/core_tests/bulletproof_plus.cpp b/tests/core_tests/bulletproof_plus.cpp
index c3879e646..f758ac0fb 100644
--- a/tests/core_tests/bulletproof_plus.cpp
+++ b/tests/core_tests/bulletproof_plus.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2020, The Monero Project
+// Copyright (c) 2014-2022, The Monero Project
//
// All rights reserved.
//
diff --git a/tests/core_tests/bulletproof_plus.h b/tests/core_tests/bulletproof_plus.h
index 481044fd4..a90a1b2cb 100644
--- a/tests/core_tests/bulletproof_plus.h
+++ b/tests/core_tests/bulletproof_plus.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2020, The Monero Project
+// Copyright (c) 2014-2022, The Monero Project
//
// All rights reserved.
//
diff --git a/tests/core_tests/multisig.cpp b/tests/core_tests/multisig.cpp
index 758938e5b..3db3d4059 100644
--- a/tests/core_tests/multisig.cpp
+++ b/tests/core_tests/multisig.cpp
@@ -81,9 +81,7 @@ static bool make_multisig_accounts(std::vector<cryptonote::account_base> &accoun
for (std::size_t account_index{0}; account_index < accounts.size(); ++account_index)
{
multisig_accounts[account_index].initialize_kex(threshold, signers, round_msgs);
-
- if (!multisig_accounts[account_index].multisig_is_ready())
- temp_round_msgs[account_index] = multisig_accounts[account_index].get_next_kex_round_msg();
+ temp_round_msgs[account_index] = multisig_accounts[account_index].get_next_kex_round_msg();
}
// perform key exchange rounds
@@ -94,9 +92,7 @@ static bool make_multisig_accounts(std::vector<cryptonote::account_base> &accoun
for (std::size_t account_index{0}; account_index < multisig_accounts.size(); ++account_index)
{
multisig_accounts[account_index].kex_update(round_msgs);
-
- if (!multisig_accounts[account_index].multisig_is_ready())
- temp_round_msgs[account_index] = multisig_accounts[account_index].get_next_kex_round_msg();
+ temp_round_msgs[account_index] = multisig_accounts[account_index].get_next_kex_round_msg();
}
}
@@ -450,8 +446,6 @@ bool gen_multisig_tx_validation_base::generate_with(std::vector<test_event_entry
rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n];
rct::ecdhDecode(ecdh_info, rct::sk2rct(scalar1), tx.rct_signatures.type == rct::RCTTypeBulletproof2 || tx.rct_signatures.type == rct::RCTTypeCLSAG || tx.rct_signatures.type == rct::RCTTypeBulletproofPlus);
rct::key C = tx.rct_signatures.outPk[n].mask;
- if (rct::is_rct_bulletproof_plus(tx.rct_signatures.type))
- C = rct::scalarmult8(C);
rct::addKeys2(Ctmp, ecdh_info.mask, ecdh_info.amount, rct::H);
CHECK_AND_ASSERT_MES(rct::equalKeys(C, Ctmp), false, "Failed to decode amount");
amount += rct::h2d(ecdh_info.amount);
diff --git a/tests/crypto/tests.txt b/tests/crypto/tests.txt
index d387aa09d..32e3b2d09 100644
--- a/tests/crypto/tests.txt
+++ b/tests/crypto/tests.txt
@@ -5481,6 +5481,8 @@ derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 13 e9
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 14 12
derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 15 26
+derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 127 2c
+derive_view_tag 0fc47054f355ced4d67de73bfa12e4c78ff19089548fffa7d07a674741860f97 128 11
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 0 70
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 1 81
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 2 a0
@@ -5489,6 +5491,8 @@ derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 13 0a
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 14 87
derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 15 76
+derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 127 4b
+derive_view_tag a36ba7b4d31349ad278a6df8f77adb76748b59f4929348e67dd92adb9fa174dc 128 39
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 0 93
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 1 67
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 2 9d
@@ -5497,6 +5501,8 @@ derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 13 cf
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 14 ef
derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 15 10
+derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 127 fc
+derive_view_tag 7498d5bf0b69e08653f6d420a17f866dd2bd490ab43074f46065cb501fe7e2d8 128 73
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 0 90
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 1 5a
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 2 de
@@ -5505,6 +5511,8 @@ derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 13 52
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 14 6f
derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 15 eb
+derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 127 93
+derive_view_tag fe7770c4b076e95ddb8026affcfab39d31c7c4a2266e0e25e343bc4badc907d0 128 dc
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 0 c6
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 1 60
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 2 f0
@@ -5513,6 +5521,8 @@ derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 13 42
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 14 b2
derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 15 61
+derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 127 c3
+derive_view_tag ea9337d0ddf480abdc4fc56a0cb223702729cb230ae7b9de50243ad25ce90e8d 128 0a
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 0 4c
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 1 9b
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 2 64
@@ -5521,6 +5531,8 @@ derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 13 24
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 14 ea
derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 15 3b
+derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 127 47
+derive_view_tag 25d538315bcb81aff9574189ea65f418aeb0392f5cbbc84cd8a33c7ade31ef0a 128 5a
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 0 74
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 1 77
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 2 a9
@@ -5529,3 +5541,5 @@ derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 13 05
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 14 ca
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 15 00
+derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 127 a6
+derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 128 0d
diff --git a/tests/functional_tests/multisig.py b/tests/functional_tests/multisig.py
index c1b50b1e2..89cb2fdc7 100755
--- a/tests/functional_tests/multisig.py
+++ b/tests/functional_tests/multisig.py
@@ -125,17 +125,18 @@ class MultisigTest():
for i in range(N_total):
res = self.wallet[i].is_multisig()
assert res.multisig == True
- assert res.ready == (M_threshold == N_total)
+ assert not res.ready
assert res.threshold == M_threshold
assert res.total == N_total
while True:
- n_empty = 0
- for i in range(len(next_stage)):
- if len(next_stage[i]) == 0:
- n_empty += 1
- assert n_empty == 0 or n_empty == len(next_stage)
- if n_empty == len(next_stage):
+ n_ready = 0
+ for i in range(N_total):
+ res = self.wallet[i].is_multisig()
+ if res.ready == True:
+ n_ready += 1
+ assert n_ready == 0 or n_ready == N_total
+ if n_ready == N_total:
break
info = next_stage
next_stage = []
@@ -162,54 +163,72 @@ class MultisigTest():
'peeled mixture ionic radar utopia puddle buying illness nuns gadget river spout cavernous bounced paradise drunk looking cottage jump tequila melting went winter adjust spout',
'dilute gutter certain antics pamphlet macro enjoy left slid guarded bogeys upload nineteen bomb jubilee enhanced irritate turnip eggs swung jukebox loudly reduce sedan slid',
]
- info = []
- wallet = [None, None, None]
- for i in range(3):
- wallet[i] = Wallet(idx = i)
- try: wallet[i].close_wallet()
+ info2of2 = []
+ wallet2of2 = [None, None]
+ for i in range(2):
+ wallet2of2[i] = Wallet(idx = i)
+ try: wallet2of2[i].close_wallet()
except: pass
- res = wallet[i].restore_deterministic_wallet(seed = seeds[i])
- res = wallet[i].is_multisig()
+ res = wallet2of2[i].restore_deterministic_wallet(seed = seeds[i])
+ res = wallet2of2[i].is_multisig()
assert not res.multisig
- res = wallet[i].prepare_multisig()
+ res = wallet2of2[i].prepare_multisig()
assert len(res.multisig_info) > 0
- info.append(res.multisig_info)
-
- for i in range(3):
- ok = False
- try: res = wallet[i].exchange_multisig_keys(info)
- except: ok = True
- assert ok
- res = wallet[i].is_multisig()
- assert not res.multisig
-
- res = wallet[0].make_multisig(info[0:2], 2)
- res = wallet[0].is_multisig()
+ info2of2.append(res.multisig_info)
+
+ kex_info = []
+ res = wallet2of2[0].make_multisig(info2of2, 2)
+ kex_info.append(res.multisig_info)
+ res = wallet2of2[1].make_multisig(info2of2, 2)
+ kex_info.append(res.multisig_info)
+ res = wallet2of2[0].exchange_multisig_keys(kex_info)
+ res = wallet2of2[0].is_multisig()
assert res.multisig
assert res.ready
ok = False
- try: res = wallet[0].prepare_multisig()
+ try: res = wallet2of2[0].prepare_multisig()
except: ok = True
assert ok
ok = False
- try: res = wallet[0].make_multisig(info[0:2], 2)
+ try: res = wallet2of2[0].make_multisig(info2of2, 2)
except: ok = True
assert ok
- res = wallet[1].make_multisig(info, 2)
- res = wallet[1].is_multisig()
+ info2of3 = []
+ wallet2of3 = [None, None, None]
+ for i in range(3):
+ wallet2of3[i] = Wallet(idx = i)
+ try: wallet2of3[i].close_wallet()
+ except: pass
+ res = wallet2of3[i].restore_deterministic_wallet(seed = seeds[i])
+ res = wallet2of3[i].is_multisig()
+ assert not res.multisig
+ res = wallet2of3[i].prepare_multisig()
+ assert len(res.multisig_info) > 0
+ info2of3.append(res.multisig_info)
+
+ for i in range(3):
+ ok = False
+ try: res = wallet2of3[i].exchange_multisig_keys(info)
+ except: ok = True
+ assert ok
+ res = wallet2of3[i].is_multisig()
+ assert not res.multisig
+
+ res = wallet2of3[1].make_multisig(info2of3, 2)
+ res = wallet2of3[1].is_multisig()
assert res.multisig
assert not res.ready
ok = False
- try: res = wallet[1].prepare_multisig()
+ try: res = wallet2of3[1].prepare_multisig()
except: ok = True
assert ok
ok = False
- try: res = wallet[1].make_multisig(info[0:2], 2)
+ try: res = wallet2of3[1].make_multisig(info2of3[0:2], 2)
except: ok = True
assert ok
diff --git a/tests/performance_tests/bulletproof_plus.h b/tests/performance_tests/bulletproof_plus.h
index 9aad61065..00dfc46f8 100644
--- a/tests/performance_tests/bulletproof_plus.h
+++ b/tests/performance_tests/bulletproof_plus.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2020, The Monero Project
+// Copyright (c) 2014-2022, The Monero Project
//
// All rights reserved.
//
diff --git a/tests/performance_tests/derive_view_tag.h b/tests/performance_tests/derive_view_tag.h
index ee4efb16d..3179ea0b2 100644
--- a/tests/performance_tests/derive_view_tag.h
+++ b/tests/performance_tests/derive_view_tag.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2021, The Monero Project
+// Copyright (c) 2014-2022, The Monero Project
//
// All rights reserved.
//
diff --git a/tests/performance_tests/out_can_be_to_acc.h b/tests/performance_tests/out_can_be_to_acc.h
index 86e236b7b..952f132a5 100644
--- a/tests/performance_tests/out_can_be_to_acc.h
+++ b/tests/performance_tests/out_can_be_to_acc.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2014-2021, The Monero Project
+// Copyright (c) 2014-2022, The Monero Project
//
// All rights reserved.
//
diff --git a/tests/unit_tests/bulletproofs_plus.cpp b/tests/unit_tests/bulletproofs_plus.cpp
index a64320233..5d5c0170c 100644
--- a/tests/unit_tests/bulletproofs_plus.cpp
+++ b/tests/unit_tests/bulletproofs_plus.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, The Monero Project
+// Copyright (c) 2017-2022, The Monero Project
//
// All rights reserved.
//
diff --git a/tests/unit_tests/multisig.cpp b/tests/unit_tests/multisig.cpp
index deee10aed..5ddd78955 100644
--- a/tests/unit_tests/multisig.cpp
+++ b/tests/unit_tests/multisig.cpp
@@ -120,7 +120,7 @@ static void check_results(const std::vector<std::string> &intermediate_infos,
for (size_t i = 0; i < wallets.size(); ++i)
{
- EXPECT_TRUE(intermediate_infos[i].empty());
+ EXPECT_TRUE(!intermediate_infos[i].empty());
bool ready;
uint32_t threshold, total;
EXPECT_TRUE(wallets[i].multisig(&ready, &threshold, &total));
@@ -171,7 +171,7 @@ static void make_wallets(std::vector<tools::wallet2>& wallets, unsigned int M)
{
ASSERT_TRUE(wallets.size() > 1 && wallets.size() <= KEYS_COUNT);
ASSERT_TRUE(M <= wallets.size());
- std::uint32_t rounds_required = multisig::multisig_kex_rounds_required(wallets.size(), M);
+ std::uint32_t total_rounds_required = multisig::multisig_kex_rounds_required(wallets.size(), M) + 1;
std::uint32_t rounds_complete{0};
// initialize wallets, get first round multisig kex msgs
@@ -203,18 +203,17 @@ static void make_wallets(std::vector<tools::wallet2>& wallets, unsigned int M)
++rounds_complete;
// perform kex rounds until kex is complete
- while (!intermediate_infos[0].empty())
+ bool ready;
+ wallets[0].multisig(&ready);
+ while (!ready)
{
- bool ready{false};
- wallets[0].multisig(&ready);
- EXPECT_FALSE(ready);
-
intermediate_infos = exchange_round(wallets, intermediate_infos);
+ wallets[0].multisig(&ready);
++rounds_complete;
}
- EXPECT_EQ(rounds_required, rounds_complete);
+ EXPECT_EQ(total_rounds_required, rounds_complete);
check_results(intermediate_infos, wallets, M);
}
diff --git a/tests/unit_tests/scaling_2021.cpp b/tests/unit_tests/scaling_2021.cpp
index 9e8d15544..36a77c107 100644
--- a/tests/unit_tests/scaling_2021.cpp
+++ b/tests/unit_tests/scaling_2021.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2019-2020, The Monero Project
+// Copyright (c) 2019-2022, The Monero Project
//
// All rights reserved.
//