Compare commits

...

117 Commits

Author SHA1 Message Date
auruya 383f3d36e6 Fix difficulty cache in get_difficulty_for_next_block (#73) 2025-12-10 10:20:29 +00:00
Some Random Crypto Guy 7d06436d08 Merge branch 'main' of https://github.com/salvium/salvium 2025-12-10 10:09:48 +00:00
somerandomcryptoguy 3bee380b18 add cross-compilation dependencies to Dockerfile.salvium (#78)
Co-authored-by: auruya <dream.glorix@gmail.com>
2025-12-10 10:08:53 +00:00
auruya 58c70115f2 fix compiler warnings 2025-12-10 10:06:10 +00:00
Some Random Crypto Guy 6be4081332 fixed self-subaddress issue 2025-12-10 10:01:31 +00:00
auruya 538e4a5d1f misc fixes 2025-12-10 09:55:53 +00:00
Some Random Crypto Guy 3e49572539 updated checkpoints ready for next release 2025-12-10 09:53:02 +00:00
Some Random Crypto Guy 05c7152ad5 Merge branch 'add-account-all-command' 2025-12-10 09:22:14 +00:00
Some Random Crypto Guy 8d31fa2842 fixed Carrot TX proof for self-send to subaddress 2025-12-09 11:40:52 +00:00
Some Random Crypto Guy 2abe39f178 Merge branch 'carrot-tx-proof-support' 2025-12-09 09:26:42 +00:00
Some Random Crypto Guy ac13287c78 Merge branch 'carrot-tx-proof-support' of https://github.com/salvium/salvium into carrot-tx-proof-support 2025-12-08 16:36:19 +00:00
Some Random Crypto Guy 305b92909e fixed InProofV3 calcs / checks 2025-12-08 16:36:12 +00:00
auruya 248667016a fix freshly unlocked output being excluded from transactions 2025-12-04 17:33:37 +03:00
Some Random Crypto Guy 10b58aac73 fixed sweeping to own subaddress 2025-12-03 16:10:54 +00:00
Some Random Crypto Guy 0221fe8a34 fixed TX proof generation for multiple destinations 2025-12-02 20:29:28 +00:00
Some Random Crypto Guy 1ff480e64d added sanity checks on Carrot vs CN; fixed InProofV2 for CN 2025-12-02 14:59:07 +00:00
Some Random Crypto Guy fd121aae19 Merge branch 'carrot-tx-proof-support' of https://github.com/salvium/salvium into carrot-tx-proof-support 2025-12-02 13:02:47 +00:00
Some Random Crypto Guy 0deb19c53c tidied TX proof code; removed large chunks of commented-out cruft 2025-12-02 13:02:39 +00:00
auruya 7f3e389d92 add carrot tx proof known values tests 2025-12-02 15:43:32 +03:00
Some Random Crypto Guy 87da2d4661 extended unit test scenarios for Carrot TX proofs 2025-12-01 20:23:27 +00:00
Some Random Crypto Guy f6075ae9ec simple unit test for Carrot TX proofs 2025-12-01 20:13:49 +00:00
Some Random Crypto Guy c424e84f4b cleaned up wallet code; fixed unit test 2025-12-01 15:44:23 +00:00
Some Random Crypto Guy 3b4efe9636 new fe_ functions for reversing point compression 2025-12-01 14:52:48 +00:00
auruya dfa27e78c6 add check_carrot_tx_proof fn 2025-11-27 14:08:39 +03:00
Some Random Crypto Guy 9b57fe3eae fixed lambda func with boost::optional 2025-11-26 09:56:13 +00:00
Some Random Crypto Guy 8f60758a3c interim checkin - pretty sure this proof cannot work without curve translation using ConvertPointE() 2025-11-25 11:57:57 +00:00
Some Random Crypto Guy 3a7ec4db32 Merge branch 'carrot-tx-proof-support' of https://github.com/salvium/salvium into carrot-tx-proof-support 2025-11-25 09:22:36 +00:00
auruya 679bc9f0d7 update carrot tx proof support 2025-11-25 12:08:02 +03:00
Some Random Crypto Guy 362eb38ff8 Merge branch 'carrot-tx-proof-support' of https://github.com/salvium/salvium into carrot-tx-proof-support 2025-11-21 12:37:48 +00:00
auruya 6243e992cf add get carrot tx proof support for sender 2025-11-21 14:50:31 +03:00
auruya e872414d57 add get carrot tx proof support for sender 2025-11-21 13:20:02 +03:00
auruya fcaf640bcb Add carrot tx proof support (get_tx_proof and check_tx_proof) 2025-11-17 19:58:40 +03:00
Some Random Crypto Guy f5237ceaf5 Merge branch 'move-xy-calculation-to-txbuilder' 2025-11-14 15:52:15 +00:00
Some Random Crypto Guy 1503ec6629 fixed CLI auto-refresh when rescan_bc 2025-11-14 15:42:58 +00:00
auruya 0f744520ad wallet cache migration v2 to v3 2025-11-14 17:26:50 +03:00
Some Random Crypto Guy caf52cca20 fixed spending of return_payment - forces rescan_bc of wallet on first load 2025-11-13 20:52:20 +00:00
auruya 1c4309c400 calculate x, y in tx_builder 2025-11-13 15:55:08 +03:00
Some Random Crypto Guy 9725b921a5 fixed scan refresh issue when Carrot keys are still encrypted 2025-11-12 14:46:41 +00:00
Some Random Crypto Guy 38d2515dc5 Merge branch 'develop' 2025-11-10 14:26:53 +00:00
Some Random Crypto Guy 13efd79f88 Merge branch 'fix-view-only-wallet' into develop 2025-11-10 13:34:36 +00:00
Some Random Crypto Guy 0c273d3571 possible fix for STAKE amount in GUI app - thanks, Tiamak 2025-11-10 12:28:41 +00:00
Some Random Crypto Guy d22389b37a added return_output_info collection to SVB scanning 2025-11-10 11:55:59 +00:00
auruya f9b060e552 add view only scan support 2025-11-10 14:36:38 +03:00
auruya 7d13f90e4a encrypt carrot keys 2025-11-10 14:35:48 +03:00
Some Random Crypto Guy f7a75a4fdc added help information 2025-11-04 11:38:32 +00:00
auruya b9fa97daad add account all command to display all accounts 2025-11-04 13:02:11 +03:00
Some Random Crypto Guy 6f0f5b5e83 fixed view-only wallet support 2025-11-03 17:12:27 +00:00
Some Random Crypto Guy 6fbd3184b1 fixed lookahead 2025-10-31 11:37:15 +00:00
Some Random Crypto Guy d5fee31ec6 partial fix for view-only wallet scanning 2025-10-29 21:14:36 +00:00
Some Random Crypto Guy 2a93e04180 removed erroneous Tor/i2p/blocklist entries 2025-10-29 20:48:36 +00:00
Some Random Crypto Guy b902ec9406 password issue fixed (kludgy) 2025-10-29 16:46:04 +00:00
Some Random Crypto Guy e3ba570fb1 publishing ARMv8 binaries 2025-10-29 15:34:00 +00:00
Some Random Crypto Guy fac65e5093 possible fix for GH action for cross-compilation for MacOS 2025-10-29 12:59:51 +00:00
Some Random Crypto Guy eebc1f1d26 Merge branch 'fix-check-tx-key' into develop 2025-10-28 11:39:56 +00:00
Some Random Crypto Guy 502ece5ba3 fixed random check_tx_key issues 2025-10-27 13:17:26 +00:00
Some Random Crypto Guy 62967db201 fixed merge of scanning code 2025-10-23 13:11:16 +01:00
Some Random Crypto Guy 4b7f863c71 fix for show_transfers not showing SC1 addresses; fixed proposal asset_type handling 2025-10-23 13:10:10 +01:00
auruya 55a6f17c91 fix check_tx_key 2025-10-23 14:40:13 +03:00
auruya 8a32d7f73b fix check_tx_key for multi-destination txs with additional derivations 2025-10-21 17:23:16 +03:00
Some Random Crypto Guy dfb6a705ea Merge branch 'blockchain-stats-testnet-fix' into develop 2025-10-21 12:23:25 +01:00
Some Random Crypto Guy 5246138398 fixed salvium-blockchain-stats for non-mainnet chains 2025-10-21 12:10:52 +01:00
Some Random Crypto Guy cc3e6a0822 Merge branch 'main' into develop 2025-10-21 12:09:43 +01:00
Some Random Crypto Guy 8e68f58eff Merge branch 'develop' of https://github.com/salvium/salvium into develop 2025-10-21 12:09:15 +01:00
auruya 0a79a4d9fd fix check_tx_key for carrot tx using x25519 derivation 2025-10-21 10:26:20 +03:00
Some Random Crypto Guy 48fb95bdc1 bumped version to v1.0.6 2025-10-16 14:58:02 +01:00
Some Random Crypto Guy bb4d3768b2 Merge branch 'wallet-ui-improvements' 2025-10-16 14:52:47 +01:00
Some Random Crypto Guy cea3f0f341 fixed Carrot wallet exceptions reporting 'unknown error'; fixed wallet.address.txt file to contain Carrot keys as well; fixed sending to subaddress from same wallet 2025-10-16 14:51:49 +01:00
Some Random Crypto Guy 35fc88c0ec bumped version 2025-10-15 13:45:16 +01:00
Some Random Crypto Guy 312413aeb0 Merge branch 'hotfix-subaddress-limit-removal-optimisation' 2025-10-15 13:31:28 +01:00
Some Random Crypto Guy 6ba060e116 fixed subaddress lookahead generation for Carrot; optimised to reduce scanning times 2025-10-15 13:29:04 +01:00
Some Random Crypto Guy 88d5e1f50e updated translation bundles; bumped version 2025-10-14 13:55:35 +01:00
Some Random Crypto Guy 644a8d3b4d Merge branch 'hotfix-carrot-address-display-in-cli' 2025-10-14 13:52:50 +01:00
Some Random Crypto Guy e80d135c15 Merge branch 'hotfix-return-from-multiple-dest-transaction' 2025-10-14 13:51:46 +01:00
Some Random Crypto Guy a40026f941 fixed return_payment when receiving Carrot output from a multiple-destination transaction 2025-10-14 13:51:04 +01:00
Some Random Crypto Guy fb25a36bf2 fixes for Carrot address display in CLI wallet 2025-10-14 10:44:26 +01:00
Some Random Crypto Guy 6f3f7c8e9a Merge branch 'hotfix-Carrot-get-tx-key' 2025-10-14 06:20:16 +01:00
Some Random Crypto Guy c6c35d5639 fixed get_tx_key implementation for Carrot (kludgy) 2025-10-14 06:19:45 +01:00
Some Random Crypto Guy 63433cc58f bumped version to v1.0.3 2025-10-14 05:05:55 +01:00
Some Random Crypto Guy 940c0e03f7 Merge branch 'hotfix-sweepall-to-self' 2025-10-14 05:04:03 +01:00
Some Random Crypto Guy 445e498fbf Merge branch 'hotfix-additional-cn-address-checks' 2025-10-14 05:03:51 +01:00
Some Random Crypto Guy 09ad75f0cc additional CN/Carrot address checks implemented in wallet 2025-10-14 05:02:28 +01:00
Some Random Crypto Guy f8d9f9335e fixed issue with post-Carrot-HF sweep_all to self 2025-10-13 21:17:45 +01:00
Some Random Crypto Guy cd31dafa97 updated ubuntu version for GH action runner 2025-10-13 19:17:52 +01:00
Some Random Crypto Guy 6aa32701b8 fixed get_block_template RPC call to handle payouts for treasury and miner 2025-10-13 16:20:39 +01:00
Some Random Crypto Guy f84a622bfa attempt to bump GH builds to use Ubuntu 22.04 2025-10-12 14:31:46 +01:00
Some Random Crypto Guy f60b7209f8 fixed wallet API regression on spend+view key access 2025-10-09 14:25:26 +01:00
Some Random Crypto Guy 3b00a41fff fixed broken carrotKeys() API method 2025-10-08 20:40:33 +01:00
somerandomcryptoguy 119a7fab57 added query_key wallet-RPC support for all Carrot keys (#69)
Co-authored-by: Some Random Crypto Guy <somerandomcryptoguy@protonmail.com>
2025-10-08 20:29:10 +01:00
auruya 6d0a4a4d7b add carrotkeys fn to wallet api (#68) 2025-10-08 20:28:50 +01:00
Some Random Crypto Guy 45404ecc71 API fixes 2025-10-07 15:12:49 +01:00
Some Random Crypto Guy e5bfc2f6ad wallet API additions for better Carrot support 2025-10-07 13:32:15 +01:00
Some Random Crypto Guy c5be51053a added support for AUDIT, BURN, STAKE TXs to PendingTransaction wallet API 2025-10-07 12:34:58 +01:00
Some Random Crypto Guy 4bfb5f51bf Merge branch 'develop' 2025-10-04 13:38:50 +01:00
Some Random Crypto Guy d7a9facee9 improved UX for address command 2025-10-04 13:36:37 +01:00
Some Random Crypto Guy 260bc3721b bumped version 2025-10-03 21:18:52 +01:00
Some Random Crypto Guy 3e0457de09 Merge branch 'develop' 2025-10-03 21:17:54 +01:00
Some Random Crypto Guy ce6f6e0593 Merge branch 'develop' of https://github.com/salvium/salvium into develop 2025-10-03 15:06:17 +01:00
somerandomcryptoguy f1efb700d2 improvements to wallet address formatting for RPC (#66)
* bumped version to v1.0.0

* added fast sync points up to 325,000

* added fast sync points up to 325,000 (part 2)

* RPC address formatting fixes

---------

Co-authored-by: Some Random Crypto Guy <somerandomcryptoguy@protonmail.com>
2025-10-03 15:06:05 +01:00
auruya dd246296b9 Fix show qr code (#65)
* enable dual address type support in show_qr_code command

* delete comments
2025-10-03 15:05:49 +01:00
Some Random Crypto Guy 22334d7c6c Merge branch 'develop' of https://github.com/salvium/salvium into develop 2025-10-03 13:42:27 +01:00
somerandomcryptoguy c07698fcc8 improvements to wallet address formatting (#64)
* bumped version to v1.0.0

* added fast sync points up to 325,000

* added fast sync points up to 325,000 (part 2)

* initial import of CLI wallet improvements for address printing

* updated wallet address display in various places

---------

Co-authored-by: Some Random Crypto Guy <somerandomcryptoguy@protonmail.com>
2025-10-03 13:10:28 +01:00
Some Random Crypto Guy ce69272638 Merge branch 'main' into develop 2025-10-02 14:41:30 +01:00
Some Random Crypto Guy 2b95d100b3 added fast sync points up to 325,000 (part 2) 2025-10-01 17:35:35 +01:00
Some Random Crypto Guy b7706d4def added fast sync points up to 325,000 2025-10-01 17:05:49 +01:00
Some Random Crypto Guy 845d46d7b3 bumped version to v1.0.0 2025-10-01 14:46:23 +01:00
Some Random Crypto Guy e4f8929f2c Merge branch 'develop' 2025-10-01 14:44:09 +01:00
Some Random Crypto Guy c03402d525 updated API methods for 3rd-party wallets 2025-10-01 14:40:47 +01:00
Some Random Crypto Guy d8b18b17f6 bumped RC version 2025-09-30 22:02:11 +01:00
Some Random Crypto Guy 5347266ed9 fixed a couple of wallet cache issues; fixed rescan_bc bug 2025-09-30 22:01:35 +01:00
somerandomcryptoguy 419c26adbf added tx_type setting for payment details - needed for wallet API (#61)
Co-authored-by: Some Random Crypto Guy <somerandomcryptoguy@protonmail.com>
2025-09-12 16:34:02 +01:00
auruya 7727bdb51e Add stake tests (#60)
* update unit tests

* add stake transaction functional test
2025-09-12 09:39:02 +01:00
Some Random Crypto Guy 37a58646fd bumped RC version 2025-09-10 09:20:17 +01:00
somerandomcryptoguy 0e747e3f15 remove 2nd fork - STAKE issues resolved cleanly (#58)
* removed HF11

* fixed problem with AUDIT generation; fixed issue with empty PROTOCOL TX after HF10

* fixed protocol_tx version if empty at HF10 commencement; added validation of correct address type for destinations on TRANSFER

* fix is_carrot for stake txs

---------

Co-authored-by: Some Random Crypto Guy <somerandomcryptoguy@protonmail.com>
Co-authored-by: auruya <dream.glorix@gmail.com>
2025-09-10 09:16:14 +01:00
auruya caf8a0a962 fix-prevalidate-protocol-tx-for-carrot (#57) 2025-09-04 20:43:16 +01:00
Some Random Crypto Guy 1b60c08dce fixed issues with STAKE over HF10-HF11 thresholds; bumped RC version 2025-09-04 16:04:29 +01:00
Some Random Crypto Guy 24f9916287 Merge branch 'develop' of https://github.com/salvium/salvium into develop 2025-05-08 09:21:06 +01:00
Some Random Crypto Guy cd22e55296 removed compilation warnings from scanner 2025-03-19 11:26:47 +00:00
79 changed files with 45180 additions and 39768 deletions
+4 -4
View File
@@ -22,7 +22,7 @@ env:
jobs:
build-cross:
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
env:
CCACHE_TEMPDIR: /tmp/.ccache-temp
strategy:
@@ -52,10 +52,10 @@ jobs:
packages: "gperf cmake python3-zmq libdbus-1-dev libharfbuzz-dev"
- name: "Cross-Mac x86_64"
host: "x86_64-apple-darwin"
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev python3-setuptools-git"
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python3-dev python3-setuptools-git"
- name: "Cross-Mac aarch64"
host: "aarch64-apple-darwin"
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev python3-setuptools-git"
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python3-dev python3-setuptools-git"
- name: "x86_64 Freebsd"
host: "x86_64-unknown-freebsd"
packages: "clang-8 gperf cmake python3-zmq libdbus-1-dev libharfbuzz-dev"
@@ -105,7 +105,7 @@ jobs:
${{env.CCACHE_SETTINGS}}
make depends target=${{ matrix.toolchain.host }} -j2
- uses: actions/upload-artifact@v4
if: ${{ matrix.toolchain.host == 'x86_64-w64-mingw32' || matrix.toolchain.host == 'x86_64-apple-darwin' || matrix.toolchain.host == 'aarch64-apple-darwin' || matrix.toolchain.host == 'x86_64-unknown-linux-gnu' }}
if: ${{ matrix.toolchain.host == 'x86_64-w64-mingw32' || matrix.toolchain.host == 'x86_64-apple-darwin' || matrix.toolchain.host == 'aarch64-apple-darwin' || matrix.toolchain.host == 'x86_64-unknown-linux-gnu' || matrix.toolchain.host == 'aarch64-linux-gnu' }}
with:
name: ${{ matrix.toolchain.name }}
path: |
+37
View File
@@ -0,0 +1,37 @@
# Use Ubuntu 24.04 as base
FROM ubuntu:24.04 AS builder
ENV DEBIAN_FRONTEND=noninteractive
# Install build dependencies including cross-compilers
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential cmake pkg-config git imagemagick libcap-dev librsvg2-bin libz-dev \
g++-mingw-w64-x86-64 clang gcc-arm-none-eabi binutils-x86-64-linux-gnu libtiff-tools \
g++-x86-64-linux-gnu gcc-x86-64-linux-gnu binutils-aarch64-linux-gnu \
g++-aarch64-linux-gnu gcc-aarch64-linux-gnu crossbuild-essential-amd64 \
libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev \
libreadline-dev libexpat1-dev libpgm-dev qttools5-dev-tools libhidapi-dev libusb-1.0-0-dev \
libprotobuf-dev protobuf-compiler libudev-dev libboost-all-dev python3 ccache doxygen graphviz \
ca-certificates curl zip libtool gperf automake autoconf \
&& rm -rf /var/lib/apt/lists/*
# Clone the develop branch
WORKDIR /opt
RUN git clone --recursive -b develop https://github.com/salvium/salvium.git
WORKDIR /opt/salvium
# make the script runnable
RUN chmod +x make_releases.sh
# Make sure the output folder exists
RUN mkdir ~/releases
# Expose the releases directory for copying zip files to the host
VOLUME ["~/releases"]
ENTRYPOINT ["/opt/salvium/make_releases.sh"]
# To access the generated zip files on your host, run the container with:
# docker run -v ~/releases:/root/releases <image>
# This will copy the zip files to your host's ~/salvium-releases directory.
+7 -7
View File
@@ -1,6 +1,6 @@
# Salvium One v1.0.0
# Salvium One v1.0.7
Copyright (c) 2023-2024, Salvium
Copyright (c) 2023-2025, Salvium
Portions Copyright (c) 2014-2023, The Monero Project
Portions Copyright (c) 2012-2013 The Cryptonote developers.
@@ -47,7 +47,7 @@ As with many development projects, the repository on GitHub is considered to be
## Supporting the project
Salvium is a 100% community-sponsored endeavor. If you want to join our efforts, the easiest thing you can do is support the project financially. Go to [https://salvium.io/donate/](https://salvium.io/donate/) for more information.
Salvium is a 100% community-sponsored endeavor. If you want to join our efforts, the easiest thing you can do is support the project financially. Go to [https://salvium.io/donate](https://salvium.io/donate) for more information.
## License
@@ -172,7 +172,7 @@ invokes cmake commands as needed.
```bash
cd salvium
git checkout v1.0.0
git checkout v1.0.7
make
```
@@ -251,7 +251,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch (
```bash
git clone https://github.com/salvium/salvium
cd salvium
git checkout v1.0.0
git checkout v1.0.7
```
* Build:
@@ -370,10 +370,10 @@ application.
cd salvium
```
* If you would like a specific [version/tag](https://github.com/salvium/salvium/tags), do a git checkout for that version. eg. 'v1.0.0'. If you don't care about the version and just want binaries from master, skip this step:
* If you would like a specific [version/tag](https://github.com/salvium/salvium/tags), do a git checkout for that version. eg. 'v1.0.7'. If you don't care about the version and just want binaries from master, skip this step:
```bash
git checkout v1.0.0
git checkout v1.0.7
```
* If you are on a 64-bit system, run:
+10 -13
View File
@@ -214,11 +214,9 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, ''
#define MAX_RINGS 0xffffffff
struct tm prevtm = {0}, currtm;
uint64_t prevsz = 0, currsz = 0;
uint64_t prevtxs = 0, currtxs = 0;
uint64_t currblks = 0;
uint32_t txhr[24] = {0};
unsigned int i;
// uint64_t currsz = 0;
// uint64_t currtxs = 0;
// uint64_t currblks = 0;
const std::map<uint8_t, std::pair<uint64_t, std::pair<std::string, std::string>>> audit_hard_forks = get_config(net_type).AUDIT_HARD_FORKS;
@@ -246,9 +244,7 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, ''
prevtm = currtm;
}
skip:
currsz += bd.size();
uint64_t coinbase_amount;
uint64_t tx_fee_amount = 0;
// currsz += bd.size();
std::set<std::string> used_assets, miner_tx_assets, protocol_tx_assets;
std::map<size_t, std::vector<std::string>> used_tx_versions;
used_assets.insert("SAL");
@@ -325,10 +321,11 @@ skip:
std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "invalid TX detected" << delimiter << std::endl;
continue;
}
currsz += bd.size();
if (db->get_prunable_tx_blob(tx_id, bd))
currsz += bd.size();
currtxs++;
// currsz += bd.size();
// if (db->get_prunable_tx_blob(tx_id, bd))
// currsz += bd.size();
// currtxs++;
db->get_prunable_tx_blob(tx_id, bd);
if (tx.type != cryptonote::transaction_type::TRANSFER &&
tx.type != cryptonote::transaction_type::BURN &&
@@ -374,7 +371,7 @@ skip:
}
}
currblks++;
// currblks++;
if (stop_requested)
break;
@@ -211,8 +211,15 @@ int main(int argc, char* argv[])
throw std::runtime_error("Failed to initialize a database");
}
const std::string filename = (boost::filesystem::path(opt_data_dir) / db->get_db_name()).string();
LOG_PRINT_L0("Loading blockchain from folder " << filename << " ...");
boost::filesystem::path folder(opt_data_dir);
if (opt_stagenet) {
folder /= std::to_string(STAGENET_VERSION);
} else if (opt_testnet) {
folder /= std::to_string(TESTNET_VERSION);
}
folder /= db->get_db_name();
LOG_PRINT_L0("Loading blockchain from folder " << folder << " ...");
const std::string filename = folder.string();
try
{
Binary file not shown.
+73 -9
View File
@@ -282,13 +282,62 @@ crypto::key_image carrot_and_legacy_account::derive_key_image(const crypto::publ
return L;
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::generate_subaddress_map()
crypto::key_image carrot_and_legacy_account::derive_key_image_view_only(const crypto::public_key &address_spend_pubkey,
const crypto::secret_key &sender_extension_g,
const crypto::secret_key &sender_extension_t,
const crypto::public_key &onetime_address) const
{
const auto it = subaddress_map.find(address_spend_pubkey);
CHECK_AND_ASSERT_THROW_MES(it != subaddress_map.cend(),
"carrot and legacy account: derive key image view only: cannot find subaddress");
const bool is_subaddress = it->second.index.is_subaddress();
const uint32_t major_index = it->second.index.major;
const uint32_t minor_index = it->second.index.minor;
const cryptonote::account_keys &keys = get_keys();
crypto::secret_key address_index_generator;
crypto::secret_key subaddress_scalar;
crypto::secret_key subaddress_extension;
crypto::secret_key address_privkey_g;
crypto::secret_key x;
CHECK_AND_ASSERT_THROW_MES(it->second.derive_type == AddressDeriveType::Carrot,
"carrot and legacy account: derive key image view only: not a Carrot address");
// s^j_gen = H_32[s_ga](j_major, j_minor)
make_carrot_index_extension_generator(keys.s_generate_address, major_index, minor_index, address_index_generator);
if (is_subaddress)
{
// k^j_subscal = H_n(K_s, j_major, j_minor, s^j_gen)
make_carrot_subaddress_scalar(keys.m_carrot_account_address.m_spend_public_key, address_index_generator, major_index, minor_index, subaddress_scalar);
}
else
{
// k^j_subscal = 1
sc_1(to_bytes(subaddress_scalar));
}
// k^g_a = k_gi * k^j_subscal
sc_mul(to_bytes(address_privkey_g), to_bytes(keys.k_generate_image), to_bytes(subaddress_scalar));
// x = k^{j,g}_addr + k^g_o
sc_add(to_bytes(x), to_bytes(address_privkey_g), to_bytes(sender_extension_g));
crypto::key_image L;
crypto::generate_key_image(onetime_address, x, L);
return L;
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::generate_subaddress_map(const std::pair<size_t, size_t>& lookahead_size)
{
const std::vector<AddressDeriveType> derive_types{AddressDeriveType::Carrot, AddressDeriveType::PreCarrot};
for (uint32_t major_index = 0; major_index <= MAX_SUBADDRESS_MAJOR_INDEX; ++major_index)
for (uint32_t major_index = 0; major_index <= lookahead_size.first; ++major_index)
{
for (uint32_t minor_index = 0; minor_index <= MAX_SUBADDRESS_MINOR_INDEX; ++minor_index)
for (uint32_t minor_index = 0; minor_index <= lookahead_size.second; ++minor_index)
{
for (const AddressDeriveType derive_type : derive_types)
{
@@ -337,7 +386,7 @@ void carrot_and_legacy_account::set_keys(const cryptonote::account_keys& keys, b
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::create_from_svb_key(const cryptonote::account_public_address& address, const crypto::secret_key& svb_key)
{
{
// top level keys
m_keys.s_master = crypto::null_skey;
make_carrot_provespend_key(m_keys.s_master, m_keys.k_prove_spend);
@@ -359,9 +408,18 @@ void carrot_and_legacy_account::create_from_svb_key(const cryptonote::account_pu
k_view_incoming_dev.view_key_scalar_mult_ed25519(crypto::get_G(),
m_keys.m_carrot_main_address.m_view_public_key
);
// Store fields for Carrot
m_keys.m_spend_secret_key = crypto::null_skey;
m_keys.m_view_secret_key = crypto::null_skey;
m_keys.m_account_address = {crypto::null_pkey, crypto::null_pkey, false};
// Update ALL addresses to be Carrot-only
m_keys.m_carrot_account_address.m_is_carrot = true;
m_keys.m_carrot_main_address.m_is_carrot = true;
// Set the default derive type for addresses
this->default_derive_type = AddressDeriveType::Carrot;
generate_subaddress_map();
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::set_carrot_keys(const AddressDeriveType default_derive_type)
@@ -393,13 +451,19 @@ void carrot_and_legacy_account::set_carrot_keys(const AddressDeriveType default_
m_keys.m_carrot_main_address.m_is_carrot = true;
this->default_derive_type = default_derive_type;
generate_subaddress_map();
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::insert_subaddresses(const std::unordered_map<crypto::public_key, subaddress_index_extended>& subaddress_map_cn)
{
for (const auto &p : subaddress_map_cn)
subaddress_map.insert({p.first, {{p.second.index.major, p.second.index.minor}, p.second.derive_type, p.second.is_return_spend_key}});
for (const auto &p : subaddress_map_cn) {
subaddress_map.insert({p.first, {{p.second.index.major, p.second.index.minor}, p.second.derive_type, p.second.is_return_spend_key}});
if (p.second.derive_type == AddressDeriveType::PreCarrot) {
// Create a matching Carrot address
const subaddress_index_extended subaddr_index{{p.second.index.major, p.second.index.minor}, AddressDeriveType::Carrot, p.second.is_return_spend_key};
const CarrotDestinationV1 addr = subaddress(subaddr_index);
subaddress_map.insert({addr.address_spend_pubkey, subaddr_index});
}
}
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::insert_return_output_info(const std::unordered_map<crypto::public_key, return_output_info_t>& roi_map)
+61 -21
View File
@@ -48,12 +48,59 @@ namespace carrot
input_context_t input_context;
crypto::public_key K_o; // output onetime address
crypto::public_key K_change; // change output onetime address
crypto::public_key K_spend_pubkey; // change output spend pubkey
crypto::key_image key_image;
crypto::secret_key sum_g;
crypto::secret_key sender_extension_t;
return_output_info_t() {
// Default constructor for serialization
input_context = input_context_t();
K_o = crypto::public_key();
K_change = crypto::public_key();
K_spend_pubkey = crypto::public_key();
key_image = crypto::key_image();
sum_g = crypto::secret_key();
sender_extension_t = crypto::secret_key();
}
return_output_info_t(
const input_context_t &input_context,
const crypto::public_key &K_o,
const crypto::public_key &K_change,
const crypto::public_key &K_spend_pubkey,
const crypto::key_image &key_image,
const crypto::secret_key &sum_g,
const crypto::secret_key &sender_extension_t):
input_context(input_context),
K_o(K_o),
K_change(K_change),
K_spend_pubkey(K_spend_pubkey),
key_image(key_image),
sum_g(sum_g),
sender_extension_t(sender_extension_t) {}
BEGIN_SERIALIZE_OBJECT()
FIELD(input_context)
FIELD(K_o)
FIELD(K_change)
FIELD(K_spend_pubkey)
FIELD(key_image)
FIELD(sum_g)
FIELD(sender_extension_t)
END_SERIALIZE()
};
// Old return_output_info_t format (for deserializing version 2 wallet caches)
struct return_output_info_retired_t {
input_context_t input_context;
crypto::public_key K_o;
crypto::public_key K_change;
crypto::key_image key_image;
crypto::secret_key x;
crypto::secret_key y;
return_output_info_t() {
// Default constructor for serialization
return_output_info_retired_t() {
input_context = input_context_t();
K_o = crypto::public_key();
K_change = crypto::public_key();
@@ -62,20 +109,6 @@ namespace carrot
y = crypto::secret_key();
}
return_output_info_t(
const input_context_t &input_context,
const crypto::public_key &K_o,
const crypto::public_key &K_change,
const crypto::key_image &key_image,
const crypto::secret_key &x,
const crypto::secret_key &y):
input_context(input_context),
K_o(K_o),
K_change(K_change),
key_image(key_image),
x(x),
y(y) {}
BEGIN_SERIALIZE_OBJECT()
FIELD(input_context)
FIELD(K_o)
@@ -144,7 +177,12 @@ namespace carrot
const crypto::secret_key &sender_extension_t,
const crypto::public_key &onetime_address) const;
void generate_subaddress_map();
crypto::key_image derive_key_image_view_only(const crypto::public_key &address_spend_pubkey,
const crypto::secret_key &sender_extension_g,
const crypto::secret_key &sender_extension_t,
const crypto::public_key &onetime_address) const;
void generate_subaddress_map(const std::pair<size_t, size_t>& lookahead_size);
crypto::secret_key generate(
const crypto::secret_key& recovery_key = crypto::secret_key(),
@@ -183,9 +221,10 @@ namespace boost
x.input_context = carrot::input_context_t();
x.K_o = crypto::public_key();
x.K_change = crypto::public_key();
x.K_spend_pubkey = crypto::public_key();
x.key_image = crypto::key_image();
x.x = crypto::secret_key();
x.y = crypto::secret_key();
x.sum_g = crypto::secret_key();
x.sender_extension_t = crypto::secret_key();
}
template <class Archive>
@@ -194,9 +233,10 @@ namespace boost
a & x.input_context;
a & x.K_o;
a & x.K_change;
a & x.K_spend_pubkey;
a & x.key_image;
a & x.x;
a & x.y;
a & x.sum_g;
a & x.sender_extension_t;
}
}
}
+3 -3
View File
@@ -41,9 +41,9 @@
namespace carrot
{
#define CARROT_DEFINE_SIMPLE_ERROR_TYPE(e, b) class e: b { using b::b; };
#define CARROT_DEFINE_SIMPLE_ERROR_TYPE(e, b) class e: public b { using b::b; };
class carrot_logic_error: std::logic_error { using std::logic_error::logic_error; };
class carrot_logic_error: public std::logic_error { using std::logic_error::logic_error; };
CARROT_DEFINE_SIMPLE_ERROR_TYPE(bad_address_type, carrot_logic_error)
CARROT_DEFINE_SIMPLE_ERROR_TYPE(component_out_of_order, carrot_logic_error)
@@ -55,7 +55,7 @@ CARROT_DEFINE_SIMPLE_ERROR_TYPE(too_few_outputs, carrot_logic_error)
CARROT_DEFINE_SIMPLE_ERROR_TYPE(too_many_outputs, carrot_logic_error)
CARROT_DEFINE_SIMPLE_ERROR_TYPE(invalid_tx_type, carrot_logic_error)
class carrot_runtime_error: std::runtime_error { using std::runtime_error::runtime_error; };
class carrot_runtime_error: public std::runtime_error { using std::runtime_error::runtime_error; };
CARROT_DEFINE_SIMPLE_ERROR_TYPE(crypto_function_failed, carrot_runtime_error)
CARROT_DEFINE_SIMPLE_ERROR_TYPE(not_enough_money, carrot_runtime_error)
+1 -1
View File
@@ -77,7 +77,7 @@ std::optional<AdditionalOutputType> get_additional_output_type(const size_t num_
}
else if (!need_change_output)
{
return AdditionalOutputType::DUMMY;
return AdditionalOutputType::CHANGE_UNIQUE;
}
else // num_selfsend == 1 && need_change_output
{
+2
View File
@@ -219,6 +219,7 @@ bool operator==(const CarrotPaymentProposalV1 &a, const CarrotPaymentProposalV1
{
return a.destination == b.destination &&
a.amount == b.amount &&
a.asset_type == b.asset_type &&
a.randomness == b.randomness;
}
//-------------------------------------------------------------------------------------------------------------------
@@ -228,6 +229,7 @@ bool operator==(const CarrotPaymentProposalSelfSendV1 &a, const CarrotPaymentPro
a.amount == b.amount &&
a.enote_type == b.enote_type &&
a.internal_message == b.internal_message &&
a.asset_type == b.asset_type &&
0 == memcmp(&a.enote_ephemeral_pubkey, &b.enote_ephemeral_pubkey, sizeof(mx25519_pubkey));
}
//-------------------------------------------------------------------------------------------------------------------
+2
View File
@@ -82,6 +82,8 @@ struct CarrotPaymentProposalSelfSendV1 final
std::optional<mx25519_pubkey> enote_ephemeral_pubkey;
/// anchor: arbitrary, pre-encrypted message for _internal_ selfsends
std::optional<janus_anchor_t> internal_message;
/// asset type
std::string asset_type;
};
struct RCTOutputEnoteProposal
+19 -19
View File
@@ -387,6 +387,11 @@ bool try_scan_carrot_enote_external_sender(const CarrotEnoteV1 &enote,
CarrotEnoteType &enote_type_out,
const bool check_pid)
{
epee::span<const crypto::public_key> main_address_spend_pubkeys;
if (destination.is_subaddress)
main_address_spend_pubkeys = {};
else
main_address_spend_pubkeys = {&destination.address_spend_pubkey, 1};
crypto::public_key recovered_address_spend_pubkey;
payment_id_t recovered_payment_id;
CarrotEnoteType recovered_enote_type;
@@ -395,7 +400,7 @@ bool try_scan_carrot_enote_external_sender(const CarrotEnoteV1 &enote,
if (!try_scan_carrot_enote_external_normal_checked(enote,
encrypted_payment_id,
s_sender_receiver_unctx,
{&destination.address_spend_pubkey, 1},
main_address_spend_pubkeys,
sender_extension_g_out,
sender_extension_t_out,
recovered_address_spend_pubkey,
@@ -470,6 +475,9 @@ bool try_scan_carrot_enote_internal_receiver(const CarrotEnoteV1 &enote,
crypto::public_key &return_address_out,
bool &is_return_out)
{
// Determine whether this is a full wallet or a watch-only wallet
const cryptonote::account_keys &keys = account.get_keys();
// input_context
const input_context_t input_context = make_carrot_input_context(enote.tx_first_key_image);
@@ -488,7 +496,6 @@ bool try_scan_carrot_enote_internal_receiver(const CarrotEnoteV1 &enote,
input_context,
s_sender_receiver);
bool normal_change_found = true;
if (!try_scan_carrot_enote_internal_burnt(enote,
s_sender_receiver,
sender_extension_g_out,
@@ -518,24 +525,17 @@ bool try_scan_carrot_enote_internal_receiver(const CarrotEnoteV1 &enote,
// calculate the key image for the return output
crypto::secret_key sum_g;
sc_add(to_bytes(sum_g), to_bytes(sender_extension_g_out), to_bytes(k_return));
crypto::key_image key_image = account.derive_key_image(
account.get_keys().m_carrot_account_address.m_spend_public_key,
sum_g,
sender_extension_t_out,
K_r
);
crypto::key_image key_image = account.derive_key_image_view_only(address_spend_pubkey_out,
sum_g,
sender_extension_t_out,
K_r
);
crypto::secret_key x, y;
account.try_searching_for_opening_for_onetime_address(
account.get_keys().m_carrot_account_address.m_spend_public_key,
sum_g,
sender_extension_t_out,
x,
y
);
// save the input context & change output key
account.insert_return_output_info({{K_r, {input_context, output_key, enote.onetime_address, key_image, x, y}}});
// HERE BE DRAGONS!!!
// SRCG: test whether this will even work for return_payment detection
account.insert_return_output_info({{K_r, {input_context, output_key, enote.onetime_address, address_spend_pubkey_out, key_image, sum_g, sender_extension_t_out}}});
//account.insert_return_output_info({{K_r, {input_context, output_key, address_spend_pubkey_out, key_image, sum_g, sender_extension_t_out}}});
// LAND AHOY!!!
}
// janus protection checks are not needed for internal scans
+2 -1
View File
@@ -275,7 +275,8 @@ void make_carrot_transaction_proposal_v1_transfer(
.proposal = CarrotPaymentProposalSelfSendV1{
.destination_address_spend_pubkey = change_address_spend_pubkey,
.amount = 0,
.enote_type = add_payment_type_selfsend ? CarrotEnoteType::PAYMENT : CarrotEnoteType::CHANGE
.enote_type = add_payment_type_selfsend ? CarrotEnoteType::PAYMENT : CarrotEnoteType::CHANGE,
.asset_type = "SAL1"
},
.subaddr_index = change_address_index
});
+27 -22
View File
@@ -183,28 +183,33 @@ namespace cryptonote
bool checkpoints::init_default_checkpoints(network_type nettype)
{
if (nettype == MAINNET) {
ADD_CHECKPOINT2(1, "b6b45052e7e182ebaeb14ab713db29ad979115e664d766aa0910e325564a27a6", "0x2");
ADD_CHECKPOINT2(10, "82724681cf6bd934eb3253d041de50206a77627ce40ffe418ce6e0fe392ec684", "0x7812a");
ADD_CHECKPOINT2(20, "4dac7b512d876df05bfa4f39b8dbacd75cb1483fbced8bfc5446ebe21b25a04f", "0xba98f");
ADD_CHECKPOINT2(30, "668246360c93ef791a59157cec9cd09722b32a966051feea399082433138f07b", "0xcc235");
ADD_CHECKPOINT2(40, "9a4183bc1d6e9828eac46505c5ef37ae5447ba6c9325dca02be9e1201f939a7d", "0xdf077");
ADD_CHECKPOINT2(50, "5cd8b089d2e77aed9b803b398c6bff07ca652100cb8fa114c91b72509aeeb7e9", "0xf37eb");
ADD_CHECKPOINT2(60, "0e1acf00dd38e0757996dcdc4b69ad54baf7ebe10ae1e8168b192acb1a0ed7f2", "0x10993b");
ADD_CHECKPOINT2(70, "988977507f388221a927e279307b548a0ae0de10ded8c4f22c315e1b483f921a", "0x121537");
ADD_CHECKPOINT2(80, "88ea1c49b20e7596e21ca8137b2a9fa98558df269a15816fe7d7495f1c63ea43", "0x13a290");
ADD_CHECKPOINT2(90, "254800bb6f9794aad95b2226ffc1a1eef0a817472e1877ae08fac6becb55b147", "0x153a55");
ADD_CHECKPOINT2(100, "ba8d75fad878af26ac2504b4868893a7f86c59f013d0f096925cf53271dd04e8", "0x16e91e");
ADD_CHECKPOINT2(110, "dca0779bfe403730b923fa0918645daeec6096b953be2c554f133460c6fcce35", "0x18acb9");
ADD_CHECKPOINT2(120, "5a57287f6b5c105ae264b88050731c5b9ad1313b916143d7585af1d345e70247", "0x1a88f5");
ADD_CHECKPOINT2(130, "4fd292ac0774461e968924f8097e78ec03eee43a2997deaddbc7993e470a61d6", "0x1c6edd");
ADD_CHECKPOINT2(140, "5a3b6ceeef5fd498ea3330acb8a0e87f2c1566c9b0100ad67237e5664d1f053b", "0x1e4991");
ADD_CHECKPOINT2(150, "78f26d08d39f7d5e1a3548277321471e16c95096fa9bcecbe8a420d406ee249b", "0x202406");
ADD_CHECKPOINT2(160, "7acaab1037ccfbadd3126d2612d5dc154020297f980df0b8df462f9c761d3326", "0x22154b");
ADD_CHECKPOINT2(170, "9541ae934e40fa6749ca3453e47cf5fdf38efbac9efcaa2714121e8a21dd2d24", "0x241ce7");
ADD_CHECKPOINT2(180, "e20bc8ac6aabb6b0792f23a29ce42a577c6a57d177a8ac1a51b68fb6de508045", "0x262b40");
ADD_CHECKPOINT2(190, "f69fdad7a15471b63a82668b618ee5b2a384291269d944b11974a723c1604124", "0x2856a3");
ADD_CHECKPOINT2(200, "eba53fa7006dfcdc837a56c0bc8f0e1883cf34861c26934d680252a6878a3f5d", "0x2aa022");
ADD_CHECKPOINT2(90000, "e125b5c1b26521f98e29df6ec88f041c176a2c0a3fcacd5bd0ad2278e9b02fd2", "0xc99801f937888"); // 3546475285149832
ADD_CHECKPOINT2(1, "b6b45052e7e182ebaeb14ab713db29ad979115e664d766aa0910e325564a27a6", "0x2");
ADD_CHECKPOINT2(10, "82724681cf6bd934eb3253d041de50206a77627ce40ffe418ce6e0fe392ec684", "0x7812a");
ADD_CHECKPOINT2(20, "4dac7b512d876df05bfa4f39b8dbacd75cb1483fbced8bfc5446ebe21b25a04f", "0xba98f");
ADD_CHECKPOINT2(30, "668246360c93ef791a59157cec9cd09722b32a966051feea399082433138f07b", "0xcc235");
ADD_CHECKPOINT2(40, "9a4183bc1d6e9828eac46505c5ef37ae5447ba6c9325dca02be9e1201f939a7d", "0xdf077");
ADD_CHECKPOINT2(50, "5cd8b089d2e77aed9b803b398c6bff07ca652100cb8fa114c91b72509aeeb7e9", "0xf37eb");
ADD_CHECKPOINT2(60, "0e1acf00dd38e0757996dcdc4b69ad54baf7ebe10ae1e8168b192acb1a0ed7f2", "0x10993b");
ADD_CHECKPOINT2(70, "988977507f388221a927e279307b548a0ae0de10ded8c4f22c315e1b483f921a", "0x121537");
ADD_CHECKPOINT2(80, "88ea1c49b20e7596e21ca8137b2a9fa98558df269a15816fe7d7495f1c63ea43", "0x13a290");
ADD_CHECKPOINT2(90, "254800bb6f9794aad95b2226ffc1a1eef0a817472e1877ae08fac6becb55b147", "0x153a55");
ADD_CHECKPOINT2(100, "ba8d75fad878af26ac2504b4868893a7f86c59f013d0f096925cf53271dd04e8", "0x16e91e");
ADD_CHECKPOINT2(110, "dca0779bfe403730b923fa0918645daeec6096b953be2c554f133460c6fcce35", "0x18acb9");
ADD_CHECKPOINT2(120, "5a57287f6b5c105ae264b88050731c5b9ad1313b916143d7585af1d345e70247", "0x1a88f5");
ADD_CHECKPOINT2(130, "4fd292ac0774461e968924f8097e78ec03eee43a2997deaddbc7993e470a61d6", "0x1c6edd");
ADD_CHECKPOINT2(140, "5a3b6ceeef5fd498ea3330acb8a0e87f2c1566c9b0100ad67237e5664d1f053b", "0x1e4991");
ADD_CHECKPOINT2(150, "78f26d08d39f7d5e1a3548277321471e16c95096fa9bcecbe8a420d406ee249b", "0x202406");
ADD_CHECKPOINT2(160, "7acaab1037ccfbadd3126d2612d5dc154020297f980df0b8df462f9c761d3326", "0x22154b");
ADD_CHECKPOINT2(170, "9541ae934e40fa6749ca3453e47cf5fdf38efbac9efcaa2714121e8a21dd2d24", "0x241ce7");
ADD_CHECKPOINT2(180, "e20bc8ac6aabb6b0792f23a29ce42a577c6a57d177a8ac1a51b68fb6de508045", "0x262b40");
ADD_CHECKPOINT2(190, "f69fdad7a15471b63a82668b618ee5b2a384291269d944b11974a723c1604124", "0x2856a3");
ADD_CHECKPOINT2(200, "eba53fa7006dfcdc837a56c0bc8f0e1883cf34861c26934d680252a6878a3f5d", "0x2aa022");
ADD_CHECKPOINT2(90000, "e125b5c1b26521f98e29df6ec88f041c176a2c0a3fcacd5bd0ad2278e9b02fd2", "0xc99801f937888"); // 3546475285149832
ADD_CHECKPOINT2(100000, "ff4e8ec805d5bfbcd01f350ac071be1d944ba73e0d27e37d12acb549902b3f3d", "0xDA97F5697F7D8"); // 3845539075979224
ADD_CHECKPOINT2(150000, "c43281eb5b2a41ee77a4465735e4820c6d14d473b568df7987541afc48f18568", "0x12BDA9ED687AAF"); // 5275087110961839
ADD_CHECKPOINT2(225000, "7648405f7cfb24341d9580275b518bb3713c68e970f547faa0b3bcae450d9ec2", "0x18CD5F1B7BA43A"); // 6981207807730746
ADD_CHECKPOINT2(300000, "a18ca65464c1f2d876dd3a00643c9be265655d6bca364eccc5e3d628b9a5cd2c", "0x1F0C2A50FE631C"); // 8739100165038876
ADD_CHECKPOINT2(375000, "07f0c907cc5cb44cef88e5899a411adddd4b0a4419210906e0583efdcafb499f", "0x240C7F9742F265"); // 10146841299710565
}
return true;
}
+1
View File
@@ -71,6 +71,7 @@ monero_add_library(cncrypto
target_link_libraries(cncrypto
PUBLIC
epee
mx25519_static
randomx
${Boost_SYSTEM_LIBRARY}
${SODIUM_LIBRARY}
+4 -1
View File
@@ -887,4 +887,7 @@ const ge_p3 ge_p3_H = {
{5858699, 5096796, 21321203, -7536921, -5553480, -11439507, -5627669, 15045946, 19977121, 5275251},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{23443568, -5110398, -8776029, -4345135, 6889568, -14710814, 7474843, 3279062, 14550766, -7453428}
};
};
// Precomputed sqrt(-486664) mod p as 32-byte little-endian array
const fe fe_sqrt_m486664 = {12222970, 8312128, 11511410, -9067497, 15300785, 241793, -25456130, -14121551, 12187136, -3972024};
+125 -2
View File
@@ -104,6 +104,21 @@ void fe_1(fe h) {
h[9] = 0;
}
int fe_equal(const fe a, const fe b)
{
fe t;
fe_sub(t, a, b);
return fe_isnonzero(t) == 0;
}
void ge_from_xy(ge_p3 *out, const fe x, const fe y)
{
fe_1(out->Z); // Z = 1
fe_copy(out->X, x); // X = x
fe_copy(out->Y, y); // Y = y
fe_mul(out->T, x, y); // T = x*y
}
/* From fe_add.c */
/*
@@ -365,7 +380,7 @@ int fe_isnegative(const fe f) {
/* From fe_isnonzero.c, modified */
static int fe_isnonzero(const fe f) {
int fe_isnonzero(const fe f) {
unsigned char s[32];
fe_tobytes(s, f);
return (((int) (s[0] | s[1] | s[2] | s[3] | s[4] | s[5] | s[6] | s[7] | s[8] |
@@ -3967,6 +3982,114 @@ int edwards_bytes_to_x25519_vartime(unsigned char *xbytes, const unsigned char *
return 0;
}
// Precomputed sqrt(-1) from Monero (fe_sqrtm1 in crypto-ops-data.c)
extern const fe fe_sqrtm1;
extern const fe fe_sqrt_m486664;
// Function to recover v from u (returns 0 on success, -1 if not on curve)
int fe_sqrt_mont(fe v_out, const fe u_in) {
fe rhs;
fe t0, t1, t2;
fe candidate, c2, check;
// Compute rhs = u^3 + A u^2 + u, A=486662
fe A_fe;
fe_frombytes_vartime(A_fe, (const unsigned char[]){0x06, 0x6D, 0x07, 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}); // Correct little-endian 486662
fe_sq(t0, u_in); // t0 = u^2
fe_mul(t1, t0, u_in); // t1 = u^3
fe_mul(t2, t0, A_fe); // t2 = A u^2
fe_add(rhs, t1, t2); // u^3 + A u^2
fe_add(rhs, rhs, u_in); // + u
// The exponentiation chain for (p+3)/8
fe_1(t1);
fe_sq(t0, t1);
fe_mul(t0, t0, t1);
fe_sq(candidate, t0);
fe_mul(candidate, candidate, t1);
fe_mul(candidate, candidate, rhs);
fe_pow22523(candidate, candidate);
fe_mul(candidate, candidate, t0);
fe_mul(candidate, candidate, rhs);
// Check c^2 == rhs or -rhs
fe_sq(c2, candidate);
fe_sub(check, c2, rhs);
if (fe_isnonzero(check)) {
fe_add(check, c2, rhs);
if (fe_isnonzero(check)) {
return -1; // Not a quadratic residue
}
fe_mul(candidate, candidate, fe_sqrtm1); // Adjust with sqrt(-1)
}
// Output v (principal root; flip to -v if needed for your map)
fe_copy(v_out, candidate);
return 0;
}
void mont_to_ed(fe x_out, fe y_out, const fe u, const fe v) {
fe inv_v, temp;
fe t1, t2, inv_t2;
fe one;
fe_1(one);
fe_invert(inv_v, v); // 1/v
fe_mul(temp, u, inv_v); // u/v
fe_mul(x_out, fe_sqrt_m486664, temp); // sqrt(-486664) * (u/v)
fe_sub(t1, u, one); // u - 1
fe_add(t2, u, one); // u + 1
fe_invert(inv_t2, t2);
fe_mul(y_out, t1, inv_t2); // (u-1)/(u+1)
}
void ed_to_mont(fe u_out, fe v_out, const fe x, const fe y) {
fe t1, t2, inv_t2;
fe one;
fe_1(one);
fe_add(t1, one, y); // 1 + y
fe_sub(t2, one, y); // 1 - y
fe_invert(inv_t2, t2);
fe_mul(u_out, t1, inv_t2); // (1+y)/(1-y)
fe inv_x;
fe_invert(inv_x, x); // 1/x
fe_mul(t1, u_out, inv_x); // u / x
fe_mul(v_out, fe_sqrt_m486664, t1); // sqrt(-486664) * (u/x)
}
// Usage: Add two Montgomery points
void add_mont_points(fe u3, fe v3, const fe u1, const fe v1, const fe u2, const fe v2) {
ge_p3 P_ed, Q_ed, sum_ed;
// Convert to Edwards
fe x1, y1, x2, y2;
mont_to_ed(x1, y1, u1, v1);
mont_to_ed(x2, y2, u2, v2);
// Load into ge_p3 (assume Z=1, T=x*y for affine)
fe_1(P_ed.Z); fe_mul(P_ed.T, x1, y1); fe_copy(P_ed.X, x1); fe_copy(P_ed.Y, y1);
fe_1(Q_ed.Z); fe_mul(Q_ed.T, x2, y2); fe_copy(Q_ed.X, x2); fe_copy(Q_ed.Y, y2);
// Add using ge_
ge_cached Q_cached;
ge_p3_to_cached(&Q_cached, &Q_ed);
ge_p1p1 sum_p1p1;
ge_add(&sum_p1p1, &P_ed, &Q_cached);
ge_p1p1_to_p3(&sum_ed, &sum_p1p1);
// Convert back (normalize to affine: divide by Z)
fe inv_z;
fe_invert(inv_z, sum_ed.Z);
fe x_out, y_out;
fe_mul(x_out, sum_ed.X, inv_z);
fe_mul(y_out, sum_ed.Y, inv_z);
ed_to_mont(u3, v3, x_out, y_out);
}
int ge_p3_is_point_at_infinity_vartime(const ge_p3 *p) {
// https://eprint.iacr.org/2008/522
// X == T == 0 and Y/Z == 1
@@ -4063,4 +4186,4 @@ void fe_dbl(fe h, const fe f)
// Reduce the output for safety to ensure the result can be used as input to
// fe_add or fe_sub without an extra call to fe_reduce
fe_reduce(h, h_res);
}
}
+10 -1
View File
@@ -176,6 +176,11 @@ int sc_isnonzero(const unsigned char *); /* Doesn't normalize */
void ge_p3_to_x25519(unsigned char *xbytes, const ge_p3 *h);
int edwards_bytes_to_x25519_vartime(unsigned char *xbytes, const unsigned char *s);
int fe_sqrt_mont(fe v_out, const fe u_in);
void mont_to_ed(fe x_out, fe y_out, const fe u, const fe v);
void ed_to_mont(fe u_out, fe v_out, const fe x, const fe y);
void add_mont_points(fe u3, fe v3, const fe u1, const fe v1, const fe u2, const fe v2);
// internal
uint64_t load_3(const unsigned char *in);
uint64_t load_4(const unsigned char *in);
@@ -192,10 +197,14 @@ void fe_sq(fe h, const fe f);
void fe_sub(fe h, const fe f, const fe g);
void fe_0(fe h);
void fe_1(fe h);
int fe_equal(const fe a, const fe b);
void ge_from_xy(ge_p3 *out, const fe x, const fe y);
int fe_isnonzero(const fe f);
int ge_p3_is_point_at_infinity_vartime(const ge_p3 *p);
void fe_ed_y_derivatives_to_wei_x(unsigned char *wei_x, const fe inv_one_minus_y, const fe one_plus_y);
void fe_reduce(fe reduced_f, const fe f);
void fe_dbl(fe h, const fe f);
void fe_dbl(fe h, const fe f);
+558 -1
View File
@@ -41,6 +41,7 @@
#include "common/varint.h"
#include "warnings.h"
#include "crypto.h"
#include "mx25519.h"
#include "hash.h"
#include "cryptonote_config.h"
@@ -90,6 +91,16 @@ namespace crypto {
return &reinterpret_cast<const unsigned char &>(scalar);
}
static const mx25519_impl* get_mx25519_impl()
{
static std::once_flag of;
static const mx25519_impl *impl;
std::call_once(of, [&](){ impl = mx25519_select_impl(MX25519_TYPE_AUTO); });
if (impl == nullptr)
throw std::runtime_error("failed to obtain a mx25519 implementation");
return impl;
}
boost::mutex &get_random_lock()
{
static boost::mutex random_lock;
@@ -504,6 +515,391 @@ namespace crypto {
memwipe(&k, sizeof(k));
}
void crypto_ops::generate_carrot_tx_proof(
const hash &prefix_hash,
const public_key &R, // X25519 u-coordinate
const public_key &A, // Ed25519
const boost::optional<public_key> &B, // Ed if present
const public_key &D, // X25519 u-coordinate
const secret_key &r,
const secret_key &a,
signature &sig)
{
// Check if we are sender or receiver
if (r != crypto::null_skey) {
// SENDER
generate_carrot_tx_proof_as_sender(prefix_hash, R, A, B, D, r, a, sig);
return;
}
// RECEIVER
// Load points (A and B and R) into ge_p3
ge_p3 A_p3;
ge_p3 B_p3;
ge_p3 R_p3;
if (ge_frombytes_vartime(&A_p3, &A) != 0)
throw std::runtime_error("recipient view pubkey is invalid");
if (B && ge_frombytes_vartime(&B_p3, &*B) != 0)
throw std::runtime_error("recipient spend pubkey is invalid");
#if !defined(NDEBUG)
{
// Debug check D == a*R
mx25519_pubkey D_x25519;
mx25519_scmul_key(get_mx25519_impl(),
&D_x25519,
reinterpret_cast<const mx25519_privkey*>(&a),
reinterpret_cast<const mx25519_pubkey*>(&R));
public_key dbg_D;
memcpy(&dbg_D, &D_x25519, sizeof(mx25519_pubkey));
assert(D == dbg_D);
}
#endif
//
// 1. Pick random nonce k
//
crypto::secret_key k;
random_scalar(k);
static const public_key zero = {{
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,0x00
}};
s_comm_2 buf;
buf.msg = prefix_hash;
buf.D = D; // X25519 u-coord
buf.R = R; // X25519 u-coord
buf.A = A; // Ed25519
buf.B = B ? *B : zero;
cn_fast_hash(config::HASH_KEY_TXPROOF_V2,
sizeof(config::HASH_KEY_TXPROOF_V2)-1,
buf.sep);
//
// 2. Compute X = ConvertPointE(k*G or k*B)
//
ge_p3 kB_or_kG_p3;
if (B)
ge_scalarmult_p3(&kB_or_kG_p3, &k, &B_p3);
else
ge_scalarmult_base(&kB_or_kG_p3, &k);
mx25519_pubkey X_x25519;
ge_p3_to_x25519(X_x25519.data, &kB_or_kG_p3);
memcpy(&buf.X, &X_x25519, sizeof(mx25519_pubkey));
//
// 3. Compute Y = k*R
//
mx25519_pubkey Y;
mx25519_scmul_key(get_mx25519_impl(),
&Y,
reinterpret_cast<const mx25519_privkey*>(&k),
reinterpret_cast<const mx25519_pubkey*>(&R));
memcpy(&buf.Y, &Y, sizeof(mx25519_pubkey));
// ---------- Extract and lift R ----------
fe u_R;
fe_frombytes_vartime(u_R, (const unsigned char *)&R);
fe v_R_cand;
if (fe_sqrt_mont(v_R_cand, u_R) != 0)
throw std::runtime_error("R not on curve");
fe x1, y1, x2, y2, v_R_neg;
ge_p3 R_ed1, R_ed2;
// +v (principal)
mont_to_ed(x1, y1, u_R, v_R_cand);
ge_from_xy(&R_ed1, x1, y1);
// -v
fe_neg(v_R_neg, v_R_cand);
mont_to_ed(x2, y2, u_R, v_R_neg);
ge_from_xy(&R_ed2, x2, y2);
// Arbitrarily choose R_sign = true (principal v from fe_sqrt_mont)
bool R_sign = true;
ge_p3 R_ed_correct = R_ed1; // +v
// ---------- Extract and lift D (consistent with chosen R_sign) ----------
fe u_D;
fe_frombytes_vartime(u_D, (const unsigned char *)&D);
fe v_D_cand;
if (fe_sqrt_mont(v_D_cand, u_D) != 0)
throw std::runtime_error("D not on curve");
fe x3, y3, x4, y4, v_D_neg;
// Compute D_ed_true = a * R_ed_correct
ge_p3 D_ed_true;
ge_scalarmult_p3(&D_ed_true, &a, &R_ed_correct);
// Normalize to affine for matching
fe inv_z;
fe_invert(inv_z, D_ed_true.Z);
fe xd_true, yd_true;
fe_mul(xd_true, D_ed_true.X, inv_z);
fe_mul(yd_true, D_ed_true.Y, inv_z);
// +v for D
mont_to_ed(x3, y3, u_D, v_D_cand);
bool D_match1 = fe_equal(x3, xd_true) && fe_equal(y3, yd_true); // Affine match (mont_to_ed gives affine x,y)
// -v for D
fe_neg(v_D_neg, v_D_cand);
mont_to_ed(x4, y4, u_D, v_D_neg);
bool D_match2 = fe_equal(x4, xd_true) && fe_equal(y4, yd_true);
bool D_sign = false;
if (D_match1)
D_sign = true;
else if (D_match2)
D_sign = false;
else
throw std::runtime_error("D lift mismatch with computed D_ed_true");
// Pack signs (MSB is set to [1] for outbound, [0] for inbound
sig.sign_mask =
(R_sign ? 0x01 : 0x00) |
(D_sign ? 0x02 : 0x00);
struct {
s_comm_2 buf;
uint8_t sign_mask;
} challenge_hash;
challenge_hash.buf = buf;
challenge_hash.sign_mask = sig.sign_mask;
//
// 7. Compute challenge c = H(prefix_hash || … || sign_mask)
//
hash_to_scalar(&challenge_hash, sizeof(challenge_hash), sig.c);
//
// 8. Compute response z = k - c*a
//
sc_mulsub(&sig.r, &sig.c, &unwrap(a), &k);
memwipe(&k, sizeof(k));
#if !defined(NDEBUG)
bool ok = check_carrot_tx_proof(prefix_hash, R, A, B, D, sig);
assert(ok);
#endif
}
void crypto_ops::generate_carrot_tx_proof_as_sender(
const hash &prefix_hash,
const public_key &R, // X25519 u-coordinate
const public_key &A, // Ed25519
const boost::optional<public_key> &B, // Ed if present
const public_key &D, // X25519 u-coordinate
const secret_key &r,
const secret_key &a,
signature &sig)
{
// Load only Ed points (A and B) into ge_p3
ge_p3 A_p3;
ge_p3 B_p3;
if (ge_frombytes_vartime(&A_p3, &A) != 0)
throw std::runtime_error("recipient view pubkey is invalid");
if (B && ge_frombytes_vartime(&B_p3, &*B) != 0)
throw std::runtime_error("recipient spend pubkey is invalid");
#if !defined(NDEBUG)
{
assert(sc_check(&r) == 0);
// Debug check R == ConvertPointE(r*G or r*B)
public_key dbg_R;
ge_p3 dbg_R_p3;
if (B)
ge_scalarmult_p3(&dbg_R_p3, &r, &B_p3);
else
ge_scalarmult_base(&dbg_R_p3, &r);
mx25519_pubkey R_x25519;
ge_p3_to_x25519(R_x25519.data, &dbg_R_p3);
memcpy(&dbg_R, &R_x25519, sizeof(mx25519_pubkey));
assert(R == dbg_R);
// Debug check D == ConvertPointE(r*A)
public_key dbg_D;
ge_p3 dbg_D_p3;
ge_scalarmult_p3(&dbg_D_p3, &r, &A_p3);
mx25519_pubkey D_x25519;
ge_p3_to_x25519(D_x25519.data, &dbg_D_p3);
memcpy(&dbg_D, &D_x25519, sizeof(mx25519_pubkey));
assert(D == dbg_D);
}
#endif
//
// 1. Pick random nonce k
//
ec_scalar k;
random_scalar(k);
static const ec_point zero = {{
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,0x00
}};
s_comm_2 buf;
buf.msg = prefix_hash;
buf.D = D; // X25519 u-coord
buf.R = R; // X25519 u-coord
buf.A = A; // Ed25519
buf.B = B ? *B : zero;
cn_fast_hash(config::HASH_KEY_TXPROOF_V2,
sizeof(config::HASH_KEY_TXPROOF_V2)-1,
buf.sep);
//
// 2. Compute X = ConvertPointE(k*G or k*B)
//
ge_p3 kB_or_kG_p3;
if (B)
ge_scalarmult_p3(&kB_or_kG_p3, &k, &B_p3);
else
ge_scalarmult_base(&kB_or_kG_p3, &k);
mx25519_pubkey X_x25519;
ge_p3_to_x25519(X_x25519.data, &kB_or_kG_p3);
memcpy(&buf.X, &X_x25519, sizeof(mx25519_pubkey));
//
// 3. Compute Y = ConvertPointE(k*A)
//
ge_p3 kA_p3;
ge_scalarmult_p3(&kA_p3, &k, &A_p3);
mx25519_pubkey Y_x25519;
ge_p3_to_x25519(Y_x25519.data, &kA_p3);
memcpy(&buf.Y, &Y_x25519, sizeof(mx25519_pubkey));
//
// 4. Compute true Ed points R_ed_true and D_ed_true
//
ge_p3 R_ed_true, D_ed_true;
if (B)
ge_scalarmult_p3(&R_ed_true, &r, &B_p3);
else
ge_scalarmult_base(&R_ed_true, &r);
ge_scalarmult_p3(&D_ed_true, &r, &A_p3);
//
// 5. Determine sign bits for R and D
//
// ---------- Extract and lift R ----------
fe u_R;
fe_frombytes_vartime(u_R, (const unsigned char *)&R);
fe v_R_cand;
if (fe_sqrt_mont(v_R_cand, u_R) != 0)
throw std::runtime_error("R not on curve");
fe x1, y1, x2, y2, v_R_neg, v_D_neg;
mont_to_ed(x1, y1, u_R, v_R_cand);
fe_neg(v_R_neg, v_R_cand);
mont_to_ed(x2, y2, u_R, v_R_neg);
// Compute affine Edwards coords of R_ed_true
fe inv_z, xr_true, yr_true;
fe_invert(inv_z, R_ed_true.Z);
fe_mul(xr_true, R_ed_true.X, inv_z);
fe_mul(yr_true, R_ed_true.Y, inv_z);
bool R_match1 = fe_equal(xr_true, x1) && fe_equal(yr_true, y1);
bool R_match2 = fe_equal(xr_true, x2) && fe_equal(yr_true, y2);
if (!R_match1 && !R_match2)
throw std::runtime_error("R mapping mismatch");
bool R_sign = R_match1;
// ---------- Extract and lift D ----------
fe u_D;
fe_frombytes_vartime(u_D, (const unsigned char *)&D);
fe v_D_cand;
if (fe_sqrt_mont(v_D_cand, u_D) != 0)
throw std::runtime_error("D not on curve");
mont_to_ed(x1, y1, u_D, v_D_cand);
fe_neg(v_D_neg, v_D_cand);
mont_to_ed(x2, y2, u_D, v_D_neg);
fe_invert(inv_z, D_ed_true.Z);
fe_mul(xr_true, D_ed_true.X, inv_z);
fe_mul(yr_true, D_ed_true.Y, inv_z);
bool D_match1 = fe_equal(xr_true, x1) && fe_equal(yr_true, y1);
bool D_match2 = fe_equal(xr_true, x2) && fe_equal(yr_true, y2);
if (!D_match1 && !D_match2)
throw std::runtime_error("D mapping mismatch");
bool D_sign = D_match1;
//
// 6. Pack sign bits into signature, include in challenge hash
//
sig.sign_mask =
(R_sign ? 0x01 : 0x00) |
(D_sign ? 0x02 : 0x00) |
0x80;
struct {
s_comm_2 buf;
uint8_t sign_mask;
} challenge_hash;
challenge_hash.buf = buf;
challenge_hash.sign_mask = sig.sign_mask;
//
// 7. Compute challenge c = H(prefix_hash || … || sign_mask)
//
hash_to_scalar(&challenge_hash, sizeof(challenge_hash), sig.c);
//
// 8. Compute response z = k - c*r
//
sc_mulsub(&sig.r, &sig.c, &unwrap(r), &k);
memwipe(&k, sizeof(k));
#if !defined(NDEBUG)
bool ok = check_carrot_tx_proof(prefix_hash, R, A, B, D, sig);
assert(ok);
#endif
}
// Verify a proof: either v1 (version == 1) or v2 (version == 2)
bool crypto_ops::check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig, const int version) {
// sanity check
@@ -608,6 +1004,167 @@ namespace crypto {
return sc_isnonzero(&c2) == 0;
}
// R and D are provided in X25519 format (u-coordinate), A and B in Ed25519.
bool crypto_ops::check_carrot_tx_proof(
const hash &prefix_hash,
const public_key &R, // X25519 u
const public_key &A, // Ed25519 viewkey
const boost::optional<public_key> &B, // Ed25519 spendkey if any
const public_key &D, // X25519 u
const signature &sig)
{
ge_p3 A_p3, B_p3;
if (ge_frombytes_vartime(&A_p3, &A) != 0)
return false;
if (B && ge_frombytes_vartime(&B_p3, &*B) != 0)
return false;
if (sc_check(&sig.c) != 0 || sc_check(&sig.r) != 0)
return false;
// Extract sign bits and direction flag
const bool R_sign = (sig.sign_mask & 0x01) != 0;
const bool D_sign = (sig.sign_mask & 0x02) != 0;
const bool outbound = (sig.sign_mask & 0x80) != 0;
//
// 1. Reconstruct R_ed and D_ed from X25519 u-coords + sign bits
//
// ----- R -----
fe u_R, v_R_candidate, v_R;
fe_frombytes_vartime(u_R, (const unsigned char *)&R);
if (fe_sqrt_mont(v_R_candidate, u_R) != 0)
return false;
if (R_sign) fe_copy(v_R, v_R_candidate);
else fe_neg(v_R, v_R_candidate);
fe x_R, y_R;
mont_to_ed(x_R, y_R, u_R, v_R);
ge_p3 R_ed;
ge_from_xy(&R_ed, x_R, y_R); // Z=1, T=X*Y
// ----- D -----
fe u_D, v_D_candidate, v_D;
fe_frombytes_vartime(u_D, (const unsigned char *)&D);
if (fe_sqrt_mont(v_D_candidate, u_D) != 0)
return false;
if (D_sign) fe_copy(v_D, v_D_candidate);
else fe_neg(v_D, v_D_candidate);
fe x_D, y_D;
mont_to_ed(x_D, y_D, u_D, v_D);
ge_p3 D_ed;
ge_from_xy(&D_ed, x_D, y_D);
//
// 2. Compute X'
// If inbound proof, X`= z*G + c*A (or z*B + c*A)
// If outbound proof, X`= z*G + c*R_ed (or z*B + c*R_ed)
//
ge_p3 c_p3;
if (outbound)
ge_scalarmult_p3(&c_p3, &sig.c, &R_ed);
else
ge_scalarmult_p3(&c_p3, &sig.c, &A_p3);
ge_p1p1 X_p1p1;
if (B)
{
// Subaddress: X' = c*A + z*B
ge_p3 rB_p3;
ge_scalarmult_p3(&rB_p3, &sig.r, &B_p3);
ge_cached rB_cached;
ge_p3_to_cached(&rB_cached, &rB_p3);
ge_add(&X_p1p1, &c_p3, &rB_cached);
}
else
{
// Main address: X' = c*R_ed + z*G
ge_p3 rG_p3;
ge_scalarmult_base(&rG_p3, &sig.r);
ge_cached rG_cached;
ge_p3_to_cached(&rG_cached, &rG_p3);
ge_add(&X_p1p1, &c_p3, &rG_cached);
}
ge_p3 X_ed_p3;
ge_p1p1_to_p3(&X_ed_p3, &X_p1p1);
mx25519_pubkey X_x25519;
ge_p3_to_x25519(X_x25519.data, &X_ed_p3);
//
// 3. Compute Y'
// If inbound, Y' = c*D_ed + z*R
// If outbound, Y' = c*D_ed + z*A
//
ge_p3 cD_p3;
ge_scalarmult_p3(&cD_p3, &sig.c, &D_ed);
ge_p3 z_p3;
if (outbound)
ge_scalarmult_p3(&z_p3, &sig.r, &A_p3);
else
ge_scalarmult_p3(&z_p3, &sig.r, &R_ed);
ge_cached z_cached;
ge_p3_to_cached(&z_cached, &z_p3);
ge_p1p1 Y_p1p1;
ge_add(&Y_p1p1, &cD_p3, &z_cached);
ge_p3 Y_ed_p3;
ge_p1p1_to_p3(&Y_ed_p3, &Y_p1p1);
mx25519_pubkey Y_x25519;
ge_p3_to_x25519(Y_x25519.data, &Y_ed_p3);
//
// 4. Rebuild the hash transcript exactly as the prover did
//
static const ec_point zero = {{
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,0x00
}};
s_comm_2 buf;
buf.msg = prefix_hash;
buf.D = D; // X25519 (same bytes as prover)
buf.R = R; // X25519
buf.A = A; // Ed25519
buf.B = B ? *B : zero;
cn_fast_hash(config::HASH_KEY_TXPROOF_V2,
sizeof(config::HASH_KEY_TXPROOF_V2)-1,
buf.sep);
memcpy(&buf.X, &X_x25519, sizeof(mx25519_pubkey));
memcpy(&buf.Y, &Y_x25519, sizeof(mx25519_pubkey));
struct {
s_comm_2 buf;
uint8_t sign_mask;
} challenge_hash;
challenge_hash.buf = buf;
challenge_hash.sign_mask = sig.sign_mask;
//
// 5. Recompute challenge and compare with sig.c
//
ec_scalar c2;
hash_to_scalar(&challenge_hash, sizeof(challenge_hash), c2);
sc_sub(&c2, &c2, &sig.c);
return sc_isnonzero(&c2) == 0;
}
static void hash_to_ec(const public_key &key, ge_p3 &res) {
hash h;
ge_p2 point;
@@ -796,4 +1353,4 @@ POP_WARNINGS
ki.data[31] ^= 0x80;
}
}
}
}
+28 -3
View File
@@ -85,6 +85,7 @@ namespace crypto {
POD_CLASS signature {
ec_scalar c, r;
uint8_t sign_mask;
friend class crypto_ops;
};
@@ -99,7 +100,7 @@ namespace crypto {
static_assert(sizeof(ec_point) == 32 && sizeof(ec_scalar) == 32 &&
sizeof(public_key) == 32 && sizeof(public_key_memsafe) == 32 && sizeof(secret_key) == 32 &&
sizeof(key_derivation) == 32 && sizeof(key_image) == 32 && sizeof(key_image_y) == 32 &&
sizeof(signature) == 64 && sizeof(view_tag) == 1, "Invalid structure size");
sizeof(signature) == 65 && sizeof(view_tag) == 1, "Invalid structure size");
class crypto_ops {
crypto_ops();
@@ -131,8 +132,14 @@ namespace crypto {
friend void generate_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
static void generate_tx_proof_v1(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
friend void generate_tx_proof_v1(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
static void generate_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, const secret_key &, signature &);
friend void generate_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, const secret_key &, signature &);
static void generate_carrot_tx_proof_as_sender(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, const secret_key &, signature &);
friend void generate_carrot_tx_proof_as_sender(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, const secret_key &, signature &);
static bool check_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &, const int);
friend bool check_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &, const int);
static bool check_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &);
friend bool check_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &);
static void derive_key_image_generator(const public_key &, ec_point &);
friend void derive_key_image_generator(const public_key &, ec_point &);
static void generate_key_image(const public_key &, const secret_key &, key_image &);
@@ -260,10 +267,28 @@ namespace crypto {
inline void generate_tx_proof_v1(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, signature &sig) {
crypto_ops::generate_tx_proof_v1(prefix_hash, R, A, B, D, r, sig);
}
/* Generation of a carrot tx proof; for carrot transactions, D is in X25519 domain (D = r*ConvertPointE(A))
* instead of Ed25519 domain (D = r*A). This version applies ConvertPointE transformation.
*/
inline void generate_carrot_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, const secret_key &a, signature &sig) {
crypto_ops::generate_carrot_tx_proof(prefix_hash, R, A, B, D, r, a, sig);
}
inline void generate_carrot_tx_proof_as_sender(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, const secret_key &a, signature &sig) {
crypto_ops::generate_carrot_tx_proof_as_sender(prefix_hash, R, A, B, D, r, a, sig);
}
inline bool check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig, const int version) {
return crypto_ops::check_tx_proof(prefix_hash, R, A, B, D, sig, version);
}
/* Verification of a carrot tx proof; R and D should be in Ed25519 domain for verification,
*/
inline bool check_carrot_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig) {
return crypto_ops::check_carrot_tx_proof(prefix_hash, R, A, B, D, sig);
}
/*
inline bool check_carrot_tx_proof_as_sender(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig) {
return crypto_ops::check_carrot_tx_proof_as_sender(prefix_hash, R, A, B, D, sig);
}
*/
inline void derive_key_image_generator(const public_key &pub, ec_point &ki_gen) {
crypto_ops::derive_key_image_generator(pub, ki_gen);
}
@@ -378,4 +403,4 @@ CRYPTO_MAKE_HASHABLE_CONSTANT_TIME(public_key_memsafe)
CRYPTO_MAKE_HASHABLE(key_image)
CRYPTO_MAKE_HASHABLE(key_image_y)
CRYPTO_MAKE_COMPARABLE(signature)
CRYPTO_MAKE_COMPARABLE(view_tag)
CRYPTO_MAKE_COMPARABLE(view_tag)
+26 -5
View File
@@ -89,12 +89,24 @@ DISABLE_VS_WARNINGS(4244 4345)
void account_keys::xor_with_key_stream(const crypto::chacha_key &key)
{
// encrypt a large enough byte stream with chacha20
epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * (2 + m_multisig_keys.size()));
epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * (8 + m_multisig_keys.size()));
const char *ptr = key_stream.data();
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
m_spend_secret_key.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
m_view_secret_key.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
s_master.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
k_prove_spend.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
s_view_balance.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
k_view_incoming.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
k_generate_image.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
s_generate_address.data[i] ^= *ptr++;
for (crypto::secret_key &k: m_multisig_keys)
{
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
@@ -116,11 +128,20 @@ DISABLE_VS_WARNINGS(4244 4345)
void account_keys::encrypt_viewkey(const crypto::chacha_key &key)
{
// encrypt a large enough byte stream with chacha20
epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * 2);
epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * 8);
const char *ptr = key_stream.data();
ptr += sizeof(crypto::secret_key);
ptr += sizeof(crypto::secret_key); // Skip m_spend_secret_key
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
m_view_secret_key.data[i] ^= *ptr++;
ptr += (2 * sizeof(crypto::secret_key)); // Skip s_master, k_prove_spend
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
s_view_balance.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
k_view_incoming.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
k_generate_image.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
s_generate_address.data[i] ^= *ptr++;
}
//-----------------------------------------------------------------
void account_keys::decrypt_viewkey(const crypto::chacha_key &key)
@@ -283,9 +304,9 @@ DISABLE_VS_WARNINGS(4244 4345)
std::string account_base::get_carrot_public_address_str(network_type nettype) const
{
// Build the cryptonote::account_public_address
account_public_address addr{m_keys.m_carrot_main_address.m_spend_public_key, m_keys.m_carrot_main_address.m_view_public_key};
account_public_address addr{m_keys.m_carrot_main_address.m_spend_public_key, m_keys.m_carrot_main_address.m_view_public_key, true};
// change this code into base 58
return get_account_address_as_str(nettype, false, addr, true);
return get_account_address_as_str(nettype, false, addr);
}
//-----------------------------------------------------------------
std::string account_base::get_public_integrated_address_str(const crypto::hash8 &payment_id, network_type nettype) const
+5
View File
@@ -75,6 +75,11 @@ namespace cryptonote
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_multisig_keys)
const crypto::chacha_iv default_iv{{0, 0, 0, 0, 0, 0, 0, 0}};
KV_SERIALIZE_VAL_POD_AS_BLOB_OPT(m_encryption_iv, default_iv)
if (m_account_address.m_spend_public_key == crypto::null_pkey) {
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(s_view_balance)
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(k_view_incoming)
KV_SERIALIZE(m_carrot_account_address)
}
END_KV_SERIALIZE_MAP()
void encrypt(const crypto::chacha_key &key);
@@ -155,10 +155,9 @@ namespace cryptonote {
network_type nettype
, bool subaddress
, account_public_address const & adr
, bool is_carrot
)
{
uint64_t address_prefix = is_carrot
uint64_t address_prefix = adr.m_is_carrot
? (subaddress ? get_config(nettype).CARROT_PUBLIC_SUBADDRESS_BASE58_PREFIX : get_config(nettype).CARROT_PUBLIC_ADDRESS_BASE58_PREFIX)
: (subaddress ? get_config(nettype).CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : get_config(nettype).CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX);
@@ -169,10 +168,9 @@ namespace cryptonote {
network_type nettype
, account_public_address const & adr
, crypto::hash8 const & payment_id
, bool is_carrot
)
{
uint64_t integrated_address_prefix = is_carrot ? get_config(nettype).CARROT_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : get_config(nettype).CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
uint64_t integrated_address_prefix = adr.m_is_carrot ? get_config(nettype).CARROT_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : get_config(nettype).CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
integrated_address iadr = {
adr, payment_id
@@ -88,14 +88,12 @@ namespace cryptonote {
network_type nettype
, bool subaddress
, const account_public_address& adr
, bool is_carrot = false
);
std::string get_account_integrated_address_as_str(
network_type nettype
, const account_public_address& adr
, const crypto::hash8& payment_id
, bool is_carrot = false
);
bool get_account_address_from_str(
@@ -1381,12 +1381,7 @@ namespace cryptonote
for (const auto &o: tx.vout)
{
if (hf_version >= HF_VERSION_ENFORCE_CARROT)
{
// from v10, require outputs be carrot outputs
CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_carrot_v1), false, "wrong variant type: "
<< o.target.type().name() << ", expected txout_to_carrot_v1 in transaction id=" << get_transaction_hash(tx));
} else if (hf_version >= HF_VERSION_CARROT) {
if (hf_version >= HF_VERSION_CARROT) {
if (tx.type != cryptonote::transaction_type::PROTOCOL) {
CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_carrot_v1), false, "wrong variant type: "
<< o.target.type().name() << ", expected txout_to_carrot_v1 in transaction id=" << get_transaction_hash(tx));
+18 -35
View File
@@ -88,8 +88,8 @@
#define PREMINE_AMOUNT_UPFRONT ((uint64_t)650000000000000ull) // 3.4% of MONEY_SUPPLY
#define PREMINE_AMOUNT_MONTHLY ((uint64_t)65000000000000ull) // 8.6%/24 of MONEY_SUPPLY
#define TREASURY_SAL1_MINT_AMOUNT ((uint64_t)65000000000000ull) // 650K
#define TREASURY_SAL1_MINT_COUNT 16 // 16 times
#define TREASURY_SAL1_MINT_AMOUNT ((uint64_t)130000000000000ull) // 1.3M
#define TREASURY_SAL1_MINT_COUNT 8 // 8 times
#define DIFFICULTY_TARGET_V2 120 // seconds
#define DIFFICULTY_TARGET_V1 60 // seconds - before first fork
@@ -244,7 +244,6 @@
#define HF_VERSION_AUDIT2 8
#define HF_VERSION_AUDIT2_PAUSE 9
#define HF_VERSION_CARROT 10
#define HF_VERSION_ENFORCE_CARROT 11
#define HF_VERSION_REQUIRE_VIEW_TAGS 255
#define HF_VERSION_ENABLE_CONVERT 255
@@ -320,22 +319,14 @@ namespace config
// treasury payout {tx-key, output-key, anchor_enc, vie_tag} tuples
const std::map<uint64_t, std::tuple<std::string, std::string, std::string, std::string>> TREASURY_SAL1_MINT_OUTPUT_DATA = {
{1000000ULL, {"a1bdd1da651fbbb845232816e1aa2d4ff29b790f10bbd4f574a012f1199e15a4", "b0733ab6f251b16458efa9ebb3fb99bd54d43173b5768fe9ffc42e0fe46ae3a8", "00", "00"}},
{1020000ULL, {"47996eccbcc078b06d0f6ece37bf3a700c2bd60adfdd898b22096f16a9ad315c", "fd6bcceb4799ee067d59b97a6f66a0f9a70f134220259d3b4d6a2278ba4aca4c", "00", "00"}},
{1040000ULL, {"a3e6754a849b80c21a77e6065fefdae29eeeabf17c407453356244a00545bdb8", "3d395454df1452d715d27190e022b20395871c99af578f7251c3f9752e0274a6", "00", "00"}},
{1060000ULL, {"0d5e97a910e0f9c606ad9c711b6595aaed142d857cde2efa519112b9a29240d5", "56c29e28bdcf4f20b4b45906b93ae7c4bf9ee82e18cd45543cb69a14ce5efb88", "00", "00"}},
{1080000ULL, {"495fa363de88915aa8b74818c4b80715a882a688b4f7127ab7cd3b6885f3567a", "d42dfe0da5579c82e8255eba8c0a17170023f14a6a5030da6abf9f10abb52cbb", "00", "00"}},
{1100000ULL, {"85ea10ec40390e4f406446fb519e974d89536154045c6df28bb3b538b254e20d", "0ce2b7dd3a8ce8b596889dac8081a62f98fd70f1f043944ab4ac592c3c59e77b", "00", "00"}},
{1120000ULL, {"40f201b38a319dda81e7201e57fea7924067a4a332ed71b8e51ec29ac2d67310", "8289aa6963b98d1034e94eae55d8be6b33d0a88f14f174ebcbaec70837986c7c", "00", "00"}},
{1140000ULL, {"c5a648cc7846341357b7b4653a58f9eb4800d88b5de587bceec7a5c28f98d05a", "3f308a203845d88e5e728fcebcdcea1f90e2f424d461617993c672a6138ad2d8", "00", "00"}},
{1160000ULL, {"4c51d6550b8eeb6cc8f0d395cc83a5f90ec2a4d86501b3f68da48d618ccf5711", "53f0bd8cebeefb3a88fffa5d7f6ad43d4712608ded561732467ca499df940454", "00", "00"}},
{1180000ULL, {"ce2f5d82118fed03d5e269e285fc16189a6cd34f38999e5c055a5dea5fce61bc", "f7fc6948b194d9bd6f2df6ecb83f04e6c8d1a2556a63fedb310a4631fe1bfc42", "00", "00"}},
{1200000ULL, {"6248028fd77fb02b5c6ea72dea10b417891a2da7aaf9565aed382e063b4981a3", "63986e1177499bdb23cd49afb519ec18f38cb1b0c386220b376d8ffdc2e37890", "00", "00"}},
{1220000ULL, {"6adcb695aa5d6d01133c68900f29e501e9549816e827ea0c164bbc78f3534dd6", "6a440ccb18f5e703e8000de3865ac40d4c18f081270d32eef377dc831f28d8d0", "00", "00"}},
{1240000ULL, {"b97a4d2259480f34f20e41c489ab5c2e5ae9ee84d8672a7eff8012f2260e121e", "e6eb9147ff40e22209d321d0f1bfbfe20acf5ceb6b9d0bfb13688ad28aa1232e", "00", "00"}},
{1260000ULL, {"4fd214602a36902f22d16927244c456e8cc5a406a9570131f138a028214ffdf0", "34060b8bd96009b9b298280ebd84fa9587fa8c9df6fb5ad0270fb6cd2098885c", "00", "00"}},
{1280000ULL, {"9d60178ec6d6599d7a31298f2559fb9c3111f2c70494b3a1638db877ea55b808", "7985ed03856a929663e954738d0938713407717835f760c7ca4d54844a128c91", "00", "00"}},
{1300000ULL, {"cd65718eab234bf419332e53bd2f48e2ade70af48c5e126ab5080321e1493dfc", "581cb4cca7a0a029ee2cac51dfc00a0c3a657d2eaf67ed3c6ae7bacc11b4f007", "00", "00"}},
{334750, {"1b2cd3ff56aa77c0cbab0473bfb96697ebdd0b25ad230136bfe41d5dc1ef265f","718cf02eabca157fd7ad7f8537db217624bfe1ca99dd09e758357e7000a5e57a","789cca3def51fb879eb7fbca271869b7","79bd0c"}},
{356350, {"b51acbf35265d09f3cfb83dcabde2746991ddf0d30b5a4ecc34043b349a77031","9dc0d2e9534cdccf83494687c55c67c8c1b29834acf97cce53124a08a9549231","588ebc2918d06c009a18a28a8ab76694","ab8c23"}},
{377950, {"771c6865980493846cbb049b34f72b937878cf799ae1126775df35064cf53526","60d340613c7721aeb03f2de1b56e2916d6cb023cc668acaca00e1606e0bb7f64","198bb0e5880c8abf8108158284ca7637","9b93f2"}},
{399550, {"d9159f9bb654230382a69bb7a739c14f9d506c21dba5be9f4cdf66126ed1b24e","c3b34e7f9977f31f659abd9306b650416a674ed7bcfb8c75b257040a1a06b8b9","6f0de181716e0a9e100228d58f11d42c","4c87fd"}},
{421150, {"1e2ab1ae204e7a9375f5b57fed2524e2d1a4702c4bbc6d420eb65d92af44c60c","826ecca0e2837860a6d2be89a410ff6aab22c5f5a66bd0a318de3abe646aa26a","bad663aa8253c40d00de0a2d1dfca60f","4a04d7"}},
{442750, {"18267940a2b37c82850f3fb83d935281ee5cc436b0797ec10ca26c9cc3c0ef01","77bdec7bbc4cabd84cc8cf0666e1fdfe299480b9b2a4cf52cc959d728fa899ab","a8807121e33471cf18aa82af4826c9a7","d35d35"}},
{464350, {"9ab50223bfecec2508e8233540dc13c3fc7e15a8d77ecb90939afee96859724d","675a1b3f2a2cd2bd0e7ec073c426125d95e12ad13e541bf019925c481c2bca15","591d2f2331832c347654ab255ad97e2d","d87c9c"}},
{485950, {"c0d7299fe0b873687e7319f1a0d1f67712ef3ccf6a579d96c5f99743add6f029","dd3d17b5145b56377e023c169ae858cdf48bd6c23e8b2a77e02765ce316e3ca7","6a9e74df01762e2b32db1dc4d69dc3e9","75debe"}}
};
// Hash domain separators
@@ -432,22 +423,14 @@ namespace config
// treasury payout {tx-key, output-key, anchor_enc, vie_tag} tuples
const std::map<uint64_t, std::tuple<std::string, std::string, std::string, std::string>> TREASURY_SAL1_MINT_OUTPUT_DATA = {
{1100, {"0d82afb3ae9c5de25a06da49e24e64d37ba05d6144f9f6153cea325c61407106","81440913c1484891d25010986806a62d1fae1b2fe045e3ac1396413638d594f1","40864bbf80397c05516368a1397eb5c3","220bda"}},
{1120, {"a032f2de4d3b5d880d3a636d9786a4a2f586bee360c323bc7521f987a2ada51b","cf8e5eeb5d37996d86c9c4c88fabaabc1ec77bab9cd5ce610a61b49f1e079ce2","9877a989e0216f76a8cdf0a568e57d45","017549"}},
{1140, {"51da7a4c34aa20a800ada615a931fbba5fd1b9f7df78f0f911097ad9d99dc230","dd784a5bf1adf9c01cb44946ca094ed4d38f8d9cccf8edfdab397db3b7631dbf","97314be452f184b54f16a558ee8a7dbe","357ac3"}},
{1160, {"a0cf49118c62cea834fabae83e12f26939ce170f308adadacf7275226e5cdf6d","7ec9cb2dfac8dfa45bab1bab2b1e31a4291a9e3d939e9c193740cc191d1b720d","4e950acc71ffd4f3835ec79202dc46fd","e7da3a"}},
{1180, {"5193e35f6cd3ff8408a442be5eaf4aa1871419ecb0c485546e9d995c4c9c2730","39fb9792b88290b3377036a150a6681334ebe5cb201e2005d2c1d7b212b3df4d","cb458163806a277c7b3513df0d5b0028","1939b5"}},
{1200, {"a2c71a7381bc48576f4cb4313cb6264d2a6e82cd2ddcbbd5460bce1db8d5021e","d0c46f29f4bbdd5538d64a438c0ab93cb0586e67fbfc7d1671f0c9809d6a7fac","af38cc2ddccb9229728ef704e360e15a","8a8dc1"}},
{1220, {"8358105081510e907c98da59636eef777460513d827aba400efbb81d82aead4c","b2b8e2f9bb13e7527d0ee1bc3ebf0ca5057139ee4f9302251455b9f27dca3b16","dc226161830e952b2533fe34b1dd3c48","fb7c67"}},
{1240, {"90b0bc153b97a93eefd59af9b47d26c0c91aadda6203de91a15f2f76fc949074","06e6a11b8742ee78beafe89f894fb9078a514ee6dd4e099182ac982ead37eacc","4fbfd0f3c2b8d514f2c675b37b9f0b6f","3308ad"}},
{1260, {"cd10148b3175804b17e3c68a6a5556364d585dee89953904b32f005a5f6d8f14","d9d6f64119776e9c97fe2fb540cdb5f40bea0c40127fda0f1f2b831cb7fa7d2b","9c21b83d29324e2d91774a623219ae28","1d647c"}},
{1280, {"5963697782de7db5111d12c853f3fec0dd53f597d80cc376c7945f1d4afa224b","cc5e5b1273c10016dc855ec1d9bfa0bcf4736a748125f5341717ad9d080fbba0","19dd1f9a0cac118564e2ccb9f36be53a","7ab2c2"}},
{1300, {"cf1d4d2d21b7b3f1a54889e0688a4654687e28a302a2ac3ad046ac2ad323f97a","5b5926da5e9199f190e86073ebd09ccb867faf1e9887c9e52a47c194915b2575","5775fa47af3c7226f2ec3017470410c9","2b03b3"}},
{1320, {"b565ec8842f3e6c22c7a23eb23f0e5684b462d2261681dff7b2ce71ab02ff170","4f94cf4d811574c9fcc27888cc972ebc6c01ae97146b9882d397b77c9232d1fd","9d142686627c918a26b6c6853b911143","685b18"}},
{1340, {"50d1b1bcacf36ba14b23b3827e36a02a7610dd3199a9f1b9d0a23ebcbd1c2d54","38818e41cf0ce7d54391978801c58087ddd5fe517dadb5e61d840f27a7a81a52","55328f4bff5120d44c5d76fc5a9dfdbd","d17be2"}},
{1360, {"7657bf97a9be9beac5f999a1e2c1226613849c41c12df5d7185d4d6613a27328","63d96a47f29fab0e727d13905cc843df40ccb76b14dc48857d0dd9ec0873e8c5","ab4f7ffa6431452f0fac7eec33e89e3e","5acd14"}},
{1380, {"845e4cbe9b9d2462fae2225da698d6a4e18340f06b312545ee9cfda8127abd41","cc65f1f22f863b31f3c4a53ac3987186c2a699f387a2bf08d8dfed54037912c6","bda3aa3bdd2b42e8183c1013e4350c98","d4a418"}},
{1400, {"4b11413fcba49df61537427ff35fdd47ee12a8467751236ff22681bbea343d2c","32d22bba59559489f422298ae653ef55ec8c87e8705daf4c89da7187c8574cbd","8868e1c1d788ce4560beb4afbe77aa5a","5a35d9"}}
{1100, {"71336a480440ed24a53b2cfd5a5292d1e618c3e843637227883ea2cc42fb346f","a135f59a05ea9e33539e4502b187b4789cc7fba79616c6902a902cc6601f0359","7f1c6970232254a9b13f7f063df2a853","62073c"}},
{1120, {"2cc49b182addc0106b601c9876c01a4b06532c05f9dd9179b2c4f47e5c7c0d74","fd62e59324389f37d0bb628a39f413c11be34d572ec3a40f465e008b9dfd5e0c","fbf8584222e299bb748009eaf2177123","b76a8d"}},
{1140, {"50f1dd5244bb6fdc62b5a18b868a4dc9e9ad15f4a7a54e5a98084a3a5213c846","942de90a3347df3e962d9b0a2745f8aedb265a8caf73aabef9aa713f4ec3e493","933702e29d680bf21ad559a2d3243621","dae32b"}},
{1160, {"114a1fef3a03c81b3b6308fb4e51db42d86945d8f71ee8798749a4aa2d8bfe57","fd77f0289b7e25ae68c86f4cd2c72ac0211a4d0e7a31c84b8d58a33bbd3c511b","d8048bb76b9313768a4c69100cba6d43","46b0a3"}},
{1180, {"cf7fe9d97a7a4cb881bff1d37bb6981bcb0691649c465d660522a43f14b32849","b76908d56108e7111374f60993e9411def7bb2899118f20f49e67a801936a1ad","f203a5acd48f93f1451d32d70716e585","d72097"}},
{1200, {"415e8fa4fb6bed8bc59591883f5e098dde6179e980eccfc4cbb3a64c4168cf47","5426c588d7c5750c76b526ef268b45f7414e2ddea40218ee92d123564c5114c4","fb1926bbef8c70ebd796ae77dffca07d","351edf"}},
{1220, {"1f47cce00e8e1c77cb3ff007491d261dae75de5bbfdbe0f4913d74a8503aaa5e","4d8aac299306989aa169982d89457852a1bab858c668c9b740d96b682bb1961b","d8c0be7926ce704290cfd8d5e0500f33","750003"}},
{1240, {"3afd93c684638d54e68f5d8d417863035f12482251feea5cf7515725a2bb0f4f","7895e6baded473b093b57993ae3497a47b0ea78cf35b37d1387f226f445d9018","76a0c37e6f3d9d508c4bce1d7162514e","09e146"}}
};
}
+26 -24
View File
@@ -877,7 +877,7 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
std::vector<uint64_t> timestamps;
std::vector<difficulty_type> difficulties;
uint64_t height;
auto new_top_hash = get_tail_id(height); // get it again now that we have the lock
top_hash = get_tail_id(height); // get it again now that we have the lock
++height;
uint8_t version = get_current_hard_fork_version();
@@ -976,11 +976,11 @@ size_t Blockchain::recalculate_difficulties(boost::optional<uint64_t> start_heig
if (start_height_opt) {
start_height = *start_height_opt;
} else {
bool found = false;
// bool found;
for (size_t i=0; i<num_mainnet_hard_forks; ++i) {
if (version == mainnet_hard_forks[i].version) {
start_height = mainnet_hard_forks[i].height;
found = true;
//found = true;
break;
}
}
@@ -1403,14 +1403,19 @@ bool Blockchain::prevalidate_protocol_transaction(const block& b, uint64_t heigh
CHECK_AND_ASSERT_MES(b.protocol_tx.vin[0].type() == typeid(txin_gen), false, "coinbase protocol transaction in the block has the wrong type");
CHECK_AND_ASSERT_MES(b.protocol_tx.version > 1, false, "Invalid coinbase protocol transaction version");
if (hf_version >= HF_VERSION_CARROT) {
CHECK_AND_ASSERT_MES(b.protocol_tx.version == TRANSACTION_VERSION_CARROT || 2, false, "protocol transaction has wrong version");
CHECK_AND_ASSERT_MES(b.protocol_tx.type == cryptonote::transaction_type::PROTOCOL, false, "protocol transaction has wrong type");
}
// Work out what the HF version _was_ when the STAKE outputs were created
uint64_t stake_lock_period = get_config(m_nettype).STAKE_LOCK_PERIOD;
uint8_t hf_version_submitted = get_ideal_hard_fork_version(height - stake_lock_period - 1);
if (hf_version >= HF_VERSION_ENFORCE_CARROT) {
CHECK_AND_ASSERT_MES(b.protocol_tx.version == TRANSACTION_VERSION_CARROT, false, "protocol transaction has wrong version");
if (hf_version >= HF_VERSION_CARROT) {
if (hf_version_submitted >= HF_VERSION_CARROT || b.protocol_tx.vout.size() == 0) {
CHECK_AND_ASSERT_MES(b.protocol_tx.version == TRANSACTION_VERSION_CARROT, false, "protocol transaction has wrong version");
} else {
CHECK_AND_ASSERT_MES(b.protocol_tx.version == 2, false, "protocol transaction has wrong version");
}
CHECK_AND_ASSERT_MES(b.protocol_tx.type == cryptonote::transaction_type::PROTOCOL, false, "protocol transaction has wrong type");
} else {
hf_version_submitted = hf_version;
}
// for v2 txes (ringct), we only accept empty rct signatures for protocol transactions,
@@ -1433,8 +1438,7 @@ bool Blockchain::prevalidate_protocol_transaction(const block& b, uint64_t heigh
return false;
}
CHECK_AND_ASSERT_MES(check_output_types(b.protocol_tx, hf_version), false, "protocol transaction has invalid output type(s) in block " << get_block_hash(b));
CHECK_AND_ASSERT_MES(check_output_types(b.protocol_tx, hf_version_submitted), false, "protocol transaction has invalid output type(s) in block " << get_block_hash(b));
return true;
}
//------------------------------------------------------------------
@@ -1521,7 +1525,6 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
case HF_VERSION_AUDIT2:
case HF_VERSION_AUDIT2_PAUSE:
case HF_VERSION_CARROT:
case HF_VERSION_ENFORCE_CARROT:
if (b.miner_tx.amount_burnt > 0) {
CHECK_AND_ASSERT_MES(money_in_use + b.miner_tx.amount_burnt > money_in_use, false, "miner transaction is overflowed by amount_burnt");
money_in_use += b.miner_tx.amount_burnt;
@@ -2093,6 +2096,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
entry.P_change = audit_entry.first.P_change;
entry.return_pubkey = audit_entry.first.return_pubkey;
entry.origin_height = matured_audit_height;
entry.is_carrot = false;
protocol_entries.push_back(entry);
}
}
@@ -2119,7 +2123,6 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
*/
// Time to construct the protocol_tx
uint64_t protocol_fee = 0;
address_parse_info treasury_address_info;
ok = cryptonote::get_account_address_from_str(treasury_address_info, m_nettype, get_config(m_nettype).TREASURY_ADDRESS);
CHECK_AND_ASSERT_MES(ok, false, "Failed to obtain treasury address info");
@@ -2950,7 +2953,7 @@ bool Blockchain::get_pricing_record(oracle::pricing_record &pr, std::map<std::st
bool r = false;
const uint64_t height = get_current_blockchain_height();
const uint8_t hf_version = m_hardfork->get_current_version();
// const uint8_t hf_version = m_hardfork->get_current_version();
epee::net_utils::http::http_simple_client http_client;
COMMAND_RPC_GET_PRICING_RECORD::request req = AUTO_VAL_INIT(req);
@@ -4514,8 +4517,7 @@ void Blockchain::get_dynamic_base_fee_estimate_2021_scaling(uint64_t grace_block
//------------------------------------------------------------------
uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const
{
const uint8_t version = get_current_hard_fork_version();
const uint64_t db_height = m_db->height();
// const uint64_t db_height = m_db->height();
if (grace_blocks >= CRYPTONOTE_REWARD_BLOCKS_WINDOW)
grace_blocks = CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1;
@@ -4666,7 +4668,7 @@ bool Blockchain::calculate_audit_payouts(const uint64_t start_height, std::vecto
// Get the AUDIT TX information for matured staked coins
std::vector<cryptonote::yield_tx_info> audit_entries;
// We get the audit_tx_info from the block where they entered the chain
int audit_tx_result = m_db->get_audit_tx_info(start_height, audit_entries);
m_db->get_audit_tx_info(start_height, audit_entries);
if (!audit_entries.size()) {
// Report error and abort
@@ -4719,7 +4721,7 @@ bool Blockchain::calculate_yield_payouts(const uint64_t start_height, std::vecto
// Get the YIELD TX information for matured staked coins
std::vector<cryptonote::yield_tx_info_carrot> yield_entries;
// We get the yield_tx_info from the block _before_ they started to accrue yield
int yield_tx_result = m_db->get_carrot_yield_tx_info(start_height, yield_entries);
m_db->get_carrot_yield_tx_info(start_height, yield_entries);
if (!yield_entries.size()) {
// Report error and abort
@@ -4785,7 +4787,7 @@ bool Blockchain::calculate_yield_payouts(const uint64_t start_height, std::vecto
// Get the YIELD TX information for matured staked coins
std::vector<cryptonote::yield_tx_info> yield_entries;
// We get the yield_tx_info from the block _before_ they started to accrue yield
int yield_tx_result = m_db->get_yield_tx_info(start_height, yield_entries);
m_db->get_yield_tx_info(start_height, yield_entries);
if (!yield_entries.size()) {
// Report error and abort
@@ -5402,7 +5404,7 @@ leave:
// Update the YBI cache data
uint64_t yield_lock_period = cryptonote::get_config(m_nettype).STAKE_LOCK_PERIOD;
uint64_t ybi_cache_expected_size = std::min(new_height, yield_lock_period);
// uint64_t ybi_cache_expected_size = std::min(new_height, yield_lock_period);
if (new_height > yield_lock_period) {
if (m_yield_block_info_cache.count(new_height - yield_lock_period - 2) != 0) {
m_yield_block_info_cache.erase(new_height - yield_lock_period - 2);
@@ -5520,7 +5522,7 @@ uint64_t Blockchain::get_next_long_term_block_weight(uint64_t block_weight) cons
const uint64_t db_height = m_db->height();
const uint64_t nblocks = std::min<uint64_t>(m_long_term_block_weights_window, db_height);
const uint8_t hf_version = get_current_hard_fork_version();
// const uint8_t hf_version = get_current_hard_fork_version();
if (db_height < CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE)
return block_weight;
@@ -6167,7 +6169,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
} while(0); \
// generate sorted tables for all amounts and absolute offsets
size_t tx_index = 0, block_index = 0;
size_t tx_index = 0; //, block_index = 0;
for (const auto &entry : blocks_entry)
{
if (m_cancel)
@@ -6238,7 +6240,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
}
}
++block_index;
//++block_index;
}
// sort and remove duplicate absolute_offsets in offset_map
@@ -6510,7 +6512,7 @@ void Blockchain::cancel()
}
#if defined(PER_BLOCK_CHECKPOINT)
static const char expected_block_hashes_hash[] = "a2a5a9bc5d606392ac5c14be55b90a92b8577b8ffdac5c63cc6174f41764c753";
static const char expected_block_hashes_hash[] = "fb30b46662cb1cfca74d443d66be5860936f9fd904ba5c6f8daecb926ce545f0";
void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints)
{
if (get_checkpoints == nullptr || !m_fast_sync)
+18 -16
View File
@@ -351,9 +351,25 @@ namespace cryptonote
bool noncarrot_found = false;
tx.type = cryptonote::transaction_type::PROTOCOL;
const bool force_carrot = hard_fork_version >= HF_VERSION_ENFORCE_CARROT;
if (force_carrot)
// Scan the protocol_data to make sure all or none are Carrot
for (auto const& entry: protocol_data) {
if (entry.is_carrot) carrot_found = true;
else noncarrot_found = true;
}
if (carrot_found && noncarrot_found) {
LOG_ERROR("Cannot mix Carrot and non-Carrot outputs in the same protocol transaction");
return false;
}
if (carrot_found && hard_fork_version < HF_VERSION_CARROT) {
LOG_ERROR("Carrot outputs found in CryptoNote protocol transaction");
return false;
}
if (carrot_found || (!noncarrot_found && hard_fork_version >= HF_VERSION_CARROT))
{
// Ensure the TX version is correct
tx.version = TRANSACTION_VERSION_CARROT;
try
{
// Create a vector of enotes
@@ -373,7 +389,6 @@ namespace cryptonote
memcpy(e.enote_ephemeral_pubkey.data, entry.return_pubkey.data, sizeof(crypto::public_key));
enotes.push_back(e);
}
carrot_found = true;
tx = store_carrot_to_coinbase_transaction_v1(enotes, std::string{}, cryptonote::transaction_type::PROTOCOL, height);
tx.amount_burnt = 0;
tx.invalidate_hashes();
@@ -412,14 +427,12 @@ namespace cryptonote
additional_tx_public_keys.push_back(entry.return_pubkey);
tx.vout.push_back(out);
carrot_found = true;
} else {
// Create the TX output for this refund
tx_out out;
cryptonote::set_tx_out(entry.amount_burnt, entry.destination_asset, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, entry.return_address, false, crypto::view_tag{}, out);
additional_tx_public_keys.push_back(entry.return_pubkey);
tx.vout.push_back(out);
noncarrot_found = true;
}
} else if (entry.type == cryptonote::transaction_type::AUDIT) {
// PAYOUT
@@ -430,19 +443,9 @@ namespace cryptonote
cryptonote::set_tx_out(entry.amount_burnt, entry.destination_asset, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, entry.return_address, false, crypto::view_tag{}, out);
additional_tx_public_keys.push_back(entry.return_pubkey);
tx.vout.push_back(out);
noncarrot_found = true;
}
}
if (carrot_found && noncarrot_found) {
LOG_ERROR("Cannot mix Carrot and non-Carrot outputs in the same protocol transaction");
return false;
}
if (carrot_found) {
// Ensure the TX version is correct
tx.version = TRANSACTION_VERSION_CARROT;
}
// Add in all of the additional TX pubkeys we need to process the payments
add_additional_tx_pub_keys_to_extra(tx.extra, additional_tx_public_keys);
@@ -594,7 +597,6 @@ namespace cryptonote
case HF_VERSION_AUDIT2:
case HF_VERSION_AUDIT2_PAUSE:
case HF_VERSION_CARROT:
case HF_VERSION_ENFORCE_CARROT:
// SRCG: subtract 20% that will be rewarded to staking users
CHECK_AND_ASSERT_MES(tx.amount_burnt == 0, false, "while creating outs: amount_burnt is nonzero");
tx.amount_burnt = amount / 5;
+10
View File
@@ -1749,6 +1749,16 @@ namespace cryptonote
LOG_PRINT_L2(" key images already seen");
continue;
}
if (version < HF_VERSION_CARROT && tx.version >= TRANSACTION_VERSION_CARROT)
{
LOG_PRINT_L2(" is a Carrot transaction - cannot be mined");
continue;
}
if (version >= HF_VERSION_CARROT && tx.version < TRANSACTION_VERSION_CARROT)
{
LOG_PRINT_L2(" is not a Carrot transaction - cannot be mined");
continue;
}
bl.tx_hashes.push_back(sorted_it->second);
total_weight += meta.weight;
+1 -1
View File
@@ -1355,7 +1355,7 @@ bool t_rpc_command_executor::print_transaction_pool_stats() {
bool t_rpc_command_executor::start_mining(cryptonote::account_public_address address, uint64_t num_threads, cryptonote::network_type nettype, bool do_background_mining, bool ignore_battery, bool is_carrot) {
cryptonote::COMMAND_RPC_START_MINING::request req;
cryptonote::COMMAND_RPC_START_MINING::response res;
req.miner_address = cryptonote::get_account_address_as_str(nettype, false, address, is_carrot);
req.miner_address = cryptonote::get_account_address_as_str(nettype, false, address);
req.threads_count = num_threads;
req.do_background_mining = do_background_mining;
req.ignore_battery = ignore_battery;
+4
View File
@@ -202,6 +202,10 @@ namespace hw {
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
crypto::signature &sig) = 0;
virtual void generate_carrot_tx_proof(const crypto::hash &prefix_hash,
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
const crypto::secret_key &a, crypto::signature &sig) = 0;
virtual bool open_tx(crypto::secret_key &tx_key) = 0;
virtual void get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) = 0;
+6
View File
@@ -282,6 +282,12 @@ namespace hw {
crypto::generate_tx_proof(prefix_hash, R, A, B, D, r, sig);
}
void device_default::generate_carrot_tx_proof(const crypto::hash &prefix_hash,
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
const crypto::secret_key &a, crypto::signature &sig) {
crypto::generate_carrot_tx_proof(prefix_hash, R, A, B, D, r, a, sig);
}
bool device_default::open_tx(crypto::secret_key &tx_key) {
cryptonote::keypair txkey = cryptonote::keypair::generate(*this);
tx_key = txkey.sec;
+4
View File
@@ -112,6 +112,10 @@ namespace hw {
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
crypto::signature &sig) override;
void generate_carrot_tx_proof(const crypto::hash &prefix_hash,
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
const crypto::secret_key &a, crypto::signature &sig) override;
bool open_tx(crypto::secret_key &tx_key) override;
void get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) override;
+8
View File
@@ -1433,6 +1433,14 @@ namespace hw {
#endif
}
void device_ledger::generate_carrot_tx_proof(const crypto::hash &prefix_hash,
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
const crypto::secret_key &a, crypto::signature &sig) {
// to-do: For now, carrot tx proofs are not supported
AUTO_LOCK_CMD();
crypto::generate_carrot_tx_proof(prefix_hash, R, A, B, D, r, a, sig);
}
bool device_ledger::open_tx(crypto::secret_key &tx_key) {
AUTO_LOCK_CMD();
this->lock();
+4
View File
@@ -253,6 +253,10 @@ namespace hw {
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
crypto::signature &sig) override;
void generate_carrot_tx_proof(const crypto::hash &prefix_hash,
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
const crypto::secret_key &a, crypto::signature &sig) override;
bool open_tx(crypto::secret_key &tx_key) override;
void get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) override;
+3 -3
View File
@@ -58,6 +58,9 @@ const hardfork_t mainnet_hard_forks[] = {
// version 9 starts from block 179200, which is on or around the 10th of March, 2025. Fork time finalised on 2025-02-24. No fork voting occurs for the v9 fork.
{ 9, 179200, 0, 1740393800 },
// version 10 Carrot - including treasury mint - starts from block 334750, which is on or around the 13th of October, 2025. Fork time finalised on 2025-09-29. No fork voting occurs for the v10 fork.
{10, 334750, 0, 1759142500 },
};
const size_t num_mainnet_hard_forks = sizeof(mainnet_hard_forks) / sizeof(mainnet_hard_forks[0]);
const uint64_t mainnet_hard_fork_version_1_till = ((uint64_t)-1);
@@ -92,9 +95,6 @@ const hardfork_t testnet_hard_forks[] = {
// version 10 Carrot - including treasury mint - starts from block 1100
{10, 1100, 0, 1739780005 },
// version 11 Carrot and CryptoNote starts from block 1200
{11, 1200, 0, 1756811153 },
};
const size_t num_testnet_hard_forks = sizeof(testnet_hard_forks) / sizeof(testnet_hard_forks[0]);
const uint64_t testnet_hard_fork_version_1_till = ((uint64_t)-1);
+1 -19
View File
@@ -881,12 +881,6 @@ namespace nodetool
if (m_nettype == cryptonote::MAINNET)
{
return {
"xwvz3ekocr3dkyxfkmgm2hvbpzx2ysqmaxgter7znnqrhoicygkfswid.onion:18083",
"4pixvbejrvihnkxmduo2agsnmc3rrulrqc7s3cbwwrep6h6hrzsibeqd.onion:18083",
"zbjkbsxc5munw3qusl7j2hpcmikhqocdf4pqhnhtpzw5nt5jrmofptid.onion:18083",
"qz43zul2x56jexzoqgkx2trzwcfnr6l3hbtfcfx54g4r3eahy3bssjyd.onion:18083",
"plowsof3t5hogddwabaeiyrno25efmzfxyro2vligremt7sxpsclfaid.onion:18083",
"plowsoffjexmxalw73tkjmf422gq6575fc7vicuu4javzn2ynnte6tyd.onion:18083",
};
}
return {};
@@ -894,10 +888,6 @@ namespace nodetool
if (m_nettype == cryptonote::MAINNET)
{
return {
"s3l6ke4ed3df466khuebb4poienoingwof7oxtbo6j4n56sghe3a.b32.i2p:18080",
"sel36x6fibfzujwvt4hf5gxolz6kd3jpvbjqg6o3ud2xtionyl2q.b32.i2p:18080",
"uqj3aphckqtjsitz7kxx5flqpwjlq5ppr3chazfued7xucv3nheq.b32.i2p:18080",
"vdmnehdjkpkg57nthgnjfuaqgku673r5bpbqg56ix6fyqoywgqrq.b32.i2p:18080",
};
}
return {};
@@ -2054,20 +2044,13 @@ namespace nodetool
return true;
static const std::vector<std::string> dns_urls = {
"blocklist.moneropulse.se"
, "blocklist.moneropulse.org"
, "blocklist.moneropulse.net"
, "blocklist.moneropulse.no"
, "blocklist.moneropulse.fr"
, "blocklist.moneropulse.de"
, "blocklist.moneropulse.ch"
};
std::vector<std::string> records;
if (!tools::dns_utils::load_txt_records_from_dns(records, dns_urls))
return true;
unsigned good = 0, bad = 0;
unsigned good = 0;
for (const auto& record : records)
{
std::vector<std::string> ips;
@@ -2091,7 +2074,6 @@ namespace nodetool
continue;
}
MWARNING("Invalid IP address or subnet from DNS blocklist: " << ip << " - " << parsed_addr.error());
++bad;
}
}
if (good > 0)
+21 -5
View File
@@ -1879,12 +1879,29 @@ namespace cryptonote
}
blobdata block_blob = t_serializable_object_to_blob(b);
crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(b.miner_tx);
const std::vector<crypto::public_key> additional_tx_pub_keys = cryptonote::get_additional_tx_pub_keys_from_extra(b.miner_tx);
if(tx_pub_key == crypto::null_pkey)
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = "Internal error: failed to create block template";
LOG_ERROR("Failed to get tx pub key in coinbase extra");
return false;
// Check for Carrot treasury payout
const uint8_t hf_version = m_core.get_blockchain_storage().get_current_hard_fork_version();
if (hf_version >= HF_VERSION_CARROT && b.miner_tx.vout.size() == 2) {
const auto treasury_payout_data = get_config(nettype()).TREASURY_SAL1_MINT_OUTPUT_DATA;
const bool treasury_payout_exists = (treasury_payout_data.count(height) == 1);
if (!treasury_payout_exists) {
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = "Internal error: failed to create block template (missing treasury payout)";
LOG_ERROR("Failed to get tx pub key in coinbase extra (missing treasury payout)");
return false;
}
tx_pub_key = additional_tx_pub_keys.back();
} else {
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = "Internal error: failed to create block template";
LOG_ERROR("Failed to get tx pub key in coinbase extra");
return false;
}
}
uint64_t next_height;
@@ -3108,7 +3125,6 @@ namespace cryptonote
CHECK_PAYMENT(req, res, COST_PER_FEE_ESTIMATE);
const uint8_t version = m_core.get_blockchain_storage().get_current_hard_fork_version();
m_core.get_blockchain_storage().get_dynamic_base_fee_estimate_2021_scaling(req.grace_blocks, res.fees);
res.fee = res.fees[0];
res.quantization_mask = Blockchain::get_fee_quantization_mask();
+124 -36
View File
@@ -237,6 +237,7 @@ namespace
const char* USAGE_SIGN_TRANSFER("sign_transfer [export_raw] [<filename>]");
const char* USAGE_SET_LOG("set_log <level>|{+,-,}<categories>");
const char* USAGE_ACCOUNT("account\n"
" account all\n"
" account new <label text with white spaces allowed>\n"
" account switch <index> \n"
" account label <index> <label text with white spaces allowed>\n"
@@ -316,7 +317,7 @@ namespace
const char* USAGE_RPC_PAYMENT_INFO("rpc_payment_info");
const char* USAGE_START_MINING_FOR_RPC("start_mining_for_rpc [<number_of_threads>]");
const char* USAGE_STOP_MINING_FOR_RPC("stop_mining_for_rpc");
const char* USAGE_SHOW_QR_CODE("show_qr_code [<subaddress_index>]");
const char* USAGE_SHOW_QR_CODE("show_qr_code [<subaddress_index>] [<address_type>]");
const char* USAGE_VERSION("version");
const char* USAGE_HELP("help [<command> | all]");
const char* USAGE_APROPOS("apropos <keyword> [<keyword> ...]");
@@ -2559,6 +2560,7 @@ bool simple_wallet::stop_mining_for_rpc(const std::vector<std::string> &args)
bool simple_wallet::show_qr_code(const std::vector<std::string> &args)
{
uint32_t subaddress_index = 0;
carrot::AddressDeriveType derive_type;
if (args.size() >= 1)
{
if (!string_tools::get_xtype_from_string(subaddress_index, args[0]))
@@ -2572,6 +2574,25 @@ bool simple_wallet::show_qr_code(const std::vector<std::string> &args)
return true;
}
}
if (args.size() >= 2)
{
// Convert string to AddressDeriveType enum
if (args[1] == "carrot" || args[1] == "Carrot")
{
derive_type = carrot::AddressDeriveType::Carrot;
}
else if (args[1] == "precarrot" || args[1] == "PreCarrot")
{
derive_type = carrot::AddressDeriveType::PreCarrot;
}
else
{
fail_msg_writer() << tr("invalid derive type: must be 'carrot' or 'precarrot'");
return true;
}
} else {
derive_type = carrot::AddressDeriveType::Auto;
}
#ifdef _WIN32
#define PRINT_UTF8(pre, x) std::wcout << pre ## x
@@ -2586,7 +2607,8 @@ bool simple_wallet::show_qr_code(const std::vector<std::string> &args)
WTEXTON();
try
{
const std::string address = "salvium:" + m_wallet->get_subaddress_as_str({m_current_subaddress_account, subaddress_index});
const std::string address = "salvium:" + m_wallet->get_subaddress_as_str({m_current_subaddress_account, subaddress_index, derive_type, false});
message_writer() << "Showing QR code for address: " << address.c_str() << std::endl;
const qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(address.c_str(), qrcodegen::QrCode::Ecc::LOW);
for (int y = -2; y < qr.getSize() + 2; y+=2)
{
@@ -3614,7 +3636,8 @@ simple_wallet::simple_wallet()
m_cmd_binder.set_handler("account",
boost::bind(&simple_wallet::on_command, this, &simple_wallet::account, _1),
tr(USAGE_ACCOUNT),
tr("If no arguments are specified, the wallet shows all the existing accounts along with their balances.\n"
tr("If no arguments are specified, the wallet shows all the existing accounts (that have balances) along with their balances.\n"
"If the \"all\" argument is specified, the wallet shows all the existing accounts, even if they have zero balances.\n"
"If the \"new\" argument is specified, the wallet creates a new account with its label initialized by the provided label text (which can be empty).\n"
"If the \"switch\" argument is specified, the wallet switches to the account specified by <index>.\n"
"If the \"label\" argument is specified, the wallet sets the label of the account specified by <index> to the provided label text.\n"
@@ -4589,7 +4612,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
{
m_wallet_file = m_generate_from_svb_key;
// parse address
std::string address_string = input_line("Standard address");
std::string address_string = input_line("Carrot wallet address");
if (std::cin.eof())
return false;
if (address_string.empty()) {
@@ -4614,7 +4637,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
}
// parse view secret key
epee::wipeable_string viewkey_string = input_secure_line("Secret view key");
epee::wipeable_string viewkey_string = input_secure_line("View-balance secret");
if (std::cin.eof())
return false;
if (viewkey_string.empty()) {
@@ -5314,7 +5337,7 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
.m_is_carrot = true
};
message_writer(console_color_white, true) << tr("Generated new Carrot wallet: ")
<< cryptonote::get_account_address_as_str(m_wallet->nettype(), false, carrot_address, true);
<< cryptonote::get_account_address_as_str(m_wallet->nettype(), false, carrot_address);
PAUSE_READLINE();
std::cout << tr("View key: ");
print_secret_key(m_wallet->get_account().get_keys().m_view_secret_key);
@@ -5594,6 +5617,12 @@ boost::optional<epee::wipeable_string> simple_wallet::open_wallet(const boost::p
m_wallet->rewrite(m_wallet_file, password);
}
}
if (m_wallet->force_rescan()) {
m_wallet->force_rescan(false);
refresh_main(m_wallet->get_refresh_from_block_height(), ResetSoft);
}
}
catch (const std::exception& e)
{
@@ -6429,6 +6458,10 @@ bool simple_wallet::refresh_main(uint64_t start_height, enum ResetType reset, bo
bool simple_wallet::refresh(const std::vector<std::string>& args)
{
uint64_t start_height = 0;
if (m_wallet->force_rescan()) {
m_wallet->force_rescan(false);
return refresh_main(start_height, ResetSoft);
}
if(!args.empty()){
try
{
@@ -6472,7 +6505,7 @@ bool simple_wallet::show_balance_unlocked(bool detailed)
if (!detailed || balance_per_subaddress.empty())
continue;
success_msg_writer() << tr("Balance per address:");
success_msg_writer() << boost::format("%15s %21s %21s %7s %21s") % tr("Address") % tr("Balance") % tr("Unlocked balance") % tr("Outputs") % tr("Label");
success_msg_writer() << boost::format("%16s %21s %21s %7s %21s") % tr("Address") % tr("Balance") % tr("Unlocked balance") % tr("Outputs") % tr("Label");
std::vector<tools::wallet2::transfer_details> transfers;
m_wallet->get_transfers(transfers);
for (const auto& i : balance_per_subaddress)
@@ -6480,9 +6513,9 @@ bool simple_wallet::show_balance_unlocked(bool detailed)
carrot::subaddress_index_extended subaddr = {{m_current_subaddress_account, i.first},
is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot};
cryptonote::subaddress_index subaddr_index = {m_current_subaddress_account, i.first};
std::string address_str = m_wallet->get_subaddress_as_str(subaddr).substr(0, 6);
std::string address_str = m_wallet->get_subaddress_as_str(subaddr).substr(0, 8);
uint64_t num_unspent_outputs = std::count_if(transfers.begin(), transfers.end(), [&subaddr_index](const tools::wallet2::transfer_details& td) { return !td.m_spent && td.m_subaddr_index == subaddr_index; });
success_msg_writer() << boost::format(tr("%8u %6s %21s %21s %7u %21s")) % i.first % address_str % print_money(i.second) % print_money(unlocked_balance_per_subaddress[i.first].first) % num_unspent_outputs % m_wallet->get_subaddress_label(subaddr_index);
success_msg_writer() << boost::format(tr("%8u %8s %21s %21s %7u %21s")) % i.first % address_str % print_money(i.second) % print_money(unlocked_balance_per_subaddress[i.first].first) % num_unspent_outputs % m_wallet->get_subaddress_label(subaddr_index);
}
}
return true;
@@ -8594,7 +8627,13 @@ bool simple_wallet::burn(const std::vector<std::string> &args_)
}
std::vector<std::string> local_args;
local_args.push_back(m_wallet->get_subaddress_as_str({m_current_subaddress_account,0}));
carrot::AddressDeriveType derive_type;
if (m_wallet->use_fork_rules(HF_VERSION_CARROT, 0)) {
derive_type = carrot::AddressDeriveType::Carrot;
} else {
derive_type = carrot::AddressDeriveType::PreCarrot;
}
local_args.push_back(m_wallet->get_subaddress_as_str({{m_current_subaddress_account, 0}, derive_type, false}));
local_args.insert(local_args.end(), args_.begin(), args_.end());
// Get the asset type
@@ -8747,7 +8786,13 @@ bool simple_wallet::stake(const std::vector<std::string> &args_)
}
std::vector<std::string> local_args;
local_args.push_back(m_wallet->get_subaddress_as_str({m_current_subaddress_account,0}));
carrot::AddressDeriveType derive_type;
if (m_wallet->use_fork_rules(HF_VERSION_CARROT, 0)) {
derive_type = carrot::AddressDeriveType::Carrot;
} else {
derive_type = carrot::AddressDeriveType::PreCarrot;
}
local_args.push_back(m_wallet->get_subaddress_as_str({{m_current_subaddress_account, 0}, derive_type, false}));
local_args.insert(local_args.end(), args_.begin(), args_.end());
if (m_wallet->get_current_hard_fork() >= HF_VERSION_SALVIUM_ONE_PROOFS) {
@@ -9398,7 +9443,7 @@ bool simple_wallet::get_tx_proof(const std::vector<std::string> &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 = "salvium_tx_proof";
if (m_wallet->save_to_file(filename, sig_str, true))
success_msg_writer() << tr("signature file saved to: ") << filename;
else
@@ -9890,7 +9935,7 @@ bool simple_wallet::get_transfers(std::vector<std::string>& local_args, std::vec
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
payment_id = payment_id.substr(0,16);
std::string note = m_wallet->get_tx_note(pd.m_tx_hash);
std::string destination = m_wallet->get_subaddress_as_str({m_current_subaddress_account, pd.m_subaddr_index.minor});
std::string destination = m_wallet->get_subaddress_as_str({{m_current_subaddress_account, pd.m_subaddr_index.minor}, pd.m_is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
const std::string type =
pd.m_tx_type == cryptonote::transaction_type::STAKE ? "stake" :
pd.m_coinbase ? tr("block") : tr("in");
@@ -10083,7 +10128,9 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
transfer.type == "burnt" ? console_color_yellow :
transfer.type == "stake" ? console_color_cyan :
transfer.type == "yield" ? console_color_magenta :
transfer.confirmed ? ((transfer.direction == "in" || transfer.direction == "block") ? console_color_green : console_color_white) : console_color_default;
transfer.confirmed ?
((transfer.direction == "in" || transfer.direction == "block") ?
(transfer.asset_type == "SAL" ? console_color_green : console_color_blue) : console_color_white) : console_color_default;
std::string destinations = "-";
if (!transfer.outputs.empty())
@@ -10093,7 +10140,7 @@ bool simple_wallet::show_transfers(const std::vector<std::string> &args_)
{
if (!destinations.empty())
destinations += ", ";
destinations += (transfer.direction == "in" ? output.first.substr(0, 6) : output.first) + ":" + print_money(output.second);
destinations += ((transfer.direction == "in" || transfer.direction == "block") ? output.first.substr(0, 8) : output.first) + ":" + print_money(output.second);
}
}
@@ -10619,7 +10666,7 @@ std::string simple_wallet::get_prompt() const
{
if (m_locked)
return std::string("[") + tr("locked due to inactivity") + "]";
std::string addr_start = m_wallet->get_subaddress_as_str({m_current_subaddress_account, 0}).substr(0, 6);
std::string addr_start = m_wallet->get_subaddress_as_str({m_current_subaddress_account, 0}).substr(0, 8);
std::string prompt = std::string("[") + tr("wallet") + " " + addr_start;
if (!m_wallet->check_connection(NULL))
prompt += tr(" (no daemon)");
@@ -10663,12 +10710,21 @@ bool simple_wallet::account(const std::vector<std::string> &args/* = std::vector
{
// print all the existing accounts
LOCK_IDLE_SCOPE();
print_accounts();
print_accounts(false);
return true;
}
std::vector<std::string> local_args = args;
std::string command = local_args[0];
if (command == "all")
{
// print all accounts including zero balance
LOCK_IDLE_SCOPE();
print_accounts(true);
return true;
}
local_args.erase(local_args.begin());
if (command == "new")
{
@@ -10800,20 +10856,20 @@ bool simple_wallet::account(const std::vector<std::string> &args/* = std::vector
return true;
}
//----------------------------------------------------------------------------------------------------
void simple_wallet::print_accounts()
void simple_wallet::print_accounts(bool show_all)
{
const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& account_tags = m_wallet->get_account_tags();
size_t num_untagged_accounts = m_wallet->get_num_subaddress_accounts();
for (const std::pair<const std::string, std::string>& p : account_tags.first)
{
const std::string& tag = p.first;
print_accounts(tag);
print_accounts(tag, show_all);
num_untagged_accounts -= std::count(account_tags.second.begin(), account_tags.second.end(), tag);
success_msg_writer() << "";
}
if (num_untagged_accounts > 0)
print_accounts("");
print_accounts("", show_all);
if (num_untagged_accounts < m_wallet->get_num_subaddress_accounts()) {
std::map<std::string, uint64_t> balances = m_wallet->balance_all(false);
@@ -10825,7 +10881,7 @@ void simple_wallet::print_accounts()
}
}
//----------------------------------------------------------------------------------------------------
void simple_wallet::print_accounts(const std::string& tag)
void simple_wallet::print_accounts(const std::string& tag, bool show_all)
{
const std::pair<std::map<std::string, std::string>, std::vector<std::string>>& account_tags = m_wallet->get_account_tags();
if (tag.empty())
@@ -10842,7 +10898,7 @@ void simple_wallet::print_accounts(const std::string& tag)
success_msg_writer() << tr("Accounts with tag: ") << tag;
success_msg_writer() << tr("Tag's description: ") << account_tags.first.find(tag)->second;
}
success_msg_writer() << boost::format(" %15s %21s %21s %21s %21s") % tr("Account") % tr("Balance") % tr("Unlocked balance") % tr("Asset") % tr("Label");
success_msg_writer() << boost::format(" %18s %21s %21s %6s %21s") % tr("Account") % tr("Balance") % tr("Unlocked balance") % tr("Asset") % tr("Label");
std::map<std::string, std::pair<uint64_t, uint64_t>> total_balances;
std::vector<std::string> asset_types_in_wallet = m_wallet->list_asset_types();
for (const auto& asset: asset_types_in_wallet) {
@@ -10854,12 +10910,12 @@ void simple_wallet::print_accounts(const std::string& tag)
auto balance = m_wallet->balance(account_index, asset, false);
auto unlocked_balance = m_wallet->unlocked_balance(account_index, asset, false);
if (balance == 0)
if (!show_all && balance == 0)
continue;
success_msg_writer() << boost::format(tr(" %c%8u %6s %21s %21s %21s %21s"))
success_msg_writer() << boost::format(tr(" %c%8u %8s %21s %21s %6s %21s"))
% (m_current_subaddress_account == account_index ? '*' : ' ')
% account_index
% m_wallet->get_subaddress_as_str({account_index, 0}).substr(0, 6)
% m_wallet->get_subaddress_as_str({{account_index, 0}, carrot::AddressDeriveType::Auto}).substr(0, 8)
% print_money(balance)
% print_money(unlocked_balance)
% asset
@@ -10870,9 +10926,9 @@ void simple_wallet::print_accounts(const std::string& tag)
if (total_balance > 0)
total_balances[asset] = std::pair<uint64_t, uint64_t>(total_balance, total_unlocked_balance);
}
success_msg_writer() << tr("------------------------------------------------------------------------------------");
success_msg_writer() << tr("------------------------------------------------------------------------------------------------------");
for (const auto& it: total_balances)
success_msg_writer() << boost::format(tr("%15s %21s %21s %15s")) % "Total" % print_money(it.second.first) % print_money(it.second.second) % it.first;
success_msg_writer() << boost::format(tr("%18s %21s %21s %6s")) % "Total" % print_money(it.second.first) % print_money(it.second.second) % it.first;
}
//----------------------------------------------------------------------------------------------------
bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::vector<std::string>()*/)
@@ -10888,16 +10944,35 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::
std::vector<std::string> local_args = args;
tools::wallet2::transfer_container transfers;
m_wallet->get_transfers(transfers);
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
auto print_address_sub = [this, &transfers, is_carrot](uint32_t index)
auto print_address_sub = [this, &transfers](uint32_t index, bool cryptonote = true, bool carrot = true)
{
bool used = std::find_if(
transfers.begin(), transfers.end(),
[this, &index](const tools::wallet2::transfer_details& td) {
return td.m_subaddr_index == cryptonote::subaddress_index{ m_current_subaddress_account, index };
}) != transfers.end();
success_msg_writer() << index << " " << m_wallet->get_subaddress_as_str({{m_current_subaddress_account, index}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot}) << " " << (index == 0 ? tr("Primary address") : m_wallet->get_subaddress_label({m_current_subaddress_account, index})) << " " << (used ? tr("(used)") : "");
if (!cryptonote) {
success_msg_writer() << boost::format(tr("%8u %96s %21s %6s"))
% index
% m_wallet->get_subaddress_as_str({{m_current_subaddress_account, index}, carrot::AddressDeriveType::Carrot})
% (index == 0 ? tr("Primary address") : m_wallet->get_subaddress_label({m_current_subaddress_account, index}))
% (used ? tr("(used)") : "");
} else if (!carrot) {
success_msg_writer() << boost::format(tr("%8u %96s %21s %6s"))
% index
% m_wallet->get_subaddress_as_str({{m_current_subaddress_account, index}, carrot::AddressDeriveType::PreCarrot})
% (index == 0 ? tr("Primary address") : m_wallet->get_subaddress_label({m_current_subaddress_account, index}))
% (used ? tr("(used)") : "");
} else {
success_msg_writer() << boost::format(tr("%8u CN %96s %21s %6s\n Carrot %96s\n"))
% index
% m_wallet->get_subaddress_as_str({{m_current_subaddress_account, index}, carrot::AddressDeriveType::PreCarrot})
% (index == 0 ? tr("Primary address") : m_wallet->get_subaddress_label({m_current_subaddress_account, index}))
% (used ? tr("(used)") : "")
% m_wallet->get_subaddress_as_str({{m_current_subaddress_account, index}, carrot::AddressDeriveType::Carrot});
}
//success_msg_writer() << index << " " << m_wallet->get_subaddress_as_str({{m_current_subaddress_account, index}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot}) << " " << (index == 0 ? tr("Primary address") : m_wallet->get_subaddress_label({m_current_subaddress_account, index})) << " " << (used ? tr("(used)") : "");
};
uint32_t index = 0;
@@ -10905,11 +10980,22 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::
{
print_address_sub(index);
}
else if (local_args.size() == 1 && local_args[0] == "all")
else if (local_args[0] == "all")
{
local_args.erase(local_args.begin());
for (; index < m_wallet->get_num_subaddresses(m_current_subaddress_account); ++index)
print_address_sub(index);
if (local_args.size() == 0) {
message_writer(console_color_green, true) << tr(" INDEX TYPE ADDRESS");
for (; index < m_wallet->get_num_subaddresses(m_current_subaddress_account); ++index)
print_address_sub(index);
} else if (local_args[0] == "cn") {
message_writer(console_color_green, true) << tr(" INDEX ADDRESS");
for (; index < m_wallet->get_num_subaddresses(m_current_subaddress_account); ++index)
print_address_sub(index, true, false);
} else {
message_writer(console_color_green, true) << tr(" INDEX ADDRESS");
for (; index < m_wallet->get_num_subaddresses(m_current_subaddress_account); ++index)
print_address_sub(index, false, true);
}
}
else if (local_args[0] == "new")
{
@@ -11168,9 +11254,9 @@ bool simple_wallet::address_book(const std::vector<std::string> &args/* = std::v
success_msg_writer() << tr("Index: ") << i;
std::string address;
if (row.m_has_payment_id)
address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), row.m_address, row.m_payment_id, row.m_is_carrot);
address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), row.m_address, row.m_payment_id);
else
address = get_account_address_as_str(m_wallet->nettype(), row.m_is_subaddress, row.m_address, row.m_is_carrot);
address = get_account_address_as_str(m_wallet->nettype(), row.m_is_subaddress, row.m_address);
success_msg_writer() << tr("Address: ") << address;
success_msg_writer() << tr("Description: ") << row.m_description << "\n";
}
@@ -11309,7 +11395,9 @@ bool simple_wallet::wallet_info(const std::vector<std::string> &args)
}
message_writer() << tr("Filename: ") << m_wallet->get_wallet_file();
message_writer() << tr("Description: ") << description;
message_writer() << tr("Address: ") << m_wallet->get_account().get_public_address_str(m_wallet->nettype());
message_writer() << tr("CN Address: ") << m_wallet->get_account().get_public_address_str(m_wallet->nettype());
message_writer() << tr("Carrot Address: ") << m_wallet->get_account().get_carrot_public_address_str(m_wallet->nettype());
std::string type;
if (m_wallet->watch_only())
type = tr("Watch only");
+2 -2
View File
@@ -205,8 +205,8 @@ namespace cryptonote
std::vector<cryptonote::tx_destination_entry> dsts, size_t num_splits
);
bool account(const std::vector<std::string> &args = std::vector<std::string>());
void print_accounts();
void print_accounts(const std::string& tag);
void print_accounts(bool show_all = false);
void print_accounts(const std::string& tag, bool show_all = false);
bool print_address(const std::vector<std::string> &args = std::vector<std::string>());
bool print_integrated_address(const std::vector<std::string> &args = std::vector<std::string>());
bool address_book(const std::vector<std::string> &args = std::vector<std::string>());
+1 -1
View File
@@ -1,5 +1,5 @@
#define DEF_SALVIUM_VERSION_TAG "@VERSIONTAG@"
#define DEF_SALVIUM_VERSION "1.0.0-rc3"
#define DEF_SALVIUM_VERSION "1.0.7"
#define DEF_MONERO_VERSION_TAG "release"
#define DEF_MONERO_VERSION "0.18.3.4"
#define DEF_MONERO_RELEASE_NAME "One"
+6
View File
@@ -166,9 +166,15 @@ uint64_t PendingTransactionImpl::amount() const
{
uint64_t result = 0;
for (const auto &ptx : m_pending_tx) {
if (ptx.tx.type == cryptonote::transaction_type::AUDIT ||
ptx.tx.type == cryptonote::transaction_type::BURN ||
ptx.tx.type == cryptonote::transaction_type::STAKE) {
result += ptx.tx.amount_burnt;
} else {
for (const auto &dest : ptx.dests) {
result += dest.amount;
}
}
}
return result;
}
+19 -5
View File
@@ -175,7 +175,13 @@ void TransactionHistoryImpl::refresh()
uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known
uint64_t fee = pd.m_amount_in - pd.m_amount_out;
uint64_t amount = pd.m_amount_in - change - fee;
if (pd.m_tx.type == cryptonote::transaction_type::AUDIT ||
pd.m_tx.type == cryptonote::transaction_type::BURN ||
pd.m_tx.type == cryptonote::transaction_type::STAKE) {
amount = pd.m_tx.amount_burnt;
if (fee > amount) fee -= amount;
}
std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id);
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
@@ -184,7 +190,7 @@ void TransactionHistoryImpl::refresh()
TransactionInfoImpl * ti = new TransactionInfoImpl();
ti->m_paymentid = payment_id;
ti->m_amount = pd.m_amount_in - change - fee;
ti->m_amount = amount;
ti->m_fee = fee;
ti->m_direction = TransactionInfo::Direction_Out;
ti->m_hash = string_tools::pod_to_hex(hash);
@@ -212,8 +218,16 @@ void TransactionHistoryImpl::refresh()
for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments_out.begin(); i != upayments_out.end(); ++i) {
const tools::wallet2::unconfirmed_transfer_details &pd = i->second;
const crypto::hash &hash = i->first;
uint64_t amount = pd.m_amount_in;
uint64_t fee = amount - pd.m_amount_out;
uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known
uint64_t fee = pd.m_amount_in - pd.m_amount_out;
uint64_t amount = pd.m_amount_in - change - fee;
if (pd.m_tx.type == cryptonote::transaction_type::AUDIT ||
pd.m_tx.type == cryptonote::transaction_type::BURN ||
pd.m_tx.type == cryptonote::transaction_type::STAKE) {
amount = pd.m_tx.amount_burnt;
if (fee > amount) fee -= amount;
}
std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id);
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
payment_id = payment_id.substr(0,16);
@@ -221,7 +235,7 @@ void TransactionHistoryImpl::refresh()
TransactionInfoImpl * ti = new TransactionInfoImpl();
ti->m_paymentid = payment_id;
ti->m_amount = amount - pd.m_change - fee;
ti->m_amount = amount;
ti->m_fee = fee;
ti->m_direction = TransactionInfo::Direction_Out;
ti->m_failed = is_failed;
+53 -7
View File
@@ -877,40 +877,64 @@ bool WalletImpl::setDevicePassphrase(const std::string &passphrase)
return status() == Status_Ok;
}
std::string WalletImpl::address(uint32_t accountIndex, uint32_t addressIndex) const
std::string WalletImpl::address(uint32_t accountIndex, uint32_t addressIndex, bool carrot) const
{
return m_wallet->get_subaddress_as_str({accountIndex, addressIndex});
return m_wallet->get_subaddress_as_str({{accountIndex, addressIndex}, carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
}
std::string WalletImpl::integratedAddress(const std::string &payment_id) const
std::string WalletImpl::integratedAddress(const std::string &payment_id, bool carrot) const
{
crypto::hash8 pid;
if (!tools::wallet2::parse_short_payment_id(payment_id, pid)) {
return "";
}
return m_wallet->get_integrated_address_as_str(pid);
return m_wallet->get_integrated_address_as_str(pid, carrot);
}
std::string WalletImpl::secretViewKey() const
{
uint32_t hf_version = m_wallet->estimate_current_hard_fork();
if (hf_version >= HF_VERSION_CARROT)
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_view_incoming)));
else
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().m_view_secret_key)));
}
std::string WalletImpl::publicViewKey() const
{
uint32_t hf_version = m_wallet->estimate_current_hard_fork();
if (hf_version >= HF_VERSION_CARROT)
return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_carrot_account_address.m_view_public_key);
else
return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_view_public_key);
}
std::string WalletImpl::secretSpendKey() const
{
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().m_spend_secret_key)));
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().m_spend_secret_key)));
}
std::string WalletImpl::publicSpendKey() const
{
uint32_t hf_version = m_wallet->estimate_current_hard_fork();
if (hf_version >= HF_VERSION_CARROT)
return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_carrot_account_address.m_spend_public_key);
else
return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_spend_public_key);
}
std::vector<std::string> WalletImpl::carrotKeys() const
{
return {
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().s_master))),
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_prove_spend))),
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().s_view_balance))),
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_view_incoming))),
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_generate_image))),
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().s_generate_address)))
};
}
std::string WalletImpl::publicMultisigSignerKey() const
{
try {
@@ -921,6 +945,26 @@ std::string WalletImpl::publicMultisigSignerKey() const
}
}
std::string WalletImpl::secretViewBalance() const
{
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().s_view_balance)));
}
std::string WalletImpl::secretProveSpend() const
{
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_prove_spend)));
}
std::string WalletImpl::secretGenerateAddress() const
{
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().s_generate_address)));
}
std::string WalletImpl::secretGenerateImage() const
{
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_generate_image)));
}
std::string WalletImpl::path() const
{
return m_wallet->path();
@@ -1588,7 +1632,8 @@ PendingTransaction* WalletImpl::restoreMultisigTransaction(const string& signDat
PendingTransaction *WalletImpl::createStakeTransaction(uint64_t amount, uint32_t mixin_count, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices)
{
// Need to populate {dst_entr, payment_id, asset_type, is_return}
const string dst_addr = m_wallet->get_subaddress_as_str({subaddr_account, 0});//MY LOCAL (SUB)ADDRESS
const bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
const string dst_addr = m_wallet->get_subaddress_as_str({{subaddr_account, 0}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});//MY LOCAL (SUB)ADDRESS
const string payment_id = "";
const string asset_type = "SAL1";
const bool is_return = false;
@@ -1605,7 +1650,8 @@ PendingTransaction *WalletImpl::createAuditTransaction(
std::set<uint32_t> subaddr_indices
) {
// Need to populate {dst_entr, payment_id, asset_type, is_return}
const string dst_addr = m_wallet->get_subaddress_as_str({subaddr_account, 0});//MY LOCAL (SUB)ADDRESS
const bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
const string dst_addr = m_wallet->get_subaddress_as_str({{subaddr_account, 0}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});//MY LOCAL (SUB)ADDRESS
const string payment_id = "";
const string asset_type = "SAL";
const bool is_return = false;
+7 -2
View File
@@ -93,13 +93,18 @@ public:
const std::string& getPassword() const override;
bool setDevicePin(const std::string &password) override;
bool setDevicePassphrase(const std::string &password) override;
std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const override;
std::string integratedAddress(const std::string &payment_id) const override;
std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0, bool carrot = true) const override;
std::string integratedAddress(const std::string &payment_id, bool carrot = true) const override;
std::string secretViewKey() const override;
std::string publicViewKey() const override;
std::string secretSpendKey() const override;
std::string publicSpendKey() const override;
std::vector<std::string> carrotKeys() const override;
std::string publicMultisigSignerKey() const override;
std::string secretViewBalance() const override;
std::string secretProveSpend() const override;
std::string secretGenerateAddress() const override;
std::string secretGenerateImage() const override;
std::string path() const override;
void stop() override;
bool store(const std::string &path) override;
+39 -3
View File
@@ -513,8 +513,8 @@ struct Wallet
virtual const std::string& getPassword() const = 0;
virtual bool setDevicePin(const std::string &pin) { (void)pin; return false; };
virtual bool setDevicePassphrase(const std::string &passphrase) { (void)passphrase; return false; };
virtual std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const = 0;
std::string mainAddress() const { return address(0, 0); }
virtual std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0, bool carrot = true) const = 0;
std::string mainAddress(bool carrot = true) const { return address(0, 0, carrot); }
virtual std::string path() const = 0;
virtual NetworkType nettype() const = 0;
bool mainnet() const { return nettype() == MAINNET; }
@@ -533,7 +533,7 @@ struct Wallet
* generated
* \return - 106 characters string representing integrated address
*/
virtual std::string integratedAddress(const std::string &payment_id) const = 0;
virtual std::string integratedAddress(const std::string &payment_id, bool carrot = true) const = 0;
/*!
* \brief secretViewKey - returns secret view key
@@ -559,12 +559,48 @@ struct Wallet
*/
virtual std::string publicSpendKey() const = 0;
/*!
* \brief allCarrotKeys - returns all Carrot keys
* [0] - s_master
* [1] - k_prove_spend
* [2] - s_view_balance
* [3] - k_view_incoming
* [4] - k_generate_image
* [5] - s_generate_address
* \return - vector of all Carrot keys
*/
virtual std::vector<std::string> carrotKeys() const = 0;
/*!
* \brief publicMultisigSignerKey - returns public signer key
* \return - public multisignature signer key or empty string if wallet is not multisig
*/
virtual std::string publicMultisigSignerKey() const = 0;
/*!
* \brief secretViewBalance - returns Carrot "view balance" secret
* \return - Carrot s_vb
*/
virtual std::string secretViewBalance() const = 0;
/*!
* \brief secretProveSpend - returns Carrot "prove spend" secret
* \return - Carrot secret k_ps
*/
virtual std::string secretProveSpend() const = 0;
/*!
* \brief secretGenerateAddress - returns Carrot "generate address" secret
* \return - Carrot secret s_ga
*/
virtual std::string secretGenerateAddress() const = 0;
/*!
* \brief secretGenerateImage - returns Carrot "generate key image" secret
* \return - Carrot secret k_gi
*/
virtual std::string secretGenerateImage() const = 0;
/*!
* \brief stop - interrupts wallet refresh() loop once (doesn't stop background refresh thread)
*/
+8 -1
View File
@@ -973,7 +973,14 @@ std::optional<crypto::key_image> try_derive_enote_key_image(
// x = k_s + k^j_subext + k^g_o
rct::key x;
if (enote_scan_info.is_carrot) {
return acc.derive_key_image(enote_scan_info.address_spend_pubkey,
// if we don't have the s_master key, derive view-only key image
if (acc.get_keys().s_master == crypto::null_skey) {
return acc.derive_key_image_view_only(enote_scan_info.address_spend_pubkey,
enote_scan_info.sender_extension_g,
enote_scan_info.sender_extension_t,
rct::rct2pk(onetime_address));
}
return acc.derive_key_image(enote_scan_info.address_spend_pubkey,
enote_scan_info.sender_extension_g,
enote_scan_info.sender_extension_t,
rct::rct2pk(onetime_address));
+33 -19
View File
@@ -91,7 +91,7 @@ static bool is_transfer_usable_for_input_selection(const wallet2::transfer_detai
&& td.m_key_image_known
&& !td.m_key_image_partial
&& !td.m_frozen
&& (top_block_index >= td.m_block_height + blocks_locked_for)
&& (top_block_index +1 >= td.m_block_height + blocks_locked_for)
// && last_locked_block_index <= top_block_index
&& td.m_subaddr_index.major == from_account
&& (from_subaddresses.empty() || from_subaddresses.count(td.m_subaddr_index.minor) == 1)
@@ -148,9 +148,10 @@ static cryptonote::tx_destination_entry make_tx_destination_entry(
const carrot::CarrotPaymentProposalV1 &payment_proposal)
{
cryptonote::tx_destination_entry dest = cryptonote::tx_destination_entry(payment_proposal.amount,
{payment_proposal.destination.address_spend_pubkey, payment_proposal.destination.address_view_pubkey},
{payment_proposal.destination.address_spend_pubkey, payment_proposal.destination.address_view_pubkey, /*m_is_carrot*/true},
payment_proposal.destination.is_subaddress);
dest.is_integrated = payment_proposal.destination.payment_id != carrot::null_payment_id;
dest.asset_type = payment_proposal.asset_type;
return dest;
}
//-------------------------------------------------------------------------------------------------------------------
@@ -165,9 +166,11 @@ static cryptonote::tx_destination_entry make_tx_destination_entry(
address_view_pubkey),
"make_tx_destination_entry: view-key multiplication failed");
return cryptonote::tx_destination_entry(payment_proposal.proposal.amount,
{payment_proposal.proposal.destination_address_spend_pubkey, address_view_pubkey},
payment_proposal.subaddr_index.index.is_subaddress());
cryptonote::tx_destination_entry dest = cryptonote::tx_destination_entry(payment_proposal.proposal.amount,
{payment_proposal.proposal.destination_address_spend_pubkey, address_view_pubkey, /*m_is_carrot*/true},
payment_proposal.subaddr_index.index.is_subaddress());
dest.asset_type = payment_proposal.proposal.asset_type;
return dest;
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
@@ -346,7 +349,7 @@ std::vector<cryptonote::tx_source_entry> get_sources(
w.get_outs(outs, selected_transfers, fake_outputs_count, true, valid_public_keys_cache); // may throw
LOG_PRINT_L2("preparing outputs");
size_t i = 0, out_index = 0;
size_t out_index = 0;
std::vector<cryptonote::tx_source_entry> sources;
for(size_t idx: selected_transfers)
{
@@ -387,7 +390,6 @@ std::vector<cryptonote::tx_source_entry> get_sources(
oe.second.mask = std::get<2>(outs[out_index][n]);
src.outputs.push_back(oe);
}
++i;
//paste real transaction to the random index
auto it_to_replace = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const tx_output_entry& a)
@@ -815,8 +817,14 @@ bool get_address_openings_x_y(
if (return_output_map.find(rct::rct2pk(src.outputs[src.real_output].second.dest)) != return_output_map.end())
{
const auto &return_output = return_output_map.at(rct::rct2pk(src.outputs[src.real_output].second.dest));
x_out = return_output.x;
y_out = return_output.y;
bool r = w.get_account().try_searching_for_opening_for_onetime_address(
return_output.K_spend_pubkey,
return_output.sum_g,
return_output.sender_extension_t,
x_out,
y_out
);
CHECK_AND_ASSERT_THROW_MES(r, "Failed to obtain openings for onetime address (return_payment)");
return true;
}
@@ -1088,14 +1096,14 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
const auto &sources = tx_proposal.sources;
// inputs
uint64_t amount_in = 0;
// uint64_t amount_in = 0;
rct::carrot_ctkeyV inSk;
inSk.reserve(sources.size());
std::vector<uint64_t> inamounts;
std::vector<unsigned int> index;
for (const auto& src: sources)
{
amount_in += src.amount;
// amount_in += src.amount;
inamounts.push_back(src.amount);
index.push_back(src.real_output);
@@ -1144,7 +1152,7 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
}
// outputs
uint64_t amount_out = 0;
// uint64_t amount_out = 0;
std::vector<uint64_t> outamounts;
rct::keyV destinations;
std::vector<std::string> destination_asset_types;
@@ -1154,7 +1162,7 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
destinations.push_back(rct::pk2rct(oep.enote.onetime_address));
destination_asset_types.push_back(oep.enote.asset_type);
outamounts.push_back(oep.amount);
amount_out += oep.amount;
// amount_out += oep.amount;
rct::ctkey key;
key.mask = rct::sk2rct(oep.amount_blinding_factor);
@@ -1288,7 +1296,8 @@ wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionPropos
carrot::encrypted_payment_id_t encrypted_payment_id;
std::vector<std::pair<bool, std::size_t>> sorted_payment_proposal_indices;
carrot::get_output_enote_proposals_from_proposal_v1(tx_proposal,
/*s_view_balance_dev=*/nullptr,
&account.s_view_balance_dev,
///*s_view_balance_dev=*/nullptr,
&account.k_view_incoming_dev,
output_enote_proposals,
encrypted_payment_id,
@@ -1360,11 +1369,16 @@ wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionPropos
ptx.change_dts = change_dts;
ptx.selected_transfers = std::move(selected_transfers);
ptx.key_images = key_images_string.str();
ptx.tx_key = shared_ephemeral_pubkey ? ephemeral_privkeys.at(0) : crypto::null_skey;
if (shared_ephemeral_pubkey)
ptx.additional_tx_keys = std::move(ephemeral_privkeys);
else
ptx.additional_tx_keys.clear();
if (ephemeral_privkeys.size() == 1) {
ptx.tx_key = ephemeral_privkeys.at(0);
ptx.additional_tx_keys.clear();
} else if (ephemeral_privkeys.size() == 2 && shared_ephemeral_pubkey) {
ptx.tx_key = (ephemeral_privkeys.at(0) == crypto::null_skey) ? ephemeral_privkeys.at(1) : ephemeral_privkeys.at(0);
ptx.additional_tx_keys.clear();
} else {
ptx.tx_key = crypto::null_skey;
ptx.additional_tx_keys = std::move(ephemeral_privkeys);
}
ptx.dests = std::move(dests);
ptx.multisig_sigs = {};
ptx.multisig_tx_key_entropy = {};
+516 -148
View File
File diff suppressed because it is too large Load Diff
+37 -8
View File
@@ -496,9 +496,10 @@ private:
bool m_coinbase;
cryptonote::subaddress_index m_subaddr_index;
cryptonote::transaction_type m_tx_type;
bool m_is_carrot;
BEGIN_SERIALIZE_OBJECT()
VERSION_FIELD(0)
VERSION_FIELD(1)
FIELD(m_tx_hash)
VARINT_FIELD(m_amount)
FIELD(m_asset_type)
@@ -510,6 +511,11 @@ private:
FIELD(m_coinbase)
FIELD(m_subaddr_index)
VARINT_FIELD(m_tx_type)
if (version < 1) {
m_is_carrot = false;
return true;
}
FIELD(m_is_carrot)
END_SERIALIZE()
};
@@ -1197,8 +1203,9 @@ private:
std::vector<crypto::public_key> get_subaddress_spend_public_keys(uint32_t account, uint32_t begin, uint32_t end) const;
//std::string get_subaddress_as_str(const cryptonote::subaddress_index& index) const;
std::string get_subaddress_as_str(const carrot::subaddress_index_extended& index) const;
std::string get_subaddress_as_str(const carrot::subaddress_index_extended& index, const uint64_t height) const;
std::string get_address_as_str() const { return get_subaddress_as_str({0, 0}); }
std::string get_integrated_address_as_str(const crypto::hash8& payment_id) const;
std::string get_integrated_address_as_str(const crypto::hash8& payment_id, bool carrot = true) const;
void add_subaddress_account(const std::string& label);
size_t get_num_subaddress_accounts() const { return m_subaddress_labels.size(); }
size_t get_num_subaddresses(uint32_t index_major) const { return index_major < m_subaddress_labels.size() ? m_subaddress_labels[index_major].size() : 0; }
@@ -1419,8 +1426,6 @@ private:
if (ver < 20)
return;
a & m_subaddresses.parent();
a & m_subaddresses_extended.parent();
a & m_return_output_info.parent();
std::unordered_map<cryptonote::subaddress_index, crypto::public_key> dummy_subaddresses_inv;
a & dummy_subaddresses_inv;
a & m_subaddress_labels;
@@ -1461,14 +1466,18 @@ private:
if(ver < 31)
{
m_background_sync_data = background_sync_data_t{};
m_subaddresses_extended = {};
m_return_output_info = {};
return;
}
a & m_background_sync_data;
a & m_subaddresses_extended.parent();
a & m_return_output_info.parent();
}
BEGIN_SERIALIZE_OBJECT()
MAGIC_FIELD("monero wallet cache")
VERSION_FIELD(2)
VERSION_FIELD(3)
FIELD(m_blockchain)
FIELD(m_transfers)
FIELD(m_transfers_indices)
@@ -1487,8 +1496,6 @@ private:
FIELD(m_scanned_pool_txs[0])
FIELD(m_scanned_pool_txs[1])
FIELD(m_subaddresses)
FIELD(m_subaddresses_extended)
FIELD(m_return_output_info)
FIELD(m_subaddress_labels)
FIELD(m_additional_tx_keys)
FIELD(m_attributes)
@@ -1508,9 +1515,21 @@ private:
if (version < 2)
{
m_background_sync_data = background_sync_data_t{};
m_subaddresses_extended = {};
m_return_output_info = {};
return true;
}
FIELD(m_background_sync_data)
FIELD(m_subaddresses_extended)
if (version == 2)
{
serializable_unordered_map<crypto::public_key, carrot::return_output_info_retired_t> old_roi;
FIELD(old_roi)
m_return_output_info = {};
m_force_rescan = true;
return true;
}
FIELD(m_return_output_info)
END_SERIALIZE()
/*!
@@ -1544,6 +1563,8 @@ private:
void set_default_priority(uint32_t p) { m_default_priority = p; }
bool auto_refresh() const { return m_auto_refresh; }
void auto_refresh(bool r) { m_auto_refresh = r; }
bool force_rescan() const { return m_force_rescan; }
void force_rescan(bool r) { m_force_rescan = r; }
AskPasswordType ask_password() const { return m_ask_password; }
void ask_password(AskPasswordType ask) { m_ask_password = ask; }
void set_min_output_count(uint32_t count) { m_min_output_count = count; }
@@ -1657,6 +1678,7 @@ private:
size_t get_num_transfer_details() const { return m_transfers.size(); }
const transfer_details &get_transfer_details(size_t idx) const;
uint8_t estimate_current_hard_fork(const uint64_t height = 0) const;
uint8_t get_current_hard_fork();
void get_hard_fork_info(uint8_t version, uint64_t &earliest_height);
bool use_fork_rules(uint8_t version, int64_t early_blocks = 0);
@@ -2188,6 +2210,7 @@ private:
uint32_t m_default_priority;
RefreshType m_refresh_type;
bool m_auto_refresh;
bool m_force_rescan = false;
bool m_first_refresh_done;
uint64_t m_refresh_from_block_height;
// If m_refresh_from_block_height is explicitly set to zero we need this to differentiate it from the case that
@@ -2289,7 +2312,7 @@ BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 12)
BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1)
BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0)
BOOST_CLASS_VERSION(tools::wallet2::multisig_tx_set, 1)
BOOST_CLASS_VERSION(tools::wallet2::payment_details, 5)
BOOST_CLASS_VERSION(tools::wallet2::payment_details, 6)
BOOST_CLASS_VERSION(tools::wallet2::pool_payment_details, 1)
BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 8)
BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 6)
@@ -2618,6 +2641,12 @@ namespace boost
a & x.m_amounts;
a & x.m_asset_type;
a & x.m_tx_type;
if (ver < 6)
{
x.m_is_carrot = false;
return;
}
a & x.m_is_carrot;
}
template <class Archive>
+1 -1
View File
@@ -152,7 +152,7 @@ bool wallet2::search_for_rpc_payment(uint64_t credits_target, uint32_t n_threads
{
tpool.submit(&waiter, [&, i] {
*(uint32_t*)(hashing_blob.data() + 39) = SWAP32LE(local_nonce-i);
const uint8_t major_version = hashing_blob[0];
// const uint8_t major_version = hashing_blob[0];
crypto::rx_slow_hash(seed_hash.data, hashing_blob.data(), hashing_blob.size(), hash[i].data);
});
}
+64 -23
View File
@@ -439,7 +439,7 @@ namespace tools
entry.subaddr_index = pd.m_subaddr_index;
entry.subaddr_indices.push_back(pd.m_subaddr_index);
//entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index);
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
bool is_carrot = m_wallet->estimate_current_hard_fork(entry.height) >= HF_VERSION_CARROT;
entry.address = m_wallet->get_subaddress_as_str({{pd.m_subaddr_index.major, pd.m_subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward(), pd.m_unlock_time);
}
@@ -473,7 +473,7 @@ namespace tools
for (uint32_t i: pd.m_subaddr_indices)
entry.subaddr_indices.push_back({pd.m_subaddr_account, i});
//entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0});
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
bool is_carrot = m_wallet->estimate_current_hard_fork(entry.height) >= HF_VERSION_CARROT;
entry.address = m_wallet->get_subaddress_as_str({{pd.m_subaddr_account, 0}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward(), pd.m_unlock_time);
}
@@ -508,7 +508,7 @@ namespace tools
for (uint32_t i: pd.m_subaddr_indices)
entry.subaddr_indices.push_back({pd.m_subaddr_account, i});
//entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0});
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
bool is_carrot = m_wallet->estimate_current_hard_fork(entry.height) >= HF_VERSION_CARROT;
entry.address = m_wallet->get_subaddress_as_str({{pd.m_subaddr_account, 0}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward(), pd.m_tx.unlock_time);
}
@@ -533,7 +533,7 @@ namespace tools
entry.subaddr_index = pd.m_subaddr_index;
entry.subaddr_indices.push_back(pd.m_subaddr_index);
//entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index);
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
bool is_carrot = m_wallet->estimate_current_hard_fork(entry.height) >= HF_VERSION_CARROT;
entry.address = m_wallet->get_subaddress_as_str({{pd.m_subaddr_index.major, pd.m_subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward(), pd.m_unlock_time);
}
@@ -542,8 +542,8 @@ namespace tools
{
if (!m_wallet) return not_open(er);
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
bool is_carrot = m_wallet->estimate_current_hard_fork() >= HF_VERSION_CARROT;
std::vector<std::string> assets_in_wallet = m_wallet->list_asset_types();
std::string asset_type = req.asset_type.empty() ? "SAL1" : boost::algorithm::to_upper_copy(req.asset_type);
// verify that the asset is in the list of in-wallet assets
@@ -627,8 +627,6 @@ namespace tools
{
if (!m_wallet) return not_open(er);
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
try
{
THROW_WALLET_EXCEPTION_IF(req.account_index >= m_wallet->get_num_subaddress_accounts(), error::account_index_outofbound);
@@ -651,8 +649,9 @@ namespace tools
res.addresses.resize(res.addresses.size() + 1);
auto& info = res.addresses.back();
const cryptonote::subaddress_index index = {req.account_index, i};
//info.address = m_wallet->get_subaddress_as_str(index);
info.address = m_wallet->get_subaddress_as_str({{req.account_index, i}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
info.address = m_wallet->get_subaddress_as_str({req.account_index, i});
info.address_cn = req.cryptonote ? m_wallet->get_subaddress_as_str({{req.account_index, i}, carrot::AddressDeriveType::PreCarrot}) : "";
info.address_carrot = req.carrot ? m_wallet->get_subaddress_as_str({{req.account_index, i}, carrot::AddressDeriveType::Carrot}) : "";
info.label = m_wallet->get_subaddress_label(index);
info.address_index = index.minor;
info.used = std::find_if(transfers.begin(), transfers.end(), [&](const tools::wallet2::transfer_details& td) { return td.m_subaddr_index == index; }) != transfers.end();
@@ -746,8 +745,6 @@ namespace tools
{
if (!m_wallet) return not_open(er);
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
try
{
res.total_balance = 0;
@@ -768,8 +765,7 @@ namespace tools
continue;
wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::subaddress_account_info info;
info.account_index = subaddr_index.major;
//info.base_address = m_wallet->get_subaddress_as_str(subaddr_index);
info.base_address = m_wallet->get_subaddress_as_str({{subaddr_index.major, subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
info.base_address = m_wallet->get_subaddress_as_str({{subaddr_index.major, 0}, carrot::AddressDeriveType::Auto});
//for (const auto& asset: asset_types) {
info.balance = m_wallet->balance(subaddr_index.major, "SAL1", req.strict_balances);
@@ -2103,8 +2099,6 @@ namespace tools
{
if (!m_wallet) return not_open(er);
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
crypto::hash payment_id;
crypto::hash8 payment_id8;
cryptonote::blobdata payment_id_blob;
@@ -2145,7 +2139,7 @@ namespace tools
rpc_payment.unlock_time = payment.m_unlock_time;
rpc_payment.locked = !m_wallet->is_transfer_unlocked(payment.m_unlock_time, payment.m_block_height);
rpc_payment.subaddr_index = payment.m_subaddr_index;
//rpc_payment.address = m_wallet->get_subaddress_as_str(payment.m_subaddr_index);
bool is_carrot = m_wallet->estimate_current_hard_fork(payment.m_block_height) >= HF_VERSION_CARROT;
rpc_payment.address = m_wallet->get_subaddress_as_str({{payment.m_subaddr_index.major, payment.m_subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
res.payments.push_back(rpc_payment);
}
@@ -2157,8 +2151,6 @@ namespace tools
{
res.payments.clear();
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
if (!m_wallet) return not_open(er);
/* If the payment ID list is empty, we get payments to any payment ID (or lack thereof) */
@@ -2176,7 +2168,7 @@ namespace tools
rpc_payment.block_height = payment.second.m_block_height;
rpc_payment.unlock_time = payment.second.m_unlock_time;
rpc_payment.subaddr_index = payment.second.m_subaddr_index;
//rpc_payment.address = m_wallet->get_subaddress_as_str(payment.second.m_subaddr_index);
bool is_carrot = m_wallet->estimate_current_hard_fork(payment.second.m_block_height) >= HF_VERSION_CARROT;
rpc_payment.address = m_wallet->get_subaddress_as_str({{payment.second.m_subaddr_index.major, payment.second.m_subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
rpc_payment.locked = !m_wallet->is_transfer_unlocked(payment.second.m_unlock_time, payment.second.m_block_height);
res.payments.push_back(std::move(rpc_payment));
@@ -2232,7 +2224,7 @@ namespace tools
rpc_payment.block_height = payment.m_block_height;
rpc_payment.unlock_time = payment.m_unlock_time;
rpc_payment.subaddr_index = payment.m_subaddr_index;
//rpc_payment.address = m_wallet->get_subaddress_as_str(payment.m_subaddr_index);
bool is_carrot = m_wallet->estimate_current_hard_fork(payment.m_block_height) >= HF_VERSION_CARROT;
rpc_payment.address = m_wallet->get_subaddress_as_str({{payment.m_subaddr_index.major, payment.m_subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
rpc_payment.locked = !m_wallet->is_transfer_unlocked(payment.m_unlock_time, payment.m_block_height);
res.payments.push_back(std::move(rpc_payment));
@@ -2363,6 +2355,55 @@ namespace tools
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().m_spend_secret_key);
res.key = std::string(key.data(), key.size());
}
else if(req.key_type.compare("s_master") == 0)
{
if (m_wallet->watch_only())
{
er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY;
er.message = "The wallet is watch-only. Cannot retrieve s_master key.";
return false;
}
CHECK_IF_BACKGROUND_SYNCING();
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().s_master);
res.key = std::string(key.data(), key.size());
}
else if(req.key_type.compare("k_prove_spend") == 0)
{
if (m_wallet->watch_only())
{
er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY;
er.message = "The wallet is watch-only. Cannot retrieve k_prove_spend key.";
return false;
}
CHECK_IF_BACKGROUND_SYNCING();
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().k_prove_spend);
res.key = std::string(key.data(), key.size());
}
else if(req.key_type.compare("s_view_balance") == 0)
{
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().s_view_balance);
res.key = std::string(key.data(), key.size());
}
else if(req.key_type.compare("s_view_balance") == 0)
{
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().s_view_balance);
res.key = std::string(key.data(), key.size());
}
else if(req.key_type.compare("k_view_incoming") == 0)
{
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().k_view_incoming);
res.key = std::string(key.data(), key.size());
}
else if(req.key_type.compare("k_generate_image") == 0)
{
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().k_generate_image);
res.key = std::string(key.data(), key.size());
}
else if(req.key_type.compare("s_generate_address") == 0)
{
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().s_generate_address);
res.key = std::string(key.data(), key.size());
}
else
{
er.message = "key_type " + req.key_type + " not found";
@@ -3313,9 +3354,9 @@ namespace tools
const auto &entry = ab[idx];
std::string address;
if (entry.m_has_payment_id)
address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), entry.m_address, entry.m_payment_id, entry.m_is_carrot);
address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), entry.m_address, entry.m_payment_id);
else
address = get_account_address_as_str(m_wallet->nettype(), entry.m_is_subaddress, entry.m_address, entry.m_is_carrot);
address = get_account_address_as_str(m_wallet->nettype(), entry.m_is_subaddress, entry.m_address);
res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx, address, entry.m_description});
}
}
@@ -139,9 +139,13 @@ namespace wallet_rpc
{
uint32_t account_index;
std::vector<uint32_t> address_index;
bool carrot;
bool cryptonote;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(account_index)
KV_SERIALIZE(address_index)
KV_SERIALIZE_OPT(carrot, true);
KV_SERIALIZE_OPT(cryptonote, true);
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
@@ -149,12 +153,16 @@ namespace wallet_rpc
struct address_info
{
std::string address;
std::string address_cn;
std::string address_carrot;
std::string label;
uint32_t address_index;
bool used;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(address)
KV_SERIALIZE(address_cn)
KV_SERIALIZE(address_carrot)
KV_SERIALIZE(label)
KV_SERIALIZE(address_index)
KV_SERIALIZE(used)
+3 -1
View File
@@ -29,11 +29,13 @@
set(functional_tests_sources
main.cpp
transactions_flow_test.cpp
stake_tx.cpp
transactions_generation_from_blockchain.cpp)
set(functional_tests_headers
transactions_flow_test.h
transactions_generation_from_blockchain.h)
transactions_generation_from_blockchain.h
stake_tx.h)
monero_add_minimal_executable(functional_tests
${functional_tests_sources}
+24
View File
@@ -37,20 +37,24 @@ using namespace epee;
#include "common/command_line.h"
#include "common/util.h"
#include "transactions_flow_test.h"
#include "stake_tx.h"
namespace po = boost::program_options;
namespace
{
const command_line::arg_descriptor<bool> arg_test_transactions_flow = {"test_transactions_flow", ""};
const command_line::arg_descriptor<bool> arg_test_stake_tx = {"test_stake_tx", "Test stake transactions"};
const command_line::arg_descriptor<std::string> arg_working_folder = {"working-folder", "", "."};
const command_line::arg_descriptor<std::string> arg_source_wallet = {"source-wallet", "", "", true};
const command_line::arg_descriptor<std::string> arg_dest_wallet = {"dest-wallet", "", "", true};
const command_line::arg_descriptor<std::string> arg_daemon_addr_a = {"daemon-addr-a", "", "127.0.0.1:8080"};
const command_line::arg_descriptor<std::string> arg_daemon_addr_b = {"daemon-addr-b", "", "127.0.0.1:8082"};
const command_line::arg_descriptor<std::string> arg_daemon_addr_stake = {"daemon-addr-stake", "", "127.0.0.1:29081"};
const command_line::arg_descriptor<uint64_t> arg_transfer_amount = {"transfer_amount", "", 60000000000000};
const command_line::arg_descriptor<uint64_t> arg_stake_amount = {"stake_amount", "Amount to stake", 100000000};
const command_line::arg_descriptor<size_t> arg_mix_in_factor = {"mix-in-factor", "", 15};
const command_line::arg_descriptor<size_t> arg_tx_count = {"tx-count", "", 100};
const command_line::arg_descriptor<size_t> arg_tx_per_second = {"tx-per-second", "", 20};
@@ -71,14 +75,17 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_options, command_line::arg_help);
command_line::add_arg(desc_options, arg_test_transactions_flow);
command_line::add_arg(desc_options, arg_test_stake_tx);
command_line::add_arg(desc_options, arg_working_folder);
command_line::add_arg(desc_options, arg_source_wallet);
command_line::add_arg(desc_options, arg_dest_wallet);
command_line::add_arg(desc_options, arg_daemon_addr_a);
command_line::add_arg(desc_options, arg_daemon_addr_b);
command_line::add_arg(desc_options, arg_daemon_addr_stake);
command_line::add_arg(desc_options, arg_transfer_amount);
command_line::add_arg(desc_options, arg_stake_amount);
command_line::add_arg(desc_options, arg_mix_in_factor);
command_line::add_arg(desc_options, arg_tx_count);
command_line::add_arg(desc_options, arg_tx_per_second);
@@ -126,6 +133,23 @@ int main(int argc, char* argv[])
return 1;
}
else if (command_line::get_arg(vm, arg_test_stake_tx))
{
std::string working_folder = command_line::get_arg(vm, arg_working_folder);
std::string wallet_name;
if(command_line::has_arg(vm, arg_source_wallet))
wallet_name = command_line::get_arg(vm, arg_source_wallet);
std::string daemon_addr_stake = command_line::get_arg(vm, arg_daemon_addr_stake);
uint64_t amount_to_stake = command_line::get_arg(vm, arg_stake_amount);
if(!stake_transaction_test(working_folder, wallet_name, daemon_addr_stake, amount_to_stake))
return 1;
std::string s;
std::cin >> s;
return 0;
}
else
{
std::cout << desc_options << std::endl;
+438
View File
@@ -0,0 +1,438 @@
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/uuid/random_generator.hpp>
#include <unordered_map>
#include "include_base_utils.h"
using namespace epee;
#include "wallet/wallet2.h"
#include "transactions_flow_test.h"
#include "stake_tx.h"
using namespace cryptonote;
// fixed-difficulty should be at least 7.
bool stop_mining(epee::net_utils::http::http_simple_client& http_client){
COMMAND_RPC_STOP_MINING::request stop_mine_req = AUTO_VAL_INIT(stop_mine_req);
COMMAND_RPC_STOP_MINING::response stop_mine_rsp = AUTO_VAL_INIT(stop_mine_rsp);
bool r = net_utils::invoke_http_json("/stop_mining", stop_mine_req, stop_mine_rsp, http_client, std::chrono::seconds(10));
CHECK_AND_ASSERT_MES(r, false, "failed to stop mining");
return true;
}
bool stake_transaction_test(std::string& working_folder,
std::string wallet_name,
std::string& daemon_addr_a,
uint64_t amount_to_stake) {
LOG_PRINT_L0("-----------------------STARTING STAKE TRANSACTION TEST-----------------------");
tools::wallet2 w1(network_type::TESTNET);
w1.inactivity_lock_timeout(0);
w1.ask_password(tools::wallet2::AskPasswordType::AskPasswordNever);
w1.setup_background_mining(tools::wallet2::BackgroundMiningSetupType::BackgroundMiningNo); // disable background mining
if(wallet_name.empty())
wallet_name = generate_random_wallet_name();
try
{
w1.generate(working_folder + "/" + wallet_name, "");
}
catch (const std::exception& e)
{
LOG_ERROR("failed to generate wallet: " << e.what());
return false;
}
w1.init(daemon_addr_a);
epee::net_utils::http::http_simple_client http_client;
bool r = http_client.set_server(daemon_addr_a, boost::none);
CHECK_AND_ASSERT_MES(r, false, "failed to connect to daemon");
// stop mining just in case
stop_mining(http_client);
// get current height
COMMAND_RPC_GET_HEIGHT::request height_req = AUTO_VAL_INIT(height_req);
COMMAND_RPC_GET_HEIGHT::response height_rsp = AUTO_VAL_INIT(height_rsp);
r = net_utils::invoke_http_json("/get_height", height_req, height_rsp, http_client, std::chrono::seconds(10));
CHECK_AND_ASSERT_MES(r, false, "failed to get height from daemon");
// pop blocks to make sure of the start height
COMMAND_RPC_POP_BLOCKS::request pop_blocks_req = AUTO_VAL_INIT(pop_blocks_req);
COMMAND_RPC_POP_BLOCKS::response pop_blocks_rsp = AUTO_VAL_INIT(pop_blocks_rsp);
pop_blocks_req.nblocks = height_rsp.height - 1; //keep at least one block
r = net_utils::invoke_http_json("/pop_blocks", pop_blocks_req, pop_blocks_rsp, http_client, std::chrono::seconds(10));
CHECK_AND_ASSERT_MES(r, false, "failed to pop blocks");
// flush tx pool to make sure of the start state
COMMAND_RPC_FLUSH_TRANSACTION_POOL::request flush_req = AUTO_VAL_INIT(flush_req);
COMMAND_RPC_FLUSH_TRANSACTION_POOL::response flush_rsp = AUTO_VAL_INIT(flush_rsp);
r = net_utils::invoke_http_json_rpc("/json_rpc", "flush_txpool", flush_req, flush_rsp, http_client, std::chrono::seconds(10));
CHECK_AND_ASSERT_MES(r, false, "failed to flush tx pool");
w1.rescan_blockchain(true, true, false);
MGINFO_GREEN("Using wallets: " << ENDL
<< "Source: " << w1.get_account().get_public_address_str(TESTNET) << ENDL << "Path: " << working_folder + "/" + wallet_name << ENDL);
//lets make some money
COMMAND_RPC_START_MINING::request start_mining_req = AUTO_VAL_INIT(start_mining_req);
COMMAND_RPC_START_MINING::response start_mining_rsp = AUTO_VAL_INIT(start_mining_rsp);
start_mining_req.miner_address = w1.get_account().get_public_address_str(TESTNET);
start_mining_req.threads_count = 1;
r = net_utils::invoke_http_json("/start_mining", start_mining_req, start_mining_rsp, http_client, std::chrono::seconds(10));
CHECK_AND_ASSERT_MES(r, false, "failed to start mining getrandom_outs");
CHECK_AND_ASSERT_MES(start_mining_rsp.status == CORE_RPC_STATUS_OK, false, "failed to start mining");
// mine until we reach to block 710
height_rsp.height = 1;
while (height_rsp.height < 710)
{
misc_utils::sleep_no_w(1000); // The difficulty should be high enough to stop mining before reaching block 800.”
r = net_utils::invoke_http_json("/get_height", height_req, height_rsp, http_client, std::chrono::seconds(10));
CHECK_AND_ASSERT_MES(r, false, "failed to get height from daemon 1");
}
// stop mining
stop_mining(http_client);
// refresh wallet
uint64_t blocks_fetched = 0;
bool received_money;
w1.refresh(true, 0, blocks_fetched, received_money);
MGINFO_GREEN("balance: " << w1.balance(0, "SAL", false));
// create and submit CN SAL stake transaction ~ block 710
std::vector<cryptonote::tx_destination_entry> dsts;
cryptonote::tx_destination_entry de;
de.addr = w1.get_account().get_keys().m_account_address;
de.amount = amount_to_stake;
de.is_subaddress = false;
dsts.push_back(de);
try
{
std::vector<tools::wallet2::pending_tx> ptx;
ptx = w1.create_transactions_2(dsts, "SAL", "SAL", cryptonote::transaction_type::STAKE, 15, 0, 0, std::vector<uint8_t>(), 0, {});
for (auto &p: ptx)
w1.commit_tx(p);
}
catch (const std::exception&)
{
LOG_ERROR("failed to create/commit CN stake transaction at height: " << height_rsp.height << "with SAL");
return false;
}
// lets make some money
r = net_utils::invoke_http_json("/start_mining", start_mining_req, start_mining_rsp, http_client, std::chrono::seconds(10));
CHECK_AND_ASSERT_MES(r, false, "failed to start mining getrandom_outs");
CHECK_AND_ASSERT_MES(start_mining_rsp.status == CORE_RPC_STATUS_OK, false, "failed to start mining");
// mine until we reach to block 1010
while (height_rsp.height < 1010) // difficulty should be high enough to stop mining before reaching block 1080
{
misc_utils::sleep_no_w(1000);
r = net_utils::invoke_http_json("/get_height", height_req, height_rsp, http_client, std::chrono::seconds(10));
CHECK_AND_ASSERT_MES(r, false, "failed to get height from daemon");
}
// stop mining if not stopped already
stop_mining(http_client);
r = net_utils::invoke_http_json("/get_height", height_req, height_rsp, http_client, std::chrono::seconds(10));
// Check the height to ensure we are receiving yield payments before the Carrot hard fork at block 1100
if (!r)
{
LOG_ERROR("failed to get height from daemon");
return false;
}
if (height_rsp.height > 1080)
{
LOG_PRINT_L0("----!! The current height (" << height_rsp.height << ") exceeds the acceptable range for this test. Please restart the node with an increased fixed difficulty. !!----");
}
// refresh wallet
blocks_fetched = 0;
received_money = false;
bool ok = false;
if(!w1.refresh(true, blocks_fetched, received_money, ok))
{
LOG_ERROR( "failed to refresh source wallet from " << daemon_addr_a );
return false;
}
// check if we have received yield from CN SAL stake transaction ~1010
tools::wallet2::transfer_container incoming_transfers;
// scan payments
w1.get_transfers(incoming_transfers);
CHECK_AND_ASSERT_MES(!incoming_transfers.empty(), false, "failed to get payments");
bool found = false;
for (const auto& p: incoming_transfers)
{
if (p.m_tx.type == cryptonote::transaction_type::PROTOCOL)
{
if (p.m_amount > amount_to_stake)
{
LOG_PRINT_L0("found yield from CN SAL stake transaction: " << p.m_amount - amount_to_stake << " at height: " << p.m_block_height);
}
else
{
LOG_ERROR("invalid yield amount from CN SAL stake transaction: " << p.m_amount - amount_to_stake << " at height: " << p.m_block_height);
return false;
}
found = true;
break;
}
}
CHECK_AND_ASSERT_MES(found, false, "failed to find yield from CN SAL stake transaction");
//create and submit CN SAL1 stake transaction
dsts.clear();
de.addr = w1.get_account().get_keys().m_account_address;
de.amount = amount_to_stake;
de.is_subaddress = false;
dsts.push_back(de);
try
{
std::vector<tools::wallet2::pending_tx> ptx;
ptx = w1.create_transactions_2(dsts, "SAL1", "SAL1", cryptonote::transaction_type::STAKE, 15, 0, 0, std::vector<uint8_t>(), 0, {});
for (auto &p: ptx)
w1.commit_tx(p);
}
catch (const std::exception&)
{
LOG_ERROR("failed to create/commit CN stake transaction at height: " << height_rsp.height << "with SAL1");
return false;
}
// lets make some money
r = net_utils::invoke_http_json("/start_mining", start_mining_req, start_mining_rsp, http_client, std::chrono::seconds(10));
CHECK_AND_ASSERT_MES(r, false, "failed to start mining getrandom_outs");
CHECK_AND_ASSERT_MES(start_mining_rsp.status == CORE_RPC_STATUS_OK, false, "failed to start mining");
// mine until we reach to block 1091
while (height_rsp.height < 1091)
{
misc_utils::sleep_no_w(100);
r = net_utils::invoke_http_json("/get_height", height_req, height_rsp, http_client, std::chrono::seconds(1));
CHECK_AND_ASSERT_MES(r, false, "failed to get height from daemon");
}
// stop mining if not stopped already
stop_mining(http_client);
// refresh wallet
blocks_fetched = 0;
received_money = false;
ok = false;
if(!w1.refresh(true, blocks_fetched, received_money, ok))
{
LOG_ERROR( "failed to refresh source wallet from " << daemon_addr_a );
return false;
}
// check if we have received yield from CN SAL stake transaction
incoming_transfers.clear();
// scan payments
w1.get_transfers(incoming_transfers);
CHECK_AND_ASSERT_MES(!incoming_transfers.empty(), false, "failed to get payments");
found = false;
for (const auto& p: incoming_transfers)
{
if (p.m_block_height < 1010) continue; // skip payments from the first stake transaction
if (p.m_tx.type == cryptonote::transaction_type::PROTOCOL)
{
if (p.m_amount > amount_to_stake)
{
LOG_PRINT_L0("found yield from CN SAL1 stake transaction: " << p.m_amount - amount_to_stake << " at height: " << p.m_block_height);
}
else
{
LOG_ERROR("invalid yield amount from CN SAL1 stake transaction: " << p.m_amount - amount_to_stake << " at height: " << p.m_block_height);
return false;
}
found = true;
break;
}
}
CHECK_AND_ASSERT_MES(found, false, "failed to find yield from CN SAL1 stake transaction");
// pop blocks to make sure of the start height
pop_blocks_req = AUTO_VAL_INIT(pop_blocks_req);
pop_blocks_req.nblocks = height_rsp.height - 1090; //keep at least 1090 blocks
r = net_utils::invoke_http_json("/pop_blocks", pop_blocks_req, pop_blocks_rsp, http_client, std::chrono::seconds(10));
CHECK_AND_ASSERT_MES(r, false, "failed to pop blocks");
// flush tx pool to make sure of the start state
r = net_utils::invoke_http_json_rpc("/json_rpc", "flush_txpool", flush_req, flush_rsp, http_client, std::chrono::seconds(10));
CHECK_AND_ASSERT_MES(r, false, "failed to flush tx pool");
// rescan blockchain to ensure wallet is in sync after popping blocks
w1.rescan_blockchain(true, true, false);
MGINFO_GREEN("Using wallets: " << ENDL
<< "Source: " << w1.get_account().get_public_address_str(TESTNET) << ENDL << "Path: " << working_folder + "/" + wallet_name << ENDL);
// Create and stake CN SAL1 stake transaction and get yield after the Carrot hard fork at block 1100 ~ block 1090
dsts.clear();
de.addr = w1.get_account().get_keys().m_account_address;
de.amount = amount_to_stake;
de.is_subaddress = false;
dsts.push_back(de);
try
{
std::vector<tools::wallet2::pending_tx> ptx;
ptx = w1.create_transactions_2(dsts, "SAL1", "SAL1", cryptonote::transaction_type::STAKE, 15, 0, 0, std::vector<uint8_t>(), 0, {});
for (auto &p: ptx)
w1.commit_tx(p);
}
catch (const std::exception&)
{
LOG_ERROR("failed to create/commit CN stake transaction at height: " << height_rsp.height << "with SAL1");
return false;
}
/// lets make some money
r = net_utils::invoke_http_json("/start_mining", start_mining_req, start_mining_rsp, http_client, std::chrono::seconds(10));
CHECK_AND_ASSERT_MES(r, false, "failed to start mining getrandom_outs");
CHECK_AND_ASSERT_MES(start_mining_rsp.status == CORE_RPC_STATUS_OK, false, "failed to start mining");
// mine until we reach to block 1100
while (height_rsp.height < 1100)
{
misc_utils::sleep_no_w(1000);
r = net_utils::invoke_http_json("/get_height", height_req, height_rsp, http_client, std::chrono::seconds(10));
CHECK_AND_ASSERT_MES(r, false, "failed to get height from daemon");
}
// stop mining if not stopped already
stop_mining(http_client);
// Carrot will not be able to connect to the network until the node is restarted
LOG_PRINT_L0("---------------------- PLEASE RESTART THE NODE NOW ----------------------");
misc_utils::sleep_no_w(1000); // wait for the log to be printed
LOG_PRINT_L2("Press any key to continue once the node is back online");
std::string input;
std::cin >> input;
// restart the http client connection
r = http_client.set_server(daemon_addr_a, boost::none);
CHECK_AND_ASSERT_MES(r, false, "failed to connect to daemon");
r = net_utils::invoke_http_json("/get_height", height_req, height_rsp, http_client, std::chrono::seconds(10));
CHECK_AND_ASSERT_MES(r, false, "Daemon is not yet back online");
LOG_PRINT_L0("Daemon is back online, continuing tests");
LOG_PRINT_L2("Carrot has been enabled.");
// restart the wallet connection
w1.init(daemon_addr_a);
// Create and stake Carrot SAL1 stake transaction ~ block 1100
dsts.clear();
de.addr = w1.get_account().get_keys().m_carrot_main_address;
de.amount = amount_to_stake;
de.is_subaddress = false;
dsts.push_back(de);
try
{
std::vector<tools::wallet2::pending_tx> ptx;
ptx = w1.create_transactions_2(dsts, "SAL1", "SAL1", cryptonote::transaction_type::STAKE, 15, 0, 0, std::vector<uint8_t>(), 0, {});
for (auto &p: ptx)
w1.commit_tx(p);
}
catch (const std::exception&)
{
LOG_ERROR("failed to create/commit Carrot stake transaction at height: " << height_rsp.height << " with SAL1");
return false;
}
// lets make some money
start_mining_req.miner_address = w1.get_account().get_carrot_public_address_str(TESTNET);
start_mining_req.threads_count = 1;
r = net_utils::invoke_http_json("/start_mining", start_mining_req, start_mining_rsp, http_client, std::chrono::seconds(10));
CHECK_AND_ASSERT_MES(r, false, "failed to start mining getrandom_outs");
CHECK_AND_ASSERT_MES(start_mining_rsp.status == CORE_RPC_STATUS_OK, false, "failed to start mining");
// mine until we reach to block 1300
while (height_rsp.height < 1300)
{
misc_utils::sleep_no_w(1000);
r = net_utils::invoke_http_json("/get_height", height_req, height_rsp, http_client, std::chrono::seconds(10));
CHECK_AND_ASSERT_MES(r, false, "failed to get height from daemon");
}
// stop mining
stop_mining(http_client);
// refresh wallet
blocks_fetched = 0;
received_money = false;
ok = false;
if(!w1.refresh(true, blocks_fetched, received_money, ok))
{
LOG_ERROR( "failed to refresh source wallet from " << daemon_addr_a );
return false;
}
// check if we have received yield from Carrot SAL1 stake transaction
incoming_transfers.clear();
// scan payments
w1.get_transfers(incoming_transfers);
CHECK_AND_ASSERT_MES(!incoming_transfers.empty(), false, "failed to get payments");
found = false;
for (const auto& p: incoming_transfers)
{
if (p.m_block_height > 1112 || p.m_block_height < 1090) continue; // Skip payments originating from CryptoNote stake transactions
if (p.m_tx.type == cryptonote::transaction_type::PROTOCOL)
{
if (p.m_amount > amount_to_stake)
{
LOG_PRINT_L0("found yield from Carrot SAL1 stake transaction: " << p.m_amount - amount_to_stake << " at height: " << p.m_block_height);
LOG_PRINT_L2("Stake was made before the Carrot hard fork and yield received after Carrot hard fork");
}
else
{
LOG_ERROR("invalid yield amount from Carrot SAL1 stake transaction: " << p.m_amount - amount_to_stake << " at height: " << p.m_block_height);
return false;
}
found = true;
break;
}
}
CHECK_AND_ASSERT_MES(found, false, "failed to find yield from Carrot SAL1 stake transaction");
// check if we have received yield from Carrot SAL1 stake transaction
incoming_transfers.clear();
// scan payments
w1.get_transfers(incoming_transfers);
CHECK_AND_ASSERT_MES(!incoming_transfers.empty(), false, "failed to get payments");
found = false;
for (const auto& p: incoming_transfers)
{
if (p.m_block_height <= 1112) continue; // // Skip payments originating from CryptoNote stake transactions
if (p.m_tx.type == cryptonote::transaction_type::PROTOCOL)
{
if (p.m_amount > amount_to_stake)
{
LOG_PRINT_L0("found yield from Carrot SAL1 stake transaction: " << p.m_amount - amount_to_stake << " at height: " << p.m_block_height);
LOG_PRINT_L0("All yields from stake transactions have been successfully found!");
}
else
{
LOG_ERROR("invalid yield amount from Carrot SAL1 stake transaction: " << p.m_amount - amount_to_stake << " at height: " << p.m_block_height);
return false;
}
found = true;
break;
}
}
CHECK_AND_ASSERT_MES(found, false, "failed to find yield from Carrot SAL1 stake transaction");
LOG_PRINT_L2("All yields from stake transactions have been successfully found!");
LOG_PRINT_L0("-----------------------STAKE TRANSACTION TEST PASSED-----------------------");
return true;
}
+34
View File
@@ -0,0 +1,34 @@
// Copyright (c) 2014-2022, The Monero Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
// conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
// used to endorse or promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
bool stake_transaction_test(std::string& working_folder,
std::string wallet_name,
std::string& daemon_addr_a,
uint64_t amount_to_stake);
@@ -28,6 +28,8 @@
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
std::string generate_random_wallet_name();
bool transactions_flow_test(std::string& working_folder,
std::string path_source_wallet,
std::string path_target_wallet,
+1
View File
@@ -44,6 +44,7 @@ set(unit_tests_sources
carrot_mock_helpers.cpp
carrot_sparc.cpp
carrot_transcript_fixed.cpp
carrot_tx_proof.cpp
chacha.cpp
checkpoints.cpp
command_line.cpp
+20 -12
View File
@@ -323,13 +323,10 @@ TEST(carrot_core, main_address_special_scan_completeness)
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
RCTOutputEnoteProposal enote_proposal;
RCTOutputEnoteProposal return_enote;
get_output_proposal_special_v1(proposal,
keys.k_view_incoming_dev,
tx_first_key_image,
cryptonote::transaction_type::TRANSFER, // tx_type
std::nullopt,
return_enote,
enote_proposal);
ASSERT_EQ(proposal.amount, enote_proposal.amount);
@@ -403,13 +400,10 @@ TEST(carrot_core, subaddress_special_scan_completeness)
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
RCTOutputEnoteProposal enote_proposal;
RCTOutputEnoteProposal return_enote;
get_output_proposal_special_v1(proposal,
keys.k_view_incoming_dev,
tx_first_key_image,
cryptonote::transaction_type::TRANSFER, // tx_type
std::nullopt,
return_enote,
enote_proposal);
ASSERT_EQ(proposal.amount, enote_proposal.amount);
@@ -460,7 +454,7 @@ TEST(carrot_core, subaddress_special_scan_completeness)
//----------------------------------------------------------------------------------------------------------------------
TEST(carrot_core, main_address_internal_scan_completeness)
{
mock::mock_carrot_and_legacy_keys keys;
carrot::carrot_and_legacy_account keys;
keys.generate();
const CarrotDestinationV1 main_address = keys.cryptonote_address();
@@ -481,10 +475,13 @@ TEST(carrot_core, main_address_internal_scan_completeness)
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
RCTOutputEnoteProposal enote_proposal;
RCTOutputEnoteProposal return_proposal;
get_output_proposal_internal_v1(proposal,
keys.s_view_balance_dev,
tx_first_key_image,
std::nullopt,
cryptonote::transaction_type::TRANSFER, // tx_type
return_proposal,
enote_proposal);
ASSERT_EQ(proposal.amount, enote_proposal.amount);
@@ -498,15 +495,19 @@ TEST(carrot_core, main_address_internal_scan_completeness)
crypto::secret_key recovered_amount_blinding_factor;
CarrotEnoteType recovered_enote_type;
janus_anchor_t recovered_internal_message;
crypto::public_key return_address_out;
bool is_return_out;
const bool scan_success = try_scan_carrot_enote_internal_receiver(enote_proposal.enote,
keys.s_view_balance_dev,
keys,
recovered_sender_extension_g,
recovered_sender_extension_t,
recovered_address_spend_pubkey,
recovered_amount,
recovered_amount_blinding_factor,
recovered_enote_type,
recovered_internal_message);
recovered_internal_message,
return_address_out,
is_return_out);
ASSERT_TRUE(scan_success);
@@ -527,7 +528,7 @@ TEST(carrot_core, main_address_internal_scan_completeness)
//----------------------------------------------------------------------------------------------------------------------
TEST(carrot_core, subaddress_internal_scan_completeness)
{
mock::mock_carrot_and_legacy_keys keys;
carrot::carrot_and_legacy_account keys;
keys.generate();
const uint32_t j_major = crypto::rand_idx(mock::MAX_SUBADDRESS_MAJOR_INDEX);
@@ -551,10 +552,13 @@ TEST(carrot_core, subaddress_internal_scan_completeness)
const crypto::key_image tx_first_key_image = rct::rct2ki(rct::pkGen());
RCTOutputEnoteProposal enote_proposal;
RCTOutputEnoteProposal return_proposal;
get_output_proposal_internal_v1(proposal,
keys.s_view_balance_dev,
tx_first_key_image,
std::nullopt,
cryptonote::transaction_type::TRANSFER, // tx_type
return_proposal,
enote_proposal);
ASSERT_EQ(proposal.amount, enote_proposal.amount);
@@ -568,15 +572,19 @@ TEST(carrot_core, subaddress_internal_scan_completeness)
crypto::secret_key recovered_amount_blinding_factor;
CarrotEnoteType recovered_enote_type;
janus_anchor_t recovered_internal_message;
crypto::public_key return_address_out;
bool is_return_out;
const bool scan_success = try_scan_carrot_enote_internal_receiver(enote_proposal.enote,
keys.s_view_balance_dev,
keys,
recovered_sender_extension_g,
recovered_sender_extension_t,
recovered_address_spend_pubkey,
recovered_amount,
recovered_amount_blinding_factor,
recovered_enote_type,
recovered_internal_message);
recovered_internal_message,
return_address_out,
is_return_out);
ASSERT_TRUE(scan_success);
+25 -23
View File
@@ -808,11 +808,11 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_10)
tx_proposal.self_sender_index = 2;
// 1 subaddress payment (subtractable)
acc0.second.emplace_back().first = CarrotPaymentProposalV1{ = {CarrotPaymentProposalV1{
acc0.second.emplace_back().first = CarrotPaymentProposalV1{
.destination = acc0.first.subaddress({{2, 3}}),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
}, true};
};
// 1 main address payment
acc1.second.emplace_back().first = CarrotPaymentProposalV1{
@@ -826,7 +826,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_10)
.destination = acc3.first.cryptonote_address(gen_payment_id()),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
}, true};
};
// specify fee per weight
tx_proposal.fee_per_weight = 314159;
@@ -914,11 +914,11 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_12)
tx_proposal.self_sender_index = 2;
// 2 subaddress payment (1 subtractable)
acc0.second.emplace_back().first = CarrotPaymentProposalV1{ = {CarrotPaymentProposalV1{
acc0.second.emplace_back().first =CarrotPaymentProposalV1{
.destination = acc0.first.subaddress({{2, 3}}),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
}, true};
};
acc0.second.push_back(acc0.second.front());
acc0.second.back().first.randomness = gen_janus_anchor(); //mangle anchor_norm
acc0.second.back().second = false; //set not subtractable, first already is
@@ -937,7 +937,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_12)
.destination = acc3.first.cryptonote_address(gen_payment_id()),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
}, true};
};
// 1 main address selfsend
tx_proposal.explicit_selfsend_proposals.emplace_back().first.proposal = CarrotPaymentProposalSelfSendV1{
@@ -955,7 +955,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_12)
.enote_type = CarrotEnoteType::CHANGE
},
.subaddr_index = {{4, 19}}
}, true};
};
// specify fee per weight
tx_proposal.fee_per_weight = 314159;
@@ -1021,11 +1021,11 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_14)
tx_proposal.self_sender_index = 2;
// 1 subaddress payment (subtractable)
acc0.second.emplace_back().first = CarrotPaymentProposalV1{ = {CarrotPaymentProposalV1{
acc0.second.emplace_back().first = CarrotPaymentProposalV1{
.destination = acc0.first.subaddress({{2, 3}}),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
}, true};
};
// 1 main address payment
acc1.second.emplace_back().first = CarrotPaymentProposalV1{
@@ -1039,7 +1039,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_14)
.destination = acc3.first.cryptonote_address(gen_payment_id()),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
}, true};
};
// specify fee per weight
tx_proposal.fee_per_weight = 314159;
@@ -1071,11 +1071,11 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_15)
tx_proposal.self_sender_index = 2;
// 2 subaddress payment (subtractable)
acc0.second.emplace_back().first = CarrotPaymentProposalV1{ = {CarrotPaymentProposalV1{
acc0.second.emplace_back().first = CarrotPaymentProposalV1{
.destination = acc0.first.subaddress({{2, 3}}),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
}, true};
};
acc0.second.push_back(acc0.second.front());
acc0.second.back().first.randomness = gen_janus_anchor(); //mangle anchor_norm
@@ -1084,7 +1084,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_15)
.destination = acc1.first.cryptonote_address(),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
}, true};
};
acc1.second.push_back(acc1.second.front());
acc1.second.back().first.randomness = gen_janus_anchor(); //mangle anchor_norm
@@ -1093,7 +1093,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_15)
.destination = acc3.first.cryptonote_address(gen_payment_id()),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
}, true};
};
// specify fee per weight
tx_proposal.fee_per_weight = 314159;
@@ -1125,11 +1125,11 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_16)
tx_proposal.self_sender_index = 2;
// 2 subaddress payment (subtractable)
acc0.second.emplace_back().first = CarrotPaymentProposalV1{ = {CarrotPaymentProposalV1{
acc0.second.emplace_back().first = CarrotPaymentProposalV1{
.destination = acc0.first.subaddress({{2, 3}}),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
}, true};
};
acc0.second.push_back(acc0.second.front());
acc0.second.back().first.randomness = gen_janus_anchor(); //mangle anchor_norm
@@ -1138,7 +1138,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_16)
.destination = acc1.first.cryptonote_address(),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
}, true};
};
acc1.second.push_back(acc1.second.front());
acc1.second.back().first.randomness = gen_janus_anchor(); //mangle anchor_norm
@@ -1147,14 +1147,16 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_16)
.destination = acc3.first.cryptonote_address(gen_payment_id()),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
}, true};
};
// 1 main address selfsend (subtractable)
tx_proposal.explicit_selfsend_proposals.emplace_back() = {CarrotPaymentProposalSelfSendV1{
.destination_address_spend_pubkey = acc2.first.carrot_account_spend_pubkey,
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.enote_type = CarrotEnoteType::PAYMENT,
// no internal messages for legacy self-sends
tx_proposal.explicit_selfsend_proposals.emplace_back() = {CarrotPaymentProposalVerifiableSelfSendV1{
.proposal = CarrotPaymentProposalSelfSendV1{
.destination_address_spend_pubkey = acc2.first.carrot_account_spend_pubkey,
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.enote_type = CarrotEnoteType::PAYMENT,
// no internal messages for legacy self-sends
}
}, true};
// 1 subaddress selfsend (subtractable)
+7 -2
View File
@@ -359,15 +359,20 @@ void mock_scan_enote_set(const std::vector<CarrotEnoteV1> &enotes,
const CarrotEnoteV1 &enote = enotes.at(output_index);
mock_scan_result_t scan_result{};
carrot::carrot_and_legacy_account account;
crypto::public_key return_address_out;
bool is_return_out;
const bool r = try_scan_carrot_enote_internal_receiver(enote,
keys.s_view_balance_dev,
account,
scan_result.sender_extension_g,
scan_result.sender_extension_t,
scan_result.address_spend_pubkey,
scan_result.amount,
scan_result.amount_blinding_factor,
scan_result.enote_type,
scan_result.internal_message);
scan_result.internal_message,
return_address_out,
is_return_out);
scan_result.output_index = output_index;
+17 -11
View File
@@ -54,7 +54,7 @@ static auto auto_wiper(T &obj)
}
//----------------------------------------------------------------------------------------------------------------------
std::tuple<std::vector<RCTOutputEnoteProposal>, crypto::public_key> make_origin_tx(
mock::mock_carrot_and_legacy_keys &alice,
carrot::carrot_and_legacy_account &alice,
CarrotDestinationV1 &bob_address
) {
// spend input
@@ -62,9 +62,10 @@ std::tuple<std::vector<RCTOutputEnoteProposal>, crypto::public_key> make_origin_
// make change output
RCTOutputEnoteProposal enote_proposal_change;
RCTOutputEnoteProposal return_proposal;
get_output_proposal_internal_v1(
CarrotPaymentProposalSelfSendV1{
.destination_address_spend_pubkey = alice.carrot_account_spend_pubkey,
.destination_address_spend_pubkey = alice.get_keys().m_carrot_account_address.m_spend_public_key,
.amount = crypto::rand<rct::xmr_amount>(),
.enote_type = CarrotEnoteType::CHANGE,
.enote_ephemeral_pubkey = gen_x25519_pubkey(),
@@ -72,6 +73,8 @@ std::tuple<std::vector<RCTOutputEnoteProposal>, crypto::public_key> make_origin_
alice.s_view_balance_dev,
tx_first_key_image,
std::nullopt,
cryptonote::transaction_type::TRANSFER, // tx_type
return_proposal,
enote_proposal_change
);
@@ -138,7 +141,7 @@ std::tuple<std::vector<RCTOutputEnoteProposal>, crypto::public_key> make_origin_
}
//----------------------------------------------------------------------------------------------------------------------
std::tuple<std::vector<RCTOutputEnoteProposal>, crypto::public_key> make_return_tx(
mock::mock_carrot_and_legacy_keys &bob,
carrot::carrot_and_legacy_account &bob,
std::vector<RCTOutputEnoteProposal> &origin_tx_outputs
) {
// [0] enote is change, [1] enote bob received
@@ -169,7 +172,7 @@ std::tuple<std::vector<RCTOutputEnoteProposal>, crypto::public_key> make_return_
received_output,
std::nullopt,
origin_tx_shared_secret_unctx,
{&bob.carrot_account_spend_pubkey, 1},
{&bob.get_keys().m_carrot_account_address.m_spend_public_key, 1},
bob.k_view_incoming_dev,
recovered_sender_extension_g,
recovered_sender_extension_t,
@@ -182,7 +185,7 @@ std::tuple<std::vector<RCTOutputEnoteProposal>, crypto::public_key> make_return_
EXPECT_TRUE(scan_success);
// check we can spend it
EXPECT_TRUE(bob.can_open_fcmp_onetime_address(bob.carrot_account_spend_pubkey,
EXPECT_TRUE(bob.can_open_fcmp_onetime_address(bob.get_keys().m_carrot_account_address.m_spend_public_key,
recovered_sender_extension_g,
recovered_sender_extension_t,
received_output.onetime_address));
@@ -237,7 +240,7 @@ std::tuple<std::vector<RCTOutputEnoteProposal>, crypto::public_key> make_return_
TEST(carrot_sparc, main_address_return_payment_normal_scan_completeness)
{
// these will generate a new format carrot address.
mock::mock_carrot_and_legacy_keys alice, bob;
carrot::carrot_and_legacy_account alice, bob;
alice.generate();
bob.generate();
@@ -332,20 +335,23 @@ TEST(carrot_sparc, main_address_return_payment_normal_scan_completeness)
crypto::secret_key recovered_amount_blinding_factor_change;
CarrotEnoteType recovered_enote_type_change;
janus_anchor_t recovered_internal_message_out_change;
crypto::public_key return_address_out;
bool is_return_out;
const bool scan_success_change = try_scan_carrot_enote_internal_receiver(change_output,
alice.s_view_balance_dev,
alice,
recovered_sender_extension_g_change,
recovered_sender_extension_t_change,
recovered_address_spend_pubkey_change,
recovered_amount_change,
recovered_amount_blinding_factor_change,
recovered_enote_type_change,
recovered_internal_message_out_change);
recovered_internal_message_out_change,
return_address_out,
is_return_out);
ASSERT_TRUE(scan_success_change);
// check spendability of the change output
EXPECT_TRUE(alice.can_open_fcmp_onetime_address(alice.carrot_account_spend_pubkey,
EXPECT_TRUE(alice.can_open_fcmp_onetime_address(alice.get_keys().m_carrot_account_address.m_spend_public_key,
recovered_sender_extension_g_change,
recovered_sender_extension_t_change,
change_output.onetime_address));
@@ -353,7 +359,7 @@ TEST(carrot_sparc, main_address_return_payment_normal_scan_completeness)
// check spendability of the return_payment
crypto::secret_key sum_g;
sc_add(to_bytes(sum_g), to_bytes(recovered_sender_extension_g_change), to_bytes(k_return));
ASSERT_TRUE(alice.can_open_fcmp_onetime_address(alice.carrot_account_spend_pubkey,
ASSERT_TRUE(alice.can_open_fcmp_onetime_address(alice.get_keys().m_carrot_account_address.m_spend_public_key,
sum_g,
recovered_sender_extension_t_change,
return_output.onetime_address));
+445
View File
@@ -0,0 +1,445 @@
// Copyright (c) 2025, Salvium (authors: SRCG, auruya)
//
// 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.
#include "gtest/gtest.h"
#include "crypto/crypto.h"
extern "C" {
#include "crypto/crypto-ops.h"
}
#include "crypto/hash.h"
#include <boost/algorithm/string.hpp>
#include "mx25519.h"
#include "carrot_core/account.h"
#include "carrot_impl/format_utils.h"
#include "string_tools.h"
using namespace carrot;
static inline void random_carrot_keys(crypto::secret_key& a,
crypto::public_key& A,
crypto::secret_key& b,
crypto::public_key& B,
const bool create_subaddress)
{
// Generate a new CN address
carrot::carrot_and_legacy_account alice;
alice.generate();
const auto& keys = alice.get_keys();
// Check for subaddress
if (create_subaddress) {
// Create subaddress
carrot::subaddress_index_extended sie{{0,1}, AddressDeriveType::Carrot, false};
carrot::CarrotDestinationV1 subaddr = alice.subaddress(sie);
a = keys.k_view_incoming;
A = subaddr.address_view_pubkey;
b = keys.k_prove_spend;
B = subaddr.address_spend_pubkey;
} else {
a = keys.k_view_incoming;
A = keys.m_carrot_account_address.m_view_public_key;
b = keys.k_prove_spend;
B = keys.m_carrot_account_address.m_spend_public_key;
}
}
TEST(carrot_tx_proofs, fuzz_stability)
{
static const size_t ITER = 5000; // increase to 50k if needed
for (size_t i = 0; i < ITER; ++i)
{
// 1. Generate random Carrot recipient view & spend keys A/B/a/b
bool use_subaddress = ((i & 1) == 1); // alternate: main/sub
crypto::secret_key a;
crypto::public_key A;
crypto::secret_key b;
crypto::public_key B;
random_carrot_keys(a, A, b, B, use_subaddress);
// 2. Generate random tx private key r
crypto::secret_key r;
crypto::random32_unbiased((unsigned char*)unwrap(r).data);
// 3. Recipient can be main address (no B) or subaddress
//const crypto::public_key *B_ptr = use_subaddress ? &B : nullptr;
// 4. Compute R = ConvertPointE(r * (G or B))
crypto::public_key R_pk;
mx25519_pubkey enote_ephemeral_pubkey_out;
carrot::make_carrot_enote_ephemeral_pubkey(r,
B,
use_subaddress,
enote_ephemeral_pubkey_out);
R_pk = carrot::raw_byte_convert<crypto::public_key>(enote_ephemeral_pubkey_out);
// 5. Compute D = ConvertPointE(r * A)
mx25519_pubkey s_sr;
bool success = carrot::make_carrot_uncontextualized_shared_key_sender(r, A, s_sr);
ASSERT_TRUE(success) << "failure to compute shared secret";
crypto::public_key D_pk = carrot::raw_byte_convert<crypto::public_key>(s_sr);
// 6. Random prefix hash
crypto::hash prefix_hash;
for (int j = 0; j < 32; j++) prefix_hash.data[j] = rand() & 0xFF;
// 7. Prove
crypto::signature sig;
crypto::generate_carrot_tx_proof(
prefix_hash,
R_pk, A,
use_subaddress ? boost::make_optional(B) : boost::none,
D_pk, r, a, sig
);
// 8. Verify
bool ok = crypto::check_carrot_tx_proof(
prefix_hash,
R_pk, A,
use_subaddress ? boost::make_optional(B) : boost::none,
D_pk, sig
);
ASSERT_TRUE(ok) << "failure at iteration " << i;
// ---------------------------------------------------------
// 9. NEGATIVE TESTS
// ---------------------------------------------------------
// 9a. Flip a bit in R
{
crypto::public_key R_bad = R_pk;
R_bad.data[5] ^= 0x20;
bool ok2 = crypto::check_carrot_tx_proof(
prefix_hash, R_bad, A,
use_subaddress ? boost::make_optional(B) : boost::none,
D_pk, sig
);
ASSERT_FALSE(ok2);
}
// 9b. Flip a bit in D
{
crypto::public_key D_bad = D_pk;
D_bad.data[7] ^= 0x10;
bool ok2 = crypto::check_carrot_tx_proof(
prefix_hash, R_pk, A,
use_subaddress ? boost::make_optional(B) : boost::none,
D_bad, sig
);
ASSERT_FALSE(ok2);
}
// 9c. Flip a bit in sig.c
{
crypto::signature sig_bad = sig;
sig_bad.c.data[3] ^= 0x80;
bool ok2 = crypto::check_carrot_tx_proof(
prefix_hash, R_pk, A,
use_subaddress ? boost::make_optional(B) : boost::none,
D_pk, sig_bad
);
ASSERT_FALSE(ok2);
}
// 9d. Flip a bit in sign_mask
{
crypto::signature sig_bad = sig;
sig_bad.sign_mask ^= 0x01; // flip R_sign
bool ok2 = crypto::check_carrot_tx_proof(
prefix_hash, R_pk, A,
use_subaddress ? boost::make_optional(B) : boost::none,
D_pk, sig_bad
);
ASSERT_FALSE(ok2);
}
// 9e. Flip a bit in sig.r
{
crypto::signature sig_bad = sig;
sig_bad.r.data[0] ^= 0x40;
bool ok2 = crypto::check_carrot_tx_proof(
prefix_hash, R_pk, A,
use_subaddress ? boost::make_optional(B) : boost::none,
D_pk, sig_bad
);
ASSERT_FALSE(ok2);
}
// 9f. Flip a bit in A
{
crypto::public_key A_bad = A;
A_bad.data[12] ^= 0x08;
bool ok2 = crypto::check_carrot_tx_proof(
prefix_hash, R_pk, A_bad,
use_subaddress ? boost::make_optional(B) : boost::none,
D_pk, sig
);
ASSERT_FALSE(ok2);
}
// 9g. Flip a bit in B (when subaddress)
if (use_subaddress)
{
crypto::public_key B_bad = B;
B_bad.data[9] ^= 0x40;
bool ok2 = crypto::check_carrot_tx_proof(
prefix_hash, R_pk, A,
boost::make_optional(B_bad),
D_pk, sig
);
ASSERT_FALSE(ok2);
}
}
}
TEST(carrot_tx_proofs, known_values_mutation_rejection_main_address)
{
// Real Salvium Carrot addresses for reproducible test (testnet) (main address)
// Sender: SC1ToqKSXRw3rE3rNQzjUA1nntvHhM6id3coWry25y4jUvHDRKDRGFv1vJRCTMHWUyVXct2aedmvzUfd3CofjTpKEhHmpnftqZk
// Recipient: SC1ToumwqT5GeDcn2JjrHoFPUMPMcNu73STkP3Sono94iZpizheMJ3ADpGGE92Wcb7b3gDCxKFT5NEp94ueQQMbu8VBYyAGHEy7
// Tx ID: e8729399d2af3dede8c110e370b3505c1669f4fba593fd740a16c1e4f425a728
// Tx priv key
crypto::secret_key r;
const char* r_hex = "748b8f3131661fd8ee0f06ab3de53649381522185ea6e8148c8daf395ded010d";
ASSERT_TRUE(epee::string_tools::hex_to_pod(r_hex, r));
// Recipient's actual Carrot keys from Salvium network
crypto::secret_key a, b; // view-incoming and prove-spend priv keys
crypto::public_key A, B; // view and spend pub keys
// recipient view-incoming key
const char* a_hex = "88b6442966238bfd349eb412fe55a717b7b363175b48ae88c34a252dd8868e05";
ASSERT_TRUE(epee::string_tools::hex_to_pod(a_hex, a));
// recipient prove-spend key
const char* b_hex = "94607b25bceb408bbb5393d25729777e92686b9447f9c989f2e735e878950d0b";
ASSERT_TRUE(epee::string_tools::hex_to_pod(b_hex, b));
// Generate pub keys from priv keys
ASSERT_TRUE(crypto::secret_key_to_public_key(a, A));
ASSERT_TRUE(crypto::secret_key_to_public_key(b, B));
// Compute R = rG (main address case)
mx25519_pubkey enote_ephemeral_pubkey_out;
carrot::make_carrot_enote_ephemeral_pubkey(r, B, false, enote_ephemeral_pubkey_out);
crypto::public_key R_G = carrot::raw_byte_convert<crypto::public_key>(enote_ephemeral_pubkey_out);
// Compute D = rA
mx25519_pubkey s_sr;
ASSERT_TRUE(carrot::make_carrot_uncontextualized_shared_key_sender(r, A, s_sr));
crypto::public_key D = carrot::raw_byte_convert<crypto::public_key>(s_sr);
// Fixed message hash
crypto::hash prefix_hash;
memset(&prefix_hash, 0, 32);
for (int i = 0; i < 32; i++) {
prefix_hash.data[i] = i; // Sequential bytes for reproducibility
}
// Generate proof with known values
crypto::signature sig;
crypto::generate_carrot_tx_proof(prefix_hash, R_G, A, boost::none, D, r, a, sig);
// Verify original proof works
ASSERT_TRUE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A, boost::none, D, sig));
// Test mutations are rejected
// 1. Mutate R by flipping one bit
{
crypto::public_key R_mutated = R_G;
R_mutated.data[0] ^= 0x01;
ASSERT_FALSE(crypto::check_carrot_tx_proof(prefix_hash, R_mutated, A, boost::none, D, sig));
}
// 2. Mutate D by flipping one bit
{
crypto::public_key D_mutated = D;
D_mutated.data[0] ^= 0x01;
ASSERT_FALSE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A, boost::none, D_mutated, sig));
}
// 3. Mutate A by flipping one bit
{
crypto::public_key A_mutated = A;
A_mutated.data[0] ^= 0x01;
ASSERT_FALSE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A_mutated, boost::none, D, sig));
}
// 4. Mutate prefix_hash by flipping one bit
{
crypto::hash hash_mutated = prefix_hash;
hash_mutated.data[0] ^= 0x01;
ASSERT_FALSE(crypto::check_carrot_tx_proof(hash_mutated, R_G, A, boost::none, D, sig));
}
// 5. Mutate signature.c by flipping one bit
{
crypto::signature sig_mutated = sig;
sig_mutated.c.data[0] ^= 0x01;
ASSERT_FALSE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A, boost::none, D, sig_mutated));
}
// 6. Mutate signature.r by flipping one bit
{
crypto::signature sig_mutated = sig;
sig_mutated.r.data[0] ^= 0x01;
ASSERT_FALSE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A, boost::none, D, sig_mutated));
}
// 7. Mutate signature.sign_mask by flipping R_sign bit
{
crypto::signature sig_mutated = sig;
sig_mutated.sign_mask ^= 0x01;
ASSERT_FALSE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A, boost::none, D, sig_mutated));
}
// 8. Mutate signature.sign_mask by flipping D_sign bit
{
crypto::signature sig_mutated = sig;
sig_mutated.sign_mask ^= 0x02;
ASSERT_FALSE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A, boost::none, D, sig_mutated));
}
}
TEST(carrot_tx_proofs, known_values_mutation_rejection_subaddress)
{
// Real Salvium Carrot addresses for reproducible test (testnet) (subaddress)
// Sender: SC1ToqKSXRw3rE3rNQzjUA1nntvHhM6id3coWry25y4jUvHDRKDRGFv1vJRCTMHWUyVXct2aedmvzUfd3CofjTpKEhHmpnftqZk
// Recipient: SC1TsCevdYfZRZCRb83i5caRDJDb45UoqBeynNciVW8LAihKchQ4MfmW7PmPJquaXDZyntRcJCfduPVtdFUb5nsQLokFM434usw
// Tx ID: 01f5e1e56df714e3af919ab443b1acc4b1bebffed03198a9aaf3d22449809453
// Tx priv key
crypto::secret_key r, a;
const char* r_hex = "4eccc86c26ac250132d141d1b447e1fe25d0d1e4f3f2d7f3aca10a2633b52808";
ASSERT_TRUE(epee::string_tools::hex_to_pod(r_hex, r));
// view and spend pub keys
crypto::public_key A, B;
cryptonote::address_parse_info info;
std::string recipent_address_str = "SC1TsCevdYfZRZCRb83i5caRDJDb45UoqBeynNciVW8LAihKchQ4MfmW7PmPJquaXDZyntRcJCfduPVtdFUb5nsQLokFM434usw";
ASSERT_TRUE(cryptonote::get_account_address_from_str(info, cryptonote::network_type::TESTNET, recipent_address_str));
A = info.address.m_view_public_key;
B = info.address.m_spend_public_key;
a = crypto::null_skey;
// Compute R = rG (subaddress case)
mx25519_pubkey enote_ephemeral_pubkey_out;
carrot::make_carrot_enote_ephemeral_pubkey(r, B, true, enote_ephemeral_pubkey_out);
crypto::public_key R_G = carrot::raw_byte_convert<crypto::public_key>(enote_ephemeral_pubkey_out);
// Compute D = rA
mx25519_pubkey s_sr;
ASSERT_TRUE(carrot::make_carrot_uncontextualized_shared_key_sender(r, A, s_sr));
crypto::public_key D = carrot::raw_byte_convert<crypto::public_key>(s_sr);
// Fixed message hash
crypto::hash prefix_hash;
memset(&prefix_hash, 0, 32);
for (int i = 0; i < 32; i++) {
prefix_hash.data[i] = i; // Sequential bytes for reproducibility
}
// Generate proof with known values
crypto::signature sig;
crypto::generate_carrot_tx_proof(prefix_hash, R_G, A, B, D, r, a, sig);
// Verify original proof works
ASSERT_TRUE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A, B, D, sig));
// Test mutations are rejected
// 1. Mutate R by flipping one bit
{
crypto::public_key R_mutated = R_G;
R_mutated.data[0] ^= 0x01;
ASSERT_FALSE(crypto::check_carrot_tx_proof(prefix_hash, R_mutated, A, B, D, sig));
}
// 2. Mutate D by flipping one bit
{
crypto::public_key D_mutated = D;
D_mutated.data[0] ^= 0x01;
ASSERT_FALSE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A, B, D_mutated, sig));
}
// 3. Mutate A by flipping one bit
{
crypto::public_key A_mutated = A;
A_mutated.data[0] ^= 0x01;
ASSERT_FALSE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A_mutated, B, D, sig));
}
// 4. Mutate prefix_hash by flipping one bit
{
crypto::hash hash_mutated = prefix_hash;
hash_mutated.data[0] ^= 0x01;
ASSERT_FALSE(crypto::check_carrot_tx_proof(hash_mutated, R_G, A, B, D, sig));
}
// 5. Mutate signature.c by flipping one bit
{
crypto::signature sig_mutated = sig;
sig_mutated.c.data[0] ^= 0x01;
ASSERT_FALSE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A, boost::none, D, sig_mutated));
}
// 6. Mutate signature.r by flipping one bit
{
crypto::signature sig_mutated = sig;
sig_mutated.r.data[0] ^= 0x01;
ASSERT_FALSE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A, B, D, sig_mutated));
}
// 7. Mutate signature.sign_mask by flipping R_sign bit
{
crypto::signature sig_mutated = sig;
sig_mutated.sign_mask ^= 0x01;
ASSERT_FALSE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A, B, D, sig_mutated));
}
// 8. Mutate signature.sign_mask by flipping D_sign bit
{
crypto::signature sig_mutated = sig;
sig_mutated.sign_mask ^= 0x02;
ASSERT_FALSE(crypto::check_carrot_tx_proof(prefix_hash, R_G, A, B, D, sig_mutated));
}
}
+1 -1
View File
@@ -75,7 +75,7 @@ TEST(Crypto, Ostream)
EXPECT_TRUE(is_formatted<crypto::hash8>());
EXPECT_TRUE(is_formatted<crypto::hash>());
EXPECT_TRUE(is_formatted<crypto::public_key>());
EXPECT_TRUE(is_formatted<crypto::signature>());
//EXPECT_TRUE(is_formatted<crypto::signature>());
EXPECT_TRUE(is_formatted<crypto::key_derivation>());
EXPECT_TRUE(is_formatted<crypto::key_image>());
EXPECT_TRUE(is_formatted<rct::key>());
+3 -3
View File
@@ -38,9 +38,9 @@
using namespace cryptonote;
const uint64_t AMOUNT_BURNT = 1000000000000; // 1 SAL
const uint64_t STAKE_REWARD = 10000000000000; // 10 SAL
const uint64_t STAKE_PAYOUT = 216001000000000000; // 216k SAL
const uint64_t AMOUNT_BURNT = 1000000000000; // 10000 SAL
const uint64_t STAKE_REWARD = 10000000000000; // 100000 SAL
const uint64_t STAKE_PAYOUT = 216001000000000000; // 2160000k SAL
const uint64_t STAKE_LOCK_PERIOD = get_config(network_type::FAKECHAIN).STAKE_LOCK_PERIOD;
const auto AUDIT_HARD_FORKS = get_config(network_type::FAKECHAIN).AUDIT_HARD_FORKS;
+2 -2
View File
@@ -84,7 +84,7 @@ TEST(wallet_tx_builder, input_selection_basic)
tools::wallet2::transfer_container transfers;
for (size_t i = 0; i < 10; ++i)
{
tools::wallet2::transfer_details &td = tools::add_element(transfers);
tools::wallet2::transfer_details &td = transfers.emplace_back();
td = gen_transfer_details();
td.m_block_height = transfers.size(); // small ascending block heights
}
@@ -206,7 +206,7 @@ TEST(wallet_tx_builder, make_carrot_transaction_proposals_wallet2_transfer_2)
std::unordered_map<crypto::key_image, std::size_t> allowed_transfers;
for (size_t i = 0; i < FCMP_PLUS_PLUS_MAX_INPUTS + 2; ++i)
{
tools::wallet2::transfer_details &td = tools::add_element(transfers);
tools::wallet2::transfer_details &td = transfers.emplace_back();
td = gen_transfer_details();
td.m_subaddr_index.major = (i % 2 == 0) ? spending_subaddr_account : (spending_subaddr_account - 1);
td.m_subaddr_index.minor = crypto::rand_range<std::uint32_t>(0, carrot::mock::MAX_SUBADDRESS_MINOR_INDEX);
+3637 -3375
View File
File diff suppressed because it is too large Load Diff
+3833 -3572
View File
File diff suppressed because one or more lines are too long
+3832 -3571
View File
File diff suppressed because it is too large Load Diff
+3973 -3712
View File
File diff suppressed because one or more lines are too long
+3832 -3571
View File
File diff suppressed because it is too large Load Diff
+3832 -3571
View File
File diff suppressed because one or more lines are too long
+3832 -3571
View File
File diff suppressed because it is too large Load Diff
+3832 -3571
View File
File diff suppressed because it is too large Load Diff
+3832 -3571
View File
File diff suppressed because one or more lines are too long
+3832 -3571
View File
File diff suppressed because it is too large Load Diff
+3832 -3571
View File
File diff suppressed because it is too large Load Diff