From 9b19be69a11d0698e78418d2b11b22b840f82fd6 Mon Sep 17 00:00:00 2001 From: wowario Date: Sun, 2 Dec 2018 17:29:51 +0300 Subject: [PATCH 01/20] initial commit --- CMakeLists.txt | 11 +- Makefile | 6 +- README.md | 708 ++---------- src/blockchain_db/blockchain_db.cpp | 575 ---------- src/blockchain_db/lmdb/db_lmdb.cpp | 4 +- src/blockchain_utilities/CMakeLists.txt | 12 +- .../blockchain_ancestry.cpp | 4 +- .../blockchain_blackball.cpp | 6 +- src/blockchain_utilities/blockchain_depth.cpp | 4 +- .../blockchain_export.cpp | 4 +- .../blockchain_import.cpp | 4 +- src/blockchain_utilities/blockchain_usage.cpp | 4 +- src/checkpoints/checkpoints.cpp | 104 +- src/common/dns_utils.cpp | 2 - src/common/updates.cpp | 6 +- .../cryptonote_basic_impl.cpp | 2 +- .../cryptonote_format_utils.cpp | 37 +- src/cryptonote_basic/difficulty.cpp | 146 +++ src/cryptonote_basic/difficulty.h | 4 + src/cryptonote_config.h | 72 +- src/cryptonote_core/blockchain.cpp | 193 ++-- src/cryptonote_core/cryptonote_core.cpp | 4 +- .../cryptonote_protocol_handler.inl | 4 +- src/daemon/CMakeLists.txt | 2 +- src/daemon/command_parser_executor.cpp | 2 +- src/daemon/command_server.cpp | 2 +- src/daemon/executor.cpp | 4 +- src/daemon/main.cpp | 6 +- src/daemon/rpc_command_executor.cpp | 4 +- src/debug_utilities/cn_deserialize.cpp | 2 +- src/gen_multisig/CMakeLists.txt | 2 +- src/p2p/net_node.h | 5 +- src/p2p/net_node.inl | 21 +- src/simplewallet/CMakeLists.txt | 2 +- src/simplewallet/simplewallet.cpp | 86 +- src/version.cpp.in | 4 +- src/wallet/CMakeLists.txt | 2 +- src/wallet/api/wallet.cpp | 2 +- src/wallet/wallet2.cpp | 64 +- src/wallet/wallet_args.cpp | 6 +- src/wallet/wallet_rpc_server.cpp | 16 +- tests/README.md | 16 +- tests/difficulty/data.txt | 1000 ----------------- tests/difficulty/export_data.pl | 54 + tests/difficulty/generate-data | 12 +- tests/difficulty/wow-data.txt | 1000 +++++++++++++++++ tests/difficulty/wow-emission | 86 ++ tests/difficulty/wownero-data.txt | 478 ++++++++ tests/difficulty/wownero-emission.txt | 26 + 49 files changed, 2225 insertions(+), 2595 deletions(-) delete mode 100644 tests/difficulty/data.txt create mode 100644 tests/difficulty/export_data.pl create mode 100644 tests/difficulty/wow-data.txt create mode 100755 tests/difficulty/wow-emission create mode 100644 tests/difficulty/wownero-data.txt create mode 100644 tests/difficulty/wownero-emission.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index f1f894dd7..38a63fe5f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -175,6 +175,15 @@ else() message(STATUS "Building without build tag") endif() +# Update and init submodules by default +if(NOT MANUAL_SUBMODULES) + find_package(Git) + if(GIT_FOUND) + message(STATUS "Initializing submodules") + execute_process(COMMAND git "submodule" "update" "--init" "--recursive" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + endif() +endif() + if(NOT MANUAL_SUBMODULES) find_package(Git) if(GIT_FOUND) @@ -236,7 +245,7 @@ endif() set_property(GLOBAL PROPERTY USE_FOLDERS ON) enable_testing() -option(BUILD_DOCUMENTATION "Build the Doxygen documentation." ON) +option(BUILD_DOCUMENTATION "Build the Doxygen documentation." OFF) option(BUILD_TESTS "Build tests." OFF) # Check whether we're on a 32-bit or 64-bit system diff --git a/Makefile b/Makefile index f9270ebf2..dae0f44e7 100644 --- a/Makefile +++ b/Makefile @@ -77,7 +77,7 @@ debug-static-win32: cmake-release: mkdir -p $(builddir)/release - cd $(builddir)/release && cmake -D CMAKE_BUILD_TYPE=Release $(topdir) + cd $(builddir)/release && cmake -D BUILD_TESTS=OFF -D CMAKE_BUILD_TYPE=Release $(topdir) release: cmake-release cd $(builddir)/release && $(MAKE) @@ -88,11 +88,11 @@ release-test: release-all: mkdir -p $(builddir)/release - cd $(builddir)/release && cmake -D BUILD_TESTS=ON -D CMAKE_BUILD_TYPE=release $(topdir) && $(MAKE) + cd $(builddir)/release && cmake -D BUILD_TESTS=OFF -D CMAKE_BUILD_TYPE=release $(topdir) && $(MAKE) release-static: mkdir -p $(builddir)/release - cd $(builddir)/release && cmake -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release $(topdir) && $(MAKE) + cd $(builddir)/release && cmake -D BUILD_TESTS=OFF -D STATIC=ON -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release $(topdir) && $(MAKE) coverage: mkdir -p $(builddir)/debug diff --git a/README.md b/README.md index 284a0377d..22a4d438d 100644 --- a/README.md +++ b/README.md @@ -1,566 +1,107 @@ -# Monero +# ~~Mo~~Wownero - Such privacy! Many coins! Wow! 🐕 Copyright (c) 2014-2018 The Monero Project. Portions Copyright (c) 2012-2013 The Cryptonote developers. -## Development resources +## Resources -- Web: [getmonero.org](https://getmonero.org) -- Forum: [forum.getmonero.org](https://forum.getmonero.org) -- Mail: [dev@getmonero.org](mailto:dev@getmonero.org) -- GitHub: [https://github.com/monero-project/monero](https://github.com/monero-project/monero) -- IRC: [#monero-dev on Freenode](https://webchat.freenode.net/?randomnick=1&channels=%23monero-dev&prompt=1&uio=d4) +- Web: [wownero.org](http://wownero.org) +- Twitter: [@w0wn3r0](https://twitter.com/w0wn3r0) +- Discord: [discord.gg/sQt74ep](https://discord.gg/sQt74ep) +- Reddit: [/r/wownero](https://www.reddit.com/r/wownero) +- Mail: [wownero@protonmail.com](mailto:wownero@protonmail.com) +- GitHub: [https://github.com/wownero/wownero](https://github.com/wownero/wownero) +- IRC: [#wownero on Freenode](https://kiwiirc.com/client/irc.freenode.net/?nick=suchchatter|?#wownero) +- Bitmessage Chan: wownero (`BM-2cSzWtrj2pzLva9GF1Jp2TYsnLjrnJpvba`) +- Network stats: [https://freeboard.io/board/c8mM2c](https://freeboard.io/board/c8mM2c) +- Wownero Funding System: [https://funding.wownero.com](https://funding.wownero.com) +- Keybase Chat Group: [https://keybase.io/team/wownero](https://keybase.io/team/wownero) -## Vulnerability response - -- Our [Vulnerability Response Process](https://github.com/monero-project/meta/blob/master/VULNERABILITY_RESPONSE_PROCESS.md) encourages responsible disclosure -- We are also available via [HackerOne](https://hackerone.com/monero) - -## Announcements - -- You can subscribe to an [announcement listserv](https://lists.getmonero.org) to get critical announcements from the Monero core team. The announcement list can be very helpful for knowing when software updates are needed. - -## Build - -### IMPORTANT - -These builds are of the master branch, which is used for active development and can be either unstable or incompatible with release software. Please compile release branches. - -| Operating System | Processor | Status | -| --------------------- | -------- |--------| -| Ubuntu 16.04 | i686 | [![Ubuntu 16.04 i686](https://build.getmonero.org/png?builder=monero-static-ubuntu-i686)](https://build.getmonero.org/builders/monero-static-ubuntu-i686) -| Ubuntu 16.04 | amd64 | [![Ubuntu 16.04 amd64](https://build.getmonero.org/png?builder=monero-static-ubuntu-amd64)](https://build.getmonero.org/builders/monero-static-ubuntu-amd64) -| Ubuntu 16.04 | armv7 | [![Ubuntu 16.04 armv7](https://build.getmonero.org/png?builder=monero-static-ubuntu-arm7)](https://build.getmonero.org/builders/monero-static-ubuntu-arm7) -| Debian Stable | armv8 | [![Debian armv8](https://build.getmonero.org/png?builder=monero-static-debian-armv8)](https://build.getmonero.org/builders/monero-static-debian-armv8) -| OSX 10.10 | amd64 | [![OSX 10.10 amd64](https://build.getmonero.org/png?builder=monero-static-osx-10.10)](https://build.getmonero.org/builders/monero-static-osx-10.10) -| OSX 10.11 | amd64 | [![OSX 10.11 amd64](https://build.getmonero.org/png?builder=monero-static-osx-10.11)](https://build.getmonero.org/builders/monero-static-osx-10.11) -| OSX 10.12 | amd64 | [![OSX 10.12 amd64](https://build.getmonero.org/png?builder=monero-static-osx-10.12)](https://build.getmonero.org/builders/monero-static-osx-10.12) -| FreeBSD 11 | amd64 | [![FreeBSD 11 amd64](https://build.getmonero.org/png?builder=monero-static-freebsd64)](https://build.getmonero.org/builders/monero-static-freebsd64) -| DragonFly BSD 4.6 | amd64 | [![DragonFly BSD amd64](https://build.getmonero.org/png?builder=monero-static-dragonflybsd-amd64)](https://build.getmonero.org/builders/monero-static-dragonflybsd-amd64) -| Windows (MSYS2/MinGW) | i686 | [![Windows (MSYS2/MinGW) i686](https://build.getmonero.org/png?builder=monero-static-win32)](https://build.getmonero.org/builders/monero-static-win32) -| Windows (MSYS2/MinGW) | amd64 | [![Windows (MSYS2/MinGW) amd64](https://build.getmonero.org/png?builder=monero-static-win64)](https://build.getmonero.org/builders/monero-static-win64) - -## Coverage - -| Type | Status | -|-----------|--------| -| Coverity | [![Coverity Status](https://scan.coverity.com/projects/9657/badge.svg)](https://scan.coverity.com/projects/9657/) -| Coveralls | [![Coveralls Status](https://coveralls.io/repos/github/monero-project/monero/badge.svg?branch=master)](https://coveralls.io/github/monero-project/monero?branch=master) -| License | [![License](https://img.shields.io/badge/license-BSD3-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) +Blockchain Explorers +- https://explore.wownero.com +- https://explorer.wowkira.com +- http://explorer.wowne.ro +- http://wow4edxxxudchgj7.onion +- http://wow5eqtzqvsg5jctqzg5g7uk3u62sfqiacj5x6lo4by7bvnj6jkvubyd.onion ## Introduction -Monero is a private, secure, untraceable, decentralised digital currency. You are your bank, you control your funds, and nobody can trace your transfers unless you allow them to do so. - -**Privacy:** Monero uses a cryptographically sound system to allow you to send and receive funds without your transactions being easily revealed on the blockchain (the ledger of transactions that everyone has). This ensures that your purchases, receipts, and all transfers remain absolutely private by default. - -**Security:** Using the power of a distributed peer-to-peer consensus network, every transaction on the network is cryptographically secured. Individual wallets have a 25 word mnemonic seed that is only displayed once, and can be written down to backup the wallet. Wallet files are encrypted with a passphrase to ensure they are useless if stolen. - -**Untraceability:** By taking advantage of ring signatures, a special property of a certain type of cryptography, Monero is able to ensure that transactions are not only untraceable, but have an optional measure of ambiguity that ensures that transactions cannot easily be tied back to an individual user or computer. - -## About this project - -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. - -**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. - -## Supporting the project - -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: `44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A` (viewkey: `f359631075708155cc3d92a32b75a7d02a5dcf27756707b47a2b31b21c389501`) - -The Bitcoin donation address is: `1KTexdemPdxSBcG55heUuTjDRYqbC5ZL8H` - -Core development funding and/or some supporting services are also graciously provided by sponsors: - -[](https://mymonero.com) -[](https://kitware.com) -[](https://dome9.com) -[](https://araxis.com) -[](https://www.jetbrains.com/) -[](https://www.navicat.com/) -[](https://www.symas.com/) - -There are also several mining pools that kindly donate a portion of their fees, [a list of them can be found on our Bitcointalk post](https://bitcointalk.org/index.php?topic=583449.0). - -## License - -See [LICENSE](LICENSE). - -## Contributing - -If you want to help out, see [CONTRIBUTING](CONTRIBUTING.md) for a set of guidelines. +Wownero is a privacy-centric memecoin that was fairly launched on April 1, 2018 with no pre-mine, stealth-mine or ICO. Wownero has a maximum supply of around 184 million WOW with a slow and steady emission over 50 years. It is a fork of Monero, but with its own genesis block, so there is no degradation of privacy due to ring signatures using different participants for the same tx outputs on opposing forks. ## Scheduled software upgrades -Monero uses a fixed-schedule software upgrade (hard fork) mechanism to implement new features. This means that users of Monero (end users and service providers) should run current versions and upgrade their software on a regular schedule. Software upgrades occur during the months of April and October. The required software for these upgrades will be available prior to the scheduled date. Please check the repository prior to this date for the proper Monero software version. Below is the historical schedule and the projected schedule for the next upgrade. +Wownero uses a fixed-schedule software upgrade (hard fork) mechanism to implement new features. This means that users of Wownero (end users and service providers) should run current versions and upgrade their software on a regular schedule. The required software for these upgrades will be available prior to the scheduled date. Please check the repository prior to this date for the proper Wownero software version. Below is the historical schedule and the projected schedule for the next upgrade. Dates are provided in the format YYYY-MM-DD. -| Software upgrade block height | Date | Fork version | Minimum Monero version | Recommended Monero version | Details | + +| Software upgrade block height | Date | Release Name | Minimum Wownero version | Recommended Wownero version | Details | | ------------------------------ | -----------| ----------------- | ---------------------- | -------------------------- | ---------------------------------------------------------------------------------- | -| 1009827 | 2016-03-22 | v2 | v0.9.4 | v0.9.4 | Allow only >= ringsize 3, blocktime = 120 seconds, fee-free blocksize 60 kb | -| 1141317 | 2016-09-21 | v3 | v0.9.4 | v0.10.0 | Splits coinbase into denominations | -| 1220516 | 2017-01-05 | v4 | v0.10.1 | v0.10.2.1 | Allow normal and RingCT transactions | -| 1288616 | 2017-04-15 | v5 | v0.10.3.0 | v0.10.3.1 | Adjusted minimum blocksize and fee algorithm | -| 1400000 | 2017-09-16 | v6 | v0.11.0.0 | v0.11.0.0 | Allow only RingCT transactions, allow only >= ringsize 5 | -| 1546000 | 2018-04-06 | v7 | v0.12.0.0 | v0.12.3.0 | Cryptonight variant 1, ringsize >= 7, sorted inputs -| 1685555 | 2018-10-18 | v8 | v0.13.0.0 | v0.13.0.0 | max transaction size at half the penalty free block size, bulletproofs enabled, cryptonight variant 2, fixed ringsize [11](https://youtu.be/KOO5S4vxi0o) -| 1686275 | 2018-10-19 | v9 | v0.13.0.0 | v0.13.0.0 | bulletproofs required -| 1788000 | 2019-03-09 | v10 | v0.14.0.0 | v0.14.0.0 | Cryptonight-R PoW, new block weight algorithm, slightly more efficient RingCT format -| 1788720 | 2019-03-10 | v11 | v0.14.0.0 | v0.14.0.0 | forbid old RingCT transaction format +| 1 | 2018-04-01 | Awesome Akita | v0.1.0.0 | v0.1.0.0 | Cryptonight variant 1, ringsize >= 8, sorted inputs +| 6969 | 2018-04-24 | Busty Brazzers | v0.2.0.0 | v0.2.0.0 | Bulletproofs, LWMA difficulty algorithm, ringsize >= 10, reduce unlock to 4 +| 53666 | 2018-10-06 | Cool Cage | v0.3.0.0 | v0.3.1.3 | Cryptonight variant 2, LWMA v2, ringsize = 22, MMS +| 63469 | 2018-11-11 | Dank Doge | v0.4.0.0 | v0.4.0.0 | LWMA v4 +| 81769 | 2019-02-19 | Erotic EggplantEmoji | v0.5.0.0 | v0.5.0.2 | Cryptonight/wow, LWMA v1 with N=144, Updated Bulletproofs, Fee Per Byte, Auto-churn X's indicate that these details have not been determined as of commit date. -## Release staging schedule and protocol +## Release staging and Contributing -Approximately three months prior to a scheduled software upgrade, a branch from Master will be created with the new release version tag. Pull requests that address bugs should then be made to both Master and the new release branch. Pull requests that require extensive review and testing (generally, optimizations and new features) should *not* be made to the release branch. +**Anyone is welcome to contribute to Wownero's codebase!** -## Compiling Monero from source +If you have a fix or code change, feel free to submit it as a pull request. Ahead of a scheduled software upgrade, a development branch will be created with the new release version tag. Pull requests that address bugs should be made to Master. Pull requests that require review and testing (generally, optimizations and new features) should be made to the development branch. All pull requests will be considered safe until the US dollar valuation of 1 Wownero equals $1000. After this valuation has been reached, more research will be needed to introduce experimental cryptography and/or code into the codebase. -### Dependencies - -The following table summarizes the tools and libraries required to build. A -few of the libraries are also included in this repository (marked as -"Vendored"). By default, the build uses the library installed on the system, -and ignores the vendored sources. However, if no library is found installed on -the system, then the vendored source will be built and used. The vendored -sources are also used for statically-linked builds because distribution -packages often include only shared library binaries (`.so`) but not static -library archives (`.a`). - -| Dep | Min. version | Vendored | Debian/Ubuntu pkg | Arch pkg | Fedora | Optional | Purpose | -| ------------ | ------------- | -------- | ------------------ | ------------ | ----------------- | -------- | -------------- | -| GCC | 4.7.3 | NO | `build-essential` | `base-devel` | `gcc` | NO | | -| CMake | 3.5 | NO | `cmake` | `cmake` | `cmake` | NO | | -| pkg-config | any | NO | `pkg-config` | `base-devel` | `pkgconf` | NO | | -| Boost | 1.58 | NO | `libboost-all-dev` | `boost` | `boost-devel` | NO | C++ libraries | -| OpenSSL | basically any | NO | `libssl-dev` | `openssl` | `openssl-devel` | NO | sha256 sum | -| libzmq | 3.0.0 | NO | `libzmq3-dev` | `zeromq` | `cppzmq-devel` | NO | ZeroMQ library | -| OpenPGM | ? | NO | `libpgm-dev` | `libpgm` | `openpgm-devel` | NO | For ZeroMQ | -| libunbound | 1.4.16 | YES | `libunbound-dev` | `unbound` | `unbound-devel` | NO | DNS resolver | -| libsodium | ? | NO | `libsodium-dev` | `libsodium` | `libsodium-devel` | NO | cryptography | -| libunwind | any | NO | `libunwind8-dev` | `libunwind` | `libunwind-devel` | YES | Stack traces | -| liblzma | any | NO | `liblzma-dev` | `xz` | `xz-devel` | YES | For libunwind | -| libreadline | 6.3.0 | NO | `libreadline6-dev` | `readline` | `readline-devel` | YES | Input editing | -| ldns | 1.6.17 | NO | `libldns-dev` | `ldns` | `ldns-devel` | YES | SSL toolkit | -| expat | 1.1 | NO | `libexpat1-dev` | `expat` | `expat-devel` | YES | XML parsing | -| GTest | 1.5 | YES | `libgtest-dev`^ | `gtest` | `gtest-devel` | YES | Test suite | -| Doxygen | any | NO | `doxygen` | `doxygen` | `doxygen` | YES | Documentation | -| Graphviz | any | NO | `graphviz` | `graphviz` | `graphviz` | YES | Documentation | - - -[^] 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 && sudo mv libg* /usr/lib/ ``` - -Debian / Ubuntu one liner for all dependencies -``` sudo apt update && sudo apt install build-essential cmake pkg-config libboost-all-dev libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev libreadline6-dev libldns-dev libexpat1-dev doxygen graphviz libpgm-dev``` - -### Cloning the repository - -Clone recursively to pull-in needed submodule(s): - -`$ git clone --recursive https://github.com/monero-project/monero` - -If you already have a repo cloned, initialize and update: - -`$ cd monero && git submodule init && git submodule update` - -### Build instructions - -Monero uses the CMake build system and a top-level [Makefile](Makefile) that -invokes cmake commands as needed. - -#### On Linux and OS X - -* Install the dependencies -* Change to the root of the source code directory, change to the most recent release branch, and build: - - cd monero - git checkout v0.14.0.1 - make - - *Optional*: If your machine has several cores and enough memory, enable - parallel build by running `make -j` instead of `make`. For - this to be worthwhile, the machine should have one core and about 2GB of RAM - available per thread. - - *Note*: If cmake can not find zmq.hpp file on OS X, installing `zmq.hpp` from - https://github.com/zeromq/cppzmq to `/usr/local/include` should fix that error. - - *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 - both unstable and incompatible with release software, though testing is always - encouraged. - -* The resulting executables can be found in `build/release/bin` - -* Add `PATH="$PATH:$HOME/monero/build/release/bin"` to `.profile` - -* Run Monero with `monerod --detach` - -* **Optional**: build and run the test suite to verify the binaries: - - make release-test - - *NOTE*: `core_tests` test may take a few hours to complete. - -* **Optional**: to build binaries suitable for debugging: - - make debug - -* **Optional**: to build statically-linked binaries: - - make release-static - -Dependencies need to be built with -fPIC. Static libraries usually aren't, so you may have to build them yourself with -fPIC. Refer to their documentation for how to build them. - -* **Optional**: build documentation in `doc/html` (omit `HAVE_DOT=YES` if `graphviz` is not installed): - - HAVE_DOT=YES doxygen Doxyfile - -#### On the Raspberry Pi - -Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch (2017-09-07 or later) from https://www.raspberrypi.org/downloads/raspbian/. If you are using Raspian Jessie, [please see note in the following section](#note-for-raspbian-jessie-users). - -* `apt-get update && apt-get upgrade` to install all of the latest software - -* Install the dependencies for Monero from the 'Debian' column in the table above. - -* Increase the system swap size: -``` - sudo /etc/init.d/dphys-swapfile stop - sudo nano /etc/dphys-swapfile - CONF_SWAPSIZE=1024 - sudo /etc/init.d/dphys-swapfile start -``` -* Clone monero and checkout most recent release version: -``` - git clone https://github.com/monero-project/monero.git - cd monero - git checkout tags/v0.14.0.1 -``` -* Build: -``` - make release -``` -* Wait 4-6 hours - -* The resulting executables can be found in `build/release/bin` - -* Add `PATH="$PATH:$HOME/monero/build/release/bin"` to `.profile` - -* Run Monero with `monerod --detach` - -* You may wish to reduce the size of the swap file after the build has finished, and delete the boost directory from your home directory - -#### *Note for Raspbian Jessie users:* - -If you are using the older Raspbian Jessie image, compiling Monero is a bit more complicated. The version of Boost available in the Debian Jessie repositories is too old to use with Monero, and thus you must compile a newer version yourself. The following explains the extra steps, and has been tested on a Raspberry Pi 2 with a clean install of minimal Raspbian Jessie. - -* As before, `apt-get update && apt-get upgrade` to install all of the latest software, and increase the system swap size - -``` - sudo /etc/init.d/dphys-swapfile stop - sudo nano /etc/dphys-swapfile - CONF_SWAPSIZE=1024 - sudo /etc/init.d/dphys-swapfile start -``` - -* Then, install the dependencies for Monero except `libunwind` and `libboost-all-dev` - -* Install the latest version of boost (this may first require invoking `apt-get remove --purge libboost*` to remove a previous version if you're not using a clean install): -``` - cd - wget https://sourceforge.net/projects/boost/files/boost/1.64.0/boost_1_64_0.tar.bz2 - tar xvfo boost_1_64_0.tar.bz2 - cd boost_1_64_0 - ./bootstrap.sh - sudo ./b2 -``` -* Wait ~8 hours -``` - sudo ./bjam cxxflags=-fPIC cflags=-fPIC -a install -``` -* Wait ~4 hours - -* From here, follow the [general Raspberry Pi instructions](#on-the-raspberry-pi) from the "Clone monero and checkout most recent release version" step. - -#### On Windows: - -Binaries for Windows are built on Windows using the MinGW toolchain within -[MSYS2 environment](https://www.msys2.org). The MSYS2 environment emulates a -POSIX system. The toolchain runs within the environment and *cross-compiles* -binaries that can run outside of the environment as a regular Windows -application. - -**Preparing the build environment** - -* Download and install the [MSYS2 installer](https://www.msys2.org), either the 64-bit or the 32-bit package, depending on your system. -* Open the MSYS shell via the `MSYS2 Shell` shortcut -* Update packages using pacman: - - pacman -Syuu - -* Exit the MSYS shell using Alt+F4 -* Edit the properties for the `MSYS2 Shell` shortcut changing "msys2_shell.bat" to "msys2_shell.cmd -mingw64" for 64-bit builds or "msys2_shell.cmd -mingw32" for 32-bit builds -* Restart MSYS shell via modified shortcut and update packages again using pacman: - - pacman -Syuu - - -* Install dependencies: - - To build for 64-bit Windows: - - pacman -S mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-hidapi - - To build for 32-bit Windows: - - pacman -S mingw-w64-i686-toolchain make mingw-w64-i686-cmake mingw-w64-i686-boost mingw-w64-i686-openssl mingw-w64-i686-zeromq mingw-w64-i686-libsodium mingw-w64-i686-hidapi - -* Open the MingW shell via `MinGW-w64-Win64 Shell` shortcut on 64-bit Windows - or `MinGW-w64-Win64 Shell` shortcut on 32-bit Windows. Note that if you are - running 64-bit Windows, you will have both 64-bit and 32-bit MinGW shells. - -**Cloning** - -* To git clone, run: - - git clone --recursive https://github.com/monero-project/monero.git - -**Building** - -* Change to the cloned directory, run: - - 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.14.0.1'. If you dont care about the version and just want binaries from master, skip this step: - - git checkout v0.14.0.1 - -* If you are on a 64-bit system, run: - - make release-static-win64 - -* If you are on a 32-bit system, run: - - make release-static-win32 - -* The resulting executables can be found in `build/release/bin` - -* **Optional**: to build Windows binaries suitable for debugging on a 64-bit system, run: - - make debug-static-win64 - -* **Optional**: to build Windows binaries suitable for debugging on a 32-bit system, run: - - make debug-static-win32 - -* The resulting executables can be found in `build/debug/bin` - -### On FreeBSD: - -The project can be built from scratch by following instructions for Linux above. If you are running monero in a jail you need to add the flag: `allow.sysvipc=1` to your jail configuration, otherwise lmdb will throw the error message: `Failed to open lmdb environment: Function not implemented`. - -We expect to add Monero into the ports tree in the near future, which will aid in managing installations using ports or packages. - -### On OpenBSD: - -#### OpenBSD < 6.2 - -This has been tested on OpenBSD 5.8. - -You will need to add a few packages to your system. `pkg_add db cmake gcc gcc-libs g++ gtest`. - -The doxygen and graphviz packages are optional and require the xbase set. - -The Boost package has a bug that will prevent librpc.a from building correctly. In order to fix this, you will have to Build boost yourself from scratch. Follow the directions here (under "Building Boost"): -https://github.com/bitcoin/bitcoin/blob/master/doc/build-openbsd.md - -You will have to add the serialization, date_time, and regex modules to Boost when building as they are needed by Monero. - -To build: `env CC=egcc CXX=eg++ CPP=ecpp DEVELOPER_LOCAL_TOOLS=1 BOOST_ROOT=/path/to/the/boost/you/built make release-static-64` - -#### OpenBSD >= 6.2 - -You will need to add a few packages to your system. `pkg_add cmake zeromq libiconv`. - -The doxygen and graphviz packages are optional and require the xbase set. - - -Build the Boost library using clang. This guide is derived from: https://github.com/bitcoin/bitcoin/blob/master/doc/build-openbsd.md - -We assume you are compiling with a non-root user and you have `doas` enabled. - -Note: do not use the boost package provided by OpenBSD, as we are installing boost to `/usr/local`. - -``` -# Create boost building directory -mkdir ~/boost -cd ~/boost - -# Fetch boost source -ftp -o boost_1_64_0.tar.bz2 https://netcologne.dl.sourceforge.net/project/boost/boost/1.64.0/boost_1_64_0.tar.bz2 - -# MUST output: (SHA256) boost_1_64_0.tar.bz2: OK -echo "7bcc5caace97baa948931d712ea5f37038dbb1c5d89b43ad4def4ed7cb683332 boost_1_64_0.tar.bz2" | sha256 -c -tar xfj boost_1_64_0.tar.bz2 - -# Fetch and apply boost patches, required for OpenBSD -ftp -o boost_test_impl_execution_monitor_ipp.patch https://raw.githubusercontent.com/openbsd/ports/bee9e6df517077a7269ff0dfd57995f5c6a10379/devel/boost/patches/patch-boost_test_impl_execution_monitor_ipp -ftp -o boost_config_platform_bsd_hpp.patch https://raw.githubusercontent.com/openbsd/ports/90658284fb786f5a60dd9d6e8d14500c167bdaa0/devel/boost/patches/patch-boost_config_platform_bsd_hpp - -# MUST output: (SHA256) boost_config_platform_bsd_hpp.patch: OK -echo "1f5e59d1154f16ee1e0cc169395f30d5e7d22a5bd9f86358f738b0ccaea5e51d boost_config_platform_bsd_hpp.patch" | sha256 -c -# MUST output: (SHA256) boost_test_impl_execution_monitor_ipp.patch: OK -echo "30cec182a1437d40c3e0bd9a866ab5ddc1400a56185b7e671bb3782634ed0206 boost_test_impl_execution_monitor_ipp.patch" | sha256 -c - -cd boost_1_64_0 -patch -p0 < ../boost_test_impl_execution_monitor_ipp.patch -patch -p0 < ../boost_config_platform_bsd_hpp.patch - -# Start building boost -echo 'using clang : : c++ : "-fvisibility=hidden -fPIC" "" "ar" "strip" "ranlib" "" : ;' > user-config.jam -./bootstrap.sh --without-icu --with-libraries=chrono,filesystem,program_options,system,thread,test,date_time,regex,serialization,locale --with-toolset=clang -./b2 toolset=clang cxxflags="-stdlib=libc++" linkflags="-stdlib=libc++" -sICONV_PATH=/usr/local -doas ./b2 -d0 runtime-link=shared threadapi=pthread threading=multi link=static variant=release --layout=tagged --build-type=complete --user-config=user-config.jam -sNO_BZIP2=1 -sICONV_PATH=/usr/local --prefix=/usr/local install -``` - -Build cppzmq - -Build the cppzmq bindings. - -We assume you are compiling with a non-root user and you have `doas` enabled. - -``` -# Create cppzmq building directory -mkdir ~/cppzmq -cd ~/cppzmq - -# Fetch cppzmq source -ftp -o cppzmq-4.2.3.tar.gz https://github.com/zeromq/cppzmq/archive/v4.2.3.tar.gz - -# MUST output: (SHA256) cppzmq-4.2.3.tar.gz: OK -echo "3e6b57bf49115f4ae893b1ff7848ead7267013087dc7be1ab27636a97144d373 cppzmq-4.2.3.tar.gz" | sha256 -c -tar xfz cppzmq-4.2.3.tar.gz - -# Start building cppzmq -cd cppzmq-4.2.3 -mkdir build -cd build -cmake .. -doas make install -``` - -Build monero: `env DEVELOPER_LOCAL_TOOLS=1 BOOST_ROOT=/usr/local make release-static` - -### On Solaris: - -The default Solaris linker can't be used, you have to install GNU ld, then run cmake manually with the path to your copy of GNU ld: - - mkdir -p build/release - cd build/release - cmake -DCMAKE_LINKER=/path/to/ld -D CMAKE_BUILD_TYPE=Release ../.. - cd ../.. - -Then you can run make as usual. - -### On Linux for Android (using docker): - - # Build image (for ARM 32-bit) - docker build -f utils/build_scripts/android32.Dockerfile -t monero-android . - # Build image (for ARM 64-bit) - docker build -f utils/build_scripts/android64.Dockerfile -t monero-android . - # Create container - docker create -it --name monero-android monero-android bash - # Get binaries - docker cp monero-android:/src/build/release/bin . - -### Building portable statically linked binaries (Cross Compiling) - -By default, in either dynamically or statically linked builds, binaries target the specific host processor on which the build happens and are not portable to other processors. Portable binaries can be built using the following targets: - -* ```make release-static-linux-x86_64``` builds binaries on Linux on x86_64 portable across POSIX systems on x86_64 processors -* ```make release-static-linux-i686``` builds binaries on Linux on x86_64 or i686 portable across POSIX systems on i686 processors -* ```make release-static-linux-armv8``` builds binaries on Linux portable across POSIX systems on armv8 processors -* ```make release-static-linux-armv7``` builds binaries on Linux portable across POSIX systems on armv7 processors -* ```make release-static-linux-armv6``` builds binaries on Linux portable across POSIX systems on armv6 processors -* ```make release-static-win64``` builds binaries on 64-bit Windows portable across 64-bit Windows systems -* ```make release-static-win32``` builds binaries on 64-bit or 32-bit Windows portable across 32-bit Windows systems - -## Installing Monero from a package - -**DISCLAIMER: These packages are not part of this repository or maintained by this project's contributors, and as such, do not go through the same review process to ensure their trustworthiness and security.** +## Installing from a package Packages are available for -* Ubuntu and [snap supported](https://snapcraft.io/docs/core/install) systems, via a community contributed build. +* Arch Linux/Manjaro - snap install monero --beta + yay -S wownero-git -Installing a snap is very quick. Snaps are secure. They are isolated with all of their dependencies. Snaps also auto update when a new version is released. +* NixOS -* Arch Linux (via [AUR](https://aur.archlinux.org/)): - - Stable release: [`monero`](https://aur.archlinux.org/packages/monero) - - Bleeding edge: [`monero-git`](https://aur.archlinux.org/packages/monero-git) + nix-shell -p altcoins.wownero -* Void Linux: +* Ubuntu 18.04/Ubuntu 16.04/Debian 9/Debian 8 (amd64) - xbps-install -S monero - -* GuixSD - - guix package -i monero - -* Docker - - # Build using all available cores - docker build -t monero . - - # or build using a specific number of cores (reduce RAM requirement) - docker build --build-arg NPROC=1 -t monero . - - # either run in foreground - docker run -it -v /monero/chain:/root/.bitmonero -v /monero/wallet:/wallet -p 18080:18080 monero - - # or in background - docker run -it -d -v /monero/chain:/root/.bitmonero -v /monero/wallet:/wallet -p 18080:18080 monero - -* The build needs 3 GB space. -* Wait one hour or more + sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys B09DF0E4B0C56A94 + sudo add-apt-repository "deb http://ppa.wownero.com/ bionic main" + sudo apt-get update + sudo apt-get install wownero Packaging for your favorite distribution would be a welcome contribution! -You can also cross-compile binaries on linux for windows and macos with the depends system. Go to contrib/depends and type: +**DISCLAIMER: These packages are not part of this repository, and as such, do not go through the same review process to ensure their trustworthiness and security.** -* ```make HOST=x86_64-linux-gnu``` for 64-bit linux binaries. -* ```make HOST=x86_64-w64-mingw32``` for 64-bit windows binaries. Requires: python3 nsis g++-mingw-w64-x86-64 wine1.6 bc -* ```make HOST=x86_64-apple-darwin11``` for darwin binaries. Requires: cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev -* ```make HOST=i686-linux-gnu``` for 32-bit linux binaries. Requires: g++-multilib bc -* ```make HOST=i686-w64-mingw32``` for 32-bit windows binaries. Requires: python3 nsis g++-mingw-w64-i686 -* ```make HOST=arm-linux-gnueabihf``` for armv6 binaries. Requires: g++-arm-linux-gnueabihf -The required packages are the names for each toolchain on apt. Depending on your distro, they may have different names. -Then go back to the source dir and type for example for windows 64bit: +## Building from Source -* ```cmake -DCMAKE_TOOLCHAIN_FILE=`pwd`/contrib/depends/x86_64-w64-mingw32``` +* Arch Linux/Manjaro -Using depends might also be easier to compile monero on windows than using msys. Activate windows subsystem for linux (for example ubuntu) install the apt build-essentials and follow the depends steps as depicted above. + sudo pacman -Syu && sudo pacman -S base-devel cmake boost openssl zeromq libpgm unbound libsodium git + git clone https://github.com/wownero/wownero + cd wownero + make -## Running monerod + +* Debian/Ubuntu + + sudo apt update && sudo apt install build-essential cmake pkg-config libboost-all-dev libssl-dev libzmq3-dev libunbound-dev libsodium-dev libpgm-dev git + git clone https://github.com/wownero/wownero + cd wownero + make + + +## Running Binaries The build places the binary in `bin/` sub-directory within the build directory from which cmake was invoked (repository root by default). To run in foreground: - ./bin/monerod + ./bin/wownerod -To list all available options, run `./bin/monerod --help`. Options can be +To list all available options, run `./bin/wownerod --help`. Options can be specified either on the command line or in a configuration file passed by the `--config-file` argument. To specify an option in the configuration file, add a line with the syntax `argumentname=value`, where `argumentname` is the name @@ -568,130 +109,39 @@ of the argument without the leading dashes, for example `log-level=1`. To run in background: - ./bin/monerod --log-file monerod.log --detach + ./bin/wownerod --log-file wownerod.log --detach To run as a systemd service, copy -[monerod.service](utils/systemd/monerod.service) to `/etc/systemd/system/` and -[monerod.conf](utils/conf/monerod.conf) to `/etc/`. The [example -service](utils/systemd/monerod.service) assumes that the user `monero` exists +[wownerod.service](utils/systemd/wownerod.service) to `/etc/systemd/system/` and +[wownerod.conf](utils/conf/wownerod.conf) to `/etc/`. The [example +service](utils/systemd/wownerod.service) assumes that the user `wownero` exists and its home is the data directory specified in the [example -config](utils/conf/monerod.conf). +config](utils/conf/wownerod.conf). -If you're on Mac, you may need to add the `--max-concurrency 1` option to -monero-wallet-cli, and possibly monerod, if you get crashes refreshing. +Once node is synced to network, run the CLI wallet by entering: -## Internationalization + ./bin/wownero-wallet-cli -See [README.i18n.md](README.i18n.md). +Type `help` in CLI wallet to see standard commands (for advanced options, type `help_advanced`). -## Using Tor +## Wownero Graphical Wallets -While Monero isn't made to integrate with Tor, it can be used wrapped with torsocks, by -setting the following configuration parameters and environment variables: +* [Wownero-Light-Wallet](https://github.com/wownero/Wownero-Light-Wallet) -* `--p2p-bind-ip 127.0.0.1` on the command line or `p2p-bind-ip=127.0.0.1` in - monerod.conf to disable listening for connections on external interfaces. -* `--no-igd` on the command line or `no-igd=1` in monerod.conf to disable IGD - (UPnP port forwarding negotiation), which is pointless with Tor. -* `DNS_PUBLIC=tcp` or `DNS_PUBLIC=tcp://x.x.x.x` where x.x.x.x is the IP of the - desired DNS server, for DNS requests to go over TCP, so that they are routed - through Tor. When IP is not specified, monerod uses the default list of - servers defined in [src/common/dns_utils.cpp](src/common/dns_utils.cpp). -* `TORSOCKS_ALLOW_INBOUND=1` to tell torsocks to allow monerod to bind to interfaces - to accept connections from the wallet. On some Linux systems, torsocks - allows binding to localhost by default, so setting this variable is only - necessary to allow binding to local LAN/VPN interfaces to allow wallets to - connect from remote hosts. On other systems, it may be needed for local wallets - as well. -* Do NOT pass `--detach` when running through torsocks with systemd, (see - [utils/systemd/monerod.service](utils/systemd/monerod.service) for details). -* If you use the wallet with a Tor daemon via the loopback IP (eg, 127.0.0.1:9050), - then use `--untrusted-daemon` unless it is your own hidden service. +![](https://light.wownero.com/wowlight.png) -Example command line to start monerod through Tor: - DNS_PUBLIC=tcp torsocks monerod --p2p-bind-ip 127.0.0.1 --no-igd +* [Wownerujo Android Wallet](https://github.com/wownero/wownerujo) -### Using Tor on Tails +![](https://fossdroid.com/images/screenshots/com.wownero.wownerujo/wownerujo-1.png) +![](https://fossdroid.com/images/screenshots/com.wownero.wownerujo/wownerujo-2.png) -TAILS ships with a very restrictive set of firewall rules. Therefore, you need -to add a rule to allow this connection too, in addition to telling torsocks to -allow inbound connections. Full example: +## Donating to Wownero Project - sudo iptables -I OUTPUT 2 -p tcp -d 127.0.0.1 -m tcp --dport 18081 -j ACCEPT - DNS_PUBLIC=tcp torsocks ./monerod --p2p-bind-ip 127.0.0.1 --no-igd --rpc-bind-ip 127.0.0.1 \ - --data-dir /home/amnesia/Persistent/your/directory/to/the/blockchain +Developers are volunteers doing this mostly for shits and giggles. If you would like to support our shenanigans and stimulant addictions, please consider donating to [WFS proposals](https://funding.wownero.com/proposals) or the [dev slush fund](https://funding.wownero.com/donate). -## Debugging +Donations may also be sent to: -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. +XMR: `44SQVPGLufPasUcuUQSZiF5c9BFzjcP8ucDxzzFDgLf1VkCEFaidJ3u2AhSKMhPLKA3jc2iS8wQHFcaigM6fXmo6AnFRn5B` -### Obtaining stack traces and core dumps on Unix systems - -We generally use the tool `gdb` (GNU debugger) to provide stack trace functionality, and `ulimit` to provide core dumps in builds which crash or segfault. - -* To use gdb in order to obtain a stack trace for a build that has stalled: - -Run the build. - -Once it stalls, enter the following command: - -``` -gdb /path/to/monerod `pidof monerod` -``` - -Type `thread apply all bt` within gdb in order to obtain the stack trace - -* If however the core dumps or segfaults: - -Enter `ulimit -c unlimited` on the command line to enable unlimited filesizes for core dumps - -Enter `echo core | sudo tee /proc/sys/kernel/core_pattern` to stop cores from being hijacked by other tools - -Run the build. - -When it terminates with an output along the lines of "Segmentation fault (core dumped)", there should be a core dump file in the same directory as monerod. It may be named just `core`, or `core.xxxx` with numbers appended. - -You can now analyse this core dump with `gdb` as follows: - -`gdb /path/to/monerod /path/to/dumpfile` - -Print the stack trace with `bt` - -* To run monero within gdb: - -Type `gdb /path/to/monerod` - -Pass command-line options with `--args` followed by the relevant arguments - -Type `run` to run monerod - -### Analysing memory corruption - -There are two tools available: - -* ASAN - -Configure Monero with the -D SANITIZE=ON cmake flag, eg: - - cd build/debug && cmake -D SANITIZE=ON -D CMAKE_BUILD_TYPE=Debug ../.. - -You can then run the monero tools normally. Performance will typically halve. - -* valgrind - -Install valgrind and run as `valgrind /path/to/monerod`. It will be very slow. - -### LMDB - -Instructions for debugging suspected blockchain corruption as per @HYC - -There is an `mdb_stat` command in the LMDB source that can print statistics about the database but it's not routinely built. This can be built with the following command: - -`cd ~/monero/external/db_drivers/liblmdb && make` - -The output of `mdb_stat -ea ` will indicate inconsistencies in the blocks, block_heights and block_info table. - -The output of `mdb_dump -s blocks ` and `mdb_dump -s block_info ` is useful for indicating whether blocks and block_info contain the same keys. - -These records are dumped as hex data, where the first line is the key and the second line is the data. +BTC: `bc1qcw9zglp3fxyl25zswemw7jczlqryms2lsmu464` diff --git a/src/blockchain_db/blockchain_db.cpp b/src/blockchain_db/blockchain_db.cpp index 682857157..0d25762d2 100644 --- a/src/blockchain_db/blockchain_db.cpp +++ b/src/blockchain_db/blockchain_db.cpp @@ -372,582 +372,7 @@ void BlockchainDB::fixup() LOG_PRINT_L1("Database is opened read only - skipping fixup check"); return; } - - // There was a bug that would cause key images for transactions without - // any outputs to not be added to the spent key image set. There are two - // instances of such transactions, in blocks 202612 and 685498. - // The key images below are those from the inputs in those transactions. - // On testnet, there are no such transactions - // See commit 533acc30eda7792c802ea8b6417917fa99b8bc2b for the fix - static const char * const mainnet_genesis_hex = "418015bb9ae982a1975da7d79277c2705727a56894ba0fb246adaabb1f4632e3"; - crypto::hash mainnet_genesis_hash; - epee::string_tools::hex_to_pod(mainnet_genesis_hex, mainnet_genesis_hash ); set_batch_transactions(true); - batch_start(); - - if (get_block_hash_from_height(0) == mainnet_genesis_hash) - { - // block 202612 (511 key images in 511 transactions) - static const char * const key_images_202612[] = - { - "51fc647fb27439fbb3672197d2068e4110391edf80d822f58607bd5757cba7f3", - "d8cf1c1bd41f13c4553186e130e6e2c1cd80135ddb418f350088926997a95ca9", - "95d2556c8acd1457dce7bfd9c83b1d82b821a55a9c9588b04b7b5cf562a65949", - "4b5d987fee1bb563a162d23e41741ad73560c003e26a09b6655f09496538daac", - "1d25ea86323d1578579d3894a54b99ea1c3e2dca547c6726c44aef67db958b02", - "92e46fb70be5d9df39ca83c4fc6ae26c594118314bb75502a9c9752a781d0b33", - "924d0cb9060d429be7e59d164a0f80a4dabc3607d44401b26fb93e7182ab435d", - "f63e4a23fec860fd4c3734623891330ac1ff5af251e83a0e6247287818b8a72f", - "5b14c5ef13738d015619b61dacefc2ade3660d25b35ef96330a8f4e2afc26526", - "d5016b012a2fb6ca23fd56ece544d847962264b4aee15efe1465805fd824a8fb", - "0a7f3da1d9dd341cd96829e484b07163099763ac7bd60603b7ee14f7dbcb278d", - "d716c03d7447d2b693f6f61b6ad36bd57344033fc1a11feaf60d569f40014530", - "23154a812e99ce226f6a87087e0812f419aed51289f1c0c359b0b61303b53a36", - "03940341e1a99d5b0c68eacfdf5a20df90d7d0a3d6089d39709cdd964490755c", - "ef09648814cfe071f5d8e9cfde57247ad09409265c4b6c84697bbb046809bd7e", - "8843ec52b0496ca4e895813cfe00bb18ea777d3618e9bd2e200287e888e2f5c7", - "8558bf39baf3df62b5d33cdf97163a365e6c44f4d6deef920730b4982b66449f", - "504d9380ce581de0af97d5800d5ca9e61d78df368907151ab1e567eb6445332a", - "673797763593c23b3ee07b43bd8760365e2c251a8a60a275528ff34a477110cc", - "25178c95e4d402c58d79c160d2c52dd3c45db2c78e6aaa8d24d35c64f19d4957", - "407c3a05dcc8bdcb0446b5d562cf05b4536fc7337344765215130d5f1e7ee906", - "4e7fa771a5455d8ee8295f01181a360cdc6467cc185c2834c7daf9fbf85b6f1d", - "6beb64cb024f9c5c988f942177fc9c1ec5ecfa85b7db0f13a17f9f98e8e46fe7", - "6406bfc4e486e64c889ea15577d66e5835c65c6a39ec081af8ac5acdc153b4b5", - "1b1da638709f9f85898af70ffaa5b88d5a4c9f2663ca92113c400ab25caf553c", - "da49407a9e1ed27abd28076a647177157c42517e2542e9b6a4921fdadf4e8742", - "3c3fdba2a792fddaeb033605163991a09933e8f05c6c934d718e50a613b64d69", - "82c60429171173739fa67c4807cab359620299e6ed2a9da80139b5b1e23c5456", - "0a19e5767e1381ac16f57cfa5aabd8463551d19f069f7f3465c11a8583253f3e", - "d0fae6ffdd4818399eae6224978f170f696150eaf699f2f218abc00c68530f96", - "0937889aeb3af5c64608d9a9f88229a068e53417448f00f9aa5e28c570cca8f8", - "d6072d269753020912524961ce8c6930cf35abe5c4b2bdc7fd678338d20a68fb", - "0e8bc9b06fcc842bdaa7df029bfd1f252d5defc014d58a006c10ab678ecf543f", - "9d42f90520f02c9258be53b123d69ddbce5f33f92a903d3fb4cf3358ff0e07b5", - "1cc05416b12cbe719617158773c3e6173435fc64e1ee44310dc696baecaeaa95", - "266b15222913c11ef6403ee15dc42c8c0e16bc5fa2f49110447802236e045797", - "791b123af3b71ac9497a010610f72207ff8ec642969b5cf0d2891b21e7eee562", - "946d4d7b084dc32495f22b35fc30394144c8e0ba04f3ad6e2c2bfb0173a2266d", - "2c015cb990c1583228d08b2d5de9227b950c3f57364fc1033bca5c0fbfd08c58", - "13fdc41862fd85507f579c69accb9cc6a40f5971bfa41e3caff598a3dcffd2fc", - "64b06d9874a83917c583c9439d1c736083377d67fda2961b623f7124663134c3", - "2fa49cd19e0aa02989991a4c3760f44be800fe8fb4d58b23aca382e10dc0d2d6", - "377628f265f799772e9fb6065be8b6eee200c329f729fe36c25ee179e4d20df9", - "ba94fa79134ce383b6a98b04dc6ad3d1b950e410d50a292bc770f9685e59fe91", - "875c924329f0733e31fe8d8aed70dc1906335b8a9984932b6368ea24edb39765", - "f31f4abb3f5ee42a5aae86d70b3bd9a9c1934933641893864dd333f89719d608", - "2bcd629e125514a780f568d3c2e8b12a2e7fbbee06e652bbeed3e7825508e31c", - "918b43581163ca1963de21bb9ac401756e75c3f00ac8dcfafc139f1ad5d7d998", - "5730dd57fa52749a0d6502b11c9d802ac495875542431310c674a65655b7c2a3", - "03f84b990683e569e2f6143bb963a2a8de411e7c4b7923117b94c7afcb4b43ea", - "b298c8510d35bd2be0ff0753ad7d98d480f4c6490bb67fb93cd4632ea726e8a7", - "0a771afbf9be104c01b89eaeb57073297d35ac8fbbcc0816820fdb9a29d26625", - "713d90d6ca1be1a4e05a5f8441dc528c699caa09eda49c09072f5f8291354c2e", - "988827f45c19330d9404309f63d536a447803cca7cb182ef005b074def09ab7d", - "9dcaa105b4def895f3faee704c250bdc924316f153cb972f3fb565beec0b7942", - "1c06c30afe65b59e9e22d6bb454e4209a03efe53cdbf27b3945d5d75b1b90427", - "49e08c13d1da209ec1aea7b7fbe0daa648e30febeb2aa5ffbaaabdd71a278ac2", - "e1c2e49ab7b829854c46a64772ded35459908e0f563edbcf5c612913b7767046", - "e08bb7d133490ca85a6325d46807170cd07618b6a5f6e1d069e44890cc366fab", - "5c73ca0691cde2f35b7933d6db33f0b642ec70d0bd3f2b0ebbd97754ca67e248", - "6404399151872a521dae767311712225dba65d810ba2feba209204221b5d772d", - "4a0c3aa6cef36f44edf08ad8fb1533d7e1186e317da8a3afb3d81af072043233", - "104b3e1af37cf10b663a7ec8452ea882082018c4d5be4cd49e7f532e2fea64e5", - "e723a46bf9684b4476c3005eb5c26511c58b7eb3c708ddf7470ee30a40834b32", - "18e6f0fa3aa779a73ceefabea27bff3202003fd2c558ec5f5d07920528947d57", - "c97e73eb593ff39e63307220796cc64974c0c8adac860a2559ab47c49bc0c860", - "13c363a962955b00db6d5a68b8307cd900ae9202d9b2deb357b8d433545244ac", - "76a488865151fab977d3639bac6cba4ba9b52aa17d28ac3580775ed0bff393e4", - "a14de587c9f4cd50bb470ecffd10026de97b9b5e327168a0a8906891d39d4319", - "b1d38ee1c4ca8ae2754a719706e6f71865e8c512310061b8d26438fedf78707e", - "772bb8a3f74be96fa84be5fa8f9a8ef355e2df54869c2e8ae6ad2bf54ed5057e", - "3083a7011da36be63e3f7cacd70ab52e364dd58783302f1cb07535a66b5735f5", - "2b1d892e3002aa3201deb4ffe28c0c43b75b8f30c96b5d43f6d5829049ecbd94", - "cb738aabe44c6fb17ade284bf27db0169e309bf8cf9c91c4e4e62856619a4c64", - "1707e04b792f4953f460f217b9bb94c84cef60736a749fb01277cfe0eaaa48c7", - "ab8b6bac9b8a4f00b78acb4bd50ed2758e0fa100964b6f298d2a943eb2af2b30", - "dd317193fef72490f3be01293b29e9c2f94eda10824a76ca74bf39dd7cb40ab2", - "4fb3d995b087af7517fcb79e71f43bac0c4fbda64d89417a40ca4a708f2e8bc1", - "549ba38c31bf926b2cb7e8f7f15d15df6388dce477a3aff0245caa44606849fc", - "7585c14ab9abbffb89d0fa9f00c78ecae9f7c9062e5d4f1fae8453b3951fc60b", - "953f855323f72461b7167e3df0f4fd746a06f5a7f98aa42acdce2eef822a0b2f", - "0931932d57dde94dcfb017179a5a0954b7d671422149738260a365ca44f50eb8", - "a3d179d16a4a275a3bb0f260cee9284db243abad637a9dbe92d02940f1c7ee8c", - "959843f1e76ff0785dafe312c2ea66380fdc32b9d6180920f05f874c74599a80", - "fbc36b3e1718fe6c338968b04caa01a7adb315d206abc63e56768d69e008a65d", - "f054de7eac5e2ea48072e7fb4db93594c5f5d2dfa0afe8266042b6adc80dfdca", - "39dfc68dc6ba8c457b2995562c867cef2f2cf994e8d6776a6b20869e25053f70", - "19ad7ca7629758c22ac83643605c8a32a6665bae8e35dbc9b4ad90343756ebb3", - "e89e80ea5c64cf7840f614f26e35a12c9c3091fa873e63b298835d9eda31a9ea", - "572c1b9a83c947f62331b83009cc2ec9e62eab7260b49929388e6500c45cd917", - "df0b21f679e6c0bf97f7b874e9f07c93c3467b092f3d9e1484e5646fda6eca5f", - "8f3b7c0f4b403af62fe83d3cfac3f1e2572af8afa4cea3f3e2e04291efe84cf6", - "aae8b8db243009d021d8c9897d52ee8125a17212f0a8b85f681ad8950ae45f0e", - "3d45a4957d27447dea83d9ae2ef392a3a86619bfcf8dda2db405a7b304997797", - "a5b0a619a8e3030b691bdba1ed951cd54e4bc2063602eae26d9791fb18e60301", - "14650df217dd64a2905cd058114e761502dff37d40e80789200bc53af29b265f", - "fd6a245ab5e4e6e18d7ba9b37478ce38248f0ab864e5511d2208ae3d25017e5f", - "fbe0be6dd42a11feb5db5ae56fcbbac41041ab04a35f1df075580e960c8eeab0", - "72f3f1213d9bec92ba9705b447d99cd0a6a446e37a3c1c50bb8ece1090bfe56e", - "20df836554e1534f62b2a6df9ce58e11c1b9b4746ce8ee3c462300a8c01f5e76", - "5c3d2a81b7331c86420ad32b6e9a212b73b1c3413724a0f91bf073eba18e2f1f", - "63264ddfb29cd36fc18f3ee6614c4101ba7229bc5ac375f912590d3f0df982f4", - "5ec4eb637761c1c9dbc6aa6649d4410508ef8d25d61ad6caa40c6ee3236d5515", - "270c70940536017915e1cdbc003de7279ec1c94cba1ef6130f4236f7e306e4f0", - "c1d1d57a7c03f6ddeeab5230a4910db8355e2143f473dea6e1d57c2f8d882b76", - "218c030a7fdc9917e9f87e2921e258d34d7740a68b5bee48a392b8a2acf1f347", - "ac47861c01c89ea64abee14cf6e1f317859ed56b69ae66377dc63e6575b7b1eb", - "23bf549c8a03f9870983c8098e974308ec362354b0dcf636c242a88f24fc2718", - "a3ce8b817e5212c851c6b95e693849a396c79b0d04b2a554de9b78933fbea2b7", - "7310120c1cc1961b0d3fce13743c8a7075ae426fe6cccaf83600f24cee106236", - "8fa0630f193777dcc4f5eccd1ad9ceacf80acdf65e52e4e01bf3a2b2fdd0dac6", - "4a5f5c87f67d573d0673f01abaebc26eaa62e6d04627588549cc9e6c142dc994", - "78971cccacc645116f9d380c167f955d54b386a22af112233f7de004fc0c8316", - "badc67216868e1de1bbe044bf0e6070e6ee0353d05c13fa0c43b1897db5219a2", - "c45b2a168bc51cbb615a79f97432cc4bb6b104da9cdc1fc640c930657452f71b", - "c17eda13541d14554c5db542155b08b6bf9cb403d425745b662ebc2b2b9b3a3b", - "313210cd9d2efc1603f07859bae7bd5fb5914f4a631b943f2f6ff5927a4e681a", - "6ee94ec8af4e6828f9b46c590ea55da640ef50e810a247d3e8cdf4b91c42d2c2", - "505b7a4d9f1ba6577aa2a941843f86c205b23b1ea21035925e587022e2f0aeed", - "98e6a7cd687e8192e300a8202999ec31ad57bc34f656f2ae90d148607ff6d29f", - "1be5db002c0a446cc2c1da363e5d08ae045cd8f5e76c8cccd65d5166393c0bdf", - "17c02ac6d390c5c735e1e873c40294220e89071fca08a5da396a131fa1ba8670", - "2540507c39ae6fdcd90de826077f0ca390da126166a25c15c048a60606a27367", - "5ab9328e525c7a017ef4f591d995ad4595d74cbf8ff4112af33b08c70661a304", - "9c105587a96c51d81422f64e46016564d22329760648c95dcac7218f3733f915", - "525afb1b94a75f1edc2b55c700071e14a2166acda003370047c30dba8ea80278", - "745d4a5d9f95ca4efa6261b6bcd4ecacd504b5b901a2ce1353c522a5c0c15dcc", - "5a5a568cd87ba34252ba254e6a320e1a7f52f13e7451bb887efb34ff881785f2", - "1ec50a80198cd830b51f4f7a0222015a268d9b40f04e7f838c7b8dc7abf63b01", - "68836b662d79349cb42f5cef54e6a066157d398cc87d3b13f29fc04e5cf364a5", - "658db317f355a9cbd86f673775cac0c308fe14967428fd283a36e300a6a53b2f", - "677d79a8c467dd9db38f0ef45c2787dd368f701a6b47bf7a5f06112c38da643e", - "2baa455d4066f5d628f9ecd315cb57deca71069db5d0d112ae0aa18a84b6f0d7", - "5e7b0889f351560081360ac2b1429b48b2f7d886227f144e3b198e2f1fa56ed9", - "c3d317fbf26e15add8b4f8f93df9de9b22297b8e4945ebab9ee249d4f72f4e45", - "3c0b705a5c1e31abc7e46d8ff3c148a033f6875454cfb67f8d2a2b9a57a5ba7e", - "a0ab74663561af2adc2d38be3569fbe7aa2454346416ac96e5eb26b1e01b1e2f", - "53526cffdb74327670566c1dacacffb7d30a43a7f1862ff8bab87737bfa5edb6", - "24c5d36ab98d88f87b2c71afb4ea8562e05c7aa0b50f3bc0f9ed50a4cd52989b", - "c3ce4de5f94dff65d11e33a865855a4404259cf45263914c884f79db4f35169d", - "f1009b6dcf30030cff872d636fb96ed233eb6ecb8ffed003c7da64e4f5a02f4c", - "e3729f58614d3b42450d1599d863983ab7e3e5c29fb57aad7958c8923a2627c4", - "31cf4792f7b5ce01b217ec80184edd2a7c49c0b21701f5494ee2c9bac67c28ca", - "b42a5c9c92a656c5bb2b759ce160fdfd245243aeb1786338faea63b62e9a60ce", - "a1efc8d5d0855933d5ac8fe5960c7acacb92fcb09bfbc929e5002f168251e648", - "c4322c7f3682ec94b0dcb42f13b498c36cf575d505aacc8ec8bf67a6a2abf4c9", - "684ee5aa3c98357aeaddcc30c6620939b52aeef729e24b4a46ccafc44f24d831", - "36180f2ae11d105e0efbfbddb94e6b45b08609a383e4e1a8fa3b06d7a8051de9", - "96c2d183eacc87581a0b17b8d07878bc10d436728510715109a7565d9972f8b5", - "3068c9d04d561c7e29e3f621280b61a61885de0e9ec35a66a3116ca7a9e09627", - "2eb94b9673ad6f8f88288fddfceae4baaeccb37bed88a35611d826ba06a5363b", - "fc8cd5fae8b81121001f7767dcd5f185c0fdcc88cce1fbb184ddbcfad697ba54", - "51521da1ecedea6d588d774eb155d936b32a14913c2f11d989bcc5116e65bf41", - "3d23542e597a83dd6307700d79058b920f281c65f832333734d8a0adec510495", - "11d2e01913ff0d4bd21970d709d88e63289492c0bbad7bff99c0d36858a841ca", - "de674f1eee3068d2bc8c2f2897d8556e5deb872869652f7d3a4e5dbc6f1063c8", - "e722d7f728526110c0921791b417afde4af1e87ae48ccc01911786197843104b", - "aaba3a4e2a7d20ab76edfbcccefc27acfd509db3638582c28230e73ffd71d340", - "1385a7209dafb9622dd4274179832e40c7fae19445383c34ef79adb0e4de0c98", - "108408531fca288d74de4a2c596eab8569e355d9ab2f8380f4d24073a6b3fa95", - "294476a86fcd39351ae452cdb8af9584067ec4501ec6182d0062bb154559fed3", - "e64b175e0284c5cb69c8db46344ed43b5ced8abfe3cbf0c197103cfd116944cd", - "cdd73a0f1fa7c14ed2177ae2163035333718847e49dd5cca6775bd20fc7553ad", - "d423d2a374bc66a4587a5e3affa327ca75b8116051320759a3b88a868a7b80d4", - "f13ad1e5b1315557d5497b58516eb3b0759d909725ddd0eb8a0dee439c6c0a48", - "3a600b547a6061186a54e491344fd50cc7d4f0566a386a40aba6545254773728", - "37a6f3f221fe17cc04a65aa544d5135e8297ecaf0853ba784dffacb74feb481b", - "0ca42d67d0f84b28861d63e919e6ce5ad527447fdc53a03d8497a0241bee9376", - "c7dbda42459e6fadb92c416eaef3e04674fc57915a93f3be4e656634c9654075", - "0e34d728ae4fe347a5afecdf886fbd4d48a65c5d0dfab807da6ae95b6b2d7a3a", - "f1bc69257ed510db5b2ed370010b037f452a29c49e36337264b3011ce2516216", - "33f98f6b8a8e202463955998fba3b790199daa893e5471554826cfd9daa5c02f", - "f8a0a37a2c9ebd7022d7cded1ee0318fd363020070b4cdaea800e44dcc1300d2", - "6862714daedb908a4d86a3a3f1e65ec2c29ae61501b4ddcaf184243dd095d71b", - "555cd19a6d21941c1174129b8bbcc70edcf0d6874262ce9e1e542351990d523d", - "2cd6b44326828f23a2aa33699754bfa072c4883f39d53616f6a6b74149b664b6", - "127f45d2eacb565c21c1030fe8054fd0a3a75705bc368924712aa145d414fa47", - "19225e2dae6e1166f21cdab1290194470ded09df9b66f3faad3c1cc9ebcf340f", - "b7b3f53f0539b2b4837b8bb9dae0ccbd200c8d36126d9f50199d68a4293a46d3", - "6b6323be01f27d6d759d9670825e8ebb9c4cd8016351702328df91cef36cfec8", - "020c31cfdfc5b22b10235745b89b311d271cf82f2ba16d03fdf7a8bc8538694b", - "62573218530182b79e40d0113b7d281dace6da33bfcd0f9318558de5e5c76f08", - "37d928416b15982f5bb8be40c5b62fae0b664e412c25891f8860c4242927e610", - "b07ad11134a5c0542d2b418ef3863e8ea8477de68d9310681818ddd40825fdb0", - "4af77cb76bab845b56470c95ce7b8cd84ce49a095984c1f3eed67b0ee344316e", - "e3fdd4668d8726ba6adc401ac662c0cf6b5c1872082c488ed7da966d425fb1c0", - "3dec71c81c7e78e879abc8da8b30e2446edbe98eeb8df9dafe9201ebb4c6a834", - "7105467d9c5e855f1362fbddf820ed5e757997862efc9000317d3830a2f60ef3", - "2821df94b021d3e77e8d9c0f3972340210f5ea2c0935cbf125cfc578d4d6722f", - "114e5807accc337a22598bded30ebf3e0cfd75877e239f10cb043f829c315ab5", - "d658a1c0354628cd7312593ab25d5b9083de8f0def6e8425f188101d256cd136", - "4818d3be9b2a38fcc8c85d6c46f69b502943f79cf2462dfb0b6499e761bcc836", - "92b8c943cb017b5f2d39264640c069f1ecced1d3ce9b3fd755d6df2fddb99458", - "6edbd0fdf064fcbccd4a9e7a8ea520b87cb7faf867f7fe8a5f53625beb575119", - "bf3b49c477dafb06af65bf09851c0fbef9dbc3152a7268d31b55a8e7a9a95860", - "0e234dbadfda1393be2f068182615dbb83736f84f87710b5c7965bdce9f4a26a", - "df5ceae34429e47b92bbd5505ba27666552e0eb619997f359c55699c3252b1ff", - "08c1c7d940d699a91a83249bd578772e8958ffe23179e6150f07b96f1b47ce1e", - "6f919a429270da0022d70062537bdc1b21c43a8abc552d8e366647e5b719d310", - "63c66e5fd5d27f6fda87912ce46fa91a5e5b3634ed147fa2986330fc2696d3fa", - "bde070b75296bca3aa494e7f549cd2bd1ff003776712bc98a3164b139b2054ab", - "66694196dac5b60cf5e0ae05db8f3894fe04d65910686806551f471a0a0472e9", - "0d2e97524b7ce4cf30b54e61b2689df036d099c53d42e2977b1671834bac39e7", - "e081af76e923455f408127862be5c9baf7de6b19b952aa2a1da997d4dfe594c0", - "121bf6ae1596983b703d62fecf60ea7dd3c3909acf1e0911652e7dadb420ed12", - "a25e7b17464df71cd84ad08b17c5268520923bc33fe78c21b756f17353ea39a0", - "e985f078fb44dbfdf3f4f34388f0f233a4e413e02297ee9a7dcc3fcceacd44f9", - "b9184cf45e6e6b112cd863b1719de1bcab2137eb957e8028edca3a204a9ebb96", - "157d177d5e4bcce0040eb4bddb681eacf9e2942e1c542a57ce851b4742a9cc4f", - "0823e06635c9a1a69fd8833d1e48df98d711c1c47c06f27bb384932db1bbe9ee", - "8beeec1fd1bcdecba235b449cc49abca69b6486ed1c0861a2bfb6a43c970b86f", - "349f61a1cfc9112e537522858a0edae732a2f8434cf4780d3d2ec1e96f581cca", - "587cdf72b5914d364f7e214a70481cf1131ee4a09e6b43e52428d2e56b000c13", - "a6aa0c179316534c7b9ffb5f42a2af98d1d3a166bfb413199579f259c7b5e6df", - "f9f3bb1ba8da5899b79186008ecfbd416b49f3b86d94045b91e34a40e41d5cff", - "0cdda65a60b7b4d94e794c9397e01f69fb29309ce4fac83e7392dbba6bc497f9", - "8fa5fce5ad09d43af7218ea5724cff2c4849a59ff73caf3bbca466e3c8538ba8", - "8874ef46008753fcc0b77eb7a4a8560e35966bf5a12bcad4203ad2b4c1f8bfbe", - "a8ee9a3aa2d0c08a951439ffb0e6d10315fc4776997b275de1ec19663e88c2c2", - "9c184cbbff464ab4d5f6bfa78c39bf0880fb93b1574139306a97acb940d415c9", - "5493a38c255c91ca49b958ed417f6c57e5bc444779d8f536f290596a31bc63d3", - "3e1e82b96cc599d9fc55ae74330692ccbfb538a4cc923975fd8876efe4b81552", - "16aaaf820c24c2726e349b0e49bbab72ca6eef7b3a2835de88d0cececa4da684", - "7fa52ba349f7203c3dbc2249f9881101a3318d21869dd59f17abf953d234db65", - "713d8018bb9ba3ab55c3a110120b9d7593514111075ef05f0fdb233ff2ceddc8", - "56063afb495759a0942f1c33f28a4fb8320c6d376cb3c9513249453e45f24a04", - "f9a6bacd9e055749b45174ecf3c3db18b78f3474761948a68adf601f54e59660", - "7ddd7c6d41572f93fe07c0300c34e455b6d1f4372204933bf45035241c8b060c", - "f81021b893a36b201de71330a2ea050b59dbf7560c62fa9cbea9261ab47a0ba2", - "a01fbe4114c18fd534ae1621404d25c08e3b6775a2631ff40279bafd8c9304f4", - "350fad8ebc938c6eb508a1483f385f577794a002bc1229db70a1b0131d174b9d", - "570cb8bce87f532c5051d8c4c864012408e672a7d492669e660251fb1e066bec", - "8cb6efbb129c84eba26d894c4293b476a6e9a1fe969c6ad18b554d2a57885f36", - "f384a98467bf7f084ca31bea121a4ec76e530f523d3225c21ed25a18544a9916", - "da127ab58ce557c2c95c20d6a291c2e5d880fff09dc28927b7bdfec97b995d72", - "a4d95b4f74366ec920d0a0c5d81265688cc18733ffc444cac9b01ae2431568aa", - "5ae2a71470570733422468bb733d53c85b1c8a6e7e6df5c05941556bcf342d1a", - "65a2d161ff0e095d3afe37584dbbe649f1e9cd440755b5a3c5b2a252d5c0b8bc", - "25ef70a8e41bb422ed7996a41160294e33238d6af17a532232f0a50b123431a2", - "f1f0f76ee901664a65b97104296babb9c7422370e99bb677ae07c2ee420e7f40", - "c3c66dda180b0330e75c8139b9f315a8c6b937f55d87d7be42e172bbac60d71e", - "5881786695a9e58e19d79f790c7d9243a847c0269c5770bdd01f5149c2a62a88", - "f2f816d3c8ebc7df06ab68d39223181aacc7be04364d1d4e69a56c33949bb983", - "80a1c3b6b2778d4846ad9fe0bb2dd5afd99aa897f8231bfaac45fde43d602d9f", - "72ad67cb043aa5df0c3dcc2464953a66893259d81c9cc7778c12bca3447fbd58", - "ad72420a7963b8d4536d8eba00b4b989992247cd8c01660e242a8e71edaf0e19", - "999d603d1cf6068e3bb6abe1bca976fa0ab84c4660b29ea8973de8b5cf5fd283", - "e137a5910f02a764c3a3d8f1579ac0c7e3cc34e58933216868efe010332c1e6e", - "10e0fa2362f59317626ae989bd1f962c583339d8d74d76c3e585243d152b91e8", - "1951c652704962f5c6e33a4d4aadfee5d53ce2253644d1ed542da3e278524a07", - "c938bccb7ba6c0217d8ba35ed91502aee051c8ae5bff05e88aab3b322aec936f", - "4d6386c689785edd5beb55911a3a9fc8914838c8192184199589beef8b6ddf9f", - "26f6f45a6468bc4b1f085fd28d63c264ee17564f9e247fc03ee179a0b579dcda", - "235b7bb82b72c61acd5979ca5f2ca740aee805a780ba22e11aae5cd34f6ec760", - "c027ffb585a1e4844b4907490b621b08c7e40a5e6f93e97bd4bb1b615bba9908", - "aa77fc8053d139b998577319de29457b78b1cc8b35a5f3526c0621eaa42ce6e8", - "afd0af9a11c5ae2a7c4a4571ce39ad57d8df70ef146ed83ad8eaff97b2387fb8", - "a1f8fee9f1da9a2b306489d00edf754187b55e97f4fe6f249432fe6c7f44d6be", - "4f12e8a123465a862060efb656299e6bef732e5954b02194308817b243e84d32", - "6a1ca62f7d6952ad2eba1c64035260319baf03deabf856ca860744fc886b3d3a", - "c72dd1fe890d6e4c1f7325a4f224e85aef6cdca8bf9441d228efaf126e02ba63", - "2f6ddcea18d891ef4252e657989de68adcc43c2175d49c0c059c5e49b9dd5aed", - "24efac0f240ed183c30398ee40307623f52113598f66c5864c90fc62643a2aec", - "6ba3ebc935e7cf7fbb446e7f5c12b19c4455e6894412b0eedee4fc945e429e9a", - "3519d6e5bc9649f97d07a07ef5471a553ffce35c7619f4f63e91a2ba38cbb729", - "65e073df352fa9917e5c2475167e6c523b68c1406e1b6e81109e2d4cc87c740d", - "d73bf816c3648a7d53d34be938c454e515efb0c974d5a225f33635540b2db90d", - "bce167790fc86a273db011757d09e2d1148521ce242c2ded58f26cc49993aacb", - "2d4286ed4039916f29602e86f47ea4c5b05998c0198509ca7799fcadfb337e8d", - "9837c495b1af4f76b09177514a0f3e1dceb509c52b91712f3c7d29dc1b91d09b", - "5c848b8291f39759903ce77f151acf40f3ab5afa2d4a45af62b320371c29a697", - "b92df5016ee947ce6a21365d3361977f7f2f6c14025a983c44e13d3e8cc72152", - "71d2f57222a39b1a7ed2df5e6fb23a964439b5a8e7d49b49d87e5cd5354baa75", - "88b44d0198fb15b0c20a97f87e021c744606bfd35eae2874f35c447aa4ac3cd4", - "29bb4c2557714119cd684da2867e689e37e3ca9c912db83ab84746816f6092ab", - "b1836d98a288752675b133b9018fa1edf174f311921d01926c1e1a5900c21029", - "a00645e090c7d96f3155ffbcfc41e526a763b0f53a98151ac4a7d4a5b14066b6", - "78aab09919d17773b0d62799b37bd2e2970f72f5d1eb6472489c367b6135374f", - "eb6123aeb28608f1c97b2bf62ef69f977cd0658a0ab491deebb1e972caa938c5", - "8dd7ef1650b1b30cdf7814ae4d61a237eb0acc3ec3ce0f72b1c25780329c2d7d", - "b1998419be3172858b990eea84fe10bb24b76c219cde277cb4305955fc7e0b65", - "1b10560016c4bc506eef9056dedc2943a17179081e6eaf85b48d37dc20eac3cc", - "1fb1d9d4d408a6734234910f554d272739a0d6fa401918d79b57be62c3f23ba2", - "dec878f54ce36788289b61d32de0d9539032aba22cd15522752f729659c7cc5c", - "fdbfd0773f5a66637b093dabf812197940d1134619a7e60a931b19915b7dab0a", - "21bd2c9aae052a1c187947d2964f2be4afa7b30248409c41a75522e88a4e7977", - "59326adab03416ec1d36699c18e5e4fa13ca1f2212d48c23bfdecb0be7263616", - "bcf263d39457c0aef8ef42fd98f8131198ec4fb203155dd5bcd759e499a9ca5c", - "f1ad083bcd8c7630eef93336d8a971ae8ae37166a6a00ac39684420c5f9afef8", - "d82ee2ac41b36e3c1787a01835bf054070486dc20f6565efedbbc37cd3bf5fa5", - "eba91a0dcbd3986299b0a3e66c116f77bd3421829291fd769522f01da26f107b", - "11016558b7e8c6386c6a3e862691dcba47e33e257f0e7df38901ea7c0eba894c", - "04f02795e34a0030e5186c8e700da8a46b1aa6bc0abed6b35c9c1cd6a73776b9", - "2628dc8ad7fb731d59456b2755a99c6701467125fa69816c21bfccabc31edf6b", - "9b7ca249ee5b45cd264492f30df09f708a7d9caed7beb9a5c6292f22e4c31f85", - "5c42e7caedf382092faaf392174792b3cf5f2fe29cb586387ee55611af0617c9", - "373f2fd5940a01feb79659c8b9099477f6d3e7b570ebb749c2ac336ea4be583d", - "fea22887147adc3a659a14902080b03e86b4b8b16985fdf0bbacaed00d812422", - "6a3e51a1443cff62af9fa12fafc8ea78ae74dac7792c9ae0f436f570ab33eb71", - "796be21e213d6d0cd6fbe2de1555fb73d1cf9edc041a9f1ff1ad583c4ca92460", - "03fcbcb31d3fd17f0eedb45ac5a51678c7c8b2b8498225d05f74e2892f017f72", - "d28da07c6c22daf9ae987b3033c33d3140e5a56fa1ffd7dc5c7853d55a45bcc7", - "fbb0ce02f50018741a12fc08eea80a18067d7bb0fcd96153d40bb8c808473aae", - "2bf7c05a0209b4ea31314f04bd754cd01c58102d7cde8c496c655b6494924354", - "1968a9e6e14ae86a1e02e6078fc4631851fce5dbac6aa34f860defd1ccfd0ded", - "d886181329c9e06462a1407f547d77b38ff2c868b86d8976aa478e1cbb3d66d4", - "0d465e02ff2f8eb0b5fb2fa9a38579c5d66337d4a16b58f8ed28d2d89fc02392", - "3196419015289807880ef24b6781734822d654dc260c0560d00bac65eacd5219", - "fa08390ddc333a2a12248d5ec3e51fff9b782227069fe5a0afbd8eba967ae8d1", - "49ae36a791cb84516688d59a1ed3e5112851d65f265078aa2d433b45fa984c8a", - "35daa428e12c59da6730760979aca3444d8b31149c6febd99fbfefa4b2372082", - "5ef1d697beba612ff31d1dc139817c313a4e2ad3977775943b635c141ef0f2a1", - "674256037ec00edb66b9355fb1d33a30a47a5d1f4bce2dd5475d89f1ea6502db", - "7b6f017bc550933af91eec32a79464f540c5e0c208703e175843ee4e9ffc0a50", - "bf0eb91da1d18dbb18fd9ff36c837387887ba142961176a44889718b2becb9dc", - "3e5ac43a05164b074a2ff6353e9882917c5a3dbe168c2695de895f7decf1a56c", - "35e8f004965347c2b18a000a83dd3a8084b8a4bf00522061ed1179aa1107c413", - "fccb0fff3a24e555ec96f702ec11d420338910d148fc7b039d67568ad3a0e032", - "5cab231048032dbf921b4fafa1951dd2da93bc3740667f31d5a1d5665b141261", - "ffedb24be73441fbcd069f7785ebb865870e0f3ed228190732e4ffd5962bb82d", - "a4fcfec18adf92f4ed822f64d2da9f5ae630885a1bfa626530f641db99aa7a30", - "f98bcee41b0e3deafa1efaa1863750dbfd9bd7430b82529b670867d852230b5d", - "8ab8d5fca047a52364a737c1af57bf656c9ad5049f08ef4c5aa252e61aa72123", - "91318b39ad94c1d58143586b6d90dd6092a9d7487e321f4976967b6ac445ff43", - "fabfbd4569ab018e12d5ffa9b1a40ca8eb2ca60a685817351d90eaa770d5eccb", - "bbc5ef34428d980e2401942ceecfe07cdf21bfb1acae0596ea1d43fccf504f69", - "26943e4201ea407a5667103fe07ca6e08ef76940f274349b0e2e776bcfb0acb6", - "e3b305ffe33e72841f8e2a8688cc5cc27d42aee7624b33b7b6399b42db392437", - "17c5a763dd57e6bcc7c4cf2db0eb5cf3e97116b67fe0dd519c97e4a4d55d5a62", - "bbd260216879ce86af8318ffcf73c9e063ca76dd8bc35d3b6be45b2b4184888b", - "41285591d0595bc42ab663051b410d51af39fe1720592e27acb1a8af72360a76", - "f29acd6068ce494d0c0fe294cad91bb8968e3fff3f595a113227ab545c3ca3e6", - "ec9013c6394528e7dd788ce7cc085ca79fcdfbb37565999d5b4b5a4e39452ecc", - "27829bd7f1a8fcddcad0cc34a3b3fc67d62a2f3e09f8e75d35035c2281e83afd", - "666bea9db4e15087204d076294d221d4cf5864f5d94de38f29132b1934a17ace", - "a3a30924cad3dbda3446e5a6324e0a1390c70f795d5ecfe17ee5c70b14f7d87d", - "19567fe5fdb10711d60aa4d9843e1c49c2a6d2fd1b5cf662e2105606bb7815d3", - "b139f1c3a2f15596b9320334e37e4184d5d584c4a81e72d821a7edcad3aa62d5", - "08f1531e0e3e8f8bae313b2c60a72d5601bf8b60d7a4d2f60e8481650340d250", - "c5895669e1ff182bf1dd6c00dc955265e08ded0952b8ca62a1c63ba11c99f4ca", - "84d1c28153f66c1a4eb5fa0df695e936d83294df31a08d8d8e2d4798d19d8ce0", - "b8699f6af853fdbe897848feb46a05d733363f304eac4c8c1932e6ea4bc062cb", - "10eb3f6c1d0661468d9ed83593e5e9c0b43c6feec6a5405a498194905ea6ed48", - "509e215a600d9cadcbf5d62632ba321d7314764218db00ce8c907e425fccc445", - "e62119b7be84c8eaad41ba7f4a35da403f4ed96b967a3134e89ee8b78f8792c2", - "f790754a95d59ea5ffe6b9b5cc386c600a9e19e8bec997c192764365f1d05376", - "990121b5aa4d6badfb7154db4cdbb4512124bc2f442bebac71ea90b5cc86f203", - "b6983dedaa891eb14c964d84461e5cd37ed27b61771c64978ba83e3ecea194fa", - "00fba1ceaa6aa1e378cd5b22a771d6070250ac37f4e17d7bf1a70b3139e9a860", - "429854e7738abf2ecf46909454039e2fc5a080eb9a3c0c5ea13b07426dac3ad9", - "ceb3e017944b0dd787be219d8629930b3f2e20e22b12dc88fd838488ebb896f3", - "eb9e5d14424c63e675fe771b73ca865f7d38cf334d65e2426e12a0b88c1a2236", - "556ee713449e6e59ac4b8b2e8310180c8f6280484e9db23456526cceb9216168", - "bc89c3aa889e0144ac526a1f861227430dde7e439cc6a7e9b25c9a049c3ca7b3", - "56d070c62ea99be66fff741a8e45fafda5f9ff783e84d5395b251f356ce4e16f", - "ace15859c399e5ecd13b1420d3c3703c6a88dfb4a084f7225e7ba40a4b444fc8", - "f03f1261ab6eb879fe9c5b0028cd400b3ffdfac4344e4c75f6cde3c05ded1f26", - "955b2fda8d0068226f89270028b316b5adac871f1c1c85435479aba14a381b0f", - "422509a98d7461a6b8ec87cbb173b2486577b59ea9b269e01c20567b38b3b3b2", - "007d4de62ad89a4f5985f0cd9b76a7293acf383b4e9e734e813b9df1d57f986f", - "13a04e32948225b7e22aa0137341ebbb811e0743032fac16be9d691a652db2eb", - "8244b11d880a52f9f9e1830a822e6eeeaf0b12fc759f8261bc2f490cb0794f3b", - "27d3415f8f8fd3048a1ee0d9487256dd1b0f9e31be663778efa5b3add63868ec", - "0053f888db916a8905320e253fe2f0666355e6fb6de36f897231920a3edfe39f", - "0bc5c0a2ea47fa3bb8be107e3b9d6b7226b1c8bd16ca1bab8f51b8e1de08aa8b", - "4ca13aaa161c79025b5cd6c9a8ac33554f5ceb479fe293d9a342c865cd9c9948", - "333afbe82e2a3df38bd1ef998f39c5feef2554697aa21b5410c0e95ef9156249", - "587c4fcabd18ff890064171fce3d5be0c4aa1bba46893fb6a827a85ab54d20f3", - "964328e4d51d67c4e2f1fd102a66b861d98199f81d18d660b1b4b52504cd772a", - "196aad5594651efd679d30b9feb0f0d172cf977b4f89aa670ec741a8bf21e669", - "9137bfd66bbf47bfa0bfcbb9f6e71b6eb3fd9776530e9fd30d3dab19e182f35d", - "8217392c4ed2313188f295d94148a027a7f43824a5f5fba33a0b8c1045d509b4", - "be9e12761519a4836e73015544163254877e1c4912fcea67a10e7026421dde75", - "7b5220421a520b876cc6cdba0d3895104d7fac914dca5b93f9fe8b226566b71e", - "5c83fccfeb4bf0eb8a94d43ebc84a81639a32f26c7ef77d0a2b626b7de7befdb", - "132fd6c92cf176f975efdb5ded53470b462a48a2815c6f54a93ce4f935784cc7", - "46a3dba364022d11aa616a2bc20e3be5c4390f38b9446edfa464d90d9af5d48f", - "34b3f3fd8a83905a37762060f51d0b116377b4820b031b8e668e16f46c5b0285", - "f0e397e033dabec859a4b9a9043c5f1fb0dba504764d6bcf2fe9bf2ffd318474", - "85ecf59c7dd3b24ad17f591bc4737f32f1384c370a7a6f2add06a811dc824b6c", - "4d03cdb1e6ad8e066a83654626d8c221433e8d4fd901c671300af37e000177f2", - "61cb9c651893e6401b25f2bdf79c9f3ddc9ffe69cf6c791c420462bd73e347e1", - "85f2686a42158cd5ad327781ecccd1bdcd346941dd4b4edc45f717de6a011800", - "92de2ab82cac528e6d4ccd61e5b7c79591dcad9909c8ad3c8276ece6d48c0f08", - "23a878a06bb94bff33083349149f3c860f2b00bc3fb28f04cbaf717c08af19a3", - "1b1cce18ff0323566b192885d7ced30f9a9531a2580240f2c593a7d5b8580974", - "08fcdec7ea1376d84f3b13a47a4b73c7781c9c7890bb28f712b58af4fd3f24fe", - "03cc08fc4ece807c6495272c412be23b045622cc6b786ed8d5c94156ae678a0e", - "c4d8a61dc3f5dcf4b83f27a90cbc37e816cf4754e12309626ec5679c99087c46", - "b29d00681e29001cdd63c4bc50e5e25715faef692aeebb678c8050e1c095e888", - "ac154617e93f2bb1afa232675f2135437a9cc9700c14c51c40084946596ba11a", - "ce9549de8e68ae89f424dd9e1cde8a4eea2069da667cfcfbe837691d37366668", - "426c45a98e2af35cc9708149f6c086ff5a3972e77d62c627d5e20de5d731cad8", - "7e21bfe240a3d9b77a129c734a1d428dbc890379fbaf862853f48b2f7470b2b0", - "fa090a71f77223a7210de6db18d9aa809e89fb15253aea28131df6c5a7639140", - "7094ad044c5ab025e088b43aa0b947601fabe58ed700a412fd96e4b917ced0c8", - "936d5cdc4f081b6fe36c356af4378d472cd7990303f2ea44da645afd7d5d7f9c", - "05342037d3b69349dce7b95529d4b2a63ceb9d9393217a68f7cc8c958a96c3ea", - "ff9e1c414ef27b1178b1de296526f50520b7ddb06286bf9c47792bfb449e40b6", - "2f2b7bedb34d2854b17ccb702cddd8bc0157e39721d58be0b2ad54ee291fc9f1", - "0d8db1f34140bbf7eb809137018a74af08cb3345b8a3e368cdda8521dab45791", - "b109e4bfabcfe4a1c38be1156d9ca851c75e6aa2e57c0869e40cd9056f571e07", - "5cb363547ca077c806fc69bc8c2006831ab89e72fd778ac1a48fa810934e350e", - "85ee928bb110fd64eae54a91fc8548883e7fc4c60a3c61b505c31cea2d295c86", - "1ec3df7d10ee6fd5f0532ad4fe771e6befc28b0bed0250bf523695d6d49a8246", - "de9db2fc07c866bd7b885fb41522b63d550d0ce2e8ac5e14464a41733c2319e6", - "9a27136422a8f56768db29ba172a7ba26c3c7aa910324e78e5ab3a3268ac3674", - "60213c315119bf9005cb533d1a5b403b4a13c59982fe7773d30fdd8f519f4205", - "40eb61ef1812eb8a4d389599bf449fc86653b2c4986061b952f46fc049de53f4", - "658ef0d8140162b5f04591be13b47456245f531208bbca3260b857ca09b803c5", - "02270fa66255048d724894e2206b4e773cc6a7b6d17ca090cdc25f317d5f53b9", - "2ec6a0147f419161f7198d05be5f93152d9ccc10672db0ea47ff1687c0f0dd15", - "4be1d8ceb96eb80ef7ce30079ded31163272aeccff5c18fe3aaa32ea2f5bed9d", - "04ecaf48f44de87243b17b4c71ebff00020738639336010fa57435a54b623798", - "e313a9feb7cfd1d56ec87b1f1062ff9a80da498f7b761af4bef0cecd1b4c385b", - "ede3748f971f22341f7f5844dc60fc03cdb30c7cc720ebe13ae588c17a78aa94", - "d90c0faa70e39b7c0a8c55457ed6e6478a4e4bf3707b08104326a1ee8377c3ab", - "c79ffd0bbc8d004cc542e212990df6498abddd3deb50fd00ed00a2ff690974d6", - "35c37d88cf73a89c4124b0ee537347c37fdb47156c8b0ecc509efc58236ed3f0", - "a99182f343ccf05e557ffa6df71f03688b2afcf314c59daa774fe78db6f47add", - "01115397a78af8a4ae2727ca7a01843235b626bd3db80888d3dfd0020d4135e2", - "4a55aced578470d2f7280096d7fe8095f294095fba4778d1977d6db9270472f0", - "4624adf8a5633f65b213b8ca46b55cb0ee36c41495f39b1ae70cbd545779b1a0", - "d72bcd5b57a9c47e7bd5e9a1103657d10beb7b6c6d41f2b2985bc3bd3cc74860", - "48baadb9a46293c92f29e7617846171356a42c3b5d18d49a05a7e173993785f7", - "3da927737af8cb0e1c77097e35c54158d18aabfb3051c45bcc7ddfe00b157b1f", - "b4a24bfdb2cb802c8d48a3a18fbfe18622a767fe7eddfca57d4555550ccd1643", - "c58f82ac7c49dcba1721a88358f07636c9df60d3fd383e5789b808dc57a1dc9d", - "5e1f756eff5155df073d30f4452bdafc4adaf4f35960771bf2c1e30137fd7a79", - "be4a332f289338d67bd4834eae3128c488a61d255e972da484b6252b67a46b89", - "d496e4a36238d03a83d8b45cf33d9388aa7568a279b034d1cdd87b457356cc5b", - "a1c5212730ccda34de393210e276bbd44720dda777bcfd602315a3eee582f7dc", - "08914ec63f6ef7fe1d678937dc0f6178883440b26b4aca29fce79068947e8397", - "49e2cd2bd9b974074d9814f93eed371620bd4ea5fbf97a625065704e8fb382d7", - "047c194111818b48ce93a4b006e4a09b9a2650757a87357111796e11e847bf23", - "5955b0baa8265341f35a6f24fdc79066ba3ca9c5354c69b6b37a9ef3a26be556", - "d7c962f3ea1938c5267cca4072548acf3afcce4d438ed62027caa211a5f98e8b", - "c8cfba67cb4ce7e291b35154a50476d9a5c6bbb5d6cbaaa5d2408547fea7b02b", - "de8a940d8a69a64ce264d2ab7320662aef2e391c587cb2aae22a86718d5dffbb", - "94176f1310b26e54d4de48f87b74aa0b60532f184a2268508dece86dd7f85d36", - "9ce6ee3fca56c9256a69df404782301300a6e5e7f5a25a1f6d68c0e9e42584c9", - "7c423c4a220c6ff43ab6432f92b166323c58ee77f8c096ba0b00d52d7bd507e8", - "0b62b9c1ae4d4988720e8d41d980b334458189de0a3dd01699338d7b07c3894e", - "64f45f6f75110624506c53716f2fc1d5fe5f88f82a5bc6a7459ce70eae56dbb3", - "12ffbeb8e52fed161af4d8a015d1a5c45dcb8240e5c8933ce3a88ba2c58f97e8", - "2ee6b7b96043c8ded9fb52f87cdd0d0580ca6f8cef183c8a656394a11c0aa393", - "aaef26b1f5726258bf9ce305a3e54bca65cf68779f90f9d24287245c27362e27", - "ef59588dce57c35d010bea4d209f44c62f0b7c7e65bc0226c0e4971934da9435", - "0e606c2f6f8dcd579faf4739312bd7327ac7796fa44a81780fe0d66fc7761fb9", - "2f307198afdbde5f95989a17e06ce1bb9ff36c441cf3b2248431534fe13bb9fe", - "51418e6df23d450aaacc74ef2df53a6b1693727b70bda9ebc43acfc23d8fb5ea", - "6e9e3ac46705ed80520695b924435b00d2b3079598bf7faca7fd1524be777e5e", - "1e96241e2876aad29ce64f5d7e7fbb8db7265449df816c0d30a96633778c5cb6", - "81788f00eb72696d811f946e65d2c96528c45590874a1defdd46651e9b79a3a1", - "d9aa5a9f1df50e933d7105a5d72b5fe96bfbb9fd4b5b0eaa5e80af12e72d497a", - "e1b6976a732d27fc5d6a96b6d3d0d1d5eaa6ec46bae4665f17e7a43aefc75280", - "9151c75edcec1cc90aa2d2c240ff657b0eef3f5f1ec37418c8854b2493114f1d", - "3e7c12d0132421f08ea0a390cfa325e422a6b35120fb2eb650f108a165237934", - "1ee0e85c7d8a91089c03f37318cdc9127026bf789e3ce4b75046eaa3eebd3458", - "4ae64a3ef66cad847409ce175bd5365c9097fd21647a05730ac6b45841add3c8", - "f0ea0f334cf1d64678a6dab08c07e2f94f339e8389bd17ebc882b5c8b736cbf2", - "da904db96060546ff69e28993ce8183766da9402ac10fae9fc1f1d67ebd83c90", - "db11820615f7b5e47778c45d2e083e77f49b608b587dc09ec26f077aba07a242", - "ff5c726a83bd785484de75bb03b421f9e8e382bf2740120a2fcf72326aa01c75", - "f8643a7efd6304980db323303ab73a6fd4f4ee1047520d39d571580395b97f21", - "8facf9737d07838aedf6030593bfb247d8c29fe8d9b18b2913408627a4424d7e", - "f0672964aa6e4c7dd4768e18827023787386927f4db89fd661444979afb43c18", - "6fd3649c8401f2704ed2be18518b870eb6bf2b9a6689d1b336f05bd8b49017f6", - "ed172dac7de827493e0c0fdd8d3299333acb678e72ed499e0224b389cc1e0fba", - "7f9a8a8cc8e34add11934a1a5882be5978a6d28405cb0a053ec5699a502b1cef", - "6ca829ebd2a0a40994f68c1db7978ec274b45c46e9b351df869a2bfe0630bbd4", - "0bbb017c437573a55db88258a9d9a01188bdd23bb6b26903b137814871661f47", - "9a6358d2541f46b6d05b80fe25a2cb025fbe9e4b227a6275908d5ee31c948569", - "a75d26c6d4ce944024f10e6d23e8b5b888d680120e15dc0e4fee8d8833ee0c6a", - "1d43d33556699b42c124b46e41243abf48727fe488428056fdd174a3861c1e3c", - "7b5bd3fecbaa093f005c4f806ea67846c0df6b04df7729925cb14724f6a8b582", - "6bdf2b54f2f5ab90191261d33dee80fede75896994016422b28db8ba62327d82", - "1a16181b250085a91ccecc118473fa2ab98515e894a7b63b347c24b5be560c7a", - "22d24891755910b48ad632358c26245bdcc375abc41f7e2c9fb3c7773dbf4e22", - "5b70c5d4a373d541619c944fcc3b61259550b0e9fba3eca16f0879e5845b43b9", - "b78f9098c9d76987b7409e63426a8d49972bb4e75289576c680cf96513d44b6d", - "916b53b8e85eb7e0a2a76d6fc8d2163430e7183ccb103d6705f54af4bb070907", - "dc3d78f43110d2aa9df83c5485ec33663ad5452b8cdeb1aeeac9d6b1487fb781", - "3975539ed5402cb9f5ab503584524dd141cc4296b666ec66d807f94f62b1c026", - "bd1f97fd89183643423073f22733880616456ca41960699f18e868cb9ac35508", - "90b468ff0f83460c3dd8cfe778c39d32c6bb1eeff9ac5de7804a4050d3b8073e", - "0e886d49d88b82c9f8dbdd2f38a535992f35ab16629724d746394db3898235fa", - "76c22e965242d1ca5614e829d9028dcad9c4b09393bcbdd318b0365557335fb9", - "59b168488ec8629f820a1efd8fc5a0c2adec4253e61d6a2945a68a9a43be9035", - "ff172b42854eaf2865caa985d2fb6283c5ab19574126623ffd615a761a5bce72", - "cb46ac9ccc024ee74c96e3cf1c13a6949a432e855dfa881b6a307c0e6daac59e", - "9971574924c0f413bf4c0f96bb9c2fbdfea8f475e33a8fb6f15fa40903b63444", - "1a95567deb0f45a8941e2248f33286485984a5e9d86d16c37d42169ad864dc36", - "205fc7f7ec7a83f0bc22d5269c91762cd00adc7428456d799be5a0cd76f08b0f", - "849dd41ef59a722901b7a0deb2c1fd3c110a91a726120a0a119cb7a15cf98438", - "a32880917c714612101af95e5c8d8eb5fb046fdcc68bae76c05b829b3fa73c2e", - "70b38d6d510d13b359dffa910329952c620a4bce4ee7a8552b9bb3a14572394d", - "ef257ab2f4226faa6ba288a6793f026609068effb866c18496a847e8b60b102d", - "e5196ab42ff53c8352288bde6b6b7312cd6f39f7d21b556b0db178d8470d5790", - "ca98f128bf085f2b718f2b3c12da7c4d98887cc94251a2b1705b637611bd83bb", - "79508f0b93a49ec19c5cb05906ca1ba3d3db8ed4f9c6884873d0d7e3e985ea51", - "9088be3f47f9debc63e928739f7163182b49eab044518b151f0b89f6b6aefdd0", - "46b2782fd669b6288a4d7348cf6671360277ba4864cc69bce3497369ac2ec31e", - "0fa5131557db67b430d516530be939ff25882adf68a076602f3dfad8c77c963a", - "3404302cc097d5457244453a4c9990804201ee8161188df811bcb32404998c71", - "856939710dbb90a8eeda875a31f9a52af759bd932b88e7b08df35414c54d4721", - "72569573b9b41d0ac5ce17764a139c6b8b36ef3ca6d92cec625dbcdae758ba22", - "9746da344e435a008d6acb4847211bb676376ecc76c825b5d44a28b89ceeb40e", - "3eafded1595516f032e33ec975f4c9c3a1055d13aa5575cf8a801d6103fdbeb4", - "e88a6d2daa863c0787cc523a2cab45c546fad788951b10d75e2b0954db24cca7", - "38f531e67f88f66de44d3357c8e8f2db456160ca31dd2024c9562f6afd260278", - }; - // block 685498 (13 key images in one transaction) - static const char * const key_images_685498[] = - { - "749b7277aa21c70c417f255fb181c3a30b44277edf657eaaebf28a2709dd2a90", - "5a9b3e1a87332d735cedaa2b5623a6a5e99d99f5a2887c8fc84293577e8bf25c", - "bea438768445eb3650cf619bf6758e94035abfe0ccda91d4a58c582df143d835", - "376e237ff4da5e5cbd6e1bba4b01412fa751f2959c0c57006589f382413df429", - "14ac2f2e044f8635a3a42ecb46c575424073c1f6e5ed5e905f516d57f63184b5", - "2d1d2ecb77b69a2901de00897d0509c1b7855a3b2f8eb1afe419008fc03cd15a", - "ea01658f0972b77ae9112d525ec073e3ec5c3b98d5ad912d95ab2636354b70b6", - "d3934864a46101d8c242415282e7fc9ee73ad16cd40355535d226ab45ecdb61a", - "ee379b05c5d02432330ebd4ea9c4f1c87d14c388568d526a0f8a22649a14e453", - "aeb7b842b410b13ca4af7a5ffd5ae6caddc8bfec653df1b945e478839a2e0057", - "451806929d9f5c3a7f365472703871abadc25b2a5a2d75472a45e86cd76c610b", - "272d9b9fcc9e253c08da9caf8233471150019582eaefef461c1f9ceff7e2c337", - "633cdedeb3b96ec4f234c670254c6f721e0b368d00b48c6b26759db7d62cf52d", - }; - - if (height() > 202612) - { - for (const auto &kis: key_images_202612) - { - crypto::key_image ki; - epee::string_tools::hex_to_pod(kis, ki); - if (!has_key_image(ki)) - { - LOG_PRINT_L1("Fixup: adding missing spent key " << ki); - add_spent_key(ki); - } - } - } - if (height() > 685498) - { - for (const auto &kis: key_images_685498) - { - crypto::key_image ki; - epee::string_tools::hex_to_pod(kis, ki); - if (!has_key_image(ki)) - { - LOG_PRINT_L1("Fixup: adding missing spent key " << ki); - add_spent_key(ki); - } - } - } - } - batch_stop(); } } // namespace cryptonote diff --git a/src/blockchain_db/lmdb/db_lmdb.cpp b/src/blockchain_db/lmdb/db_lmdb.cpp index 37907175f..e671f8862 100644 --- a/src/blockchain_db/lmdb/db_lmdb.cpp +++ b/src/blockchain_db/lmdb/db_lmdb.cpp @@ -742,7 +742,7 @@ void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t l bi.bi_diff = cumulative_difficulty; bi.bi_hash = blk_hash; bi.bi_cum_rct = num_rct_outs; - if (blk.major_version >= 4) + if (m_height > 0 && blk.major_version >= 4) { uint64_t last_height = m_height-1; MDB_val_set(h, last_height); @@ -1224,7 +1224,7 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags) if (is_hdd_result) { if (is_hdd_result.value()) - MCLOG_RED(el::Level::Warning, "global", "The blockchain is on a rotating drive: this will be very slow, use a SSD if possible"); + MCLOG_RED(el::Level::Debug, "global", "The blockchain is on a rotating drive: this will be very slow, use a SSD if possible"); } m_folder = filename; diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt index 24a750eb0..fcef05c56 100644 --- a/src/blockchain_utilities/CMakeLists.txt +++ b/src/blockchain_utilities/CMakeLists.txt @@ -141,7 +141,7 @@ endif() set_property(TARGET blockchain_import PROPERTY - OUTPUT_NAME "monero-blockchain-import") + OUTPUT_NAME "wownero-blockchain-import") install(TARGETS blockchain_import DESTINATION bin) monero_add_executable(blockchain_export @@ -162,7 +162,7 @@ target_link_libraries(blockchain_export set_property(TARGET blockchain_export PROPERTY - OUTPUT_NAME "monero-blockchain-export") + OUTPUT_NAME "wownero-blockchain-export") install(TARGETS blockchain_export DESTINATION bin) monero_add_executable(blockchain_blackball @@ -184,7 +184,7 @@ target_link_libraries(blockchain_blackball set_property(TARGET blockchain_blackball PROPERTY - OUTPUT_NAME "monero-blockchain-mark-spent-outputs") + OUTPUT_NAME "wownero-blockchain-mark-spent-outputs") install(TARGETS blockchain_blackball DESTINATION bin) @@ -206,7 +206,7 @@ target_link_libraries(blockchain_usage set_property(TARGET blockchain_usage PROPERTY - OUTPUT_NAME "monero-blockchain-usage") + OUTPUT_NAME "wownero-blockchain-usage") install(TARGETS blockchain_usage DESTINATION bin) monero_add_executable(blockchain_ancestry @@ -227,7 +227,7 @@ target_link_libraries(blockchain_ancestry set_property(TARGET blockchain_ancestry PROPERTY - OUTPUT_NAME "monero-blockchain-ancestry") + OUTPUT_NAME "wownero-blockchain-ancestry") install(TARGETS blockchain_ancestry DESTINATION bin) monero_add_executable(blockchain_depth @@ -248,6 +248,6 @@ target_link_libraries(blockchain_depth set_property(TARGET blockchain_depth PROPERTY - OUTPUT_NAME "monero-blockchain-depth") + OUTPUT_NAME "wownero-blockchain-depth") install(TARGETS blockchain_depth DESTINATION bin) diff --git a/src/blockchain_utilities/blockchain_ancestry.cpp b/src/blockchain_utilities/blockchain_ancestry.cpp index 2f0bbffd6..351db4e1d 100644 --- a/src/blockchain_utilities/blockchain_ancestry.cpp +++ b/src/blockchain_utilities/blockchain_ancestry.cpp @@ -282,12 +282,12 @@ int main(int argc, char* argv[]) if (command_line::get_arg(vm, command_line::arg_help)) { - std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; + std::cout << "Wownero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; std::cout << desc_options << std::endl; return 1; } - mlog_configure(mlog_get_default_log_path("monero-blockchain-ancestry.log"), true); + mlog_configure(mlog_get_default_log_path("wownero-blockchain-ancestry.log"), true); if (!command_line::is_arg_defaulted(vm, arg_log_level)) mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str()); else diff --git a/src/blockchain_utilities/blockchain_blackball.cpp b/src/blockchain_utilities/blockchain_blackball.cpp index d2ce5cf76..998196286 100644 --- a/src/blockchain_utilities/blockchain_blackball.cpp +++ b/src/blockchain_utilities/blockchain_blackball.cpp @@ -1029,7 +1029,7 @@ int main(int argc, char* argv[]) const command_line::arg_descriptor arg_rct_only = {"rct-only", "Only work on ringCT outputs", false}; const command_line::arg_descriptor arg_check_subsets = {"check-subsets", "Check ring subsets (very expensive)", false}; const command_line::arg_descriptor arg_verbose = {"verbose", "Verbose output)", false}; - const command_line::arg_descriptor > arg_inputs = {"inputs", "Path to Monero DB, and path to any fork DBs"}; + const command_line::arg_descriptor > arg_inputs = {"inputs", "Path to Wownero DB, and path to any fork DBs"}; const command_line::arg_descriptor arg_db_sync_mode = { "db-sync-mode" , "Specify sync option, using format [safe|fast|fastest]:[nrecords_per_sync]." @@ -1071,12 +1071,12 @@ int main(int argc, char* argv[]) if (command_line::get_arg(vm, command_line::arg_help)) { - std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; + std::cout << "Wownero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; std::cout << desc_options << std::endl; return 1; } - mlog_configure(mlog_get_default_log_path("monero-blockchain-find-spent-outputs.log"), true); + mlog_configure(mlog_get_default_log_path("wownero-blockchain-find-spent-outputs.log"), true); if (!command_line::is_arg_defaulted(vm, arg_log_level)) mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str()); else diff --git a/src/blockchain_utilities/blockchain_depth.cpp b/src/blockchain_utilities/blockchain_depth.cpp index dd2387e5b..4dc6f6cef 100644 --- a/src/blockchain_utilities/blockchain_depth.cpp +++ b/src/blockchain_utilities/blockchain_depth.cpp @@ -97,12 +97,12 @@ int main(int argc, char* argv[]) if (command_line::get_arg(vm, command_line::arg_help)) { - std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; + std::cout << "Wownero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; std::cout << desc_options << std::endl; return 1; } - mlog_configure(mlog_get_default_log_path("monero-blockchain-depth.log"), true); + mlog_configure(mlog_get_default_log_path("wownero-blockchain-depth.log"), true); if (!command_line::is_arg_defaulted(vm, arg_log_level)) mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str()); else diff --git a/src/blockchain_utilities/blockchain_export.cpp b/src/blockchain_utilities/blockchain_export.cpp index 5a49f3478..1fe6d8360 100644 --- a/src/blockchain_utilities/blockchain_export.cpp +++ b/src/blockchain_utilities/blockchain_export.cpp @@ -97,12 +97,12 @@ int main(int argc, char* argv[]) if (command_line::get_arg(vm, command_line::arg_help)) { - std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; + std::cout << "Wownero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; std::cout << desc_options << std::endl; return 1; } - mlog_configure(mlog_get_default_log_path("monero-blockchain-export.log"), true); + mlog_configure(mlog_get_default_log_path("wownero-blockchain-export.log"), true); if (!command_line::is_arg_defaulted(vm, arg_log_level)) mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str()); else diff --git a/src/blockchain_utilities/blockchain_import.cpp b/src/blockchain_utilities/blockchain_import.cpp index b78b8b5c9..a268415e9 100644 --- a/src/blockchain_utilities/blockchain_import.cpp +++ b/src/blockchain_utilities/blockchain_import.cpp @@ -643,7 +643,7 @@ int main(int argc, char* argv[]) if (command_line::get_arg(vm, command_line::arg_help)) { - std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; + std::cout << "Wownero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; std::cout << desc_options << std::endl; return 1; } @@ -681,7 +681,7 @@ int main(int argc, char* argv[]) m_config_folder = command_line::get_arg(vm, cryptonote::arg_data_dir); db_arg_str = command_line::get_arg(vm, arg_database); - mlog_configure(mlog_get_default_log_path("monero-blockchain-import.log"), true); + mlog_configure(mlog_get_default_log_path("wownero-blockchain-import.log"), true); if (!command_line::is_arg_defaulted(vm, arg_log_level)) mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str()); else diff --git a/src/blockchain_utilities/blockchain_usage.cpp b/src/blockchain_utilities/blockchain_usage.cpp index 38a0b2648..61be42b44 100644 --- a/src/blockchain_utilities/blockchain_usage.cpp +++ b/src/blockchain_utilities/blockchain_usage.cpp @@ -129,12 +129,12 @@ int main(int argc, char* argv[]) if (command_line::get_arg(vm, command_line::arg_help)) { - std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; + std::cout << "Wownero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; std::cout << desc_options << std::endl; return 1; } - mlog_configure(mlog_get_default_log_path("monero-blockchain-usage.log"), true); + mlog_configure(mlog_get_default_log_path("wownero-blockchain-usage.log"), true); if (!command_line::is_arg_defaulted(vm, arg_log_level)) mlog_set_log(command_line::get_arg(vm, arg_log_level).c_str()); else diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp index 3f2563e90..abf6d62be 100644 --- a/src/checkpoints/checkpoints.cpp +++ b/src/checkpoints/checkpoints.cpp @@ -163,54 +163,59 @@ namespace cryptonote { if (nettype == TESTNET) { - ADD_CHECKPOINT(0, "48ca7cd3c8de5b6a4d53d2861fbdaedca141553559f9be9520068053cda8430b"); - ADD_CHECKPOINT(1000000, "46b690b710a07ea051bc4a6b6842ac37be691089c0f7758cfeec4d5fc0b4a258"); - ADD_CHECKPOINT(1058600, "12904f6b4d9e60fd875674e07147d2c83d6716253f046af7b894c3e81da7e1bd"); return true; } if (nettype == STAGENET) { - ADD_CHECKPOINT(0, "76ee3cc98646292206cd3e86f74d88b4dcc1d937088645e9b0cbca84b7ce74eb"); - ADD_CHECKPOINT(10000, "1f8b0ce313f8b9ba9a46108bfd285c45ad7c2176871fd41c3a690d4830ce2fd5"); return true; } - ADD_CHECKPOINT(1, "771fbcd656ec1464d3a02ead5e18644030007a0fc664c0a964d30922821a8148"); - ADD_CHECKPOINT(10, "c0e3b387e47042f72d8ccdca88071ff96bff1ac7cde09ae113dbb7ad3fe92381"); - ADD_CHECKPOINT(100, "ac3e11ca545e57c49fca2b4e8c48c03c23be047c43e471e1394528b1f9f80b2d"); - ADD_CHECKPOINT(1000, "5acfc45acffd2b2e7345caf42fa02308c5793f15ec33946e969e829f40b03876"); - ADD_CHECKPOINT(10000, "c758b7c81f928be3295d45e230646de8b852ec96a821eac3fea4daf3fcac0ca2"); - ADD_CHECKPOINT(22231, "7cb10e29d67e1c069e6e11b17d30b809724255fee2f6868dc14cfc6ed44dfb25"); - ADD_CHECKPOINT(29556, "53c484a8ed91e4da621bb2fa88106dbde426fe90d7ef07b9c1e5127fb6f3a7f6"); - ADD_CHECKPOINT(50000, "0fe8758ab06a8b9cb35b7328fd4f757af530a5d37759f9d3e421023231f7b31c"); - ADD_CHECKPOINT(80000, "a62dcd7b536f22e003ebae8726e9e7276f63d594e264b6f0cd7aab27b66e75e3"); - ADD_CHECKPOINT(202612, "bbd604d2ba11ba27935e006ed39c9bfdd99b76bf4a50654bc1e1e61217962698"); - ADD_CHECKPOINT(202613, "e2aa337e78df1f98f462b3b1e560c6b914dec47b610698b7b7d1e3e86b6197c2"); - ADD_CHECKPOINT(202614, "c29e3dc37d8da3e72e506e31a213a58771b24450144305bcba9e70fa4d6ea6fb"); - ADD_CHECKPOINT(205000, "5d3d7a26e6dc7535e34f03def711daa8c263785f73ec1fadef8a45880fde8063"); - ADD_CHECKPOINT(220000, "9613f455933c00e3e33ac315cc6b455ee8aa0c567163836858c2d9caff111553"); - ADD_CHECKPOINT(230300, "bae7a80c46859db355556e3a9204a337ae8f24309926a1312323fdecf1920e61"); - ADD_CHECKPOINT(230700, "93e631240ceac831da1aebfc5dac8f722c430463024763ebafa888796ceaeedf"); - ADD_CHECKPOINT(231350, "b5add137199b820e1ea26640e5c3e121fd85faa86a1e39cf7e6cc097bdeb1131"); - ADD_CHECKPOINT(232150, "955de8e6b6508af2c24f7334f97beeea651d78e9ade3ab18fec3763be3201aa8"); - ADD_CHECKPOINT(249380, "654fb0a81ce3e5caf7e3264a70f447d4bd07586c08fa50f6638cc54da0a52b2d"); - ADD_CHECKPOINT(460000, "75037a7aed3e765db96c75bcf908f59d690a5f3390baebb9edeafd336a1c4831"); - ADD_CHECKPOINT(500000, "2428f0dbe49796be05ed81b347f53e1f7f44aed0abf641446ec2b94cae066b02"); - ADD_CHECKPOINT(600000, "f5828ebf7d7d1cb61762c4dfe3ccf4ecab2e1aad23e8113668d981713b7a54c5"); - ADD_CHECKPOINT(700000, "12be9b3d210b93f574d2526abb9c1ab2a881b479131fd0d4f7dac93875f503cd"); - ADD_CHECKPOINT(825000, "56503f9ad766774b575be3aff73245e9d159be88132c93d1754764f28da2ff60"); - ADD_CHECKPOINT(900000, "d9958d0e7dcf91a5a7b11de225927bf7efc6eb26240315ce12372be902cc1337"); - ADD_CHECKPOINT(913193, "5292d5d56f6ba4de33a58d9a34d263e2cb3c6fee0aed2286fd4ac7f36d53c85f"); - ADD_CHECKPOINT(1000000, "a886ef5149902d8342475fee9bb296341b891ac67c4842f47a833f23c00ed721"); - ADD_CHECKPOINT(1100000, "3fd720c5c8b3072fc1ccda922dec1ef25f9ed88a1e6ad4103d0fe00b180a5903"); - ADD_CHECKPOINT(1150000, "1dd16f626d18e1e988490dfd06de5920e22629c972c58b4d8daddea0038627b2"); - ADD_CHECKPOINT(1200000, "fa7d13a90850882060479d100141ff84286599ae39c3277c8ea784393f882d1f"); - ADD_CHECKPOINT(1300000, "31b34272343a44a9f4ac7de7a8fcf3b7d8a3124d7d6870affd510d2f37e74cd0"); - ADD_CHECKPOINT(1390000, "a8f5649dd4ded60eedab475f2bec8c934681c07e3cf640e9be0617554f13ff6c"); - ADD_CHECKPOINT(1450000, "ac94e8860093bc7c83e4e91215cba1d663421ecf4067a0ae609c3a8b52bcfac2"); - ADD_CHECKPOINT(1530000, "01759bce497ec38e63c78b1038892169203bb78f87e488172f6b854fcd63ba7e"); - ADD_CHECKPOINT(1579000, "7d0d7a2346373afd41ed1e744a939fc5d474a7dbaa257be5c6fff4009e789241"); - ADD_CHECKPOINT(1668900, "ac2dcaf3d2f58ffcf8391639f0f1ebafcb8eac43c49479c7c37f611868d07568"); - ADD_CHECKPOINT(1775600, "1c6e01c661dc22cab939e79ec6a5272190624ce8356d2f7b958e4f9a57fdb05e"); + ADD_CHECKPOINT(1, "97f4ce4d7879b3bea54dcec738cd2ebb7952b4e9bb9743262310cd5fec749340"); + ADD_CHECKPOINT(10, "305472c87ff86d8afb3ec42634828462b0ed3d929fc05fa1ae668c3bee04837a"); + ADD_CHECKPOINT(100, "a92b9deae26e19322041cbc2f850fa905748ae1e5bf69b35ca90b247c5cbfc04"); + ADD_CHECKPOINT(1000, "62921e13030b29264439cafaf8320cf8aa039ee6ba7ba29c72f11b50a079269a"); + ADD_CHECKPOINT(2000, "b3e1d73e3d0243239481aa76cb075cf2428556f5dc4f2e30428ea2ba36693e97"); + ADD_CHECKPOINT(3000, "83a6e1ab394e80b8442b7b70b0e4c3a9fa0143e0ca51a33e829537ef5dd1bf13"); + ADD_CHECKPOINT(4000, "7c70722d8cb8106b4bec67e1790614cc6e98db7afd0843b96cdff6960a0e0073"); + ADD_CHECKPOINT(5000, "331ee74008e174e5fd1956f64c52793961b321a1366f7c6f7d324e8265df34f6"); + ADD_CHECKPOINT(6969, "aa7b66e8c461065139b55c29538a39c33ceda93e587f84d490ed573d80511c87"); //Hard fork to v8 + ADD_CHECKPOINT(7000, "2711bd33b107f744ad8bf98c1acefa18658780079496bd2f3a36f2e20b261f8e"); + ADD_CHECKPOINT(7500, "5975967c4624f13f058acafe7adf9355e03e8e802eeadc84ccb22ea588bc0762"); + ADD_CHECKPOINT(7900, "d9bc18cb35feb6b26bc5a19bbdbf7c852d9cc02883acb5bbce2e87d8b2c86069"); + ADD_CHECKPOINT(10000, "bc5bfbf1b26c8f976d1d792ece4c6a7e93064bec62b72f1d5beae74c3f273b3b"); + ADD_CHECKPOINT(20000, "52cc7edcb49eb02f28a653b824089a726f4050eb210263ee6f4180d388a1e5cc"); + ADD_CHECKPOINT(30000, "d22fde5dd240ade16d3250eb0aa5d1c16dc7cb51c20484e05eb274911032b3fa"); + ADD_CHECKPOINT(40000, "aee0d642322542ba069cb1c58ab2acd3560f108d4682c3dc3cb15a54d442d91f"); + ADD_CHECKPOINT(50000, "5286ac2a0f39b3aefcba363cd71f2760bd1e0d763cbc81026ebdc3f80a86541f"); + ADD_CHECKPOINT(53666, "3f43f56f66ef0c43cf2fd14d0d28fa2aae0ef8f40716773511345750770f1255"); //Hard fork to v9 + ADD_CHECKPOINT(54500, "8ed3078b389c2b44add007803d741b58d3fbed2e1ba4139bda702152d8773c9b"); + ADD_CHECKPOINT(55000, "4b662ceccefc3247edb4d654dd610b8fb496e85b88a5de43cc2bdd28171b15ff"); + ADD_CHECKPOINT(57000, "08a79f09f12bb5d230b63963356a760d51618e526cfc636047a6f3798217c177"); + ADD_CHECKPOINT(59000, "180b51ee2c5fbcd4362eb7a29df9422481310dd77d10bccdf8930724c31e007e"); + ADD_CHECKPOINT(59900, "18cc0653ef39cb304c68045dba5eb6b885f936281cd939dea04d0e6c9cd4ae2e"); + ADD_CHECKPOINT(60000, "0f02aa57a63f79f63dafed9063abe228a37cb19f00430dc3168b8a8f4ae8016c"); + ADD_CHECKPOINT(61000, "509aca8c54eb5fe44623768757b6e890ae39d512478c75f614cbff3d91809350"); + ADD_CHECKPOINT(62000, "7fe91ad256c08dbd961e04738968be22fb481093fbfa7959bde7796ccceba0e2"); + ADD_CHECKPOINT(62150, "1a7c75f8ebeda0e20eb5877181eafd7db0fc887e3fed43e0b27ab2e7bccafd10"); + ADD_CHECKPOINT(62269, "4969555d60742afb93925fd96d83ac28f45e6e3c0e583c9fb3c92d9b2100d38f"); + ADD_CHECKPOINT(62405, "4d0ae890cf9f875f231c7069508ad28dc429d14814b52db114dfab7519a27584"); + ADD_CHECKPOINT(62419, "bd8bf5ac4c4fb07ab4d0d492bd1699def5c095ab6943ad3b63a89d1d8b1ce748"); + ADD_CHECKPOINT(62425, "41a922dba6f3906871b2ccaf31ec9c91033470c503959093dae796deda8940ea"); + ADD_CHECKPOINT(62479, "a2e8ff4205ba2980eb70921b0b21b5fc656ee273664ea94b860c68ca069b60dd"); + ADD_CHECKPOINT(62503, "25fa115962988b4b8f8cfd22744a3e653b22ead8c8468e64caf334fc75a97d08"); + ADD_CHECKPOINT(62550, "bde522a8a81c392c98c979434aa1dd9d20b4ca52230ba6ae0362872757808a48"); + ADD_CHECKPOINT(62629, "8368e1ce1d421f1fc969364558433e2b2363d0ffcb5f2d946633095e3e6734f5"); + ADD_CHECKPOINT(62720, "f871cddd75951e2fe24c282d2bd28396fc922ea519b354ace992a0162cb333ff"); + ADD_CHECKPOINT(62733, "8331dbeeaf23173d2235a062373a437befadb6492cceb7640127bf18653a9e61"); + ADD_CHECKPOINT(62877, "62d44adc05d7d4fd9d15239c5575612207beab0bcf2da49158bf89e365441ca1"); + ADD_CHECKPOINT(63469, "4e33a9343fc5b86661ec0affaeb5b5a065290602c02d817337e4a979fe5747d8"); //Hard fork to v10 + ADD_CHECKPOINT(69800, "5c65428a664738bc083d1ccd6a1b5ff4305f98e7633f44033816801429b33ce1"); + ADD_CHECKPOINT(75000, "e93492f79b5344e7edb31537ee65b3e908bf71110cff8188c0c62fefc015d342"); + ADD_CHECKPOINT(79500, "9bbfd6f2257ce9084de30179944b7695c9b918c9c03a8a63306ab6c5828ff857"); + ADD_CHECKPOINT(80920, "8fca818344f97ea3912557cbd8be659cf6a5bc1203514c27338e234251d72dfb"); + ADD_CHECKPOINT(81769, "41db9fef8d0ccfa78b570ee9525d4f55de77b510c3ae4b08a1d51b9aec9ade1d"); //Hard fork to v11 + ADD_CHECKPOINT(82069, "fdea800d23d0b2eea19dec8af31e453e883e8315c97e25c8bb3e88ca164f8369"); //Hard fork to v12 + ADD_CHECKPOINT(85000, "31d62ab75470b15aedee6674b78767b53f10951786e991c26035743c267b247a"); + return true; } @@ -255,22 +260,13 @@ namespace cryptonote std::vector records; // All four MoneroPulse domains have DNSSEC on and valid - static const std::vector dns_urls = { "checkpoints.moneropulse.se" - , "checkpoints.moneropulse.org" - , "checkpoints.moneropulse.net" - , "checkpoints.moneropulse.co" + static const std::vector dns_urls = { }; - static const std::vector testnet_dns_urls = { "testpoints.moneropulse.se" - , "testpoints.moneropulse.org" - , "testpoints.moneropulse.net" - , "testpoints.moneropulse.co" + static const std::vector testnet_dns_urls = { }; - static const std::vector stagenet_dns_urls = { "stagenetpoints.moneropulse.se" - , "stagenetpoints.moneropulse.org" - , "stagenetpoints.moneropulse.net" - , "stagenetpoints.moneropulse.co" + static const std::vector stagenet_dns_urls = { }; if (!tools::dns_utils::load_txt_records_from_dns(records, nettype == TESTNET ? testnet_dns_urls : nettype == STAGENET ? stagenet_dns_urls : dns_urls)) diff --git a/src/common/dns_utils.cpp b/src/common/dns_utils.cpp index f2b270981..e1e9e8a5b 100644 --- a/src/common/dns_utils.cpp +++ b/src/common/dns_utils.cpp @@ -103,8 +103,6 @@ get_builtin_ds(void) { static const char * const ds[] = { - ". IN DS 19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5\n", - ". IN DS 20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D\n", NULL }; return ds; diff --git a/src/common/updates.cpp b/src/common/updates.cpp index 9f12f8dbc..391cc65e8 100644 --- a/src/common/updates.cpp +++ b/src/common/updates.cpp @@ -46,10 +46,6 @@ namespace tools // All four MoneroPulse domains have DNSSEC on and valid static const std::vector dns_urls = { - "updates.moneropulse.org", - "updates.moneropulse.net", - "updates.moneropulse.co", - "updates.moneropulse.se" }; if (!tools::dns_utils::load_txt_records_from_dns(records, dns_urls)) @@ -99,7 +95,7 @@ namespace tools std::string get_update_url(const std::string &software, const std::string &subdir, const std::string &buildtag, const std::string &version, bool user) { - const char *base = user ? "https://downloads.getmonero.org/" : "https://updates.getmonero.org/"; + const char *base = user ? "" : ""; #ifdef _WIN32 static const char *extension = strncmp(buildtag.c_str(), "install-", 8) ? ".zip" : ".exe"; #else diff --git a/src/cryptonote_basic/cryptonote_basic_impl.cpp b/src/cryptonote_basic/cryptonote_basic_impl.cpp index b18ef1c5c..f000a8dab 100644 --- a/src/cryptonote_basic/cryptonote_basic_impl.cpp +++ b/src/cryptonote_basic/cryptonote_basic_impl.cpp @@ -93,7 +93,7 @@ namespace cryptonote { const int emission_speed_factor = EMISSION_SPEED_FACTOR_PER_MINUTE - (target_minutes-1); uint64_t base_reward = (MONEY_SUPPLY - already_generated_coins) >> emission_speed_factor; - if (base_reward < FINAL_SUBSIDY_PER_MINUTE*target_minutes) + if (base_reward <= FINAL_SUBSIDY_PER_MINUTE*target_minutes) { base_reward = FINAL_SUBSIDY_PER_MINUTE*target_minutes; } diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 5fcfa33f6..7f1dc19cc 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -767,7 +767,7 @@ namespace cryptonote { switch (decimal_point) { - case 12: + case 11: case 9: case 6: case 3: @@ -790,8 +790,8 @@ namespace cryptonote decimal_point = default_decimal_point; switch (std::atomic_load(&default_decimal_point)) { - case 12: - return "monero"; + case 11: + return "wownero"; case 9: return "millinero"; case 6: @@ -991,29 +991,7 @@ namespace cryptonote //--------------------------------------------------------------- bool calculate_block_hash(const block& b, crypto::hash& res) { - // EXCEPTION FOR BLOCK 202612 - const std::string correct_blob_hash_202612 = "3a8a2b3a29b50fc86ff73dd087ea43c6f0d6b8f936c849194d5c84c737903966"; - const std::string existing_block_id_202612 = "bbd604d2ba11ba27935e006ed39c9bfdd99b76bf4a50654bc1e1e61217962698"; - crypto::hash block_blob_hash = get_blob_hash(block_to_blob(b)); - - if (string_tools::pod_to_hex(block_blob_hash) == correct_blob_hash_202612) - { - string_tools::hex_to_pod(existing_block_id_202612, res); - return true; - } - bool hash_result = get_object_hash(get_block_hashing_blob(b), res); - - if (hash_result) - { - // make sure that we aren't looking at a block with the 202612 block id but not the correct blobdata - if (string_tools::pod_to_hex(res) == existing_block_id_202612) - { - LOG_ERROR("Block with block id for 202612 but incorrect block blob hash found!"); - res = null_hash; - return false; - } - } - return hash_result; + return get_object_hash(get_block_hashing_blob(b), res); } //--------------------------------------------------------------- bool get_block_hash(const block& b, crypto::hash& res) @@ -1045,13 +1023,6 @@ namespace cryptonote //--------------------------------------------------------------- bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height) { - // block 202612 bug workaround - const std::string longhash_202612 = "84f64766475d51837ac9efbef1926486e58563c95a19fef4aec3254f03000000"; - if (height == 202612) - { - string_tools::hex_to_pod(longhash_202612, res); - return true; - } blobdata bd = get_block_hashing_blob(b); const int cn_variant = b.major_version >= 7 ? b.major_version - 6 : 0; crypto::cn_slow_hash(bd.data(), bd.size(), res, cn_variant); diff --git a/src/cryptonote_basic/difficulty.cpp b/src/cryptonote_basic/difficulty.cpp index cb2a00a12..43147a07f 100644 --- a/src/cryptonote_basic/difficulty.cpp +++ b/src/cryptonote_basic/difficulty.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include "common/int-util.h" #include "crypto/hash.h" @@ -162,4 +163,149 @@ namespace cryptonote { return (low + time_span - 1) / time_span; } + // LWMA difficulty algorithm + // Background: https://github.com/zawy12/difficulty-algorithms/issues/3 + // Copyright (c) 2017-2018 Zawy + difficulty_type next_difficulty_v2(std::vector timestamps, std::vector cumulative_difficulties, size_t target_seconds) { + + const int64_t T = static_cast(target_seconds); + size_t N = DIFFICULTY_WINDOW_V2; + if (timestamps.size() < 4) { + return 1; + } else if ( timestamps.size() < N+1 ) { + N = timestamps.size() - 1; + } else { + timestamps.resize(N+1); + cumulative_difficulties.resize(N+1); + } + const double adjust = 0.998; + const double k = N * (N + 1) / 2; + double LWMA(0), sum_inverse_D(0), harmonic_mean_D(0), nextDifficulty(0); + int64_t solveTime(0); + uint64_t difficulty(0), next_difficulty(0); + for (size_t i = 1; i <= N; i++) { + solveTime = static_cast(timestamps[i]) - static_cast(timestamps[i - 1]); + solveTime = std::min((T * 7), std::max(solveTime, (-7 * T))); + difficulty = cumulative_difficulties[i] - cumulative_difficulties[i - 1]; + LWMA += (int64_t)(solveTime * i) / k; + sum_inverse_D += 1 / static_cast(difficulty); + } + harmonic_mean_D = N / sum_inverse_D; + if (static_cast(boost::math::round(LWMA)) < T / 20) + LWMA = static_cast(T / 20); + + nextDifficulty = harmonic_mean_D * T / LWMA * adjust; + next_difficulty = static_cast(nextDifficulty); + return next_difficulty; + } + + // LWMA-2 + difficulty_type next_difficulty_v3(std::vector timestamps, std::vector cumulative_difficulties) { + + int64_t T = DIFFICULTY_TARGET_V2; + int64_t N = DIFFICULTY_WINDOW_V2; + int64_t L(0), ST, sum_3_ST(0), next_D, prev_D; + assert(timestamps.size() == cumulative_difficulties.size() && timestamps.size() <= static_cast(N+1) ); + for ( int64_t i = 1; i <= N; i++ ) { + ST = static_cast(timestamps[i]) - static_cast(timestamps[i-1]); + ST = std::max(-4*T, std::min(ST, 6*T)); + L += ST * i ; + if ( i > N-3 ) { + sum_3_ST += ST; + } + } + next_D = (static_cast(cumulative_difficulties[N] - cumulative_difficulties[0])*T*(N+1)*99)/(100*2*L); + prev_D = cumulative_difficulties[N] - cumulative_difficulties[N-1]; + next_D = std::max((prev_D*67)/100, std::min(next_D, (prev_D*150)/100)); + if ( sum_3_ST < (8*T)/10) { + next_D = std::max(next_D,(prev_D*108)/100); + } + return static_cast(next_D); + } + + // LWMA-4 + difficulty_type next_difficulty_v4(std::vector timestamps, std::vector cumulative_difficulties, size_t height) { + + uint64_t T = DIFFICULTY_TARGET_V2; + uint64_t N = DIFFICULTY_WINDOW_V2; + uint64_t L(0), ST(0), next_D, prev_D, avg_D, i; + assert(timestamps.size() == cumulative_difficulties.size() && timestamps.size() <= N+1 ); + if ( height <= 63469 + 1 ) { return 100000069; } + std::vectorTS(N+1); + TS[0] = timestamps[0]; + for ( i = 1; i <= N; i++) { + if ( timestamps[i] > TS[i-1] ) { TS[i] = timestamps[i]; } + else { TS[i] = TS[i-1]; } + } + for ( i = 1; i <= N; i++) { + if ( i > 4 && TS[i]-TS[i-1] > 5*T && TS[i-1] - TS[i-4] < (14*T)/10 ) { ST = 2*T; } + else if ( i > 7 && TS[i]-TS[i-1] > 5*T && TS[i-1] - TS[i-7] < 4*T ) { ST = 2*T; } + else { + ST = std::min(5*T ,TS[i] - TS[i-1]); + } + L += ST * i ; + } + if (L < N*N*T/20 ) { L = N*N*T/20; } + avg_D = ( cumulative_difficulties[N] - cumulative_difficulties[0] )/ N; + if (avg_D > 2000000*N*N*T) { + next_D = (avg_D/(200*L))*(N*(N+1)*T*97); + } + else { next_D = (avg_D*N*(N+1)*T*97)/(200*L); } + prev_D = cumulative_difficulties[N] - cumulative_difficulties[N-1] ; + if ( ( TS[N] - TS[N-1] < (2*T)/10 ) || + ( TS[N] - TS[N-2] < (5*T)/10 ) || + ( TS[N] - TS[N-3] < (8*T)/10 ) ) + { + next_D = std::max( next_D, std::min( (prev_D*110)/100, (105*avg_D)/100 ) ); + } + i = 1000000000; + while (i > 1) { + if ( next_D > i*100 ) { next_D = ((next_D+i/2)/i)*i; break; } + else { i /= 10; } + } + if ( next_D > 100000 ) { + next_D = ((next_D+500)/1000)*1000 + std::min(static_cast(999), (TS[N]-TS[N-10])/10); + } + return static_cast(next_D); + } + + difficulty_type next_difficulty_v5(std::vector timestamps, std::vector cumulative_difficulties, size_t target_seconds) { + + if(timestamps.size() > DIFFICULTY_WINDOW_V3) + { + timestamps.resize(DIFFICULTY_WINDOW_V3); + cumulative_difficulties.resize(DIFFICULTY_WINDOW_V3); + } + + size_t length = timestamps.size(); + assert(length == cumulative_difficulties.size()); + if (length < DIFFICULTY_FORK_HEIGHT + 72) { + return DIFFICULTY_RESET; + } + static_assert(DIFFICULTY_WINDOW_V3 >= 2, "Window is too small"); + assert(length <= DIFFICULTY_WINDOW_V3); + sort(timestamps.begin(), timestamps.end()); + size_t cut_begin, cut_end; + static_assert(2 * DIFFICULTY_CUT_V2 <= DIFFICULTY_WINDOW_V3 - 2, "Cut length is too large"); + if (length <= DIFFICULTY_WINDOW_V3 - 2 * DIFFICULTY_CUT_V2) { + cut_begin = 0; + cut_end = length; + } else { + cut_begin = (length - (DIFFICULTY_WINDOW_V3 - 2 * DIFFICULTY_CUT_V2) + 1) / 2; + cut_end = cut_begin + (DIFFICULTY_WINDOW_V3 - 2 * DIFFICULTY_CUT_V2); + } + assert(/*cut_begin >= 0 &&*/ cut_begin + 2 <= cut_end && cut_end <= length); + uint64_t time_span = timestamps[cut_end - 1] - timestamps[cut_begin]; + if (time_span == 0) { + time_span = 1; + } + difficulty_type total_work = cumulative_difficulties[cut_end - 1] - cumulative_difficulties[cut_begin]; + assert(total_work > 0); + uint64_t low, high; + mul(total_work, target_seconds, low, high); + if (high != 0 || low + time_span - 1 < low) { + return 0; + } + return (low + time_span - 1) / time_span; + } } diff --git a/src/cryptonote_basic/difficulty.h b/src/cryptonote_basic/difficulty.h index b06538467..65c5956b1 100644 --- a/src/cryptonote_basic/difficulty.h +++ b/src/cryptonote_basic/difficulty.h @@ -53,4 +53,8 @@ namespace cryptonote */ bool check_hash(const crypto::hash &hash, difficulty_type difficulty); difficulty_type next_difficulty(std::vector timestamps, std::vector cumulative_difficulties, size_t target_seconds); + difficulty_type next_difficulty_v2(std::vector timestamps, std::vector cumulative_difficulties, size_t target_seconds); + difficulty_type next_difficulty_v3(std::vector timestamps, std::vector cumulative_difficulties); + difficulty_type next_difficulty_v4(std::vector timestamps, std::vector cumulative_difficulties, size_t height); + difficulty_type next_difficulty_v5(std::vector timestamps, std::vector cumulative_difficulties, size_t target_seconds); } diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index 147e1e7ce..b1ca88466 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -42,17 +42,20 @@ #define CRYPTONOTE_PUBLIC_ADDRESS_TEXTBLOB_VER 0 #define CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW 60 #define CURRENT_TRANSACTION_VERSION 2 -#define CURRENT_BLOCK_MAJOR_VERSION 1 -#define CURRENT_BLOCK_MINOR_VERSION 0 +#define CURRENT_BLOCK_MAJOR_VERSION 7 +#define CURRENT_BLOCK_MINOR_VERSION 7 +#define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT_V2 300*2 #define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT 60*60*2 -#define CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE 10 +#define CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE 4 +#define BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V3 12 +#define BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V2 11 #define BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW 60 // MONEY_SUPPLY - total number coins to be generated #define MONEY_SUPPLY ((uint64_t)(-1)) -#define EMISSION_SPEED_FACTOR_PER_MINUTE (20) -#define FINAL_SUBSIDY_PER_MINUTE ((uint64_t)300000000000) // 3 * pow(10, 11) +#define EMISSION_SPEED_FACTOR_PER_MINUTE (24) +#define FINAL_SUBSIDY_PER_MINUTE ((uint64_t)(0)) #define CRYPTONOTE_REWARD_BLOCKS_WINDOW 100 #define CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE_V2 60000 //size of block (bytes) after which reward for block calculated using block size @@ -61,9 +64,9 @@ #define CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE 100000 // size in blocks of the long term block weight median window #define CRYPTONOTE_SHORT_TERM_BLOCK_WEIGHT_SURGE_FACTOR 50 #define CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE 600 -#define CRYPTONOTE_DISPLAY_DECIMAL_POINT 12 +#define CRYPTONOTE_DISPLAY_DECIMAL_POINT 11 // COIN - number of smallest units in one coin -#define COIN ((uint64_t)1000000000000) // pow(10, 12) +#define COIN ((uint64_t)100000000000) // pow(10, 11) #define FEE_PER_KB_OLD ((uint64_t)10000000000) // pow(10, 10) #define FEE_PER_KB ((uint64_t)2000000000) // 2 * pow(10, 9) @@ -76,13 +79,20 @@ #define ORPHANED_BLOCKS_MAX_COUNT 100 -#define DIFFICULTY_TARGET_V2 120 // seconds -#define DIFFICULTY_TARGET_V1 60 // seconds - before first fork +#define DIFFICULTY_TARGET_V2 300 +#define DIFFICULTY_TARGET_V1 300 +#define DIFFICULTY_WINDOW_V3 144 +#define DIFFICULTY_WINDOW_V2 60 #define DIFFICULTY_WINDOW 720 // blocks +#define DIFFICULTY_LAG_V2 3 #define DIFFICULTY_LAG 15 // !!! +#define DIFFICULTY_CUT_V2 12 #define DIFFICULTY_CUT 60 // timestamps to cut after sorting +#define DIFFICULTY_BLOCKS_COUNT_V3 DIFFICULTY_WINDOW_V3 + DIFFICULTY_LAG_V2 +#define DIFFICULTY_BLOCKS_COUNT_V2 DIFFICULTY_WINDOW_V2 + 1 // added +1 to make N=N #define DIFFICULTY_BLOCKS_COUNT DIFFICULTY_WINDOW + DIFFICULTY_LAG - +#define DIFFICULTY_FORK_HEIGHT 81769 // ~14 February 2019 +#define DIFFICULTY_RESET 10000000 // 10 million #define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V1 DIFFICULTY_TARGET_V1 * CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS #define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V2 DIFFICULTY_TARGET_V2 * CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS @@ -125,7 +135,7 @@ #define ALLOW_DEBUG_COMMANDS -#define CRYPTONOTE_NAME "bitmonero" +#define CRYPTONOTE_NAME "wownero" #define CRYPTONOTE_POOLDATA_FILENAME "poolstate.bin" #define CRYPTONOTE_BLOCKCHAINDATA_FILENAME "data.mdb" #define CRYPTONOTE_BLOCKCHAINDATA_LOCK_FILENAME "lock.mdb" @@ -136,12 +146,12 @@ #define HF_VERSION_DYNAMIC_FEE 4 #define HF_VERSION_MIN_MIXIN_4 6 -#define HF_VERSION_MIN_MIXIN_6 7 -#define HF_VERSION_MIN_MIXIN_10 8 +#define HF_VERSION_MIN_MIXIN_7 7 +#define HF_VERSION_MIN_MIXIN_21 9 #define HF_VERSION_ENFORCE_RCT 6 -#define HF_VERSION_PER_BYTE_FEE 8 -#define HF_VERSION_LONG_TERM_BLOCK_WEIGHT 10 -#define HF_VERSION_SMALLER_BP 10 +#define HF_VERSION_PER_BYTE_FEE 12 +#define HF_VERSION_LONG_TERM_BLOCK_WEIGHT 13 +#define HF_VERSION_SMALLER_BP 13 #define PER_KB_FEE_QUANTIZATION_DECIMALS 8 @@ -160,31 +170,31 @@ namespace config uint64_t const BASE_REWARD_CLAMP_THRESHOLD = ((uint64_t)100000000); // pow(10, 8) std::string const P2P_REMOTE_DEBUG_TRUSTED_PUB_KEY = "0000000000000000000000000000000000000000000000000000000000000000"; - uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 18; - uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 19; - uint64_t const CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX = 42; - uint16_t const P2P_DEFAULT_PORT = 18080; - uint16_t const RPC_DEFAULT_PORT = 18081; - uint16_t const ZMQ_RPC_DEFAULT_PORT = 18082; + uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 4146; + uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 6810; + uint64_t const CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX = 12208; + uint16_t const P2P_DEFAULT_PORT = 34567; + uint16_t const RPC_DEFAULT_PORT = 34568; + uint16_t const ZMQ_RPC_DEFAULT_PORT = 34569; boost::uuids::uuid const NETWORK_ID = { { - 0x12 ,0x30, 0xF1, 0x71 , 0x61, 0x04 , 0x41, 0x61, 0x17, 0x31, 0x00, 0x82, 0x16, 0xA1, 0xA1, 0x10 + 0x11, 0x33, 0xFF, 0x77 , 0x61, 0x04 , 0x41, 0x61, 0x17, 0x31, 0x00, 0x82, 0x16, 0xA1, 0xA1, 0x10 } }; // Bender's nightmare - std::string const GENESIS_TX = "013c01ff0001ffffffffffff03029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd08807121017767aafcde9be00dcfd098715ebcf7f410daebc582fda69d24a28e9d0bc890d1"; - uint32_t const GENESIS_NONCE = 10000; + std::string const GENESIS_TX = "013c01ff0001ffffffffff1f029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd08807121012a1a936be5d91c01ee876e38c13fab0ee11cbe86011a2bf7740fb5ebd39d267d"; + uint32_t const GENESIS_NONCE = 70; namespace testnet { uint64_t const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 53; uint64_t const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 54; uint64_t const CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX = 63; - uint16_t const P2P_DEFAULT_PORT = 28080; - uint16_t const RPC_DEFAULT_PORT = 28081; - uint16_t const ZMQ_RPC_DEFAULT_PORT = 28082; + uint16_t const P2P_DEFAULT_PORT = 11180; + uint16_t const RPC_DEFAULT_PORT = 11181; + uint16_t const ZMQ_RPC_DEFAULT_PORT = 11182; boost::uuids::uuid const NETWORK_ID = { { - 0x12 ,0x30, 0xF1, 0x71 , 0x61, 0x04 , 0x41, 0x61, 0x17, 0x31, 0x00, 0x82, 0x16, 0xA1, 0xA1, 0x11 + 0x11, 0x33, 0xFF, 0x77 , 0x61, 0x04 , 0x41, 0x61, 0x17, 0x31, 0x00, 0x82, 0x16, 0xA1, 0xA1, 0x11 } }; // Bender's daydream - std::string const GENESIS_TX = "013c01ff0001ffffffffffff03029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd08807121017767aafcde9be00dcfd098715ebcf7f410daebc582fda69d24a28e9d0bc890d1"; - uint32_t const GENESIS_NONCE = 10001; + std::string const GENESIS_TX = "013c01ff0001ffffffffff1f029b2e4c0281c0b02e7c53291a94d1d0cbff8883f8024f5142ee494ffbbd088071210160eb755f618a2336055dee60f307fe0ded81c5b37b53d310175ca9ee69b0c8ad"; + uint32_t const GENESIS_NONCE = 70; } namespace stagenet diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 94a66fb6b..3f6b7ce9b 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -90,34 +90,13 @@ static const struct { uint8_t threshold; time_t time; } mainnet_hard_forks[] = { - // version 1 from the start of the blockchain - { 1, 1, 0, 1341378000 }, - - // version 2 starts from block 1009827, which is on or around the 20th of March, 2016. Fork time finalised on 2015-09-20. No fork voting occurs for the v2 fork. - { 2, 1009827, 0, 1442763710 }, - - // version 3 starts from block 1141317, which is on or around the 24th of September, 2016. Fork time finalised on 2016-03-21. - { 3, 1141317, 0, 1458558528 }, - - // version 4 starts from block 1220516, which is on or around the 5th of January, 2017. Fork time finalised on 2016-09-18. - { 4, 1220516, 0, 1483574400 }, - - // version 5 starts from block 1288616, which is on or around the 15th of April, 2017. Fork time finalised on 2017-03-14. - { 5, 1288616, 0, 1489520158 }, - - // version 6 starts from block 1400000, which is on or around the 16th of September, 2017. Fork time finalised on 2017-08-18. - { 6, 1400000, 0, 1503046577 }, - - // version 7 starts from block 1546000, which is on or around the 6th of April, 2018. Fork time finalised on 2018-03-17. - { 7, 1546000, 0, 1521303150 }, - - // version 8 starts from block 1685555, which is on or around the 18th of October, 2018. Fork time finalised on 2018-09-02. - { 8, 1685555, 0, 1535889547 }, - - // version 9 starts from block 1686275, which is on or around the 19th of October, 2018. Fork time finalised on 2018-09-02. - { 9, 1686275, 0, 1535889548 }, + { 7, 1, 0, 1519605000 }, + { 8, 6969, 0, 1524214739 }, + { 9, 53666, 0, 1538689773 }, + { 10, 63469, 0, 1541700352 }, + { 11, 81769, 0, 1549238400 }, + { 12, 82069, 0, 1549318761 }, }; -static const uint64_t mainnet_hard_fork_version_1_till = 1009826; static const struct { uint8_t version; @@ -125,23 +104,14 @@ static const struct { uint8_t threshold; time_t time; } testnet_hard_forks[] = { - // version 1 from the start of the blockchain - { 1, 1, 0, 1341378000 }, - - // version 2 starts from block 624634, which is on or around the 23rd of November, 2015. Fork time finalised on 2015-11-20. No fork voting occurs for the v2 fork. - { 2, 624634, 0, 1445355000 }, - - // versions 3-5 were passed in rapid succession from September 18th, 2016 - { 3, 800500, 0, 1472415034 }, - { 4, 801219, 0, 1472415035 }, - { 5, 802660, 0, 1472415036 + 86400*180 }, // add 5 months on testnet to shut the update warning up since there's a large gap to v6 - - { 6, 971400, 0, 1501709789 }, - { 7, 1057027, 0, 1512211236 }, - { 8, 1057058, 0, 1533211200 }, - { 9, 1057778, 0, 1533297600 }, + { 7, 1, 0, 1341378000 }, + { 8, 10, 0, 1522624244 }, + { 9, 20, 0, 1543754910 }, + { 10, 30, 0, 1543795200 }, + { 11, 40, 0, 1543922445 }, + { 12, 50, 0, 1551916800 }, + { 13, 60, 0, 1551993902 }, }; -static const uint64_t testnet_hard_fork_version_1_till = 624633; static const struct { uint8_t version; @@ -149,18 +119,13 @@ static const struct { uint8_t threshold; time_t time; } stagenet_hard_forks[] = { - // version 1 from the start of the blockchain - { 1, 1, 0, 1341378000 }, - - // versions 2-7 in rapid succession from March 13th, 2018 - { 2, 32000, 0, 1521000000 }, - { 3, 33000, 0, 1521120000 }, - { 4, 34000, 0, 1521240000 }, - { 5, 35000, 0, 1521360000 }, - { 6, 36000, 0, 1521480000 }, - { 7, 37000, 0, 1521600000 }, - { 8, 176456, 0, 1537821770 }, - { 9, 177176, 0, 1537821771 }, + { 7, 1, 0, 1341378000 }, + { 8, 10, 0, 1522624244 }, + { 9, 20, 0, 1543754910 }, + { 10, 30, 0, 1543795200 }, + { 11, 40, 0, 1543922445 }, + { 12, 50, 0, 1551916800 }, + { 13, 60, 0, 1551993902 }, }; //------------------------------------------------------------------ @@ -371,9 +336,9 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline if (m_nettype == FAKECHAIN || m_nettype == STAGENET) m_hardfork = new HardFork(*db, 1, 0); else if (m_nettype == TESTNET) - m_hardfork = new HardFork(*db, 1, testnet_hard_fork_version_1_till); + m_hardfork = new HardFork(*db, 1, 0); else - m_hardfork = new HardFork(*db, 1, mainnet_hard_fork_version_1_till); + m_hardfork = new HardFork(*db, 1, 0); } if (m_nettype == FAKECHAIN) { @@ -837,20 +802,31 @@ difficulty_type Blockchain::get_difficulty_for_next_block() std::vector timestamps; std::vector difficulties; auto height = m_db->height(); + + uint8_t version = get_current_hard_fork_version(); + uint64_t difficulty_blocks_count = DIFFICULTY_BLOCKS_COUNT; + if (version >= 11) { + difficulty_blocks_count = DIFFICULTY_BLOCKS_COUNT_V3; + } else if (version <= 10 && version >= 8) { + difficulty_blocks_count = DIFFICULTY_BLOCKS_COUNT_V2; + } else { + difficulty_blocks_count = DIFFICULTY_BLOCKS_COUNT; + } + // ND: Speedup // 1. Keep a list of the last 735 (or less) blocks that is used to compute difficulty, // then when the next block difficulty is queried, push the latest height data and // pop the oldest one from the list. This only requires 1x read per height instead // of doing 735 (DIFFICULTY_BLOCKS_COUNT). - if (m_timestamps_and_difficulties_height != 0 && ((height - m_timestamps_and_difficulties_height) == 1) && m_timestamps.size() >= DIFFICULTY_BLOCKS_COUNT) + if (m_timestamps_and_difficulties_height != 0 && ((height - m_timestamps_and_difficulties_height) == 1) && m_timestamps.size() >= difficulty_blocks_count) { uint64_t index = height - 1; m_timestamps.push_back(m_db->get_block_timestamp(index)); m_difficulties.push_back(m_db->get_block_cumulative_difficulty(index)); - while (m_timestamps.size() > DIFFICULTY_BLOCKS_COUNT) + while (m_timestamps.size() > difficulty_blocks_count) m_timestamps.erase(m_timestamps.begin()); - while (m_difficulties.size() > DIFFICULTY_BLOCKS_COUNT) + while (m_difficulties.size() > difficulty_blocks_count) m_difficulties.erase(m_difficulties.begin()); m_timestamps_and_difficulties_height = height; @@ -859,7 +835,7 @@ difficulty_type Blockchain::get_difficulty_for_next_block() } else { - size_t offset = height - std::min < size_t > (height, static_cast(DIFFICULTY_BLOCKS_COUNT)); + size_t offset = height - std::min < size_t > (height, static_cast(difficulty_blocks_count)); if (offset == 0) ++offset; @@ -883,6 +859,18 @@ difficulty_type Blockchain::get_difficulty_for_next_block() size_t target = get_difficulty_target(); difficulty_type diff = next_difficulty(timestamps, difficulties, target); + if (version >= 11) { + difficulty_type diff = next_difficulty_v5(timestamps, difficulties, target); + } else if (version == 10) { + difficulty_type diff = next_difficulty_v4(timestamps, difficulties, height); + } else if (version == 9) { + difficulty_type diff = next_difficulty_v3(timestamps, difficulties); + } else if (version == 8) { + difficulty_type diff = next_difficulty_v2(timestamps, difficulties, target); + } else { + difficulty_type diff = next_difficulty(timestamps, difficulties, target); + } + CRITICAL_REGION_LOCAL1(m_difficulty_lock); m_difficulty_for_next_block_top_hash = top_hash; m_difficulty_for_next_block = diff; @@ -1057,15 +1045,26 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std: std::vector timestamps; std::vector cumulative_difficulties; + uint8_t version = get_current_hard_fork_version(); + size_t height = m_db->height(); + size_t difficulty_blocks_count = DIFFICULTY_BLOCKS_COUNT; + if (version >= 11) { + difficulty_blocks_count = DIFFICULTY_BLOCKS_COUNT_V3; + } else if (version <= 10 && version >= 8) { + difficulty_blocks_count = DIFFICULTY_BLOCKS_COUNT_V2; + } else { + difficulty_blocks_count = DIFFICULTY_BLOCKS_COUNT; + } + // if the alt chain isn't long enough to calculate the difficulty target // based on its blocks alone, need to get more blocks from the main chain - if(alt_chain.size()< DIFFICULTY_BLOCKS_COUNT) + if(alt_chain.size()< difficulty_blocks_count) { CRITICAL_REGION_LOCAL(m_blockchain_lock); // Figure out start and stop offsets for main chain blocks size_t main_chain_stop_offset = alt_chain.size() ? alt_chain.front()->second.height : bei.height; - size_t main_chain_count = DIFFICULTY_BLOCKS_COUNT - std::min(static_cast(DIFFICULTY_BLOCKS_COUNT), alt_chain.size()); + size_t main_chain_count = difficulty_blocks_count - std::min(static_cast(difficulty_blocks_count), alt_chain.size()); main_chain_count = std::min(main_chain_count, main_chain_stop_offset); size_t main_chain_start_offset = main_chain_stop_offset - main_chain_count; @@ -1080,7 +1079,7 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std: } // make sure we haven't accidentally grabbed too many blocks...maybe don't need this check? - CHECK_AND_ASSERT_MES((alt_chain.size() + timestamps.size()) <= DIFFICULTY_BLOCKS_COUNT, false, "Internal error, alt_chain.size()[" << alt_chain.size() << "] + vtimestampsec.size()[" << timestamps.size() << "] NOT <= DIFFICULTY_WINDOW[]" << DIFFICULTY_BLOCKS_COUNT); + CHECK_AND_ASSERT_MES((alt_chain.size() + timestamps.size()) <= difficulty_blocks_count, false, "Internal error, alt_chain.size()[" << alt_chain.size() << "] + vtimestampsec.size()[" << timestamps.size() << "] NOT <= DIFFICULTY_WINDOW[]" << difficulty_blocks_count); for (auto it : alt_chain) { @@ -1092,8 +1091,8 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std: // and timestamps from it alone else { - timestamps.resize(static_cast(DIFFICULTY_BLOCKS_COUNT)); - cumulative_difficulties.resize(static_cast(DIFFICULTY_BLOCKS_COUNT)); + timestamps.resize(static_cast(difficulty_blocks_count)); + cumulative_difficulties.resize(static_cast(difficulty_blocks_count)); size_t count = 0; size_t max_i = timestamps.size()-1; // get difficulties and timestamps from most recent blocks in alt chain @@ -1102,7 +1101,7 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std: timestamps[max_i - count] = it->second.bl.timestamp; cumulative_difficulties[max_i - count] = it->second.cumulative_difficulty; count++; - if(count >= DIFFICULTY_BLOCKS_COUNT) + if(count >= difficulty_blocks_count) break; } } @@ -1111,7 +1110,17 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std: size_t target = get_ideal_hard_fork_version(bei.height) < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; // calculate the difficulty target for the block and return it - return next_difficulty(timestamps, cumulative_difficulties, target); + if (version >= 11) { + return next_difficulty_v5(timestamps, cumulative_difficulties, target); + } else if (version == 10) { + return next_difficulty_v4(timestamps, cumulative_difficulties, height); + } else if (version == 9) { + return next_difficulty_v3(timestamps, cumulative_difficulties); + } else if (version == 8) { + return next_difficulty_v2(timestamps, cumulative_difficulties, target); + } else { + return next_difficulty(timestamps, cumulative_difficulties, target); + } } //------------------------------------------------------------------ // This function does a sanity check on basic things that all miner @@ -1422,12 +1431,13 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m bool Blockchain::complete_timestamps_vector(uint64_t start_top_height, std::vector& timestamps) { LOG_PRINT_L3("Blockchain::" << __func__); - - if(timestamps.size() >= BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW) + uint8_t version = get_current_hard_fork_version(); + size_t blockchain_timestamp_check_window = version >= 11 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V3 : version == 11 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V2 : BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW; + if(timestamps.size() >= blockchain_timestamp_check_window) return true; CRITICAL_REGION_LOCAL(m_blockchain_lock); - size_t need_elements = BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW - timestamps.size(); + size_t need_elements = blockchain_timestamp_check_window - timestamps.size(); CHECK_AND_ASSERT_MES(start_top_height < m_db->height(), false, "internal error: passed start_height not < " << " m_db->height() -- " << start_top_height << " >= " << m_db->height()); size_t stop_offset = start_top_height > need_elements ? start_top_height - need_elements : 0; timestamps.reserve(timestamps.size() + start_top_height - stop_offset); @@ -2381,26 +2391,26 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context } } - // from v8, allow bulletproofs - if (hf_version < 8) { + // from v11, allow bulletproofs + if (hf_version < 11) { if (tx.version >= 2) { const bool bulletproof = rct::is_rct_bulletproof(tx.rct_signatures.type); if (bulletproof || !tx.rct_signatures.p.bulletproofs.empty()) { - MERROR_VER("Bulletproofs are not allowed before v8"); + MERROR_VER("New Bulletproofs are not allowed before v11"); tvc.m_invalid_output = true; return false; } } } - // from v9, forbid borromean range proofs - if (hf_version > 8) { + // from v12, forbid borromean range proofs + if (hf_version > 11) { if (tx.version >= 2) { const bool borromean = rct::is_rct_borromean(tx.rct_signatures.type); if (borromean) { - MERROR_VER("Borromean range proofs are not allowed after v8"); + MERROR_VER("Borromean range proofs are not allowed after v11"); tvc.m_invalid_output = true; return false; } @@ -2539,7 +2549,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, { size_t n_unmixable = 0, n_mixable = 0; size_t mixin = std::numeric_limits::max(); - const size_t min_mixin = hf_version >= HF_VERSION_MIN_MIXIN_10 ? 10 : hf_version >= HF_VERSION_MIN_MIXIN_6 ? 6 : hf_version >= HF_VERSION_MIN_MIXIN_4 ? 4 : 2; + const size_t min_mixin = hf_version >= HF_VERSION_MIN_MIXIN_21 ? 21 : hf_version >= HF_VERSION_MIN_MIXIN_7 ? 7 : hf_version >= HF_VERSION_MIN_MIXIN_4 ? 4 : 2; for (const auto& txin : tx.vin) { // non txin_to_key inputs will be rejected below @@ -2568,9 +2578,9 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } } - if (((hf_version == HF_VERSION_MIN_MIXIN_10 || hf_version == HF_VERSION_MIN_MIXIN_10+1) && mixin != 10) || (hf_version >= HF_VERSION_MIN_MIXIN_10+2 && mixin > 10)) + if (hf_version >= HF_VERSION_MIN_MIXIN_21 && mixin != 21) { - MERROR_VER("Tx " << get_transaction_hash(tx) << " has invalid ring size (" << (mixin + 1) << "), it should be 11"); + MERROR_VER("Tx " << get_transaction_hash(tx) << " has invalid ring size (" << (mixin + 1) << "), it should be 22"); tvc.m_low_mixin = true; return false; } @@ -2895,13 +2905,13 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, // for bulletproofs, check they're only multi-output after v8 if (rct::is_rct_bulletproof(rv.type)) { - if (hf_version < 8) + if (hf_version < 11) { for (const rct::Bulletproof &proof: rv.p.bulletproofs) { if (proof.V.size() > 1) { - MERROR_VER("Multi output bulletproofs are invalid before v8"); + MERROR_VER("Multi output bulletproofs are invalid before v11"); return false; } } @@ -3168,10 +3178,11 @@ bool Blockchain::check_block_timestamp(std::vector& timestamps, const { LOG_PRINT_L3("Blockchain::" << __func__); median_ts = epee::misc_utils::median(timestamps); - + uint8_t version = get_current_hard_fork_version(); + size_t blockchain_timestamp_check_window = version >= 11 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V3 : version == 11 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V2 : BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW; if(b.timestamp < median_ts) { - MERROR_VER("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", less than median of last " << BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW << " blocks, " << median_ts); + MERROR_VER("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", less than median of last " << blockchain_timestamp_check_window << " blocks, " << median_ts); return false; } @@ -3188,14 +3199,18 @@ bool Blockchain::check_block_timestamp(std::vector& timestamps, const bool Blockchain::check_block_timestamp(const block& b, uint64_t& median_ts) const { LOG_PRINT_L3("Blockchain::" << __func__); - if(b.timestamp > get_adjusted_time() + CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT) + uint8_t version = get_current_hard_fork_version(); + uint64_t cryptonote_block_future_time_limit = version >= 8 ? CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT_V2 : CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT; + size_t blockchain_timestamp_check_window = version >= 11 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V3 : version == 11 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V2 : BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW; + + if(b.timestamp > get_adjusted_time() + cryptonote_block_future_time_limit) { - MERROR_VER("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", bigger than adjusted time + 2 hours"); + MERROR_VER("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", bigger than adjusted time + 10 minutes"); return false; } // if not enough blocks, no proper median yet, return true - if(m_db->height() < BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW) + if(m_db->height() < blockchain_timestamp_check_window) { return true; } @@ -3204,7 +3219,7 @@ bool Blockchain::check_block_timestamp(const block& b, uint64_t& median_ts) cons auto h = m_db->height(); // need most recent 60 blocks, get index of first of those - size_t offset = h - BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW; + size_t offset = h - blockchain_timestamp_check_window; timestamps.reserve(h - offset); for(;offset < h; ++offset) { @@ -4540,7 +4555,7 @@ void Blockchain::cancel() } #if defined(PER_BLOCK_CHECKPOINT) -static const char expected_block_hashes_hash[] = "570ce2357b08fadac6058e34f95c5e08323f9325de260d07b091a281a948a7b0"; +static const char expected_block_hashes_hash[] = "57465b2e1d648df696dc02b4aeddc357493678e9e5b145226fd4af1aceb02165"; void Blockchain::load_compiled_in_block_hashes() { const bool testnet = m_nettype == TESTNET; diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index b0ed7d941..1bfefb42d 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -449,8 +449,8 @@ namespace cryptonote if (boost::filesystem::exists(old_files / "blockchain.bin")) { MWARNING("Found old-style blockchain.bin in " << old_files.string()); - MWARNING("Monero now uses a new format. You can either remove blockchain.bin to start syncing"); - MWARNING("the blockchain anew, or use monero-blockchain-export and monero-blockchain-import to"); + MWARNING("Wownero now uses a new format. You can either remove blockchain.bin to start syncing"); + MWARNING("the blockchain anew, or use wownero-blockchain-export and wownero-blockchain-import to"); MWARNING("convert your existing blockchain.bin to the new format. See README.md for instructions."); return false; } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index c2c660e8c..1f4c5f466 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -302,7 +302,7 @@ namespace cryptonote int64_t diff = static_cast(hshd.current_height) - static_cast(m_core.get_current_blockchain_height()); uint64_t abs_diff = std::abs(diff); uint64_t max_block_height = std::max(hshd.current_height,m_core.get_current_blockchain_height()); - uint64_t last_block_v1 = m_core.get_nettype() == TESTNET ? 624633 : m_core.get_nettype() == MAINNET ? 1009826 : (uint64_t)-1; + uint64_t last_block_v1 = m_core.get_nettype() == TESTNET ? 0 : m_core.get_nettype() == MAINNET ? 0 : (uint64_t)-1; uint64_t diff_v2 = max_block_height > last_block_v1 ? std::min(abs_diff, max_block_height - last_block_v1) : 0; MCLOG(is_inital ? el::Level::Info : el::Level::Debug, "global", context << "Sync data returned a new top block candidate: " << m_core.get_current_blockchain_height() << " -> " << hshd.current_height << " [Your node is " << abs_diff << " blocks (" << ((abs_diff - diff_v2) / (24 * 60 * 60 / DIFFICULTY_TARGET_V1)) + (diff_v2 / (24 * 60 * 60 / DIFFICULTY_TARGET_V2)) << " days) " @@ -1592,7 +1592,7 @@ skip: if(m_synchronized.compare_exchange_strong(val_expected, true)) { MGINFO_YELLOW(ENDL << "**********************************************************************" << ENDL - << "You are now synchronized with the network. You may now start monero-wallet-cli." << ENDL + << "You are now synchronized with the network. You may now start wownero-wallet-cli." << ENDL << ENDL << "Use the \"help\" command to see the list of available commands." << ENDL << "**********************************************************************"); diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index f645836a4..65dd7fd15 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -109,5 +109,5 @@ target_link_libraries(daemon ${EXTRA_LIBRARIES}) set_property(TARGET daemon PROPERTY - OUTPUT_NAME "monerod") + OUTPUT_NAME "wownerod") install(TARGETS daemon DESTINATION bin) diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index 1638cf505..7253f830e 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -676,7 +676,7 @@ bool t_command_parser_executor::sync_info(const std::vector& args) bool t_command_parser_executor::version(const std::vector& args) { - std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << std::endl; + std::cout << "Wownero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << std::endl; return true; } diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 35504f2c9..74b4f0a53 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -335,7 +335,7 @@ bool t_command_server::help(const std::vector& args) std::string t_command_server::get_commands_str() { std::stringstream ss; - ss << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << std::endl; + ss << "Wownero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << std::endl; ss << "Commands: " << std::endl; std::string usage = m_command_lookup.get_usage(); boost::replace_all(usage, "\n", "\n "); diff --git a/src/daemon/executor.cpp b/src/daemon/executor.cpp index fbc7d04fd..d410dadb1 100644 --- a/src/daemon/executor.cpp +++ b/src/daemon/executor.cpp @@ -40,7 +40,7 @@ namespace daemonize { - std::string const t_executor::NAME = "Monero Daemon"; + std::string const t_executor::NAME = "Wownero Daemon"; void t_executor::init_options( boost::program_options::options_description & configurable_options @@ -58,7 +58,7 @@ namespace daemonize boost::program_options::variables_map const & vm ) { - LOG_PRINT_L0("Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ") Daemonised"); + LOG_PRINT_L0("Wownero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ") Daemonised"); return t_daemon{vm}; } diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp index f483ba6c9..47ce1d154 100644 --- a/src/daemon/main.cpp +++ b/src/daemon/main.cpp @@ -119,7 +119,7 @@ int main(int argc, char const * argv[]) if (command_line::get_arg(vm, command_line::arg_help)) { - std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; + std::cout << "Wownero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; std::cout << "Usage: " + std::string{argv[0]} + " [options|settings] [daemon_command...]" << std::endl << std::endl; std::cout << visible_options << std::endl; return 0; @@ -128,7 +128,7 @@ int main(int argc, char const * argv[]) // Monero Version if (command_line::get_arg(vm, command_line::arg_version)) { - std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL; + std::cout << "Wownero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL; return 0; } @@ -284,7 +284,7 @@ int main(int argc, char const * argv[]) tools::set_max_concurrency(command_line::get_arg(vm, daemon_args::arg_max_concurrency)); // logging is now set up - MGINFO("Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"); + MGINFO("Wownero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"); MINFO("Moving from main() into the daemonize now."); diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 3348989f2..d9b9de6b7 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -1165,10 +1165,10 @@ bool t_rpc_command_executor::print_status() bool daemon_is_alive = m_rpc_client->check_connection(); if(daemon_is_alive) { - tools::success_msg_writer() << "monerod is running"; + tools::success_msg_writer() << "wownerod is running"; } else { - tools::fail_msg_writer() << "monerod is NOT running"; + tools::fail_msg_writer() << "wownerod is NOT running"; } return true; diff --git a/src/debug_utilities/cn_deserialize.cpp b/src/debug_utilities/cn_deserialize.cpp index 3e2552230..2683be500 100644 --- a/src/debug_utilities/cn_deserialize.cpp +++ b/src/debug_utilities/cn_deserialize.cpp @@ -105,7 +105,7 @@ int main(int argc, char* argv[]) if (command_line::get_arg(vm, command_line::arg_help)) { - std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; + std::cout << "Wownero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL << ENDL; std::cout << desc_options << std::endl; return 1; } diff --git a/src/gen_multisig/CMakeLists.txt b/src/gen_multisig/CMakeLists.txt index 18a6a9efe..83583e804 100644 --- a/src/gen_multisig/CMakeLists.txt +++ b/src/gen_multisig/CMakeLists.txt @@ -50,5 +50,5 @@ add_dependencies(gen_multisig version) set_property(TARGET gen_multisig PROPERTY - OUTPUT_NAME "monero-gen-trusted-multisig") + OUTPUT_NAME "wownero-gen-trusted-multisig") install(TARGETS gen_multisig DESTINATION bin) diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 90e2f78b1..af452dfad 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -127,10 +127,7 @@ namespace nodetool virtual std::map get_blocked_hosts() { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return m_blocked_hosts; } private: const std::vector m_seed_nodes_list = - { "seeds.moneroseeds.se" - , "seeds.moneroseeds.ae.org" - , "seeds.moneroseeds.ch" - , "seeds.moneroseeds.li" + { }; bool islimitup=false; diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 9390626a8..75cc7536a 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -391,30 +391,21 @@ namespace nodetool std::set full_addrs; if (nettype == cryptonote::TESTNET) { - full_addrs.insert("212.83.175.67:28080"); - full_addrs.insert("5.9.100.248:28080"); - full_addrs.insert("163.172.182.165:28080"); - full_addrs.insert("195.154.123.123:28080"); - full_addrs.insert("212.83.172.165:28080"); + full_addrs.insert("206.189.166.14:11180"); + full_addrs.insert("104.236.48.55:11180"); } else if (nettype == cryptonote::STAGENET) { - full_addrs.insert("162.210.173.150:38080"); - full_addrs.insert("162.210.173.151:38080"); } else if (nettype == cryptonote::FAKECHAIN) { } else { - full_addrs.insert("107.152.130.98:18080"); - full_addrs.insert("212.83.175.67:18080"); - full_addrs.insert("5.9.100.248:18080"); - full_addrs.insert("163.172.182.165:18080"); - full_addrs.insert("161.67.132.39:18080"); - full_addrs.insert("198.74.231.92:18080"); - full_addrs.insert("195.154.123.123:18080"); - full_addrs.insert("212.83.172.165:18080"); + full_addrs.insert("66.70.218.230:34567"); + full_addrs.insert("34.209.48.213:34567"); + full_addrs.insert("159.65.91.59:34567"); + full_addrs.insert("138.197.31.246:34567"); } return full_addrs; } diff --git a/src/simplewallet/CMakeLists.txt b/src/simplewallet/CMakeLists.txt index c31cdebde..02e25156c 100644 --- a/src/simplewallet/CMakeLists.txt +++ b/src/simplewallet/CMakeLists.txt @@ -60,5 +60,5 @@ target_link_libraries(simplewallet ${EXTRA_LIBRARIES}) set_property(TARGET simplewallet PROPERTY - OUTPUT_NAME "monero-wallet-cli") + OUTPUT_NAME "wownero-wallet-cli") install(TARGETS simplewallet DESTINATION bin) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 4a4754471..f6467152a 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -86,9 +86,9 @@ typedef cryptonote::simple_wallet sw; #define EXTENDED_LOGS_FILE "wallet_details.log" -#define DEFAULT_MIX 10 +#define DEFAULT_MIX 21 -#define MIN_RING_SIZE 11 // Used to inform user about min ring size -- does not track actual protocol +#define MIN_RING_SIZE 22 // Used to inform user about min ring size -- does not track actual protocol #define LOCK_IDLE_SCOPE() \ bool auto_refresh_enabled = m_auto_refresh_enabled.load(std::memory_order_relaxed); \ @@ -138,7 +138,7 @@ namespace const command_line::arg_descriptor arg_non_deterministic = {"non-deterministic", sw::tr("Generate non-deterministic view and spend keys"), false}; const command_line::arg_descriptor arg_allow_mismatched_daemon_version = {"allow-mismatched-daemon-version", sw::tr("Allow communicating with a daemon that uses a different RPC version"), false}; const command_line::arg_descriptor arg_restore_height = {"restore-height", sw::tr("Restore from specific blockchain height"), 0}; - const command_line::arg_descriptor arg_do_not_relay = {"do-not-relay", sw::tr("The newly created transaction will not be relayed to the monero network"), false}; + const command_line::arg_descriptor arg_do_not_relay = {"do-not-relay", sw::tr("The newly created transaction will not be relayed to the wownero network"), false}; const command_line::arg_descriptor arg_create_address_file = {"create-address-file", sw::tr("Create an address file for new wallets"), false}; const command_line::arg_descriptor arg_subaddress_lookahead = {"subaddress-lookahead", tools::wallet2::tr("Set subaddress lookahead sizes to :"), ""}; const command_line::arg_descriptor arg_use_english_language_names = {"use-english-language-names", sw::tr("Display English language names"), false}; @@ -339,7 +339,7 @@ namespace std::stringstream prompt; prompt << sw::tr("For URL: ") << url << ", " << dnssec_str << std::endl - << sw::tr(" Monero Address = ") << addresses[0] + << sw::tr(" Wownero Address = ") << addresses[0] << std::endl << sw::tr("Is this OK? (Y/n) ") ; @@ -1387,7 +1387,7 @@ bool simple_wallet::export_raw_multisig(const std::vector &args) for (auto &ptx: txs.m_ptx) { const crypto::hash txid = cryptonote::get_transaction_hash(ptx.tx); - const std::string filename = std::string("raw_multisig_monero_tx_") + epee::string_tools::pod_to_hex(txid); + const std::string filename = std::string("raw_multisig_wownero_tx_") + epee::string_tools::pod_to_hex(txid); if (!filenames.empty()) filenames += ", "; filenames += filename; @@ -1808,7 +1808,7 @@ bool simple_wallet::save_known_rings(const std::vector &args) bool simple_wallet::version(const std::vector &args) { - message_writer() << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; + message_writer() << "Wownero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; return true; } @@ -2047,7 +2047,7 @@ bool simple_wallet::set_unit(const std::vector &args/* = std::vecto const std::string &unit = args[1]; unsigned int decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT; - if (unit == "monero") + if (unit == "wownero") decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT; else if (unit == "millinero") decimal_point = CRYPTONOTE_DISPLAY_DECIMAL_POINT - 3; @@ -2425,8 +2425,8 @@ simple_wallet::simple_wallet() " Set the fee to default/unimportant/normal/elevated/priority.\n " "confirm-missing-payment-id <1|0>\n " "ask-password <0|1|2 (or never|action|decrypt)>\n " - "unit \n " - " Set the default monero (sub-)unit.\n " + "unit \n " + " Set the default wownero (sub-)unit.\n " "min-outputs-count [n]\n " " Try to keep at least that many outputs of value at least min-outputs-value.\n " "min-outputs-value [n]\n " @@ -2442,12 +2442,12 @@ simple_wallet::simple_wallet() "auto-low-priority <1|0>\n " " Whether to automatically use the low priority fee level when it's safe to do so.\n " "segregate-pre-fork-outputs <1|0>\n " - " Set this if you intend to spend outputs on both Monero AND a key reusing fork.\n " + " Set this if you intend to spend outputs on both Wownero AND a key reusing fork.\n " "key-reuse-mitigation2 <1|0>\n " - " Set this if you are not sure whether you will spend on a key reusing Monero fork later.\n" + " Set this if you are not sure whether you will spend on a key reusing Wownero fork later.\n" "subaddress-lookahead :\n " " Set the lookahead sizes for the subaddress hash table.\n " - " Set this if you are not sure whether you will spend on a key reusing Monero fork later.\n " + " Set this if you are not sure whether you will spend on a key reusing Wownero fork later.\n " "segregation-height \n " " Set to the height of a key reusing fork you want to use, 0 to use default.")); m_cmd_binder.set_handler("encrypted_seed", @@ -2719,7 +2719,7 @@ bool simple_wallet::set_variable(const std::vector &args) CHECK_SIMPLE_VARIABLE("priority", set_default_priority, tr("0, 1, 2, 3, or 4, or one of ") << join_priority_strings(", ")); CHECK_SIMPLE_VARIABLE("confirm-missing-payment-id", set_confirm_missing_payment_id, tr("0 or 1")); CHECK_SIMPLE_VARIABLE("ask-password", set_ask_password, tr("0|1|2 (or never|action|decrypt)")); - CHECK_SIMPLE_VARIABLE("unit", set_unit, tr("monero, millinero, micronero, nanonero, piconero")); + CHECK_SIMPLE_VARIABLE("unit", set_unit, tr("wownero, millinero, micronero, nanonero, piconero")); CHECK_SIMPLE_VARIABLE("min-outputs-count", set_min_output_count, tr("unsigned integer")); CHECK_SIMPLE_VARIABLE("min-outputs-value", set_min_output_value, tr("amount")); CHECK_SIMPLE_VARIABLE("merge-destinations", set_merge_destinations, tr("0 or 1")); @@ -3667,7 +3667,7 @@ boost::optional simple_wallet::new_wallet(const boost::pr "To start synchronizing with the daemon, use the \"refresh\" command.\n" "Use the \"help\" command to see the list of available commands.\n" "Use \"help \" to see a command's documentation.\n" - "Always use the \"exit\" command when closing monero-wallet-cli to save \n" + "Always use the \"exit\" command when closing wownero-wallet-cli to save \n" "your current session's state. Otherwise, you might need to synchronize \n" "your wallet again (your wallet keys are NOT at risk in any case).\n") ; @@ -4871,7 +4871,7 @@ bool simple_wallet::transfer_main(int transfer_type, const std::vectormultisig()) { - bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_monero_tx"); + bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_wownero_tx"); if (!r) { fail_msg_writer() << tr("Failed to write transaction(s) to file"); } else { - success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_monero_tx"; + success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_wownero_tx"; } } else if (m_wallet->watch_only()) { - bool r = m_wallet->save_tx(ptx_vector, "unsigned_monero_tx"); + bool r = m_wallet->save_tx(ptx_vector, "unsigned_wownero_tx"); if (!r) { fail_msg_writer() << tr("Failed to write transaction(s) to file"); } else { - success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "unsigned_monero_tx"; + success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "unsigned_wownero_tx"; } } else @@ -5216,26 +5216,26 @@ bool simple_wallet::sweep_unmixable(const std::vector &args_) // actually commit the transactions if (m_wallet->multisig()) { - bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_monero_tx"); + bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_wownero_tx"); if (!r) { fail_msg_writer() << tr("Failed to write transaction(s) to file"); } else { - success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_monero_tx"; + success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_wownero_tx"; } } else if (m_wallet->watch_only()) { - bool r = m_wallet->save_tx(ptx_vector, "unsigned_monero_tx"); + bool r = m_wallet->save_tx(ptx_vector, "unsigned_wownero_tx"); if (!r) { fail_msg_writer() << tr("Failed to write transaction(s) to file"); } else { - success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "unsigned_monero_tx"; + success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "unsigned_wownero_tx"; } } else @@ -5447,7 +5447,7 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vectorconfirm_missing_payment_id() && !info.is_subaddress) + if (m_long_payment_id_support && !payment_id_seen && m_wallet->confirm_missing_payment_id() && !info.is_subaddress && !is_wallet_address) { std::string accepted = input_line(tr("No payment id is included with this transaction. Is this okay? (Y/Yes/N/No): ")); if (std::cin.eof()) @@ -5520,26 +5520,26 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vectormultisig()) { - bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_monero_tx"); + bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_wownero_tx"); if (!r) { fail_msg_writer() << tr("Failed to write transaction(s) to file"); } else { - success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_monero_tx"; + success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_wownero_tx"; } } else if (m_wallet->watch_only()) { - bool r = m_wallet->save_tx(ptx_vector, "unsigned_monero_tx"); + bool r = m_wallet->save_tx(ptx_vector, "unsigned_wownero_tx"); if (!r) { fail_msg_writer() << tr("Failed to write transaction(s) to file"); } else { - success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "unsigned_monero_tx"; + success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "unsigned_wownero_tx"; } } else @@ -5749,26 +5749,26 @@ bool simple_wallet::sweep_single(const std::vector &args_) // actually commit the transactions if (m_wallet->multisig()) { - bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_monero_tx"); + bool r = m_wallet->save_multisig_tx(ptx_vector, "multisig_wownero_tx"); if (!r) { fail_msg_writer() << tr("Failed to write transaction(s) to file"); } else { - success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_monero_tx"; + success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "multisig_wownero_tx"; } } else if (m_wallet->watch_only()) { - bool r = m_wallet->save_tx(ptx_vector, "unsigned_monero_tx"); + bool r = m_wallet->save_tx(ptx_vector, "unsigned_wownero_tx"); if (!r) { fail_msg_writer() << tr("Failed to write transaction(s) to file"); } else { - success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "unsigned_monero_tx"; + success_msg_writer(true) << tr("Unsigned transaction(s) successfully written to file: ") << "unsigned_wownero_tx"; } } else @@ -6042,7 +6042,7 @@ bool simple_wallet::sign_transfer(const std::vector &args_) std::vector ptx; try { - bool r = m_wallet->sign_tx("unsigned_monero_tx", "signed_monero_tx", ptx, [&](const tools::wallet2::unsigned_tx_set &tx){ return accept_loaded_tx(tx); }, export_raw); + bool r = m_wallet->sign_tx("unsigned_wownero_tx", "signed_wownero_tx", ptx, [&](const tools::wallet2::unsigned_tx_set &tx){ return accept_loaded_tx(tx); }, export_raw); if (!r) { fail_msg_writer() << tr("Failed to sign transaction"); @@ -6062,7 +6062,7 @@ bool simple_wallet::sign_transfer(const std::vector &args_) txids_as_text += (", "); txids_as_text += epee::string_tools::pod_to_hex(get_transaction_hash(t.tx)); } - success_msg_writer(true) << tr("Transaction successfully signed to file ") << "signed_monero_tx" << ", txid " << txids_as_text; + success_msg_writer(true) << tr("Transaction successfully signed to file ") << "signed_wownero_tx" << ", txid " << txids_as_text; if (export_raw) { std::string rawfiles_as_text; @@ -6070,7 +6070,7 @@ bool simple_wallet::sign_transfer(const std::vector &args_) { if (i > 0) rawfiles_as_text += ", "; - rawfiles_as_text += "signed_monero_tx_raw" + (ptx.size() == 1 ? "" : ("_" + std::to_string(i))); + rawfiles_as_text += "signed_wownero_tx_raw" + (ptx.size() == 1 ? "" : ("_" + std::to_string(i))); } success_msg_writer(true) << tr("Transaction raw hex data exported to ") << rawfiles_as_text; } @@ -6090,7 +6090,7 @@ bool simple_wallet::submit_transfer(const std::vector &args_) try { std::vector ptx_vector; - bool r = m_wallet->load_tx("signed_monero_tx", ptx_vector, [&](const tools::wallet2::signed_tx_set &tx){ return accept_loaded_tx(tx); }); + bool r = m_wallet->load_tx("signed_wownero_tx", ptx_vector, [&](const tools::wallet2::signed_tx_set &tx){ return accept_loaded_tx(tx); }); if (!r) { fail_msg_writer() << tr("Failed to load transaction from file"); @@ -6243,7 +6243,7 @@ bool simple_wallet::get_tx_proof(const std::vector &args) try { std::string sig_str = m_wallet->get_tx_proof(txid, info.address, info.is_subaddress, args.size() == 3 ? args[2] : ""); - const std::string filename = "monero_tx_proof"; + const std::string filename = "wownero_tx_proof"; if (epee::file_io_utils::save_string_to_file(filename, sig_str)) success_msg_writer() << tr("signature file saved to: ") << filename; else @@ -6458,7 +6458,7 @@ bool simple_wallet::get_spend_proof(const std::vector &args) try { const std::string sig_str = m_wallet->get_spend_proof(txid, args.size() == 2 ? args[1] : ""); - const std::string filename = "monero_spend_proof"; + const std::string filename = "wownero_spend_proof"; if (epee::file_io_utils::save_string_to_file(filename, sig_str)) success_msg_writer() << tr("signature file saved to: ") << filename; else @@ -6553,7 +6553,7 @@ bool simple_wallet::get_reserve_proof(const std::vector &args) try { const std::string sig_str = m_wallet->get_reserve_proof(account_minreserve, args.size() == 2 ? args[1] : ""); - const std::string filename = "monero_reserve_proof"; + const std::string filename = "wownero_reserve_proof"; if (epee::file_io_utils::save_string_to_file(filename, sig_str)) success_msg_writer() << tr("signature file saved to: ") << filename; else @@ -8077,7 +8077,7 @@ void simple_wallet::commit_or_save(std::vector& ptx_ cryptonote::blobdata blob; tx_to_blob(ptx.tx, blob); const std::string blob_hex = epee::string_tools::buff_to_hex_nodelimer(blob); - const std::string filename = "raw_monero_tx" + (ptx_vector.size() == 1 ? "" : ("_" + std::to_string(i++))); + const std::string filename = "raw_wownero_tx" + (ptx_vector.size() == 1 ? "" : ("_" + std::to_string(i++))); if (epee::file_io_utils::save_string_to_file(filename, blob_hex)) success_msg_writer(true) << tr("Transaction successfully saved to ") << filename << tr(", txid ") << txid; else @@ -8136,12 +8136,12 @@ int main(int argc, char* argv[]) bool should_terminate = false; std::tie(vm, should_terminate) = wallet_args::main( argc, argv, - "monero-wallet-cli [--wallet-file=|--generate-new-wallet=] []", - sw::tr("This is the command line monero wallet. It needs to connect to a monero\ndaemon to work correctly.\nWARNING: Do not reuse your Monero keys on another fork, UNLESS this fork has key reuse mitigations built in. Doing so will harm your privacy."), + "wownero-wallet-cli [--wallet-file=|--generate-new-wallet=] []", + sw::tr("This is the command line wownero wallet. It needs to connect to a wownero\ndaemon to work correctly.\nWARNING: Do not reuse your Wownero keys on another fork, UNLESS this fork has key reuse mitigations built in. Doing so will harm your privacy."), desc_params, positional_options, [](const std::string &s, bool emphasis){ tools::scoped_message_writer(emphasis ? epee::console_color_white : epee::console_color_default, true) << s; }, - "monero-wallet-cli.log" + "wownero-wallet-cli.log" ); if (!vm) diff --git a/src/version.cpp.in b/src/version.cpp.in index 4546aeb69..722e0d21c 100644 --- a/src/version.cpp.in +++ b/src/version.cpp.in @@ -1,6 +1,6 @@ #define DEF_MONERO_VERSION_TAG "@VERSIONTAG@" -#define DEF_MONERO_VERSION "0.14.0.2" -#define DEF_MONERO_RELEASE_NAME "Boron Butterfly" +#define DEF_MONERO_VERSION "0.6.0.0" +#define DEF_MONERO_RELEASE_NAME "TBD" #define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG #include "version.h" diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index be10b9f62..cdabd4924 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -100,7 +100,7 @@ target_link_libraries(wallet_rpc_server ${EXTRA_LIBRARIES}) set_property(TARGET wallet_rpc_server PROPERTY - OUTPUT_NAME "monero-wallet-rpc") + OUTPUT_NAME "wownero-wallet-rpc") install(TARGETS wallet_rpc_server DESTINATION bin) diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index ea1a93b67..211a7fd64 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -60,7 +60,7 @@ namespace Monero { namespace { // copy-pasted from simplewallet - static const size_t DEFAULT_MIXIN = 6; + static const size_t DEFAULT_MIXIN = 21; static const int DEFAULT_REFRESH_INTERVAL_MILLIS = 1000 * 10; // limit maximum refresh interval as one minute static const int MAX_REFRESH_INTERVAL_MILLIS = 1000 * 60 * 1; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 85dfbd9d7..bb0b41c1c 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -92,9 +92,9 @@ using namespace cryptonote; #define CHACHA8_KEY_TAIL 0x8c #define CACHE_KEY_TAIL 0x8d -#define UNSIGNED_TX_PREFIX "Monero unsigned tx set\004" -#define SIGNED_TX_PREFIX "Monero signed tx set\004" -#define MULTISIG_UNSIGNED_TX_PREFIX "Monero multisig unsigned tx set\001" +#define UNSIGNED_TX_PREFIX "Wownero unsigned tx set\004" +#define SIGNED_TX_PREFIX "Wownero signed tx set\004" +#define MULTISIG_UNSIGNED_TX_PREFIX "Wownero multisig unsigned tx set\001" #define RECENT_OUTPUT_RATIO (0.5) // 50% of outputs are from the recent zone #define RECENT_OUTPUT_DAYS (1.8) // last 1.8 day makes up the recent zone (taken from monerolink.pdf, Miller et al) @@ -108,11 +108,11 @@ using namespace cryptonote; #define SUBADDRESS_LOOKAHEAD_MAJOR 50 #define SUBADDRESS_LOOKAHEAD_MINOR 200 -#define KEY_IMAGE_EXPORT_FILE_MAGIC "Monero key image export\002" +#define KEY_IMAGE_EXPORT_FILE_MAGIC "Wownero key image export\002" -#define MULTISIG_EXPORT_FILE_MAGIC "Monero multisig export\001" +#define MULTISIG_EXPORT_FILE_MAGIC "Wownero multisig export\001" -#define OUTPUT_EXPORT_FILE_MAGIC "Monero output export\003" +#define OUTPUT_EXPORT_FILE_MAGIC "Wownero output export\003" #define SEGREGATION_FORK_HEIGHT 99999999 #define TESTNET_SEGREGATION_FORK_HEIGHT 99999999 @@ -825,9 +825,9 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): m_first_refresh_done(false), m_refresh_from_block_height(0), m_explicit_refresh_from_block_height(true), - m_confirm_missing_payment_id(true), + m_confirm_missing_payment_id(false), m_confirm_non_default_ring_size(true), - m_ask_password(AskPasswordToDecrypt), + m_ask_password(AskPasswordOnAction), m_min_output_count(0), m_min_output_value(0), m_merge_destinations(false), @@ -835,8 +835,8 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): m_confirm_backlog_threshold(0), m_confirm_export_overwrite(true), m_auto_low_priority(true), - m_segregate_pre_fork_outputs(true), - m_key_reuse_mitigation2(true), + m_segregate_pre_fork_outputs(false), + m_key_reuse_mitigation2(false), m_segregation_height(0), m_ignore_fractional_outputs(true), m_is_initialized(false), @@ -1308,8 +1308,8 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons if (!m_encrypt_keys_after_refresh) { boost::optional pwd = m_callback->on_get_password("output received"); - THROW_WALLET_EXCEPTION_IF(!pwd, error::password_needed, tr("Password is needed to compute key image for incoming monero")); - THROW_WALLET_EXCEPTION_IF(!verify_password(*pwd), error::password_needed, tr("Invalid password: password is needed to compute key image for incoming monero")); + THROW_WALLET_EXCEPTION_IF(!pwd, error::password_needed, tr("Password is needed to compute key image for incoming wownero")); + THROW_WALLET_EXCEPTION_IF(!verify_password(*pwd), error::password_needed, tr("Invalid password: password is needed to compute key image for incoming wownero")); decrypt_keys(*pwd); m_encrypt_keys_after_refresh = *pwd; } @@ -5115,8 +5115,7 @@ bool wallet2::is_tx_spendtime_unlocked(uint64_t unlock_time, uint64_t block_heig uint64_t current_time = static_cast(time(NULL)); // XXX: this needs to be fast, so we'd need to get the starting heights // from the daemon to be correct once voting kicks in - uint64_t v2height = m_nettype == TESTNET ? 624634 : m_nettype == STAGENET ? 32000 : 1009827; - uint64_t leeway = block_height < v2height ? CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V1 : CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V2; + uint64_t leeway = CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V2; if(current_time + leeway >= unlock_time) return true; else @@ -6196,20 +6195,14 @@ int wallet2::get_fee_algorithm() const uint64_t wallet2::get_min_ring_size() const { if (use_fork_rules(8, 10)) - return 11; - if (use_fork_rules(7, 10)) - return 7; - if (use_fork_rules(6, 10)) - return 5; - if (use_fork_rules(2, 10)) - return 3; + return 22; return 0; } //------------------------------------------------------------------------------------------------------------------------------ uint64_t wallet2::get_max_ring_size() const { if (use_fork_rules(8, 10)) - return 11; + return 22; return 0; } //------------------------------------------------------------------------------------------------------------------------------ @@ -10206,18 +10199,7 @@ uint64_t wallet2::get_daemon_blockchain_target_height(string &err) uint64_t wallet2::get_approximate_blockchain_height() const { - // time of v2 fork - const time_t fork_time = m_nettype == TESTNET ? 1448285909 : m_nettype == STAGENET ? 1520937818 : 1458748658; - // v2 fork block - const uint64_t fork_block = m_nettype == TESTNET ? 624634 : m_nettype == STAGENET ? 32000 : 1009827; - // avg seconds per block - const int seconds_per_block = DIFFICULTY_TARGET_V2; - // Calculated blockchain height - uint64_t approx_blockchain_height = fork_block + (time(NULL) - fork_time)/seconds_per_block; - // testnet got some huge rollbacks, so the estimation is way off - static const uint64_t approximate_testnet_rolled_back_blocks = 303967; - if (m_nettype == TESTNET && approx_blockchain_height > approximate_testnet_rolled_back_blocks) - approx_blockchain_height -= approximate_testnet_rolled_back_blocks; + uint64_t approx_blockchain_height = m_nettype == TESTNET ? 0 : (time(NULL) - 1522624244)/339; LOG_PRINT_L2("Calculated blockchain height: " << approx_blockchain_height); return approx_blockchain_height; } @@ -11356,7 +11338,7 @@ std::string wallet2::make_uri(const std::string &address, const std::string &pay } } - std::string uri = "monero:" + address; + std::string uri = "wownero:" + address; unsigned int n_fields = 0; if (!payment_id.empty()) @@ -11385,13 +11367,13 @@ std::string wallet2::make_uri(const std::string &address, const std::string &pay //---------------------------------------------------------------------------------------------------- bool wallet2::parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &amount, std::string &tx_description, std::string &recipient_name, std::vector &unknown_parameters, std::string &error) { - if (uri.substr(0, 7) != "monero:") + if (uri.substr(0, 8) != "wownero:") { - error = std::string("URI has wrong scheme (expected \"monero:\"): ") + uri; + error = std::string("URI has wrong scheme (expected \"wownero:\"): ") + uri; return false; } - std::string remainder = uri.substr(7); + std::string remainder = uri.substr(8); const char *ptr = strchr(remainder.c_str(), '?'); address = ptr ? remainder.substr(0, ptr-remainder.c_str()) : remainder; @@ -11645,15 +11627,11 @@ uint64_t wallet2::get_segregation_fork_height() const if (m_segregation_height > 0) return m_segregation_height; - static const bool use_dns = true; + static const bool use_dns = false; if (use_dns) { // All four MoneroPulse domains have DNSSEC on and valid static const std::vector dns_urls = { - "segheights.moneropulse.org", - "segheights.moneropulse.net", - "segheights.moneropulse.co", - "segheights.moneropulse.se" }; const uint64_t current_height = get_blockchain_current_height(); diff --git a/src/wallet/wallet_args.cpp b/src/wallet/wallet_args.cpp index 95a4e0ad6..63e1250dd 100644 --- a/src/wallet/wallet_args.cpp +++ b/src/wallet/wallet_args.cpp @@ -140,7 +140,7 @@ namespace wallet_args if (command_line::get_arg(vm, command_line::arg_help)) { - Print(print) << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL; + Print(print) << "Wownero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << ENDL; Print(print) << wallet_args::tr("This is the command line monero wallet. It needs to connect to a monero\n" "daemon to work correctly.") << ENDL; Print(print) << wallet_args::tr("Usage:") << ENDL << " " << usage; @@ -150,7 +150,7 @@ namespace wallet_args } else if (command_line::get_arg(vm, command_line::arg_version)) { - Print(print) << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; + Print(print) << "Wownero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; should_terminate = true; return true; } @@ -201,7 +201,7 @@ namespace wallet_args if (!command_line::is_arg_defaulted(vm, arg_max_concurrency)) tools::set_max_concurrency(command_line::get_arg(vm, arg_max_concurrency)); - Print(print) << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; + Print(print) << "Wownero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")"; if (!command_line::is_arg_defaulted(vm, arg_log_level)) MINFO("Setting log level = " << command_line::get_arg(vm, arg_log_level)); diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index e452d21e8..bd31162ca 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -64,7 +64,7 @@ namespace const command_line::arg_descriptor arg_wallet_dir = {"wallet-dir", "Directory for newly created wallets"}; const command_line::arg_descriptor arg_prompt_for_password = {"prompt-for-password", "Prompts for password when not provided", false}; - constexpr const char default_rpc_username[] = "monero"; + constexpr const char default_rpc_username[] = "wownero"; boost::optional password_prompter(const char *prompt, bool verify) { @@ -205,7 +205,7 @@ namespace tools string_encoding::base64_encode(rand_128bit.data(), rand_128bit.size()) ); - std::string temp = "monero-wallet-rpc." + bind_port + ".login"; + std::string temp = "wownero-wallet-rpc." + bind_port + ".login"; rpc_login_file = tools::private_file::create(temp); if (!rpc_login_file.handle()) { @@ -632,7 +632,7 @@ namespace tools } if (addresses.empty()) { - er.message = std::string("No Monero address found at ") + url; + er.message = std::string("No Wownero address found at ") + url; return {}; } return addresses[0]; @@ -1660,7 +1660,7 @@ namespace tools } if (addresses.empty()) { - er.message = std::string("No Monero address found at ") + url; + er.message = std::string("No Wownero address found at ") + url; return {}; } return addresses[0]; @@ -2445,7 +2445,7 @@ namespace tools } if (addresses.empty()) { - er.message = std::string("No Monero address found at ") + url; + er.message = std::string("No Wownero address found at ") + url; return {}; } return addresses[0]; @@ -3541,12 +3541,12 @@ int main(int argc, char** argv) { bool should_terminate = false; std::tie(vm, should_terminate) = wallet_args::main( argc, argv, - "monero-wallet-rpc [--wallet-file=|--generate-from-json=|--wallet-dir=] [--rpc-bind-port=]", - tools::wallet_rpc_server::tr("This is the RPC monero wallet. It needs to connect to a monero\ndaemon to work correctly."), + "wownero-wallet-rpc [--wallet-file=|--generate-from-json=|--wallet-dir=] [--rpc-bind-port=]", + tools::wallet_rpc_server::tr("This is the RPC wownero wallet. It needs to connect to a wownero\ndaemon to work correctly."), desc_params, po::positional_options_description(), [](const std::string &s, bool emphasis){ epee::set_console_color(emphasis ? epee::console_color_white : epee::console_color_default, true); std::cout << s << std::endl; if (emphasis) epee::reset_console_color(); }, - "monero-wallet-rpc.log", + "wownero-wallet-rpc.log", true ); if (!vm) diff --git a/tests/README.md b/tests/README.md index 0bf097254..234a81f29 100644 --- a/tests/README.md +++ b/tests/README.md @@ -3,7 +3,7 @@ To run all tests, run: ``` -cd /path/to/monero +cd /path/to/wownero make [-jn] debug-test # where n is number of compiler processes ``` @@ -11,11 +11,11 @@ To test a release build, replace `debug-test` with `release-test` in the previou # Core tests -Core tests take longer than any other Monero tests, due to the high amount of computational work involved in validating core components. +Core tests take longer than any other Wownero tests, due to the high amount of computational work involved in validating core components. Tests are located in `tests/core_tests/`, and follow a straightforward naming convention. Most cases cover core functionality (`block_reward.cpp`, `chaingen.cpp`, `rct.cpp`, etc.), while some cover basic security tests (`double_spend.cpp` & `integer_overflow.cpp`). -To run only Monero's core tests (after building): +To run only Wownero's core tests (after building): ``` cd build/debug/tests/core @@ -34,7 +34,7 @@ Crypto tests are located under the `tests/crypto` directory. Tests correspond to components under `src/crypto/`. A quick comparison reveals the pattern, and new tests should continue the naming convention. -To run only Monero's crypto tests (after building): +To run only Wownero's crypto tests (after building): ``` cd build/debug/tests/crypto @@ -54,7 +54,7 @@ Functional tests are located under the `tests/functional` directory. First, run a regtest daemon in the offline mode and with a fixed difficulty: ``` -monerod --regtest --offline --fixed-difficulty 1 +wownerod --regtest --offline --fixed-difficulty 1 ``` Alternatively, you can run multiple daemons and let them connect with each other by using `--add-exclusive-node`. In this case, make sure that the same fixed difficulty is given to all the daemons. @@ -75,7 +75,7 @@ An additional helper utility is provided `contrib/fuzz_testing/fuzz.sh`. AFL mus Hash tests exist under `tests/hash`, and include a set of target hashes in text files. -To run only Monero's hash tests (after building): +To run only Wownero's hash tests (after building): ``` cd build/debug/tests/hash @@ -96,7 +96,7 @@ To run the same tests on a release build, replace `debug` with `release`. Performance tests are located in `tests/performance_tests`, and test features for performance metrics on the host machine. -To run only Monero's performance tests (after building): +To run only Wownero's performance tests (after building): ``` cd build/debug/tests/performance_tests @@ -111,7 +111,7 @@ To run the same tests on a release build, replace `debug` with `release`. Unit tests are defined under the `tests/unit_tests` directory. Independent components are tested individually to ensure they work properly on their own. -To run only Monero's unit tests (after building): +To run only Wownero's unit tests (after building): ``` cd build/debug/tests/unit_tests diff --git a/tests/difficulty/data.txt b/tests/difficulty/data.txt deleted file mode 100644 index 17c48025e..000000000 --- a/tests/difficulty/data.txt +++ /dev/null @@ -1,1000 +0,0 @@ -996 1 -985 1 -978 11 -1001 80 -1040 480 -1196 1108 -1109 925 -1241 1434 -1243 1843 -986 2664 -920 3870 -2213 4613 -2472 1581 -2592 1439 -2712 1439 -3056 1439 -3392 1289 -3723 1176 -3937 1088 -4219 1054 -4447 1002 -4681 971 -4702 942 -4683 967 -4942 997 -4958 968 -5126 993 -5188 981 -5322 995 -5545 991 -5531 969 -5792 994 -5831 969 -6020 985 -5943 971 -6179 994 -6409 987 -6587 967 -6736 957 -7027 952 -7293 926 -7494 905 -7747 893 -7805 876 -7911 884 -7923 886 -7996 899 -8149 905 -8101 901 -8258 916 -8395 918 -8295 915 -8430 930 -8719 941 -8698 920 -8819 934 -8788 937 -8804 951 -8797 966 -8857 980 -8848 990 -8981 1005 -8896 1005 -9012 1020 -9104 1031 -9295 1035 -9312 1026 -9432 1038 -9731 1038 -9991 1017 -10291 1002 -10510 982 -10607 972 -10563 975 -10669 987 -10567 993 -10813 1005 -11075 1002 -11045 988 -11253 1000 -11321 994 -11492 999 -11721 995 -11839 984 -11855 985 -12017 994 -12104 990 -12062 993 -12162 1004 -12075 1009 -12211 1020 -12345 1027 -12257 1025 -12155 1036 -12326 1047 -12620 1058 -12772 1044 -12993 1041 -13173 1032 -13081 1027 -13079 1037 -13082 1048 -13072 1058 -13078 1068 -13069 1079 -13060 1089 -13056 1100 -13057 1111 -13062 1122 -13056 1133 -13060 1144 -13052 1155 -13057 1166 -13049 1178 -13041 1189 -13042 1201 -13036 1213 -13041 1224 -13036 1236 -13034 1249 -13031 1261 -13029 1273 -13025 1286 -13030 1298 -13020 1311 -13026 1324 -13021 1337 -13028 1350 -13038 1363 -13037 1376 -13026 1390 -13024 1403 -13014 1417 -13011 1431 -13011 1445 -13004 1459 -13000 1474 -12992 1488 -13000 1503 -13003 1517 -13004 1532 -12995 1547 -12987 1562 -12983 1578 -12992 1593 -12993 1609 -12983 1624 -12973 1640 -12967 1656 -12961 1673 -12960 1689 -12963 1705 -12971 1722 -12968 1739 -12973 1756 -12968 1773 -12965 1791 -12966 1808 -12958 1826 -12949 1844 -12957 1862 -12947 1880 -12957 1898 -12958 1917 -12967 1936 -12975 1955 -12973 1974 -12965 1993 -12974 2013 -12975 2033 -12979 2052 -12978 2073 -12968 2093 -12965 2113 -12975 2134 -12966 2155 -12962 2176 -12955 2197 -12960 2219 -12969 2241 -12965 2263 -12973 2285 -12962 2307 -12956 2330 -12947 2352 -12948 2376 -12944 2399 -12950 2422 -12949 2446 -12942 2470 -12942 2494 -12945 2519 -12934 2543 -12932 2568 -12932 2593 -12939 2619 -12940 2644 -12931 2670 -12930 2696 -12922 2723 -12920 2749 -12925 2776 -12916 2804 -12922 2831 -12923 2859 -12922 2887 -12924 2915 -12927 2944 -12932 2972 -12925 3001 -12924 3031 -12933 3061 -12942 3091 -12934 3121 -12925 3151 -12930 3182 -12927 3213 -12935 3245 -12931 3277 -12938 3309 -12931 3341 -12933 3374 -12940 3407 -12935 3440 -12944 3474 -12943 3508 -12954 3542 -12952 3577 -12956 3612 -12959 3647 -12968 3683 -12975 3719 -12976 3756 -12979 3792 -12988 3830 -12993 3867 -12986 3905 -12979 3943 -12980 3982 -12974 4021 -12972 4060 -12978 4100 -12980 4140 -12989 4181 -12997 4222 -12988 4263 -12996 4305 -13001 4347 -12998 4389 -12996 4432 -13001 4476 -12995 4520 -12991 4564 -12984 4609 -12993 4654 -12991 4699 -12983 4745 -12987 4792 -12978 4839 -12982 4886 -12973 4934 -12964 4982 -12961 5031 -12970 5080 -12981 5130 -12974 5180 -12972 5231 -12962 5282 -12970 5334 -12969 5386 -12973 5439 -12971 5492 -12964 5546 -12975 5600 -12985 5655 -12977 5711 -12971 5767 -12964 5823 -12974 5880 -12982 5938 -12991 5996 -12993 6055 -13002 6114 -12991 6174 -12988 6234 -12987 6295 -12995 6357 -12986 6419 -12993 6482 -13000 6545 -13005 6610 -13002 6674 -12997 6740 -12998 6806 -12999 6872 -13009 6940 -13004 7008 -13006 7076 -12996 7146 -13002 7216 -13014 7286 -13026 7358 -13024 7430 -13029 7502 -13033 7576 -13037 7650 -13045 7725 -13047 7801 -13058 7877 -13058 7954 -13062 8032 -13052 8111 -13049 8190 -13056 8270 -13057 8351 -13047 8433 -13039 8516 -13031 8599 -13038 8683 -13029 8768 -13019 8854 -13010 8941 -13005 9029 -12995 9117 -12988 9206 -12999 9296 -12998 9387 -12991 9479 -12997 9572 -13002 9666 -12993 9761 -13004 9856 -13005 9953 -13013 10050 -13006 10149 -13000 10248 -12995 10348 -12996 10450 -12996 10552 -12995 10655 -12998 10760 -12993 10865 -12998 10972 -12989 11079 -12998 11188 -12991 11297 -13003 11408 -13008 11519 -12997 11632 -13005 11746 -13009 11861 -13009 11977 -13005 12095 -13000 12213 -12998 12333 -12993 12454 -12986 12575 -12994 12699 -12996 12823 -13004 12949 -13008 13075 -13008 13203 -13007 13333 -13002 13463 -13013 13595 -13013 13728 -13009 13863 -13023 13999 -13027 14136 -13027 14274 -13028 14414 -13025 14555 -13015 14698 -13020 14842 -13020 14987 -13023 15134 -13013 15282 -13023 15432 -13023 15583 -13037 15735 -13048 15889 -13061 16045 -13050 16202 -13063 16361 -13061 16521 -13049 16683 -13059 16846 -13059 17011 -13060 17178 -13075 17346 -13071 17516 -13073 17687 -13082 17861 -13091 18036 -13083 18212 -13090 18391 -13078 18571 -13084 18753 -13092 18936 -13085 19122 -13077 19309 -13067 19498 -13078 19689 -13093 19882 -13090 20077 -13094 20273 -13098 20472 -13114 20672 -13108 20875 -13117 21079 -13112 21286 -13102 21494 -13109 21705 -13122 21917 -13121 22132 -13129 22348 -13122 22567 -13115 22788 -13113 23012 -13121 23237 -13114 23464 -13103 23694 -13093 23926 -13098 24161 -13099 24397 -13092 24636 -13102 24877 -13104 25121 -13092 25367 -13103 25616 -13099 25866 -13089 26120 -13093 26376 -13101 26634 -13113 26895 -13121 27158 -13128 27424 -13138 27693 -13140 27964 -13157 28238 -13157 28514 -13144 28794 -13155 29076 -13156 29360 -13173 29648 -13163 29938 -13180 30231 -13188 30510 -13191 30789 -13177 31082 -13174 31386 -13164 31693 -13168 32003 -13173 32316 -13167 32632 -13177 32951 -13188 33273 -13201 33599 -13209 33900 -13220 34209 -13211 34512 -13208 34848 -13194 35188 -13181 35532 -13198 35878 -13192 36228 -13201 36582 -13208 36939 -13197 37299 -13197 37663 -13196 38030 -13208 38401 -13215 38776 -13226 39154 -13215 39517 -13231 39902 -13244 40275 -13254 40625 -13253 40987 -13241 41386 -13241 41789 -13235 42195 -13237 42606 -13239 43020 -13243 43439 -13233 43861 -13241 44288 -13231 44719 -13252 45154 -13263 45593 -13251 46003 -13261 46451 -13277 46902 -13290 47305 -13280 47714 -13291 48177 -13293 48640 -13284 49104 -13278 49580 -13281 50061 -13277 50547 -13284 51037 -13294 51532 -13286 52027 -13285 52532 -13292 53041 -13283 53556 -13291 54075 -13281 54600 -13278 55129 -13267 55664 -13269 56203 -13279 56749 -13269 57299 -13270 57855 -13278 58416 -13283 58982 -13282 59554 -13278 60132 -13272 60715 -13275 61304 -13278 61898 -13286 62498 -13285 63104 -13291 63716 -13295 64334 -13305 64953 -13311 65530 -13310 66133 -13314 66773 -13323 67404 -13316 68007 -13324 68665 -13322 69323 -13330 69994 -13322 70637 -13315 71320 -13322 72010 -13332 72706 -13333 73397 -13337 74101 -13337 74793 -13339 75516 -13340 76234 -13347 76964 -13351 77664 -13353 78389 -13349 79132 -13357 79896 -13347 80641 -13347 81420 -13336 82205 -13330 82998 -13325 83799 -13324 84608 -13315 85424 -13316 86248 -13325 87080 -13321 87921 -13327 88769 -13320 89625 -13313 90490 -13322 91363 -13327 92245 -13318 93135 -13313 94033 -13304 94941 -13298 95857 -13301 96782 -13294 97716 -13284 98658 -13291 99610 -13287 100571 -13296 101542 -13296 102521 -13299 103511 -13303 104509 -13312 105518 -13313 106536 -13315 107564 -13307 108602 -13299 109649 -13289 110707 -13297 111776 -13289 112854 -13295 113943 -13304 115042 -13296 116152 -13296 117273 -13301 118405 -13292 119547 -13301 120701 -13305 121865 -13294 123041 -13301 124228 -13308 125427 -13315 126637 -13309 127859 -13316 129092 -13322 130338 -13324 131596 -13330 133488 -13328 133531 -13328 134902 -13335 134923 -13334 136229 -13328 136252 -13328 137668 -13330 137690 -13336 139046 -13328 139046 -13317 140806 -13324 140806 -13316 142922 -13317 143004 -13326 145371 -13330 145383 -13332 147263 -13332 147287 -13327 148653 -13324 148653 -13323 163065 -13314 163080 -13323 168505 -13322 168505 -13327 171944 -13331 171960 -13336 175501 -13343 175501 -13336 183009 -13341 183009 -13334 190931 -13338 190931 -13337 199306 -13329 199306 -13333 205710 -13338 205710 -13335 213997 -13330 213997 -13329 221480 -13322 221505 -13326 229542 -13325 229542 -13324 231843 -13331 231870 -13329 234634 -13326 234634 -13332 243740 -13339 243769 -13328 246606 -13332 246635 -13325 254183 -13332 254183 -13335 258637 -13325 258637 -13330 265577 -13321 265611 -13315 275411 -13319 275411 -13309 278711 -13303 278711 -13313 290738 -13310 290738 -13305 295289 -13294 295289 -13293 302850 -13288 302850 -13285 309294 -13280 309336 -13288 319541 -13295 319541 -13304 333707 -13309 333755 -13301 346209 -13293 346209 -13301 357909 -13304 357909 -13310 378490 -13309 378490 -13310 399550 -13302 399550 -13313 417848 -13313 417848 -13324 441697 -13315 441697 -13319 451301 -13311 451382 -13317 465329 -13314 465329 -13318 471489 -13322 471489 -13313 483202 -13311 483293 -13300 498367 -13302 498367 -13296 508613 -13290 508613 -13280 525295 -13278 525295 -13280 535015 -13288 535015 -13288 551838 -13285 551838 -13292 561986 -13303 562101 -13300 601209 -13290 601209 -13293 610660 -13292 610660 -13296 626756 -13297 626756 -13294 635012 -13305 635152 -13299 643158 -13299 643158 -13300 652606 -13304 652606 -13296 664186 -13287 664335 -13296 673132 -13301 673132 -13301 673132 -13310 673132 -13307 673132 -13303 673132 -13302 673132 -13296 673132 -13293 673132 -13305 673132 -13294 673132 -13297 673132 -13288 673132 -13294 673132 -13296 673132 -13304 673132 -13297 673132 -13306 686583 -13296 708022 -13302 721171 -13301 745255 -13293 789338 -13288 801708 -13294 835723 -13295 915543 -13291 998038 -13298 1109322 -13302 1209204 -13312 1246864 -13314 1263152 -13320 1296983 -13325 1342856 -13324 1436884 -13320 1601878 -13325 1643370 -13324 1806409 -13334 1891570 -13330 2094126 -13319 2424286 -13319 2650457 -13325 2715282 -13311 3092010 -13318 3244961 -13306 3321946 -13321 3446480 -13328 3644333 -13334 3714812 -13328 3928889 -13313 4152287 -13324 4497011 -13324 4645094 -13325 6541658 -13339 8449869 -13337 11545575 -13316 11814280 -13302 12028489 -13324 12187097 -13349 12345701 -13354 12540339 -13396 12735943 -13396 12903378 -13425 13103440 -13461 13283023 -13509 13429115 -13499 13679122 -13521 13861980 -13523 14155832 -13530 14344931 -13518 14573230 -13511 14765343 -13547 14959879 -13528 15192777 -13579 15390669 -13611 15588557 -13644 15789960 -13676 15991403 -13708 16236622 -13753 16441249 -13812 16688228 -13787 16893368 -13812 17230031 -13793 17481566 -13811 17688803 -13792 17896037 -13830 18196823 -13819 18357684 -13834 18565439 -13870 18821700 -13859 19079284 -13897 19288103 -13883 19547559 -13908 19860081 -13963 20070522 -14012 20285173 -14080 20560219 -14140 20732016 -14148 21019918 -14162 21379326 -14150 21746458 -14130 22069652 -14183 22420034 -14219 22677730 -14213 23091563 -14260 23414683 -14276 23874363 -14274 24276963 -14318 24756195 -14390 25185364 -14462 25644616 -14544 26226598 -14557 26681976 -14554 27332346 -14521 27938692 -14602 28610024 -14588 29308918 -14613 30156474 -14580 31024769 -14543 32013610 -14577 32963488 -14676 34023072 -14675 35029231 -14691 36188609 -14767 37370414 -14885 38518184 -14878 39730624 -14974 40937806 -15008 41959660 -15009 43997972 -15039 46631045 -15039 49969426 -15147 53632107 -15180 57066286 -15368 60514005 -15379 64138256 -15356 67460179 -15403 64918929 -15560 68486057 -15590 67591981 -15580 65909365 -15514 64245787 -15641 66071664 -15587 68860387 -15537 71048137 -15710 73745734 -15627 76599571 -15618 79052834 -15739 81934490 -15985 82669438 -15981 81374711 -16103 80244245 -16349 79139295 -16328 78279627 -16544 77533151 -16615 75616132 -16616 74926817 -16593 76917472 -16584 79312606 -16665 80101567 -16634 82504177 -16867 85030276 -16971 86883326 -16895 88319800 -16965 90469139 -16950 90465262 -16877 91890415 -17117 93106091 -17238 94321256 -17441 95846287 -17491 92979967 -17446 90954994 -17394 87623370 -17542 85999264 -17681 87412539 -17819 88988409 -17725 91038329 -17943 92336744 -17991 93029082 -18031 92973033 -17970 94737578 -18141 93980118 -18320 95237016 -18545 97391127 -18712 96561416 -18849 93797276 -19074 91336444 -19161 89892725 -19148 90669712 -19232 92693040 -19181 94222209 -19254 96265222 -19423 97254840 -19535 99319736 -19760 101190729 -19980 102676323 -20185 104459744 -20111 103054687 -20358 105521248 -20374 107126438 -20374 105088747 -20372 101446709 -20283 103568593 -20594 101467587 -20811 102246714 -20967 104790689 -21208 106029191 -21371 108934133 -21676 106529418 -21593 108090230 -21829 103189197 -22124 105891192 -22244 108822755 -22548 110958877 -22829 109385192 -23059 111573148 -23240 113687895 -23411 115823707 -23340 118555853 -23628 121634310 -23764 123624077 -24150 126599053 -24048 129441066 -23992 129644548 -24056 131850076 -23959 124550087 -24027 127640855 -24241 125937704 -24583 120297009 -24504 122340050 -24487 118247531 -24522 119488591 -24540 121684801 -24460 123559998 -24412 126151243 -24638 128166276 -24577 129779407 -24541 125648210 -24616 128007653 -24872 130198716 -24743 131132412 -24629 133370101 -24483 135940710 -24513 133837081 -24476 132688318 -24687 130598203 -25045 131756130 -25247 134089716 -25356 135086151 -25531 135840432 -25757 134049326 -25820 135059817 -25838 134720446 -25847 133583240 -25722 135102319 -25599 136767738 -25942 137938928 -25959 137181584 -26362 134741089 -26356 131382998 -26249 129571190 -26193 128461060 -26203 125528898 -26296 125772324 -26356 127272308 -26510 128659463 -26803 129420466 -26965 130811709 -27050 129187186 -27343 128796859 -27408 126306858 -27344 124109316 -27400 123585673 -27354 124051058 -27479 124120333 -27752 124602137 -27936 126110194 -28045 127727168 -28334 129412894 -28508 127266450 -28641 125300988 -28748 124441135 -29000 122351487 diff --git a/tests/difficulty/export_data.pl b/tests/difficulty/export_data.pl new file mode 100644 index 000000000..0298fe338 --- /dev/null +++ b/tests/difficulty/export_data.pl @@ -0,0 +1,54 @@ +#!/usr/bin/perl + +# BSD 3-Clause License +# +# Copyright (c) 2018, Karbowanec +# 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 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. + +# Returns timestamps and difficulty data for wownero (monero RPC) +# Run: perl export_data.pl 50 300 + +$IP='explorer.wowne.ro:11181'; +$begin=$ARGV[0]; +if ($#ARGV == 1 ) { $end=$ARGV[1]; } +else { + $h=`curl -s -X POST http://$IP/json_rpc -d '{"params": {},"jsonrpc":"2.0","id":"test","method":"getblockcount"}' -H 'Content-Type: application/json'`; + $h=~/"count"\D+(\d+)/sg; + $end=$1; + print "$end\n"; +} +print "also printed to file\n\n"; +open (F,">wownero-data.txt"); +for ($i=$begin; $i<$end; $i++) { + $k=qq(-d '{"params":{"height":$i},"jsonrpc":"2.0","id":"test","method":"getblockheaderbyheight"}' -H 'Content-Type: application/json'); + $k=`curl -s -X POST http://$IP/json_rpc $k`; + $k=~/"difficulty"\D+(\d+).+"timestamp"\D+(\d+)/sg; + ($d, $t)=($1,$2); + print "$i\t$t\t$d\n"; + print F "$i\t$t\t$d\n"; + +} +close F; diff --git a/tests/difficulty/generate-data b/tests/difficulty/generate-data index c41ce025d..0e776727d 100755 --- a/tests/difficulty/generate-data +++ b/tests/difficulty/generate-data @@ -32,10 +32,10 @@ from random import randint -DIFFICULTY_TARGET = 120 -DIFFICULTY_WINDOW = 720 -DIFFICULTY_LAG = 15 -DIFFICULTY_CUT = 60 +DIFFICULTY_TARGET = 300 +DIFFICULTY_WINDOW = 144 +DIFFICULTY_LAG = 3 +DIFFICULTY_CUT = 12 UINT_MAX = (1 << 64) - 1 @@ -43,8 +43,8 @@ def difficulty(): times = [] diffs = [] while True: - if len(times) <= 1: - diff = 1 + if len(times) <= 10: + diff = 1069 else: begin = max(len(times) - DIFFICULTY_WINDOW - DIFFICULTY_LAG, 0) end = min(begin + DIFFICULTY_WINDOW, len(times)) diff --git a/tests/difficulty/wow-data.txt b/tests/difficulty/wow-data.txt new file mode 100644 index 000000000..5bbc4439c --- /dev/null +++ b/tests/difficulty/wow-data.txt @@ -0,0 +1,1000 @@ +995 1069 +1032 1069 +1053 1069 +1264 1069 +1261 1069 +1570 1069 +1738 1069 +1718 1069 +1622 1069 +1866 1069 +1802 1069 +1829 3682 +2832 4951 +2583 3156 +3305 3672 +3835 3397 +4507 3122 +5327 2791 +5474 2456 +5712 2540 +5496 2574 +5334 2737 +6029 2911 +5807 2902 +5578 3074 +5963 3258 +6268 3452 +6091 3492 +6606 3690 +7068 3665 +7047 3568 +7051 3744 +7998 3929 +8998 3576 +9274 3263 +9343 3272 +10005 3363 +10040 3228 +9810 3322 +9994 3433 +9758 3546 +10053 3664 +11100 3780 +10826 3501 +10463 3605 +10903 3712 +10761 3822 +11600 3935 +11747 3861 +11689 3916 +12362 4025 +13510 3914 +14360 3649 +15008 3499 +15674 3412 +16084 3327 +16147 3302 +15960 3354 +16485 3421 +16427 3412 +17133 3478 +17857 3403 +17957 3318 +18710 3357 +18950 3271 +19210 3282 +20199 3289 +20741 3171 +20845 3132 +21201 3163 +21621 3154 +21899 3136 +22693 3139 +22947 3068 +22842 3074 +22800 3116 +23140 3159 +23881 3174 +23562 3113 +23407 3154 +23455 3195 +23223 3237 +23310 3280 +23892 3323 +23674 3365 +24472 3409 +24259 3368 +24467 3411 +25492 3455 +25428 3353 +25574 3394 +25308 3424 +25245 3466 +26226 3508 +26694 3459 +27125 3437 +27506 3420 +27302 3409 +27213 3448 +27711 3487 +27708 3499 +27700 3538 +27701 3578 +27694 3618 +27686 3659 +27675 3700 +27668 3742 +27676 3784 +27672 3826 +27677 3869 +27682 3913 +27678 3956 +27684 4001 +27681 4046 +27677 4091 +27675 4137 +27675 4184 +27666 4231 +27672 4278 +27665 4326 +27669 4375 +27668 4418 +27660 4418 +27670 4460 +27662 4461 +27660 4534 +27671 4534 +27663 4573 +27672 4574 +27674 4667 +27670 4668 +27678 4717 +27684 4718 +27681 4775 +27680 4775 +27686 4819 +27675 4819 +27670 4872 +27669 4872 +27666 4919 +27662 4919 +27666 4938 +27664 4938 +27653 5076 +27642 5076 +27635 5076 +27636 5076 +27626 5076 +27619 5146 +27622 5259 +27614 5394 +27625 5572 +27634 5804 +27641 5838 +27638 5906 +27647 5944 +27653 5995 +27658 6060 +27649 6114 +27656 6185 +27658 6229 +27650 6270 +27651 6344 +27647 6465 +27637 6625 +27627 6651 +27639 6680 +27643 7021 +27653 7433 +27663 7586 +27665 7658 +27657 7878 +27649 7948 +27655 8077 +27651 8127 +27651 8188 +27662 8238 +27665 8478 +27663 8677 +27662 8762 +27660 8854 +27656 9010 +27647 9342 +27657 9453 +27667 9554 +27658 10007 +27657 10897 +27652 11687 +27663 12390 +27654 13194 +27647 13639 +27653 13911 +27654 14114 +27657 14600 +27659 14817 +27664 15878 +27653 17214 +27656 17567 +27644 19242 +27652 19983 +27652 20819 +27644 23841 +27661 26037 +27661 26808 +27653 28709 +27648 31196 +27648 33241 +27651 39181 +27640 40719 +27647 41788 +27655 43456 +27647 46144 +27656 47948 +27649 49895 +27648 52157 +27650 53942 +27660 56625 +27651 59747 +27639 64797 +27625 66841 +27654 76231 +27666 83764 +27699 86684 +27739 118603 +27759 126479 +27790 138334 +27843 147801 +27874 159630 +27917 240159 +27912 367275 +27850 676598 +27809 831659 +27759 1065989 +27634 2267177 +27186 5230850 +27792 5723763 +29418 6144718 +29414 6703797 +29749 7031689 +30418 6854115 +31944 7126179 +33023 5768775 +32645 5514464 +33673 5888393 +35188 5206040 +36923 5871441 +38855 6498473 +39616 6631237 +39971 7909003 +39945 9972757 +41505 14296572 +40526 20169397 +41333 4214732 +42781 5333764 +44339 5496043 +45323 4912825 +45763 3668236 +46077 3500080 +47055 3561345 +47969 3470275 +48074 2980670 +48393 2613300 +48813 2336894 +48988 2354393 +48976 2484122 +49036 2721340 +48778 2936811 +48538 3205459 +48566 3256775 +48536 3087933 +49040 2898516 +49848 2820503 +49856 2812669 +50671 2821637 +50412 2734469 +50452 2662676 +51196 2692677 +51462 2688994 +51468 2704067 +51778 2737529 +52008 2769402 +52488 2780176 +52929 2817114 +53763 2840588 +53740 2884678 +53645 2921423 +54264 2961426 +55037 2891712 +55223 2928569 +55467 2894164 +55954 2924968 +56281 2931759 +56029 2900638 +56410 2902157 +56889 2935514 +57223 2931760 +56936 2938226 +56882 2915074 +57630 2897810 +58041 2850822 +58728 2873530 +59194 2904811 +59038 2883435 +59737 2833650 +60508 2846314 +60368 2852477 +60547 2834318 +60471 2857733 +60207 2862888 +60298 2880234 +61002 2863771 +60922 2893079 +60938 2918445 +61416 2919606 +61983 2908978 +61684 2897692 +62540 2861291 +62340 2860706 +62993 2873891 +62970 2851662 +63817 2836631 +64354 2854835 +64996 2874607 +65812 2891592 +66342 2914353 +66814 2937038 +67499 2929632 +67704 2954169 +67784 2974619 +68344 2963956 +68730 2966118 +68729 2965456 +68656 2959531 +69316 2966998 +70112 2955118 +70000 2977222 +69858 2932726 +70619 2912941 +71371 2885820 +71901 2846621 +72299 2829983 +72417 2818121 +73186 2791439 +73755 2798973 +73954 2815316 +74421 2798104 +75258 2798245 +75038 2814579 +75721 2835759 +76481 2816931 +76700 2801379 +76903 2812675 +77324 2825555 +77687 2812100 +77773 2783265 +78607 2768773 +79196 2762605 +79889 2773877 +80044 2744997 +80060 2730459 +80734 2738443 +81181 2729249 +80980 2710375 +80789 2715853 +81088 2705182 +81161 2677079 +81654 2679036 +82078 2679014 +81869 2660894 +82125 2628942 +82897 2606977 +82841 2621524 +82920 2566963 +83131 2522955 +83279 2524246 +83072 2576056 +83215 2558597 +83213 2558406 +83838 2563076 +83775 2625367 +83874 2702529 +83723 2797526 +84414 2788487 +84580 2758644 +85079 2694560 +85489 2643322 +85902 2522185 +86619 2517706 +86895 2575621 +86939 2646087 +87540 2691873 +88025 2708857 +88638 2724009 +89435 2784434 +90128 2813425 +90747 2814616 +90767 2835699 +91088 2848196 +90907 2809114 +91142 2800637 +91314 2778274 +91742 2747466 +91902 2723314 +92261 2666402 +92885 2646284 +92977 2641493 +92793 2654976 +92787 2620981 +93521 2616306 +93864 2565885 +93756 2536207 +93591 2531104 +94117 2548066 +94526 2540505 +94958 2549388 +94894 2561093 +95080 2581395 +95287 2581721 +95977 2618322 +96017 2598678 +96752 2563067 +96909 2593505 +97496 2638294 +97475 2642418 +97826 2619396 +98335 2645556 +98853 2636099 +99199 2643250 +98972 2631740 +99609 2632997 +100378 2604968 +100977 2600828 +101040 2609519 +101141 2620860 +100922 2599747 +100723 2642644 +100998 2610145 +101485 2607770 +101440 2604627 +101462 2634349 +101412 2615709 +101789 2583973 +102142 2554302 +102774 2547143 +103394 2532826 +104085 2528745 +104285 2478274 +105000 2458343 +105735 2469367 +105660 2480553 +105770 2495937 +106262 2514558 +106795 2519060 +106565 2527402 +107308 2525146 +107784 2577330 +108369 2611076 +108675 2632693 +108708 2663459 +109412 2652978 +109203 2638930 +109320 2635210 +110082 2631667 +110500 2582602 +110282 2571705 +110963 2584214 +110841 2583063 +111048 2545714 +111021 2561447 +111606 2579609 +112153 2550322 +112470 2522850 +113004 2515238 +113559 2543274 +114210 2576123 +114647 2567913 +114805 2566525 +115476 2613195 +115493 2604548 +115353 2602555 +115989 2618634 +115733 2637027 +115970 2642320 +116347 2670170 +116357 2724575 +116975 2696208 +117659 2667663 +117900 2672973 +117670 2657510 +118145 2620669 +118027 2632516 +118479 2642156 +119198 2680859 +119308 2649900 +119208 2640827 +119810 2689570 +120155 2674382 +120770 2670237 +120888 2676414 +121211 2654437 +121220 2655597 +121237 2644841 +121243 2610087 +121255 2624709 +121265 2611522 +121271 2655355 +121270 2650778 +121279 2628229 +121286 2588290 +121289 2592850 +121284 2592095 +121275 2557753 +121278 2539206 +121278 2528353 +121275 2524270 +121282 2506323 +121274 2506995 +121284 2541431 +121271 2551307 +121268 2585073 +121276 2613709 +121265 2644990 +121271 2700795 +121287 2722938 +121285 2725923 +121301 2773611 +121315 2813019 +121322 2864069 +121313 2933360 +121330 2995880 +121336 3053673 +121335 3052235 +121347 3062935 +121363 3078536 +121363 3081264 +121355 3096808 +121344 3140316 +121358 3156509 +121366 3195798 +121374 3255474 +121382 3256820 +121380 3267242 +121397 3278499 +121399 3345106 +121414 3356681 +121434 3380794 +121439 3399177 +121428 3436204 +121435 3493429 +121423 3547632 +121438 3561070 +121436 3583081 +121442 3617264 +121444 3721942 +121443 3734442 +121455 3853143 +121448 3885051 +121452 3984597 +121443 3993581 +121447 4058385 +121442 4154497 +121451 4258088 +121451 4289803 +121445 4343295 +121454 4436511 +121456 4611743 +121453 4702011 +121476 4761624 +121462 4788329 +121460 4809715 +121450 4835434 +121450 4877857 +121447 4962731 +121451 4990124 +121465 5015521 +121459 5042729 +121459 5144674 +121483 5264672 +121497 5470823 +121523 5688444 +121532 5947746 +121552 6054328 +121570 6357993 +121573 6666838 +121584 6742268 +121570 6801138 +121553 7066744 +121582 7256596 +121614 7420236 +121597 7740944 +121597 8062677 +121608 8476694 +121613 8730910 +121596 8809765 +121580 9223206 +121612 9366955 +121601 9513095 +121624 10141719 +121627 10421270 +121659 10721470 +121680 11178496 +121676 11423139 +121675 11606764 +121717 11768024 +121717 12569307 +121717 13446334 +121742 14093972 +121763 15177950 +121812 16448696 +121822 18133347 +121796 19547321 +121843 20297770 +121914 22401723 +121993 23181253 +122045 23616556 +122128 24855688 +122141 26267703 +122154 26814048 +122242 29096657 +122275 29456251 +122318 33919746 +122378 40433966 +122345 41136379 +122464 44334317 +122435 46540694 +122472 49103388 +122521 55379501 +122708 71991965 +122851 72558867 +122880 75421386 +122899 93415891 +123006 109193567 +123239 162303536 +123585 181484548 +123752 230545203 +123888 232921611 +123698 235998864 +124144 241798489 +124245 247564948 +124919 248318184 +124681 253292641 +125207 263815332 +125737 266553990 +126357 246541506 +126830 237512626 +127489 246810899 +127582 261154571 +127651 263646728 +127679 256774461 +128014 241660080 +128427 258844954 +128887 281135478 +129154 293432887 +128895 292571823 +129810 307610443 +130199 289828043 +131059 291537870 +131326 290103115 +131586 273410565 +132036 254510554 +132201 246118686 +132649 232187432 +132408 241180267 +132949 251175658 +133640 262375857 +133653 260129696 +134032 255837548 +133799 251566461 +134353 262940133 +134697 265613449 +135171 255900779 +134922 254513320 +134674 240861604 +135353 243119107 +135165 245056094 +135844 241919008 +136096 245008262 +136577 246972760 +137234 248068232 +137144 248384335 +137663 240878434 +138369 247020939 +138785 250347868 +139197 251645098 +139660 251435399 +140148 251315107 +140409 256607487 +140747 257965199 +140584 258581417 +140742 263718545 +140743 265470098 +141203 261395782 +141470 261852867 +142218 258380708 +142283 253711095 +142505 256922084 +142755 254533900 +143430 248216648 +143559 246531604 +143545 244987119 +144033 242824345 +144277 240456378 +144068 241144028 +143973 242908330 +144522 244878202 +144420 248917701 +144520 252957972 +144465 251012119 +144395 251526342 +144745 246148182 +145210 248998567 +145842 250092035 +145619 250750572 +146271 246513925 +146717 248798604 +146756 251997761 +147105 250737509 +147454 253203523 +147628 256127583 +147952 256890306 +148475 258725409 +148267 261698186 +149017 264427610 +148867 267002727 +149532 270309264 +149474 270802481 +149936 268525490 +149933 267012271 +150191 267627423 +150231 265901707 +150393 264049541 +150147 266543834 +149923 265830376 +149844 265089189 +150063 266474655 +150467 266156482 +150465 265803638 +150492 266580033 +150743 265886182 +151311 267221241 +151110 265597822 +151792 268053664 +151959 267982464 +152556 270216683 +153199 272935986 +153789 275632752 +153800 277188686 +153598 279655274 +154107 282583945 +154942 285274387 +154645 287041556 +155056 288984473 +155817 291629042 +156603 294801348 +157294 295035375 +157025 294146204 +157003 294711271 +156769 292536950 +156898 293719561 +156787 290405394 +156743 286589282 +156533 285487816 +157118 287431674 +157824 290665055 +157709 290067620 +158477 287138509 +159299 287100874 +160122 289369136 +160844 286668563 +161205 281991107 +160938 282420995 +161654 282917556 +161500 285398625 +161815 286541139 +162218 289854492 +162786 291403067 +163039 294053937 +163409 298329828 +163156 303007899 +162939 304093432 +163518 309974555 +163562 304608296 +164222 297583087 +164603 290592360 +164840 287578145 +164839 290613464 +165708 292360913 +165698 289690704 +166019 290560413 +166350 294856824 +167125 294728148 +166841 297439409 +167711 298549501 +167529 300305890 +168349 303986837 +168918 303686964 +168847 305393729 +168649 307955466 +168726 304855612 +168559 308181200 +168259 306241552 +168234 308008177 +168110 302315422 +168612 305569935 +168636 305906187 +168943 303298778 +169653 301149180 +169853 301293931 +170468 298101957 +171114 298614194 +171063 300042721 +170995 301785489 +171225 306682290 +171298 311945986 +172080 311291808 +171773 315726088 +172326 323427260 +172289 328370981 +173001 332611063 +173705 337064873 +174340 342519630 +174656 345843455 +174523 339924055 +174341 339873797 +174548 333200374 +175197 327767930 +175490 332415219 +175633 335241473 +175901 343102999 +176136 343610518 +177060 341197556 +177143 341160366 +178005 347451087 +177907 349245778 +178871 342297735 +178990 339909931 +179103 334471279 +179664 335884481 +179453 337146585 +179874 339131572 +180608 339036879 +181123 334295264 +181378 332514788 +181368 331815034 +181610 332269354 +181353 335658267 +181024 331029523 +181799 333376639 +181533 330803620 +181852 335446096 +182249 327651485 +182786 330853981 +182815 334091425 +183694 332995651 +183716 334941456 +184256 336817329 +184978 332025859 +185709 332429866 +186457 333570075 +186612 336524124 +186578 337557879 +186476 341429329 +187155 341215996 +188068 341116974 +188507 339736778 +189327 341201420 +189402 338445712 +189296 333925182 +189261 334673213 +190126 328066673 +190122 329180589 +190650 324569768 +190780 318579181 +190626 314776776 +190969 311946966 +190890 314166566 +191777 318188885 +192207 320045959 +192265 321161086 +191971 319260853 +192249 319445928 +192402 314917166 +192248 315201117 +192683 318108496 +192519 322772416 +193154 319241285 +193916 320586463 +193723 323274138 +194704 330040238 +195184 329634117 +194926 330080108 +195479 329739241 +195867 322514365 +196687 321976322 +196958 321040287 +197636 321145380 +198200 322276370 +198827 323986641 +199777 326794962 +200567 327018587 +200520 332006083 +200278 335733043 +200046 338549668 +199874 344296960 +200291 337647450 +200243 338518067 +200885 339309954 +201357 338288801 +202241 336350601 +202324 332561061 +202067 335711646 +202071 330866236 +202854 326704296 +203434 322209733 +203411 316221784 +204065 316466802 +204968 315581800 +205767 319955897 +206645 323462328 +206375 325976933 +206808 324289361 +206693 332228958 +206362 329743222 +206454 328646099 +207209 325507293 +207638 330329090 +208223 331728429 +209130 335067410 +209345 331974791 +209858 330643804 +209559 331699357 +210330 326178275 +210934 318999077 +211625 314071095 +211897 309683222 +212655 309930370 +212357 309518492 +212981 308824617 +212827 309611918 +212525 309432426 +212433 306566068 +212347 309064398 +212464 306201238 +212317 304175447 +212979 306911064 +213358 305985547 +213100 304258564 +213302 301599350 +213762 297612234 +214130 296020082 +214450 296234760 +214731 294572059 +214622 294482263 +215333 299276824 +215502 303917135 +215508 308471741 +216410 307678437 +217063 307855228 +217710 306409096 +218169 305830083 +218818 310027240 +219686 311250740 +219811 310516485 +220644 311941543 +221030 310173680 +221355 314460254 +222269 312101320 +222080 316701627 +222437 316159429 +223294 318094589 +223663 317363253 +224509 318081780 +224990 313101859 +224756 309166502 +224703 305336221 +225030 307306494 +225654 305219315 +226438 298967393 +226566 299617506 +226556 293222112 +226628 290248822 +226447 288884926 +226249 284101629 +226513 283997093 +226774 283049805 +226727 279807754 +226542 280858724 +226858 275240062 +227152 279692044 +227553 279292362 +227985 281158595 +227794 285760970 +228507 286322073 +228532 287162103 +228962 285663441 +229467 285996180 +230298 285382009 +231050 288707650 +230873 295006850 +231488 297892791 diff --git a/tests/difficulty/wow-emission b/tests/difficulty/wow-emission new file mode 100755 index 000000000..d033fcdb2 --- /dev/null +++ b/tests/difficulty/wow-emission @@ -0,0 +1,86 @@ +#!/usr/bin/python3 + +# BSD 3-Clause License +# +# Copyright (c) 2018, Jason Rhinelander +# 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 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. + +import requests + +# CONFIGURATION: + +# Target time +DIFFICULTY_TARGET = 300 + +# Range of blocks to analyze: +N = 20 + +# Fork height/name pairs (purely cosmetic to indicate forks in the output) +forks = { + 50: 'v12', +} + +# First element is the URL to fetch; the rest are keys to follow to get the current network height: +api_height = ('http://explorer.wowne.ro:8082/api/networkinfo', 'data', 'height') + +# First element is a format with {} to be replaced with the block height; the rest are keys to +# follow to get that block's timestamp: +api_block = ('http://explorer.wowne.ro:8082/api/block/{}', 'data', 'timestamp') + +# Run: ./wow-emission + +# END CONFIGURATION + +time_period = N*DIFFICULTY_TARGET +h = requests.get(api_height[0]).json() +for i in api_height[1:]: + h = h[i] + +h -= 1 # want height of last block, not current network height + +next_ts = None +next_h = None +while h > 1: + contains_fork = None + for fork_height, fork_name in forks.items(): + if h is not None and h < fork_height <= next_h: + contains_fork = fork_name + break + + ts = requests.get(api_block[0].format(h)).json() + for i in api_block[1:]: + ts = ts[i] + + if next_ts is not None: + elapsed = next_ts - ts + print("{} -- {}: elapsed time = {}s = {:.2f}% of {} target = avg solvetime = {:.2f} minutes over {} blocks".format( + h, next_h, elapsed, 100.*elapsed/time_period, time_period, elapsed/60/N, N), end='') + + print(" --- ({} FORK) ---".format(contains_fork) if contains_fork is not None else "") + + next_ts = ts + next_h = h + h -= N diff --git a/tests/difficulty/wownero-data.txt b/tests/difficulty/wownero-data.txt new file mode 100644 index 000000000..cf799af5e --- /dev/null +++ b/tests/difficulty/wownero-data.txt @@ -0,0 +1,478 @@ +50 1544807250 111 +51 1544807251 111 +52 1544807267 7120 +53 1544807942 15340 +54 1544808953 9094 +55 1544809140 5748 +56 1544809214 6054 +57 1544809224 6678 +58 1544809698 7557 +59 1544809817 7066 +60 1544809969 7524 +61 1544810017 7901 +62 1544810260 8566 +63 1544810847 8717 +64 1544811309 8063 +65 1544811461 7758 +66 1544812116 8017 +67 1544812450 7458 +68 1544812700 7412 +69 1544813448 7477 +70 1544813747 6956 +71 1544814796 6957 +72 1544815060 6287 +73 1544816309 6315 +74 1544816329 5670 +75 1544816563 5840 +76 1544816875 5881 +77 1544816886 5874 +78 1544817057 6046 +79 1544817102 6123 +80 1544817107 6278 +81 1544817697 6462 +82 1544818307 6286 +83 1544818405 6114 +84 1544818452 6222 +85 1544818601 6360 +86 1544818970 6443 +87 1544818977 6406 +88 1544819349 6563 +89 1544819355 6524 +90 1544819520 6680 +91 1544819607 6752 +92 1544819648 6866 +93 1544819784 7007 +94 1544819815 7097 +95 1544820138 7246 +96 1544820865 7234 +97 1544821319 7011 +98 1544821764 6935 +99 1544821949 6867 +100 1544821999 6920 +101 1544822103 7035 +102 1544822445 7127 +103 1544822499 7107 +104 1544822630 7220 +105 1544823113 7299 +106 1544823164 7216 +107 1544823207 7327 +108 1544823462 7443 +109 1544824390 7464 +110 1544824483 7194 +111 1544824693 7279 +112 1544825153 7316 +113 1544825447 7252 +114 1544826047 7254 +115 1544826816 7140 +116 1544826840 6970 +117 1544826893 7068 +118 1544827218 7155 +119 1544827246 7147 +120 1544827258 7243 +121 1544827533 7346 +122 1544827536 7353 +123 1544828070 7353 +124 1544828149 7459 +125 1544828183 7459 +126 1544828541 7374 +127 1544829151 7374 +128 1544829353 7451 +129 1544829375 7451 +130 1544829428 7545 +131 1544830618 7545 +132 1544830629 7522 +133 1544831213 7522 +134 1544831672 7414 +135 1544831917 7414 +136 1544831961 7447 +137 1544832296 7447 +138 1544833065 7542 +139 1544833372 7542 +140 1544833650 7624 +141 1544833856 7624 +142 1544834092 7335 +143 1544834232 7335 +144 1544834476 7428 +145 1544834789 7428 +146 1544834932 7428 +147 1544835167 7428 +148 1544835577 7428 +149 1544835658 7343 +150 1544835719 7296 +151 1544835874 7312 +152 1544836381 7388 +153 1544836763 7378 +154 1544838241 7247 +155 1544838310 7249 +156 1544838531 7258 +157 1544838683 7287 +158 1544838835 7306 +159 1544838915 7349 +160 1544838937 7364 +161 1544839139 7360 +162 1544839632 7402 +163 1544839639 7419 +164 1544839766 7390 +165 1544840188 7449 +166 1544840324 7509 +167 1544840725 7549 +168 1544841073 7493 +169 1544841395 7470 +170 1544841881 7184 +171 1544841925 7238 +172 1544842032 7255 +173 1544842532 7288 +174 1544842689 7325 +175 1544842741 7375 +176 1544842993 7439 +177 1544843611 7461 +178 1544843765 7418 +179 1544843882 7485 +180 1544843949 7530 +181 1544844471 7501 +182 1544846276 7538 +183 1544847445 7518 +184 1544848223 7506 +185 1544848650 7500 +186 1544849293 7456 +187 1544849307 7451 +188 1544849402 7504 +189 1544849696 7602 +190 1544849805 7623 +191 1544850606 7640 +192 1544850744 7592 +193 1544851386 7559 +194 1544851652 7554 +195 1544851664 7562 +196 1544851719 7554 +197 1544852399 7483 +198 1544853145 7216 +199 1544853804 7070 +200 1544853963 6948 +201 1544854234 6987 +202 1544854678 6928 +203 1544854843 6973 +204 1544854969 7100 +205 1544855088 7107 +206 1544855298 7303 +207 1544855399 7204 +208 1544855417 7448 +209 1544855575 7332 +210 1544855609 7340 +211 1544855796 7418 +212 1544855904 7423 +213 1544856100 7328 +214 1544856230 7195 +215 1544856308 7073 +216 1544856344 7161 +217 1544856487 7234 +218 1544856585 7172 +219 1544856616 7155 +220 1544856803 7166 +221 1544857509 7221 +222 1544857649 7188 +223 1544857660 7247 +224 1544857833 7252 +225 1544857938 7259 +226 1544858083 7275 +227 1544858344 7250 +228 1544858447 7259 +229 1544858893 7228 +230 1544858949 7266 +231 1544859066 7398 +232 1544859349 7488 +233 1544859494 7555 +234 1544859567 7577 +235 1544859701 7583 +236 1544859708 7566 +237 1544860014 7488 +238 1544860386 7471 +239 1544860779 7496 +240 1544860825 7563 +241 1544860938 7552 +242 1544861189 7529 +243 1544861265 7526 +244 1544861486 7707 +245 1544861493 7628 +246 1544862214 7662 +247 1544862688 7740 +248 1544863295 7745 +249 1544863944 7853 +250 1544864067 8024 +251 1544864418 8003 +252 1544864496 8018 +253 1544864539 8026 +254 1544864626 7946 +255 1544864834 7858 +256 1544865358 7914 +257 1544865404 7889 +258 1544865649 7958 +259 1544865881 7960 +260 1544865929 7917 +261 1544866026 8004 +262 1544866446 7980 +263 1544866493 7917 +264 1544866753 7783 +265 1544867010 7651 +266 1544867925 7900 +267 1544868777 7825 +268 1544868824 7948 +269 1544868950 8054 +270 1544868952 8098 +271 1544869472 8061 +272 1544869999 8019 +273 1544870138 8202 +274 1544871229 8222 +275 1544871380 8236 +276 1544871780 8280 +277 1544871970 8322 +278 1544872078 8256 +279 1544872085 8311 +280 1544872452 8328 +281 1544872501 8301 +282 1544873065 8133 +283 1544873233 8028 +284 1544873620 8042 +285 1544873974 8033 +286 1544874612 8077 +287 1544875073 8080 +288 1544875609 8051 +289 1544875708 8398 +290 1544876318 8146 +291 1544876424 8172 +292 1544877283 8120 +293 1544877669 8120 +294 1544877696 8121 +295 1544877838 8133 +296 1544877838 8102 +297 1544878052 8219 +298 1544878058 8089 +299 1544878076 8085 +300 1544878965 8098 +301 1544879506 8051 +302 1544879566 7999 +303 1544880167 7978 +304 1544880273 7933 +305 1544881431 8034 +306 1544881686 7910 +307 1544882187 7918 +308 1544882801 7844 +309 1544883397 7799 +310 1544883541 7811 +311 1544883614 7842 +312 1544883818 7989 +313 1544884565 7982 +314 1544884766 8013 +315 1544886005 8030 +316 1544886045 7949 +317 1544886716 8256 +318 1544887183 8546 +319 1544887281 8598 +320 1544887408 8689 +321 1544887955 8555 +322 1544888070 8496 +323 1544889324 8394 +324 1544889572 8315 +325 1544889624 8196 +326 1544889758 8361 +327 1544889858 8380 +328 1544889945 8497 +329 1544890524 8377 +330 1544890752 8333 +331 1544890803 8050 +332 1544891097 8207 +333 1544891104 8235 +334 1544891284 8295 +335 1544891836 8325 +336 1544892064 8377 +337 1544892318 8366 +338 1544892754 8392 +339 1544892932 8128 +340 1544893094 8108 +341 1544893454 8153 +342 1544893586 8155 +343 1544893598 8144 +344 1544893663 8171 +345 1544894071 8052 +346 1544895132 8051 +347 1544895173 8069 +348 1544896179 8054 +349 1544896208 8092 +350 1544896727 8078 +351 1544897097 7971 +352 1544897286 7961 +353 1544897682 7937 +354 1544897896 7858 +355 1544898916 7868 +356 1544898949 7996 +357 1544899556 7955 +358 1544899775 7935 +359 1544899776 7979 +360 1544899865 7995 +361 1544899872 7943 +362 1544899941 7777 +363 1544899968 7797 +364 1544900311 7686 +365 1544900552 7699 +366 1544901232 7622 +367 1544901496 7608 +368 1544901957 7603 +369 1544903562 7541 +370 1544903755 7527 +371 1544904768 7335 +372 1544905506 7390 +373 1544906057 7350 +374 1544906297 7386 +375 1544906315 7398 +376 1544906337 7406 +377 1544906502 7456 +378 1544906547 7459 +379 1544906847 7497 +380 1544906977 7433 +381 1544907319 7526 +382 1544907476 7485 +383 1544907712 7551 +384 1544908725 7587 +385 1544909776 7298 +386 1544910660 7324 +387 1544911294 7149 +388 1544911645 7023 +389 1544911681 6940 +390 1544911980 6931 +391 1544912010 7013 +392 1544912240 7014 +393 1544912442 7024 +394 1544913183 7052 +395 1544913211 7006 +396 1544913287 6996 +397 1544913368 7006 +398 1544913989 6984 +399 1544914107 6986 +400 1544914282 6859 +401 1544914450 6833 +402 1544915089 6824 +403 1544915455 6724 +404 1544915634 6681 +405 1544916158 6668 +406 1544916276 6694 +407 1544917136 6766 +408 1544917168 6743 +409 1544918023 6880 +410 1544918389 6774 +411 1544918585 6826 +412 1544918714 6835 +413 1544919342 6831 +414 1544919630 6721 +415 1544919924 6752 +416 1544920303 6721 +417 1544920319 6776 +418 1544920340 6691 +419 1544920453 6685 +420 1544920898 6703 +421 1544920982 6711 +422 1544921194 6757 +423 1544921328 6695 +424 1544921394 6694 +425 1544921691 6646 +426 1544922123 6594 +427 1544922578 6691 +428 1544922740 6723 +429 1544923162 6617 +430 1544923243 6584 +431 1544923473 6528 +432 1544923667 6492 +433 1544923733 6481 +434 1544924157 6471 +435 1544924409 6582 +436 1544924576 6587 +437 1544924759 6574 +438 1544924994 6627 +439 1544925510 6614 +440 1544925648 6784 +441 1544926443 6768 +442 1544927071 6770 +443 1544927398 6788 +444 1544927604 6855 +445 1544927649 6797 +446 1544927686 6787 +447 1544927735 6771 +448 1544927802 6855 +449 1544928132 6867 +450 1544928608 7002 +451 1544928617 6952 +452 1544928941 7032 +453 1544929052 7069 +454 1544929625 7028 +455 1544929726 6940 +456 1544930394 7001 +457 1544930716 6863 +458 1544931293 6964 +459 1544931301 6938 +460 1544931430 6899 +461 1544931614 6903 +462 1544931777 6902 +463 1544932345 6895 +464 1544932452 6977 +465 1544932485 6946 +466 1544932507 6860 +467 1544932867 6902 +468 1544933220 6835 +469 1544933524 6837 +470 1544933953 6823 +471 1544934005 6835 +472 1544934020 6750 +473 1544934376 6758 +474 1544934584 6679 +475 1544934715 6696 +476 1544934868 6727 +477 1544935152 6708 +478 1544935226 6672 +479 1544935396 6575 +480 1544935475 6618 +481 1544935482 6792 +482 1544935519 6785 +483 1544935559 6895 +484 1544935587 6825 +485 1544935624 6855 +486 1544935731 6834 +487 1544936206 6851 +488 1544936888 6913 +489 1544937586 6877 +490 1544937937 7023 +491 1544937990 6993 +492 1544938149 7073 +493 1544938379 7049 +494 1544939193 7024 +495 1544939423 6996 +496 1544939930 6970 +497 1544940085 6974 +498 1544940211 6964 +499 1544940274 7017 +500 1544940355 7052 +501 1544940405 7177 +502 1544940551 7203 +503 1544940662 7194 +504 1544941378 7387 +505 1544941980 7271 +506 1544942561 7413 +507 1544943576 7566 +508 1544943849 7656 +509 1544943988 7655 +510 1544944013 7466 +511 1544944287 7416 +512 1544944317 7336 +513 1544944801 7307 +514 1544945739 7340 +515 1544946982 7352 +516 1544947008 7405 +517 1544947289 7427 +518 1544947560 7444 +519 1544948145 7650 +520 1544949215 7732 +521 1544949531 7801 +522 1544949707 7817 +523 1544950260 7660 +524 1544950430 7610 +525 1544950640 7655 +526 1544950742 7660 +527 1544950774 7653 diff --git a/tests/difficulty/wownero-emission.txt b/tests/difficulty/wownero-emission.txt new file mode 100644 index 000000000..a5dcfcc08 --- /dev/null +++ b/tests/difficulty/wownero-emission.txt @@ -0,0 +1,26 @@ +511 -- 531: elapsed time = 7762s = 129.37% of 6000 target = avg solvetime = 6.47 minutes over 20 blocks +491 -- 511: elapsed time = 6297s = 104.95% of 6000 target = avg solvetime = 5.25 minutes over 20 blocks +471 -- 491: elapsed time = 3985s = 66.42% of 6000 target = avg solvetime = 3.32 minutes over 20 blocks +451 -- 471: elapsed time = 5388s = 89.80% of 6000 target = avg solvetime = 4.49 minutes over 20 blocks +431 -- 451: elapsed time = 5144s = 85.73% of 6000 target = avg solvetime = 4.29 minutes over 20 blocks +411 -- 431: elapsed time = 4888s = 81.47% of 6000 target = avg solvetime = 4.07 minutes over 20 blocks +391 -- 411: elapsed time = 6575s = 109.58% of 6000 target = avg solvetime = 5.48 minutes over 20 blocks +371 -- 391: elapsed time = 7242s = 120.70% of 6000 target = avg solvetime = 6.04 minutes over 20 blocks +351 -- 371: elapsed time = 7671s = 127.85% of 6000 target = avg solvetime = 6.39 minutes over 20 blocks +331 -- 351: elapsed time = 6294s = 104.90% of 6000 target = avg solvetime = 5.25 minutes over 20 blocks +311 -- 331: elapsed time = 7189s = 119.82% of 6000 target = avg solvetime = 5.99 minutes over 20 blocks +291 -- 311: elapsed time = 7190s = 119.83% of 6000 target = avg solvetime = 5.99 minutes over 20 blocks +271 -- 291: elapsed time = 6952s = 115.87% of 6000 target = avg solvetime = 5.79 minutes over 20 blocks +251 -- 271: elapsed time = 5054s = 84.23% of 6000 target = avg solvetime = 4.21 minutes over 20 blocks +231 -- 251: elapsed time = 5352s = 89.20% of 6000 target = avg solvetime = 4.46 minutes over 20 blocks +211 -- 231: elapsed time = 3270s = 54.50% of 6000 target = avg solvetime = 2.73 minutes over 20 blocks +191 -- 211: elapsed time = 5190s = 86.50% of 6000 target = avg solvetime = 4.33 minutes over 20 blocks +171 -- 191: elapsed time = 8681s = 144.68% of 6000 target = avg solvetime = 7.23 minutes over 20 blocks +151 -- 171: elapsed time = 6051s = 100.85% of 6000 target = avg solvetime = 5.04 minutes over 20 blocks +131 -- 151: elapsed time = 5256s = 87.60% of 6000 target = avg solvetime = 4.38 minutes over 20 blocks +111 -- 131: elapsed time = 5925s = 98.75% of 6000 target = avg solvetime = 4.94 minutes over 20 blocks +91 -- 111: elapsed time = 5086s = 84.77% of 6000 target = avg solvetime = 4.24 minutes over 20 blocks +71 -- 91: elapsed time = 4811s = 80.18% of 6000 target = avg solvetime = 4.01 minutes over 20 blocks +51 -- 71: elapsed time = 7545s = 125.75% of 6000 target = avg solvetime = 6.29 minutes over 20 blocks +31 -- 51: elapsed time = 121s = 2.02% of 6000 target = avg solvetime = 0.10 minutes over 20 blocks --- (v12 FORK) --- +11 -- 31: elapsed time = 78s = 1.30% of 6000 target = avg solvetime = 0.07 minutes over 20 blocks From 3cfc60d416454c2132cfcab4350fd43017e52f82 Mon Sep 17 00:00:00 2001 From: wowario Date: Tue, 18 Dec 2018 01:46:48 +0300 Subject: [PATCH 02/20] add cumulative difficulty to diff command --- src/daemon/rpc_command_executor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index d9b9de6b7..f9bf511db 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -324,6 +324,7 @@ bool t_rpc_command_executor::show_difficulty() { tools::success_msg_writer() << "BH: " << res.height << ", TH: " << res.top_block_hash << ", DIFF: " << res.difficulty + << ", CUM_DIFF: " << res.cumulative_difficulty << ", HR: " << res.difficulty / res.target << " H/s"; return true; From 2b1829f64934537d4f9c2d6d541859168e9aa5df Mon Sep 17 00:00:00 2001 From: wowario <38101080+wowario@users.noreply.github.com> Date: Fri, 29 Jun 2018 12:36:11 +0300 Subject: [PATCH 03/20] Captain obvious warning --- src/cryptonote_core/cryptonote_core.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 1bfefb42d..be14ba54e 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1524,6 +1524,9 @@ namespace cryptonote MGINFO_YELLOW(ENDL << "**********************************************************************" << ENDL << main_message << ENDL << ENDL + << "Caution: Wownero is highly experimental software compiled by a ragtag team of stoners with as much" << ENDL + << "skill as Verge developers. Storing your life savings in WOW is probably not a good idea." << ENDL + << ENDL << "You can set the level of process detailization through \"set_log \" command," << ENDL << "where is between 0 (no details) and 4 (very verbose), or custom category based levels (eg, *:WARNING)." << ENDL << ENDL From 2fbe9f773087dfc96c74d50c725685372aae2d21 Mon Sep 17 00:00:00 2001 From: wowario Date: Thu, 7 Feb 2019 17:02:29 +0300 Subject: [PATCH 04/20] add lwma-1 --- src/cryptonote_basic/difficulty.cpp | 65 ++++++++++++++--------------- src/cryptonote_basic/difficulty.h | 2 +- src/cryptonote_config.h | 5 +-- src/cryptonote_core/blockchain.cpp | 29 +++++++++---- 4 files changed, 53 insertions(+), 48 deletions(-) diff --git a/src/cryptonote_basic/difficulty.cpp b/src/cryptonote_basic/difficulty.cpp index 43147a07f..eea827911 100644 --- a/src/cryptonote_basic/difficulty.cpp +++ b/src/cryptonote_basic/difficulty.cpp @@ -269,43 +269,40 @@ namespace cryptonote { return static_cast(next_D); } - difficulty_type next_difficulty_v5(std::vector timestamps, std::vector cumulative_difficulties, size_t target_seconds) { - - if(timestamps.size() > DIFFICULTY_WINDOW_V3) - { - timestamps.resize(DIFFICULTY_WINDOW_V3); - cumulative_difficulties.resize(DIFFICULTY_WINDOW_V3); - } + // LWMA-1 difficulty algorithm + // Copyright (c) 2017-2019 Zawy, MIT License + // https://github.com/zawy12/difficulty-algorithms/issues/3 + difficulty_type next_difficulty_v5(std::vector timestamps, std::vector cumulative_difficulties, uint64_t T, uint64_t N, uint64_t HEIGHT, uint64_t FORK_HEIGHT, uint64_t difficulty_guess) { + assert(timestamps.size() == cumulative_difficulties.size() && timestamps.size() <= N+1 ); - size_t length = timestamps.size(); - assert(length == cumulative_difficulties.size()); - if (length < DIFFICULTY_FORK_HEIGHT + 72) { - return DIFFICULTY_RESET; + if (HEIGHT >= FORK_HEIGHT && HEIGHT < FORK_HEIGHT + N) { return difficulty_guess; } + assert(timestamps.size() == N+1); + + uint64_t L(0), next_D, i, this_timestamp(0), previous_timestamp(0), avg_D; + + previous_timestamp = timestamps[0]-T; + for ( i = 1; i <= N; i++) { + // Safely prevent out-of-sequence timestamps + if ( timestamps[i] > previous_timestamp ) { this_timestamp = timestamps[i]; } + else { this_timestamp = previous_timestamp+1; } + L += i*std::min(6*T ,this_timestamp - previous_timestamp); + previous_timestamp = this_timestamp; } - static_assert(DIFFICULTY_WINDOW_V3 >= 2, "Window is too small"); - assert(length <= DIFFICULTY_WINDOW_V3); - sort(timestamps.begin(), timestamps.end()); - size_t cut_begin, cut_end; - static_assert(2 * DIFFICULTY_CUT_V2 <= DIFFICULTY_WINDOW_V3 - 2, "Cut length is too large"); - if (length <= DIFFICULTY_WINDOW_V3 - 2 * DIFFICULTY_CUT_V2) { - cut_begin = 0; - cut_end = length; - } else { - cut_begin = (length - (DIFFICULTY_WINDOW_V3 - 2 * DIFFICULTY_CUT_V2) + 1) / 2; - cut_end = cut_begin + (DIFFICULTY_WINDOW_V3 - 2 * DIFFICULTY_CUT_V2); + if (L < N*N*T/20 ) { L = N*N*T/20; } + avg_D = ( cumulative_difficulties[N] - cumulative_difficulties[0] )/ N; + + // Prevent round off error for small D and overflow for large D. + if (avg_D > 2000000*N*N*T) { + next_D = (avg_D/(200*L))*(N*(N+1)*T*99); } - assert(/*cut_begin >= 0 &&*/ cut_begin + 2 <= cut_end && cut_end <= length); - uint64_t time_span = timestamps[cut_end - 1] - timestamps[cut_begin]; - if (time_span == 0) { - time_span = 1; + else { next_D = (avg_D*N*(N+1)*T*99)/(200*L); } + + // Make all insignificant digits zero for easy reading. + i = 1000000000; + while (i > 1) { + if ( next_D > i*100 ) { next_D = ((next_D+i/2)/i)*i; break; } + else { i /= 10; } } - difficulty_type total_work = cumulative_difficulties[cut_end - 1] - cumulative_difficulties[cut_begin]; - assert(total_work > 0); - uint64_t low, high; - mul(total_work, target_seconds, low, high); - if (high != 0 || low + time_span - 1 < low) { - return 0; - } - return (low + time_span - 1) / time_span; + return next_D; } } diff --git a/src/cryptonote_basic/difficulty.h b/src/cryptonote_basic/difficulty.h index 65c5956b1..24af48960 100644 --- a/src/cryptonote_basic/difficulty.h +++ b/src/cryptonote_basic/difficulty.h @@ -56,5 +56,5 @@ namespace cryptonote difficulty_type next_difficulty_v2(std::vector timestamps, std::vector cumulative_difficulties, size_t target_seconds); difficulty_type next_difficulty_v3(std::vector timestamps, std::vector cumulative_difficulties); difficulty_type next_difficulty_v4(std::vector timestamps, std::vector cumulative_difficulties, size_t height); - difficulty_type next_difficulty_v5(std::vector timestamps, std::vector cumulative_difficulties, size_t target_seconds); + difficulty_type next_difficulty_v5(std::vector timestamps, std::vector cumulative_difficulties, uint64_t T, uint64_t N, uint64_t HEIGHT, uint64_t FORK_HEIGHT, uint64_t difficulty_guess); } diff --git a/src/cryptonote_config.h b/src/cryptonote_config.h index b1ca88466..e7de6969b 100644 --- a/src/cryptonote_config.h +++ b/src/cryptonote_config.h @@ -48,7 +48,6 @@ #define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT 60*60*2 #define CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE 4 -#define BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V3 12 #define BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V2 11 #define BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW 60 @@ -84,11 +83,9 @@ #define DIFFICULTY_WINDOW_V3 144 #define DIFFICULTY_WINDOW_V2 60 #define DIFFICULTY_WINDOW 720 // blocks -#define DIFFICULTY_LAG_V2 3 #define DIFFICULTY_LAG 15 // !!! -#define DIFFICULTY_CUT_V2 12 #define DIFFICULTY_CUT 60 // timestamps to cut after sorting -#define DIFFICULTY_BLOCKS_COUNT_V3 DIFFICULTY_WINDOW_V3 + DIFFICULTY_LAG_V2 +#define DIFFICULTY_BLOCKS_COUNT_V3 DIFFICULTY_WINDOW_V3 + 1 // added +1 to make N=N #define DIFFICULTY_BLOCKS_COUNT_V2 DIFFICULTY_WINDOW_V2 + 1 // added +1 to make N=N #define DIFFICULTY_BLOCKS_COUNT DIFFICULTY_WINDOW + DIFFICULTY_LAG #define DIFFICULTY_FORK_HEIGHT 81769 // ~14 February 2019 diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 3f6b7ce9b..0b18e0810 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -857,18 +857,24 @@ difficulty_type Blockchain::get_difficulty_for_next_block() m_difficulties = difficulties; } size_t target = get_difficulty_target(); + uint64_t T = DIFFICULTY_TARGET_V2; + uint64_t N = DIFFICULTY_WINDOW_V3; + uint64_t HEIGHT = m_db->height(); + uint64_t FORK_HEIGHT = DIFFICULTY_FORK_HEIGHT; + uint64_t difficulty_guess = DIFFICULTY_RESET; + difficulty_type diff = next_difficulty(timestamps, difficulties, target); if (version >= 11) { - difficulty_type diff = next_difficulty_v5(timestamps, difficulties, target); + diff = next_difficulty_v5(timestamps, difficulties, T, N, HEIGHT, FORK_HEIGHT, difficulty_guess); } else if (version == 10) { - difficulty_type diff = next_difficulty_v4(timestamps, difficulties, height); + diff = next_difficulty_v4(timestamps, difficulties, height); } else if (version == 9) { - difficulty_type diff = next_difficulty_v3(timestamps, difficulties); + diff = next_difficulty_v3(timestamps, difficulties); } else if (version == 8) { - difficulty_type diff = next_difficulty_v2(timestamps, difficulties, target); + diff = next_difficulty_v2(timestamps, difficulties, target); } else { - difficulty_type diff = next_difficulty(timestamps, difficulties, target); + diff = next_difficulty(timestamps, difficulties, target); } CRITICAL_REGION_LOCAL1(m_difficulty_lock); @@ -1108,10 +1114,15 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std: // FIXME: This will fail if fork activation heights are subject to voting size_t target = get_ideal_hard_fork_version(bei.height) < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2; + uint64_t T = DIFFICULTY_TARGET_V2; + uint64_t N = DIFFICULTY_WINDOW_V3; + uint64_t HEIGHT = m_db->height(); + uint64_t FORK_HEIGHT = DIFFICULTY_FORK_HEIGHT; + uint64_t difficulty_guess = DIFFICULTY_RESET; // calculate the difficulty target for the block and return it if (version >= 11) { - return next_difficulty_v5(timestamps, cumulative_difficulties, target); + return next_difficulty_v5(timestamps, cumulative_difficulties, T, N, HEIGHT, FORK_HEIGHT, difficulty_guess); } else if (version == 10) { return next_difficulty_v4(timestamps, cumulative_difficulties, height); } else if (version == 9) { @@ -1432,7 +1443,7 @@ bool Blockchain::complete_timestamps_vector(uint64_t start_top_height, std::vect { LOG_PRINT_L3("Blockchain::" << __func__); uint8_t version = get_current_hard_fork_version(); - size_t blockchain_timestamp_check_window = version >= 11 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V3 : version == 11 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V2 : BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW; + size_t blockchain_timestamp_check_window = version >= 10 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V2 : BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW; if(timestamps.size() >= blockchain_timestamp_check_window) return true; @@ -3179,7 +3190,7 @@ bool Blockchain::check_block_timestamp(std::vector& timestamps, const LOG_PRINT_L3("Blockchain::" << __func__); median_ts = epee::misc_utils::median(timestamps); uint8_t version = get_current_hard_fork_version(); - size_t blockchain_timestamp_check_window = version >= 11 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V3 : version == 11 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V2 : BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW; + size_t blockchain_timestamp_check_window = version >= 10 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V2 : BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW; if(b.timestamp < median_ts) { MERROR_VER("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", less than median of last " << blockchain_timestamp_check_window << " blocks, " << median_ts); @@ -3201,7 +3212,7 @@ bool Blockchain::check_block_timestamp(const block& b, uint64_t& median_ts) cons LOG_PRINT_L3("Blockchain::" << __func__); uint8_t version = get_current_hard_fork_version(); uint64_t cryptonote_block_future_time_limit = version >= 8 ? CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT_V2 : CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT; - size_t blockchain_timestamp_check_window = version >= 11 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V3 : version == 11 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V2 : BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW; + size_t blockchain_timestamp_check_window = version >= 10 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V2 : BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW; if(b.timestamp > get_adjusted_time() + cryptonote_block_future_time_limit) { From f84028f88cf565fca8e3e6839f86ec0f224edbb6 Mon Sep 17 00:00:00 2001 From: wowario Date: Sun, 20 May 2018 21:56:40 +0300 Subject: [PATCH 05/20] make simplewallet simple --- src/cryptonote_core/cryptonote_core.cpp | 5 ++- .../cryptonote_protocol_handler.inl | 4 +- src/simplewallet/simplewallet.cpp | 39 ++++++++++++++++--- src/simplewallet/simplewallet.h | 1 + 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index be14ba54e..36aa7bf4e 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1530,8 +1530,9 @@ namespace cryptonote << "You can set the level of process detailization through \"set_log \" command," << ENDL << "where is between 0 (no details) and 4 (very verbose), or custom category based levels (eg, *:WARNING)." << ENDL << ENDL - << "Use the \"help\" command to see the list of available commands." << ENDL - << "Use \"help \" to see a command's documentation." << ENDL + << "Use the \"help\" command to see a simplified list of available commands." << ENDL + << "Use the \"help_advanced\" command to see an advanced list of available commands." << ENDL + << "Use \"help_advanced \" to see a command's documentation." << ENDL << "**********************************************************************" << ENDL); m_starter_message_showed = true; } diff --git a/src/cryptonote_protocol/cryptonote_protocol_handler.inl b/src/cryptonote_protocol/cryptonote_protocol_handler.inl index 1f4c5f466..754782942 100644 --- a/src/cryptonote_protocol/cryptonote_protocol_handler.inl +++ b/src/cryptonote_protocol/cryptonote_protocol_handler.inl @@ -1594,7 +1594,9 @@ skip: MGINFO_YELLOW(ENDL << "**********************************************************************" << ENDL << "You are now synchronized with the network. You may now start wownero-wallet-cli." << ENDL << ENDL - << "Use the \"help\" command to see the list of available commands." << ENDL + << "Use the \"help\" command to see a simplified list of available commands." << ENDL + << "Use the \"help_advanced\" command to see an advanced list of available commands." << ENDL + << "Use \"help_advanced \" to see a command's documentation." << ENDL << "**********************************************************************"); m_core.on_synchronized(); } diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index f6467152a..bee35518c 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2267,6 +2267,27 @@ bool simple_wallet::set_ignore_fractional_outputs(const std::vector } bool simple_wallet::help(const std::vector &args/* = std::vector()*/) +{ + success_msg_writer() << + "Commands:\n" << + tr(" \"balance\" - Show balance.\n") << + tr(" \"address new\" - Create new subaddress.\n") << + tr(" \"address all\" - Show all addresses.\n") << + tr(" \"transfer [address] [amount]\" - Send WOW to an address.\n") << + tr(" \"show_transfers [in|out|pending|failed|pool]\" - Show transactions.\n") << + tr(" \"sweep_all [address]\" - Send whole balance to another wallet.\n") << + tr(" \"seed\" - Show secret 25 words that can be used to recover this wallet.\n") << + tr(" \"refresh\" - Synchronize wallet with the Wownero network.\n") << + tr(" \"status\" - Check current status of wallet.\n") << + tr(" \"version\" - Check software version.\n") << + tr(" \"help\" - Show simplified help section.\n") << + tr(" \"help_advanced\" - Show advanced help section.\n") << + tr(" \"save\" - Save wallet.\n") << + " \"exit\" - Exit wallet.\n\n"; + return true; +} + +bool simple_wallet::help_advanced(const std::vector &args/* = std::vector()*/) { if(args.empty()) { @@ -2630,8 +2651,12 @@ simple_wallet::simple_wallet() tr("Returns version information")); m_cmd_binder.set_handler("help", boost::bind(&simple_wallet::help, this, _1), - tr("help []"), - tr("Show the help section or the documentation about a .")); + tr("help"), + tr("Show simplified help section.")); + m_cmd_binder.set_handler("help_advanced", + boost::bind(&simple_wallet::help_advanced, this, _1), + tr("help_advanced []"), + tr("Show the advanced help section or the documentation about a .")); } //---------------------------------------------------------------------------------------------------- bool simple_wallet::set_variable(const std::vector &args) @@ -3665,8 +3690,9 @@ boost::optional simple_wallet::new_wallet(const boost::pr "**********************************************************************\n" << tr("Your wallet has been generated!\n" "To start synchronizing with the daemon, use the \"refresh\" command.\n" - "Use the \"help\" command to see the list of available commands.\n" - "Use \"help \" to see a command's documentation.\n" + "Use the \"help\" command to see a simplified list of available commands.\n" + "Use the \"help_advanced\" command to see an advanced list of available commands.\n" + "Use \"help_advanced \" to see a command's documentation.\n" "Always use the \"exit\" command when closing wownero-wallet-cli to save \n" "your current session's state. Otherwise, you might need to synchronize \n" "your wallet again (your wallet keys are NOT at risk in any case).\n") @@ -3914,8 +3940,9 @@ bool simple_wallet::open_wallet(const boost::program_options::variables_map& vm) } success_msg_writer() << "**********************************************************************\n" << - tr("Use the \"help\" command to see the list of available commands.\n") << - tr("Use \"help \" to see a command's documentation.\n") << + tr("Use the \"help\" command to see a simplified list of available commands.\n") << + tr("Use the \"help_advanced\" command to see an advanced list of available commands.\n") << + tr("Use \"help_advanced \" to see a command's documentation.\n") << "**********************************************************************"; return true; } diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 3ad16cde9..281699753 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -140,6 +140,7 @@ namespace cryptonote bool set_segregation_height(const std::vector &args = std::vector()); bool set_ignore_fractional_outputs(const std::vector &args = std::vector()); bool help(const std::vector &args = std::vector()); + bool help_advanced(const std::vector &args = std::vector()); bool start_mining(const std::vector &args); bool stop_mining(const std::vector &args); bool set_daemon(const std::vector &args); From d6c12cd641256572c25ed62bc99955bc98018c3c Mon Sep 17 00:00:00 2001 From: wowario Date: Wed, 26 Dec 2018 23:35:28 +0300 Subject: [PATCH 06/20] remove warning about reusing keys --- src/simplewallet/simplewallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index bee35518c..82d6fb960 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -8164,7 +8164,7 @@ int main(int argc, char* argv[]) std::tie(vm, should_terminate) = wallet_args::main( argc, argv, "wownero-wallet-cli [--wallet-file=|--generate-new-wallet=] []", - sw::tr("This is the command line wownero wallet. It needs to connect to a wownero\ndaemon to work correctly.\nWARNING: Do not reuse your Wownero keys on another fork, UNLESS this fork has key reuse mitigations built in. Doing so will harm your privacy."), + sw::tr("This is the command line wownero wallet. It needs to connect to a wownero\ndaemon to work correctly."), desc_params, positional_options, [](const std::string &s, bool emphasis){ tools::scoped_message_writer(emphasis ? epee::console_color_white : epee::console_color_default, true) << s; }, From bbf8d72e185127f21a492a16dacccc22126a5ad2 Mon Sep 17 00:00:00 2001 From: wowario Date: Thu, 27 Dec 2018 15:30:45 +0300 Subject: [PATCH 07/20] move utility binaries to cmake debug --- Makefile | 2 +- src/CMakeLists.txt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index dae0f44e7..8545784bf 100644 --- a/Makefile +++ b/Makefile @@ -61,7 +61,7 @@ debug-test: debug-all: mkdir -p $(builddir)/debug - cd $(builddir)/debug && cmake -D BUILD_TESTS=ON -D BUILD_SHARED_LIBS=OFF -D CMAKE_BUILD_TYPE=Debug $(topdir) && $(MAKE) + cd $(builddir)/debug && cmake -D BUILD_TESTS=OFF -D BUILD_SHARED_LIBS=OFF -D CMAKE_BUILD_TYPE=Debug $(topdir) && $(MAKE) debug-static-all: mkdir -p $(builddir)/debug diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3b71c38cd..b5ef7ef73 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -130,14 +130,14 @@ endif() add_subdirectory(cryptonote_protocol) if(NOT IOS) add_subdirectory(simplewallet) - add_subdirectory(gen_multisig) add_subdirectory(daemonizer) add_subdirectory(daemon) - add_subdirectory(blockchain_utilities) endif() if(CMAKE_BUILD_TYPE STREQUAL Debug) add_subdirectory(debug_utilities) + add_subdirectory(blockchain_utilities) + add_subdirectory(gen_multisig) endif() if(PER_BLOCK_CHECKPOINT) From 2e06575559dae63e1f0b39a6c1918f635b4ee4d5 Mon Sep 17 00:00:00 2001 From: wowario Date: Fri, 28 Dec 2018 01:00:00 +0300 Subject: [PATCH 08/20] update snap and add pkgbuild --- PKGBUILD | 39 ++++++++++++++++++ contrib/snap/monerod-wrapper | 8 ---- contrib/snap/setup/gui/icon.png | Bin 1531 -> 18274 bytes contrib/snap/snapcraft.yaml | 26 ++++++------ contrib/snap/wownerod-wrapper | 8 ++++ contrib/snap/{monerod.conf => wownerod.conf} | 4 +- utils/conf/{monerod.conf => wownerod.conf} | 6 +-- .../{monerod.service => wownerod.service} | 18 ++++---- 8 files changed, 73 insertions(+), 36 deletions(-) create mode 100644 PKGBUILD delete mode 100755 contrib/snap/monerod-wrapper create mode 100755 contrib/snap/wownerod-wrapper rename contrib/snap/{monerod.conf => wownerod.conf} (79%) rename utils/conf/{monerod.conf => wownerod.conf} (69%) rename utils/systemd/{monerod.service => wownerod.service} (66%) diff --git a/PKGBUILD b/PKGBUILD new file mode 100644 index 000000000..875ce5da6 --- /dev/null +++ b/PKGBUILD @@ -0,0 +1,39 @@ +# Maintainer: wowario +# Contributor: wowario + +pkgbase=('wownero-git') +pkgname=('wownero-git') +pkgver=0.5.0.2 +pkgrel=1 +pkgdesc="a fairly launched privacy-centric meme coin with no premine and a finite supply" +license=('custom:Cryptonote') +arch=('x86_64') +url="http://wownero.org/" +depends=('boost-libs' 'zeromq' 'unbound' 'libusb') +makedepends=('git' 'cmake' 'boost') +provides=('wownero-git') + +source=("${pkgname}"::"git+https://github.com/wownero/wownero") + +sha256sums=('SKIP') + +pkgver() { + cd "${srcdir}/${pkgname}" + printf "$(echo ${pkgver} | sed 's/\.r.*//').r%s.g%s" \ + "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)" +} + +build() { + cd "${srcdir}/${pkgname}" + git fetch --tags && git checkout tags/v0.5.0.2 -b v0.5.0.2 + USE_SINGLE_BUILDDIR=1 make +} + +package_wownero-git() { + install -Dm644 "${srcdir}/${pkgname}/LICENSE" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" + install -Dm644 "${srcdir}/${pkgname}/utils/conf/wownerod.conf" "${pkgdir}/etc/wownerod.conf" + install -Dm644 "${srcdir}/${pkgname}/utils/systemd/wownerod.service" "${pkgdir}/usr/lib/systemd/system/wownerod.service" + install -Dm755 "${srcdir}/${pkgname}/build/release/bin/wownerod" "${pkgdir}/usr/bin/wownerod" + install -Dm755 "${srcdir}/${pkgname}/build/release/bin/wownero-wallet-cli" "${pkgdir}/usr/bin/wownero-wallet-cli" + install -Dm755 "${srcdir}/${pkgname}/build/release/bin/wownero-wallet-rpc" "${pkgdir}/usr/bin/wownero-wallet-rpc" +} diff --git a/contrib/snap/monerod-wrapper b/contrib/snap/monerod-wrapper deleted file mode 100755 index f7266e11c..000000000 --- a/contrib/snap/monerod-wrapper +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -if [ ! -d "$SNAP_USER_DATA/etc" ]; then - mkdir $SNAP_USER_DATA/etc/ - cp -R $SNAP/etc/monerod.conf $SNAP_USER_DATA/etc/monerod.conf -fi - -exec "$SNAP/bin/monerod" "$@" diff --git a/contrib/snap/setup/gui/icon.png b/contrib/snap/setup/gui/icon.png index b7e8212704942b8be4e919ad51ff3375fe31c428..2107136abeff9cb4163792b1fe22e97a309d6d9d 100644 GIT binary patch literal 18274 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 Date: Mon, 21 Jan 2019 22:55:08 +0800 Subject: [PATCH 09/20] allow system libminiupnp, revert #6b8539 --- CMakeLists.txt | 8 +++++++ external/CMakeLists.txt | 48 +++++++++++++++++++++++++++++------------ src/p2p/net_node.inl | 13 ++++++++--- 3 files changed, 52 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 38a63fe5f..95a42ed7f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -489,6 +489,14 @@ ExternalProject_Add(generate_translations_header include_directories("${CMAKE_CURRENT_BINARY_DIR}/translations") add_subdirectory(external) +# Final setup for miniupnpc +if(UPNP_STATIC OR IOS) + add_definitions("-DUPNP_STATIC") +else() + add_definitions("-DUPNP_DYNAMIC") + include_directories(${UPNP_INCLUDE}) +endif() + # Final setup for libunbound include_directories(${UNBOUND_INCLUDE}) link_directories(${UNBOUND_LIBRARY_DIRS}) diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 7553f87ea..b4f712ee6 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -34,22 +34,42 @@ # We always compile if we are building statically to reduce static dependency issues... # ...except for FreeBSD, because FreeBSD is a special case that doesn't play well with # others. - -find_package(Miniupnpc REQUIRED) - -message(STATUS "Using in-tree miniupnpc") -add_subdirectory(miniupnp/miniupnpc) -set_property(TARGET libminiupnpc-static PROPERTY FOLDER "external") -if(MSVC) - set_property(TARGET libminiupnpc-static APPEND_STRING PROPERTY COMPILE_FLAGS " -wd4244 -wd4267") -elseif(NOT MSVC) - set_property(TARGET libminiupnpc-static APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-undef -Wno-unused-result -Wno-unused-value") -endif() -if(CMAKE_SYSTEM_NAME MATCHES "NetBSD") - set_property(TARGET libminiupnpc-static APPEND_STRING PROPERTY COMPILE_FLAGS " -D_NETBSD_SOURCE") +if(NOT IOS) + find_package(Miniupnpc QUIET) endif() -set(UPNP_LIBRARIES "libminiupnpc-static" PARENT_SCOPE) +# If we have the correct shared version and we're not building static, use it +if(STATIC OR IOS) + set(USE_SHARED_MINIUPNPC false) +elseif(MINIUPNP_FOUND AND MINIUPNPC_VERSION_1_7_OR_HIGHER) + set(USE_SHARED_MINIUPNPC true) +endif() + +if(USE_SHARED_MINIUPNPC) + message(STATUS "Using shared miniupnpc found at ${MINIUPNP_INCLUDE_DIR}") + + set(UPNP_STATIC false PARENT_SCOPE) + set(UPNP_INCLUDE ${MINIUPNP_INCLUDE_DIR} PARENT_SCOPE) + set(UPNP_LIBRARIES ${MINIUPNP_LIBRARY} PARENT_SCOPE) +else() + if(STATIC) + message(STATUS "Using miniupnpc from local source tree for static build") + else() + message(STATUS "Using miniupnpc from local source tree (/external/miniupnp/miniupnpc)") + endif() + + add_subdirectory(miniupnp/miniupnpc) + + set_property(TARGET libminiupnpc-static PROPERTY FOLDER "external") + if(MSVC) + set_property(TARGET libminiupnpc-static APPEND_STRING PROPERTY COMPILE_FLAGS " -wd4244 -wd4267") + elseif(NOT MSVC) + set_property(TARGET libminiupnpc-static APPEND_STRING PROPERTY COMPILE_FLAGS " -Wno-undef -Wno-unused-result -Wno-unused-value") + endif() + + set(UPNP_STATIC true PARENT_SCOPE) + set(UPNP_LIBRARIES "libminiupnpc-static" PARENT_SCOPE) +endif() find_package(Unbound) diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 75cc7536a..dc4436dd7 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -49,9 +49,16 @@ #include "storages/levin_abstract_invoke2.h" #include "cryptonote_core/cryptonote_core.h" -#include -#include -#include +// We have to look for miniupnpc headers in different places, dependent on if its compiled or external +#ifdef UPNP_STATIC + #include + #include + #include +#else + #include "miniupnpc.h" + #include "upnpcommands.h" + #include "upnperrors.h" +#endif #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "net.p2p" From 0742c615e2a1264279ff0375938d8203ead6f6a9 Mon Sep 17 00:00:00 2001 From: wowario Date: Tue, 29 Jan 2019 10:08:14 +0300 Subject: [PATCH 10/20] simplewallet: add churn command --- src/simplewallet/simplewallet.cpp | 115 +++++++++++++++++++++++++++++- src/simplewallet/simplewallet.h | 1 + src/wallet/wallet2.cpp | 1 + src/wallet/wallet2.h | 3 + 4 files changed, 117 insertions(+), 3 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 82d6fb960..ccfaf70d1 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -64,6 +64,9 @@ #include "wallet/wallet_args.h" #include "version.h" #include +#include +#include +#include #ifdef WIN32 #include @@ -2655,8 +2658,14 @@ simple_wallet::simple_wallet() tr("Show simplified help section.")); m_cmd_binder.set_handler("help_advanced", boost::bind(&simple_wallet::help_advanced, this, _1), - tr("help_advanced []"), - tr("Show the advanced help section or the documentation about a .")); + tr("help_advanced"), + tr("Show the help section or the documentation about a .")); + m_cmd_binder.set_handler("churn", + boost::bind(&simple_wallet::churn, this, _1), + tr("churn"), + tr("Churning can enhance privacy by increasing the distance of your funds from the source. This is especially\n" + "important if your funds come from an exchange or a pool that shares public mining data. Each churn also helps\n" + "create more decoy outputs, which is good for constructing stronger ring signatures.")); } //---------------------------------------------------------------------------------------------------- bool simple_wallet::set_variable(const std::vector &args) @@ -4220,10 +4229,12 @@ void simple_wallet::on_money_received(uint64_t height, const crypto::hash &txid, (m_long_payment_id_support ? tr("WARNING: this transaction uses an unencrypted payment ID: consider using subaddresses instead.") : tr("WARNING: this transaction uses an unencrypted payment ID: these are obsolete. Support will be withdrawn in the future. Use subaddresses instead.")); } } + if (m_wallet->auto_confirm_churn() == 0) { if (m_auto_refresh_refreshing) m_cmd_binder.print_prompt(); else m_refresh_progress_reporter.update(height, true); + } } //---------------------------------------------------------------------------------------------------- void simple_wallet::on_unconfirmed_money_received(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx, uint64_t amount, const cryptonote::subaddress_index& subaddr_index) @@ -4233,15 +4244,18 @@ void simple_wallet::on_unconfirmed_money_received(uint64_t height, const crypto: //---------------------------------------------------------------------------------------------------- void simple_wallet::on_money_spent(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& in_tx, uint64_t amount, const cryptonote::transaction& spend_tx, const cryptonote::subaddress_index& subaddr_index) { + message_writer(console_color_magenta, false) << "\r" << tr("Height ") << height << ", " << tr("txid ") << txid << ", " << tr("spent ") << print_money(amount) << ", " << tr("idx ") << subaddr_index; + if (m_wallet->auto_confirm_churn() == 0) { if (m_auto_refresh_refreshing) m_cmd_binder.print_prompt(); else m_refresh_progress_reporter.update(height, true); + } } //---------------------------------------------------------------------------------------------------- void simple_wallet::on_skip_transaction(uint64_t height, const crypto::hash &txid, const cryptonote::transaction& tx) @@ -4371,11 +4385,12 @@ bool simple_wallet::show_balance_unlocked(bool detailed) extra = tr(" (Some owned outputs have partial key images - import_multisig_info needed)"); else if (m_wallet->has_unknown_key_images()) extra += tr(" (Some owned outputs have missing key images - import_key_images needed)"); + if (m_wallet->auto_confirm_churn() == 0) { success_msg_writer() << tr("Currently selected account: [") << m_current_subaddress_account << tr("] ") << m_wallet->get_subaddress_label({m_current_subaddress_account, 0}); const std::string tag = m_wallet->get_account_tags().second[m_current_subaddress_account]; success_msg_writer() << tr("Tag: ") << (tag.empty() ? std::string{tr("(No tag assigned)")} : tag); success_msg_writer() << tr("Balance: ") << print_money(m_wallet->balance(m_current_subaddress_account)) << ", " - << tr("unlocked balance: ") << print_money(m_wallet->unlocked_balance(m_current_subaddress_account)) << extra; + << tr("unlocked balance: ") << print_money(m_wallet->unlocked_balance(m_current_subaddress_account)) << extra;} std::map balance_per_subaddress = m_wallet->balance_per_subaddress(m_current_subaddress_account); std::map unlocked_balance_per_subaddress = m_wallet->unlocked_balance_per_subaddress(m_current_subaddress_account); if (!detailed || balance_per_subaddress.empty()) @@ -5473,6 +5488,8 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vectorget_address() == info.address; + // prompt is there is no payment id and confirmation is required if (m_long_payment_id_support && !payment_id_seen && m_wallet->confirm_missing_payment_id() && !info.is_subaddress && !is_wallet_address) { @@ -5501,6 +5518,9 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vectorauto_confirm_churn() == 0) + { uint64_t total_fee = 0, total_sent = 0; for (size_t n = 0; n < ptx_vector.size(); ++n) { @@ -5543,6 +5563,7 @@ bool simple_wallet::sweep_main(uint64_t below, bool locked, const std::vectormultisig()) @@ -8080,6 +8101,89 @@ bool simple_wallet::process_command(const std::vector &args) return m_cmd_binder.process_command_vec(args); } //---------------------------------------------------------------------------------------------------- +bool simple_wallet::churn(const std::vector &args_) +{ + // Check if wallet is on MAINNET + if (m_wallet->nettype() != cryptonote::MAINNET) + { + fail_msg_writer() << tr("Churning is only possible on Mainnet, operation cancelled."); + return true; + } + + // Check balance + if (m_wallet->unlocked_balance(m_current_subaddress_account) < 2) + { + fail_msg_writer() << tr("You need to have an unlocked balance of more than 2 WOW, operation cancelled."); + return true; + } + + // Warning + std::string warning = input_line(tr("Warning: Churning requires your wallet to be open for a few hours. If wallet is left unattended, make sure your computer is in a secure location. Continue?")); + if (std::cin.eof() || !command_line::is_yes(warning)) + { + message_writer() << tr("Operation cancelled."); + return true; + } + + // Generate pseudo-random integers for the number of churns and interval pause + int churn_number, pause_time, i; + srand (time(NULL)); // seed RNG with current time + churn_number = rand()%(8 + 1 - 2) + 2; // churn between 2 to 8 times + pause_time = rand()%(120 + 1 - 60) + 60; // wait between 1 to 2 hours per interval + float total_time = static_cast(churn_number*pause_time)/60; // calculate how long churning will take in hours + + // Calculate fee + float churn_fee = m_wallet->use_fork_rules(HF_VERSION_PER_BYTE_FEE) ? static_cast(churn_number * 0.015) : static_cast(churn_number * 0.032654); // usual 2/2 tx fee + + // Prompt confirmation + printf ("----------------\nNumber of churns: %d\nTotal duration: %.2f hours\nEstimated cost: %.2f WOW\n----------------\n", churn_number, total_time, churn_fee); + std::string confirm_churn = input_line(tr("Continue?")); + if (std::cin.eof() || !command_line::is_yes(confirm_churn)) + { + message_writer() << tr("Operation cancelled."); + return true; + } + + // Get main address + std::vector local_args = args_; + std::string address_str; + address_str = m_wallet->get_account().get_public_address_str(m_wallet->nettype()); + local_args.push_back(address_str); + message_writer() << tr("Sweeping entire wallet balance to your main address:"); + message_writer(console_color_white, true) << address_str; + + // Disable wallet confirmations to allow for automation + m_wallet->auto_confirm_churn(1); + m_wallet->ask_password(tools::wallet2::AskPasswordNever); + m_wallet->always_confirm_transfers(0); + m_wallet->auto_refresh(0); + + // Loop sweep_all to main address for churn_number times with pause_time each interval + for (i = 1; i <= churn_number; ++i) { + refresh(local_args); + sweep_all(local_args); + message_writer(console_color_magenta, true) << tr("\n---------------- ") << i << tr(" out of ") << churn_number << tr(" churns ----------------"); + + if (i == churn_number) + { + success_msg_writer() << tr("\nChurning compete."); + + // Re-enable confirmations + m_wallet->auto_confirm_churn(0); + m_wallet->ask_password(tools::wallet2::AskPasswordOnAction); + m_wallet->always_confirm_transfers(1); + m_wallet->auto_refresh(1); + } + else + { + message_writer() << tr("\nInterval pause, please wait...\n"); + sleep(pause_time*60); + } + } + + return true; +} +//---------------------------------------------------------------------------------------------------- void simple_wallet::interrupt() { if (m_in_manual_refresh.load(std::memory_order_relaxed)) @@ -8112,9 +8216,14 @@ void simple_wallet::commit_or_save(std::vector& ptx_ } else { + if (m_wallet->auto_confirm_churn() == 0) { m_wallet->commit_tx(ptx); success_msg_writer(true) << tr("Transaction successfully submitted, transaction ") << txid << ENDL << tr("You can check its status by using the `show_transfers` command."); + } else { + m_wallet->commit_tx(ptx); + success_msg_writer(true) << tr("Churn successfully executed, transaction ") << txid; + } } // if no exception, remove element from vector ptx_vector.pop_back(); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 281699753..c2c61f01a 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -141,6 +141,7 @@ namespace cryptonote bool set_ignore_fractional_outputs(const std::vector &args = std::vector()); bool help(const std::vector &args = std::vector()); bool help_advanced(const std::vector &args = std::vector()); + bool churn(const std::vector &args); bool start_mining(const std::vector &args); bool stop_mining(const std::vector &args); bool set_daemon(const std::vector &args); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index bb0b41c1c..bda25316e 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -816,6 +816,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): m_nettype(nettype), m_multisig_rounds_passed(0), m_always_confirm_transfers(true), + m_auto_confirm_churn(false), m_print_ring_members(false), m_store_tx_info(true), m_default_mixin(0), diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 3bbe8eced..66163e77f 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -913,6 +913,8 @@ namespace tools bool always_confirm_transfers() const { return m_always_confirm_transfers; } void always_confirm_transfers(bool always) { m_always_confirm_transfers = always; } + bool auto_confirm_churn() const { return m_auto_confirm_churn; } + void auto_confirm_churn(bool always) { m_auto_confirm_churn = always; } bool print_ring_members() const { return m_print_ring_members; } void print_ring_members(bool value) { m_print_ring_members = value; } bool store_tx_info() const { return m_store_tx_info; } @@ -1316,6 +1318,7 @@ namespace tools uint32_t m_multisig_rounds_passed; std::vector m_multisig_derivations; bool m_always_confirm_transfers; + bool m_auto_confirm_churn; bool m_print_ring_members; bool m_store_tx_info; /*!< request txkey to be returned in RPC, and store in the wallet cache file */ uint32_t m_default_mixin; From dee662ce101cfabd4683b4048635dbc982ffede8 Mon Sep 17 00:00:00 2001 From: wowario Date: Wed, 6 Feb 2019 16:35:53 +0300 Subject: [PATCH 11/20] update genesis block timestamp date --- src/cryptonote_core/blockchain.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 0b18e0810..26faa615c 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -394,9 +394,9 @@ bool Blockchain::init(BlockchainDB* db, const network_type nettype, bool offline uint64_t top_block_timestamp = m_db->get_top_block_timestamp(); uint64_t timestamp_diff = time(NULL) - top_block_timestamp; - // genesis block has no timestamp, could probably change it to have timestamp of 1341378000... + // genesis block has no timestamp, could probably change it to have timestamp of 1522624244 (2018-04-01 23:10:44, block 1)... if(!top_block_timestamp) - timestamp_diff = time(NULL) - 1341378000; + timestamp_diff = time(NULL) - 1522624244; // create general purpose async service queue From 0dbbd13baafb733bb66cbe77394cf715d8c6d9b9 Mon Sep 17 00:00:00 2001 From: SChernykh Date: Mon, 4 Feb 2019 17:49:19 +0100 Subject: [PATCH 12/20] Cryptonight Wow --- src/crypto/chacha.h | 8 +- src/crypto/hash-ops.h | 2 +- src/crypto/hash.h | 8 +- src/crypto/slow-hash.c | 63 ++- src/crypto/variant4_random_math.h | 425 ++++++++++++++++++ .../cryptonote_format_utils.cpp | 4 +- tests/hash/CMakeLists.txt | 2 +- tests/hash/main.cpp | 29 +- tests/hash/tests-slow-4.txt | 10 + 9 files changed, 527 insertions(+), 24 deletions(-) create mode 100644 src/crypto/variant4_random_math.h create mode 100644 tests/hash/tests-slow-4.txt diff --git a/src/crypto/chacha.h b/src/crypto/chacha.h index 6e85ad0e9..0610f7051 100644 --- a/src/crypto/chacha.h +++ b/src/crypto/chacha.h @@ -73,18 +73,18 @@ namespace crypto { inline void generate_chacha_key(const void *data, size_t size, chacha_key& key, uint64_t kdf_rounds) { static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key"); epee::mlocked> pwd_hash; - crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 0/*prehashed*/); + crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 0/*prehashed*/, 0/*height*/); for (uint64_t n = 1; n < kdf_rounds; ++n) - crypto::cn_slow_hash(pwd_hash.data(), pwd_hash.size(), pwd_hash.data(), 0/*variant*/, 0/*prehashed*/); + crypto::cn_slow_hash(pwd_hash.data(), pwd_hash.size(), pwd_hash.data(), 0/*variant*/, 0/*prehashed*/, 0/*height*/); memcpy(&unwrap(unwrap(key)), pwd_hash.data(), sizeof(key)); } inline void generate_chacha_key_prehashed(const void *data, size_t size, chacha_key& key, uint64_t kdf_rounds) { static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key"); epee::mlocked> pwd_hash; - crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 1/*prehashed*/); + crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 1/*prehashed*/, 0/*height*/); for (uint64_t n = 1; n < kdf_rounds; ++n) - crypto::cn_slow_hash(pwd_hash.data(), pwd_hash.size(), pwd_hash.data(), 0/*variant*/, 0/*prehashed*/); + crypto::cn_slow_hash(pwd_hash.data(), pwd_hash.size(), pwd_hash.data(), 0/*variant*/, 0/*prehashed*/, 0/*height*/); memcpy(&unwrap(unwrap(key)), pwd_hash.data(), sizeof(key)); } diff --git a/src/crypto/hash-ops.h b/src/crypto/hash-ops.h index d77d55cf3..891896120 100644 --- a/src/crypto/hash-ops.h +++ b/src/crypto/hash-ops.h @@ -79,7 +79,7 @@ enum { }; void cn_fast_hash(const void *data, size_t length, char *hash); -void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed); +void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, uint64_t height); void hash_extra_blake(const void *data, size_t length, char *hash); void hash_extra_groestl(const void *data, size_t length, char *hash); diff --git a/src/crypto/hash.h b/src/crypto/hash.h index 995e2294e..165fe6bb0 100644 --- a/src/crypto/hash.h +++ b/src/crypto/hash.h @@ -71,12 +71,12 @@ namespace crypto { return h; } - inline void cn_slow_hash(const void *data, std::size_t length, hash &hash, int variant = 0) { - cn_slow_hash(data, length, reinterpret_cast(&hash), variant, 0/*prehashed*/); + inline void cn_slow_hash(const void *data, std::size_t length, hash &hash, int variant = 0, uint64_t height = 0) { + cn_slow_hash(data, length, reinterpret_cast(&hash), variant, 0/*prehashed*/, height); } - inline void cn_slow_hash_prehashed(const void *data, std::size_t length, hash &hash, int variant = 0) { - cn_slow_hash(data, length, reinterpret_cast(&hash), variant, 1/*prehashed*/); + inline void cn_slow_hash_prehashed(const void *data, std::size_t length, hash &hash, int variant = 0, uint64_t height = 0) { + cn_slow_hash(data, length, reinterpret_cast(&hash), variant, 1/*prehashed*/, height); } inline void tree_hash(const hash *hashes, std::size_t count, hash &root_hash) { diff --git a/src/crypto/slow-hash.c b/src/crypto/slow-hash.c index dcbabccab..aa756e2da 100644 --- a/src/crypto/slow-hash.c +++ b/src/crypto/slow-hash.c @@ -39,6 +39,7 @@ #include "hash-ops.h" #include "oaes_lib.h" #include "variant2_int_sqrt.h" +#include "variant4_random_math.h" #define MEMORY (1 << 21) // 2MB scratchpad #define ITER (1 << 20) @@ -172,7 +173,7 @@ extern void aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *ex const uint64_t sqrt_input = SWAP64LE(((uint64_t*)(ptr))[0]) + division_result #define VARIANT2_INTEGER_MATH_SSE2(b, ptr) \ - do if (variant >= 2) \ + do if ((variant == 2) || (variant == 3)) \ { \ VARIANT2_INTEGER_MATH_DIVISION_STEP(b, ptr); \ VARIANT2_INTEGER_MATH_SQRT_STEP_SSE2(); \ @@ -182,7 +183,7 @@ extern void aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *ex #if defined DBL_MANT_DIG && (DBL_MANT_DIG >= 50) // double precision floating point type has enough bits of precision on current platform #define VARIANT2_PORTABLE_INTEGER_MATH(b, ptr) \ - do if (variant >= 2) \ + do if ((variant == 2) || (variant == 3)) \ { \ VARIANT2_INTEGER_MATH_DIVISION_STEP(b, ptr); \ VARIANT2_INTEGER_MATH_SQRT_STEP_FP64(); \ @@ -192,7 +193,7 @@ extern void aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *ex // double precision floating point type is not good enough on current platform // fall back to the reference code (integer only) #define VARIANT2_PORTABLE_INTEGER_MATH(b, ptr) \ - do if (variant >= 2) \ + do if ((variant == 2) || (variant == 3)) \ { \ VARIANT2_INTEGER_MATH_DIVISION_STEP(b, ptr); \ VARIANT2_INTEGER_MATH_SQRT_STEP_REF(); \ @@ -214,6 +215,46 @@ extern void aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *ex lo ^= SWAP64LE(*(U64(hp_state + (j ^ 0x20)) + 1)); \ } while (0) +#define V4_REG_LOAD(dst, src) \ + do { \ + memcpy((dst), (src), sizeof(v4_reg)); \ + if (sizeof(v4_reg) == sizeof(uint32_t)) \ + *(dst) = SWAP32LE(*(dst)); \ + else \ + *(dst) = SWAP64LE(*(dst)); \ + } while (0) + +#define VARIANT4_RANDOM_MATH_INIT() \ + v4_reg r[8]; \ + struct V4_Instruction code[TOTAL_LATENCY * ALU_COUNT + 1]; \ + do if (variant >= 4) \ + { \ + for (int i = 0; i < 4; ++i) \ + V4_REG_LOAD(r + i, (uint8_t*)(state.hs.w + 12) + sizeof(v4_reg) * i); \ + v4_random_math_init(code, height); \ + } while (0) + +#define VARIANT4_RANDOM_MATH(a, b, r, _b, _b1) \ + do if (variant >= 4) \ + { \ + uint64_t t; \ + memcpy(&t, b, sizeof(uint64_t)); \ + \ + if (sizeof(v4_reg) == sizeof(uint32_t)) \ + t ^= SWAP64LE((r[0] + r[1]) | ((uint64_t)(r[2] + r[3]) << 32)); \ + else \ + t ^= SWAP64LE((r[0] + r[1]) ^ (r[2] + r[3])); \ + \ + memcpy(b, &t, sizeof(uint64_t)); \ + \ + V4_REG_LOAD(r + 4, a); \ + V4_REG_LOAD(r + 5, (uint64_t*)(a) + 1); \ + V4_REG_LOAD(r + 6, _b); \ + V4_REG_LOAD(r + 7, _b1); \ + \ + v4_random_math(code, r); \ + } while (0) + #if !defined NO_AES && (defined(__x86_64__) || (defined(_MSC_VER) && defined(_WIN64))) // Optimised code below, uses x86-specific intrinsics, SSE2, AES-NI @@ -298,6 +339,7 @@ extern void aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *ex p = U64(&hp_state[j]); \ b[0] = p[0]; b[1] = p[1]; \ VARIANT2_INTEGER_MATH_SSE2(b, c); \ + VARIANT4_RANDOM_MATH(a, b, r, &_b, &_b1); \ __mul(); \ VARIANT2_2(); \ VARIANT2_SHUFFLE_ADD_SSE2(hp_state, j); \ @@ -694,7 +736,7 @@ void slow_hash_free_state(void) * @param length the length in bytes of the data * @param hash a pointer to a buffer in which the final 256 bit hash will be stored */ -void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed) +void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, uint64_t height) { RDATA_ALIGN16 uint8_t expandedKey[240]; /* These buffers are aligned to use later with SSE functions */ @@ -730,6 +772,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int VARIANT1_INIT64(); VARIANT2_INIT64(); + VARIANT4_RANDOM_MATH_INIT(); /* CryptoNight Step 2: Iteratively encrypt the results from Keccak to fill * the 2MB large random access buffer. @@ -901,6 +944,7 @@ union cn_slow_hash_state p = U64(&hp_state[j]); \ b[0] = p[0]; b[1] = p[1]; \ VARIANT2_PORTABLE_INTEGER_MATH(b, c); \ + VARIANT4_RANDOM_MATH(a, b, r, &_b, &_b1); \ __mul(); \ VARIANT2_2(); \ VARIANT2_SHUFFLE_ADD_NEON(hp_state, j); \ @@ -1063,7 +1107,7 @@ STATIC INLINE void aligned_free(void *ptr) } #endif /* FORCE_USE_HEAP */ -void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed) +void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, uint64_t height) { RDATA_ALIGN16 uint8_t expandedKey[240]; @@ -1100,6 +1144,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int VARIANT1_INIT64(); VARIANT2_INIT64(); + VARIANT4_RANDOM_MATH_INIT(); /* CryptoNight Step 2: Iteratively encrypt the results from Keccak to fill * the 2MB large random access buffer. @@ -1278,7 +1323,7 @@ STATIC INLINE void xor_blocks(uint8_t* a, const uint8_t* b) U64(a)[1] ^= U64(b)[1]; } -void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed) +void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, uint64_t height) { uint8_t text[INIT_SIZE_BYTE]; uint8_t a[AES_BLOCK_SIZE]; @@ -1317,6 +1362,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int VARIANT1_INIT64(); VARIANT2_INIT64(); + VARIANT4_RANDOM_MATH_INIT(); // use aligned data memcpy(expandedKey, aes_ctx->key->exp_data, aes_ctx->key->exp_data_len); @@ -1353,6 +1399,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int copy_block(c, p); VARIANT2_PORTABLE_INTEGER_MATH(c, c1); + VARIANT4_RANDOM_MATH(a, c, r, b, b + AES_BLOCK_SIZE); mul(c1, c, d); VARIANT2_2_PORTABLE(); VARIANT2_PORTABLE_SHUFFLE_ADD(long_state, j); @@ -1476,7 +1523,7 @@ union cn_slow_hash_state { }; #pragma pack(pop) -void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed) { +void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, uint64_t height) { #ifndef FORCE_USE_HEAP uint8_t long_state[MEMORY]; #else @@ -1505,6 +1552,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int VARIANT1_PORTABLE_INIT(); VARIANT2_PORTABLE_INIT(); + VARIANT4_RANDOM_MATH_INIT(); oaes_key_import_data(aes_ctx, aes_key, AES_KEY_SIZE); for (i = 0; i < MEMORY / INIT_SIZE_BYTE; i++) { @@ -1537,6 +1585,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int j = e2i(c1, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE; copy_block(c2, &long_state[j]); VARIANT2_PORTABLE_INTEGER_MATH(c2, c1); + VARIANT4_RANDOM_MATH(a, c2, r, b, b + AES_BLOCK_SIZE); mul(c1, c2, d); VARIANT2_2_PORTABLE(); VARIANT2_PORTABLE_SHUFFLE_ADD(long_state, j); diff --git a/src/crypto/variant4_random_math.h b/src/crypto/variant4_random_math.h new file mode 100644 index 000000000..2c190287b --- /dev/null +++ b/src/crypto/variant4_random_math.h @@ -0,0 +1,425 @@ +#ifndef VARIANT4_RANDOM_MATH_H +#define VARIANT4_RANDOM_MATH_H + +// Register size can be configured to either 32 bit (uint32_t) or 64 bit (uint64_t) +typedef uint32_t v4_reg; + +enum V4_Settings +{ + // Generate code with minimal theoretical latency = 45 cycles, which is equivalent to 15 multiplications + TOTAL_LATENCY = 15 * 3, + + // Always generate at least 60 instructions + NUM_INSTRUCTIONS = 60, + + // Available ALUs for MUL + // Modern CPUs typically have only 1 ALU which can do multiplications + ALU_COUNT_MUL = 1, + + // Total available ALUs + // Modern CPUs have 4 ALUs, but we use only 3 because random math executes together with other main loop code + ALU_COUNT = 3, +}; + +enum V4_InstructionList +{ + MUL, // a*b + ADD, // a+b + C, C is an unsigned 32-bit constant + SUB, // a-b + ROR, // rotate right "a" by "b & 31" bits + ROL, // rotate left "a" by "b & 31" bits + XOR, // a^b + RET, // finish execution + V4_INSTRUCTION_COUNT = RET, +}; + +// V4_InstructionDefinition is used to generate code from random data +// Every random sequence of bytes is a valid code +// +// There are 8 registers in total: +// - 4 variable registers +// - 4 constant registers initialized from loop variables +// +// This is why dst_index is 2 bits +enum V4_InstructionDefinition +{ + V4_OPCODE_BITS = 3, + V4_DST_INDEX_BITS = 2, + V4_SRC_INDEX_BITS = 3, +}; + +struct V4_Instruction +{ + uint8_t opcode; + uint8_t dst_index; + uint8_t src_index; + uint32_t C; +}; + +#ifndef FORCEINLINE +#ifdef __GNUC__ +#define FORCEINLINE __attribute__((always_inline)) inline +#elif _MSC_VER +#define FORCEINLINE __forceinline +#else +#define FORCEINLINE inline +#endif +#endif + +#ifndef UNREACHABLE_CODE +#ifdef __GNUC__ +#define UNREACHABLE_CODE __builtin_unreachable() +#elif _MSC_VER +#define UNREACHABLE_CODE __assume(false) +#else +#define UNREACHABLE_CODE +#endif +#endif + +// Random math interpreter's loop is fully unrolled and inlined to achieve 100% branch prediction on CPU: +// every switch-case will point to the same destination on every iteration of Cryptonight main loop +// +// This is about as fast as it can get without using low-level machine code generation +static FORCEINLINE void v4_random_math(const struct V4_Instruction* code, v4_reg* r) +{ + enum + { + REG_BITS = sizeof(v4_reg) * 8, + }; + +#define V4_EXEC(i) \ + { \ + const struct V4_Instruction* op = code + i; \ + const v4_reg src = r[op->src_index]; \ + v4_reg* dst = r + op->dst_index; \ + switch (op->opcode) \ + { \ + case MUL: \ + *dst *= src; \ + break; \ + case ADD: \ + *dst += src + op->C; \ + break; \ + case SUB: \ + *dst -= src; \ + break; \ + case ROR: \ + { \ + const uint32_t shift = src % REG_BITS; \ + *dst = (*dst >> shift) | (*dst << ((REG_BITS - shift) % REG_BITS)); \ + } \ + break; \ + case ROL: \ + { \ + const uint32_t shift = src % REG_BITS; \ + *dst = (*dst << shift) | (*dst >> ((REG_BITS - shift) % REG_BITS)); \ + } \ + break; \ + case XOR: \ + *dst ^= src; \ + break; \ + case RET: \ + return; \ + default: \ + UNREACHABLE_CODE; \ + break; \ + } \ + } + +#define V4_EXEC_10(j) \ + V4_EXEC(j + 0) \ + V4_EXEC(j + 1) \ + V4_EXEC(j + 2) \ + V4_EXEC(j + 3) \ + V4_EXEC(j + 4) \ + V4_EXEC(j + 5) \ + V4_EXEC(j + 6) \ + V4_EXEC(j + 7) \ + V4_EXEC(j + 8) \ + V4_EXEC(j + 9) + + // Generated program can have 60 + a few more (usually 2-3) instructions to achieve required latency + // I've checked all block heights < 10,000,000 and here is the distribution of program sizes: + // + // 60 28495 + // 61 106077 + // 62 2455855 + // 63 5114930 + // 64 1020868 + // 65 1109026 + // 66 151756 + // 67 8429 + // 68 4477 + // 69 87 + + // Unroll 70 instructions here + V4_EXEC_10(0); // instructions 0-9 + V4_EXEC_10(10); // instructions 10-19 + V4_EXEC_10(20); // instructions 20-29 + V4_EXEC_10(30); // instructions 30-39 + V4_EXEC_10(40); // instructions 40-49 + V4_EXEC_10(50); // instructions 50-59 + V4_EXEC_10(60); // instructions 60-69 + +#undef V4_EXEC_10 +#undef V4_EXEC +} + +// If we don't have enough data available, generate more +static FORCEINLINE void check_data(size_t* data_index, const size_t bytes_needed, int8_t* data, const size_t data_size) +{ + if (*data_index + bytes_needed > data_size) + { + hash_extra_blake(data, data_size, (char*) data); + *data_index = 0; + } +} + +// Generates as many random math operations as possible with given latency and ALU restrictions +static inline int v4_random_math_init(struct V4_Instruction* code, const uint64_t height) +{ + // MUL is 3 cycles, 3-way addition and rotations are 2 cycles, SUB/XOR are 1 cycle + // These latencies match real-life instruction latencies for Intel CPUs starting from Sandy Bridge and up to Skylake/Coffee lake + // + // AMD Ryzen has the same latencies except 1-cycle ROR/ROL, so it'll be a bit faster than Intel Sandy Bridge and newer processors + // Surprisingly, Intel Nehalem also has 1-cycle ROR/ROL, so it'll also be faster than Intel Sandy Bridge and newer processors + // AMD Bulldozer has 4 cycles latency for MUL (slower than Intel) and 1 cycle for ROR/ROL (faster than Intel), so average performance will be the same + // Source: https://www.agner.org/optimize/instruction_tables.pdf + const int op_latency[V4_INSTRUCTION_COUNT] = { 3, 2, 1, 2, 2, 1 }; + + // Instruction latencies for theoretical ASIC implementation + const int asic_op_latency[V4_INSTRUCTION_COUNT] = { 3, 1, 1, 1, 1, 1 }; + + // Available ALUs for each instruction + const int op_ALUs[V4_INSTRUCTION_COUNT] = { ALU_COUNT_MUL, ALU_COUNT, ALU_COUNT, ALU_COUNT, ALU_COUNT, ALU_COUNT }; + + int8_t data[32]; + memset(data, 0, sizeof(data)); + uint64_t tmp = SWAP64LE(height); + memcpy(data, &tmp, sizeof(uint64_t)); + + // Set data_index past the last byte in data + // to trigger full data update with blake hash + // before we start using it + size_t data_index = sizeof(data); + + int code_size; + do { + int latency[8]; + int asic_latency[8]; + + // Tracks previous instruction and value of the source operand for registers R0-R3 throughout code execution + // byte 0: current value of the destination register + // byte 1: instruction opcode + // byte 2: current value of the source register + // + // Registers R4-R7 are constant and are treated as having the same value because when we do + // the same operation twice with two constant source registers, it can be optimized into a single operation + uint32_t inst_data[8] = { 0, 1, 2, 3, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF }; + + bool alu_busy[TOTAL_LATENCY + 1][ALU_COUNT]; + bool is_rotation[V4_INSTRUCTION_COUNT]; + bool rotated[4]; + int rotate_count = 0; + + memset(latency, 0, sizeof(latency)); + memset(asic_latency, 0, sizeof(asic_latency)); + memset(alu_busy, 0, sizeof(alu_busy)); + memset(is_rotation, 0, sizeof(is_rotation)); + memset(rotated, 0, sizeof(rotated)); + is_rotation[ROR] = true; + is_rotation[ROL] = true; + + int num_retries = 0; + code_size = 0; + + int total_iterations = 0; + + // Generate random code to achieve minimal required latency for our abstract CPU + // Try to get this latency for all 4 registers + while (((latency[0] < TOTAL_LATENCY) || (latency[1] < TOTAL_LATENCY) || (latency[2] < TOTAL_LATENCY) || (latency[3] < TOTAL_LATENCY)) && (num_retries < 64)) + { + // Fail-safe to guarantee loop termination + ++total_iterations; + if (total_iterations > 256) + break; + + check_data(&data_index, 1, data, sizeof(data)); + + const uint8_t c = ((uint8_t*)data)[data_index++]; + + // MUL = opcodes 0-2 + // ADD = opcode 3 + // SUB = opcode 4 + // ROR/ROL = opcode 5, shift direction is selected randomly + // XOR = opcodes 6-7 + uint8_t opcode = c & ((1 << V4_OPCODE_BITS) - 1); + if (opcode == 5) + { + check_data(&data_index, 1, data, sizeof(data)); + opcode = (data[data_index++] >= 0) ? ROR : ROL; + } + else if (opcode >= 6) + { + opcode = XOR; + } + else + { + opcode = (opcode <= 2) ? MUL : (opcode - 2); + } + + uint8_t dst_index = (c >> V4_OPCODE_BITS) & ((1 << V4_DST_INDEX_BITS) - 1); + uint8_t src_index = (c >> (V4_OPCODE_BITS + V4_DST_INDEX_BITS)) & ((1 << V4_SRC_INDEX_BITS) - 1); + + const int a = dst_index; + int b = src_index; + + // Don't do ADD/SUB/XOR with the same register + if (((opcode == ADD) || (opcode == SUB) || (opcode == XOR)) && (a == b)) + { + // a is always < 4, so we don't need to check bounds here + b = a + 4; + src_index = b; + } + + // Don't do rotation with the same destination twice because it's equal to a single rotation + if (is_rotation[opcode] && rotated[a]) + { + continue; + } + + // Don't do the same instruction (except MUL) with the same source value twice because all other cases can be optimized: + // 2xADD(a, b, C) = ADD(a, b*2, C1+C2), same for SUB and rotations + // 2xXOR(a, b) = NOP + if ((opcode != MUL) && ((inst_data[a] & 0xFFFF00) == (opcode << 8) + ((inst_data[b] & 255) << 16))) + { + continue; + } + + // Find which ALU is available (and when) for this instruction + int next_latency = (latency[a] > latency[b]) ? latency[a] : latency[b]; + int alu_index = -1; + while (next_latency < TOTAL_LATENCY) + { + for (int i = op_ALUs[opcode] - 1; i >= 0; --i) + { + if (!alu_busy[next_latency][i]) + { + // ADD is implemented as two 1-cycle instructions on a real CPU, so do an additional availability check + if ((opcode == ADD) && alu_busy[next_latency + 1][i]) + { + continue; + } + + // Rotation can only start when previous rotation is finished, so do an additional availability check + if (is_rotation[opcode] && (next_latency < rotate_count * op_latency[opcode])) + { + continue; + } + + alu_index = i; + break; + } + } + if (alu_index >= 0) + { + break; + } + ++next_latency; + } + + // Don't generate instructions that leave some register unchanged for more than 7 cycles + if (next_latency > latency[a] + 7) + { + continue; + } + + next_latency += op_latency[opcode]; + + if (next_latency <= TOTAL_LATENCY) + { + if (is_rotation[opcode]) + { + ++rotate_count; + } + + // Mark ALU as busy only for the first cycle when it starts executing the instruction because ALUs are fully pipelined + alu_busy[next_latency - op_latency[opcode]][alu_index] = true; + latency[a] = next_latency; + + // ASIC is supposed to have enough ALUs to run as many independent instructions per cycle as possible, so latency calculation for ASIC is simple + asic_latency[a] = ((asic_latency[a] > asic_latency[b]) ? asic_latency[a] : asic_latency[b]) + asic_op_latency[opcode]; + + rotated[a] = is_rotation[opcode]; + + inst_data[a] = code_size + (opcode << 8) + ((inst_data[b] & 255) << 16); + + code[code_size].opcode = opcode; + code[code_size].dst_index = dst_index; + code[code_size].src_index = src_index; + code[code_size].C = 0; + + if (opcode == ADD) + { + // ADD instruction is implemented as two 1-cycle instructions on a real CPU, so mark ALU as busy for the next cycle too + alu_busy[next_latency - op_latency[opcode] + 1][alu_index] = true; + + // ADD instruction requires 4 more random bytes for 32-bit constant "C" in "a = a + b + C" + check_data(&data_index, sizeof(uint32_t), data, sizeof(data)); + uint32_t t; + memcpy(&t, data + data_index, sizeof(uint32_t)); + code[code_size].C = SWAP32LE(t); + data_index += sizeof(uint32_t); + } + + ++code_size; + if (code_size >= NUM_INSTRUCTIONS) + { + break; + } + } + else + { + ++num_retries; + } + } + + // ASIC has more execution resources and can extract as much parallelism from the code as possible + // We need to add a few more MUL and ROR instructions to achieve minimal required latency for ASIC + // Get this latency for at least 1 of the 4 registers + const int prev_code_size = code_size; + while ((asic_latency[0] < TOTAL_LATENCY) && (asic_latency[1] < TOTAL_LATENCY) && (asic_latency[2] < TOTAL_LATENCY) && (asic_latency[3] < TOTAL_LATENCY)) + { + int min_idx = 0; + int max_idx = 0; + for (int i = 1; i < 4; ++i) + { + if (asic_latency[i] < asic_latency[min_idx]) min_idx = i; + if (asic_latency[i] > asic_latency[max_idx]) max_idx = i; + } + + const uint8_t pattern[3] = { ROR, MUL, MUL }; + const uint8_t opcode = pattern[(code_size - prev_code_size) % 3]; + latency[min_idx] = latency[max_idx] + op_latency[opcode]; + asic_latency[min_idx] = asic_latency[max_idx] + asic_op_latency[opcode]; + + code[code_size].opcode = opcode; + code[code_size].dst_index = min_idx; + code[code_size].src_index = max_idx; + code[code_size].C = 0; + ++code_size; + } + + // There is ~99.8% chance that code_size >= NUM_INSTRUCTIONS here, so second iteration is required rarely + } while (code_size < NUM_INSTRUCTIONS); + + // Add final instruction to stop the interpreter + code[code_size].opcode = RET; + code[code_size].dst_index = 0; + code[code_size].src_index = 0; + code[code_size].C = 0; + + return code_size; +} + +#endif diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 7f1dc19cc..631abe225 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -1024,8 +1024,8 @@ namespace cryptonote bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height) { blobdata bd = get_block_hashing_blob(b); - const int cn_variant = b.major_version >= 7 ? b.major_version - 6 : 0; - crypto::cn_slow_hash(bd.data(), bd.size(), res, cn_variant); + const int cn_variant = b.major_version >= 11 ? 4 : b.major_version >= 9 && b.major_version <= 10 ? 2 : 1; + crypto::cn_slow_hash(bd.data(), bd.size(), res, cn_variant, height); return true; } //--------------------------------------------------------------- diff --git a/tests/hash/CMakeLists.txt b/tests/hash/CMakeLists.txt index 433cf94e9..105cf2c13 100644 --- a/tests/hash/CMakeLists.txt +++ b/tests/hash/CMakeLists.txt @@ -43,7 +43,7 @@ set_property(TARGET hash-tests PROPERTY FOLDER "tests") -foreach (hash IN ITEMS fast slow slow-1 slow-2 tree extra-blake extra-groestl extra-jh extra-skein) +foreach (hash IN ITEMS fast slow slow-1 slow-2 slow-4 tree extra-blake extra-groestl extra-jh extra-skein) add_test( NAME "hash-${hash}" COMMAND hash-tests "${hash}" "${CMAKE_CURRENT_SOURCE_DIR}/tests-${hash}.txt") diff --git a/tests/hash/main.cpp b/tests/hash/main.cpp index 7767d0d3b..acfd99e96 100644 --- a/tests/hash/main.cpp +++ b/tests/hash/main.cpp @@ -44,6 +44,13 @@ using namespace std; using namespace crypto; typedef crypto::hash chash; +struct V4_Data +{ + const void* data; + size_t length; + uint64_t height; +}; + PUSH_WARNINGS DISABLE_VS_WARNINGS(4297) extern "C" { @@ -54,13 +61,17 @@ extern "C" { tree_hash((const char (*)[crypto::HASH_SIZE]) data, length >> 5, hash); } static void cn_slow_hash_0(const void *data, size_t length, char *hash) { - return cn_slow_hash(data, length, hash, 0/*variant*/, 0/*prehashed*/); + return cn_slow_hash(data, length, hash, 0/*variant*/, 0/*prehashed*/, 0/*height*/); } static void cn_slow_hash_1(const void *data, size_t length, char *hash) { - return cn_slow_hash(data, length, hash, 1/*variant*/, 0/*prehashed*/); + return cn_slow_hash(data, length, hash, 1/*variant*/, 0/*prehashed*/, 0/*height*/); } static void cn_slow_hash_2(const void *data, size_t length, char *hash) { - return cn_slow_hash(data, length, hash, 2/*variant*/, 0/*prehashed*/); + return cn_slow_hash(data, length, hash, 2/*variant*/, 0/*prehashed*/, 0/*height*/); + } + static void cn_slow_hash_4(const void *data, size_t, char *hash) { + const V4_Data* p = reinterpret_cast(data); + return cn_slow_hash(p->data, p->length, hash, 4/*variant*/, 0/*prehashed*/, p->height); } } POP_WARNINGS @@ -72,7 +83,7 @@ struct hash_func { } hashes[] = {{"fast", cn_fast_hash}, {"slow", cn_slow_hash_0}, {"tree", hash_tree}, {"extra-blake", hash_extra_blake}, {"extra-groestl", hash_extra_groestl}, {"extra-jh", hash_extra_jh}, {"extra-skein", hash_extra_skein}, - {"slow-1", cn_slow_hash_1}, {"slow-2", cn_slow_hash_2}}; + {"slow-1", cn_slow_hash_1}, {"slow-2", cn_slow_hash_2}, {"slow-4", cn_slow_hash_4}}; int test_variant2_int_sqrt(); int test_variant2_int_sqrt_ref(); @@ -140,7 +151,15 @@ int main(int argc, char *argv[]) { input.exceptions(ios_base::badbit | ios_base::failbit | ios_base::eofbit); input.clear(input.rdstate()); get(input, data); - f(data.data(), data.size(), (char *) &actual); + if (f == cn_slow_hash_4) { + V4_Data d; + d.data = data.data(); + d.length = data.size(); + get(input, d.height); + f(&d, 0, (char *) &actual); + } else { + f(data.data(), data.size(), (char *) &actual); + } if (expected != actual) { size_t i; cerr << "Hash mismatch on test " << test << endl << "Input: "; diff --git a/tests/hash/tests-slow-4.txt b/tests/hash/tests-slow-4.txt new file mode 100644 index 000000000..78e72b744 --- /dev/null +++ b/tests/hash/tests-slow-4.txt @@ -0,0 +1,10 @@ +9d47bf4c41b7e8e727e681715acb47fa1677cdba9ca7bcb05ad8cc8abd5daa66 5468697320697320612074657374205468697320697320612074657374205468697320697320612074657374 1806260 +0d4a495cb844a3ca8ba4edb8e6bcf829ef1c06d9cdea2b62ca46c2a21b8b0a79 4c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e67 1806261 +a1d6d848b5c5915fccd2f64cf216c6b1a02cf7c77bc80d8d4e51b419e88ff0dd 656c69742c2073656420646f20656975736d6f642074656d706f7220696e6369646964756e74207574206c61626f7265 1806262 +af3a8544a0221a148c2ac90484b19861e3afca33fe17021efb8ad6496b567915 657420646f6c6f7265206d61676e6120616c697175612e20557420656e696d206164206d696e696d2076656e69616d2c 1806263 +313399e0963ae8a99dab8af66d343e097dae0c0feb08dbc43ccdafef5515f413 71756973206e6f737472756420657865726369746174696f6e20756c6c616d636f206c61626f726973206e697369 1806264 +6021c6ef90bff9ae94a7506d623d3a7a86c1756d655f50dd558f716d64622a34 757420616c697175697020657820656120636f6d6d6f646f20636f6e7365717561742e20447569732061757465 1806265 +2b13000535f3db5f9b9b84a65c4351f386cd2cdedebb8c3ad2eab086e6a3fee5 697275726520646f6c6f7220696e20726570726568656e646572697420696e20766f6c7570746174652076656c6974 1806266 +fc0e1dad8e895749dc90eb690bc1ba059a1cd772afaaf65a106bf9e5e6b80503 657373652063696c6c756d20646f6c6f726520657520667567696174206e756c6c612070617269617475722e 1806267 +b60b0afe144deff7d903ed2d5545e77ebe66a3c51fee7016eeb8fee9eb630c0f 4578636570746575722073696e74206f6363616563617420637570696461746174206e6f6e2070726f6964656e742c 1806268 +64774b27e7d5fec862fc4c0c13ac6bf09123b6f05bb0e4b75c97f379a2b3a679 73756e7420696e2063756c706120717569206f666669636961206465736572756e74206d6f6c6c697420616e696d20696420657374206c61626f72756d2e 1806269 From 87d457d4ac5334e5a41cb3f2995a08a71bd5fd6f Mon Sep 17 00:00:00 2001 From: wowario Date: Sat, 9 Mar 2019 10:46:03 +0300 Subject: [PATCH 13/20] support old BP --- .../cryptonote_boost_serialization.h | 6 +- .../cryptonote_format_utils.cpp | 26 +- src/cryptonote_core/blockchain.cpp | 37 +- src/cryptonote_core/cryptonote_core.cpp | 40 + src/cryptonote_core/cryptonote_tx_utils.cpp | 4 +- src/cryptonote_core/tx_pool.cpp | 4 +- src/ringct/CMakeLists.txt | 3 +- src/ringct/bulletproofs.h | 7 + src/ringct/bulletproofs2.cc | 1160 +++++++++++++++++ src/ringct/rctSigs.cpp | 226 +++- src/ringct/rctSigs.h | 2 + src/ringct/rctTypes.cpp | 34 + src/ringct/rctTypes.h | 44 +- src/wallet/wallet2.cpp | 18 +- 14 files changed, 1559 insertions(+), 52 deletions(-) create mode 100644 src/ringct/bulletproofs2.cc diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h index d1e321994..32de9a7ac 100644 --- a/src/cryptonote_basic/cryptonote_boost_serialization.h +++ b/src/cryptonote_basic/cryptonote_boost_serialization.h @@ -294,7 +294,7 @@ namespace boost a & x.type; if (x.type == rct::RCTTypeNull) return; - if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof && x.type != rct::RCTTypeBulletproof2) + if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof && x.type != rct::RCTTypeBulletproof2 && x.type != rct::RCTTypeFullBulletproof && x.type != rct::RCTTypeSimpleBulletproof) throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); // a & x.message; message is not serialized, as it can be reconstructed from the tx data // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets @@ -322,7 +322,7 @@ namespace boost a & x.type; if (x.type == rct::RCTTypeNull) return; - if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof && x.type != rct::RCTTypeBulletproof2) + if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple && x.type != rct::RCTTypeBulletproof && x.type != rct::RCTTypeBulletproof2 && x.type != rct::RCTTypeFullBulletproof && x.type != rct::RCTTypeSimpleBulletproof) throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); // a & x.message; message is not serialized, as it can be reconstructed from the tx data // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets @@ -336,7 +336,7 @@ namespace boost if (x.p.rangeSigs.empty()) a & x.p.bulletproofs; a & x.p.MGs; - if (x.type == rct::RCTTypeBulletproof || x.type == rct::RCTTypeBulletproof2) + if (x.type == rct::RCTTypeBulletproof || x.type == rct::RCTTypeBulletproof2 || x.type == rct::RCTTypeSimpleBulletproof) a & x.p.pseudoOuts; } } diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp index 631abe225..bfdafc80e 100644 --- a/src/cryptonote_basic/cryptonote_format_utils.cpp +++ b/src/cryptonote_basic/cryptonote_format_utils.cpp @@ -147,7 +147,7 @@ namespace cryptonote if (!base_only) { const bool bulletproof = rct::is_rct_bulletproof(rv.type); - if (bulletproof) + if (bulletproof && rv.type == rct::RCTTypeBulletproof) { if (rv.p.bulletproofs.size() != 1) { @@ -160,7 +160,7 @@ namespace cryptonote return false; } const size_t max_outputs = 1 << (rv.p.bulletproofs[0].L.size() - 6); - if (max_outputs < tx.vout.size()) + if (max_outputs < tx.vout.size() && rv.type == rct::RCTTypeBulletproof) { LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs max outputs in tx " << get_transaction_hash(tx)); return false; @@ -171,6 +171,26 @@ namespace cryptonote for (size_t i = 0; i < n_amounts; ++i) rv.p.bulletproofs[0].V[i] = rct::scalarmultKey(rv.outPk[i].mask, rct::INV_EIGHT); } + else if (bulletproof) + { + if (rct::n_bulletproof_v1_amounts(rv.p.bulletproofs) != tx.vout.size()) + { + LOG_PRINT_L1("Failed to parse transaction from blob, bad bulletproofs size in tx " << get_transaction_hash(tx)); + return false; + } + size_t idx = 0; + for (size_t n = 0; n < rv.outPk.size(); ++n) + { + //rv.p.bulletproofs[n].V.resize(1); + //rv.p.bulletproofs[n].V[0] = rv.outPk[n].mask; + CHECK_AND_ASSERT_MES(rv.p.bulletproofs[n].L.size() >= 6, false, "Bad bulletproofs L size"); // at least 64 bits + const size_t n_amounts = rct::n_bulletproof_v1_amounts(rv.p.bulletproofs[n]); + CHECK_AND_ASSERT_MES(idx + n_amounts <= rv.outPk.size(), false, "Internal error filling out V"); + rv.p.bulletproofs[n].V.resize(n_amounts); + for (size_t i = 0; i < n_amounts; ++i) + rv.p.bulletproofs[n].V[i] = rv.outPk[idx++].mask; + } + } } } return true; @@ -356,6 +376,8 @@ namespace cryptonote const size_t n_outputs = tx.vout.size(); if (n_outputs <= 2) return blob_size; + if (rv.type != rct::RCTTypeBulletproof) + return blob_size; const uint64_t bp_base = 368; const size_t n_padded_outputs = rct::n_bulletproof_max_amounts(rv.p.bulletproofs); size_t nlr = 0; diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 26faa615c..85342ebf6 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2402,13 +2402,13 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context } } - // from v11, allow bulletproofs - if (hf_version < 11) { + // from v8, allow bulletproofs + if (hf_version < 8) { if (tx.version >= 2) { const bool bulletproof = rct::is_rct_bulletproof(tx.rct_signatures.type); if (bulletproof || !tx.rct_signatures.p.bulletproofs.empty()) { - MERROR_VER("New Bulletproofs are not allowed before v11"); + MERROR_VER("New Bulletproofs are not allowed before v8"); tvc.m_invalid_output = true; return false; } @@ -2428,7 +2428,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context } } - // from v10, allow bulletproofs v2 + // from v13, allow bulletproofs v2 if (hf_version < HF_VERSION_SMALLER_BP) { if (tx.version >= 2) { if (tx.rct_signatures.type == rct::RCTTypeBulletproof2) @@ -2440,7 +2440,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context } } - // from v11, allow only bulletproofs v2 + // from v14, allow only bulletproofs v2 if (hf_version > HF_VERSION_SMALLER_BP) { if (tx.version >= 2) { if (tx.rct_signatures.type == rct::RCTTypeBulletproof) @@ -2452,6 +2452,19 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context } } + // from v12, forbid old bulletproofs + if (hf_version > 11) { + if (tx.version >= 2) { + const bool old_bulletproof = rct::is_rct_old_bulletproof(tx.rct_signatures.type); + if (old_bulletproof) + { + MERROR_VER("Old Bulletproofs are not allowed after v11"); + tvc.m_invalid_output = true; + return false; + } + } + } + return true; } //------------------------------------------------------------------ @@ -2477,7 +2490,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr rv.message = rct::hash2rct(tx_prefix_hash); // mixRing - full and simple store it in opposite ways - if (rv.type == rct::RCTTypeFull) + if (rv.type == rct::RCTTypeFull || rv.type == rct::RCTTypeFullBulletproof) { CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys"); rv.mixRing.resize(pubkeys[0].size()); @@ -2492,7 +2505,7 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr } } } - else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rv.type == rct::RCTTypeBulletproof2) + else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rv.type == rct::RCTTypeBulletproof2 || rv.type == rct::RCTTypeSimpleBulletproof) { CHECK_AND_ASSERT_MES(!pubkeys.empty() && !pubkeys[0].empty(), false, "empty pubkeys"); rv.mixRing.resize(pubkeys.size()); @@ -2511,14 +2524,14 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr } // II - if (rv.type == rct::RCTTypeFull) + if (rv.type == rct::RCTTypeFull || rv.type == rct::RCTTypeFullBulletproof) { rv.p.MGs.resize(1); rv.p.MGs[0].II.resize(tx.vin.size()); for (size_t n = 0; n < tx.vin.size(); ++n) rv.p.MGs[0].II[n] = rct::ki2rct(boost::get(tx.vin[n]).k_image); } - else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rv.type == rct::RCTTypeBulletproof2) + else if (rv.type == rct::RCTTypeSimple || rv.type == rct::RCTTypeBulletproof || rv.type == rct::RCTTypeBulletproof2 || rv.type == rct::RCTTypeSimpleBulletproof) { CHECK_AND_ASSERT_MES(rv.p.MGs.size() == tx.vin.size(), false, "Bad MGs size"); for (size_t n = 0; n < tx.vin.size(); ++n) @@ -2791,6 +2804,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, return false; } case rct::RCTTypeSimple: + case rct::RCTTypeSimpleBulletproof: case rct::RCTTypeBulletproof: case rct::RCTTypeBulletproof2: { @@ -2850,6 +2864,7 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, break; } case rct::RCTTypeFull: + case rct::RCTTypeFullBulletproof: { // check all this, either reconstructed (so should really pass), or not { @@ -2916,13 +2931,13 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, // for bulletproofs, check they're only multi-output after v8 if (rct::is_rct_bulletproof(rv.type)) { - if (hf_version < 11) + if (hf_version < 8) { for (const rct::Bulletproof &proof: rv.p.bulletproofs) { if (proof.V.size() > 1) { - MERROR_VER("Multi output bulletproofs are invalid before v11"); + MERROR_VER("Multi output bulletproofs are invalid before v8"); return false; } } diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 36aa7bf4e..0d32c3084 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -753,6 +753,44 @@ namespace cryptonote return false; } + // resolve outPk references in rct txes + // outPk aren't the only thing that need resolving for a fully resolved tx, + // but outPk (1) are needed now to check range proof semantics, and + // (2) do not need access to the blockchain to find data + if (tx.version >= 2) + { + rct::rctSig &rv = tx.rct_signatures; + if (rv.type != rct::RCTTypeBulletproof){ + if (rv.outPk.size() != tx.vout.size()) + { + LOG_PRINT_L1("WRONG TRANSACTION BLOB, Bad outPk size in tx " << tx_hash << ", rejected"); + tvc.m_verifivation_failed = true; + return false; + } + for (size_t n = 0; n < tx.rct_signatures.outPk.size(); ++n) + rv.outPk[n].dest = rct::pk2rct(boost::get(tx.vout[n].target).key); + const bool bulletproof = rct::is_rct_bulletproof(rv.type); + if (bulletproof) + { + if (rct::n_bulletproof_v1_amounts(rv.p.bulletproofs) != tx.vout.size()) + { + LOG_PRINT_L1("WRONG TRANSACTION BLOB, Bad bulletproofs size in tx " << tx_hash << ", rejected"); + tvc.m_verifivation_failed = true; + return false; + } + size_t idx = 0; + for (size_t n = 0; n < rv.p.bulletproofs.size(); ++n) + { + CHECK_AND_ASSERT_MES(rv.p.bulletproofs[n].L.size() >= 6, false, "Bad bulletproofs L size"); // at least 64 bits + const size_t n_amounts = rct::n_bulletproof_v1_amounts(rv.p.bulletproofs[n]); + CHECK_AND_ASSERT_MES(idx + n_amounts <= rv.outPk.size(), false, "Internal error filling out V"); + rv.p.bulletproofs[n].V.clear(); + for (size_t i = 0; i < n_amounts; ++i) + rv.p.bulletproofs[n].V.push_back(rv.outPk[idx++].mask); + } + } + } + } return true; } //----------------------------------------------------------------------------------------------- @@ -811,6 +849,7 @@ namespace cryptonote tx_info[n].result = false; break; case rct::RCTTypeSimple: + case rct::RCTTypeSimpleBulletproof: if (!rct::verRctSemanticsSimple(rv)) { MERROR_VER("rct signature semantics check failed"); @@ -821,6 +860,7 @@ namespace cryptonote } break; case rct::RCTTypeFull: + case rct::RCTTypeFullBulletproof: if (!rct::verRct(rv, true)) { MERROR_VER("rct signature semantics check failed"); diff --git a/src/cryptonote_core/cryptonote_tx_utils.cpp b/src/cryptonote_core/cryptonote_tx_utils.cpp index 51203e8c3..842a7df76 100644 --- a/src/cryptonote_core/cryptonote_tx_utils.cpp +++ b/src/cryptonote_core/cryptonote_tx_utils.cpp @@ -585,7 +585,9 @@ namespace cryptonote crypto::hash tx_prefix_hash; get_transaction_prefix_hash(tx, tx_prefix_hash); rct::ctkeyV outSk; - if (use_simple_rct) + if (rct_config.range_proof_type != rct::RangeProofPaddedBulletproof && use_simple_rct) + tx.rct_signatures = rct::genRctSimple_old(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, msout ? &kLRki : NULL, msout, index, outSk, rct_config, hwdev); + else if (use_simple_rct && rct_config.range_proof_type == rct::RangeProofPaddedBulletproof) tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, amount_keys, msout ? &kLRki : NULL, msout, index, outSk, rct_config, hwdev); else tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, amount_keys, msout ? &kLRki[0] : NULL, msout, sources[0].real_output, outSk, rct_config, hwdev); // same index assumption diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index c31a2073b..f113fbd4b 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -82,8 +82,8 @@ namespace cryptonote uint64_t get_transaction_weight_limit(uint8_t version) { - // from v8, limit a tx to 50% of the minimum block weight - if (version >= 8) + // from v12, limit a tx to 50% of the minimum block weight + if (version >= 12) return get_min_block_weight(version) / 2 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; else return get_min_block_weight(version) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE; diff --git a/src/ringct/CMakeLists.txt b/src/ringct/CMakeLists.txt index 29f166a3b..1c5169a93 100644 --- a/src/ringct/CMakeLists.txt +++ b/src/ringct/CMakeLists.txt @@ -31,7 +31,8 @@ set(ringct_basic_sources rctTypes.cpp rctCryptoOps.c multiexp.cc - bulletproofs.cc) + bulletproofs.cc + bulletproofs2.cc) set(ringct_basic_private_headers rctOps.h diff --git a/src/ringct/bulletproofs.h b/src/ringct/bulletproofs.h index b86202ccc..75b86d6f3 100644 --- a/src/ringct/bulletproofs.h +++ b/src/ringct/bulletproofs.h @@ -42,9 +42,16 @@ Bulletproof bulletproof_PROVE(const rct::key &v, const rct::key &gamma); Bulletproof bulletproof_PROVE(uint64_t v, const rct::key &gamma); Bulletproof bulletproof_PROVE(const rct::keyV &v, const rct::keyV &gamma); Bulletproof bulletproof_PROVE(const std::vector &v, const rct::keyV &gamma); +Bulletproof bulletproof_PROVE_old(const rct::key &v, const rct::key &gamma); +Bulletproof bulletproof_PROVE_old(uint64_t v, const rct::key &gamma); +Bulletproof bulletproof_PROVE_old(const rct::keyV &v, const rct::keyV &gamma); +Bulletproof bulletproof_PROVE_old(const std::vector &v, const rct::keyV &gamma); bool bulletproof_VERIFY(const Bulletproof &proof); bool bulletproof_VERIFY(const std::vector &proofs); bool bulletproof_VERIFY(const std::vector &proofs); +bool bulletproof_VERIFY_old(const Bulletproof &proof); +bool bulletproof_VERIFY_old(const std::vector &proofs); +bool bulletproof_VERIFY_old(const std::vector &proofs); } diff --git a/src/ringct/bulletproofs2.cc b/src/ringct/bulletproofs2.cc new file mode 100644 index 000000000..b2d63638f --- /dev/null +++ b/src/ringct/bulletproofs2.cc @@ -0,0 +1,1160 @@ +// Copyright (c) 2017-2018, The Monero And Italo Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Adapted from Java code by Sarang Noether + +#include +#include +#include +#include "misc_log_ex.h" +#include "common/perf_timer.h" +extern "C" +{ +#include "crypto/crypto-ops.h" +} +#include "rctOps.h" +#include "multiexp.h" +#include "bulletproofs.h" +#include + +#undef MONERO_DEFAULT_LOG_CATEGORY +#define MONERO_DEFAULT_LOG_CATEGORY "bulletproofs" + +//#define DEBUG_BP + +#define PERF_TIMER_START_BP(x) PERF_TIMER_START_UNIT(x, 1000000) + +namespace rct +{ + +static rct::key vector_exponent(const rct::keyV &a, const rct::keyV &b); +static rct::keyV vector_powers(const rct::key &x, size_t n); +static rct::keyV vector_dup(const rct::key &x, size_t n); +static rct::key inner_product(const rct::keyV &a, const rct::keyV &b); + +static constexpr size_t maxN = 64; +static constexpr size_t maxM = 16; +static rct::key Hi[maxN*maxM], Gi[maxN*maxM]; +static ge_p3 Hi_p3[maxN*maxM], Gi_p3[maxN*maxM]; +static ge_dsmp Gprecomp[maxN*maxM], Hprecomp[maxN*maxM]; +static std::shared_ptr HiGi_cache; +static const rct::key TWO = { {0x02, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 , 0x00, 0x00, 0x00,0x00 } }; +static const rct::keyV oneN = vector_dup(rct::identity(), maxN); +static const rct::keyV twoN = vector_powers(TWO, maxN); +static const rct::key ip12 = inner_product(oneN, twoN); +static boost::mutex init_mutex; + +static inline rct::key multiexp(const std::vector &data, bool HiGi) +{ + static const size_t STEP = getenv("STRAUS_STEP") ? atoi(getenv("STRAUS_STEP")) : 0; + if (HiGi || data.size() < 1000) + return straus(data, HiGi ? HiGi_cache: NULL, STEP); + else + return bos_coster_heap_conv_robust(data); +} + +//addKeys3acc_p3 +//aAbB += a*A + b*B where a, b are scalars, A, B are curve points +//A and B must be input after applying "precomp" +static void addKeys3acc_p3(ge_p3 *aAbB, const key &a, const ge_dsmp A, const key &b, const ge_dsmp B) +{ + ge_p3 rv; + ge_p1p1 p1; + ge_p2 p2; + ge_double_scalarmult_precomp_vartime2_p3(&rv, a.bytes, A, b.bytes, B); + ge_cached cached; + ge_p3_to_cached(&cached, aAbB); + ge_add(&p1, &rv, &cached); + ge_p1p1_to_p3(aAbB, &p1); +} + +static void addKeys_acc_p3(ge_p3 *acc_p3, const rct::key &a, const rct::key &point) +{ + ge_p3 p3; + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&p3, point.bytes) == 0, "ge_frombytes_vartime failed"); + ge_scalarmult_p3(&p3, a.bytes, &p3); + ge_cached cached; + ge_p3_to_cached(&cached, acc_p3); + ge_p1p1 p1; + ge_add(&p1, &p3, &cached); + ge_p1p1_to_p3(acc_p3, &p1); +} + +static rct::key get_exponent(const rct::key &base, size_t idx) +{ + static const std::string salt("bulletproof"); + std::string hashed = std::string((const char*)base.bytes, sizeof(base)) + salt + tools::get_varint_data(idx); + return rct::hashToPoint(rct::hash2rct(crypto::cn_fast_hash(hashed.data(), hashed.size()))); +} + +static void init_exponents() +{ + boost::lock_guard lock(init_mutex); + + static bool init_done = false; + if (init_done) + return; + std::vector data; + for (size_t i = 0; i < maxN*maxM; ++i) + { + Hi[i] = get_exponent(rct::H, i * 2); + rct::precomp(Hprecomp[i], Hi[i]); + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&Hi_p3[i], Hi[i].bytes) == 0, "ge_frombytes_vartime failed"); + Gi[i] = get_exponent(rct::H, i * 2 + 1); + rct::precomp(Gprecomp[i], Gi[i]); + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&Gi_p3[i], Gi[i].bytes) == 0, "ge_frombytes_vartime failed"); + + data.push_back({rct::zero(), Gi[i]}); + data.push_back({rct::zero(), Hi[i]}); + } + HiGi_cache = straus_init_cache(data); + size_t cache_size = (sizeof(Hi)+sizeof(Hprecomp)+sizeof(Hi_p3))*2 + straus_get_cache_size(HiGi_cache); + MINFO("cache size: " << cache_size/1024 << " kB"); + init_done = true; +} + +/* Given two scalar arrays, construct a vector commitment */ +static rct::key vector_exponent(const rct::keyV &a, const rct::keyV &b) +{ + CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); + CHECK_AND_ASSERT_THROW_MES(a.size() <= maxN*maxM, "Incompatible sizes of a and maxN"); + + std::vector multiexp_data; + multiexp_data.reserve(a.size()*2); + for (size_t i = 0; i < a.size(); ++i) + { + multiexp_data.emplace_back(a[i], Gi_p3[i]); + multiexp_data.emplace_back(b[i], Hi_p3[i]); + } + return multiexp(multiexp_data, true); +} + +/* Compute a custom vector-scalar commitment */ +static rct::key vector_exponent_custom(const rct::keyV &A, const rct::keyV &B, const rct::keyV &a, const rct::keyV &b) +{ + CHECK_AND_ASSERT_THROW_MES(A.size() == B.size(), "Incompatible sizes of A and B"); + CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); + CHECK_AND_ASSERT_THROW_MES(a.size() == A.size(), "Incompatible sizes of a and A"); + CHECK_AND_ASSERT_THROW_MES(a.size() <= maxN*maxM, "Incompatible sizes of a and maxN"); + + std::vector multiexp_data; + multiexp_data.reserve(a.size()*2); + for (size_t i = 0; i < a.size(); ++i) + { + multiexp_data.resize(multiexp_data.size() + 1); + multiexp_data.back().scalar = a[i]; + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&multiexp_data.back().point, A[i].bytes) == 0, "ge_frombytes_vartime failed"); + multiexp_data.resize(multiexp_data.size() + 1); + multiexp_data.back().scalar = b[i]; + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&multiexp_data.back().point, B[i].bytes) == 0, "ge_frombytes_vartime failed"); + } + return multiexp(multiexp_data, false); +} + +/* Given a scalar, construct a vector of powers */ +static rct::keyV vector_powers(const rct::key &x, size_t n) +{ + rct::keyV res(n); + if (n == 0) + return res; + res[0] = rct::identity(); + if (n == 1) + return res; + res[1] = x; + for (size_t i = 2; i < n; ++i) + { + sc_mul(res[i].bytes, res[i-1].bytes, x.bytes); + } + return res; +} + +/* Given a scalar, return the sum of its powers from 0 to n-1 */ +static rct::key vector_power_sum(const rct::key &x, size_t n) +{ + if (n == 0) + return rct::zero(); + rct::key res = rct::identity(); + if (n == 1) + return res; + rct::key prev = x; + for (size_t i = 1; i < n; ++i) + { + if (i > 1) + sc_mul(prev.bytes, prev.bytes, x.bytes); + sc_add(res.bytes, res.bytes, prev.bytes); + } + return res; +} + +/* Given two scalar arrays, construct the inner product */ +static rct::key inner_product(const rct::keyV &a, const rct::keyV &b) +{ + CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); + rct::key res = rct::zero(); + for (size_t i = 0; i < a.size(); ++i) + { + sc_muladd(res.bytes, a[i].bytes, b[i].bytes, res.bytes); + } + return res; +} + +/* Given two scalar arrays, construct the Hadamard product */ +static rct::keyV hadamard(const rct::keyV &a, const rct::keyV &b) +{ + CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); + rct::keyV res(a.size()); + for (size_t i = 0; i < a.size(); ++i) + { + sc_mul(res[i].bytes, a[i].bytes, b[i].bytes); + } + return res; +} + +/* Given two curvepoint arrays, construct the Hadamard product */ +static rct::keyV hadamard2(const rct::keyV &a, const rct::keyV &b) +{ + CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); + rct::keyV res(a.size()); + for (size_t i = 0; i < a.size(); ++i) + { + rct::addKeys(res[i], a[i], b[i]); + } + return res; +} + +/* Add two vectors */ +static rct::keyV vector_add(const rct::keyV &a, const rct::keyV &b) +{ + CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); + rct::keyV res(a.size()); + for (size_t i = 0; i < a.size(); ++i) + { + sc_add(res[i].bytes, a[i].bytes, b[i].bytes); + } + return res; +} + +/* Subtract two vectors */ +static rct::keyV vector_subtract(const rct::keyV &a, const rct::keyV &b) +{ + CHECK_AND_ASSERT_THROW_MES(a.size() == b.size(), "Incompatible sizes of a and b"); + rct::keyV res(a.size()); + for (size_t i = 0; i < a.size(); ++i) + { + sc_sub(res[i].bytes, a[i].bytes, b[i].bytes); + } + return res; +} + +/* Multiply a scalar and a vector */ +static rct::keyV vector_scalar(const rct::keyV &a, const rct::key &x) +{ + rct::keyV res(a.size()); + for (size_t i = 0; i < a.size(); ++i) + { + sc_mul(res[i].bytes, a[i].bytes, x.bytes); + } + return res; +} + +/* Create a vector from copies of a single value */ +static rct::keyV vector_dup(const rct::key &x, size_t N) +{ + return rct::keyV(N, x); +} + +/* Exponentiate a curve vector by a scalar */ +static rct::keyV vector_scalar2(const rct::keyV &a, const rct::key &x) +{ + rct::keyV res(a.size()); + for (size_t i = 0; i < a.size(); ++i) + { + rct::scalarmultKey(res[i], a[i], x); + } + return res; +} + +/* Get the sum of a vector's elements */ +static rct::key vector_sum(const rct::keyV &a) +{ + rct::key res = rct::zero(); + for (size_t i = 0; i < a.size(); ++i) + { + sc_add(res.bytes, res.bytes, a[i].bytes); + } + return res; +} + +static rct::key switch_endianness(rct::key k) +{ + std::reverse(k.bytes, k.bytes + sizeof(k)); + return k; +} + +/* Compute the inverse of a scalar, the stupid way */ +static rct::key invert(const rct::key &x) +{ + rct::key inv; + + BN_CTX *ctx = BN_CTX_new(); + BIGNUM *X = BN_new(); + BIGNUM *L = BN_new(); + BIGNUM *I = BN_new(); + + BN_bin2bn(switch_endianness(x).bytes, sizeof(rct::key), X); + BN_bin2bn(switch_endianness(rct::curveOrder()).bytes, sizeof(rct::key), L); + + CHECK_AND_ASSERT_THROW_MES(BN_mod_inverse(I, X, L, ctx), "Failed to invert"); + + const int len = BN_num_bytes(I); + CHECK_AND_ASSERT_THROW_MES((size_t)len <= sizeof(rct::key), "Invalid number length"); + inv = rct::zero(); + BN_bn2bin(I, inv.bytes); + std::reverse(inv.bytes, inv.bytes + len); + + BN_free(I); + BN_free(L); + BN_free(X); + BN_CTX_free(ctx); + +#ifdef DEBUG_BP + rct::key tmp; + sc_mul(tmp.bytes, inv.bytes, x.bytes); + CHECK_AND_ASSERT_THROW_MES(tmp == rct::identity(), "invert failed"); +#endif + return inv; +} + +/* Compute the slice of a vector */ +static rct::keyV slice(const rct::keyV &a, size_t start, size_t stop) +{ + CHECK_AND_ASSERT_THROW_MES(start < a.size(), "Invalid start index"); + CHECK_AND_ASSERT_THROW_MES(stop <= a.size(), "Invalid stop index"); + CHECK_AND_ASSERT_THROW_MES(start < stop, "Invalid start/stop indices"); + rct::keyV res(stop - start); + for (size_t i = start; i < stop; ++i) + { + res[i - start] = a[i]; + } + return res; +} + +static rct::key hash_cache_mash(rct::key &hash_cache, const rct::key &mash0, const rct::key &mash1) +{ + rct::keyV data; + data.reserve(3); + data.push_back(hash_cache); + data.push_back(mash0); + data.push_back(mash1); + return hash_cache = rct::hash_to_scalar(data); +} + +static rct::key hash_cache_mash(rct::key &hash_cache, const rct::key &mash0, const rct::key &mash1, const rct::key &mash2) +{ + rct::keyV data; + data.reserve(4); + data.push_back(hash_cache); + data.push_back(mash0); + data.push_back(mash1); + data.push_back(mash2); + return hash_cache = rct::hash_to_scalar(data); +} + +static rct::key hash_cache_mash(rct::key &hash_cache, const rct::key &mash0, const rct::key &mash1, const rct::key &mash2, const rct::key &mash3) +{ + rct::keyV data; + data.reserve(5); + data.push_back(hash_cache); + data.push_back(mash0); + data.push_back(mash1); + data.push_back(mash2); + data.push_back(mash3); + return hash_cache = rct::hash_to_scalar(data); +} + +/* Given a value v (0..2^N-1) and a mask gamma, construct a range proof */ +Bulletproof bulletproof_PROVE_old(const rct::key &sv, const rct::key &gamma) +{ + init_exponents(); + + PERF_TIMER_UNIT(PROVE, 1000000); + + constexpr size_t logN = 6; // log2(64) + constexpr size_t N = 1< 0; ) + { + if (sv[i/8] & (((uint64_t)1)<<(i%8))) + { + aL[i] = rct::identity(); + } + else + { + aL[i] = rct::zero(); + } + sc_sub(aR[i].bytes, aL[i].bytes, rct::identity().bytes); + } + PERF_TIMER_STOP(PROVE_aLaR); + + rct::key hash_cache = rct::hash_to_scalar(V); + + // DEBUG: Test to ensure this recovers the value +#ifdef DEBUG_BP + uint64_t test_aL = 0, test_aR = 0; + for (size_t i = 0; i < N; ++i) + { + if (aL[i] == rct::identity()) + test_aL += ((uint64_t)1)< 1) + { + // PAPER LINE 15 + nprime /= 2; + + // PAPER LINES 16-17 + rct::key cL = inner_product(slice(aprime, 0, nprime), slice(bprime, nprime, bprime.size())); + rct::key cR = inner_product(slice(aprime, nprime, aprime.size()), slice(bprime, 0, nprime)); + + // PAPER LINES 18-19 + L[round] = vector_exponent_custom(slice(Gprime, nprime, Gprime.size()), slice(Hprime, 0, nprime), slice(aprime, 0, nprime), slice(bprime, nprime, bprime.size())); + sc_mul(tmp.bytes, cL.bytes, x_ip.bytes); + rct::addKeys(L[round], L[round], rct::scalarmultKey(rct::H, tmp)); + R[round] = vector_exponent_custom(slice(Gprime, 0, nprime), slice(Hprime, nprime, Hprime.size()), slice(aprime, nprime, aprime.size()), slice(bprime, 0, nprime)); + sc_mul(tmp.bytes, cR.bytes, x_ip.bytes); + rct::addKeys(R[round], R[round], rct::scalarmultKey(rct::H, tmp)); + + // PAPER LINES 21-22 + w[round] = hash_cache_mash(hash_cache, L[round], R[round]); + + // PAPER LINES 24-25 + const rct::key winv = invert(w[round]); + Gprime = hadamard2(vector_scalar2(slice(Gprime, 0, nprime), winv), vector_scalar2(slice(Gprime, nprime, Gprime.size()), w[round])); + Hprime = hadamard2(vector_scalar2(slice(Hprime, 0, nprime), w[round]), vector_scalar2(slice(Hprime, nprime, Hprime.size()), winv)); + + // PAPER LINES 28-29 + aprime = vector_add(vector_scalar(slice(aprime, 0, nprime), w[round]), vector_scalar(slice(aprime, nprime, aprime.size()), winv)); + bprime = vector_add(vector_scalar(slice(bprime, 0, nprime), winv), vector_scalar(slice(bprime, nprime, bprime.size()), w[round])); + + ++round; + } + PERF_TIMER_STOP(PROVE_step4); + + // PAPER LINE 58 (with inclusions from PAPER LINE 8 and PAPER LINE 20) + return Bulletproof(V, A, S, T1, T2, taux, mu, L, R, aprime[0], bprime[0], t); +} + +Bulletproof bulletproof_PROVE_old(uint64_t v, const rct::key &gamma) +{ + // vG + gammaH + PERF_TIMER_START_BP(PROVE_v); + rct::key sv = rct::zero(); + sv.bytes[0] = v & 255; + sv.bytes[1] = (v >> 8) & 255; + sv.bytes[2] = (v >> 16) & 255; + sv.bytes[3] = (v >> 24) & 255; + sv.bytes[4] = (v >> 32) & 255; + sv.bytes[5] = (v >> 40) & 255; + sv.bytes[6] = (v >> 48) & 255; + sv.bytes[7] = (v >> 56) & 255; + PERF_TIMER_STOP(PROVE_v); + return bulletproof_PROVE_old(sv, gamma); +} + +/* Given a set of values v (0..2^N-1) and masks gamma, construct a range proof */ +Bulletproof bulletproof_PROVE_old(const rct::keyV &sv, const rct::keyV &gamma) +{ + CHECK_AND_ASSERT_THROW_MES(sv.size() == gamma.size(), "Incompatible sizes of sv and gamma"); + CHECK_AND_ASSERT_THROW_MES(!sv.empty(), "sv is empty"); + + init_exponents(); + + PERF_TIMER_UNIT(PROVE, 1000000); + + constexpr size_t logN = 6; // log2(64) + constexpr size_t N = 1< 0; ) + { + if (j >= sv.size()) + { + aL[j*N+i] = rct::zero(); + } + else if (sv[j][i/8] & (((uint64_t)1)<<(i%8))) + { + aL[j*N+i] = rct::identity(); + } + else + { + aL[j*N+i] = rct::zero(); + } + sc_sub(aR[j*N+i].bytes, aL[j*N+i].bytes, rct::identity().bytes); + } + } + PERF_TIMER_STOP(PROVE_aLaR); + + rct::key hash_cache = rct::hash_to_scalar(V); + + // DEBUG: Test to ensure this recovers the value +#ifdef DEBUG_BP + for (size_t j = 0; j < M; ++j) + { + uint64_t test_aL = 0, test_aR = 0; + for (size_t i = 0; i < N; ++i) + { + if (aL[j*N+i] == rct::identity()) + test_aL += ((uint64_t)1)<= (j-1)*N && i < j*N) + { + CHECK_AND_ASSERT_THROW_MES(1+j < zpow.size(), "invalid zpow index"); + CHECK_AND_ASSERT_THROW_MES(i-(j-1)*N < twoN.size(), "invalid twoN index"); + sc_muladd(zero_twos[i].bytes, zpow[1+j].bytes, twoN[i-(j-1)*N].bytes, zero_twos[i].bytes); + } + } + } + + rct::keyV r0 = vector_add(aR, zMN); + const auto yMN = vector_powers(y, MN); + r0 = hadamard(r0, yMN); + r0 = vector_add(r0, zero_twos); + rct::keyV r1 = hadamard(yMN, sR); + + // Polynomial construction before PAPER LINE 46 + rct::key t1_1 = inner_product(l0, r1); + rct::key t1_2 = inner_product(l1, r0); + rct::key t1; + sc_add(t1.bytes, t1_1.bytes, t1_2.bytes); + rct::key t2 = inner_product(l1, r1); + + PERF_TIMER_STOP(PROVE_step1); + + PERF_TIMER_START_BP(PROVE_step2); + // PAPER LINES 47-48 + rct::key tau1 = rct::skGen(), tau2 = rct::skGen(); + + rct::key T1 = rct::addKeys(rct::scalarmultKey(rct::H, t1), rct::scalarmultBase(tau1)); + rct::key T2 = rct::addKeys(rct::scalarmultKey(rct::H, t2), rct::scalarmultBase(tau2)); + + // PAPER LINES 49-51 + rct::key x = hash_cache_mash(hash_cache, z, T1, T2); + + // PAPER LINES 52-53 + rct::key taux; + sc_mul(taux.bytes, tau1.bytes, x.bytes); + rct::key xsq; + sc_mul(xsq.bytes, x.bytes, x.bytes); + sc_muladd(taux.bytes, tau2.bytes, xsq.bytes, taux.bytes); + for (size_t j = 1; j <= sv.size(); ++j) + { + CHECK_AND_ASSERT_THROW_MES(j+1 < zpow.size(), "invalid zpow index"); + sc_muladd(taux.bytes, zpow[j+1].bytes, gamma[j-1].bytes, taux.bytes); + } + rct::key mu; + sc_muladd(mu.bytes, x.bytes, rho.bytes, alpha.bytes); + + // PAPER LINES 54-57 + rct::keyV l = l0; + l = vector_add(l, vector_scalar(l1, x)); + rct::keyV r = r0; + r = vector_add(r, vector_scalar(r1, x)); + PERF_TIMER_STOP(PROVE_step2); + + PERF_TIMER_START_BP(PROVE_step3); + rct::key t = inner_product(l, r); + + // DEBUG: Test if the l and r vectors match the polynomial forms +#ifdef DEBUG_BP + rct::key test_t; + const rct::key t0 = inner_product(l0, r0); + sc_muladd(test_t.bytes, t1.bytes, x.bytes, t0.bytes); + sc_muladd(test_t.bytes, t2.bytes, xsq.bytes, test_t.bytes); + CHECK_AND_ASSERT_THROW_MES(test_t == t, "test_t check failed"); +#endif + + // PAPER LINES 32-33 + rct::key x_ip = hash_cache_mash(hash_cache, x, taux, mu, t); + + // These are used in the inner product rounds + size_t nprime = MN; + rct::keyV Gprime(MN); + rct::keyV Hprime(MN); + rct::keyV aprime(MN); + rct::keyV bprime(MN); + const rct::key yinv = invert(y); + rct::key yinvpow = rct::identity(); + for (size_t i = 0; i < MN; ++i) + { + Gprime[i] = Gi[i]; + Hprime[i] = scalarmultKey(Hi[i], yinvpow); + sc_mul(yinvpow.bytes, yinvpow.bytes, yinv.bytes); + aprime[i] = l[i]; + bprime[i] = r[i]; + } + rct::keyV L(logMN); + rct::keyV R(logMN); + int round = 0; + rct::keyV w(logMN); // this is the challenge x in the inner product protocol + PERF_TIMER_STOP(PROVE_step3); + + PERF_TIMER_START_BP(PROVE_step4); + // PAPER LINE 13 + while (nprime > 1) + { + // PAPER LINE 15 + nprime /= 2; + + // PAPER LINES 16-17 + rct::key cL = inner_product(slice(aprime, 0, nprime), slice(bprime, nprime, bprime.size())); + rct::key cR = inner_product(slice(aprime, nprime, aprime.size()), slice(bprime, 0, nprime)); + + // PAPER LINES 18-19 + L[round] = vector_exponent_custom(slice(Gprime, nprime, Gprime.size()), slice(Hprime, 0, nprime), slice(aprime, 0, nprime), slice(bprime, nprime, bprime.size())); + sc_mul(tmp.bytes, cL.bytes, x_ip.bytes); + rct::addKeys(L[round], L[round], rct::scalarmultKey(rct::H, tmp)); + R[round] = vector_exponent_custom(slice(Gprime, 0, nprime), slice(Hprime, nprime, Hprime.size()), slice(aprime, nprime, aprime.size()), slice(bprime, 0, nprime)); + sc_mul(tmp.bytes, cR.bytes, x_ip.bytes); + rct::addKeys(R[round], R[round], rct::scalarmultKey(rct::H, tmp)); + + // PAPER LINES 21-22 + w[round] = hash_cache_mash(hash_cache, L[round], R[round]); + + // PAPER LINES 24-25 + const rct::key winv = invert(w[round]); + Gprime = hadamard2(vector_scalar2(slice(Gprime, 0, nprime), winv), vector_scalar2(slice(Gprime, nprime, Gprime.size()), w[round])); + Hprime = hadamard2(vector_scalar2(slice(Hprime, 0, nprime), w[round]), vector_scalar2(slice(Hprime, nprime, Hprime.size()), winv)); + + // PAPER LINES 28-29 + aprime = vector_add(vector_scalar(slice(aprime, 0, nprime), w[round]), vector_scalar(slice(aprime, nprime, aprime.size()), winv)); + bprime = vector_add(vector_scalar(slice(bprime, 0, nprime), winv), vector_scalar(slice(bprime, nprime, bprime.size()), w[round])); + + ++round; + } + PERF_TIMER_STOP(PROVE_step4); + + // PAPER LINE 58 (with inclusions from PAPER LINE 8 and PAPER LINE 20) + return Bulletproof(V, A, S, T1, T2, taux, mu, L, R, aprime[0], bprime[0], t); +} + +Bulletproof bulletproof_PROVE_old(const std::vector &v, const rct::keyV &gamma) +{ + CHECK_AND_ASSERT_THROW_MES(v.size() == gamma.size(), "Incompatible sizes of v and gamma"); + + // vG + gammaH + PERF_TIMER_START_BP(PROVE_v); + rct::keyV sv(v.size()); + for (size_t i = 0; i < v.size(); ++i) + { + sv[i] = rct::zero(); + sv[i].bytes[0] = v[i] & 255; + sv[i].bytes[1] = (v[i] >> 8) & 255; + sv[i].bytes[2] = (v[i] >> 16) & 255; + sv[i].bytes[3] = (v[i] >> 24) & 255; + sv[i].bytes[4] = (v[i] >> 32) & 255; + sv[i].bytes[5] = (v[i] >> 40) & 255; + sv[i].bytes[6] = (v[i] >> 48) & 255; + sv[i].bytes[7] = (v[i] >> 56) & 255; + } + PERF_TIMER_STOP(PROVE_v); + return bulletproof_PROVE_old(sv, gamma); +} + +/* Given a range proof, determine if it is valid */ +bool bulletproof_VERIFY_old(const std::vector &proofs) +{ + init_exponents(); + + PERF_TIMER_START_BP(VERIFY); + + // sanity and figure out which proof is longest + size_t max_length = 0; + for (const Bulletproof *p: proofs) + { + const Bulletproof &proof = *p; + CHECK_AND_ASSERT_MES(proof.V.size() >= 1, false, "V does not have at least one element"); + CHECK_AND_ASSERT_MES(proof.L.size() == proof.R.size(), false, "Mismatched L and R sizes"); + CHECK_AND_ASSERT_MES(proof.L.size() > 0, false, "Empty proof"); + + max_length = std::max(max_length, proof.L.size()); + } + CHECK_AND_ASSERT_MES(max_length < 32, false, "At least one proof is too large"); + size_t maxMN = 1u << max_length; + + const size_t logN = 6; + const size_t N = 1 << logN; + rct::key tmp; + + // setup weighted aggregates + rct::key Z0 = rct::identity(); + rct::key z1 = rct::zero(); + rct::key Z2 = rct::identity(); + rct::key z3 = rct::zero(); + rct::keyV z4(maxMN, rct::zero()), z5(maxMN, rct::zero()); + for (const Bulletproof *p: proofs) + { + const Bulletproof &proof = *p; + + size_t M, logM; + for (logM = 0; (M = 1< multiexp_data; + multiexp_data.reserve(3+proof.V.size()); + multiexp_data.emplace_back(tmp, rct::H); + for (size_t j = 0; j < proof.V.size(); j++) + { + multiexp_data.emplace_back(zpow[j+2], proof.V[j]); + } + multiexp_data.emplace_back(x, proof.T1); + rct::key xsq; + sc_mul(xsq.bytes, x.bytes, x.bytes); + multiexp_data.emplace_back(xsq, proof.T2); + L61Right = multiexp(multiexp_data, false); + PERF_TIMER_STOP(VERIFY_line_61rl_new); + } + else + { + PERF_TIMER_START_BP(VERIFY_line_61rl_old); + sc_muladd(tmp.bytes, z.bytes, ip1y.bytes, k.bytes); + L61Right = rct::scalarmultKey(rct::H, tmp); + ge_p3 L61Right_p3; + CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&L61Right_p3, L61Right.bytes) == 0, "ge_frombytes_vartime failed"); + for (size_t j = 0; j+1 < proof.V.size(); j += 2) + { + CHECK_AND_ASSERT_MES(j+2+1 < zpow.size(), false, "invalid zpow index"); + ge_dsmp precomp0, precomp1; + rct::precomp(precomp0, j < proof.V.size() ? proof.V[j] : rct::identity()); + rct::precomp(precomp1, j+1 < proof.V.size() ? proof.V[j+1] : rct::identity()); + rct::addKeys3acc_p3(&L61Right_p3, zpow[j+2], precomp0, zpow[j+2+1], precomp1); + } + for (size_t j = proof.V.size() & 0xfffffffe; j < M; j++) + { + CHECK_AND_ASSERT_MES(j+2 < zpow.size(), false, "invalid zpow index"); + // faster equivalent to: + // tmp = rct::scalarmultKey(j < proof.V.size() ? proof.V[j] : rct::identity(), zpow[j+2]); + // rct::addKeys(L61Right, L61Right, tmp); + if (j < proof.V.size()) + addKeys_acc_p3(&L61Right_p3, zpow[j+2], proof.V[j]); + } + + addKeys_acc_p3(&L61Right_p3, x, proof.T1); + + rct::key xsq; + sc_mul(xsq.bytes, x.bytes, x.bytes); + addKeys_acc_p3(&L61Right_p3, xsq, proof.T2); + ge_p3_tobytes(L61Right.bytes, &L61Right_p3); + PERF_TIMER_STOP(VERIFY_line_61rl_old); + } + + if (!(L61Right == L61Left)) + { + MERROR("Verification failure at step 1"); + return false; + } + + PERF_TIMER_START_BP(VERIFY_line_62); + // PAPER LINE 62 + rct::addKeys(Z0, Z0, rct::scalarmultKey(rct::addKeys(proof.A, rct::scalarmultKey(proof.S, x)), weight)); + PERF_TIMER_STOP(VERIFY_line_62); + + // Compute the number of rounds for the inner product + const size_t rounds = logM+logN; + CHECK_AND_ASSERT_MES(rounds > 0, false, "Zero rounds"); + + PERF_TIMER_START_BP(VERIFY_line_21_22); + // PAPER LINES 21-22 + // The inner product challenges are computed per round + rct::keyV w(rounds); + for (size_t i = 0; i < rounds; ++i) + { + w[i] = hash_cache_mash(hash_cache, proof.L[i], proof.R[i]); + } + PERF_TIMER_STOP(VERIFY_line_21_22); + + PERF_TIMER_START_BP(VERIFY_line_24_25); + // Basically PAPER LINES 24-25 + // Compute the curvepoints from G[i] and H[i] + rct::key yinvpow = rct::identity(); + rct::key ypow = rct::identity(); + + PERF_TIMER_START_BP(VERIFY_line_24_25_invert); + const rct::key yinv = invert(y); + rct::keyV winv(rounds); + for (size_t i = 0; i < rounds; ++i) + winv[i] = invert(w[i]); + PERF_TIMER_STOP(VERIFY_line_24_25_invert); + + for (size_t i = 0; i < MN; ++i) + { + // Convert the index to binary IN REVERSE and construct the scalar exponent + rct::key g_scalar = proof.a; + rct::key h_scalar; + sc_mul(h_scalar.bytes, proof.b.bytes, yinvpow.bytes); + + for (size_t j = rounds; j-- > 0; ) + { + size_t J = w.size() - j - 1; + + if ((i & (((size_t)1)< multiexp_data; + multiexp_data.reserve(2*rounds); + + sc_muladd(z1.bytes, proof.mu.bytes, weight.bytes, z1.bytes); + for (size_t i = 0; i < rounds; ++i) + { + sc_mul(tmp.bytes, w[i].bytes, w[i].bytes); + multiexp_data.emplace_back(tmp, proof.L[i]); + sc_mul(tmp.bytes, winv[i].bytes, winv[i].bytes); + multiexp_data.emplace_back(tmp, proof.R[i]); + } + rct::key acc = multiexp(multiexp_data, false); + rct::addKeys(Z2, Z2, rct::scalarmultKey(acc, weight)); + sc_mulsub(tmp.bytes, proof.a.bytes, proof.b.bytes, proof.t.bytes); + sc_mul(tmp.bytes, tmp.bytes, x_ip.bytes); + sc_muladd(z3.bytes, tmp.bytes, weight.bytes, z3.bytes); + PERF_TIMER_STOP(VERIFY_line_26_new); + } + + // now check all proofs at once + PERF_TIMER_START_BP(VERIFY_step2_check); + rct::key Y = Z0; + sc_sub(tmp.bytes, rct::zero().bytes, z1.bytes); + rct::addKeys(Y, Y, rct::scalarmultBase(tmp)); + rct::addKeys(Y, Y, Z2); + rct::addKeys(Y, Y, rct::scalarmultKey(rct::H, z3)); + + std::vector multiexp_data; + multiexp_data.reserve(2 * maxMN); + for (size_t i = 0; i < maxMN; ++i) + { + sc_sub(tmp.bytes, rct::zero().bytes, z4[i].bytes); + multiexp_data.emplace_back(tmp, Gi_p3[i]); + sc_sub(tmp.bytes, rct::zero().bytes, z5[i].bytes); + multiexp_data.emplace_back(tmp, Hi_p3[i]); + } + rct::addKeys(Y, Y, multiexp(multiexp_data, true)); + PERF_TIMER_STOP(VERIFY_step2_check); + + if (!(Y == rct::identity())) + { + MERROR("Verification failure at step 2"); + return false; + } + + PERF_TIMER_STOP(VERIFY); + return true; +} + +bool bulletproof_VERIFY_old(const std::vector &proofs) +{ + std::vector proof_pointers; + for (const Bulletproof &proof: proofs) + proof_pointers.push_back(&proof); + return bulletproof_VERIFY_old(proof_pointers); +} + +bool bulletproof_VERIFY_old(const Bulletproof &proof) +{ + std::vector proofs; + proofs.push_back(&proof); + return bulletproof_VERIFY_old(proofs); +} + +} diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 2208f04d1..9d9004c55 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -57,6 +57,23 @@ namespace rct { return proof; } + Bulletproof proveRangeBulletproof_old(key &C, key &mask, uint64_t amount) + { + mask = rct::skGen(); + Bulletproof proof = bulletproof_PROVE_old(amount, mask); + CHECK_AND_ASSERT_THROW_MES(proof.V.size() == 1, "V has not exactly one element"); + C = proof.V[0]; + return proof; + } + + Bulletproof proveRangeBulletproof_old(keyV &C, keyV &masks, const std::vector &amounts) + { + masks = rct::skvGen(amounts.size()); + Bulletproof proof = bulletproof_PROVE_old(amounts, masks); + CHECK_AND_ASSERT_THROW_MES(proof.V.size() == amounts.size(), "V does not have the expected size"); + C = proof.V; + return proof; + } bool verBulletproof(const Bulletproof &proof) { try { return bulletproof_VERIFY(proof); } @@ -71,6 +88,19 @@ namespace rct { catch (...) { return false; } } + bool verBulletproof_old(const Bulletproof &proof) + { + try { return bulletproof_VERIFY_old(proof); } + // we can get deep throws from ge_frombytes_vartime if input isn't valid + catch (...) { return false; } + } + + bool verBulletproof_old(const std::vector &proofs) + { + try { return bulletproof_VERIFY_old(proofs); } + // we can get deep throws from ge_frombytes_vartime if input isn't valid + catch (...) { return false; } + } //Borromean (c.f. gmax/andytoshi's paper) boroSig genBorromean(const key64 x, const key64 P1, const key64 P2, const bits indices) { key64 L[2], alpha; @@ -385,7 +415,7 @@ namespace rct { hashes.push_back(hash2rct(h)); keyV kv; - if (rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2) + if (rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeSimpleBulletproof || rv.type == RCTTypeFullBulletproof) { kv.reserve((6*2+9) * rv.p.bulletproofs.size()); for (const auto &p: rv.p.bulletproofs) @@ -647,6 +677,7 @@ namespace rct { // Note: For txn fees, the last index in the amounts vector should contain that // Thus the amounts vector will be "one" longer than the destinations vectort rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector & amounts, const ctkeyM &mixRing, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, unsigned int index, ctkeyV &outSk, const RCTConfig &rct_config, hw::device &hwdev) { + const bool bulletproof = rct_config.range_proof_type != RangeProofBorromean; CHECK_AND_ASSERT_THROW_MES(amounts.size() == destinations.size() || amounts.size() == destinations.size() + 1, "Different number of amounts/destinations"); CHECK_AND_ASSERT_THROW_MES(amount_keys.size() == destinations.size(), "Different number of amount_keys/destinations"); CHECK_AND_ASSERT_THROW_MES(index < mixRing.size(), "Bad index into mixRing"); @@ -656,10 +687,11 @@ namespace rct { CHECK_AND_ASSERT_THROW_MES((kLRki && msout) || (!kLRki && !msout), "Only one of kLRki/msout is present"); rctSig rv; - rv.type = RCTTypeFull; + rv.type = bulletproof ? RCTTypeFullBulletproof : RCTTypeFull; rv.message = message; rv.outPk.resize(destinations.size()); - rv.p.rangeSigs.resize(destinations.size()); + if (!bulletproof) + rv.p.rangeSigs.resize(destinations.size()); rv.ecdhInfo.resize(destinations.size()); size_t i = 0; @@ -669,10 +701,45 @@ namespace rct { //add destination to sig rv.outPk[i].dest = copy(destinations[i]); //compute range proof - rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, amounts[i]); + if (!bulletproof) + { + rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, amounts[i]); + #ifdef DBG CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof"); #endif + } + } + rv.p.bulletproofs.clear(); + if (bulletproof) + { + std::vector proof_amounts; + size_t amounts_proved = 0; + while (amounts_proved < amounts.size()) + { + size_t batch_size = 1; + if (rct_config.range_proof_type == RangeProofMultiOutputBulletproof) + while (batch_size * 2 + amounts_proved <= amounts.size()) + batch_size *= 2; + rct::keyV C, masks; + std::vector batch_amounts(batch_size); + for (i = 0; i < batch_size; ++i) + batch_amounts[i] = amounts[i + amounts_proved]; + rv.p.bulletproofs.push_back(proveRangeBulletproof_old(C, masks, batch_amounts)); + #ifdef DBG + CHECK_AND_ASSERT_THROW_MES(verBulletproof_old(rv.p.bulletproofs.back()), "verBulletproof failed on newly created proof"); + #endif + for (i = 0; i < batch_size; ++i) + { + rv.outPk[i + amounts_proved].mask = C[i]; + outSk[i + amounts_proved].mask = masks[i]; + } + amounts_proved += batch_size; + } + } + + for (i = 0; i < outSk.size(); ++i) + { //mask amount and mask rv.ecdhInfo[i].mask = copy(outSk[i].mask); rv.ecdhInfo[i].amount = d2h(amounts[i]); @@ -845,6 +912,103 @@ namespace rct { } return genRctSimple(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, amount_keys, kLRki, msout, index, outSk, rct_config, hwdev); } + //RCT simple + //for post-rct only + rctSig genRctSimple_old(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector &inamounts, const vector &outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector *kLRki, multisig_out *msout, const std::vector & index, ctkeyV &outSk, const RCTConfig &rct_config, hw::device &hwdev) { + const bool bulletproof = rct_config.range_proof_type != RangeProofBorromean; + CHECK_AND_ASSERT_THROW_MES(inamounts.size() > 0, "Empty inamounts"); + CHECK_AND_ASSERT_THROW_MES(inamounts.size() == inSk.size(), "Different number of inamounts/inSk"); + CHECK_AND_ASSERT_THROW_MES(outamounts.size() == destinations.size(), "Different number of amounts/destinations"); + CHECK_AND_ASSERT_THROW_MES(amount_keys.size() == destinations.size(), "Different number of amount_keys/destinations"); + CHECK_AND_ASSERT_THROW_MES(index.size() == inSk.size(), "Different number of index/inSk"); + CHECK_AND_ASSERT_THROW_MES(mixRing.size() == inSk.size(), "Different number of mixRing/inSk"); + for (size_t n = 0; n < mixRing.size(); ++n) { + CHECK_AND_ASSERT_THROW_MES(index[n] < mixRing[n].size(), "Bad index into mixRing"); + } + CHECK_AND_ASSERT_THROW_MES((kLRki && msout) || (!kLRki && !msout), "Only one of kLRki/msout is present"); + if (kLRki && msout) { + CHECK_AND_ASSERT_THROW_MES(kLRki->size() == inamounts.size(), "Mismatched kLRki/inamounts sizes"); + } + + rctSig rv; + rv.type = bulletproof ? RCTTypeSimpleBulletproof : RCTTypeSimple; + rv.message = message; + rv.outPk.resize(destinations.size()); + if (bulletproof) + rv.p.bulletproofs.resize(destinations.size()); + else + rv.p.rangeSigs.resize(destinations.size()); + rv.ecdhInfo.resize(destinations.size()); + + size_t i; + keyV masks(destinations.size()); //sk mask.. + outSk.resize(destinations.size()); + key sumout = zero(); + for (i = 0; i < destinations.size(); i++) { + + //add destination to sig + rv.outPk[i].dest = copy(destinations[i]); + //compute range proof + if (bulletproof) + rv.p.bulletproofs[i] = proveRangeBulletproof_old(rv.outPk[i].mask, outSk[i].mask, outamounts[i]); + else + rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, outamounts[i]); + #ifdef DBG + if (bulletproof) + CHECK_AND_ASSERT_THROW_MES(verBulletproof_old(rv.p.bulletproofs[i]), "verBulletproof failed on newly created proof"); + else + CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof"); + #endif + + sc_add(sumout.bytes, outSk[i].mask.bytes, sumout.bytes); + + //mask amount and mask + rv.ecdhInfo[i].mask = copy(outSk[i].mask); + rv.ecdhInfo[i].amount = d2h(outamounts[i]); + hwdev.ecdhEncode(rv.ecdhInfo[i], amount_keys[i], rv.type == RCTTypeSimpleBulletproof); + } + + //set txn fee + rv.txnFee = txnFee; +// TODO: unused ?? +// key txnFeeKey = scalarmultH(d2h(rv.txnFee)); + rv.mixRing = mixRing; + keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts; + pseudoOuts.resize(inamounts.size()); + rv.p.MGs.resize(inamounts.size()); + key sumpouts = zero(); //sum pseudoOut masks + keyV a(inamounts.size()); + for (i = 0 ; i < inamounts.size() - 1; i++) { + skGen(a[i]); + sc_add(sumpouts.bytes, a[i].bytes, sumpouts.bytes); + genC(pseudoOuts[i], a[i], inamounts[i]); + } + rv.mixRing = mixRing; + sc_sub(a[i].bytes, sumout.bytes, sumpouts.bytes); + genC(pseudoOuts[i], a[i], inamounts[i]); + DP(pseudoOuts[i]); + + key full_message = get_pre_mlsag_hash(rv,hwdev); + if (msout) + msout->c.resize(inamounts.size()); + for (i = 0 ; i < inamounts.size(); i++) { + rv.p.MGs[i] = proveRctMGSimple(full_message, rv.mixRing[i], inSk[i], a[i], pseudoOuts[i], kLRki ? &(*kLRki)[i]: NULL, msout ? &msout->c[i] : NULL, index[i], hwdev); + } + return rv; + } + + rctSig genRctSimple_old(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector &inamounts, const vector &outamounts, const keyV &amount_keys, const std::vector *kLRki, multisig_out *msout, xmr_amount txnFee, unsigned int mixin, const RCTConfig &rct_config, hw::device &hwdev) { + std::vector index; + index.resize(inPk.size()); + ctkeyM mixRing; + ctkeyV outSk; + mixRing.resize(inPk.size()); + for (size_t i = 0; i < inPk.size(); ++i) { + mixRing[i].resize(mixin+1); + index[i] = populateFromBlockchainSimple(mixRing[i], inPk[i], mixin); + } + return genRctSimple_old(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, amount_keys, kLRki, msout, index, outSk, rct_config, hwdev); + } //RingCT protocol //genRct: @@ -858,10 +1022,13 @@ namespace rct { // must know the destination private key to find the correct amount, else will return a random number bool verRct(const rctSig & rv, bool semantics) { PERF_TIMER(verRct); - CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "verRct called on non-full rctSig"); + const bool bulletproof = is_rct_bulletproof(rv.type); + CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeFullBulletproof, false, "verRct called on non-full rctSig"); if (semantics) { + if (rv.type == RCTTypeBulletproof) CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs"); + else CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); CHECK_AND_ASSERT_MES(rv.p.MGs.size() == 1, false, "full rctSig has not one MG"); } @@ -876,10 +1043,23 @@ namespace rct { if (semantics) { tools::threadpool& tpool = tools::threadpool::getInstance(); tools::threadpool::waiter waiter; - std::deque results(rv.outPk.size(), false); + std::deque results(bulletproof ? rv.p.bulletproofs.size() : rv.outPk.size(), false); DP("range proofs verified?"); - for (size_t i = 0; i < rv.outPk.size(); i++) - tpool.submit(&waiter, [&, i] { results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); }); + if (bulletproof && rv.type == RCTTypeBulletproof) + { + for (size_t i = 0; i < rv.p.bulletproofs.size(); i++) + tpool.submit(&waiter, [&, i] { results[i] = verBulletproof(rv.p.bulletproofs[i]); }); + } + else if (bulletproof) + { + for (size_t i = 0; i < rv.p.bulletproofs.size(); i++) + tpool.submit(&waiter, [&, i] { results[i] = verBulletproof_old(rv.p.bulletproofs[i]); }); + } + else + { + for (size_t i = 0; i < rv.outPk.size(); i++) + tpool.submit(&waiter, [&, i] { results[i] = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); }); + } waiter.wait(&tpool); for (size_t i = 0; i < results.size(); ++i) { @@ -933,12 +1113,15 @@ namespace rct { { CHECK_AND_ASSERT_MES(rvp, false, "rctSig pointer is NULL"); const rctSig &rv = *rvp; - CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2, + CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeSimpleBulletproof, false, "verRctSemanticsSimple called on non simple rctSig"); const bool bulletproof = is_rct_bulletproof(rv.type); - if (bulletproof) + if (rv.type == RCTTypeBulletproof) { CHECK_AND_ASSERT_MES(rv.outPk.size() == n_bulletproof_amounts(rv.p.bulletproofs), false, "Mismatched sizes of outPk and bulletproofs"); + } + else if (bulletproof) + { CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.p.pseudoOuts and rv.p.MGs"); CHECK_AND_ASSERT_MES(rv.pseudoOuts.empty(), false, "rv.pseudoOuts is not empty"); } @@ -992,12 +1175,23 @@ namespace rct { offset += rv.p.rangeSigs.size(); } } + for (const rctSig *rvp: rvv) + { + const rctSig &rv = *rvp; + if (rv.type != RCTTypeBulletproof){ + if (!proofs.empty() && !verBulletproof_old(proofs)) + { + LOG_PRINT_L1("Aggregate range proof verified failed"); + return false; + } + } else { if (!proofs.empty() && !verBulletproof(proofs)) { LOG_PRINT_L1("Aggregate range proof verified failed"); return false; } - + } + } waiter.wait(&tpool); for (size_t i = 0; i < results.size(); ++i) { if (!results[i]) { @@ -1033,7 +1227,7 @@ namespace rct { { PERF_TIMER(verRctNonSemanticsSimple); - CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2, + CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeSimpleBulletproof, false, "verRctNonSemanticsSimple called on non simple rctSig"); const bool bulletproof = is_rct_bulletproof(rv.type); // semantics check is early, and mixRing/MGs aren't resolved yet @@ -1094,7 +1288,7 @@ namespace rct { // uses the attached ecdh info to find the amounts represented by each output commitment // must know the destination private key to find the correct amount, else will return a random number xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev) { - CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "decodeRct called on non-full rctSig"); + CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeFullBulletproof, false, "decodeRct called on non-full rctSig"); CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index"); CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo"); @@ -1124,7 +1318,7 @@ namespace rct { } xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key &mask, hw::device &hwdev) { - CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2, false, "decodeRct called on non simple rctSig"); + CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeSimpleBulletproof, false, "decodeRct called on non simple rctSig"); CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index"); CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.ecdhInfo.size(), "Mismatched sizes of rv.outPk and rv.ecdhInfo"); @@ -1154,12 +1348,12 @@ namespace rct { } bool signMultisig(rctSig &rv, const std::vector &indices, const keyV &k, const multisig_out &msout, const key &secret_key) { - CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2, + CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull || rv.type == RCTTypeSimple || rv.type == RCTTypeBulletproof || rv.type == RCTTypeBulletproof2 || rv.type == RCTTypeFullBulletproof || rv.type == RCTTypeSimpleBulletproof, false, "unsupported rct type"); CHECK_AND_ASSERT_MES(indices.size() == k.size(), false, "Mismatched k/indices sizes"); CHECK_AND_ASSERT_MES(k.size() == rv.p.MGs.size(), false, "Mismatched k/MGs size"); CHECK_AND_ASSERT_MES(k.size() == msout.c.size(), false, "Mismatched k/msout.c size"); - if (rv.type == RCTTypeFull) + if (rv.type == RCTTypeFull || rv.type == RCTTypeFullBulletproof) { CHECK_AND_ASSERT_MES(rv.p.MGs.size() == 1, false, "MGs not a single element"); } diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index 10a8686ee..7418dcec4 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -123,6 +123,8 @@ namespace rct { rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector & amounts, const keyV &amount_keys, const multisig_kLRki *kLRki, multisig_out *msout, const int mixin, const RCTConfig &rct_config, hw::device &hwdev); rctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector & inamounts, const std::vector & outamounts, const keyV &amount_keys, const std::vector *kLRki, multisig_out *msout, xmr_amount txnFee, unsigned int mixin, const RCTConfig &rct_config, hw::device &hwdev); rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const std::vector & inamounts, const std::vector & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector *kLRki, multisig_out *msout, const std::vector & index, ctkeyV &outSk, const RCTConfig &rct_config, hw::device &hwdev); + rctSig genRctSimple_old(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const std::vector & inamounts, const std::vector & outamounts, const keyV &amount_keys, const std::vector *kLRki, multisig_out *msout, xmr_amount txnFee, unsigned int mixin, const RCTConfig &rct_config, hw::device &hwdev); + rctSig genRctSimple_old(const key & message, const ctkeyV & inSk, const keyV & destinations, const std::vector & inamounts, const std::vector & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector *kLRki, multisig_out *msout, const std::vector & index, ctkeyV &outSk, const RCTConfig &rct_config, hw::device &hwdev); bool verRct(const rctSig & rv, bool semantics); static inline bool verRct(const rctSig & rv) { return verRct(rv, true) && verRct(rv, false); } bool verRctSemanticsSimple(const rctSig & rv); diff --git a/src/ringct/rctTypes.cpp b/src/ringct/rctTypes.cpp index f01e683cb..74bf7ff6b 100644 --- a/src/ringct/rctTypes.cpp +++ b/src/ringct/rctTypes.cpp @@ -216,6 +216,7 @@ namespace rct { switch (type) { case RCTTypeSimple: + case RCTTypeSimpleBulletproof: case RCTTypeBulletproof: case RCTTypeBulletproof2: return true; @@ -228,6 +229,8 @@ namespace rct { { switch (type) { + case RCTTypeSimpleBulletproof: + case RCTTypeFullBulletproof: case RCTTypeBulletproof: case RCTTypeBulletproof2: return true; @@ -236,6 +239,18 @@ namespace rct { } } + bool is_rct_old_bulletproof(int type) + { + switch (type) + { + case RCTTypeSimpleBulletproof: + case RCTTypeFullBulletproof: + return true; + default: + return false; + } + } + bool is_rct_borromean(int type) { switch (type) @@ -248,6 +263,25 @@ namespace rct { } } + size_t n_bulletproof_v1_amounts(const Bulletproof &proof) + { + CHECK_AND_ASSERT_MES(proof.L.size() >= 6, 0, "Invalid bulletproof L size"); + CHECK_AND_ASSERT_MES(proof.L.size() <= 31, 0, "Insane bulletproof L size"); + return 1 << (proof.L.size() - 6); + } + size_t n_bulletproof_v1_amounts(const std::vector &proofs) + { + size_t n = 0; + for (const Bulletproof &proof: proofs) + { + size_t n2 = n_bulletproof_v1_amounts(proof); + CHECK_AND_ASSERT_MES(n2 < std::numeric_limits::max() - n, 0, "Invalid number of bulletproofs"); + if (n2 == 0) + return 0; + n += n2; + } + return n; + } size_t n_bulletproof_amounts(const Bulletproof &proof) { CHECK_AND_ASSERT_MES(proof.L.size() >= 6, 0, "Invalid bulletproof L size"); diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index c6c21ad22..12eb16d13 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -214,8 +214,10 @@ namespace rct { }; size_t n_bulletproof_amounts(const Bulletproof &proof); + size_t n_bulletproof_v1_amounts(const Bulletproof &proof); size_t n_bulletproof_max_amounts(const Bulletproof &proof); size_t n_bulletproof_amounts(const std::vector &proofs); + size_t n_bulletproof_v1_amounts(const std::vector &proofs); size_t n_bulletproof_max_amounts(const std::vector &proofs); //A container to hold all signatures necessary for RingCT @@ -229,8 +231,10 @@ namespace rct { RCTTypeNull = 0, RCTTypeFull = 1, RCTTypeSimple = 2, - RCTTypeBulletproof = 3, - RCTTypeBulletproof2 = 4, + RCTTypeFullBulletproof = 3, + RCTTypeSimpleBulletproof = 4, + RCTTypeBulletproof = 5, + RCTTypeBulletproof2 = 6, }; enum RangeProofType { RangeProofBorromean, RangeProofBulletproof, RangeProofMultiOutputBulletproof, RangeProofPaddedBulletproof }; struct RCTConfig { @@ -253,7 +257,7 @@ namespace rct { FIELD(type) if (type == RCTTypeNull) return true; - if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2) + if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeFullBulletproof && type != RCTTypeSimpleBulletproof) return false; VARINT_FIELD(txnFee) // inputs/outputs not saved, only here for serialization help @@ -326,9 +330,24 @@ namespace rct { { if (type == RCTTypeNull) return true; - if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2) + if (type != RCTTypeFull && type != RCTTypeSimple && type != RCTTypeBulletproof && type != RCTTypeBulletproof2 && type != RCTTypeFullBulletproof && type != RCTTypeSimpleBulletproof) return false; - if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2) + if (type == RCTTypeSimpleBulletproof || type == RCTTypeFullBulletproof) + { + ar.tag("bp"); + ar.begin_array(); + PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, bulletproofs); + if (bulletproofs.size() != outputs) + return false; + for (size_t i = 0; i < outputs; ++i) + { + FIELDS(bulletproofs[i]) + if (outputs - i > 1) + ar.delimit_array(); + } + ar.end_array(); + } + else if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2) { uint32_t nbp = bulletproofs.size(); if (type == RCTTypeBulletproof2) @@ -370,7 +389,7 @@ namespace rct { ar.begin_array(); // we keep a byte for size of MGs, because we don't know whether this is // a simple or full rct signature, and it's starting to annoy the hell out of me - size_t mg_elements = (type == RCTTypeSimple || type == RCTTypeBulletproof || type == RCTTypeBulletproof2) ? inputs : 1; + size_t mg_elements = (type == RCTTypeSimple || type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeSimpleBulletproof) ? inputs : 1; PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_elements, MGs); if (MGs.size() != mg_elements) return false; @@ -388,7 +407,7 @@ namespace rct { for (size_t j = 0; j < mixin + 1; ++j) { ar.begin_array(); - size_t mg_ss2_elements = ((type == RCTTypeSimple || type == RCTTypeBulletproof || type == RCTTypeBulletproof2) ? 1 : inputs) + 1; + size_t mg_ss2_elements = ((type == RCTTypeSimple || type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeSimpleBulletproof) ? 1 : inputs) + 1; PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_ss2_elements, MGs[i].ss[j]); if (MGs[i].ss[j].size() != mg_ss2_elements) return false; @@ -414,7 +433,7 @@ namespace rct { ar.delimit_array(); } ar.end_array(); - if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2) + if (type == RCTTypeBulletproof || type == RCTTypeBulletproof2 || type == RCTTypeSimpleBulletproof) { ar.tag("pseudoOuts"); ar.begin_array(); @@ -438,12 +457,16 @@ namespace rct { keyV& get_pseudo_outs() { - return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 ? p.pseudoOuts : pseudoOuts; + if (type == RCTTypeBulletproof) + return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 ? p.pseudoOuts : pseudoOuts; + return type == RCTTypeSimpleBulletproof ? p.pseudoOuts : pseudoOuts; } keyV const& get_pseudo_outs() const { - return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 ? p.pseudoOuts : pseudoOuts; + if (type == RCTTypeBulletproof) + return type == RCTTypeBulletproof || type == RCTTypeBulletproof2 ? p.pseudoOuts : pseudoOuts; + return type == RCTTypeSimpleBulletproof ? p.pseudoOuts : pseudoOuts; } }; @@ -550,6 +573,7 @@ namespace rct { bool is_rct_simple(int type); bool is_rct_bulletproof(int type); + bool is_rct_old_bulletproof(int type); bool is_rct_borromean(int type); static inline const rct::key &pk2rct(const crypto::public_key &pk) { return (const rct::key&)pk; } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index bda25316e..d298fa250 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -694,7 +694,7 @@ uint64_t estimate_tx_weight(bool use_rct, int n_inputs, int mixin, int n_outputs uint8_t get_bulletproof_fork() { - return 8; + return 11; } uint64_t estimate_fee(bool use_per_byte_fee, bool use_rct, int n_inputs, int mixin, int n_outputs, size_t extra_size, bool bulletproof, uint64_t base_fee, uint64_t fee_multiplier, uint64_t fee_quantization_mask) @@ -1280,10 +1280,12 @@ static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation & switch (rv.type) { case rct::RCTTypeSimple: + case rct::RCTTypeSimpleBulletproof: case rct::RCTTypeBulletproof: case rct::RCTTypeBulletproof2: return rct::decodeRctSimple(rv, rct::sk2rct(scalar1), i, mask, hwdev); case rct::RCTTypeFull: + case rct::RCTTypeFullBulletproof: return rct::decodeRct(rv, rct::sk2rct(scalar1), i, mask, hwdev); default: LOG_ERROR("Unsupported rct type: " << rv.type); @@ -5570,7 +5572,7 @@ bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector additional_tx_keys; @@ -5995,7 +5997,7 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector 1) rct_config.range_proof_type = rct::RangeProofPaddedBulletproof; - rct_config.bp_version = use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1; + rct_config.bp_version = use_fork_rules(12, 0) ? 1 : 2; } bool r = cryptonote::construct_tx_with_tx_key(m_account.get_keys(), m_subaddresses, sources, sd.splitted_dsts, ptx.change_dts.addr, sd.extra, tx, sd.unlock_time, ptx.tx_key, ptx.additional_tx_keys, sd.use_rct, rct_config, &msout, false); THROW_WALLET_EXCEPTION_IF(!r, error::tx_not_constructed, sd.sources, sd.splitted_dsts, sd.unlock_time, m_nettype); @@ -6150,6 +6152,10 @@ uint64_t wallet2::get_dynamic_base_fee_estimate() const //---------------------------------------------------------------------------------------------------- uint64_t wallet2::get_base_fee() const { + if (use_fork_rules(HF_VERSION_PER_BYTE_FEE)) + { + return FEE_PER_BYTE; + } if(m_light_wallet) { if (use_fork_rules(HF_VERSION_PER_BYTE_FEE)) @@ -8315,7 +8321,7 @@ std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_2(std::vector wallet2::create_transactions_from(const crypton const bool bulletproof = use_fork_rules(get_bulletproof_fork(), 0); const rct::RCTConfig rct_config { bulletproof ? rct::RangeProofPaddedBulletproof : rct::RangeProofBorromean, - bulletproof ? (use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1) : 0, + bulletproof ? (use_fork_rules(12, 0) ? 1 : 2) : 0, }; const uint64_t base_fee = get_base_fee(); const uint64_t fee_multiplier = get_fee_multiplier(priority, get_fee_algorithm()); From d77ac12218492fbe71b47f496d1dd80b1bbb9715 Mon Sep 17 00:00:00 2001 From: wowario Date: Sat, 9 Mar 2019 14:44:59 +0300 Subject: [PATCH 14/20] update checkpoints.dat --- src/blocks/checkpoints.dat | Bin 221956 -> 10852 bytes src/checkpoints/checkpoints.cpp | 1 + src/cryptonote_core/blockchain.cpp | 2 +- 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/blocks/checkpoints.dat b/src/blocks/checkpoints.dat index adc433522a41adf3dd189c74e282867b9e5c1413..fcb9214e5a00a79fa1562c968c011599758e9939 100644 GIT binary patch literal 10852 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 z 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 literal 221956 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 z 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�u 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≪E%5oH#Ce;y+E*fb-{o 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

!^ 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 diff --git a/src/checkpoints/checkpoints.cpp b/src/checkpoints/checkpoints.cpp index abf6d62be..2887d4685 100644 --- a/src/checkpoints/checkpoints.cpp +++ b/src/checkpoints/checkpoints.cpp @@ -215,6 +215,7 @@ namespace cryptonote ADD_CHECKPOINT(81769, "41db9fef8d0ccfa78b570ee9525d4f55de77b510c3ae4b08a1d51b9aec9ade1d"); //Hard fork to v11 ADD_CHECKPOINT(82069, "fdea800d23d0b2eea19dec8af31e453e883e8315c97e25c8bb3e88ca164f8369"); //Hard fork to v12 ADD_CHECKPOINT(85000, "31d62ab75470b15aedee6674b78767b53f10951786e991c26035743c267b247a"); + ADD_CHECKPOINT(87000, "a788e5a7233ca2198ad6446ddc454b05d578e72253ed2bbca969527230f6eec2"); return true; } diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 85342ebf6..1fda54f34 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -4581,7 +4581,7 @@ void Blockchain::cancel() } #if defined(PER_BLOCK_CHECKPOINT) -static const char expected_block_hashes_hash[] = "57465b2e1d648df696dc02b4aeddc357493678e9e5b145226fd4af1aceb02165"; +static const char expected_block_hashes_hash[] = "0397d49ea848f57a340c2e7f0438ad76c2e61d68aa470e96d1ad8e99fa5138d9"; void Blockchain::load_compiled_in_block_hashes() { const bool testnet = m_nettype == TESTNET; From 05e391fc54a9ea8fcfb8a1aab5613384d87ab847 Mon Sep 17 00:00:00 2001 From: wowario Date: Sun, 10 Mar 2019 11:02:53 +0300 Subject: [PATCH 15/20] Build fixes for some platforms --- src/cryptonote_core/blockchain.cpp | 8 ++++---- src/cryptonote_core/blockchain.h | 2 +- src/cryptonote_core/cryptonote_core.cpp | 2 +- tests/core_tests/v2_tests.h | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 1fda54f34..504713daf 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -1184,7 +1184,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl } } - std::vector last_blocks_weights; + std::vector last_blocks_weights; get_last_n_blocks_weights(last_blocks_weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW); if (!get_block_reward(epee::misc_utils::median(last_blocks_weights), cumulative_block_weight, already_generated_coins, base_reward, version)) { @@ -1219,7 +1219,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl } //------------------------------------------------------------------ // get the block weights of the last blocks, and return by reference . -void Blockchain::get_last_n_blocks_weights(std::vector& weights, size_t count) const +void Blockchain::get_last_n_blocks_weights(std::vector& weights, size_t count) const { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -3079,7 +3079,7 @@ uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const grace_blocks = CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1; const uint64_t min_block_weight = get_min_block_weight(version); - std::vector weights; + std::vector weights; get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW - grace_blocks); weights.reserve(grace_blocks); for (size_t i = 0; i < grace_blocks; ++i) @@ -3697,7 +3697,7 @@ bool Blockchain::update_next_cumulative_weight_limit(uint64_t *long_term_effecti if (hf_version < HF_VERSION_LONG_TERM_BLOCK_WEIGHT) { - std::vector weights; + std::vector weights; get_last_n_blocks_weights(weights, CRYPTONOTE_REWARD_BLOCKS_WINDOW); m_current_block_cumul_weight_median = epee::misc_utils::median(weights); long_term_block_weight = weights.back(); diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index 175d93671..a79ff22a1 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -1262,7 +1262,7 @@ namespace cryptonote * @param sz return-by-reference the list of weights * @param count the number of blocks to get weights for */ - void get_last_n_blocks_weights(std::vector& weights, size_t count) const; + void get_last_n_blocks_weights(std::vector& weights, size_t count) const; /** * @brief checks if a transaction is unlocked (its outputs spendable) diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 0d32c3084..04e6ea42d 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1803,7 +1803,7 @@ namespace cryptonote for (size_t n = 0; n < sizeof(seconds)/sizeof(seconds[0]); ++n) { unsigned int b = 0; - for (time_t ts: timestamps) b += ts >= now - seconds[n]; + for (time_t ts: timestamps) b += ts >= (time_t)(now - seconds[n]); const double p = probability(b, seconds[n] / DIFFICULTY_TARGET_V2); MDEBUG("blocks in the last " << seconds[n] / 60 << " minutes: " << b << " (probability " << p << ")"); if (p < threshold) diff --git a/tests/core_tests/v2_tests.h b/tests/core_tests/v2_tests.h index ecb23818c..cd2bcb839 100644 --- a/tests/core_tests/v2_tests.h +++ b/tests/core_tests/v2_tests.h @@ -81,7 +81,7 @@ template<> struct get_test_options { const std::pair hard_forks[3] = {std::make_pair(1, 0), std::make_pair(2, 1), std::make_pair(0, 0)}; const cryptonote::test_options test_options = { - hard_forks + hard_forks, 0 }; }; From ce3c9b111947e9a4d3e0aa660d739f2ff28af7e6 Mon Sep 17 00:00:00 2001 From: wowario Date: Sun, 10 Mar 2019 11:47:43 +0300 Subject: [PATCH 16/20] update README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 22a4d438d..52f3844bd 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ Dates are provided in the format YYYY-MM-DD. | 53666 | 2018-10-06 | Cool Cage | v0.3.0.0 | v0.3.1.3 | Cryptonight variant 2, LWMA v2, ringsize = 22, MMS | 63469 | 2018-11-11 | Dank Doge | v0.4.0.0 | v0.4.0.0 | LWMA v4 | 81769 | 2019-02-19 | Erotic EggplantEmoji | v0.5.0.0 | v0.5.0.2 | Cryptonight/wow, LWMA v1 with N=144, Updated Bulletproofs, Fee Per Byte, Auto-churn +| XXXXX | 2019-XX-XX | XXX XXX | v0.6.0.0 | v0.6.0.0 | New PoW based on RandomX, new block weight algorithm, slightly more efficient RingCT format X's indicate that these details have not been determined as of commit date. From 661e3841fbb6082f10dd123ab2d56c6027ca25df Mon Sep 17 00:00:00 2001 From: wowario Date: Sun, 10 Mar 2019 13:53:28 +0300 Subject: [PATCH 17/20] rename debug utilities --- src/debug_utilities/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/debug_utilities/CMakeLists.txt b/src/debug_utilities/CMakeLists.txt index 1bcbfd0cf..31306294b 100644 --- a/src/debug_utilities/CMakeLists.txt +++ b/src/debug_utilities/CMakeLists.txt @@ -46,7 +46,7 @@ target_link_libraries(cn_deserialize set_property(TARGET cn_deserialize PROPERTY - OUTPUT_NAME "monero-utils-deserialize") + OUTPUT_NAME "wownero-utils-deserialize") set(object_sizes_sources @@ -67,5 +67,5 @@ target_link_libraries(object_sizes set_property(TARGET object_sizes PROPERTY - OUTPUT_NAME "monero-utils-object-sizes") + OUTPUT_NAME "wownero-utils-object-sizes") From d6422cc81cc0f6592768bbd227238f011bc3e88d Mon Sep 17 00:00:00 2001 From: wowario Date: Mon, 11 Mar 2019 01:36:34 +0300 Subject: [PATCH 18/20] update version to 0.5.1 --- src/version.cpp.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/version.cpp.in b/src/version.cpp.in index 722e0d21c..e699ad125 100644 --- a/src/version.cpp.in +++ b/src/version.cpp.in @@ -1,6 +1,6 @@ #define DEF_MONERO_VERSION_TAG "@VERSIONTAG@" -#define DEF_MONERO_VERSION "0.6.0.0" -#define DEF_MONERO_RELEASE_NAME "TBD" +#define DEF_MONERO_VERSION "0.5.1.0" +#define DEF_MONERO_RELEASE_NAME "Erotic EggplantEmoji" #define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG #include "version.h" From 95a64143423405578cfbf5a5058d16cfc7ce2f14 Mon Sep 17 00:00:00 2001 From: wowario Date: Mon, 14 Jan 2019 00:10:58 +0300 Subject: [PATCH 19/20] add v0.5 ASCII art --- README.md | 3 +-- src/cryptonote_core/cryptonote_core.cpp | 30 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 52f3844bd..451fcdb81 100644 --- a/README.md +++ b/README.md @@ -41,8 +41,7 @@ Dates are provided in the format YYYY-MM-DD. | 6969 | 2018-04-24 | Busty Brazzers | v0.2.0.0 | v0.2.0.0 | Bulletproofs, LWMA difficulty algorithm, ringsize >= 10, reduce unlock to 4 | 53666 | 2018-10-06 | Cool Cage | v0.3.0.0 | v0.3.1.3 | Cryptonight variant 2, LWMA v2, ringsize = 22, MMS | 63469 | 2018-11-11 | Dank Doge | v0.4.0.0 | v0.4.0.0 | LWMA v4 -| 81769 | 2019-02-19 | Erotic EggplantEmoji | v0.5.0.0 | v0.5.0.2 | Cryptonight/wow, LWMA v1 with N=144, Updated Bulletproofs, Fee Per Byte, Auto-churn -| XXXXX | 2019-XX-XX | XXX XXX | v0.6.0.0 | v0.6.0.0 | New PoW based on RandomX, new block weight algorithm, slightly more efficient RingCT format +| 81769 | 2019-02-19 | Erotic EggplantEmoji | v0.5.0.0 | v0.5.1.0 | Cryptonight/wow, LWMA v1 with N=144, Updated Bulletproofs, Fee Per Byte, Auto-churn X's indicate that these details have not been determined as of commit date. diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 04e6ea42d..beed2d44e 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -1561,6 +1561,36 @@ namespace cryptonote main_message = "The daemon is running offline and will not attempt to sync to the Monero network."; else main_message = "The daemon will start synchronizing with the network. This may take a long time to complete."; + MGINFO_MAGENTA(ENDL << + "\n \n" + " :+:` \n" + " :++:` \n" + " :++:` \n" + " /++++++++++. \n" + " :++++++++++++` ______ _ _\n" + " -+++++++++++++++:..` | ____| | | (_)\n" + " -/+++++++++++/++++. | |__ _ __ ___ | |_ _ ___ \n" + " `://+++++++++/+++++/` | __| | '__/ _ || __| |/ __|\n" + " -/:::/++++++++++++++:` | |____| | | (_) | |_| | (__ \n" + " `::::/++++++++++++++++:` |______|_| |___/|__||_||___|\n" + " :::::/+++++++++++++++++/` ______ _ _ _____ _ _ \n" + " `-::::/+++++++++++++++++++. | ____| | | | | | ___| (_(_)\n" + " .::::/++++++++++++++++++++:` | |__ __ _ __ _ _ __ | | __ _ _ __ | |_| |__ _ __ ___ ___ _ _ \n" + " .::::/++++++++++++++++++++++:. | __| / _` |/ _` | '_ || |/ _` | '_ | __| __| | '_ ` _ | / _ | | |\n" + " .:--://+++++++++++++++++++++++/:. | |___| (_| | (_| | |_) | | (_| | | | | |_| |___ | | | | | | (_) | | |\n" + " `---://////++++++++++++++++++++++/-` |______|__, ||__, | .__/|_||__,_|_| |_|__||_____||_| |_| |_| ___/| |_|\n" + " .---:///////++++++++++++++++++//+++:` __/ | __/ | | _/ | \n" + " `.-.-:////////++++++++++++++//////++: |___/ |___/|_| |__/ \n" + " `...-//////////+++++++++///////////:` \n" + " bow `...:////////////+++++++/////////:- \n" + " chicka `...-://////////////+++////////::. \n" + " wow wow! `...-::///://////////////////--- \n" + " `...-:://////////////////:..- \n" + " `....-::://////////////-... \n" + " `.-----:::://////::.... \n" + " ``.....------....-. \n" + " ``........`` \n" + "\n \n" << ENDL); MGINFO_YELLOW(ENDL << "**********************************************************************" << ENDL << main_message << ENDL << ENDL From 7b06d497f4714c21197cf13b5f6197ef547966fe Mon Sep 17 00:00:00 2001 From: wowario Date: Mon, 11 Mar 2019 12:04:49 +0300 Subject: [PATCH 20/20] debug level for no incoming connections warning --- src/p2p/net_node.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index dc4436dd7..38d0d82be 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -1332,7 +1332,7 @@ namespace nodetool return true; if (get_incoming_connections_count() == 0) { - const el::Level level = el::Level::Warning; + const el::Level level = el::Level::Debug; MCLOG_RED(level, "global", "No incoming connections - check firewalls/routers allow port " << get_this_peer_port()); } return true;