Compare commits

..

72 Commits

Author SHA1 Message Date
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 1667514896 bumped RC version 2025-09-03 16:01:50 +01:00
auruya 041cd03098 Add hardfork for stake carrot integration (#55)
* Add hardfork for stake carrot integration

* Add hardfork for stake carrot integration

* fixed assertion failure

* Add hardfork for stake carrot integration

* Add hardfork for stake carrot integration

---------

Co-authored-by: Some Random Crypto Guy <somerandomcryptoguy@protonmail.com>
2025-09-03 16:00:34 +01:00
somerandomcryptoguy 0c4998b091 added --generate-from-svb-key functionality (#53)
Co-authored-by: Some Random Crypto Guy <somerandomcryptoguy@protonmail.com>
2025-09-03 12:33:09 +01:00
auruya 7f25459169 add dest_asset_type check (#52) 2025-09-01 14:18:11 +01:00
Some Random Crypto Guy 91814ebfd9 updated testnet seed nodes; bumped RC version 2025-08-21 13:31:03 +01:00
Some Random Crypto Guy 7b3e8007c8 bumped TESTNET DB version to prevent conflicts 2025-08-20 15:50:45 +01:00
Some Random Crypto Guy 141761957d bumped version to RC1 2025-08-20 15:48:05 +01:00
akildemir b2ab2f606c Security fixes (#48)
* unify tx versions; add missing protocol tx checks

* fixed errors with protocol_tx handling pre-Carrot

* fixed error caused by setting coinbase_tx version to 4

* fix eph pubkey check for protocol tx verification

* Update tx_pool.cpp

---------

Co-authored-by: Some Random Crypto Guy <somerandomcryptoguy@protonmail.com>
Co-authored-by: somerandomcryptoguy <139346562+somerandomcryptoguy@users.noreply.github.com>
2025-08-20 15:26:36 +01:00
Some Random Crypto Guy 40f0420e02 Merge branch 'carrot-integration' into develop 2025-08-19 12:07:40 +01:00
Some Random Crypto Guy 44e54ad555 fixed pre-Carrot sweep_all command; bumped RC version 2025-08-18 15:51:45 +01:00
Some Random Crypto Guy 69407ed8a5 bumped RC version 2025-08-18 12:08:24 +01:00
akildemir 13ee838f3c support for sweep_all (#47) 2025-08-18 12:06:56 +01:00
Some Random Crypto Guy ec33664734 fixed support for multiple STAKE TXs in same block, post-Carrot; bumped RC version 2025-08-14 17:42:47 +01:00
Some Random Crypto Guy bfe8c4606d fixed AUDIT payout scanning for split AUDIT TXs; bumped RC version 2025-08-14 12:07:53 +01:00
akildemir a159bed3ba remove tools::add_element 2025-08-08 14:54:55 +03:00
jeffro256 52f2065db4 carrot_core: update unclamped mx25519 for arm64 2025-08-08 14:28:54 +03:00
akildemir 0fd570a2c4 Merge pull request #46 from salvium/wallet-api-address-book-fix
fixed wallet API address book functionality to support Carrot addresses
2025-08-08 14:07:56 +03:00
Some Random Crypto Guy 7df7f2740d fixed wallet API address book functionality to support Carrot addresses 2025-08-08 11:14:55 +01:00
Some Random Crypto Guy 458af34c67 bumped version number to RC5 2025-08-06 19:31:59 +01:00
akildemir 21a3f5ca6f Merge pull request #44 from salvium/address-book-fixes 2025-08-06 20:52:36 +03:00
akildemir bc7a797b71 Merge pull request #43 from salvium/wallet-balance-stake-fix 2025-08-06 20:51:41 +03:00
Some Random Crypto Guy 9e888834e7 updated address book functionality to support Carrot addresses 2025-08-06 15:32:54 +01:00
Some Random Crypto Guy 9709fad4d6 fixed stake unlock time 2025-08-06 15:04:19 +01:00
akildemir 906e55f87b add return into type to wallet serialization (#42) 2025-08-06 15:00:56 +01:00
akildemir 92812621be Merge pull request #41 from salvium/background-sync-fix
background sync fix for Carrot accounts/keys
2025-08-06 13:35:38 +03:00
Some Random Crypto Guy 82b14a776f fixed background syncing issue with Carrot account keys 2025-08-06 11:27:23 +01:00
Some Random Crypto Guy 54a33225a2 fixed wallet balance during STAKE/AUDIT TX lifetime 2025-08-05 14:49:34 +01:00
akildemir d7584de579 Merge pull request #40 from salvium/fix-stake-tx-amount
merge STAKE/BURN TX fees fix
2025-08-05 16:28:28 +03:00
Some Random Crypto Guy a4c0d85bbb fixed stake/burn TX amount - stopped fees being deducted from amount_burnt 2025-08-05 14:07:26 +01:00
akildemir 609615779c Merge pull request #38 from salvium/tx-parallel-scanning-fix
Fixed wallet scanning parallelisation, speed up wallet scans from chain
2025-08-04 16:08:25 +03:00
Some Random Crypto Guy b9364b5749 fixed parallel scanning of blocks for wallet - max 10 blocks at a time, because of return_payment limitations 2025-08-04 14:02:05 +01:00
akildemir 7cd7bef3ca Merge pull request #37 from salvium/tx-size-calc-fix
Carrot TX size calculation fixes
2025-08-04 13:35:48 +03:00
Some Random Crypto Guy 80bd2d1ff0 fixed calculation for Carrot TX sizes - added Salvium-specific data 2025-08-04 11:01:33 +01:00
akildemir cbe84499fb use internal notes instead of special ones for change and return enotes (#36) 2025-08-01 13:54:59 +01:00
akildemir a8ee0a90e0 Merge pull request #34 from salvium/miner-fixes
fixed miner resumption issue
2025-08-01 14:24:14 +03:00
akildemir c9de861216 Merge pull request #35 from salvium/wallet-creation-address-fix
fixed incorrect wallet address displayed for Carrot when creating a new wallet
2025-08-01 14:22:22 +03:00
Some Random Crypto Guy 0239202f09 fixed incorrect wallet address displayed for Carrot when creating a new wallet 2025-07-31 11:48:43 +01:00
Some Random Crypto Guy 2dd6208a80 added mechanism to forcibly stop miner when crossing Carrot HF boundary, and allow mining to be restarted 2025-07-31 10:39:34 +01:00
akildemir 37594fe8fa Implement return tx functionality for post-carrot (#33)
* return tx fixes

* save change output for return tx

* successfully receive returned tx

* complete post-carrot return txs
2025-07-30 15:33:35 +01:00
akildemir e5c9b05ed6 Merge pull request #31 from salvium/ringct-fix
fixed RCT types to support ECDHINFO for Salvium One; fixed input and output limits for TXs
2025-07-24 14:44:06 +03:00
akildemir 9ba621b3ae post carrot stake tx support (#32)
* add support for stake txs and payouts post-carrot

* fixes on the stake return payments

* complete post-carrot stake txs

* rename protocol_tx_data to protocol_tx_data_t
2025-07-23 14:58:11 +01:00
Some Random Crypto Guy 9e20133ed9 fixed input and output limits for TXs 2025-07-23 10:35:14 +01:00
Some Random Crypto Guy 9c11200666 fixed RCT types to support ECDHINFO for Salvium One; fixed input and output limits for TXs 2025-07-23 10:21:13 +01:00
akildemir 3e0649a8d2 Merge pull request #29 from salvium/mining-migration-from-CN-to-Carrot
fixed migration of mining services from CN to Carrot
2025-07-23 12:07:20 +03:00
akildemir b87bfe002a Merge pull request #27 from salvium/carrot-addressing
fixed Carrot subaddress displaying in CLI wallet
2025-07-23 11:57:36 +03:00
Some Random Crypto Guy f2e69594a7 fixed migration of mining from CN to Carrot, including stopping miners from using wrong address pre- and post-Carrot HF 2025-07-22 11:51:21 +01:00
Some Random Crypto Guy 7b39504050 fixed carrot syncing of mainnet 2025-07-21 19:33:17 +01:00
Some Random Crypto Guy eff99e9287 fixed Carrot subaddress display in CLI wallet 2025-07-21 19:30:47 +01:00
somerandomcryptoguy 6f8fcdab03 fixed make depends for all major platforms (#26)
* fixed 'make depends' building

* fixed capture vars for MacOS

* fixed Linux build for make depends

---------

Co-authored-by: Some Random Crypto Guy <somerandomcryptoguy@protonmail.com>
2025-07-19 12:25:45 +01:00
87 changed files with 2682 additions and 676 deletions
+35
View File
@@ -0,0 +1,35 @@
# Use Ubuntu 24.04 as base
FROM ubuntu:24.04 AS builder
ENV DEBIAN_FRONTEND=noninteractive
# Install build dependencies
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 \
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 \
&& 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 Zero v0.9.9
# Salvium One v1.0.0
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 v0.9.9
git checkout v1.0.0
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 v0.9.9
git checkout v1.0.0
```
* 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. 'v0.9.9'. 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.0'. If you don't care about the version and just want binaries from master, skip this step:
```bash
git checkout v0.9.9
git checkout v1.0.0
```
* If you are on a 64-bit system, run:
+1 -1
View File
@@ -19,7 +19,7 @@ mingw32_native_packages :=
mingw32_packages = icu4c libiconv
linux_native_packages :=
linux_packages :=
linux_packages := eudev
freebsd_native_packages := freebsd_base
freebsd_packages :=
+1 -1
View File
@@ -238,7 +238,7 @@ void BlockchainDB::add_transaction(const crypto::hash& blk_hash, const std::pair
LOG_PRINT_L1("Failed to get output unlock time, aborting transaction addition");
throw std::runtime_error("Unexpected error getting output unlock_time, aborting");
}
if (miner_tx && tx.version == 2)
if (miner_tx && tx.version >= 2)
{
cryptonote::tx_out vout = tx.vout[i];
// TODO: avoid multiple expensive zeroCommitVartime call here + get_outs_by_last_locked_block + ver_non_input_consensus
+13
View File
@@ -207,6 +207,17 @@ typedef struct yield_tx_info {
crypto::public_key return_pubkey;
} yield_tx_info;
typedef struct yield_tx_info_carrot {
uint64_t block_height;
uint8_t version;
crypto::hash tx_hash;
uint64_t locked_coins;
crypto::public_key return_address;
crypto::public_key return_pubkey;
carrot::view_tag_t return_view_tag;
carrot::encrypted_janus_anchor_t return_anchor_enc;
} yield_tx_info_carrot;
#define DBF_SAFE 1
#define DBF_FAST 2
#define DBF_FASTEST 4
@@ -1924,6 +1935,8 @@ public:
virtual int get_yield_block_info(const uint64_t height, yield_block_info& ybi) const = 0;
virtual int get_yield_tx_info(const uint64_t height, std::vector<yield_tx_info>& yti_container) const = 0;
virtual int get_carrot_yield_tx_info(const uint64_t height, std::vector<yield_tx_info_carrot>& yti_container) const = 0;
/**
* @brief set whether or not to automatically remove logs
+113 -8
View File
@@ -298,6 +298,7 @@ const char* const LMDB_YIELD_TXS = "yield_txs";
const char* const LMDB_YIELD_BLOCKS = "yield_blocks";
const char* const LMDB_AUDIT_TXS = "audit_txs";
const char* const LMDB_AUDIT_BLOCKS = "audit_blocks";
const char* const LMDB_CARROT_YIELD_TXS = "carrot_yield_txs";
const char zerokey[8] = {0};
const MDB_val zerokval = { sizeof(zerokey), (void *)zerokey };
@@ -947,6 +948,43 @@ int BlockchainLMDB::get_yield_tx_info(const uint64_t height, std::vector<yield_t
return 0;
}
int BlockchainLMDB::get_carrot_yield_tx_info(const uint64_t height, std::vector<yield_tx_info_carrot>& yti_container) const {
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
// Clear the container
yti_container.clear();
// Query for the (presumably matured) YIELD_TX_INFO information
TXN_PREFIX_RDONLY();
RCURSOR(carrot_yield_txs);
MDB_val v;
MDB_val_set(k, height);
MDB_cursor_op op = MDB_SET;
while (1)
{
int ret = mdb_cursor_get(m_cur_carrot_yield_txs, &k, &v, op);
op = MDB_NEXT_DUP;
if (ret == MDB_NOTFOUND)
break;
if (ret)
throw0(DB_ERROR(lmdb_error("Failed to enumerate yield TX info: ", ret).c_str()));
// Get the data
yield_tx_info_carrot *p = (yield_tx_info_carrot*)v.mv_data;
// Push result back into the container
yti_container.emplace_back(*p);
// Update the height retrospectively (because the DB stores the count of elements there to handle duplicates, because it's rubbish)
yti_container.back().block_height = height;
}
TXN_POSTFIX_RDONLY();
// Return success to caller
return 0;
}
void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t long_term_block_weight, const difficulty_type& cumulative_difficulty, const uint64_t& coins_generated, uint64_t num_rct_outs, oracle::asset_type_counts& cum_rct_by_asset_type, const crypto::hash& blk_hash, uint64_t slippage_total, uint64_t yield_total, uint64_t audit_total, const cryptonote::network_type nettype, cryptonote::yield_block_info& ybi, cryptonote::audit_block_info& abi)
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
@@ -1215,6 +1253,7 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
CURSOR(circ_supply_tally)
CURSOR(yield_txs)
CURSOR(audit_txs)
CURSOR(carrot_yield_txs)
MDB_val_set(val_tx_id, tx_id);
MDB_val_set(val_h, tx_hash);
@@ -1321,7 +1360,12 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
write_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, final_burn_tally);
}
if (tx.type == cryptonote::transaction_type::BURN || tx.type == cryptonote::transaction_type::CONVERT || tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::AUDIT || tx.type == cryptonote::transaction_type::TRANSFER) {
if (tx.type == cryptonote::transaction_type::BURN ||
tx.type == cryptonote::transaction_type::CONVERT ||
tx.type == cryptonote::transaction_type::STAKE ||
tx.type == cryptonote::transaction_type::AUDIT ||
tx.type == cryptonote::transaction_type::TRANSFER)
{
// Get the current tally value for the source currency type
MDB_val_copy<uint64_t> source_idx(cryptonote::asset_id_from_type(tx.source_asset_type));
@@ -1390,10 +1434,44 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
write_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, final_burn_tally);
}
}
// Is there yield_tx data to add?
if (tx.type == cryptonote::transaction_type::STAKE) {
// Is there yield_tx data to add?
if (tx.version >= TRANSACTION_VERSION_CARROT && tx.type == cryptonote::transaction_type::STAKE)
{
// Create the object we are going to write to the database
yield_tx_info_carrot yield_data;
yield_data.block_height = m_height;
yield_data.tx_hash = tx_hash;
yield_data.return_pubkey = tx.protocol_tx_data.return_pubkey;
yield_data.return_address = tx.protocol_tx_data.return_address;
yield_data.return_view_tag = tx.protocol_tx_data.return_view_tag;
yield_data.return_anchor_enc = tx.protocol_tx_data.return_anchor_enc;
yield_data.locked_coins = tx.amount_burnt;
// Because LMDB is shockingly bad at handling duplicates, we have resorted to using a counter of elements
// in the first element of the struct.
MDB_val data;
MDB_val_set(val_height, m_height);
result = mdb_cursor_get(m_cur_carrot_yield_txs, &val_height, &data, MDB_SET);
if (!result)
{
mdb_size_t num_elems = 0;
result = mdb_cursor_count(m_cur_carrot_yield_txs, &num_elems);
if (result)
throw0(DB_ERROR(std::string("Failed to get number of yield TXs for height: ").append(mdb_strerror(result)).c_str()));
yield_data.block_height = num_elems;
}
else if (result != MDB_NOTFOUND)
throw0(DB_ERROR(lmdb_error("Failed to get output amount in db transaction: ", result).c_str()));
else
yield_data.block_height = 0;
// Now we know how many there are, write out the data to the DB
MDB_val_set(val_yield_tx_data, yield_data);
result = mdb_cursor_put(m_cur_carrot_yield_txs, &val_height, &val_yield_tx_data, MDB_APPENDDUP);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to add tx carrot yield data to db transaction: ", result).c_str()));
} else if (tx.type == cryptonote::transaction_type::STAKE) {
// Create the object we are going to write to the database
yield_tx_info yield_data;
yield_data.block_height = m_height;
@@ -1444,7 +1522,7 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
if (result)
throw0(DB_ERROR( lmdb_error("Failed to add tx yield data to db transaction: ", result).c_str() ));
}
// Is there audit_tx data to add?
if (tx.type == cryptonote::transaction_type::AUDIT) {
@@ -1498,7 +1576,7 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
if (result)
throw0(DB_ERROR( lmdb_error("Failed to add tx audit data to db transaction: ", result).c_str() ));
}
return tx_id;
}
@@ -1522,6 +1600,7 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
CURSOR(circ_supply_tally)
CURSOR(yield_txs)
CURSOR(audit_txs)
CURSOR(carrot_yield_txs)
MDB_val_set(val_h, tx_hash);
@@ -1720,7 +1799,28 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
}
// Is there yield_tx data to remove?
if (tx.type == cryptonote::transaction_type::STAKE) {
if (tx.version >= TRANSACTION_VERSION_CARROT && tx.type == cryptonote::transaction_type::STAKE) {
// Remove any yield_tx data for this transaction
MDB_val_set(val_height, m_height);
MDB_val v;
MDB_cursor_op op = MDB_SET;
while (1) {
result = mdb_cursor_get(m_cur_carrot_yield_txs, &val_height, &v, op);
if (result == MDB_NOTFOUND) {
throw1(DB_ERROR("Failed to locate carrot yield tx for removal from db transaction"));
} else if (result) {
throw1(DB_ERROR(lmdb_error("Failed to locate carrot yield_tx data for removal: ", result).c_str()));
}
op = MDB_NEXT_DUP;
const yield_tx_info_carrot yti = *(const yield_tx_info_carrot*)v.mv_data;
if (yti.tx_hash == tx_hash) {
result = mdb_cursor_del(m_cur_carrot_yield_txs, 0);
if (result)
throw1(DB_ERROR(lmdb_error("Failed to add removal of carrot yield_tx data to db transaction: ", result).c_str()));
break;
}
}
} else if (tx.type == cryptonote::transaction_type::STAKE) {
// Remove any yield_tx data for this transaction
MDB_val_set(val_height, m_height);
MDB_val v;
@@ -2284,10 +2384,12 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
lmdb_db_open(txn, LMDB_YIELD_TXS, MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED | MDB_CREATE, m_yield_txs, "Failed to open db handle for m_yield_txs");
lmdb_db_open(txn, LMDB_YIELD_BLOCKS, MDB_INTEGERKEY | MDB_CREATE, m_yield_blocks, "Failed to open db handle for m_yield_blocks");
lmdb_db_open(txn, LMDB_AUDIT_TXS, MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED | MDB_CREATE, m_audit_txs, "Failed to open db handle for m_audit_txs");
lmdb_db_open(txn, LMDB_AUDIT_BLOCKS, MDB_INTEGERKEY | MDB_CREATE, m_audit_blocks, "Failed to open db handle for m_audit_blocks");
lmdb_db_open(txn, LMDB_CARROT_YIELD_TXS, MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED | MDB_CREATE, m_carrot_yield_txs, "Failed to open db handle for m_carrot_yield_txs");
mdb_set_dupsort(txn, m_spent_keys, compare_hash32);
mdb_set_dupsort(txn, m_block_heights, compare_hash32);
mdb_set_dupsort(txn, m_tx_indices, compare_hash32);
@@ -2312,6 +2414,7 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
mdb_set_dupsort(txn, m_yield_txs, compare_uint64);
mdb_set_dupsort(txn, m_audit_txs, compare_uint64);
mdb_set_dupsort(txn, m_carrot_yield_txs, compare_uint64);
if (!(mdb_flags & MDB_RDONLY))
{
@@ -2497,6 +2600,8 @@ void BlockchainLMDB::reset()
throw0(DB_ERROR(lmdb_error("Failed to drop m_audit_txs: ", result).c_str()));
if (auto result = mdb_drop(txn, m_audit_blocks, 0))
throw0(DB_ERROR(lmdb_error("Failed to drop m_audit_blocks: ", result).c_str()));
if (auto result = mdb_drop(txn, m_carrot_yield_txs, 0))
throw0(DB_ERROR(lmdb_error("Failed to drop m_carrot_yield_txs: ", result).c_str()));
// init with current version
MDB_val_str(k, "version");
+7 -1
View File
@@ -80,6 +80,7 @@ typedef struct mdb_txn_cursors
MDB_cursor *m_txc_yield_blocks;
MDB_cursor *m_txc_audit_txs;
MDB_cursor *m_txc_audit_blocks;
MDB_cursor *m_txc_carrot_yield_txs;
} mdb_txn_cursors;
@@ -108,6 +109,7 @@ typedef struct mdb_txn_cursors
#define m_cur_yield_blocks m_cursors->m_txc_yield_blocks
#define m_cur_audit_txs m_cursors->m_txc_audit_txs
#define m_cur_audit_blocks m_cursors->m_txc_audit_blocks
#define m_cur_carrot_yield_txs m_cursors->m_txc_carrot_yield_txs
typedef struct mdb_rflags
{
@@ -137,6 +139,7 @@ typedef struct mdb_rflags
bool m_rf_yield_blocks;
bool m_rf_audit_txs;
bool m_rf_audit_blocks;
bool m_rf_carrot_yield_txs;
} mdb_rflags;
typedef struct mdb_threadinfo
@@ -474,6 +477,7 @@ private:
virtual int get_yield_block_info(const uint64_t height, yield_block_info& ybi) const;
virtual int get_yield_tx_info(const uint64_t height, std::vector<yield_tx_info>& yti_container) const;
virtual int get_carrot_yield_tx_info(const uint64_t height, std::vector<yield_tx_info_carrot>& yti_container) const;
private:
MDB_env* m_env;
@@ -511,10 +515,12 @@ private:
MDB_dbi m_yield_txs;
MDB_dbi m_yield_blocks;
MDB_dbi m_audit_txs;
MDB_dbi m_audit_blocks;
MDB_dbi m_carrot_yield_txs;
mutable uint64_t m_cum_size; // used in batch size estimation
mutable unsigned int m_cum_count;
std::string m_folder;
+1
View File
@@ -145,6 +145,7 @@ public:
virtual int get_yield_block_info(const uint64_t height, yield_block_info& ybi) const override { return 0; }
virtual int get_yield_tx_info(const uint64_t height, std::vector<yield_tx_info>& yti_container) const override { return 0; }
virtual int get_carrot_yield_tx_info(const uint64_t height, std::vector<yield_tx_info_carrot>& yti_container) const override { return 0; }
virtual cryptonote::blobdata get_txpool_tx_blob(const crypto::hash& txid, relay_category tx_category) const override { return ""; }
virtual bool for_all_txpool_txes(std::function<bool(const crypto::hash&, const cryptonote::txpool_tx_meta_t&, const cryptonote::blobdata_ref*)>, bool include_blob = false, relay_category category = relay_category::broadcasted) const override { return false; }
Binary file not shown.
+1
View File
@@ -29,6 +29,7 @@
set(carrot_core_sources
account_secrets.cpp
address_utils.cpp
account.cpp
carrot_enote_types.cpp
core_types.cpp
destination.cpp
@@ -110,7 +110,7 @@ CarrotDestinationV1 carrot_and_legacy_account::subaddress(const subaddress_index
return addr;
}
//----------------------------------------------------------------------------------------------------------------------
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> carrot_and_legacy_account::get_subaddress_map_cn() const
const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> carrot_and_legacy_account::get_subaddress_map_cn() const
{
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> res;
for (const auto &p : subaddress_map)
@@ -120,10 +120,15 @@ std::unordered_map<crypto::public_key, cryptonote::subaddress_index> carrot_and_
return res;
}
//----------------------------------------------------------------------------------------------------------------------
std::unordered_map<crypto::public_key, subaddress_index_extended>& carrot_and_legacy_account::get_subaddress_map_ref() {
const std::unordered_map<crypto::public_key, subaddress_index_extended>& carrot_and_legacy_account::get_subaddress_map_ref() const {
return subaddress_map;
}
//----------------------------------------------------------------------------------------------------------------------
const std::unordered_map<crypto::public_key, return_output_info_t>&
carrot_and_legacy_account::get_return_output_map_ref() const {
return return_output_map;
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::opening_for_subaddress(const subaddress_index_extended &subaddress_index,
crypto::secret_key &address_privkey_g_out,
crypto::secret_key &address_privkey_t_out,
@@ -310,6 +315,55 @@ crypto::secret_key carrot_and_legacy_account::generate(
return retval;
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::set_keys(const cryptonote::account_keys& keys, bool copy_spend_secret_keys)
{
// CN keys
m_keys.m_account_address = keys.m_account_address;
if (copy_spend_secret_keys) m_keys.m_spend_secret_key = keys.m_spend_secret_key;
m_keys.m_view_secret_key = keys.m_view_secret_key;
if (copy_spend_secret_keys) m_keys.m_multisig_keys = keys.m_multisig_keys;
m_keys.m_device = keys.m_device;
m_keys.m_encryption_iv = keys.m_encryption_iv;
// Carrot keys
if (copy_spend_secret_keys) m_keys.s_master = keys.s_master;
if (copy_spend_secret_keys) m_keys.k_prove_spend = keys.k_prove_spend;
m_keys.s_view_balance = keys.s_view_balance;
m_keys.k_view_incoming = keys.k_view_incoming;
m_keys.k_generate_image = keys.k_generate_image;
m_keys.s_generate_address = keys.s_generate_address;
m_keys.m_carrot_account_address = keys.m_carrot_account_address;
m_keys.m_carrot_main_address = keys.m_carrot_main_address;
}
//----------------------------------------------------------------------------------------------------------------------
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);
m_keys.s_view_balance = svb_key;
// view balance keys
make_carrot_viewincoming_key(m_keys.s_view_balance, m_keys.k_view_incoming);
make_carrot_generateimage_key(m_keys.s_view_balance, m_keys.k_generate_image);
make_carrot_generateaddress_secret(m_keys.s_view_balance, m_keys.s_generate_address);
// carrot account address - use the provided address spend pubkey
m_keys.m_carrot_account_address = address;
k_view_incoming_dev.view_key_scalar_mult_ed25519(m_keys.m_carrot_account_address.m_spend_public_key,
m_keys.m_carrot_account_address.m_view_public_key
);
// carrot main wallet address
m_keys.m_carrot_main_address = address;
k_view_incoming_dev.view_key_scalar_mult_ed25519(crypto::get_G(),
m_keys.m_carrot_main_address.m_view_public_key
);
this->default_derive_type = AddressDeriveType::Carrot;
generate_subaddress_map();
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::set_carrot_keys(const AddressDeriveType default_derive_type)
{
// top level keys
@@ -328,6 +382,7 @@ void carrot_and_legacy_account::set_carrot_keys(const AddressDeriveType default_
m_keys.m_carrot_account_address.m_spend_public_key,
m_keys.m_carrot_account_address.m_view_public_key
);
m_keys.m_carrot_account_address.m_is_carrot = true;
// carrot main wallet address
m_keys.m_carrot_main_address.m_spend_public_key = m_keys.m_carrot_account_address.m_spend_public_key;
@@ -335,6 +390,7 @@ void carrot_and_legacy_account::set_carrot_keys(const AddressDeriveType default_
crypto::get_G(),
m_keys.m_carrot_main_address.m_view_public_key
);
m_keys.m_carrot_main_address.m_is_carrot = true;
this->default_derive_type = default_derive_type;
generate_subaddress_map();
@@ -345,7 +401,12 @@ void carrot_and_legacy_account::insert_subaddresses(const std::unordered_map<cry
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}});
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::insert_return_output_info(const std::unordered_map<crypto::public_key, return_output_info_t>& roi_map)
{
for (const auto &p : roi_map)
return_output_map.insert({p.first, p.second});
}
//----------------------------------------------------------------------------------------------------------------------
AddressDeriveType carrot_and_legacy_account::resolve_derive_type(const AddressDeriveType derive_type) const
{
@@ -28,11 +28,11 @@
#pragma once
#include "carrot_core/account_secrets.h"
#include "carrot_core/address_utils.h"
#include "carrot_core/destination.h"
#include "carrot_core/device_ram_borrowed.h"
#include "carrot_core/enote_utils.h"
#include "account_secrets.h"
#include "address_utils.h"
#include "destination.h"
#include "device_ram_borrowed.h"
#include "enote_utils.h"
#include "carrot_impl/subaddress_index.h"
#include "cryptonote_basic/account.h"
#include "cryptonote_basic/subaddress_index.h"
@@ -43,6 +43,50 @@ static constexpr std::uint32_t MAX_SUBADDRESS_MINOR_INDEX = 20;
namespace carrot
{
struct return_output_info_t {
input_context_t input_context;
crypto::public_key K_o; // output onetime address
crypto::public_key K_change; // change output onetime address
crypto::key_image key_image;
crypto::secret_key x;
crypto::secret_key y;
return_output_info_t() {
// Default constructor for serialization
input_context = input_context_t();
K_o = crypto::public_key();
K_change = crypto::public_key();
key_image = crypto::key_image();
x = crypto::secret_key();
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)
FIELD(K_change)
FIELD(key_image)
FIELD(x)
FIELD(y)
END_SERIALIZE()
};
class carrot_and_legacy_account : public cryptonote::account_base
{
public:
@@ -57,6 +101,8 @@ namespace carrot
s_generate_address_dev(get_keys().s_generate_address)
{}
void set_keys(const cryptonote::account_keys& keys, bool copy_spend_secret_keys);
carrot_and_legacy_account(const carrot_and_legacy_account &k) = delete;
carrot_and_legacy_account(carrot_and_legacy_account&&) = delete;
@@ -68,9 +114,10 @@ namespace carrot
CarrotDestinationV1 subaddress(const subaddress_index_extended &subaddress_index) const;
std::unordered_map<crypto::public_key, cryptonote::subaddress_index> get_subaddress_map_cn() const;
std::unordered_map<crypto::public_key, subaddress_index_extended>& get_subaddress_map_ref();
const std::unordered_map<crypto::public_key, cryptonote::subaddress_index> get_subaddress_map_cn() const;
const std::unordered_map<crypto::public_key, subaddress_index_extended>& get_subaddress_map_ref() const;
const std::unordered_map<crypto::public_key, return_output_info_t>& get_return_output_map_ref() const;
// brief: opening_for_subaddress - return (k^g_a, k^t_a) for j s.t. K^j_s = (k^g_a * G + k^t_a * T)
void opening_for_subaddress(const subaddress_index_extended &subaddress_index,
crypto::secret_key &address_privkey_g_out,
@@ -106,12 +153,50 @@ namespace carrot
const AddressDeriveType default_derive_type = AddressDeriveType::Carrot
);
void create_from_svb_key(const cryptonote::account_public_address& address, const crypto::secret_key& svb_key);
void set_carrot_keys(const AddressDeriveType default_derive_type = AddressDeriveType::Carrot);
void insert_subaddresses(const std::unordered_map<crypto::public_key, subaddress_index_extended>& subaddress_map);
void insert_return_output_info(
const std::unordered_map<crypto::public_key, return_output_info_t>& input_context_map
);
AddressDeriveType resolve_derive_type(const AddressDeriveType derive_type) const;
private:
std::unordered_map<crypto::public_key, subaddress_index_extended> subaddress_map;
// Kr -> return_output_info
std::unordered_map<crypto::public_key, return_output_info_t> return_output_map;
};
}
namespace boost
{
namespace serialization
{
template <class Archive>
inline typename std::enable_if<!Archive::is_loading::value, void>::type initialize_transfer_details(Archive &a, carrot::return_output_info_t &x, const boost::serialization::version_type ver)
{
}
template <class Archive>
inline typename std::enable_if<Archive::is_loading::value, void>::type initialize_transfer_details(Archive &a, carrot::return_output_info_t &x, const boost::serialization::version_type ver)
{
x.input_context = carrot::input_context_t();
x.K_o = crypto::public_key();
x.K_change = crypto::public_key();
x.key_image = crypto::key_image();
x.x = crypto::secret_key();
x.y = crypto::secret_key();
}
template <class Archive>
inline void serialize(Archive &a, carrot::return_output_info_t &x, const boost::serialization::version_type ver)
{
a & x.input_context;
a & x.K_o;
a & x.K_change;
a & x.key_image;
a & x.x;
a & x.y;
}
}
}
+2
View File
@@ -75,6 +75,8 @@ struct CarrotEnoteV1 final
mx25519_pubkey enote_ephemeral_pubkey;
/// L_0
crypto::key_image tx_first_key_image;
// transaction output keys
std::vector<crypto::public_key> tx_output_keys;
};
/// equality operators
+1 -1
View File
@@ -70,7 +70,7 @@ static constexpr const unsigned char CARROT_DOMAIN_SEP_SUBADDRESS_SCALAR[] = "Ca
static constexpr const unsigned int CARROT_MIN_TX_OUTPUTS = 2;
static constexpr const unsigned int CARROT_MAX_TX_OUTPUTS = 8;
static constexpr const unsigned int CARROT_MIN_TX_INPUTS = 1;
static constexpr const unsigned int CARROT_MAX_TX_INPUTS = 128;
static constexpr const unsigned int CARROT_MAX_TX_INPUTS = 64;
// SPARC addressing protocol domain separators
static constexpr const unsigned char SPARC_DOMAIN_SEP_RETURN_PUBKEY_ENCRYPTION_MASK[] = "SPARC return pubkey encryption mask";
+4
View File
@@ -134,6 +134,10 @@ struct view_incoming_key_device
const crypto::public_key &onetime_address,
janus_anchor_t &anchor_special_out) const = 0;
virtual void make_internal_return_privkey(const input_context_t &input_context,
const crypto::public_key &onetime_address,
crypto::secret_key &return_privkey_out) const = 0;
virtual ~view_incoming_key_device() = default;
};
+7
View File
@@ -55,6 +55,13 @@ bool view_incoming_key_ram_borrowed_device::view_key_scalar_mult_x25519(const mx
return make_carrot_uncontextualized_shared_key_receiver(m_k_view_incoming, D, kvD);
}
//-------------------------------------------------------------------------------------------------------------------
void view_incoming_key_ram_borrowed_device::make_internal_return_privkey(const input_context_t &input_context,
const crypto::public_key &onetime_address,
crypto::secret_key &return_privkey_out) const
{
make_sparc_return_privkey(to_bytes(m_k_view_incoming), input_context, onetime_address, return_privkey_out);
}
//-------------------------------------------------------------------------------------------------------------------
void view_incoming_key_ram_borrowed_device::make_janus_anchor_special(
const mx25519_pubkey &enote_ephemeral_pubkey,
const input_context_t &input_context,
+4
View File
@@ -60,6 +60,10 @@ public:
const crypto::public_key &onetime_address,
janus_anchor_t &anchor_special_out) const override;
void make_internal_return_privkey(const input_context_t &input_context,
const crypto::public_key &onetime_address,
crypto::secret_key &return_privkey_out) const override;
protected:
const crypto::secret_key &m_k_view_incoming;
};
+16 -9
View File
@@ -159,6 +159,7 @@ void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &norm
const view_incoming_key_device *k_view_dev,
const crypto::key_image &tx_first_key_image,
std::vector<RCTOutputEnoteProposal> &output_enote_proposals_out,
RCTOutputEnoteProposal &return_enote_out,
encrypted_payment_id_t &encrypted_payment_id_out,
cryptonote::transaction_type tx_type,
size_t &change_index_out,
@@ -173,7 +174,11 @@ void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &norm
// assert payment proposals numbers
const size_t num_selfsend_proposals = selfsend_payment_proposals.size();
const size_t num_proposals = normal_payment_proposals.size() + num_selfsend_proposals;
CARROT_CHECK_AND_THROW(num_proposals >= CARROT_MIN_TX_OUTPUTS, too_few_outputs, "too few payment proposals");
if (tx_type == cryptonote::transaction_type::STAKE || tx_type == cryptonote::transaction_type::BURN) {
CARROT_CHECK_AND_THROW(num_proposals == 1, too_few_outputs, "tx doesn't have correct number of proposals");
} else {
CARROT_CHECK_AND_THROW(num_proposals >= CARROT_MIN_TX_OUTPUTS, too_few_outputs, "too few payment proposals");
}
CARROT_CHECK_AND_THROW(num_proposals <= CARROT_MAX_TX_OUTPUTS, too_many_outputs, "too many payment proposals");
CARROT_CHECK_AND_THROW(num_selfsend_proposals, too_few_outputs, "no selfsend payment proposal");
@@ -211,7 +216,7 @@ void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &norm
// construct normal enotes
for (size_t i = 0; i < normal_payment_proposals.size(); ++i)
{
auto &output_entry = tools::add_element(sortable_data);
auto &output_entry = sortable_data.emplace_back();
output_entry.second = {false, i};
encrypted_payment_id_t encrypted_payment_id;
@@ -265,7 +270,7 @@ void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &norm
{
const CarrotPaymentProposalSelfSendV1 &selfsend_payment_proposal = selfsend_payment_proposals.at(i);
auto &output_entry = tools::add_element(sortable_data);
auto &output_entry = sortable_data.emplace_back();
output_entry.second = {true, i};
if (s_view_balance_dev != nullptr)
@@ -274,6 +279,8 @@ void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &norm
*s_view_balance_dev,
tx_first_key_image,
other_enote_ephemeral_pubkey,
tx_type,
return_enote_out,
output_entry.first);
}
else if (k_view_dev != nullptr)
@@ -354,11 +361,11 @@ void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &norm
component_out_of_order, "this set contains duplicate onetime addresses");
// assert all K_o lie in prime order subgroup
for (const RCTOutputEnoteProposal &output_enote_proposal : output_enote_proposals_out)
{
CARROT_CHECK_AND_THROW(rct::isInMainSubgroup(rct::pk2rct(output_enote_proposal.enote.onetime_address)),
invalid_point, "this set contains an invalid onetime address");
}
// for (const RCTOutputEnoteProposal &output_enote_proposal : output_enote_proposals_out)
// {
// CARROT_CHECK_AND_THROW(rct::isInMainSubgroup(rct::pk2rct(output_enote_proposal.enote.onetime_address)),
// invalid_point, "this set contains an invalid onetime address");
// }
// assert unique and non-trivial k_a
memcmp_set<crypto::secret_key> amount_blinding_factors;
@@ -409,7 +416,7 @@ void get_coinbase_output_enotes(const std::vector<CarrotPaymentProposalV1> &norm
{
get_coinbase_output_proposal_v1(normal_payment_proposals[i],
block_index,
tools::add_element(output_coinbase_enotes_out));
output_coinbase_enotes_out.emplace_back());
}
// assert uniqueness and non-trivial-ness of D_e
@@ -109,6 +109,7 @@ void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &norm
const view_incoming_key_device *k_view_dev,
const crypto::key_image &tx_first_key_image,
std::vector<RCTOutputEnoteProposal> &output_enote_proposals_out,
RCTOutputEnoteProposal &return_enote_out,
encrypted_payment_id_t &encrypted_payment_id_out,
cryptonote::transaction_type tx_type,
size_t &change_index_out,
+35 -2
View File
@@ -179,7 +179,7 @@ static void get_external_output_proposal_parts(const mx25519_pubkey &s_sender_re
encrypted_amount_t &encrypted_amount_out,
encrypted_payment_id_t &encrypted_payment_id_out,
view_tag_t &view_tag_out,
encrypted_return_pubkey_t return_pubkey_out)
encrypted_return_pubkey_t &return_pubkey_out)
{
// 1. s^ctx_sr = H_32(s_sr, D_e, input_context)
make_carrot_sender_receiver_secret(s_sender_receiver_unctx.data,
@@ -331,7 +331,7 @@ void get_output_proposal_normal_v1(const CarrotPaymentProposalV1 &proposal,
CarrotEnoteType::PAYMENT,
output_enote_out.enote.enote_ephemeral_pubkey,
input_context,
s_view_balance_dev,
s_view_balance_dev, // we need it to calculate the return pubkey
false, // coinbase_amount_commitment
s_sender_receiver,
output_enote_out.amount_blinding_factor,
@@ -499,6 +499,8 @@ void get_output_proposal_internal_v1(const CarrotPaymentProposalSelfSendV1 &prop
const view_balance_secret_device &s_view_balance_dev,
const crypto::key_image &tx_first_key_image,
const std::optional<mx25519_pubkey> &other_enote_ephemeral_pubkey,
const cryptonote::transaction_type tx_type,
RCTOutputEnoteProposal &return_enote_out,
RCTOutputEnoteProposal &output_enote_out)
{
// 1. sanity checks
@@ -560,6 +562,37 @@ void get_output_proposal_internal_v1(const CarrotPaymentProposalSelfSendV1 &prop
output_enote_out.enote.asset_type = "SAL1";
output_enote_out.enote.return_enc = crypto::rand<carrot::encrypted_return_pubkey_t>();
output_enote_out.amount = proposal.amount;
// 10. construct the stake return enote
if (tx_type == cryptonote::transaction_type::STAKE) {
// make k_return
crypto::secret_key k_return;
s_view_balance_dev.make_internal_return_privkey(input_context, output_enote_out.enote.onetime_address, k_return);
// compute K_return = k_return * G
crypto::public_key return_pub;
crypto::secret_key_to_public_key(k_return, return_pub);
// Make a destination address for the return
CarrotDestinationV1 return_destination;
make_carrot_main_address_v1(output_enote_out.enote.onetime_address, return_pub, return_destination);
// Create the return proposal, using the return address and the amount
const CarrotPaymentProposalV1 proposal_return = CarrotPaymentProposalV1{
.destination = return_destination,
.amount = 0,
.randomness = gen_janus_anchor()
};
encrypted_payment_id_t encrypted_payment_id_return;
get_output_proposal_return_v1(
proposal_return,
tx_first_key_image,
nullptr, // s_view_balance_dev
return_enote_out,
encrypted_payment_id_return
);
}
}
//-------------------------------------------------------------------------------------------------------------------
CarrotPaymentProposalV1 gen_carrot_payment_proposal_v1(const bool is_subaddress,
+2
View File
@@ -175,6 +175,8 @@ void get_output_proposal_internal_v1(const CarrotPaymentProposalSelfSendV1 &prop
const view_balance_secret_device &s_view_balance_dev,
const crypto::key_image &tx_first_key_image,
const std::optional<mx25519_pubkey> &other_enote_ephemeral_pubkey,
const cryptonote::transaction_type tx_type,
RCTOutputEnoteProposal &return_enote_out,
RCTOutputEnoteProposal &output_enote_out);
/**
* brief: gen_jamtis_payment_proposal_v1 - generate a random proposal
+188 -20
View File
@@ -70,6 +70,104 @@ static crypto::secret_key make_enote_ephemeral_privkey_sender(const janus_anchor
return enote_ephemeral_privkey;
}
//-------------------------------------------------------------------------------------------------------------------
bool scan_return_output(
const crypto::public_key &return_onetime_address,
const mx25519_pubkey &return_ephemeral_pubkey,
const carrot::view_tag_t &return_view_tag,
const carrot::encrypted_janus_anchor_t &return_anchor_enc,
const carrot::encrypted_amount_t &return_amount_enc,
const std::optional<rct::key> amount_commitment,
const carrot::input_context_t &return_input_context,
carrot::carrot_and_legacy_account &account,
crypto::public_key &address_spend_pubkey_out,
rct::xmr_amount &amount_out,
crypto::secret_key &amount_blinding_factor_out
) {
const auto &return_output_map = account.get_return_output_map_ref();
CHECK_AND_ASSERT_MES(return_output_map.count(return_onetime_address), false, "return output not found");
const auto &origin_tx = return_output_map.at(return_onetime_address);
// 1. make k_return
crypto::secret_key k_return;
account.s_view_balance_dev.make_internal_return_privkey(origin_tx.input_context, origin_tx.K_o, k_return);
// 2. compute K_return' = k_return * G
crypto::public_key K_return;
crypto::secret_key_to_public_key(k_return, K_return);
// 3. ssr
mx25519_pubkey shared_secret_return_unctx;
crypto::hash shared_secret_return;
carrot::make_carrot_uncontextualized_shared_key_receiver(k_return, return_ephemeral_pubkey, shared_secret_return_unctx);
carrot::make_carrot_sender_receiver_secret(
shared_secret_return_unctx.data,
return_ephemeral_pubkey,
return_input_context,
shared_secret_return
);
// 4. verify the view_tag
CHECK_AND_ASSERT_MES(
carrot::test_carrot_view_tag(
shared_secret_return_unctx.data,
return_input_context,
return_onetime_address,
return_view_tag
),
false,
"view tag verification failed for carrot coinbase enote"
);
// 5. compute anchor_return
carrot::janus_anchor_t recovered_anchor_return =
carrot::decrypt_carrot_anchor(return_anchor_enc, shared_secret_return, return_onetime_address);
// 6. compute d_e'
crypto::secret_key recovered_ephemeral_privkey_return;
carrot::make_carrot_enote_ephemeral_privkey(
recovered_anchor_return,
return_input_context,
origin_tx.K_change,
carrot::null_payment_id,
recovered_ephemeral_privkey_return
);
// 7. compute D_e'
mx25519_pubkey recovered_ephemeral_pubkey_return;
carrot::make_carrot_enote_ephemeral_pubkey(
recovered_ephemeral_privkey_return,
origin_tx.K_change,
false,
recovered_ephemeral_pubkey_return
);
// 8. verify the enote ephemeral pubkey
CHECK_AND_ASSERT_MES(
memcmp(recovered_ephemeral_pubkey_return.data, return_ephemeral_pubkey.data, sizeof(mx25519_pubkey)) == 0,
false,
"carrot coinbase enote protection verification failed"
);
amount_out = carrot::decrypt_carrot_amount(return_amount_enc, shared_secret_return, return_onetime_address);
address_spend_pubkey_out = origin_tx.K_change;
if (amount_commitment)
{
CHECK_AND_ASSERT_MES(
carrot::try_recompute_carrot_amount_commitment(shared_secret_return,
amount_out,
address_spend_pubkey_out,
carrot::CarrotEnoteType::PAYMENT,
amount_commitment.value(),
amount_blinding_factor_out
),
false,
"failed to recompute carrot amount commitment for return output"
);
}
return true;
}
//-------------------------------------------------------------------------------------------------------------------
static bool try_scan_carrot_coinbase_enote_checked(
const CarrotCoinbaseEnoteV1 &enote,
const mx25519_pubkey &s_sender_receiver_unctx,
@@ -361,43 +459,113 @@ bool try_scan_carrot_enote_external_receiver(const CarrotEnoteV1 &enote,
}
//-------------------------------------------------------------------------------------------------------------------
bool try_scan_carrot_enote_internal_receiver(const CarrotEnoteV1 &enote,
const view_balance_secret_device &s_view_balance_dev,
carrot::carrot_and_legacy_account &account,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out,
crypto::public_key &address_spend_pubkey_out,
rct::xmr_amount &amount_out,
crypto::secret_key &amount_blinding_factor_out,
CarrotEnoteType &enote_type_out,
janus_anchor_t &internal_message_out)
janus_anchor_t &internal_message_out,
crypto::public_key &return_address_out,
bool &is_return_out)
{
// input_context
const input_context_t input_context = make_carrot_input_context(enote.tx_first_key_image);
// assume that the enote is not a return output
is_return_out = false;
// vt = H_3(s_sr || input_context || Ko)
view_tag_t nominal_view_tag;
s_view_balance_dev.make_internal_view_tag(input_context, enote.onetime_address, nominal_view_tag);
account.s_view_balance_dev.make_internal_view_tag(input_context, enote.onetime_address, nominal_view_tag);
// test view tag
if (nominal_view_tag != enote.view_tag)
if (nominal_view_tag == enote.view_tag) {
// s^ctx_sr = H_32(s_vb, D_e, input_context)
crypto::hash s_sender_receiver;
account.s_view_balance_dev.make_internal_sender_receiver_secret(enote.enote_ephemeral_pubkey,
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,
sender_extension_t_out,
address_spend_pubkey_out,
amount_out,
amount_blinding_factor_out,
enote_type_out,
internal_message_out))
return false;
// we received a change output
// save the Kr = K_change + K_return to out subaddress map
for (const auto &output_key : enote.tx_output_keys) {
// make k_return
crypto::secret_key k_return;
const carrot::input_context_t input_context = carrot::make_carrot_input_context(enote.tx_first_key_image);
account.s_view_balance_dev.make_internal_return_privkey(input_context, output_key, k_return);
// compute K_return = k_return * G
crypto::public_key K_return;
crypto::secret_key_to_public_key(k_return, K_return);
// compute K_r = K_return + K_o
crypto::public_key K_r = rct::rct2pk(rct::addKeys(rct::pk2rct(K_return), rct::pk2rct(enote.onetime_address)));
// 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::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}}});
}
// janus protection checks are not needed for internal scans
return true;
}
// check for known return addresses
const auto &return_map = account.get_return_output_map_ref();
if (return_map.find(enote.onetime_address) == return_map.end())
return false;
// s^ctx_sr = H_32(s_vb, D_e, input_context)
crypto::hash s_sender_receiver;
s_view_balance_dev.make_internal_sender_receiver_secret(enote.enote_ephemeral_pubkey,
input_context,
s_sender_receiver);
// scan the return output
if (!scan_return_output(
enote.onetime_address,
enote.enote_ephemeral_pubkey,
enote.view_tag,
enote.anchor_enc,
enote.amount_enc,
enote.amount_commitment,
carrot::make_carrot_input_context(enote.tx_first_key_image),
account,
address_spend_pubkey_out,
amount_out,
amount_blinding_factor_out))
return false;
return try_scan_carrot_enote_internal_burnt(enote,
s_sender_receiver,
sender_extension_g_out,
sender_extension_t_out,
address_spend_pubkey_out,
amount_out,
amount_blinding_factor_out,
enote_type_out,
internal_message_out);
// janus protection checks are not needed for internal scans
// if we come here, we have a return output
is_return_out = true;
return_address_out = enote.onetime_address;
return true;
}
//-------------------------------------------------------------------------------------------------------------------
} //namespace carrot
+19 -2
View File
@@ -39,6 +39,7 @@
#include "carrot_enote_types.h"
#include "device.h"
#include "span.h"
#include "account.h"
//third party headers
@@ -178,14 +179,30 @@ bool try_scan_carrot_enote_external_receiver(const CarrotEnoteV1 &enote,
* return: true iff the scan process succeeded
*/
bool try_scan_carrot_enote_internal_receiver(const CarrotEnoteV1 &enote,
const view_balance_secret_device &s_view_balance_dev,
carrot::carrot_and_legacy_account &account,
crypto::secret_key &sender_extension_g_out,
crypto::secret_key &sender_extension_t_out,
crypto::public_key &address_spend_pubkey_out,
rct::xmr_amount &amount_out,
crypto::secret_key &amount_blinding_factor_out,
CarrotEnoteType &enote_type_out,
janus_anchor_t &internal_message_out);
janus_anchor_t &internal_message_out,
crypto::public_key &return_address_out,
bool &is_return_out);
//! @TODO: try_scan_carrot_enote_internal_sender(): can't validate burning w/o passing s_sr = s_vb
bool scan_return_output(
const crypto::public_key &return_onetime_address,
const mx25519_pubkey &return_ephemeral_pubkey,
const carrot::view_tag_t &return_view_tag,
const carrot::encrypted_janus_anchor_t &return_anchor_enc,
const carrot::encrypted_amount_t &return_amount_enc,
const std::optional<rct::key> amount_commitment,
const carrot::input_context_t &return_input_context,
carrot::carrot_and_legacy_account &account,
crypto::public_key &address_spend_pubkey_out,
rct::xmr_amount &amount_out,
crypto::secret_key &amount_blinding_factor_out
);
} //namespace carrot
-1
View File
@@ -27,7 +27,6 @@
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set(carrot_impl_sources
account.cpp
address_device_ram_borrowed.cpp
address_utils_compat.cpp
format_utils.cpp
+34 -9
View File
@@ -33,7 +33,6 @@
#include "carrot_core/enote_utils.h"
#include "carrot_core/exceptions.h"
#include "carrot_core/payment_proposal.h"
#include "common/container_helpers.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "cryptonote_core/cryptonote_tx_utils.h"
#include "cryptonote_config.h"
@@ -198,7 +197,9 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotE
const std::vector<cryptonote::tx_source_entry> &sources,
const rct::xmr_amount fee,
const cryptonote::transaction_type tx_type,
const std::vector<uint8_t> change_masks,
const rct::xmr_amount tx_amount_burnt,
const std::vector<uint8_t> &change_masks,
const carrot::RCTOutputEnoteProposal &return_enote,
const encrypted_payment_id_t encrypted_payment_id)
{
const size_t nins = key_images.size();
@@ -208,11 +209,15 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotE
cryptonote::transaction tx;
tx.pruned = true;
tx.version = TRANSACTION_VERSION_N_OUTS;
tx.unlock_time = 0;
tx.source_asset_type = "SAL1";
tx.destination_asset_type = "SAL1";
tx.type = tx_type == cryptonote::transaction_type::RETURN ? cryptonote::transaction_type::TRANSFER : tx_type;
tx.version = TRANSACTION_VERSION_CARROT;
tx.type =
tx_type == cryptonote::transaction_type::RETURN ? cryptonote::transaction_type::TRANSFER : tx_type;
tx.amount_burnt = (
tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::BURN
) ? tx_amount_burnt : 0;
tx.return_address_change_mask.assign(change_masks.begin(), change_masks.end());
tx.vin.reserve(nins);
tx.vout.reserve(nouts);
@@ -252,16 +257,28 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotE
}});
//a_enc
rct::ecdhTuple &ecdh_tuple = tools::add_element(tx.rct_signatures.ecdhInfo);
rct::ecdhTuple &ecdh_tuple = tx.rct_signatures.ecdhInfo.emplace_back();
memcpy(ecdh_tuple.amount.bytes, enote.amount_enc.bytes, sizeof(ecdh_tuple.amount));
//C_a
tx.rct_signatures.outPk.push_back(rct::ctkey{rct::key{}, enote.amount_commitment});
//K_return
crypto::public_key K_return;
memcpy(K_return.data, enote.return_enc.bytes, sizeof(crypto::public_key));
tx.return_address_list.push_back(K_return);
if (tx_type != cryptonote::transaction_type::STAKE) {
crypto::public_key K_return;
memcpy(K_return.data, enote.return_enc.bytes, sizeof(crypto::public_key));
tx.return_address_list.push_back(K_return);
}
}
// store the return pubkey for stake txs
if (tx_type == cryptonote::transaction_type::STAKE)
{
tx.protocol_tx_data.version = 1;
memcpy(tx.protocol_tx_data.return_address.data, return_enote.enote.onetime_address.data, sizeof(crypto::public_key));
memcpy(tx.protocol_tx_data.return_pubkey.data, return_enote.enote.enote_ephemeral_pubkey.data, sizeof(crypto::public_key));
tx.protocol_tx_data.return_view_tag = return_enote.enote.view_tag;
tx.protocol_tx_data.return_anchor_enc = return_enote.enote.anchor_enc;
}
//ephemeral pubkeys: D_e
@@ -337,6 +354,14 @@ bool try_load_carrot_enote_from_transaction_v1(const cryptonote::transaction &tx
//D_e
enote_out.enote_ephemeral_pubkey = enote_ephemeral_pubkeys[ephemeral_pubkey_index];
// save all output keys in order to calculate Kr values.
for (const auto& out: tx.vout) {
const cryptonote::txout_to_carrot_v1 * const carrot_out = boost::strict_get<cryptonote::txout_to_carrot_v1>(&out.target);
if (carrot_out) {
enote_out.tx_output_keys.push_back(carrot_out->key);
}
}
return true;
}
@@ -395,7 +420,7 @@ cryptonote::transaction store_carrot_to_coinbase_transaction_v1(
cryptonote::transaction tx;
tx.type = tx_type;
tx.pruned = false;
tx.version = 2;
tx.version = TRANSACTION_VERSION_CARROT;
tx.unlock_time = CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
tx.vin.reserve(1);
tx.vout.reserve(nouts);
+4 -1
View File
@@ -35,6 +35,7 @@
#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_basic/tx_extra.h"
#include "cryptonote_core/cryptonote_tx_utils.h"
#include "carrot_core/payment_proposal.h"
//third party headers
@@ -105,7 +106,9 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotE
const std::vector<cryptonote::tx_source_entry> &sources,
const rct::xmr_amount fee,
const cryptonote::transaction_type tx_type,
const std::vector<uint8_t> change_masks,
const rct::xmr_amount tx_amount_burnt,
const std::vector<uint8_t> &change_masks,
const RCTOutputEnoteProposal &return_enote,
const encrypted_payment_id_t encrypted_payment_id);
/**
* brief: try_load_carrot_enote_from_transaction_v1 - load one non-coinbase Carrot enote from a cryptonote::transaction
+12 -18
View File
@@ -318,33 +318,27 @@ std::vector<std::size_t> get_input_counts_in_preferred_order()
// 1 or 2 randomly, then
// other ascending powers of 2, then
// other ascending positive numbers
//! @TODO: MRL discussion about 2 vs 1 default input count when 1 input can pay. If we default
// to 1, then that may reveal more information about the amount, and reveals that one can't pay
// with 1 output when using 2. Vice versa, if we default to 2, then that means that one only
// owns 1 output when using 1. It may be the most advantageous to randomly switch between
// preferring 1 vs 2. See: https://lavalle.pl/planning/node437.html. Con to this approach: if we
// default to 1 over 2 always then there's scenarios where we net save tx fees and proving time.
//static_assert(CARROT_MAX_TX_INPUTS == FCMP_PLUS_PLUS_MAX_INPUTS, "inconsistent input count max limit");
static_assert(CARROT_MIN_TX_INPUTS == 1 && CARROT_MAX_TX_INPUTS == 128,
// preferring 1 vs 2. See: https://lavalle.pl/planning/node437.html. Con to this approach: if we
// default to 1 over 2 always then there's scenarios where we net save tx fees and proving time.
//static_assert(CARROT_MAX_TX_INPUTS == FCMP_PLUS_PLUS_MAX_INPUTS, "inconsistent input count max limit");
static_assert(CARROT_MIN_TX_INPUTS == 1 && CARROT_MAX_TX_INPUTS == 64,
"refactor this function for different input count limits");
const bool random_bit = 0 == (crypto::rand<uint8_t>() & 0x01);
if (random_bit)
return {2, 1, 4, 8, 16, 32, 64, 128, 3, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 65, 66, 67, 68, 69, 70, 71, 72, 73,
74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127};
return {2, 1, 4, 8, 16, 32, 64, 3, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63};
else
return {1, 2, 4, 8, 16, 32, 64, 128, 3, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 65, 66, 67, 68, 69, 70, 71, 72, 73,
74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116,
117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127};
return {1, 2, 4, 8, 16, 32, 64, 3, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63};
}
//-------------------------------------------------------------------------------------------------------------------
select_inputs_func_t make_single_transfer_input_selector(
+4
View File
@@ -60,6 +60,7 @@ void get_output_enote_proposals_from_proposal_v1(const CarrotTransactionProposal
// derive enote proposals
size_t change_index;
RCTOutputEnoteProposal return_enote_out;
std::unordered_map<crypto::public_key, size_t> normal_payments_indices;
get_output_enote_proposals(tx_proposal.normal_payment_proposals,
selfsend_payment_proposal_cores,
@@ -68,6 +69,7 @@ void get_output_enote_proposals_from_proposal_v1(const CarrotTransactionProposal
k_view_dev,
tx_proposal.key_images_sorted.at(0),
output_enote_proposals_out,
return_enote_out,
encrypted_payment_id_out,
tx_proposal.tx_type,
change_index,
@@ -127,7 +129,9 @@ void make_pruned_transaction_from_proposal_v1(const CarrotTransactionProposalV1
tx_proposal.sources,
tx_proposal.fee,
cryptonote::transaction_type::TRANSFER,
0, // tx_amount_burnt
{}, // change_masks
{}, // return_enote
encrypted_payment_id);
// add extra payload and sort
+2
View File
@@ -86,6 +86,8 @@ struct CarrotTransactionProposalV1
rct::xmr_amount fee;
/// transaction type, e.g.
cryptonote::transaction_type tx_type;
/// how much money tx burns
rct::xmr_amount amount_burnt;
/// This field is truly "extra". It should contain only tx.extra fields that aren't present in a
/// normal Carrot transaction, i.e. NOT ephemeral pubkeys nor encrypted PIDs
+26 -4
View File
@@ -90,7 +90,7 @@ static void append_additional_payment_proposal_if_necessary(
//-------------------------------------------------------------------------------------------------------------------
std::uint64_t get_carrot_default_tx_extra_size(const std::size_t n_outputs)
{
CHECK_AND_ASSERT_THROW_MES(n_outputs <= FCMP_PLUS_PLUS_MAX_OUTPUTS,
CHECK_AND_ASSERT_THROW_MES(n_outputs <= CARROT_MAX_TX_OUTPUTS,
"get_carrot_default_tx_extra_size: n_outputs too high: " << n_outputs);
CHECK_AND_ASSERT_THROW_MES(n_outputs >= CARROT_MIN_TX_OUTPUTS,
"get_carrot_default_tx_extra_size: n_outputs too low: " << n_outputs);
@@ -217,8 +217,19 @@ void make_carrot_transaction_proposal_v1(const std::vector<CarrotPaymentProposal
input_amount_sum -= normal_payment_proposal.amount;
for (const CarrotPaymentProposalVerifiableSelfSendV1 &selfsend_payment_proposal : selfsend_payment_proposals)
input_amount_sum -= selfsend_payment_proposal.proposal.amount;
CHECK_AND_ASSERT_THROW_MES(input_amount_sum == 0,
"make_carrot_transaction_proposal_v1: post-carved transaction does not balance");
if (tx_type != cryptonote::transaction_type::STAKE &&
tx_type != cryptonote::transaction_type::BURN)
{
CHECK_AND_ASSERT_THROW_MES(input_amount_sum == 0,
"make_carrot_transaction_proposal_v1: post-carved transaction does not balance");
} else {
tx_proposal_out.amount_burnt = input_amount_sum.convert_to<uint64_t>();
CHECK_AND_ASSERT_THROW_MES(tx_proposal_out.amount_burnt >= 0,
"make_carrot_transaction_proposal_v1: post-carved transaction burnt amount is negative: "
<< tx_proposal_out.amount_burnt);
input_amount_sum -= tx_proposal_out.amount_burnt;
}
// collect and sort key images
tx_proposal_out.key_images_sorted.reserve(selected_inputs.size());
@@ -273,7 +284,8 @@ void make_carrot_transaction_proposal_v1_transfer(
carve_fees_and_balance_func_t carve_fees_and_balance =
[
&subtractable_normal_payment_proposals,
&subtractable_selfsend_payment_proposals
&subtractable_selfsend_payment_proposals,
&tx_type
]
(
const boost::multiprecision::uint128_t &input_sum_amount,
@@ -382,6 +394,16 @@ void make_carrot_transaction_proposal_v1_transfer(
CHECK_AND_ASSERT_THROW_MES(fee_remainder == 0,
"make unsigned transaction transfer subtractable: bug: fee remainder at end of carve function");
// remove the self send payment we have made to ourself now that we have our change payment.
if (tx_type == cryptonote::transaction_type::STAKE ||
tx_type == cryptonote::transaction_type::BURN)
{
selfsend_payment_proposals.back().proposal.enote_ephemeral_pubkey =
selfsend_payment_proposals.front().proposal.enote_ephemeral_pubkey;
selfsend_payment_proposals.erase(selfsend_payment_proposals.begin());
}
}; //end carve_fees_and_balance
// make unsigned transaction with fee carving callback
+11 -4
View File
@@ -92,19 +92,26 @@ static size_t estimate_rct_tx_size_carrot(int n_inputs, int mixin, int n_outputs
size += 1 + 6;
// vin
size += n_inputs * (1+6+4+(mixin+1)*2+32);
size += n_inputs * (1+1+4+(mixin+1)*2+32);
// vout
size += n_outputs * (3+4+16+32);
size += n_outputs * (1+1+32+4+3+16);
// extra
size += extra_size;
// rct signatures
// type
size += 1;
// amount_burnt
size += 8;
// return_address data
// NOTE: this will be wrong by 21 bytes for STAKE TXs using `protocol_tx_data_t`
size += n_outputs * (32 + 1);
// rct signatures
// rangeSigs
if (bulletproof || bulletproof_plus)
{
-7
View File
@@ -138,13 +138,6 @@ bool keys_match_internal_values(const std::unordered_map<KeyT, ValueT> &map, Pre
return true;
}
/// convenience wrapper for getting the last element after emplacing back
template <typename ContainerT>
typename ContainerT::value_type& add_element(ContainerT &container)
{
container.emplace_back();
return container.back();
}
/// convenience erasor for unordered maps: std::erase_if(std::unordered_map) is C++20
template <typename KeyT, typename ValueT, typename PredT>
void for_all_in_map_erase_if(std::unordered_map<KeyT, ValueT> &map_inout, PredT predicate)
+3
View File
@@ -151,6 +151,8 @@ DISABLE_VS_WARNINGS(4244 4345)
void account_base::forget_spend_key()
{
m_keys.m_spend_secret_key = crypto::secret_key();
m_keys.s_master = m_keys.m_spend_secret_key;
m_keys.k_prove_spend = m_keys.m_spend_secret_key;
m_keys.m_multisig_keys.clear();
}
//-----------------------------------------------------------------
@@ -163,6 +165,7 @@ DISABLE_VS_WARNINGS(4244 4345)
"Unexpected derived public spend key");
m_keys.m_spend_secret_key = spend_secret_key;
m_keys.s_master = m_keys.m_spend_secret_key;
}
//-----------------------------------------------------------------
crypto::secret_key account_base::generate(const crypto::secret_key& recovery_key, bool recover, bool two_random)
+37 -3
View File
@@ -197,6 +197,23 @@ namespace cryptonote
};
class protocol_tx_data_t {
public:
uint8_t version;
crypto::public_key return_address;
crypto::public_key return_pubkey;
carrot::view_tag_t return_view_tag;
carrot::encrypted_janus_anchor_t return_anchor_enc;
BEGIN_SERIALIZE_OBJECT()
VARINT_FIELD(version)
FIELD(return_address)
FIELD(return_pubkey)
FIELD(return_view_tag)
FIELD(return_anchor_enc)
END_SERIALIZE()
};
class transaction_prefix
{
@@ -227,6 +244,8 @@ namespace cryptonote
// Slippage limit
uint64_t amount_slippage_limit;
protocol_tx_data_t protocol_tx_data;
BEGIN_SERIALIZE()
VARINT_FIELD(version)
if(version == 0 || CURRENT_TRANSACTION_VERSION < version) return false;
@@ -244,8 +263,14 @@ namespace cryptonote
FIELD(return_address_list)
FIELD(return_address_change_mask)
} else {
FIELD(return_address)
FIELD(return_pubkey)
if (type == cryptonote::transaction_type::STAKE &&
version >= TRANSACTION_VERSION_CARROT)
{
FIELD(protocol_tx_data)
} else {
FIELD(return_address)
FIELD(return_pubkey)
}
}
FIELD(source_asset_type)
FIELD(destination_asset_type)
@@ -268,6 +293,10 @@ namespace cryptonote
return_address_list.clear();
return_address_change_mask.clear();
return_pubkey = crypto::null_pkey;
protocol_tx_data.return_address = crypto::null_pkey;
protocol_tx_data.return_pubkey = crypto::null_pkey;
protocol_tx_data.return_view_tag = {};
protocol_tx_data.return_anchor_enc = {};
source_asset_type.clear();
destination_asset_type.clear();
amount_burnt = 0;
@@ -669,6 +698,7 @@ namespace cryptonote
{
crypto::public_key m_spend_public_key;
crypto::public_key m_view_public_key;
bool m_is_carrot;
BEGIN_SERIALIZE_OBJECT()
FIELD(m_spend_public_key)
@@ -683,7 +713,8 @@ namespace cryptonote
bool operator==(const account_public_address& rhs) const
{
return m_spend_public_key == rhs.m_spend_public_key &&
m_view_public_key == rhs.m_view_public_key;
m_view_public_key == rhs.m_view_public_key &&
m_is_carrot == rhs.m_is_carrot;
}
bool operator!=(const account_public_address& rhs) const
@@ -752,6 +783,7 @@ VARIANT_TAG(binary_archive, cryptonote::txout_to_scripthash, 0x1);
VARIANT_TAG(binary_archive, cryptonote::txout_to_key, 0x2);
VARIANT_TAG(binary_archive, cryptonote::txout_to_tagged_key, 0x3);
VARIANT_TAG(binary_archive, cryptonote::txout_to_carrot_v1, 0x4);
VARIANT_TAG(binary_archive, cryptonote::protocol_tx_data_t, 0x0);
VARIANT_TAG(binary_archive, cryptonote::transaction, 0xcc);
VARIANT_TAG(binary_archive, cryptonote::block, 0xbb);
@@ -764,6 +796,7 @@ VARIANT_TAG(json_archive, cryptonote::txout_to_scripthash, "scripthash");
VARIANT_TAG(json_archive, cryptonote::txout_to_key, "key");
VARIANT_TAG(json_archive, cryptonote::txout_to_tagged_key, "tagged_key");
VARIANT_TAG(json_archive, cryptonote::txout_to_carrot_v1, "carrot_v1");
VARIANT_TAG(json_archive, cryptonote::protocol_tx_data_t, "protocol_tx_data");
VARIANT_TAG(json_archive, cryptonote::transaction, "tx");
VARIANT_TAG(json_archive, cryptonote::block, "block");
@@ -776,5 +809,6 @@ VARIANT_TAG(debug_archive, cryptonote::txout_to_scripthash, "scripthash");
VARIANT_TAG(debug_archive, cryptonote::txout_to_key, "key");
VARIANT_TAG(debug_archive, cryptonote::txout_to_tagged_key, "tagged_key");
VARIANT_TAG(debug_archive, cryptonote::txout_to_carrot_v1, "carrot_v1");
VARIANT_TAG(debug_archive, cryptonote::protocol_tx_data_t, "protocol_tx_data");
VARIANT_TAG(debug_archive, cryptonote::transaction, "tx");
VARIANT_TAG(debug_archive, cryptonote::block, "block");
@@ -285,6 +285,8 @@ namespace cryptonote {
LOG_PRINT_L1("Failed to validate address keys");
return false;
}
info.address.m_is_carrot = info.is_carrot;
}
else
{
@@ -316,6 +318,7 @@ namespace cryptonote {
//we success
info.address = blob.m_address;
info.address.m_is_carrot = false;
info.is_subaddress = false;
info.has_payment_id = false;
info.is_carrot = false;
@@ -73,6 +73,11 @@ namespace boost
a & reinterpret_cast<char (&)[sizeof(crypto::key_image)]>(x);
}
template <class Archive>
inline void serialize(Archive &a, carrot::input_context_t &x, const boost::serialization::version_type ver)
{
a & reinterpret_cast<char (&)[sizeof(carrot::input_context_t)]>(x);
}
template <class Archive>
inline void serialize(Archive &a, crypto::view_tag &x, const boost::serialization::version_type ver)
{
a & reinterpret_cast<char (&)[sizeof(crypto::view_tag)]>(x);
@@ -166,6 +171,15 @@ namespace boost
a & x.target;
}
template <class Archive>
inline void serialize(Archive &a, cryptonote::protocol_tx_data_t &x, const boost::serialization::version_type ver)
{
a & x.version;
a & x.return_address;
a & x.return_pubkey;
a & x.return_view_tag;
a & x.return_anchor_enc;
}
template <class Archive>
inline void serialize(Archive &a, cryptonote::transaction_prefix &x, const boost::serialization::version_type ver)
@@ -183,8 +197,14 @@ namespace boost
a & x.return_address_list;
a & x.return_address_change_mask;
} else {
a & x.return_address;
a & x.return_pubkey;
if (x.type == cryptonote::transaction_type::STAKE &&
x.version >= TRANSACTION_VERSION_CARROT)
{
a & x.protocol_tx_data;
} else {
a & x.return_address;
a & x.return_pubkey;
}
}
a & x.source_asset_type;
a & x.destination_asset_type;
@@ -209,8 +229,14 @@ namespace boost
a & x.return_address_list;
a & x.return_address_change_mask;
} else {
a & x.return_address;
a & x.return_pubkey;
if (x.type == cryptonote::transaction_type::STAKE &&
x.version >= TRANSACTION_VERSION_CARROT)
{
a & x.protocol_tx_data;
} else {
a & x.return_address;
a & x.return_pubkey;
}
}
a & x.source_asset_type;
a & x.destination_asset_type;
@@ -113,7 +113,7 @@ namespace cryptonote
const bool plus = (
rv.type == rct::RCTTypeBulletproofPlus ||
rv.type == rct::RCTTypeFullProofs ||
rv.type == rct::RCTTypeSalviumZero ||
/*rv.type == rct::RCTTypeSalviumZero ||*/
rv.type == rct::RCTTypeSalviumOne);
const uint64_t bp_base = (32 * ((plus ? 6 : 9) + 7 * 2)) / 2; // notional size of a 2 output proof, normalized to 1 proof (ie, divided by 2)
const size_t n_outputs = tx.vout.size();
@@ -1381,17 +1381,26 @@ namespace cryptonote
for (const auto &o: tx.vout)
{
if (hf_version >= HF_VERSION_CARROT)
{
// from v11, 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 (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));
} else {
CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_key) ||
o.target.type() == typeid(txout_to_tagged_key) ||
o.target.type() == typeid(txout_to_carrot_v1), false, "wrong variant type: "
<< o.target.type().name() << ", expected txout_to_key or txout_to_tagged_key or txout_to_carrot_v1 in protocol transaction");
// require all outputs in a tx be of the same type
CHECK_AND_ASSERT_MES(o.target.type() == tx.vout[0].target.type(), false, "non-matching variant types: "
<< o.target.type().name() << " and " << tx.vout[0].target.type().name() << ", "
<< "expected matching variant types in protocol transaction");
}
}
else {
// require outputs be of type txout_to_key OR txout_to_tagged_key
// to allow grace period before requiring all to be txout_to_tagged_key
CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_key) || o.target.type() == typeid(txout_to_tagged_key), false, "wrong variant type: "
<< o.target.type().name() << ", expected txout_to_key or txout_to_tagged_key in transaction");
// require all outputs in a tx be of the same type
CHECK_AND_ASSERT_MES(o.target.type() == tx.vout[0].target.type(), false, "non-matching variant types: "
<< o.target.type().name() << " and " << tx.vout[0].target.type().name() << ", "
+21 -3
View File
@@ -104,7 +104,9 @@ namespace cryptonote
}
miner::miner(i_miner_handler* phandler, const get_block_hash_t &gbh):m_stop(1),
miner::miner(i_miner_handler* phandler, const get_block_hash_t &gbh):
m_forced_stop(1),
m_stop(1),
m_template{},
m_template_no(0),
m_diffic(0),
@@ -176,6 +178,8 @@ namespace cryptonote
if(!m_phandler->get_block_template(bl, m_mine_address, di, height, expected_reward, extra_nonce, seed_height, seed_hash))
{
LOG_ERROR("Failed to get_block_template(), stopping mining");
m_forced_stop = true;
m_stop = true;
return false;
}
set_block_template(bl, di, height, expected_reward);
@@ -185,7 +189,17 @@ namespace cryptonote
bool miner::on_idle()
{
m_update_block_template_interval.do_call([&](){
if(is_mining())request_block_template();
if(is_mining()) {
// Request the block template
request_block_template();
} else {
// Check for forced stop
if (m_forced_stop) {
if (!m_threads_active) {
stop();
}
}
}
return true;
});
@@ -393,8 +407,12 @@ namespace cryptonote
return false;
}
request_block_template();//lets update block template
if (!request_block_template()) {
LOG_ERROR("Unable to start miner - unknown error");
return false;
}
m_forced_stop = false;
m_stop = false;
m_thread_index = 0;
set_is_background_mining_enabled(do_background);
+1
View File
@@ -118,6 +118,7 @@ namespace cryptonote
};
std::atomic<bool> m_forced_stop;
std::atomic<bool> m_stop;
epee::critical_section m_template_lock;
block m_template;
+21 -36
View File
@@ -44,9 +44,10 @@
#define CRYPTONOTE_MAX_TX_PER_BLOCK 0x10000000
#define CRYPTONOTE_PUBLIC_ADDRESS_TEXTBLOB_VER 0
#define CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW 60
#define CURRENT_TRANSACTION_VERSION 3
#define CURRENT_TRANSACTION_VERSION 4
#define TRANSACTION_VERSION_2_OUTS 2
#define TRANSACTION_VERSION_N_OUTS 3
#define TRANSACTION_VERSION_CARROT 4
#define CURRENT_BLOCK_MAJOR_VERSION 1
#define CURRENT_BLOCK_MINOR_VERSION 1
#define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT 60*60*2
@@ -87,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
@@ -249,7 +250,7 @@
#define HF_VERSION_ENABLE_ORACLE 255
#define HF_VERSION_SLIPPAGE_YIELD 255
#define TESTNET_VERSION 14
#define TESTNET_VERSION 15
#define STAGENET_VERSION 1
#define PER_KB_FEE_QUANTIZATION_DECIMALS 8
@@ -318,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
@@ -430,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, {"310fe378b82e2475a87c83eef07c57d92fa0731d5c499258e774852158276968","f17c5a62efcc96710f2a173538fe5e79b190b44ab6facc01a619f0e227ac18d1","eb613855f58d093ae9d527e8ab69b46a","6c74f9"}},
{1120, {"adb74cea72d46ebe5a45e5886189e71c493308686f076ccfc053cb9b4f55b656","b40e958589238cc48a5a7f03f1f28406a13b2f724efe831cfec43c06bb0fcb60","77a51cc3a6c3c53d631cadad72f57c1d","e5fec0"}},
{1140, {"c5e9a67c695ddcab05b980de0c8cc7d9d5b9c09af58047843bfd7d29a038f671","6c3253bd837b899b87aedc97114feb9c10c2d255ce59b7ba5b470817a37d16df","fcf6e1907ea30dbafd510e33a37cc2dc","fa5c45"}},
{1160, {"e95c8b7fc3d0e7e34d544e97d3b526e94f70acb7d85cb0053b3cb830e863ac38","3a5ae93e922985ea1a126ff8fd35f6cbf39fa556525e15a049765510eb1360e1","7ea6d62add9387a28f3dbed5dbac120b","c26ac2"}},
{1180, {"9d453235a25e470df2d7ceaa8f8d4216a8939a0a93bc0c43f1b902cb461ee82d","2e990ed2de4d745389d88aa9868634ce430f09d2c0cb5ebc2ee8b9bcc23e221a","807e07241a48b97acb537241822a97b8","eb37c0"}},
{1200, {"92dbd7da158d4bcb4f002dcc505ed4c49ac4e4782724b506a5977119c80c4869","4505a9be31c2ac6ad5faa2ea8332b85b1a3efee84d0456740d208b8188ad842a","8870c1f88de25b5443d8323759f386d9","443c65"}},
{1220, {"1e4d5c44a112c3ac89ab2c7065bb2c8c094779b3a5d696652198f7dd05f88b77","01f56bf134652852688e30dd9559bdb5fe2edd046bed99dbea23b655d2475b89","da088fc1e1c2f2d0e348f06f2079e8ed","d7c7bf"}},
{1240, {"91ba144c930a65996b2d7c3b296217c692225756392b580a323838e48b357452","f2d5f4f2aaa599a52c557270cbc5cde138b753a98297d975b74a7223a9b80b13","0dc3a42cb1f36768a09c316790936356","172c22"}},
{1260, {"1d626ede8d2e34e888384655b30942a25be272a3ea58d49835265e6933a91a52","05666de1b136236f0682e128c0c3ae84578a6a4b233a578dd676e581a582fe19","c81ccf7f93e01e7872543907de8d17d0","21dd94"}},
{1280, {"85b10b5ae72ab59281f47be4edede17b7a017dc7ddd839813e3a12c0b2a39874","d34d259e2fee2b0c977242c473cf83997920c1e64ff7a9c364b2bf4abba4d5cc","43de6ce100ca3eeb6734fc44cc9f7dbc","574e2f"}},
{1300, {"c699ca0dcbe538e6be9e997acb50a4c13557d1e8cf3a105ab47406cb68300656","50c5e0fa60907c2c2b66748562193b7f5bfcba269eb398c5c975c9e125933942","05aeb41f8863152a7e0e36f216ecbb12","e413dd"}},
{1320, {"76bb126d6e0e5d3d7ee7ccabd194a248cbe006ae7d57f89c31c6826f8e7b4851","e1f2277b378630d92bc03003e3d206899404afde342fcb3a9b1790f798c8b5a4","26274ad1bbf5d483f2c747754b898873","a2df70"}},
{1340, {"5738dc94686bfffe1789b695785f270f6f616b1a52e071e975cf915063233b1a","982b4848ed74f2a5d899f778b182c35f40535579349435b466ff98c3fce678e7","67bd9b9a434b9028564a065a30ae34fa","36ceec"}},
{1360, {"473b6f6941d6eafaef007d3d932763b6e39c1428d3ba76dfff452ddc67f93b48","76e3b1280b959672d86c357e6f5fcd8a74e61a0a948a0541a0eb384060705bac","74b5ad8847471e3d8723ad3e101b8a13","5e5671"}},
{1380, {"6dae9a14e24b96caf8f7acb0edad936a4ecaa194a61b3785a2b1d08cbca0a158","26ca204c10481ed2447447e3893e0c46c9f01df16c3f3a671cf9e6054176ee5d","7c04e4ed28bfac12a0050bdae44f9979","4c23ad"}},
{1400, {"a5837480d8e17ddb9b29baaf5671545d59d18b546de0d7092768882cff94d125","dc76eb3892b3721da947fc71de8ab30ef32651f1b6bb1a45eb7b80882217a5ac","bb490940a3cdf4a17fd0bec0b578531f","84d899"}}
{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"}}
};
}
+266 -65
View File
@@ -1348,6 +1348,11 @@ bool Blockchain::prevalidate_miner_transaction(const block& b, uint64_t height,
CHECK_AND_ASSERT_MES(b.miner_tx.vin[0].type() == typeid(txin_gen), false, "coinbase transaction in the block has the wrong type");
CHECK_AND_ASSERT_MES(b.miner_tx.version > 1, false, "Invalid coinbase transaction version");
if (hf_version >= HF_VERSION_CARROT) {
CHECK_AND_ASSERT_MES(b.miner_tx.version == TRANSACTION_VERSION_CARROT, false, "miner transaction has wrong version");
CHECK_AND_ASSERT_MES(b.miner_tx.type == cryptonote::transaction_type::MINER, false, "miner transaction has wrong type");
}
// for v2 txes (ringct), we only accept empty rct signatures for miner transactions,
if (hf_version >= HF_VERSION_REJECT_SIGS_IN_COINBASE && b.miner_tx.version >= 2)
{
@@ -1398,6 +1403,21 @@ 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");
// 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_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,
if (hf_version >= HF_VERSION_REJECT_SIGS_IN_COINBASE && b.protocol_tx.version >= 2)
{
@@ -1418,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;
}
//------------------------------------------------------------------
@@ -1427,8 +1446,8 @@ std::tuple<bool, size_t> Blockchain::validate_treasury_payout(const transaction&
// find the treasury output
const auto [tx_key, onetime_address, anchor_enc, viewtag] = treasury_data;
//const auto expected_output_key = std::get<1>(treasury_output_data);
const auto &output = std::find_if(tx.vout.begin(), tx.vout.end(), [&onetime_address](const tx_out &o) {
const auto &oa = onetime_address; // Alias to support capture compatibility on MacOS
const auto &output = std::find_if(tx.vout.begin(), tx.vout.end(), [&oa](const tx_out &o) {
std::string output_key;
if (o.target.type() == typeid(txout_to_carrot_v1)) {
output_key = epee::string_tools::pod_to_hex(boost::get<txout_to_carrot_v1>(o.target).key);
@@ -1436,7 +1455,7 @@ std::tuple<bool, size_t> Blockchain::validate_treasury_payout(const transaction&
return false;
}
return output_key == onetime_address;
return output_key == oa;
});
if (output == tx.vout.end()) {
@@ -1563,6 +1582,7 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height,
// Get the staking data for the block that matured this time
cryptonote::yield_block_info ybi_matured;
std::vector<std::pair<yield_tx_info, uint64_t>> yield_payouts;
std::vector<std::pair<yield_tx_info_carrot, uint64_t>> carrot_yield_payouts;
uint64_t matured_height = height - stake_lock_period - 1;
bool ok = get_ybi_entry(matured_height, ybi_matured);
if (!ok) {
@@ -1571,10 +1591,17 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height,
} else if (ybi_matured.locked_coins_this_block == 0) {
LOG_PRINT_L1("Block at height: " << height << " - no yield payouts due - skipping");
} else {
// Iterate over the cached data for block yield, calculating the yield payouts due
if (!calculate_yield_payouts(matured_height, yield_payouts)) {
LOG_ERROR("Block at height: " << height << " - Failed to obtain yield payout information - aborting");
return false;
// Iterate over the cached data for block yield, calculating the yield payouts due
if (get_ideal_hard_fork_version(matured_height) >= HF_VERSION_CARROT) {
if (!calculate_yield_payouts(matured_height, carrot_yield_payouts)) {
LOG_ERROR("Block at height: " << height << " - Failed to obtain carrot yield payout information - aborting");
return false;
}
} else {
if (!calculate_yield_payouts(matured_height, yield_payouts)) {
LOG_ERROR("Block at height: " << height << " - Failed to obtain yield payout information - aborting");
return false;
}
}
}
@@ -1607,7 +1634,56 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height,
}
// Check we have the correct number of entries
CHECK_AND_ASSERT_MES(b.protocol_tx.vout.size() == yield_payouts.size() + audit_payouts.size(), false, "Invalid number of outputs in protocol_tx - aborting");
CHECK_AND_ASSERT_MES(
b.protocol_tx.vout.size() == yield_payouts.size() + audit_payouts.size() + carrot_yield_payouts.size(),
false, "Invalid number of outputs in protocol_tx - aborting"
);
if (hf_version >= HF_VERSION_CARROT) {
size_t output_idx = 0;
for (auto it = carrot_yield_payouts.begin(); it != carrot_yield_payouts.end(); it++, output_idx++) {
// Verify the output key
crypto::public_key out_key;
cryptonote::get_output_public_key(b.protocol_tx.vout[output_idx], out_key);
CHECK_AND_ASSERT_MES(out_key == it->first.return_address, false, "Incorrect output key detected in protocol_tx");
// Verify the return pubkey
if (b.protocol_tx.vout.size() > 1) {
const auto additional_pubkeys = cryptonote::get_additional_tx_pub_keys_from_extra(b.protocol_tx.extra);
CHECK_AND_ASSERT_MES(additional_pubkeys.size() > output_idx, false, "Missing return pubkey detected in protocol_tx");
CHECK_AND_ASSERT_MES(additional_pubkeys[output_idx] == it->first.return_pubkey, false, "Incorrect return pubkey detected in protocol_tx");
} else {
const auto main_pubkey = cryptonote::get_tx_pub_key_from_extra(b.protocol_tx.extra);
CHECK_AND_ASSERT_MES(main_pubkey == it->first.return_pubkey, false, "Incorrect return pubkey detected in protocol_tx");
}
// Verify the output amount
uint64_t expected_amount = it->second;
CHECK_AND_ASSERT_MES(b.protocol_tx.vout[output_idx].amount == expected_amount, false, "Incorrect output amount detected in protocol_tx. expected_amount: " << expected_amount);
// Verify the output asset type
std::string out_asset_type;
cryptonote::get_output_asset_type(b.protocol_tx.vout[output_idx], out_asset_type);
CHECK_AND_ASSERT_MES(out_asset_type == "SAL1", false, "Incorrect output asset_type (!= SAL1) detected in protocol_tx");
// Verify the view tag
CHECK_AND_ASSERT_MES(
boost::get<cryptonote::txout_to_carrot_v1>(
b.protocol_tx.vout[output_idx].target
).view_tag == it->first.return_view_tag, false, "Incorrect view tag detected in protocol_tx"
);
// Verify the anchor encrypted
CHECK_AND_ASSERT_MES(
boost::get<cryptonote::txout_to_carrot_v1>(
b.protocol_tx.vout[output_idx].target
).encrypted_janus_anchor == it->first.return_anchor_enc, false, "Incorrect anchor detected in protocol_tx"
);
}
return true;
}
// Merge the yield and audit payouts into an iterable vector
std::vector<std::pair<yield_tx_info, uint64_t>> payouts{yield_payouts};
@@ -1866,6 +1942,15 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
b.timestamp = median_ts;
}
// Verify that we aren't mixing Carrot and non-Carrot
if (b.major_version >= HF_VERSION_CARROT && !miner_address.m_is_carrot) {
LOG_ERROR("mining to CryptoNote wallet address, but Carrot wallet address is required");
return false;
} else if (b.major_version < HF_VERSION_CARROT && miner_address.m_is_carrot) {
LOG_ERROR("mining to Carrot wallet address, but Carrot isn't supported yet");
return false;
}
std::map<std::string, uint64_t> circ_supply = get_db().get_circulating_supply();
// Check if we are supposed to be obtaining PRs from the Oracle
@@ -1905,36 +1990,69 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
cryptonote::yield_block_info ybi_matured;
bool ok = get_ybi_entry(start_height, ybi_matured);
if (ok && ybi_matured.locked_coins_this_block > 0) {
// Iterate over the cached data for block yield, calculating the yield payouts due
std::vector<std::pair<yield_tx_info, uint64_t>> yield_payouts;
if (!calculate_yield_payouts(start_height, yield_payouts)) {
LOG_ERROR("Failed to obtain yield payout information - aborting");
return false;
}
// Work out what the asset_type should be based on height of submission
uint8_t hf_submitted = m_hardfork->get_ideal_version(start_height);
// Create the protocol_metadata entries here
for (const auto& yield_entry: yield_payouts) {
cryptonote::protocol_data_entry entry;
entry.amount_burnt = yield_entry.second;
entry.amount_minted = 0;
entry.amount_slippage_limit = 0;
if (hf_submitted >= HF_VERSION_SALVIUM_ONE_PROOFS) {
entry.source_asset = "SAL1";
entry.destination_asset = "SAL1";
} else {
entry.source_asset = "SAL";
entry.destination_asset = "SAL";
// Iterate over the cached data for block yield, calculating the yield payouts due
std::vector<std::pair<yield_tx_info, uint64_t>> yield_payouts;
std::vector<std::pair<yield_tx_info_carrot, uint64_t>> carrot_yield_payouts;
if (hf_submitted >= HF_VERSION_CARROT) {
if (!calculate_yield_payouts(start_height, carrot_yield_payouts)) {
LOG_ERROR("Failed to obtain yield payout information - aborting");
return false;
}
} else {
if (!calculate_yield_payouts(start_height, yield_payouts)) {
LOG_ERROR("Failed to obtain yield payout information - aborting");
return false;
}
}
// Create the protocol_metadata entries here
if (!carrot_yield_payouts.empty()) {
for (const auto& yield_entry: carrot_yield_payouts) {
cryptonote::protocol_data_entry entry;
entry.amount_burnt = yield_entry.second;
entry.amount_minted = 0;
entry.amount_slippage_limit = 0;
if (hf_submitted >= HF_VERSION_SALVIUM_ONE_PROOFS) {
entry.source_asset = "SAL1";
entry.destination_asset = "SAL1";
} else {
entry.source_asset = "SAL";
entry.destination_asset = "SAL";
}
entry.return_address = yield_entry.first.return_address;
entry.type = cryptonote::transaction_type::STAKE;
entry.return_pubkey = yield_entry.first.return_pubkey;
entry.origin_height = start_height;
entry.return_view_tag = yield_entry.first.return_view_tag;
entry.return_anchor_enc = yield_entry.first.return_anchor_enc;
entry.is_carrot = true;
protocol_entries.push_back(entry);
}
} else {
for (const auto& yield_entry: yield_payouts) {
cryptonote::protocol_data_entry entry;
entry.amount_burnt = yield_entry.second;
entry.amount_minted = 0;
entry.amount_slippage_limit = 0;
if (hf_submitted >= HF_VERSION_SALVIUM_ONE_PROOFS) {
entry.source_asset = "SAL1";
entry.destination_asset = "SAL1";
} else {
entry.source_asset = "SAL";
entry.destination_asset = "SAL";
}
entry.return_address = yield_entry.first.return_address;
entry.type = cryptonote::transaction_type::STAKE;
entry.P_change = yield_entry.first.P_change;
entry.return_pubkey = yield_entry.first.return_pubkey;
entry.origin_height = start_height;
entry.is_carrot = false;
protocol_entries.push_back(entry);
}
entry.return_address = yield_entry.first.return_address;
entry.type = cryptonote::transaction_type::STAKE;
entry.P_change = yield_entry.first.P_change;
entry.return_pubkey = yield_entry.first.return_pubkey;
entry.origin_height = start_height;
protocol_entries.push_back(entry);
}
}
@@ -1978,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);
}
}
@@ -2355,7 +2474,7 @@ bool Blockchain::handle_alternative_block(const block& b, const crypto::hash& id
if(!prevalidate_protocol_transaction(b, bei.height, hf_version))
{
MERROR_VER("Block with id: " << epee::string_tools::pod_to_hex(id) << " (as alternative) has incorrect miner transaction.");
MERROR_VER("Block with id: " << epee::string_tools::pod_to_hex(id) << " (as alternative) has incorrect protocol transaction.");
bvc.m_verifivation_failed = true;
return false;
}
@@ -3608,7 +3727,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
*/
if (hf_version >= HF_VERSION_CARROT) {
// from v11, force the new SalviumOne RCT data
// from v10, force the new SalviumOne RCT data
if (tx.type == cryptonote::transaction_type::TRANSFER || tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::BURN || tx.type == cryptonote::transaction_type::CONVERT || tx.type == cryptonote::transaction_type::AUDIT) {
if (tx.rct_signatures.type != rct::RCTTypeSalviumOne) {
MERROR_VER("SalviumOne data required after v" + std::to_string(HF_VERSION_CARROT));
@@ -3683,7 +3802,7 @@ bool Blockchain::check_tx_type_and_version(const transaction& tx, tx_verificatio
}
// Check for v1 TXs - genesis block protocol_tx exception required!
if (tx.version == 1 && epee::string_tools::pod_to_hex(cryptonote::get_transaction_hash(tx)) == "4f78ff511e860acd03138737a71505eb62eb78b620e180e58c8e13ed0e1e3e19") {
if (tx.version == 1) {
MERROR("v1 TXs are not permitted");
tvc.m_version_mismatch = true;
return false;
@@ -3691,7 +3810,7 @@ bool Blockchain::check_tx_type_and_version(const transaction& tx, tx_verificatio
}
// After v2 allow N-out TXs for TRANSFER ONLY
if (hf_version >= HF_VERSION_ENABLE_N_OUTS) {
if (hf_version >= HF_VERSION_ENABLE_N_OUTS && hf_version < HF_VERSION_CARROT) {
if (tx.version >= TRANSACTION_VERSION_N_OUTS && tx.type != cryptonote::transaction_type::TRANSFER) {
MERROR("N-out TXs are only permitted for TRANSFER TX type");
tvc.m_version_mismatch = true;
@@ -3699,6 +3818,14 @@ bool Blockchain::check_tx_type_and_version(const transaction& tx, tx_verificatio
}
}
if (hf_version >= HF_VERSION_CARROT) {
if (tx.version != TRANSACTION_VERSION_CARROT) {
MERROR("TX version " + std::to_string(tx.version) + " is not supported, expected " + std::to_string(TRANSACTION_VERSION_CARROT));
tvc.m_version_mismatch = true;
return false;
}
}
// Make sure CONVERT TXs are disabled until we are ready - belt and braces!
if (hf_version < HF_VERSION_ENABLE_CONVERT) {
if (tx.type == cryptonote::transaction_type::CONVERT) {
@@ -3708,6 +3835,32 @@ bool Blockchain::check_tx_type_and_version(const transaction& tx, tx_verificatio
}
}
if (tx.type == cryptonote::transaction_type::AUDIT) {
// Make sure we are supposed to accept AUDIT txs at this point
const std::map<uint8_t, std::pair<uint64_t, std::pair<std::string, std::string>>> audit_hard_forks = get_config(m_nettype).AUDIT_HARD_FORKS;
CHECK_AND_ASSERT_MES(audit_hard_forks.find(hf_version) != audit_hard_forks.end(), false, "trying to audit outside an audit fork");
std::string expected_asset_type = audit_hard_forks.at(hf_version).second.first;
CHECK_AND_ASSERT_MES(tx.source_asset_type == expected_asset_type, false, "trying to spend " << tx.source_asset_type << " coins in an AUDIT TX");
} else {
if (hf_version >= HF_VERSION_SALVIUM_ONE_PROOFS) {
CHECK_AND_ASSERT_MES(tx.source_asset_type == "SAL1", false, "trying to spend " << tx.source_asset_type << " coins in a non-AUDIT TX");
} else {
CHECK_AND_ASSERT_MES(tx.source_asset_type == "SAL", false, "trying to spend " << tx.source_asset_type << " coins in a non-AUDIT TX");
}
}
if (tx.type == cryptonote::transaction_type::BURN) {
CHECK_AND_ASSERT_MES(tx.destination_asset_type == "BURN", false, "incorrect burn tx destination type:" << tx.destination_asset_type);
} else {
if (tx.source_asset_type != tx.destination_asset_type) {
MERROR_VER("Tx " << get_transaction_hash(tx) << " has mismatched asset types: " << tx.source_asset_type << " != " << tx.destination_asset_type);
tvc.m_verifivation_failed = true;
return false;
}
}
// Check for invalid TX types
if (tx.type == cryptonote::transaction_type::UNSET || tx.type > cryptonote::transaction_type::MAX) {
MERROR("TX type `" + std::to_string(tx.type) + "' is not supported");
@@ -3742,7 +3895,7 @@ bool Blockchain::have_tx_keyimges_as_spent(const transaction &tx) const
bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_prefix_hash, const std::vector<std::vector<rct::ctkey>> &pubkeys, const uint8_t &hf_version)
{
PERF_TIMER(expand_transaction_2);
CHECK_AND_ASSERT_MES(tx.version == 2 || tx.version == 3, false, "Transaction version is not 2/3");
CHECK_AND_ASSERT_MES(tx.version == 2 || tx.version == 3 || tx.version == 4, false, "Transaction version is not 2/3/4");
rct::rctSig &rv = tx.rct_signatures;
@@ -3941,14 +4094,6 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
return false;
}
// min/max tx version based on HF, and we accept v1 txes if having a non mixable
const size_t max_tx_version = (hf_version >= HF_VERSION_ENABLE_N_OUTS) ? TRANSACTION_VERSION_N_OUTS : TRANSACTION_VERSION_2_OUTS;
if (tx.version > max_tx_version)
{
MERROR_VER("transaction version " << (unsigned)tx.version << " is higher than max accepted version " << max_tx_version);
tvc.m_verifivation_failed = true;
return false;
}
const size_t min_tx_version = (n_unmixable > 0 ? 1 : 2);
if (tx.version < min_tx_version)
{
@@ -3979,20 +4124,6 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
}
}
if (tx.type == cryptonote::transaction_type::AUDIT) {
// Make sure we are supposed to accept AUDIT txs at this point
const std::map<uint8_t, std::pair<uint64_t, std::pair<std::string, std::string>>> audit_hard_forks = get_config(m_nettype).AUDIT_HARD_FORKS;
CHECK_AND_ASSERT_MES(audit_hard_forks.find(hf_version) != audit_hard_forks.end(), false, "trying to audit outside an audit fork");
std::string expected_asset_type = audit_hard_forks.at(hf_version).second.first;
CHECK_AND_ASSERT_MES(tx.source_asset_type == expected_asset_type, false, "trying to spend " << tx.source_asset_type << " coins in an AUDIT TX");
} else {
if (hf_version >= HF_VERSION_SALVIUM_ONE_PROOFS) {
CHECK_AND_ASSERT_MES(tx.source_asset_type == "SAL1", false, "trying to spend " << tx.source_asset_type << " coins in a non-AUDIT TX");
} else {
CHECK_AND_ASSERT_MES(tx.source_asset_type == "SAL", false, "trying to spend " << tx.source_asset_type << " coins in a non-AUDIT TX");
}
}
std::vector<std::vector<rct::ctkey>> pubkeys(tx.vin.size());
std::vector < uint64_t > results;
results.resize(tx.vin.size(), 0);
@@ -4582,6 +4713,72 @@ bool Blockchain::get_abi_entry(const uint64_t height, cryptonote::audit_block_in
return true;
}
//------------------------------------------------------------------
bool Blockchain::calculate_yield_payouts(const uint64_t start_height, std::vector<std::pair<yield_tx_info_carrot, uint64_t>>& yield_container)
{
LOG_PRINT_L3("Blockchain::" << __func__);
// Clear the yield payout amounts
yield_container.clear();
// 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);
if (!yield_entries.size()) {
// Report error and abort
LOG_ERROR("calculate_yield_payouts() called for carrot, but no yield TXs found at height " << start_height << " - aborting");
return false;
}
// Build a blacklist of staking TXs _not_ to pay out for
const std::set<std::string> txs_blacklist = {};
// Get the YBI information for the 21,600 blocks that the matured TX(s), we can calculate yield
for (const auto& entry: yield_entries) {
// Check to see if this entry is in the blacklist
if (txs_blacklist.find(epee::string_tools::pod_to_hex(entry.tx_hash)) != txs_blacklist.end()) {
LOG_ERROR("calculate_yield_payouts() found blacklisted TX at height " << start_height << " - skipping payout");
continue;
}
yield_container.emplace_back(std::make_pair(entry, entry.locked_coins));
}
// Iterate over the cached yield_block_info data
uint64_t yield_lock_period = cryptonote::get_config(m_nettype).STAKE_LOCK_PERIOD;
for (uint64_t idx = start_height+1; idx <= start_height + yield_lock_period; ++idx) {
// Get the next block
if (m_yield_block_info_cache.count(idx) == 0) {
LOG_ERROR("failed to locate yield information for block height " << idx <<" - aborting");
return false;
}
yield_block_info ybi = m_yield_block_info_cache[idx];
if (ybi.slippage_total_this_block == 0) continue;
if (ybi.locked_coins_tally == 0) continue;
boost::multiprecision::int128_t slippage_128 = ybi.slippage_total_this_block;
// Get the total number of coins locked at this height
boost::multiprecision::int128_t locked_total_128 = ybi.locked_coins_tally;
// Iterate over the yield_container, adding each proportion of the yield
for (auto& entry: yield_container) {
boost::multiprecision::int128_t locked_coins_128 = entry.first.locked_coins;
boost::multiprecision::int128_t yield_128 = (slippage_128 * locked_coins_128) / locked_total_128;
uint64_t yield_u64 = boost::numeric_cast<uint64_t>(yield_128);
if (entry.second + yield_u64 < entry.second) {
throw std::overflow_error("uint64_t addition overflow");
}
entry.second += yield_u64;
}
}
// Return success to caller
return true;
}
//------------------------------------------------------------------
//------------------------------------------------------------------
bool Blockchain::calculate_yield_payouts(const uint64_t start_height, std::vector<std::pair<yield_tx_info, uint64_t>>& yield_container)
{
LOG_PRINT_L3("Blockchain::" << __func__);
@@ -4632,10 +4829,14 @@ bool Blockchain::calculate_yield_payouts(const uint64_t start_height, std::vecto
// Iterate over the yield_container, adding each proportion of the yield
for (auto& entry: yield_container) {
boost::multiprecision::int128_t locked_coins_128 = entry.first.locked_coins;
boost::multiprecision::int128_t yield_128 = (slippage_128 * locked_coins_128) / locked_total_128;
entry.second += yield_128.convert_to<uint64_t>();
uint64_t yield_u64 = boost::numeric_cast<uint64_t>(yield_128);
if (entry.second + yield_u64 < entry.second) {
throw std::overflow_error("uint64_t addition overflow");
}
entry.second += yield_u64;
}
}
@@ -6313,7 +6514,7 @@ void Blockchain::cancel()
}
#if defined(PER_BLOCK_CHECKPOINT)
static const char expected_block_hashes_hash[] = "a2a5a9bc5d606392ac5c14be55b90a92b8577b8ffdac5c63cc6174f41764c753";
static const char expected_block_hashes_hash[] = "1cf6e8892e0512c246cef62610ccf524f30f484e307ae01959a5a7dd166aa328";
void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints)
{
if (get_checkpoints == nullptr || !m_fast_sync)
+7
View File
@@ -1175,6 +1175,13 @@ namespace cryptonote
*/
bool calculate_yield_payouts(const uint64_t start_height, std::vector<std::pair<yield_tx_info, uint64_t>>& yield_payouts);
/**
* calculate the yield payouts
*
* @return TRUE if the payouts were calculated successfully, FALSE otherwise
*/
bool calculate_yield_payouts(const uint64_t start_height, std::vector<std::pair<yield_tx_info_carrot, uint64_t>>& yield_payouts);
/**
* @brief get the ABI entry for a particular height from the cache
*
+4 -2
View File
@@ -843,7 +843,9 @@ namespace cryptonote
bad_semantics_txes_lock.unlock();
uint8_t hf_version = m_blockchain_storage.get_current_hard_fork_version();
const size_t max_tx_version = (hf_version >= HF_VERSION_ENABLE_N_OUTS) ? TRANSACTION_VERSION_N_OUTS : TRANSACTION_VERSION_2_OUTS;
const size_t max_tx_version = hf_version >= HF_VERSION_CARROT ? TRANSACTION_VERSION_CARROT :
hf_version >= HF_VERSION_ENABLE_N_OUTS ? TRANSACTION_VERSION_N_OUTS :
TRANSACTION_VERSION_2_OUTS;
if (tx.version == 0 || tx.version > max_tx_version)
{
// v2 is the latest one we know
@@ -1020,7 +1022,7 @@ namespace cryptonote
}
if (!rvv.empty())
{
LOG_PRINT_L1("One transaction among this group has bad semantics, verifying one at a time");
LOG_PRINT_L1("Verifying transactions one at a time");
ret = false;
for (size_t n = 0; n < tx_info.size(); ++n)
{
+48 -32
View File
@@ -346,14 +346,30 @@ namespace cryptonote
// Clear the TX contents
tx.set_null();
tx.version = 2;
bool carrot_found = false;
bool noncarrot_found = false;
tx.type = cryptonote::transaction_type::PROTOCOL;
// Force the TX type to 2
tx.version = 2;
const bool do_carrot = hard_fork_version >= HF_VERSION_CARROT;
if (do_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
@@ -362,32 +378,17 @@ namespace cryptonote
// Iterate over the protocol_data we received, creating an enote for each entry
for (auto const& entry: protocol_data) {
carrot::CarrotDestinationV1 destination;
carrot::make_carrot_main_address_v1(entry.P_change,
entry.return_address,
destination);
CHECK_AND_ASSERT_THROW_MES(!destination.is_subaddress,
"construct_protocol_tx: subaddress are not allowed in miner transactions");
CHECK_AND_ASSERT_THROW_MES(destination.payment_id == carrot::null_payment_id,
"construct_protocol_tx: integrated addresses are not allowed in miner transactions");
LOG_PRINT_L2(((entry.type == cryptonote::transaction_type::STAKE) ? "Yield TX payout submitted " : "Audit TX payout submitted ") << entry.amount_burnt << entry.source_asset);
const carrot::CarrotPaymentProposalV1 payment_proposal{
.destination = destination,
.amount = entry.amount_burnt,
.asset_type = "SAL1",
.randomness = carrot::gen_janus_anchor()
};
// Build the proposal
carrot::CarrotCoinbaseEnoteV1 e;
get_coinbase_output_proposal_v1(payment_proposal, height, e);
e.onetime_address = entry.return_address;
e.amount = entry.amount_burnt;
e.asset_type = entry.destination_asset;
e.view_tag = entry.return_view_tag;
e.anchor_enc = entry.return_anchor_enc;
e.block_index = height;
memcpy(e.enote_ephemeral_pubkey.data, entry.return_pubkey.data, sizeof(crypto::public_key));
enotes.push_back(e);
}
tx = store_carrot_to_coinbase_transaction_v1(enotes, std::string{}, cryptonote::transaction_type::PROTOCOL, height);
tx.amount_burnt = 0;
tx.invalidate_hashes();
@@ -410,14 +411,29 @@ namespace cryptonote
std::vector<crypto::public_key> additional_tx_public_keys;
for (auto const& entry: protocol_data) {
if (entry.type == cryptonote::transaction_type::STAKE) {
// PAYOUT
LOG_PRINT_L2("Yield TX payout submitted " << entry.amount_burnt << entry.source_asset);
if (entry.is_carrot) {
tx_out out;
out.amount = entry.amount_burnt;
out.target = txout_to_carrot_v1 {
.key = entry.return_address,
.asset_type = entry.destination_asset,
.view_tag = entry.return_view_tag,
.encrypted_janus_anchor = entry.return_anchor_enc,
};
// 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);
additional_tx_public_keys.push_back(entry.return_pubkey);
tx.vout.push_back(out);
} 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);
}
} else if (entry.type == cryptonote::transaction_type::AUDIT) {
// PAYOUT
LOG_PRINT_L2("Audit TX payout submitted " << entry.amount_burnt << entry.source_asset);
@@ -68,7 +68,10 @@ namespace cryptonote
uint8_t type;
crypto::public_key P_change;
crypto::public_key return_pubkey;
carrot::view_tag_t return_view_tag;
carrot::encrypted_janus_anchor_t return_anchor_enc;
uint64_t origin_height;
bool is_carrot;
};
//---------------------------------------------------------------
+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;
@@ -187,7 +187,7 @@ bool ver_rct_non_semantics_simple_cached
// mixring. Future versions of the protocol may differ in this regard, but if this assumptions
// holds true in the future, enable the verification hash by modifying the `untested_tx`
// condition below.
const bool untested_tx = tx.version > 3 || tx.rct_signatures.type > rct::RCTTypeSalviumOne;
const bool untested_tx = tx.version > 4 || tx.rct_signatures.type > rct::RCTTypeSalviumOne;
VER_ASSERT(!untested_tx, "Unknown TX type. Make sure RCT cache works correctly with this type and then enable it in the code here.");
// Don't cache older (or newer) rctSig types
+10 -1
View File
@@ -374,6 +374,8 @@ bool t_command_parser_executor::start_mining(const std::vector<std::string>& arg
return true;
}
bool is_carrot = m_executor.current_hard_fork_version() >= HF_VERSION_CARROT;
cryptonote::address_parse_info info;
cryptonote::network_type nettype = cryptonote::MAINNET;
if(!cryptonote::get_account_address_from_str(info, cryptonote::MAINNET, args.front()))
@@ -420,6 +422,13 @@ bool t_command_parser_executor::start_mining(const std::vector<std::string>& arg
tools::fail_msg_writer() << "subaddress for mining reward is not yet supported!" << std::endl;
return true;
}
if (info.is_carrot && !is_carrot) {
tools::fail_msg_writer() << "mining to Carrot wallet address, but Carrot isn't supported yet" << std::endl;
return true;
} else if (!info.is_carrot && is_carrot) {
tools::fail_msg_writer() << "mining to CryptoNote wallet address, but Carrot wallet address is required" << std::endl;
return true;
}
if(nettype != cryptonote::MAINNET)
std::cout << "Mining to a " << (nettype == cryptonote::TESTNET ? "testnet" : "stagenet") << " address, make sure this is intentional!" << std::endl;
uint64_t threads_count = 1;
@@ -470,7 +479,7 @@ bool t_command_parser_executor::start_mining(const std::vector<std::string>& arg
}
}
m_executor.start_mining(info.address, threads_count, nettype, do_background_mining, ignore_battery);
m_executor.start_mining(info.address, threads_count, nettype, do_background_mining, ignore_battery, info.is_carrot);
return true;
}
+30 -2
View File
@@ -1352,10 +1352,10 @@ bool t_rpc_command_executor::print_transaction_pool_stats() {
return true;
}
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 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);
req.miner_address = cryptonote::get_account_address_as_str(nettype, false, address, is_carrot);
req.threads_count = num_threads;
req.do_background_mining = do_background_mining;
req.ignore_battery = ignore_battery;
@@ -1650,6 +1650,34 @@ bool t_rpc_command_executor::in_peers(bool set, uint32_t limit)
return true;
}
uint8_t t_rpc_command_executor::current_hard_fork_version()
{
cryptonote::COMMAND_RPC_HARD_FORK_INFO::request req;
cryptonote::COMMAND_RPC_HARD_FORK_INFO::response res;
std::string fail_message = "Unsuccessful";
epee::json_rpc::error error_resp;
req.version = 0;
if (m_is_rpc)
{
if (!m_rpc_client->json_rpc_request(req, res, "hard_fork_info", fail_message.c_str()))
{
return 0;
}
}
else
{
if (!m_rpc_server->on_hard_fork_info(req, res, error_resp) || res.status != CORE_RPC_STATUS_OK)
{
tools::fail_msg_writer() << make_error(fail_message, res.status);
return 0;
}
}
return res.version;
}
bool t_rpc_command_executor::hard_fork_info(uint8_t version)
{
cryptonote::COMMAND_RPC_HARD_FORK_INFO::request req;
+3 -1
View File
@@ -106,7 +106,7 @@ public:
bool print_transaction_pool_stats();
bool start_mining(cryptonote::account_public_address address, uint64_t num_threads, cryptonote::network_type nettype, bool do_background_mining = false, bool ignore_battery = false);
bool start_mining(cryptonote::account_public_address address, uint64_t num_threads, cryptonote::network_type nettype, bool do_background_mining = false, bool ignore_battery = false, bool is_carrot = false);
bool stop_mining();
@@ -128,6 +128,8 @@ public:
bool in_peers(bool set, uint32_t limit);
uint8_t current_hard_fork_version();
bool hard_fork_info(uint8_t version);
bool print_bans();
+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);
+3 -3
View File
@@ -733,9 +733,9 @@ namespace nodetool
std::set<std::string> full_addrs;
if (m_nettype == cryptonote::TESTNET)
{
full_addrs.insert("72.5.43.63:29080");
full_addrs.insert("195.85.114.217:29080");
full_addrs.insert("206.188.197.72:29080");
full_addrs.insert("165.22.6.67:29080");
full_addrs.insert("165.22.15.162:29080");
full_addrs.insert("162.243.163.207:29080");
}
else if (m_nettype == cryptonote::STAGENET)
{
+3 -2
View File
@@ -446,7 +446,7 @@ namespace rct {
return false;
for (size_t i = 0; i < outputs; ++i)
{
if (type == RCTTypeBulletproofPlus || type == RCTTypeFullProofs || type == RCTTypeSalviumZero)
if (type == RCTTypeBulletproofPlus || type == RCTTypeFullProofs || type == RCTTypeSalviumZero || type == RCTTypeSalviumOne)
{
// Since RCTTypeBulletproof2 enote types, we don't serialize the blinding factor, and only serialize the
// first 8 bytes of ecdhInfo[i].amount
@@ -504,7 +504,7 @@ namespace rct {
FIELD(outPk)
VARINT_FIELD(txnFee)
FIELD(p_r)
if (type == RCTTypeSalviumZero)
if (type == RCTTypeSalviumZero || type == RCTTypeSalviumOne)
{
FIELD(salvium_data)
}
@@ -673,6 +673,7 @@ namespace rct {
FIELD(bulletproofs_plus)
FIELD(MGs)
FIELD(CLSAGs)
FIELD(TCLSAGs)
FIELD(pseudoOuts)
END_SERIALIZE()
};
+23 -1
View File
@@ -1432,7 +1432,7 @@ namespace cryptonote
RPC_TRACKER(start_mining);
CHECK_CORE_READY();
cryptonote::address_parse_info info;
if(!get_account_address_from_str(info, nettype(), req.miner_address))
if(!get_account_address_from_str(info, m_core.get_nettype(), req.miner_address))
{
res.status = "Failed, wrong address";
LOG_PRINT_L0(res.status);
@@ -1445,6 +1445,17 @@ namespace cryptonote
return true;
}
const uint8_t version = m_core.get_blockchain_storage().get_current_hard_fork_version();
if (info.is_carrot && version < HF_VERSION_CARROT) {
res.status = "Mining to Carrot wallet address, but Carrot isn't supported yet";
LOG_PRINT_L0(res.status);
return true;
} else if (!info.is_carrot && version >= HF_VERSION_CARROT) {
res.status = "Mining to CryptoNote wallet address, but Carrot wallet address is required";
LOG_PRINT_L0(res.status);
return true;
}
unsigned int concurrency_count = boost::thread::hardware_concurrency() * 4;
// if we couldn't detect threads, set it to a ridiculously high number
@@ -1958,6 +1969,17 @@ namespace cryptonote
return false;
}
const uint8_t version = m_core.get_blockchain_storage().get_current_hard_fork_version();
if (info.is_carrot && version < HF_VERSION_CARROT) {
error_resp.code = CORE_RPC_ERROR_CODE_INVALID_CLIENT;
error_resp.message = "Mining to Carrot wallet address, but Carrot isn't supported yet";
return false;
} else if (!info.is_carrot && version >= HF_VERSION_CARROT) {
error_resp.code = CORE_RPC_ERROR_CODE_INVALID_CLIENT;
error_resp.message = "Mining to CryptoNote wallet address, but Carrot wallet address is required";
return false;
}
block b;
cryptonote::blobdata blob_reserve;
size_t reserved_offset;
+3
View File
@@ -38,6 +38,7 @@
#include "crypto/crypto.h"
#include "crypto/hash.h"
#include "mx25519.h"
#include "carrot_core/core_types.h"
// read
template <template <bool> class Archive>
@@ -85,6 +86,7 @@ BLOB_SERIALIZER(crypto::public_key);
BLOB_SERIALIZER(crypto::secret_key);
BLOB_SERIALIZER(crypto::key_derivation);
BLOB_SERIALIZER(crypto::key_image);
BLOB_SERIALIZER(carrot::input_context_t);
BLOB_SERIALIZER(crypto::signature);
BLOB_SERIALIZER(crypto::view_tag);
BLOB_SERIALIZER(crypto::ec_point);
@@ -95,6 +97,7 @@ VARIANT_TAG(debug_archive, crypto::public_key, "public_key");
VARIANT_TAG(debug_archive, crypto::secret_key, "secret_key");
VARIANT_TAG(debug_archive, crypto::key_derivation, "key_derivation");
VARIANT_TAG(debug_archive, crypto::key_image, "key_image");
VARIANT_TAG(debug_archive, carrot::input_context_t, "input_context");
VARIANT_TAG(debug_archive, crypto::signature, "signature");
VARIANT_TAG(debug_archive, crypto::view_tag, "view_tag");
VARIANT_TAG(debug_archive, crypto::ec_point, "ec_point");
+43 -4
View File
@@ -277,8 +277,14 @@ void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::t
INSERT_INTO_JSON_OBJECT(dest, return_address_list, tx.return_address_list);
INSERT_INTO_JSON_OBJECT(dest, return_address_change_mask, tx.return_address_change_mask);
} else {
INSERT_INTO_JSON_OBJECT(dest, return_address, tx.return_address);
INSERT_INTO_JSON_OBJECT(dest, return_pubkey, tx.return_pubkey);
if (tx.type == cryptonote::transaction_type::STAKE &&
tx.version >= TRANSACTION_VERSION_CARROT)
{
INSERT_INTO_JSON_OBJECT(dest, protocol_tx_data, tx.protocol_tx_data);
} else {
INSERT_INTO_JSON_OBJECT(dest, return_address, tx.return_address);
INSERT_INTO_JSON_OBJECT(dest, return_pubkey, tx.return_pubkey);
}
}
INSERT_INTO_JSON_OBJECT(dest, source_asset_type, tx.source_asset_type);
INSERT_INTO_JSON_OBJECT(dest, destination_asset_type, tx.destination_asset_type);
@@ -320,8 +326,14 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::transaction& tx)
GET_FROM_JSON_OBJECT(val, tx.return_address_list, return_address_list);
GET_FROM_JSON_OBJECT(val, tx.return_address_change_mask, return_address_change_mask);
} else {
GET_FROM_JSON_OBJECT(val, tx.return_address, return_address);
GET_FROM_JSON_OBJECT(val, tx.return_pubkey, return_pubkey);
if (tx.type == cryptonote::transaction_type::STAKE &&
tx.version >= TRANSACTION_VERSION_CARROT)
{
GET_FROM_JSON_OBJECT(val, tx.protocol_tx_data, protocol_tx_data);
} else {
GET_FROM_JSON_OBJECT(val, tx.return_address, return_address);
GET_FROM_JSON_OBJECT(val, tx.return_pubkey, return_pubkey);
}
}
GET_FROM_JSON_OBJECT(val, tx.source_asset_type, source_asset_type);
GET_FROM_JSON_OBJECT(val, tx.destination_asset_type, destination_asset_type);
@@ -506,6 +518,33 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_scripthash&
}
}
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::protocol_tx_data_t& ptd)
{
dest.StartObject();
INSERT_INTO_JSON_OBJECT(dest, version, ptd.version);
INSERT_INTO_JSON_OBJECT(dest, return_address, ptd.return_address);
INSERT_INTO_JSON_OBJECT(dest, return_pubkey, ptd.return_pubkey);
INSERT_INTO_JSON_OBJECT(dest, return_view_tag, ptd.return_view_tag);
INSERT_INTO_JSON_OBJECT(dest, return_anchor_enc, ptd.return_anchor_enc);
dest.EndObject();
}
void fromJsonValue(const rapidjson::Value& val, cryptonote::protocol_tx_data_t& ptd)
{
if (!val.IsObject())
{
throw WRONG_TYPE("json object");
}
GET_FROM_JSON_OBJECT(val, ptd.version, version);
GET_FROM_JSON_OBJECT(val, ptd.return_address, return_address);
GET_FROM_JSON_OBJECT(val, ptd.return_pubkey, return_pubkey);
GET_FROM_JSON_OBJECT(val, ptd.return_view_tag, return_view_tag);
GET_FROM_JSON_OBJECT(val, ptd.return_anchor_enc, return_anchor_enc);
}
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txin_to_key& txin)
{
dest.StartObject();
+3
View File
@@ -218,6 +218,9 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_scripthash&
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txin_to_key& txin);
void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_key& txin);
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::protocol_tx_data_t& ptd);
void fromJsonValue(const rapidjson::Value& val, cryptonote::protocol_tx_data_t& ptd);
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txout_target_v& txout);
void fromJsonValue(const rapidjson::Value& val, cryptonote::txout_target_v& txout);
+199 -47
View File
@@ -192,6 +192,7 @@ namespace
const command_line::arg_descriptor<std::string> arg_generate_new_wallet = {"generate-new-wallet", sw::tr("Generate new wallet and save it to <arg>"), ""};
const command_line::arg_descriptor<std::string> arg_generate_from_device = {"generate-from-device", sw::tr("Generate new wallet from device and save it to <arg>"), ""};
const command_line::arg_descriptor<std::string> arg_generate_from_view_key = {"generate-from-view-key", sw::tr("Generate incoming-only wallet from view key"), ""};
const command_line::arg_descriptor<std::string> arg_generate_from_svb_key = {"generate-from-svb-key", sw::tr("Generate full view-only wallet from view key"), ""};
const command_line::arg_descriptor<std::string> arg_generate_from_spend_key = {"generate-from-spend-key", sw::tr("Generate deterministic wallet from spend key"), ""};
const command_line::arg_descriptor<std::string> arg_generate_from_keys = {"generate-from-keys", sw::tr("Generate wallet from private keys"), ""};
const command_line::arg_descriptor<std::string> arg_generate_from_multisig_keys = {"generate-from-multisig-keys", sw::tr("Generate a master wallet from multisig wallet keys"), ""};
@@ -315,7 +316,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> ...]");
@@ -868,12 +869,21 @@ bool simple_wallet::carrot_keys(const std::vector<std::string> &args/* = std::ve
printf("master secret: ");
print_secret_key(m_wallet->get_account().get_keys().s_master);
putchar('\n');
printf("view-received secret: ");
print_secret_key(m_wallet->get_account().get_keys().m_view_secret_key);
printf("prove-spend key: ");
print_secret_key(m_wallet->get_account().get_keys().k_prove_spend);
putchar('\n');
printf("view-all secret: ");
printf("view-balance secret: ");
print_secret_key(m_wallet->get_account().get_keys().s_view_balance);
putchar('\n');
printf("generate-image key: ");
print_secret_key(m_wallet->get_account().get_keys().k_generate_image);
putchar('\n');
printf("view-incoming key: ");
print_secret_key(m_wallet->get_account().get_keys().k_view_incoming);
putchar('\n');
printf("generate-address secret: ");
print_secret_key(m_wallet->get_account().get_keys().s_generate_address);
putchar('\n');
}
// TODO: print the public wallet address for the different wallet tiers
//std::cout << "public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_view_public_key) << std::endl;
@@ -894,7 +904,8 @@ bool simple_wallet::viewkey(const std::vector<std::string> &args/* = std::vector
print_secret_key(m_wallet->get_account().get_keys().m_view_secret_key);
putchar('\n');
}
std::cout << "public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_view_public_key) << std::endl;
std::cout << "CN public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_view_public_key) << std::endl;
std::cout << "Carrot public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_carrot_account_address.m_view_public_key) << std::endl;
return true;
}
@@ -917,7 +928,8 @@ bool simple_wallet::spendkey(const std::vector<std::string> &args/* = std::vecto
print_secret_key(m_wallet->get_account().get_keys().m_spend_secret_key);
putchar('\n');
}
std::cout << "public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_spend_public_key) << std::endl;
std::cout << "CN public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_spend_public_key) << std::endl;
std::cout << "Carrot public: " << string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_carrot_account_address.m_spend_public_key) << std::endl;
return true;
}
@@ -2547,6 +2559,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]))
@@ -2560,6 +2573,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
@@ -2574,7 +2606,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)
{
@@ -4413,12 +4446,12 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
bool welcome = false;
if((!m_generate_new.empty()) + (!m_wallet_file.empty()) + (!m_generate_from_device.empty()) + (!m_generate_from_view_key.empty()) + (!m_generate_from_spend_key.empty()) + (!m_generate_from_keys.empty()) + (!m_generate_from_multisig_keys.empty()) + (!m_generate_from_json.empty()) > 1)
if((!m_generate_new.empty()) + (!m_wallet_file.empty()) + (!m_generate_from_device.empty()) + (!m_generate_from_view_key.empty()) + (!m_generate_from_svb_key.empty()) + (!m_generate_from_spend_key.empty()) + (!m_generate_from_keys.empty()) + (!m_generate_from_multisig_keys.empty()) + (!m_generate_from_json.empty()) > 1)
{
fail_msg_writer() << tr("can't specify more than one of --generate-new-wallet=\"wallet_name\", --wallet-file=\"wallet_name\", --generate-from-view-key=\"wallet_name\", --generate-from-spend-key=\"wallet_name\", --generate-from-keys=\"wallet_name\", --generate-from-multisig-keys=\"wallet_name\", --generate-from-json=\"jsonfilename\" and --generate-from-device=\"wallet_name\"");
fail_msg_writer() << tr("can't specify more than one of --generate-new-wallet=\"wallet_name\", --wallet-file=\"wallet_name\", --generate-from-view-key=\"wallet_name\", --generate-from-svb-key=\"wallet_name\", --generate-from-spend-key=\"wallet_name\", --generate-from-keys=\"wallet_name\", --generate-from-multisig-keys=\"wallet_name\", --generate-from-json=\"jsonfilename\" and --generate-from-device=\"wallet_name\"");
return false;
}
else if (m_generate_new.empty() && m_wallet_file.empty() && m_generate_from_device.empty() && m_generate_from_view_key.empty() && m_generate_from_spend_key.empty() && m_generate_from_keys.empty() && m_generate_from_multisig_keys.empty() && m_generate_from_json.empty())
else if (m_generate_new.empty() && m_wallet_file.empty() && m_generate_from_device.empty() && m_generate_from_view_key.empty() && m_generate_from_svb_key.empty() && m_generate_from_spend_key.empty() && m_generate_from_keys.empty() && m_generate_from_multisig_keys.empty() && m_generate_from_json.empty())
{
if(!ask_wallet_create_if_needed()) return false;
}
@@ -4573,6 +4606,69 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
password = *r;
welcome = true;
}
else if (!m_generate_from_svb_key.empty())
{
m_wallet_file = m_generate_from_svb_key;
// parse address
std::string address_string = input_line("Standard address");
if (std::cin.eof())
return false;
if (address_string.empty()) {
fail_msg_writer() << tr("No data supplied, cancelled");
return false;
}
cryptonote::address_parse_info info;
if(!get_account_address_from_str(info, nettype, address_string))
{
fail_msg_writer() << tr("failed to parse address");
return false;
}
if (info.is_subaddress)
{
fail_msg_writer() << tr("This address is a subaddress which cannot be used here.");
return false;
}
if (!info.is_carrot)
{
fail_msg_writer() << tr("This address is not a Carrot address, and cannot be used here.");
return false;
}
// parse view secret key
epee::wipeable_string viewkey_string = input_secure_line("Secret view key");
if (std::cin.eof())
return false;
if (viewkey_string.empty()) {
fail_msg_writer() << tr("No data supplied, cancelled");
return false;
}
crypto::secret_key viewkey;
if (!viewkey_string.hex_to_pod(unwrap(unwrap(viewkey))))
{
fail_msg_writer() << tr("failed to parse view key secret key");
return false;
}
// Create all of the necessary keys for Carrot view-only wallet
/*
// check the view key matches the given address
crypto::public_key pkey;
if (!crypto::secret_key_to_public_key(viewkey, pkey)) {
fail_msg_writer() << tr("failed to verify view key secret key");
return false;
}
if (info.address.m_view_public_key != pkey) {
fail_msg_writer() << tr("view key does not match standard address");
return false;
}
*/
auto r = new_wallet(vm, info.address, boost::none, viewkey);
CHECK_AND_ASSERT_MES(r, false, tr("account creation failed"));
password = *r;
welcome = true;
}
else if (!m_generate_from_spend_key.empty())
{
m_wallet_file = m_generate_from_spend_key;
@@ -5038,6 +5134,7 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_
m_generate_new = command_line::get_arg(vm, arg_generate_new_wallet);
m_generate_from_device = command_line::get_arg(vm, arg_generate_from_device);
m_generate_from_view_key = command_line::get_arg(vm, arg_generate_from_view_key);
m_generate_from_svb_key = command_line::get_arg(vm, arg_generate_from_svb_key);
m_generate_from_spend_key = command_line::get_arg(vm, arg_generate_from_spend_key);
m_generate_from_keys = command_line::get_arg(vm, arg_generate_from_keys);
m_generate_from_multisig_keys = command_line::get_arg(vm, arg_generate_from_multisig_keys);
@@ -5053,6 +5150,7 @@ bool simple_wallet::handle_command_line(const boost::program_options::variables_
m_subaddress_lookahead = command_line::get_arg(vm, arg_subaddress_lookahead);
m_use_english_language_names = command_line::get_arg(vm, arg_use_english_language_names);
m_restoring = !m_generate_from_view_key.empty() ||
!m_generate_from_svb_key.empty() ||
!m_generate_from_spend_key.empty() ||
!m_generate_from_keys.empty() ||
!m_generate_from_multisig_keys.empty() ||
@@ -5227,16 +5325,17 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
try
{
recovery_val = m_wallet->generate(m_wallet_file, std::move(rc.second).password(), recovery_key, recover, two_random, create_address_file);
message_writer(console_color_white, true) << tr("Generated new legacy wallet: ")
message_writer(console_color_white, true) << tr("Generated new legacy (CN) wallet: ")
<< m_wallet->get_account().get_public_address_str(m_wallet->nettype());
const auto addr = m_wallet->get_account().cryptonote_address(carrot::null_payment_id, carrot::AddressDeriveType::Carrot);
cryptonote::account_public_address carrot_address {
.m_spend_public_key = addr.address_spend_pubkey,
.m_view_public_key = addr.address_view_pubkey
.m_view_public_key = addr.address_view_pubkey,
.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);
message_writer(console_color_white, true) << tr("Generated new Carrot wallet: ")
<< cryptonote::get_account_address_as_str(m_wallet->nettype(), false, carrot_address, true);
PAUSE_READLINE();
std::cout << tr("View key: ");
print_secret_key(m_wallet->get_account().get_keys().m_view_secret_key);
@@ -5275,8 +5374,9 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
}
//----------------------------------------------------------------------------------------------------
boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
const cryptonote::account_public_address& address, const boost::optional<crypto::secret_key>& spendkey,
const crypto::secret_key& viewkey)
const cryptonote::account_public_address& address,
const boost::optional<crypto::secret_key>& spendkey,
const crypto::secret_key& viewkey)
{
std::pair<std::unique_ptr<tools::wallet2>, tools::password_container> rc;
try { rc = tools::wallet2::make_new(vm, false, password_prompter); }
@@ -5473,11 +5573,11 @@ boost::optional<epee::wipeable_string> simple_wallet::open_wallet(const boost::p
else if (m_wallet->is_background_wallet())
prefix = tr("Opened background wallet");
else
prefix = tr("Opened legacy wallet");
prefix = tr("Opened legacy (CN) wallet");
message_writer(console_color_white, true) <<
prefix << ": " << m_wallet->get_account().get_public_address_str(m_wallet->nettype());
prefix = tr("Opened carrot wallet");
prefix = tr("Opened Carrot wallet");
message_writer(console_color_white, true) <<
prefix << ": " << m_wallet->get_account().get_carrot_public_address_str(m_wallet->nettype());
if (m_wallet->get_account().get_device()) {
@@ -6374,6 +6474,7 @@ bool simple_wallet::show_balance_unlocked(bool detailed)
const std::string tag = m_wallet->get_account_tags().second[m_current_subaddress_account];
success_msg_writer() << tr("Tag: ") << (tag.empty() ? std::string{tr("(No tag assigned)")} : tag);
std::vector<std::string> asset_types = m_wallet->list_asset_types();
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
for (auto& asset: asset_types) {
uint64_t blocks_to_unlock, time_to_unlock;
uint64_t unlocked_balance = m_wallet->unlocked_balance(m_current_subaddress_account, asset, false, &blocks_to_unlock, &time_to_unlock);
@@ -6392,15 +6493,17 @@ 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)
{
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_index).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;
@@ -7690,7 +7793,6 @@ bool simple_wallet::sweep_unmixable(const std::vector<std::string> &args_)
bool simple_wallet::sweep_main(uint32_t account, uint64_t below, bool locked, const std::vector<std::string> &args_)
{
CHECK_IF_BACKGROUND_SYNCING("cannot sweep");
std::string asset_type = (args_.size() > 1) ? args_.back() : (m_wallet->get_current_hard_fork() >= HF_VERSION_SALVIUM_ONE_PROOFS) ? "SAL1" : "SAL";
auto print_usage = [this, account, below]()
{
if (below)
@@ -7823,6 +7925,8 @@ bool simple_wallet::sweep_main(uint32_t account, uint64_t below, bool locked, co
}
}
std::string asset_type = (m_wallet->get_current_hard_fork() >= HF_VERSION_SALVIUM_ONE_PROOFS) ? "SAL1" : "SAL";
std::vector<uint8_t> extra;
bool payment_id_seen = false;
if (local_args.size() >= 2)
@@ -7914,8 +8018,8 @@ bool simple_wallet::sweep_main(uint32_t account, uint64_t below, bool locked, co
prompt << tr("WARNING: Outputs of multiple addresses are being used together, which might potentially compromise your privacy.\n");
}
if (!process_ring_members(ptx_vector, prompt, m_wallet->print_ring_members()))
return true;
// if (!process_ring_members(ptx_vector, prompt, m_wallet->print_ring_members()))
// return true;
if (ptx_vector.size() > 1) {
prompt << boost::format(tr("Sweeping %s in %llu transactions for a total fee of %s. Is this okay?")) %
@@ -8511,7 +8615,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
@@ -8664,7 +8774,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) {
@@ -8843,29 +8959,31 @@ bool simple_wallet::donate(const std::vector<std::string> &args_)
fail_msg_writer() << tr("amount is wrong: ") << local_args.back() << ", " << tr("expected number from 0 to ") << print_money(std::numeric_limits<uint64_t>::max());
return true;
}
uint8_t hf_version = m_wallet->get_current_hard_fork();
// push back address, amount, payment id
std::string address_str;
std::string DONATION_ADDR = (hf_version >= HF_VERSION_CARROT) ? SALVIUM_DONATION_ADDR_CARROT : SALVIUM_DONATION_ADDR;
if (m_wallet->nettype() != cryptonote::MAINNET)
{
// if not mainnet, convert donation address string to the relevant network type
address_parse_info info;
if (!cryptonote::get_account_address_from_str(info, cryptonote::MAINNET, SALVIUM_DONATION_ADDR))
if (!cryptonote::get_account_address_from_str(info, cryptonote::MAINNET, DONATION_ADDR))
{
fail_msg_writer() << tr("Failed to parse donation address: ") << SALVIUM_DONATION_ADDR;
fail_msg_writer() << tr("Failed to parse donation address: ") << DONATION_ADDR;
return true;
}
address_str = cryptonote::get_account_address_as_str(m_wallet->nettype(), info.is_subaddress, info.address);
}
else
{
address_str = SALVIUM_DONATION_ADDR;
address_str = DONATION_ADDR;
}
local_args.push_back(address_str);
local_args.push_back(amount_str);
if (!payment_id_str.empty())
local_args.push_back(payment_id_str);
if (m_wallet->nettype() == cryptonote::MAINNET)
message_writer() << (boost::format(tr("Donating %s %s to The Salvium Team (donate.salvium.io or %s).")) % amount_str % cryptonote::get_unit(cryptonote::get_default_decimal_point()) % SALVIUM_DONATION_ADDR).str();
message_writer() << (boost::format(tr("Donating %s %s to The Salvium Team (donate.salvium.io or %s).")) % amount_str % cryptonote::get_unit(cryptonote::get_default_decimal_point()) % DONATION_ADDR).str();
else
message_writer() << (boost::format(tr("Donating %s %s to %s.")) % amount_str % cryptonote::get_unit(cryptonote::get_default_decimal_point()) % address_str).str();
transfer(local_args);
@@ -9805,7 +9923,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");
@@ -10008,7 +10126,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);
}
}
@@ -10534,7 +10652,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)");
@@ -10757,7 +10875,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) {
@@ -10771,10 +10889,10 @@ void simple_wallet::print_accounts(const std::string& tag)
auto unlocked_balance = m_wallet->unlocked_balance(account_index, asset, false);
if (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
@@ -10785,9 +10903,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>()*/)
@@ -10804,14 +10922,34 @@ bool simple_wallet::print_address(const std::vector<std::string> &args/* = std::
tools::wallet2::transfer_container transfers;
m_wallet->get_transfers(transfers);
auto print_address_sub = [this, &transfers](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}) << " " << (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;
@@ -10819,11 +10957,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")
{
@@ -11058,7 +11207,7 @@ bool simple_wallet::address_book(const std::vector<std::string> &args/* = std::v
description += " ";
description += args[i];
}
m_wallet->add_address_book_row(info.address, info.has_payment_id ? &info.payment_id : NULL, description, info.is_subaddress);
m_wallet->add_address_book_row(info.address, info.has_payment_id ? &info.payment_id : NULL, description, info.is_subaddress, info.is_carrot);
}
else
{
@@ -11082,9 +11231,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);
address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), row.m_address, row.m_payment_id, row.m_is_carrot);
else
address = get_account_address_as_str(m_wallet->nettype(), row.m_is_subaddress, row.m_address);
address = get_account_address_as_str(m_wallet->nettype(), row.m_is_subaddress, row.m_address, row.m_is_carrot);
success_msg_writer() << tr("Address: ") << address;
success_msg_writer() << tr("Description: ") << row.m_description << "\n";
}
@@ -11223,7 +11372,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");
@@ -11810,6 +11961,7 @@ int main(int argc, char* argv[])
command_line::add_arg(desc_params, arg_generate_new_wallet);
command_line::add_arg(desc_params, arg_generate_from_device);
command_line::add_arg(desc_params, arg_generate_from_view_key);
command_line::add_arg(desc_params, arg_generate_from_svb_key);
command_line::add_arg(desc_params, arg_generate_from_spend_key);
command_line::add_arg(desc_params, arg_generate_from_keys);
command_line::add_arg(desc_params, arg_generate_from_multisig_keys);
+10 -4
View File
@@ -53,8 +53,10 @@
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.simplewallet"
// Hardcode Monero's donation address (see #1447)
constexpr const char SALVIUM_DONATION_ADDR[] = "888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H";
// Hardcode Salvium's donation address (pre-Carrot and post-Carrot)
constexpr const char SALVIUM_DONATION_ADDR[] = "SaLvdVvjgeffnxRDFoEEgeSt3jVcU49W7byT6nawvJFVio9XR7V53tU4mdo3HjjKno2WApvqxmcmKLZyoeSZZQ6jVqSGj1TLsNL";
constexpr const char SALVIUM_DONATION_ADDR_CARROT[] = "SC11sFBPrGmNuT8AiTPUW479BwkdPJwBxdjKEhZ96yDfFg3B4mawgcpE1YfCAa1zwzUiRTMP9eqB54av48ALhzUu1Q5QoPGUfh";
/*!
* \namespace cryptonote
@@ -99,8 +101,11 @@ namespace cryptonote
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm, const crypto::secret_key& recovery_key,
bool recover, bool two_random, const std::string &old_language);
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm, const cryptonote::account_public_address& address,
const boost::optional<crypto::secret_key>& spendkey, const crypto::secret_key& viewkey);
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm,
const cryptonote::account_public_address& address,
const boost::optional<crypto::secret_key>& spendkey,
const crypto::secret_key& viewkey
);
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm,
const epee::wipeable_string &multisig_keys, const epee::wipeable_string &seed_pass, const std::string &old_language);
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm);
@@ -428,6 +433,7 @@ namespace cryptonote
std::string m_generate_new;
std::string m_generate_from_device;
std::string m_generate_from_view_key;
std::string m_generate_from_svb_key;
std::string m_generate_from_spend_key;
std::string m_generate_from_keys;
std::string m_generate_from_multisig_keys;
+1 -1
View File
@@ -1,5 +1,5 @@
#define DEF_SALVIUM_VERSION_TAG "@VERSIONTAG@"
#define DEF_SALVIUM_VERSION "0.9.9-rc4"
#define DEF_SALVIUM_VERSION "1.0.1"
#define DEF_MONERO_VERSION_TAG "release"
#define DEF_MONERO_VERSION "0.18.3.4"
#define DEF_MONERO_RELEASE_NAME "One"
+2 -2
View File
@@ -62,7 +62,7 @@ bool AddressBookImpl::addRow(const std::string &dst_addr , const std::string &pa
return false;
}
bool r = m_wallet->m_wallet->add_address_book_row(info.address, info.has_payment_id ? &info.payment_id : NULL,description,info.is_subaddress);
bool r = m_wallet->m_wallet->add_address_book_row(info.address, info.has_payment_id ? &info.payment_id : NULL,description,info.is_subaddress,info.is_carrot);
if (r)
refresh();
else
@@ -81,7 +81,7 @@ bool AddressBookImpl::setDescription(std::size_t index, const std::string &descr
tools::wallet2::address_book_row entry = ab[index];
entry.m_description = description;
bool r = m_wallet->m_wallet->set_address_book_row(index, entry.m_address, entry.m_has_payment_id ? &entry.m_payment_id : nullptr, entry.m_description, entry.m_is_subaddress);
bool r = m_wallet->m_wallet->set_address_book_row(index, entry.m_address, entry.m_has_payment_id ? &entry.m_payment_id : nullptr, entry.m_description, entry.m_is_subaddress,entry.m_is_carrot);
if (r)
refresh();
else
+8 -6
View File
@@ -877,18 +877,18 @@ 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
@@ -1588,7 +1588,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 +1606,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;
+2 -2
View File
@@ -93,8 +93,8 @@ 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;
+3 -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
+103 -30
View File
@@ -36,7 +36,6 @@
#include "carrot_core/lazy_amount_commitment.h"
#include "carrot_core/scan.h"
#include "carrot_impl/format_utils.h"
#include "common/container_helpers.h"
#include "crypto/generators.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "ringct/rctOps.h"
@@ -133,6 +132,9 @@ static bool try_load_pre_carrot_enote(const bool is_coinbase,
enote_out.amount_commitment = rct::Z;
}
// Check for protocol_tx outputs
enote_out.is_protocol_tx_output = (tx.type == cryptonote::transaction_type::PROTOCOL);
return true;
}
//-------------------------------------------------------------------------------------------------------------------
@@ -169,8 +171,8 @@ static std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_pre_car
crypto::ec_scalar amount_key;
if (!hwdev.derivation_to_scalar(receive_info->derivation,
enote.local_output_index,
amount_key))
enote.is_protocol_tx_output ? 0 : enote.local_output_index,
amount_key))
return std::nullopt;
// a
@@ -209,16 +211,16 @@ static std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_pre_car
// derive k^g_o
crypto::secret_key sender_extension_g;
if (!hwdev.derivation_to_scalar(receive_info->derivation,
enote.local_output_index,
sender_extension_g))
enote.is_protocol_tx_output ? 0 : enote.local_output_index,
sender_extension_g))
return std::nullopt;
// K^j_s'
crypto::public_key address_spend_pubkey;
if (!hwdev.derive_subaddress_public_key(enote.onetime_address,
receive_info->derivation,
enote.local_output_index,
address_spend_pubkey))
receive_info->derivation,
enote.is_protocol_tx_output ? 0 : enote.local_output_index,
address_spend_pubkey))
return std::nullopt;
//pid
@@ -255,26 +257,66 @@ static std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_pre_car
.amount_blinding_factor = amount_blinding_factor,
.asset_type = enote.asset_type,
.main_tx_pubkey_index = main_deriv_idx,
.is_carrot = false
.is_carrot = false,
.is_return = false
};
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
static std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_carrot_coinbase_enote(
const carrot::CarrotCoinbaseEnoteV1 &enote,
const mx25519_pubkey &s_sender_receiver_unctx,
const crypto::public_key &main_address_spend_pubkey)
const crypto::public_key &main_address_spend_pubkey,
carrot::carrot_and_legacy_account &account)
{
enote_view_incoming_scan_info_t res;
bool found_in_return = false;
if (!carrot::try_scan_carrot_coinbase_enote_receiver(enote,
s_sender_receiver_unctx,
main_address_spend_pubkey,
res.sender_extension_g,
res.sender_extension_t))
return std::nullopt;
{
// check for known return addresses
const auto &return_map = account.get_return_output_map_ref();
if (return_map.find(enote.onetime_address) == return_map.end())
return std::nullopt;
found_in_return = true;
}
if (found_in_return) {
// scan the return output
crypto::public_key address_spend_pubkey;
carrot::encrypted_amount_t amount_enc;
crypto::secret_key amount_blinding_factor;
rct::xmr_amount amount;
if (!scan_return_output(
enote.onetime_address,
enote.enote_ephemeral_pubkey,
enote.view_tag,
enote.anchor_enc,
amount_enc,
std::nullopt, // no amount commitment for coinbase enotes
account.get_return_output_map_ref()
.at(enote.onetime_address).input_context,
account,
address_spend_pubkey,
amount,
amount_blinding_factor)
) {
return std::nullopt;
}
res.address_spend_pubkey = address_spend_pubkey;
res.return_address = enote.onetime_address;
res.is_return = true;
} else {
// we received a coinbase enote
res.address_spend_pubkey = main_address_spend_pubkey;
res.is_return = false;
}
res.address_spend_pubkey = main_address_spend_pubkey;
res.payment_id = crypto::null_hash;
res.subaddr_index = carrot::subaddress_index_extended{{0, 0}};
res.amount = enote.amount;
@@ -326,6 +368,7 @@ static std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_carrot_
res.main_tx_pubkey_index = 0;
res.asset_type = enote.asset_type;
res.is_carrot = true;
res.is_return = false;
return res;
}
@@ -344,9 +387,13 @@ static std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_carrot_
{
enote_view_incoming_scan_info_t res;
// assume not a return output
res.is_return = false;
crypto::secret_key amount_blinding_factor_sk;
carrot::payment_id_t payment_id;
carrot::CarrotEnoteType dummy_enote_type;
carrot::janus_anchor_t internal_message;
if (!carrot::try_scan_carrot_enote_external_receiver(enote,
encrypted_payment_id,
s_sender_receiver_unctx,
@@ -359,20 +406,38 @@ static std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_carrot_
amount_blinding_factor_sk,
payment_id,
dummy_enote_type))
return std::nullopt;
{
if (!carrot::try_scan_carrot_enote_internal_receiver(enote,
account,
res.sender_extension_g,
res.sender_extension_t,
res.address_spend_pubkey,
res.amount,
amount_blinding_factor_sk,
dummy_enote_type,
internal_message,
res.return_address,
res.is_return))
{
return std::nullopt;
}
}
const auto subaddr_it = account.get_subaddress_map_ref().find(res.address_spend_pubkey);
CHECK_AND_ASSERT_MES(subaddr_it != account.get_subaddress_map_ref().cend(),
std::nullopt,
"view_incoming_scan_carrot_enote: carrot enote scanned successfully, "
"but the recovered address spend pubkey was not found in the subaddress map");
if (!res.is_return) {
const auto subaddr_it = account.get_subaddress_map_ref().find(res.address_spend_pubkey);
CHECK_AND_ASSERT_MES(subaddr_it != account.get_subaddress_map_ref().cend(),
std::nullopt,
"view_incoming_scan_carrot_enote: carrot enote scanned successfully, "
"but the recovered address spend pubkey was not found in the subaddress map");
const carrot::subaddress_index_extended subaddr_index = subaddr_it->second;
memset(&res.payment_id, 0, sizeof(res.payment_id));
memcpy(&res.payment_id, &payment_id, sizeof(carrot::payment_id_t));
res.subaddr_index = subaddr_index;
} else {
res.subaddr_index = {{0, 0}, carrot::AddressDeriveType::Carrot, true};
}
const carrot::subaddress_index_extended subaddr_index = subaddr_it->second;
memset(&res.payment_id, 0, sizeof(res.payment_id));
memcpy(&res.payment_id, &payment_id, sizeof(carrot::payment_id_t));
res.subaddr_index = subaddr_index;
res.amount_blinding_factor = rct::sk2rct(amount_blinding_factor_sk);
res.main_tx_pubkey_index = 0;
res.asset_type = enote.asset_type;
@@ -424,14 +489,14 @@ void perform_ecdh_derivations(const epee::span<const crypto::public_key> main_tx
{
hwdev.generate_key_derivation(main_tx_ephemeral_pubkey,
k_view_incoming,
tools::add_element(main_derivations_out));
main_derivations_out.emplace_back());
}
for (const crypto::public_key &additional_tx_ephemeral_pubkey : additional_tx_ephemeral_pubkeys)
{
hwdev.generate_key_derivation(additional_tx_ephemeral_pubkey,
k_view_incoming,
tools::add_element(additional_derivations_out));
additional_derivations_out.emplace_back());
}
}
}
@@ -516,7 +581,8 @@ std::optional<enote_view_incoming_scan_info_t> view_incoming_scan_enote(
return view_incoming_scan_carrot_coinbase_enote(enote,
s_sender_receiver_unctx,
address.m_spend_public_key);
address.m_spend_public_key,
account);
}
std::optional<enote_view_incoming_scan_info_t> operator()(const carrot::CarrotEnoteV1 &enote) const
@@ -827,7 +893,7 @@ std::vector<std::optional<enote_view_incoming_scan_info_t>> view_incoming_scan_t
// 3. do view-incoming scan for each output enotes
hw::device &hwdev = hw::get_device("default");
carrot::carrot_and_legacy_account dummy_account;
dummy_account.get_subaddress_map_ref()[address.m_spend_public_key] = {{}};
dummy_account.insert_subaddresses({{address.m_spend_public_key, {{0, 0}, carrot::AddressDeriveType::Carrot, false}}});
for (size_t local_output_index = 0; local_output_index < n_outputs; ++local_output_index)
{
auto &enote_scan_info = res[local_output_index];
@@ -860,10 +926,17 @@ bool is_long_payment_id(const crypto::hash &pid)
std::optional<crypto::key_image> try_derive_enote_key_image(
const enote_view_incoming_scan_info_t &enote_scan_info,
const carrot::carrot_and_legacy_account &acc)
{
{
// we skip the return output key image generation here to do it in process_new_scanned_transaction.
if (!enote_scan_info.subaddr_index)
return std::nullopt;
// if we have a carrot return enote, we return already derived key image
if (enote_scan_info.is_carrot && enote_scan_info.is_return)
{
return acc.get_return_output_map_ref().at(enote_scan_info.return_address).key_image;
}
// k^j_subext
rct::key subaddress_extension;
if (enote_scan_info.subaddr_index->index.is_subaddress())
+9 -1
View File
@@ -31,7 +31,7 @@
//local headers
#include "carrot_core/carrot_enote_types.h"
#include "carrot_core/device.h"
#include "carrot_impl/account.h"
#include "carrot_core/account.h"
#include "carrot_impl/subaddress_index.h"
#include "crypto/crypto.h"
#include "cryptonote_basic/account.h"
@@ -78,6 +78,12 @@ struct enote_view_incoming_scan_info_t
// whether this output is to a carrot address or not
bool is_carrot;
// whether this output is a return output
bool is_return;
// Kr = K_return + K_o
crypto::public_key return_address;
};
struct PreCarrotEnote
@@ -94,6 +100,8 @@ struct PreCarrotEnote
bool short_amount;
rct::key amount_commitment;
rct::ecdhTuple amount;
bool is_protocol_tx_output;
};
using MoneroEnoteVariant = std::variant<PreCarrotEnote,
+117 -88
View File
@@ -541,9 +541,6 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
const std::set<uint32_t> &subaddr_indices,
const wallet2::unique_index_container &subtract_fee_from_outputs)
{
wallet2::transfer_container transfers;
w.get_transfers(transfers);
const bool use_per_byte_fee = w.use_fork_rules(HF_VERSION_PER_BYTE_FEE, 0);
CHECK_AND_ASSERT_THROW_MES(use_per_byte_fee,
"make_carrot_transaction_proposals_wallet2_transfer: not using per-byte base fee");
@@ -590,7 +587,7 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
const size_t n_inputs = input_key_images.size();
CARROT_CHECK_AND_THROW(n_inputs, carrot::too_few_inputs, "no key images provided");
CARROT_CHECK_AND_THROW(n_dests_per_tx, carrot::too_few_outputs, "sweep must have at least one destination");
CARROT_CHECK_AND_THROW(n_dests_per_tx <= FCMP_PLUS_PLUS_MAX_OUTPUTS,
CARROT_CHECK_AND_THROW(n_dests_per_tx <= carrot::CARROT_MAX_TX_OUTPUTS,
carrot::too_many_outputs, "too many sweep destinations per transaction");
// Check that the key image is usable and isn't spent, collect amounts, and get subaddress account index
@@ -636,11 +633,11 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
|| (!is_selfsend_dest && normal_payment_proposals.size() == i+1),
__func__ << ": BUG in build_payment_proposals: incorrect count for payment proposal lists");
}
CARROT_CHECK_AND_THROW(normal_payment_proposals.size() < FCMP_PLUS_PLUS_MAX_OUTPUTS,
CARROT_CHECK_AND_THROW(normal_payment_proposals.size() < carrot::CARROT_MAX_TX_OUTPUTS,
carrot::too_many_outputs, "too many *outgoing* sweep destinations per tx, we also need 1 self-send output");
// make `n_txs` tx proposals with `n_output` payment proposals each
const size_t n_txs = div_ceil<size_t>(n_inputs, FCMP_PLUS_PLUS_MAX_INPUTS);
const size_t n_txs = div_ceil<size_t>(n_inputs, carrot::CARROT_MAX_TX_INPUTS);
std::vector<carrot::CarrotTransactionProposalV1> tx_proposals(n_txs);
size_t ki_idx = 0;
for (carrot::CarrotTransactionProposalV1 &tx_proposal : tx_proposals)
@@ -650,7 +647,7 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
selfsend_payment_proposals.back().proposal.enote_type = carrot::CarrotEnoteType::CHANGE;
// collect inputs for this tx
const size_t ki_idx_end = std::min<size_t>(n_inputs, ki_idx + FCMP_PLUS_PLUS_MAX_INPUTS);
const size_t ki_idx_end = std::min<size_t>(n_inputs, ki_idx + carrot::CARROT_MAX_TX_INPUTS);
std::vector<carrot::CarrotSelectedInput> selected_inputs;
selected_inputs.reserve(n_inputs - ki_idx_end);
for (; ki_idx < ki_idx_end; ++ki_idx)
@@ -664,7 +661,7 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
tx_type,
std::move(selected_inputs),
change_address_spend_pubkey,
{{subaddr_account, 0}, carrot::AddressDeriveType::PreCarrot}, //! @TODO: handle Carrot keys
{{subaddr_account, 0}, carrot::AddressDeriveType::Carrot},
tx_proposal);
// populate the sources
@@ -742,7 +739,7 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
subaddr_account,
subaddr_indices,
only_below ? only_below : MONEY_SUPPLY,
0,
1, // ignore_below
top_block_index))
continue;
@@ -812,85 +809,110 @@ bool get_address_openings_x_y(
crypto::secret_key &x_out,
crypto::secret_key &y_out)
{
// If the output is a return output, we can use the return output secret key
// to derive x and y directly.
const auto& return_output_map = w.get_account().get_return_output_map_ref();
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;
return true;
}
const std::vector<crypto::public_key> v_pubkeys{src.real_out_tx_key};
const std::vector<crypto::public_key> v_pubkeys_empty{};
const epee::span<const crypto::public_key> main_tx_ephemeral_pubkeys = (src.real_out_tx_key == crypto::null_pkey) ? epee::to_span(v_pubkeys_empty) : epee::to_span(v_pubkeys);
const epee::span<const crypto::public_key> additional_tx_ephemeral_pubkeys = epee::to_span(src.real_out_additional_tx_keys);
// 2. perform ECDH derivations
std::vector<crypto::key_derivation> main_derivations;
std::vector<crypto::key_derivation> additional_derivations;
bool is_carrot = carrot::is_carrot_transaction_v1(tx);
wallet::perform_ecdh_derivations(
main_tx_ephemeral_pubkeys,
additional_tx_ephemeral_pubkeys,
is_carrot ? w.get_account().get_keys().k_view_incoming : w.get_account().get_keys().m_view_secret_key,
w.get_account().get_keys().get_device(),
is_carrot,
main_derivations,
additional_derivations
);
// we have to try both internal and external derivations
bool r = false;
for (size_t i = 0; i < 2; ++i) {
// perform ECDH derivations
std::vector<crypto::key_derivation> main_derivations;
std::vector<crypto::key_derivation> additional_derivations;
if (i == 0) {
wallet::perform_ecdh_derivations(
main_tx_ephemeral_pubkeys,
additional_tx_ephemeral_pubkeys,
w.get_account().get_keys().k_view_incoming,
w.get_account().get_keys().get_device(),
src.carrot,
main_derivations,
additional_derivations
);
} else {
crypto::key_derivation main_derivation;
memcpy(main_derivation.data, w.get_account().get_keys().s_view_balance.data, sizeof(crypto::secret_key));
main_derivations.push_back(main_derivation);
}
crypto::hash s_sender_receiver;
const crypto::key_derivation &kd = main_derivations.size()
? main_derivations[0]
: additional_derivations[src.real_output_in_tx_index];
const mx25519_pubkey s_sender_receiver_unctx = carrot::raw_byte_convert<mx25519_pubkey>(kd);
crypto::hash s_sender_receiver;
const crypto::key_derivation &kd = main_derivations.size()
? main_derivations[0]
: additional_derivations[src.real_output_in_tx_index];
const mx25519_pubkey s_sender_receiver_unctx = carrot::raw_byte_convert<mx25519_pubkey>(kd);
// ephemeral pubkeys
const epee::span<const crypto::public_key> enote_ephemeral_pubkeys_pk =
main_tx_ephemeral_pubkeys.empty() ? additional_tx_ephemeral_pubkeys : main_tx_ephemeral_pubkeys;
const epee::span<const mx25519_pubkey> enote_ephemeral_pubkeys = {
reinterpret_cast<const mx25519_pubkey*>(enote_ephemeral_pubkeys_pk.data()),
enote_ephemeral_pubkeys_pk.size()
};
// ephemeral pubkeys
const epee::span<const crypto::public_key> enote_ephemeral_pubkeys_pk =
main_tx_ephemeral_pubkeys.empty() ? additional_tx_ephemeral_pubkeys : main_tx_ephemeral_pubkeys;
const epee::span<const mx25519_pubkey> enote_ephemeral_pubkeys = {
reinterpret_cast<const mx25519_pubkey*>(enote_ephemeral_pubkeys_pk.data()),
enote_ephemeral_pubkeys_pk.size()
};
const bool shared_ephemeral_pubkey = enote_ephemeral_pubkeys.size() == 1;
const size_t ephemeral_pubkey_index = shared_ephemeral_pubkey ? 0 : src.real_output_in_tx_index;
const bool shared_ephemeral_pubkey = enote_ephemeral_pubkeys.size() == 1;
const size_t ephemeral_pubkey_index = shared_ephemeral_pubkey ? 0 : src.real_output_in_tx_index;
// input_context
carrot::input_context_t input_context;
if (src.coinbase) {
input_context = carrot::make_carrot_input_context_coinbase(src.block_index);
} else {
input_context = carrot::make_carrot_input_context(src.first_rct_key_image);
// input_context
carrot::input_context_t input_context;
if (src.coinbase) {
input_context = carrot::make_carrot_input_context_coinbase(src.block_index);
} else {
input_context = carrot::make_carrot_input_context(src.first_rct_key_image);
}
// s^ctx_sr = H_32(s_sr, D_e, input_context)
make_carrot_sender_receiver_secret(s_sender_receiver_unctx.data,
enote_ephemeral_pubkeys[ephemeral_pubkey_index],
input_context,
s_sender_receiver);
// get the k_og and k_ot
crypto::secret_key sender_extension_g_out;
crypto::secret_key sender_extension_t_out;
crypto::public_key address_spend_pubkey_out;
carrot::payment_id_t nominal_payment_id_out;
carrot::janus_anchor_t nominal_janus_anchor_out;
carrot::encrypted_janus_anchor_t encrypted_janus_anchor;
carrot::encrypted_payment_id_t encrypted_payment_id;
carrot::scan_carrot_dest_info(
rct::rct2pk(src.outputs[src.real_output].second.dest),
src.outputs[src.real_output].second.mask,
encrypted_janus_anchor,
encrypted_payment_id,
s_sender_receiver,
sender_extension_g_out,
sender_extension_t_out,
address_spend_pubkey_out,
nominal_payment_id_out,
nominal_janus_anchor_out
);
r = w.get_account().try_searching_for_opening_for_onetime_address(
address_spend_pubkey_out,
sender_extension_g_out,
sender_extension_t_out,
x_out,
y_out
);
// If we found the opening, we can stop here
if (r) {
break;
}
}
// s^ctx_sr = H_32(s_sr, D_e, input_context)
make_carrot_sender_receiver_secret(s_sender_receiver_unctx.data,
enote_ephemeral_pubkeys[ephemeral_pubkey_index],
input_context,
s_sender_receiver);
// get the k_og and k_ot
crypto::secret_key sender_extension_g_out;
crypto::secret_key sender_extension_t_out;
crypto::public_key address_spend_pubkey_out;
carrot::payment_id_t nominal_payment_id_out;
carrot::janus_anchor_t nominal_janus_anchor_out;
carrot::encrypted_janus_anchor_t encrypted_janus_anchor;
carrot::encrypted_payment_id_t encrypted_payment_id;
carrot::scan_carrot_dest_info(
rct::rct2pk(src.outputs[src.real_output].second.dest),
src.outputs[src.real_output].second.mask,
encrypted_janus_anchor,
encrypted_payment_id,
s_sender_receiver,
sender_extension_g_out,
sender_extension_t_out,
address_spend_pubkey_out,
nominal_payment_id_out,
nominal_janus_anchor_out
);
bool r = w.get_account().try_searching_for_opening_for_onetime_address(
address_spend_pubkey_out,
sender_extension_g_out,
sender_extension_t_out,
x_out,
y_out
);
CHECK_AND_ASSERT_THROW_MES(r, "Failed to obtain openings for onetime address");
return true;
}
//-------------------------------------------------------------------------------------------------------------------
@@ -1003,14 +1025,16 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
std::vector<carrot::RCTOutputEnoteProposal> output_enote_proposals;
carrot::encrypted_payment_id_t encrypted_payment_id;
size_t change_index;
carrot::RCTOutputEnoteProposal return_enote_out;
std::unordered_map<crypto::public_key, size_t> payments_indices;
carrot::get_output_enote_proposals(tx_proposal.normal_payment_proposals,
selfsend_payment_proposal_cores,
tx_proposal.dummy_encrypted_payment_id,
nullptr,
&w.get_account().s_view_balance_dev,
&addr_dev,
tx_proposal.key_images_sorted.at(0),
output_enote_proposals,
return_enote_out,
encrypted_payment_id,
tx_proposal.tx_type,
change_index,
@@ -1054,7 +1078,9 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
tx_proposal.sources,
tx_proposal.fee,
tx_proposal.tx_type,
tx_proposal.amount_burnt,
change_masks,
return_enote_out,
encrypted_payment_id);
// aliases
@@ -1216,16 +1242,24 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
//-------------------------------------------------------------------------------------------------------------------
wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionProposalV1 &tx_proposal,
const wallet2::transfer_container &transfers,
const crypto::secret_key &k_view,
hw::device &hwdev)
const carrot::carrot_and_legacy_account &account)
{
const std::size_t n_inputs = tx_proposal.key_images_sorted.size();
const std::size_t n_outputs = tx_proposal.normal_payment_proposals.size() +
tx_proposal.selfsend_payment_proposals.size();
const bool shared_ephemeral_pubkey = n_outputs == 2;
CARROT_CHECK_AND_THROW(
tx_proposal.tx_type != cryptonote::transaction_type::UNSET,
carrot::missing_components,
"make_pending_carrot_tx: tx proposal has unset tx type"
);
CARROT_CHECK_AND_THROW(n_inputs >= 1, carrot::too_few_inputs, "carrot tx proposal missing inputs");
CARROT_CHECK_AND_THROW(n_outputs >= 2, carrot::too_few_outputs, "carrot tx proposal missing outputs");
if (tx_proposal.tx_type == cryptonote::transaction_type::STAKE || tx_proposal.tx_type == cryptonote::transaction_type::BURN) {
CARROT_CHECK_AND_THROW(n_outputs == 1, carrot::too_few_outputs, "carrot tx proposal doesn't have correct number of outputs");
} else {
CARROT_CHECK_AND_THROW(n_outputs >= 2, carrot::too_few_outputs, "carrot tx proposal missing outputs");
}
const crypto::key_image &tx_first_key_image = tx_proposal.key_images_sorted.at(0);
@@ -1249,16 +1283,13 @@ wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionPropos
key_images_string << ki;
}
//! @TODO: HW device
carrot::view_incoming_key_ram_borrowed_device k_view_dev(k_view);
// get order of payment proposals
std::vector<carrot::RCTOutputEnoteProposal> output_enote_proposals;
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,
&k_view_dev,
&account.k_view_incoming_dev,
output_enote_proposals,
encrypted_payment_id,
&sorted_payment_proposal_indices);
@@ -1288,7 +1319,7 @@ wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionPropos
if (is_selfsend)
{
dest = make_tx_destination_entry(tx_proposal.selfsend_payment_proposals.at(payment_idx.second),
k_view_dev);
account.k_view_incoming_dev);
ephemeral_privkeys.push_back(crypto::null_skey);
}
else // !is_selfsend
@@ -1348,11 +1379,9 @@ wallet2::pending_tx finalize_all_proofs_from_transfer_details_as_pending_tx(
const wallet2::transfer_container &transfers,
const wallet2 &w)
{
const auto acc_keys = w.get_account().get_keys();
wallet2::pending_tx ptx = make_pending_carrot_tx(tx_proposal,
transfers,
acc_keys.m_view_secret_key,
acc_keys.get_device());
w.get_account());
ptx.tx = finalize_all_proofs_from_transfer_details(
tx_proposal,
+1 -7
View File
@@ -125,19 +125,13 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
const std::set<uint32_t> &subaddr_indices);
wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionProposalV1 &tx_proposal,
const wallet2::transfer_container &transfers,
const crypto::secret_key &k_view,
hw::device &hwdev);
const carrot::carrot_and_legacy_account &account);
cryptonote::transaction finalize_all_proofs_from_transfer_details(
const carrot::CarrotTransactionProposalV1 &tx_proposal,
const cryptonote::transaction_type tx_type,
const wallet2 &w);
wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionProposalV1 &tx_proposal,
const wallet2::transfer_container &transfers,
const crypto::secret_key &k_view,
hw::device &hwdev);
wallet2::pending_tx finalize_all_proofs_from_transfer_details_as_pending_tx(
const carrot::CarrotTransactionProposalV1 &tx_proposal,
const wallet2::transfer_container &transfers,
+197 -88
View File
@@ -98,6 +98,7 @@ using namespace epee;
#include "carrot_impl/format_utils.h"
#include "tx_builder.h"
#include "scanning_tools.h"
#include "carrot_core/scan.h"
extern "C"
{
@@ -1583,15 +1584,41 @@ crypto::public_key wallet2::get_subaddress_spend_public_key(const cryptonote::su
return hwdev.get_subaddress_spend_public_key(m_account.get_keys(), index);
}
//----------------------------------------------------------------------------------------------------
std::string wallet2::get_subaddress_as_str(const cryptonote::subaddress_index& index) const
std::string wallet2::get_subaddress_as_str(const carrot::subaddress_index_extended& subaddr_const) const
{
cryptonote::account_public_address address = get_subaddress(index);
return cryptonote::get_account_address_as_str(m_nettype, !index.is_zero(), address);
// Make a non-const copy of subaddr
carrot::subaddress_index_extended subaddr(subaddr_const);
// Handle "auto" - the account class doesn't know the HF version, so this is the cleanest solution
if (subaddr.derive_type == carrot::AddressDeriveType::Auto) {
// Get the HF version
uint32_t hf_version = estimate_current_hard_fork();
THROW_WALLET_EXCEPTION_IF(hf_version == 0, error::wallet_internal_error, "unable to estimate the current hard fork - cannot generate subaddress as string");
// Change the non-const derivation type
subaddr.derive_type = (hf_version >= HF_VERSION_CARROT) ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot;
}
// Build the cryptonote::account_public_address
carrot::CarrotDestinationV1 address = m_account.subaddress(subaddr);
account_public_address addr{address.address_spend_pubkey, address.address_view_pubkey};
addr.m_is_carrot = subaddr.derive_type == carrot::AddressDeriveType::Carrot;
// change this code into base 58
return cryptonote::get_account_address_as_str(m_nettype, address.is_subaddress, addr, addr.m_is_carrot);
}
//----------------------------------------------------------------------------------------------------
std::string wallet2::get_integrated_address_as_str(const crypto::hash8& payment_id) const
std::string wallet2::get_integrated_address_as_str(const crypto::hash8& payment_id, bool carrot) const
{
return cryptonote::get_account_integrated_address_as_str(m_nettype, get_address(), payment_id);
carrot::subaddress_index_extended subaddr{{0, 0}, carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot};
carrot::CarrotDestinationV1 address = m_account.subaddress(subaddr);
// Build the cryptonote::account_public_address
account_public_address addr{address.address_spend_pubkey, address.address_view_pubkey};
addr.m_is_carrot = carrot;
return cryptonote::get_account_integrated_address_as_str(m_nettype, addr, payment_id, carrot);
}
//----------------------------------------------------------------------------------------------------
void wallet2::add_subaddress_account(const std::string& label)
@@ -2615,7 +2642,7 @@ void wallet2::process_new_scanned_transaction(
continue;
// update m_transfers view-incoming scan info, and default values
transfer_details& td = tools::add_element(m_transfers);
transfer_details& td = m_transfers.emplace_back();
td.m_block_height = height;
td.m_internal_output_index = local_output_index;
td.m_global_output_index = o_indices.at(local_output_index);
@@ -2647,8 +2674,8 @@ void wallet2::process_new_scanned_transaction(
}
}
// override the key image for PROTOCOL/RETURN tx outputs.
if (td.m_td_origin_idx != std::numeric_limits<uint64_t>::max())
// override the key image for pre-carrot PROTOCOL/RETURN tx outputs.
if (!enote_scan_info->is_carrot && td.m_td_origin_idx != std::numeric_limits<uint64_t>::max())
{
THROW_WALLET_EXCEPTION_IF(td.m_td_origin_idx >= get_num_transfer_details(), error::wallet_internal_error, "cannot locate return_payment TX origin in m_transfers");
const transfer_details &td_origin = m_transfers[td.m_td_origin_idx];
@@ -2664,20 +2691,22 @@ void wallet2::process_new_scanned_transaction(
origin_tx_data.output_index = td_origin.m_internal_output_index;
origin_tx_data.tx_type = td_origin.m_tx.type;
rct::salvium_input_data_t sid;
THROW_WALLET_EXCEPTION_IF(!cryptonote::generate_key_image_helper(m_account.get_keys(),
m_account.get_subaddress_map_cn(),
onetime_address,
tx_public_key,
additional_tx_public_keys,
local_output_index,
in_ephemeral,
ki,
hwdev,
true,
origin_tx_data,
sid),
error::wallet_internal_error,
"failed to obtain key image for protocol_tx output");
THROW_WALLET_EXCEPTION_IF(!cryptonote::generate_key_image_helper(
m_account.get_keys(),
m_account.get_subaddress_map_cn(),
onetime_address,
tx_public_key,
additional_tx_public_keys,
local_output_index,
in_ephemeral,
ki,
hwdev,
true,
origin_tx_data,
sid),
error::wallet_internal_error,
"failed to obtain key image for protocol_tx output"
);
td.m_key_image_known = true;
td.m_key_image = ki;
}
@@ -2696,6 +2725,37 @@ void wallet2::process_new_scanned_transaction(
// update m_transfer_indices
m_transfers_indices[enote_scan_info->asset_type].insert(m_transfers.size()-1);
// Check for STAKE / AUDIT TX payouts
if (tx.type == cryptonote::transaction_type::PROTOCOL && td.m_td_origin_idx != std::numeric_limits<uint64_t>::max()) {
// Attempt to get the origin TX
THROW_WALLET_EXCEPTION_IF(td.m_td_origin_idx >= get_num_transfer_details(),
error::wallet_internal_error,
"cannot locate return_payment TX origin in m_transfers");
const transfer_details &td_origin = m_transfers[td.m_td_origin_idx];
THROW_WALLET_EXCEPTION_IF(td_origin.m_tx.type != cryptonote::transaction_type::AUDIT && td_origin.m_tx.type != cryptonote::transaction_type::STAKE,
error::wallet_internal_error,
"incorrect TX type for protocol_tx origin in m_transfers");
// Get the output key for the change entry
crypto::public_key pk_locked_coins = crypto::null_pkey;
THROW_WALLET_EXCEPTION_IF(!get_output_public_key(td_origin.m_tx.vout[td_origin.m_internal_output_index], pk_locked_coins),
error::wallet_internal_error,
"Failed to get output public key for locked coins");
// At this point, we need to clear the "locked coins" count, because otherwise we will be counting yield stakes twice in our balance
if (!m_locked_coins.erase(pk_locked_coins)) {
LOG_ERROR("Failed to remove protocol_tx entry from m_locked_coins - possible duplicate output key detected");
}
}
// Check for STAKE / AUDIT TXs
if (tx.type == cryptonote::transaction_type::AUDIT || tx.type == cryptonote::transaction_type::STAKE) {
// Add a "locked coins" entry so users don't freak out when they STAKE/AUDITOA
m_locked_coins.insert({onetime_address, {0, tx.amount_burnt, tx.source_asset_type}});
}
// update multisig info
if (m_multisig)
{
@@ -2841,20 +2901,8 @@ void wallet2::process_new_scanned_transaction(
// save the change output key in our subaddress_map, so we can receive money to it later
// from protocol or return txs
for (const auto &entry: tx_amounts_individual_outs[i->first]) {
const crypto::public_key &onetime_address = std::get<1>(entry);
carrot::AddressDeriveType derive_type;
if (use_fork_rules(HF_VERSION_CARROT, 0)) {
derive_type = carrot::AddressDeriveType::Carrot;
} else {
derive_type = carrot::AddressDeriveType::PreCarrot;
}
const carrot::subaddress_index_extended subaddr_ext = {i->first.major, i->first.minor, derive_type, true};
// save to m_subaddresses as well, so that we can populate account subaddress map
// when we open the wallet first time.
m_subaddresses_extended[onetime_address] = subaddr_ext;
// update m_salvium_txs
m_salvium_txs.insert({onetime_address, m_transfers.size()-1});
m_salvium_txs.insert({std::get<1>(entry), m_transfers.size()-1});
}
// delete change output from amounts
@@ -2900,6 +2948,8 @@ void wallet2::process_new_scanned_transaction(
payment.m_timestamp = ts;
payment.m_coinbase = miner_tx;
payment.m_subaddr_index = i.first;
payment.m_tx_type = tx.type;
payment.m_is_carrot = (tx.version >= TRANSACTION_VERSION_CARROT);
if (pool) {
if (emplace_or_replace(m_unconfirmed_payments, payment_id, pool_payment_details{payment, double_spend_seen}))
all_same = false;
@@ -3104,8 +3154,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b,
m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx);
LOG_PRINT_L2("Processed block: " << bl_id << ", height " << height << ", " << miner_tx_handle_time + txs_handle_time << "(" << miner_tx_handle_time << "/" << txs_handle_time <<")ms");
}else
{
} else {
if (!(height % 128))
LOG_PRINT_L2( "Skipped block by timestamp, height: " << height << ", block time " << b.timestamp << ", account time " << m_account.get_createtime());
}
@@ -3373,44 +3422,32 @@ void wallet2::process_parsed_blocks(const uint64_t start_height, const std::vect
size_t i = 0;
size_t tx_output_idx = 0;
while (i < blocks.size()) {
const parsed_block &par_blk = parsed_blocks.at(i);
const std::uint64_t height = start_height + i;
const bool skip_scan_for_this_block = should_skip_block(par_blk.block, height);
if (!skip_scan_for_this_block && m_refresh_type != RefreshNoCoinbase)
tx_scan_job(par_blk.block.miner_tx, tx_output_idx);
tx_output_idx += par_blk.block.miner_tx.vout.size();
if (!skip_scan_for_this_block && m_refresh_type != RefreshNoCoinbase)
tx_scan_job(par_blk.block.protocol_tx, tx_output_idx);
tx_output_idx += par_blk.block.protocol_tx.vout.size();
for (const cryptonote::transaction &tx : par_blk.txes)
tools::threadpool::waiter scan_blocks_waiter(tpool);
for (size_t j = 0; j < 10; ++j)
{
if (!skip_scan_for_this_block)
tx_scan_job(tx, tx_output_idx);
tx_output_idx += tx.vout.size();
if (i+j >= blocks.size()) break;
const parsed_block &par_blk = parsed_blocks.at(i+j);
const std::uint64_t height = start_height + i + j;
const bool skip_scan_for_this_block = should_skip_block(par_blk.block, height);
if (!skip_scan_for_this_block && m_refresh_type != RefreshNoCoinbase)
tpool.submit(&scan_blocks_waiter, std::bind(tx_scan_job, std::cref(par_blk.block.miner_tx), tx_output_idx));
tx_output_idx += par_blk.block.miner_tx.vout.size();
if (!skip_scan_for_this_block && m_refresh_type != RefreshNoCoinbase)
tpool.submit(&scan_blocks_waiter, std::bind(tx_scan_job, std::cref(par_blk.block.protocol_tx), tx_output_idx));
tx_output_idx += par_blk.block.protocol_tx.vout.size();
for (const cryptonote::transaction &tx : par_blk.txes)
{
if (!skip_scan_for_this_block)
tpool.submit(&scan_blocks_waiter, std::bind(tx_scan_job, std::cref(tx), tx_output_idx));
tx_output_idx += tx.vout.size();
}
}
// tools::threadpool::waiter scan_blocks_waiter(tpool);
// for (size_t j = 0; j < 10; ++j)
// {
// if (!skip_scan_for_this_block && m_refresh_type != RefreshNoCoinbase)
// tpool.submit(&scan_blocks_waiter, std::bind(tx_scan_job, std::cref(par_blk.block.miner_tx), tx_output_idx));
// tx_output_idx += par_blk.block.miner_tx.vout.size();
// if (!skip_scan_for_this_block && m_refresh_type != RefreshNoCoinbase)
// tpool.submit(&scan_blocks_waiter, std::bind(tx_scan_job, std::cref(par_blk.block.protocol_tx), tx_output_idx));
// tx_output_idx += par_blk.block.protocol_tx.vout.size();
// for (const cryptonote::transaction &tx : par_blk.txes)
// {
// if (!skip_scan_for_this_block)
// tpool.submit(&scan_blocks_waiter, std::bind(tx_scan_job, std::cref(tx), tx_output_idx));
// tx_output_idx += tx.vout.size();
// }
// if (++i >= blocks.size()) break;
// }
// if (!scan_blocks_waiter.wait())
// {
// THROW_WALLET_EXCEPTION_IF(password_failure, error::password_needed);
// THROW_WALLET_EXCEPTION(error::wallet_internal_error, "Unrecognized exception in enote scanning threadpool");
// }
i++;
if (!scan_blocks_waiter.wait())
{
THROW_WALLET_EXCEPTION_IF(password_failure, error::password_needed);
THROW_WALLET_EXCEPTION(error::wallet_internal_error, "Unrecognized exception in enote scanning threadpool");
}
i+=10;
}
// Start processing blockchain entries with scanned outputs
@@ -3458,6 +3495,23 @@ void wallet2::process_parsed_blocks(const uint64_t start_height, const std::vect
tx_output_idx += n_block_outputs;
++current_index;
}
// save accumulated return subaddresses to m_subaddresses_extended, so that we can re-insert
// them into account after we restart the wallet.
m_subaddresses_extended.clear();
for (const auto& subaddr: m_account.get_subaddress_map_ref()) {
if (subaddr.second.derive_type == carrot::AddressDeriveType::PreCarrot &&
subaddr.second.is_return_spend_key == true
) {
m_subaddresses_extended.insert({subaddr.first, subaddr.second});
}
}
// save accumulated return output info to wallet
m_return_output_info.clear();
for (const auto& output_info: m_account.get_return_output_map_ref()) {
m_return_output_info.insert({output_info.first, output_info.second});
}
}
//----------------------------------------------------------------------------------------------------
void wallet2::refresh(bool trusted_daemon)
@@ -4021,7 +4075,7 @@ void wallet2::fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height,
}
bool wallet2::add_address_book_row(const cryptonote::account_public_address &address, const crypto::hash8 *payment_id, const std::string &description, bool is_subaddress)
bool wallet2::add_address_book_row(const cryptonote::account_public_address &address, const crypto::hash8 *payment_id, const std::string &description, bool is_subaddress, bool is_carrot)
{
wallet2::address_book_row a;
a.m_address = address;
@@ -4029,6 +4083,7 @@ bool wallet2::add_address_book_row(const cryptonote::account_public_address &add
a.m_payment_id = payment_id ? *payment_id : crypto::null_hash8;
a.m_description = description;
a.m_is_subaddress = is_subaddress;
a.m_is_carrot = is_carrot;
auto old_size = m_address_book.size();
m_address_book.push_back(a);
@@ -4037,7 +4092,7 @@ bool wallet2::add_address_book_row(const cryptonote::account_public_address &add
return false;
}
bool wallet2::set_address_book_row(size_t row_id, const cryptonote::account_public_address &address, const crypto::hash8 *payment_id, const std::string &description, bool is_subaddress)
bool wallet2::set_address_book_row(size_t row_id, const cryptonote::account_public_address &address, const crypto::hash8 *payment_id, const std::string &description, bool is_subaddress, bool is_carrot)
{
wallet2::address_book_row a;
a.m_address = address;
@@ -4045,6 +4100,7 @@ bool wallet2::set_address_book_row(size_t row_id, const cryptonote::account_publ
a.m_payment_id = payment_id ? *payment_id : crypto::null_hash8;
a.m_description = description;
a.m_is_subaddress = is_subaddress;
a.m_is_carrot = is_carrot;
const auto size = m_address_book.size();
if (row_id >= size)
@@ -4485,6 +4541,7 @@ bool wallet2::clear()
m_transfers.clear();
m_transfers_indices.clear();
m_locked_coins.clear();
m_salvium_txs.clear();
m_key_images.clear();
m_pub_keys.clear();
m_unconfirmed_txs.clear();
@@ -4503,6 +4560,8 @@ bool wallet2::clear()
m_pool_info_query_time = 0;
m_skip_to_height = 0;
m_background_sync_data = background_sync_data_t{};
m_subaddresses_extended.clear();
m_return_output_info.clear();
return true;
}
//----------------------------------------------------------------------------------------------------
@@ -4512,6 +4571,7 @@ void wallet2::clear_soft(bool keep_key_images)
m_transfers.clear();
m_transfers_indices.clear();
m_locked_coins.clear();
m_salvium_txs.clear();
if (!keep_key_images)
m_key_images.clear();
m_pub_keys.clear();
@@ -4524,6 +4584,8 @@ void wallet2::clear_soft(bool keep_key_images)
m_pool_info_query_time = 0;
m_skip_to_height = 0;
m_background_sync_data = background_sync_data_t{};
m_subaddresses_extended.clear();
m_return_output_info.clear();
cryptonote::block b;
generate_genesis(b);
@@ -5812,7 +5874,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip
*/
void wallet2::generate(const std::string& wallet_, const epee::wipeable_string& password,
const cryptonote::account_public_address &account_public_address,
const crypto::secret_key& viewkey, bool create_address_file)
const crypto::secret_key& viewkey, bool create_address_file)
{
clear();
prepare_file_names(wallet_);
@@ -5824,7 +5886,11 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
}
m_account.create_from_viewkey(account_public_address, viewkey);
if (account_public_address.m_is_carrot) {
m_account.create_from_svb_key(account_public_address, viewkey);
} else {
m_account.create_from_viewkey(account_public_address, viewkey);
}
init_type(hw::device::device_type::SOFTWARE);
m_watch_only = true;
m_account_public_address = account_public_address;
@@ -6586,17 +6652,21 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
if (get_num_subaddress_accounts() == 0)
add_subaddress_account(tr("Primary account"));
m_account.insert_subaddresses(m_subaddresses_extended);
// populate account subaddress list
if (!m_subaddresses.empty())
{
// if we have subaddresses, we need to insert them into the account
for (const auto &subaddress : m_subaddresses)
m_account.insert_subaddresses(
// TODO: we assume none of these subaddresses are return tx subaddresses
// we assume none of these subaddresses are return tx subaddresses
{{subaddress.first, {{subaddress.second.major, subaddress.second.minor}, carrot::AddressDeriveType::PreCarrot, false}}}
);
}
m_account.insert_subaddresses(m_subaddresses_extended);
// populate account return output info
m_account.insert_return_output_info(m_return_output_info);
try
{
@@ -6803,9 +6873,8 @@ void wallet2::process_background_cache_on_open()
background_w2->m_custom_background_key = m_custom_background_key;
// TODO: uncommet this block
// carrot::carrot_and_legacy_account account = m_account;
// account.forget_spend_key();
// background_w2->m_account = account;
background_w2->m_account.set_keys(m_account.get_keys(), false);
background_w2->m_account.forget_spend_key();
// Load background cache from file
background_w2->clear();
@@ -7417,6 +7486,14 @@ bool wallet2::is_transfer_unlocked(const transfer_details& td)
uint64_t unlock_time = 0;
if (!cryptonote::get_output_unlock_time(td.m_tx.vout[td.m_internal_output_index], unlock_time))
return false;
if (unlock_time == 0) {
if (td.m_tx.type == cryptonote::transaction_type::MINER || td.m_tx.type == cryptonote::transaction_type::PROTOCOL) {
unlock_time = CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW;
} else {
unlock_time = CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE;
}
}
return is_transfer_unlocked(unlock_time, td.m_block_height);
}
//----------------------------------------------------------------------------------------------------
@@ -10724,8 +10801,13 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp
boost::unique_lock<hw::device> hwdev_lock (hwdev);
hw::reset_mode rst(hwdev);
const bool do_carrot_tx_construction = use_fork_rules(HF_VERSION_CARROT);
if (do_carrot_tx_construction)
// Sanity check correct CN/Carrot address is being used
const uint8_t hf_version = get_current_hard_fork();
for (const auto &entry: dsts) {
THROW_WALLET_EXCEPTION_IF(entry.addr.m_is_carrot && hf_version < HF_VERSION_CARROT, error::wallet_internal_error, "Carrot address supplied, but Carrot not yet active");
THROW_WALLET_EXCEPTION_IF(!entry.addr.m_is_carrot && hf_version >= HF_VERSION_CARROT, error::wallet_internal_error, "CryptoNote address supplied, but Carrot is now active");
}
if (hf_version >= HF_VERSION_CARROT)
{
const auto tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_transfer(*this, dsts, priority, extra, tx_type, subaddr_account, subaddr_indices, subtract_fee_from_outputs);
std::vector<pending_tx> ptx_vector;
@@ -11566,9 +11648,8 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below
const auto tx_proposals = tools::wallet::make_carrot_transaction_proposals_wallet2_sweep_all(*this, below, address, is_subaddress, outputs, priority, extra, tx_type, subaddr_account, subaddr_indices);
std::vector<pending_tx> ptx_vector;
ptx_vector.reserve(tx_proposals.size());
// TODO: use CLSAGS here..
// for (const auto &tx_proposal : tx_proposals)
// ptx_vector.push_back(tools::wallet::finalize_all_proofs_from_transfer_details_as_pending_tx(tx_proposal, *this));
for (const auto &tx_proposal : tx_proposals)
ptx_vector.push_back(tools::wallet::finalize_all_proofs_from_transfer_details_as_pending_tx(tx_proposal, *this));
return ptx_vector;
}
@@ -11691,7 +11772,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_return(std::vector
THROW_WALLET_EXCEPTION_IF(idx >= get_num_transfer_details(), error::wallet_internal_error, tr("cannot locate return_payment origin index in m_transfers"));
const transfer_details& td_origin = get_transfer_details(idx);
const std::string asset_type = td_origin.m_tx.source_asset_type;
bool is_subaddress = true;
bool is_subaddress = false;
size_t outputs = 1;
std::vector<size_t> unused_dust_indices = {};
size_t fake_outs_count = get_min_ring_size() - 1; // Use the default ring size
@@ -11808,6 +11889,9 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_return(std::vector
return create_transactions_from(address, cryptonote::transaction_type::RETURN, asset_type, is_subaddress, outputs, transfers_indices, unused_dust_indices, fake_outs_count, unlock_time, priority, extra);
}
// pre-carrot return payment construction uses subaddress true
is_subaddress = true;
// To return a payment, we need to know the y value to process the F value
// ...but the y value is calculated differently depending on the original TX
@@ -12242,6 +12326,31 @@ void wallet2::device_show_address(uint32_t account_index, uint32_t address_index
hwdev.display_address(subaddress_index{account_index, address_index}, payment_id);
}
//----------------------------------------------------------------------------------------------------
uint8_t wallet2::estimate_current_hard_fork(const uint64_t height) const
{
// Get the last-seen top height by the wallet
uint64_t guessed_height = (height > 0) ? height : m_blockchain.size();
// Get the correct hardfork table, based on current net type
const hardfork_t *hfs =
(m_nettype == cryptonote::MAINNET) ? mainnet_hard_forks :
(m_nettype == cryptonote::TESTNET) ? testnet_hard_forks :
stagenet_hard_forks;
size_t hfs_count =
(m_nettype == cryptonote::MAINNET) ? num_mainnet_hard_forks :
(m_nettype == cryptonote::TESTNET) ? num_testnet_hard_forks :
num_stagenet_hard_forks;
// Iterate over the hard fork table, to see what the current fork is for the guessed height
for (size_t i = hfs_count-1; i>=0; --i) {
if (hfs[i].height <= guessed_height)
return hfs[i].version;
}
// return "no value found" to the caller
return 0;
}
//----------------------------------------------------------------------------------------------------
uint8_t wallet2::get_current_hard_fork()
{
if (m_offline)
+51 -10
View File
@@ -47,7 +47,7 @@
#include <random>
#include "include_base_utils.h"
#include "carrot_impl/account.h"
#include "carrot_core/account.h"
#include "carrot_impl/carrot_offchain_serialization.h"
#include "cryptonote_basic/account.h"
#include "cryptonote_basic/account_boost_serialization.h"
@@ -391,6 +391,19 @@ private:
return output_public_key;
};
const crypto::public_key get_eph_public_key() const {
crypto::public_key eph_public_key;
eph_public_key = cryptonote::get_tx_pub_key_from_extra(m_tx);
if (eph_public_key != crypto::null_pkey)
return eph_public_key;
const auto additional_pub_keys = cryptonote::get_additional_tx_pub_keys_from_extra(m_tx);
if (!additional_pub_keys.empty())
return additional_pub_keys[m_internal_output_index];
return crypto::null_pkey;
};
bool is_carrot() const
{
THROW_WALLET_EXCEPTION_IF(m_tx.vout.size() <= m_internal_output_index,
@@ -483,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)
@@ -497,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()
};
@@ -846,6 +865,7 @@ private:
std::string m_description;
bool m_is_subaddress;
bool m_has_payment_id;
bool m_is_carrot;
BEGIN_SERIALIZE_OBJECT()
VERSION_FIELD(0)
@@ -854,6 +874,7 @@ private:
FIELD(m_description)
FIELD(m_is_subaddress)
FIELD(m_has_payment_id)
FIELD(m_is_carrot)
END_SERIALIZE()
};
@@ -1004,6 +1025,7 @@ private:
* \param account_public_address The account's public address
* \param viewkey view secret key
* \param create_address_file Whether to create an address file
* \param is_carrot Whether viewkey is k_v (CN) or s_vb (Carrot)
*/
void generate(const std::string& wallet, const epee::wipeable_string& password,
const cryptonote::account_public_address &account_public_address,
@@ -1179,9 +1201,11 @@ private:
boost::optional<cryptonote::subaddress_index> get_subaddress_index(const cryptonote::account_public_address& address) const;
crypto::public_key get_subaddress_spend_public_key(const cryptonote::subaddress_index& index) const;
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 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; }
@@ -1402,7 +1426,6 @@ private:
if (ver < 20)
return;
a & m_subaddresses.parent();
a & m_subaddresses_extended.parent();
std::unordered_map<cryptonote::subaddress_index, crypto::public_key> dummy_subaddresses_inv;
a & dummy_subaddresses_inv;
a & m_subaddress_labels;
@@ -1443,9 +1466,13 @@ 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()
@@ -1469,7 +1496,6 @@ private:
FIELD(m_scanned_pool_txs[0])
FIELD(m_scanned_pool_txs[1])
FIELD(m_subaddresses)
FIELD(m_subaddresses_extended)
FIELD(m_subaddress_labels)
FIELD(m_additional_tx_keys)
FIELD(m_attributes)
@@ -1489,9 +1515,13 @@ 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)
FIELD(m_return_output_info)
END_SERIALIZE()
/*!
@@ -1630,14 +1660,15 @@ private:
* \brief GUI Address book get/store
*/
std::vector<address_book_row> get_address_book() const { return m_address_book; }
bool add_address_book_row(const cryptonote::account_public_address &address, const crypto::hash8 *payment_id, const std::string &description, bool is_subaddress);
bool set_address_book_row(size_t row_id, const cryptonote::account_public_address &address, const crypto::hash8 *payment_id, const std::string &description, bool is_subaddress);
bool add_address_book_row(const cryptonote::account_public_address &address, const crypto::hash8 *payment_id, const std::string &description, bool is_subaddress, bool is_carrot);
bool set_address_book_row(size_t row_id, const cryptonote::account_public_address &address, const crypto::hash8 *payment_id, const std::string &description, bool is_subaddress, bool is_carrot);
bool delete_address_book_row(std::size_t row_id);
uint64_t get_num_rct_outputs();
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);
@@ -2133,6 +2164,7 @@ private:
cryptonote::account_public_address m_account_public_address;
serializable_unordered_map<crypto::public_key, cryptonote::subaddress_index> m_subaddresses;
serializable_unordered_map<crypto::public_key, carrot::subaddress_index_extended> m_subaddresses_extended;
serializable_unordered_map<crypto::public_key, carrot::return_output_info_t> m_return_output_info;
std::vector<std::vector<std::string>> m_subaddress_labels;
serializable_unordered_map<crypto::hash, std::string> m_tx_notes;
serializable_unordered_map<std::string, std::string> m_attributes;
@@ -2269,11 +2301,11 @@ 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)
BOOST_CLASS_VERSION(tools::wallet2::address_book_row, 18)
BOOST_CLASS_VERSION(tools::wallet2::address_book_row, 19)
BOOST_CLASS_VERSION(tools::wallet2::reserve_proof_entry, 0)
BOOST_CLASS_VERSION(tools::wallet2::unsigned_tx_set, 1)
BOOST_CLASS_VERSION(tools::wallet2::signed_tx_set, 1)
@@ -2598,6 +2630,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>
@@ -2651,6 +2689,9 @@ namespace boost
a & x.m_has_payment_id;
if (x.m_has_payment_id)
a & x.m_payment_id;
if (ver < 19)
return;
a & x.m_is_carrot;
}
template <class Archive>
+34 -14
View File
@@ -438,7 +438,9 @@ namespace tools
entry.type = pd.m_coinbase ? "block" : "in";
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);
//entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index);
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);
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -470,7 +472,9 @@ namespace tools
entry.subaddr_index = { pd.m_subaddr_account, 0 };
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});
//entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0});
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);
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -503,7 +507,9 @@ namespace tools
entry.subaddr_index = { pd.m_subaddr_account, 0 };
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});
//entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0});
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);
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -526,7 +532,9 @@ namespace tools
entry.type = "pool";
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);
//entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index);
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);
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -534,6 +542,8 @@ namespace tools
{
if (!m_wallet) return not_open(er);
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
@@ -590,7 +600,7 @@ namespace tools
info.account_index = account_index;
info.address_index = i;
cryptonote::subaddress_index index = {info.account_index, info.address_index};
info.address = m_wallet->get_subaddress_as_str(index);
info.address = m_wallet->get_subaddress_as_str({{info.account_index, info.address_index}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
info.balance = balance_per_subaddress[i];
info.unlocked_balance = unlocked_balance_per_subaddress[i].first;
info.blocks_to_unlock = unlocked_balance_per_subaddress[i].second.first;
@@ -616,6 +626,7 @@ namespace tools
bool wallet_rpc_server::on_getaddress(const wallet_rpc::COMMAND_RPC_GET_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
if (!m_wallet) return not_open(er);
try
{
THROW_WALLET_EXCEPTION_IF(req.account_index >= m_wallet->get_num_subaddress_accounts(), error::account_index_outofbound);
@@ -638,7 +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});
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();
@@ -731,6 +744,7 @@ namespace tools
bool wallet_rpc_server::on_get_accounts(const wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
if (!m_wallet) return not_open(er);
try
{
res.total_balance = 0;
@@ -751,7 +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, 0}, carrot::AddressDeriveType::Auto});
//for (const auto& asset: asset_types) {
info.balance = m_wallet->balance(subaddr_index.major, "SAL1", req.strict_balances);
@@ -2084,6 +2098,7 @@ namespace tools
bool wallet_rpc_server::on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
if (!m_wallet) return not_open(er);
crypto::hash payment_id;
crypto::hash8 payment_id8;
cryptonote::blobdata payment_id_blob;
@@ -2124,7 +2139,8 @@ 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);
}
@@ -2134,6 +2150,7 @@ namespace tools
bool wallet_rpc_server::on_get_bulk_payments(const wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_BULK_PAYMENTS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
res.payments.clear();
if (!m_wallet) return not_open(er);
/* If the payment ID list is empty, we get payments to any payment ID (or lack thereof) */
@@ -2151,7 +2168,8 @@ 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));
}
@@ -2206,7 +2224,8 @@ 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));
}
@@ -3286,9 +3305,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);
address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), entry.m_address, entry.m_payment_id, entry.m_is_carrot);
else
address = get_account_address_as_str(m_wallet->nettype(), entry.m_is_subaddress, entry.m_address);
address = get_account_address_as_str(m_wallet->nettype(), entry.m_is_subaddress, entry.m_address, entry.m_is_carrot);
res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx, address, entry.m_description});
}
}
@@ -3328,7 +3347,7 @@ namespace tools
er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + req.address;
return false;
}
if (!m_wallet->add_address_book_row(info.address, info.has_payment_id ? &info.payment_id : NULL, req.description, info.is_subaddress))
if (!m_wallet->add_address_book_row(info.address, info.has_payment_id ? &info.payment_id : NULL, req.description, info.is_subaddress, info.is_carrot))
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "Failed to add address book entry";
@@ -3387,12 +3406,13 @@ namespace tools
entry.m_is_subaddress = info.is_subaddress;
if (info.has_payment_id)
entry.m_payment_id = info.payment_id;
entry.m_is_carrot = info.is_carrot;
}
if (req.set_description)
entry.m_description = req.description;
if (!m_wallet->set_address_book_row(req.index, entry.m_address, req.set_address && entry.m_has_payment_id ? &entry.m_payment_id : NULL, entry.m_description, entry.m_is_subaddress))
if (!m_wallet->set_address_book_row(req.index, entry.m_address, req.set_address && entry.m_has_payment_id ? &entry.m_payment_id : NULL, entry.m_description, entry.m_is_subaddress, entry.m_is_carrot))
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "Failed to edit address book entry";
@@ -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,
+22 -6
View File
@@ -454,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();
@@ -475,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);
@@ -492,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);
@@ -521,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);
@@ -545,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);
@@ -562,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);
@@ -714,6 +728,7 @@ static void subtest_2out_transfer_get_output_enote_proposals_completeness(const
std::vector<RCTOutputEnoteProposal> enote_proposals;
encrypted_payment_id_t encrypted_payment_id;
size_t change_index;
RCTOutputEnoteProposal return_enote;
std::unordered_map<crypto::public_key, size_t> normal_payments_indices;
get_output_enote_proposals({bob_payment_proposal},
{alice_payment_proposal},
@@ -722,6 +737,7 @@ static void subtest_2out_transfer_get_output_enote_proposals_completeness(const
&alice.k_view_incoming_dev,
tx_first_key_image,
enote_proposals,
return_enote,
encrypted_payment_id,
cryptonote::transaction_type::TRANSFER,
change_index,
+3
View File
@@ -417,7 +417,10 @@ TEST(carrot_fcmp, receive_scan_spend_and_verify_serialized_carrot_tx)
tx_proposal.key_images_sorted,
tx_proposal.sources,
tx_proposal.fee,
tx_proposal.tx_type,
tx_proposal.amount_burnt,
{}, // change_masks
{}, // return_enote
encrypted_payment_id);
ASSERT_EQ(2, tx.version);
+71 -68
View File
@@ -38,7 +38,6 @@
#include "carrot_impl/tx_proposal_utils.h"
#include "carrot_impl/input_selection.h"
#include "carrot_mock_helpers.h"
#include "common/container_helpers.h"
#include "crypto/generators.h"
#include "cryptonote_basic/account.h"
#include "cryptonote_basic/subaddress_index.h"
@@ -63,7 +62,7 @@ static void unittest_scan_enote_set_multi_account(const std::vector<CarrotEnoteV
res.reserve(accounts.size());
for (const auto *account : accounts)
mock_scan_enote_set(enotes, encrypted_payment_id, *account, tools::add_element(res));
mock_scan_enote_set(enotes, encrypted_payment_id, *account, res.emplace_back());
}
//----------------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------------
@@ -206,6 +205,7 @@ static void subtest_multi_account_transfer_over_transaction(const unittest_trans
std::vector<RCTOutputEnoteProposal> rederived_output_enote_proposals;
encrypted_payment_id_t rederived_encrypted_payment_id;
size_t change_index;
RCTOutputEnoteProposal return_enote;
std::unordered_map<crypto::public_key, size_t> normal_payments_indices;
get_output_enote_proposals(tx_proposal.normal_payment_proposals,
modified_selfsend_payment_proposals,
@@ -214,6 +214,7 @@ static void subtest_multi_account_transfer_over_transaction(const unittest_trans
&ss_keys.k_view_incoming_dev,
parsed_key_images.at(0),
rederived_output_enote_proposals,
return_enote,
rederived_encrypted_payment_id,
cryptonote::transaction_type::TRANSFER,
change_index,
@@ -355,7 +356,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_1)
acc1.generate();
// 1 normal payment
CarrotPaymentProposalV1 &normal_payment_proposal = tools::add_element( tx_proposal.per_account_payments[0].second).first;
CarrotPaymentProposalV1 &normal_payment_proposal = tx_proposal.per_account_payments[0].second.emplace_back().first;
normal_payment_proposal = CarrotPaymentProposalV1{
.destination = acc0.cryptonote_address(),
.amount = crypto::rand_idx((rct::xmr_amount) 1ull << 63),
@@ -394,21 +395,21 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_2)
tx_proposal.self_sender_index = 2;
// 1 subaddress payment
tools::add_element(acc0.second).first = 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()
};
// 1 main address payment
tools::add_element(acc1.second).first = CarrotPaymentProposalV1{
acc1.second.emplace_back().first = CarrotPaymentProposalV1{
.destination = acc1.first.cryptonote_address(),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
};
// 1 integrated address payment
tools::add_element(acc3.second).first = CarrotPaymentProposalV1{
acc3.second.emplace_back().first = CarrotPaymentProposalV1{
.destination = acc3.first.cryptonote_address(gen_payment_id()),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
@@ -443,7 +444,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_3)
tx_proposal.self_sender_index = 2;
// 2 subaddress payment
tools::add_element(acc0.second).first = 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()
@@ -452,7 +453,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_3)
acc0.second.back().first.randomness = gen_janus_anchor(); //mangle anchor_norm
// 2 main address payment
tools::add_element(acc1.second).first = CarrotPaymentProposalV1{
acc1.second.emplace_back().first = CarrotPaymentProposalV1{
.destination = acc1.first.cryptonote_address(),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
@@ -461,7 +462,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_3)
acc1.second.back().first.randomness = gen_janus_anchor(); //mangle anchor_norm
// 1 integrated address payment
tools::add_element(acc3.second).first = CarrotPaymentProposalV1{
acc3.second.emplace_back().first = CarrotPaymentProposalV1{
.destination = acc3.first.cryptonote_address(gen_payment_id()),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
@@ -496,7 +497,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_4)
tx_proposal.self_sender_index = 2;
// 2 subaddress payment
tools::add_element(acc0.second).first = 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()
@@ -505,7 +506,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_4)
acc0.second.back().first.randomness = gen_janus_anchor(); //mangle anchor_norm
// 2 main address payment
tools::add_element(acc1.second).first = CarrotPaymentProposalV1{
acc1.second.emplace_back().first = CarrotPaymentProposalV1{
.destination = acc1.first.cryptonote_address(),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
@@ -514,14 +515,14 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_4)
acc1.second.back().first.randomness = gen_janus_anchor(); //mangle anchor_norm
// 1 integrated address payment
tools::add_element(acc3.second).first = CarrotPaymentProposalV1{
acc3.second.emplace_back().first = CarrotPaymentProposalV1{
.destination = acc3.first.cryptonote_address(gen_payment_id()),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
};
// 1 main address selfsend
tools::add_element(tx_proposal.explicit_selfsend_proposals).first.proposal = CarrotPaymentProposalSelfSendV1{
tx_proposal.explicit_selfsend_proposals.emplace_back().first.proposal = CarrotPaymentProposalSelfSendV1{
.destination_address_spend_pubkey = acc2.first.carrot_account_spend_pubkey,
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.enote_type = CarrotEnoteType::PAYMENT,
@@ -529,7 +530,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_4)
};
// 1 subaddress selfsend
tools::add_element(tx_proposal.explicit_selfsend_proposals).first = CarrotPaymentProposalVerifiableSelfSendV1{
tx_proposal.explicit_selfsend_proposals.emplace_back().first = CarrotPaymentProposalVerifiableSelfSendV1{
.proposal = CarrotPaymentProposalSelfSendV1{
.destination_address_spend_pubkey = acc2.first.subaddress({{4, 19}}).address_spend_pubkey,
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
@@ -560,7 +561,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_5)
acc1.generate(AddressDeriveType::PreCarrot);
// 1 normal payment
CarrotPaymentProposalV1 &normal_payment_proposal = tools::add_element( tx_proposal.per_account_payments[0].second).first;
CarrotPaymentProposalV1 &normal_payment_proposal = tx_proposal.per_account_payments[0].second.emplace_back().first;
normal_payment_proposal = CarrotPaymentProposalV1{
.destination = acc0.cryptonote_address(),
.amount = crypto::rand_idx((rct::xmr_amount) 1ull << 63),
@@ -599,21 +600,21 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_6)
tx_proposal.self_sender_index = 2;
// 1 subaddress payment
tools::add_element(acc0.second).first = 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()
};
// 1 main address payment
tools::add_element(acc1.second).first = CarrotPaymentProposalV1{
acc1.second.emplace_back().first = CarrotPaymentProposalV1{
.destination = acc1.first.cryptonote_address(),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
};
// 1 integrated address payment
tools::add_element(acc3.second).first = CarrotPaymentProposalV1{
acc3.second.emplace_back().first = CarrotPaymentProposalV1{
.destination = acc3.first.cryptonote_address(gen_payment_id()),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
@@ -648,7 +649,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_7)
tx_proposal.self_sender_index = 2;
// 2 subaddress payment
tools::add_element(acc0.second).first = 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()
@@ -657,7 +658,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_7)
acc0.second.back().first.randomness = gen_janus_anchor(); //mangle anchor_norm
// 2 main address payment
tools::add_element(acc1.second).first = CarrotPaymentProposalV1{
acc1.second.emplace_back().first = CarrotPaymentProposalV1{
.destination = acc1.first.cryptonote_address(),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
@@ -666,7 +667,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_7)
acc1.second.back().first.randomness = gen_janus_anchor(); //mangle anchor_norm
// 1 integrated address payment
tools::add_element(acc3.second).first = CarrotPaymentProposalV1{
acc3.second.emplace_back().first = CarrotPaymentProposalV1{
.destination = acc3.first.cryptonote_address(gen_payment_id()),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
@@ -701,7 +702,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_8)
tx_proposal.self_sender_index = 2;
// 2 subaddress payment
tools::add_element(acc0.second).first = 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()
@@ -710,7 +711,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_8)
acc0.second.back().first.randomness = gen_janus_anchor(); //mangle anchor_norm
// 2 main address payment
tools::add_element(acc1.second).first = CarrotPaymentProposalV1{
acc1.second.emplace_back().first = CarrotPaymentProposalV1{
.destination = acc1.first.cryptonote_address(),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
@@ -719,14 +720,14 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_8)
acc1.second.back().first.randomness = gen_janus_anchor(); //mangle anchor_norm
// 1 integrated address payment
tools::add_element(acc3.second).first = CarrotPaymentProposalV1{
acc3.second.emplace_back().first = CarrotPaymentProposalV1{
.destination = acc3.first.cryptonote_address(gen_payment_id()),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
};
// 1 main address selfsend
tools::add_element(tx_proposal.explicit_selfsend_proposals).first.proposal = CarrotPaymentProposalSelfSendV1{
tx_proposal.explicit_selfsend_proposals.emplace_back().first.proposal = CarrotPaymentProposalSelfSendV1{
.destination_address_spend_pubkey = acc2.first.carrot_account_spend_pubkey,
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.enote_type = CarrotEnoteType::PAYMENT,
@@ -734,7 +735,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_8)
};
// 1 subaddress selfsend
tools::add_element(tx_proposal.explicit_selfsend_proposals).first = CarrotPaymentProposalVerifiableSelfSendV1{
tx_proposal.explicit_selfsend_proposals.emplace_back().first = CarrotPaymentProposalVerifiableSelfSendV1{
.proposal = CarrotPaymentProposalSelfSendV1{
.destination_address_spend_pubkey = acc2.first.subaddress({{4, 19}}).address_spend_pubkey,
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
@@ -766,7 +767,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_9)
acc1.generate();
// 1 normal payment (subtractable)
CarrotPaymentProposalV1 &normal_payment_proposal = tools::add_element( tx_proposal.per_account_payments[0].second).first;
CarrotPaymentProposalV1 &normal_payment_proposal = tx_proposal.per_account_payments[0].second.emplace_back().first;
normal_payment_proposal = CarrotPaymentProposalV1{
.destination = acc0.cryptonote_address(),
.amount = crypto::rand_idx((rct::xmr_amount) 1ull << 63),
@@ -807,25 +808,25 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_10)
tx_proposal.self_sender_index = 2;
// 1 subaddress payment (subtractable)
tools::add_element(acc0.second) = {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
tools::add_element(acc1.second).first = CarrotPaymentProposalV1{
acc1.second.emplace_back().first = CarrotPaymentProposalV1{
.destination = acc1.first.cryptonote_address(),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
};
// 1 integrated address payment
tools::add_element(acc3.second) = {CarrotPaymentProposalV1{
acc3.second.emplace_back().first = CarrotPaymentProposalV1{
.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;
@@ -857,7 +858,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_11)
tx_proposal.self_sender_index = 2;
// 2 subaddress payment
tools::add_element(acc0.second).first = 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()
@@ -867,7 +868,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_11)
acc0.second.back().second = true; //set copy as subtractable
// 2 main address payment
tools::add_element(acc1.second).first = CarrotPaymentProposalV1{
acc1.second.emplace_back().first = CarrotPaymentProposalV1{
.destination = acc1.first.cryptonote_address(),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
@@ -877,7 +878,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_11)
acc1.second.back().second = true; //set copy as subtractable
// 1 integrated address payment
tools::add_element(acc3.second).first = CarrotPaymentProposalV1{
acc3.second.emplace_back().first = CarrotPaymentProposalV1{
.destination = acc3.first.cryptonote_address(gen_payment_id()),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
@@ -913,17 +914,17 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_12)
tx_proposal.self_sender_index = 2;
// 2 subaddress payment (1 subtractable)
tools::add_element(acc0.second) = {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
// 2 main address payment
tools::add_element(acc1.second).first = CarrotPaymentProposalV1{
acc1.second.emplace_back().first = CarrotPaymentProposalV1{
.destination = acc1.first.cryptonote_address(),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
@@ -932,14 +933,14 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_12)
acc1.second.back().first.randomness = gen_janus_anchor(); //mangle anchor_norm
// 1 integrated address payment (subtractable)
tools::add_element(acc3.second) = {CarrotPaymentProposalV1{
acc3.second.emplace_back().first = CarrotPaymentProposalV1{
.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
tools::add_element(tx_proposal.explicit_selfsend_proposals).first.proposal = CarrotPaymentProposalSelfSendV1{
tx_proposal.explicit_selfsend_proposals.emplace_back().first.proposal = CarrotPaymentProposalSelfSendV1{
.destination_address_spend_pubkey = acc2.first.carrot_account_spend_pubkey,
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.enote_type = CarrotEnoteType::PAYMENT,
@@ -947,14 +948,14 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_12)
};
// 1 subaddress selfsend (subtractable)
tools::add_element(tx_proposal.explicit_selfsend_proposals) = {CarrotPaymentProposalVerifiableSelfSendV1{
tx_proposal.explicit_selfsend_proposals.emplace_back().first = CarrotPaymentProposalVerifiableSelfSendV1{
.proposal = CarrotPaymentProposalSelfSendV1{
.destination_address_spend_pubkey = acc2.first.subaddress({{4, 19}}).address_spend_pubkey,
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.enote_type = CarrotEnoteType::CHANGE
},
.subaddr_index = {{4, 19}}
}, true};
};
// specify fee per weight
tx_proposal.fee_per_weight = 314159;
@@ -979,7 +980,7 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_13)
acc1.generate(AddressDeriveType::PreCarrot);
// 1 normal payment (subtractable)
CarrotPaymentProposalV1 &normal_payment_proposal = tools::add_element( tx_proposal.per_account_payments[0].second).first;
CarrotPaymentProposalV1 &normal_payment_proposal = tx_proposal.per_account_payments[0].second.emplace_back().first;
normal_payment_proposal = CarrotPaymentProposalV1{
.destination = acc0.cryptonote_address(),
.amount = crypto::rand_idx((rct::xmr_amount) 1ull << 63),
@@ -1020,25 +1021,25 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_14)
tx_proposal.self_sender_index = 2;
// 1 subaddress payment (subtractable)
tools::add_element(acc0.second) = {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
tools::add_element(acc1.second).first = CarrotPaymentProposalV1{
acc1.second.emplace_back().first = CarrotPaymentProposalV1{
.destination = acc1.first.cryptonote_address(),
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
.randomness = gen_janus_anchor()
};
// 1 integrated address payment (subtractable)
tools::add_element(acc3.second) = {CarrotPaymentProposalV1{
acc3.second.emplace_back().first = CarrotPaymentProposalV1{
.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;
@@ -1070,29 +1071,29 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_15)
tx_proposal.self_sender_index = 2;
// 2 subaddress payment (subtractable)
tools::add_element(acc0.second) = {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
// 2 main address payment (subtractable)
tools::add_element(acc1.second) = {CarrotPaymentProposalV1{
acc1.second.emplace_back().first = CarrotPaymentProposalV1{
.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
// 1 integrated address payment (subtractable)
tools::add_element(acc3.second) = {CarrotPaymentProposalV1{
acc3.second.emplace_back().first = CarrotPaymentProposalV1{
.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;
@@ -1124,40 +1125,42 @@ TEST(carrot_impl, multi_account_transfer_over_transaction_16)
tx_proposal.self_sender_index = 2;
// 2 subaddress payment (subtractable)
tools::add_element(acc0.second) = {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
// 2 main address payment (subtractable)
tools::add_element(acc1.second) = {CarrotPaymentProposalV1{
acc1.second.emplace_back().first = CarrotPaymentProposalV1{
.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
// 1 integrated address payment (subtractable)
tools::add_element(acc3.second) = {CarrotPaymentProposalV1{
acc3.second.emplace_back().first = CarrotPaymentProposalV1{
.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)
tools::add_element(tx_proposal.explicit_selfsend_proposals) = {{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};
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)
tools::add_element(tx_proposal.explicit_selfsend_proposals) = {CarrotPaymentProposalVerifiableSelfSendV1{
tx_proposal.explicit_selfsend_proposals.emplace_back() = {CarrotPaymentProposalVerifiableSelfSendV1{
.proposal = CarrotPaymentProposalSelfSendV1{
.destination_address_spend_pubkey = acc2.first.subaddress({{4, 19}}).address_spend_pubkey,
.amount = crypto::rand_idx<rct::xmr_amount>(1000000),
+2
View File
@@ -188,6 +188,7 @@ static void subtest_legacy_2out_transfer_get_output_enote_proposals_completeness
std::vector<RCTOutputEnoteProposal> enote_proposals;
encrypted_payment_id_t encrypted_payment_id;
size_t change_index;
RCTOutputEnoteProposal return_enote;
std::unordered_map<crypto::public_key, size_t> normal_payments_indices;
get_output_enote_proposals({bob_payment_proposal},
{alice_payment_proposal},
@@ -196,6 +197,7 @@ static void subtest_legacy_2out_transfer_get_output_enote_proposals_completeness
&alive_k_v_dev,
tx_first_key_image,
enote_proposals,
return_enote,
encrypted_payment_id,
cryptonote::transaction_type::TRANSFER,
change_index,
+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;
+18 -12
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));
@@ -226,7 +229,7 @@ std::tuple<std::vector<RCTOutputEnoteProposal>, crypto::public_key> make_return_
get_output_proposal_return_v1(
proposal_return,
tx_return_first_key_image,
&bob.s_view_balance_dev,
nullptr, // s_view_balance_dev
enote_proposal_return,
encrypted_payment_id_return
);
@@ -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));
+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);
+2 -2
View File
@@ -157,7 +157,7 @@ TEST(x25519, scmul_key_convergence)
for (unsigned char j = 0; j < 8; ++j)
{
// add 2^i + j (sometimes with duplicates, which is okay)
mx25519_privkey &s = tools::add_element(scalars);
mx25519_privkey &s = scalars.emplace_back();
memset(s.data, 0, sizeof(mx25519_privkey));
const int msb_byte_index = i >> 3;
const int msb_bit_index = i & 7;
@@ -169,7 +169,7 @@ TEST(x25519, scmul_key_convergence)
scalars.push_back(hex2pod<mx25519_privkey>("ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f"));
// add random
const rct::key a = rct::skGen();
memcpy(tools::add_element(scalars).data, &a, sizeof(mx25519_privkey));
memcpy(scalars.emplace_back().data, &a, sizeof(mx25519_privkey));
std::vector<std::pair<rct::key, mx25519_pubkey>> points;
// add base point