Compare commits

...

121 Commits

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

* added fast sync points up to 325,000

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

* RPC address formatting fixes

---------

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

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

* added fast sync points up to 325,000

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

* initial import of CLI wallet improvements for address printing

* updated wallet address display in various places

---------

Co-authored-by: Some Random Crypto Guy <somerandomcryptoguy@protonmail.com>
2025-10-03 13:10:28 +01:00
Some Random Crypto Guy ce69272638 Merge branch 'main' into develop 2025-10-02 14:41:30 +01:00
Some Random Crypto Guy 2b95d100b3 added fast sync points up to 325,000 (part 2) 2025-10-01 17:35:35 +01:00
Some Random Crypto Guy b7706d4def added fast sync points up to 325,000 2025-10-01 17:05:49 +01:00
Some Random Crypto Guy 24f9916287 Merge branch 'develop' of https://github.com/salvium/salvium into develop 2025-05-08 09:21:06 +01:00
Some Random Crypto Guy cd22e55296 removed compilation warnings from scanner 2025-03-19 11:26:47 +00:00
125 changed files with 49266 additions and 40271 deletions
+4 -4
View File
@@ -22,7 +22,7 @@ env:
jobs:
build-cross:
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
env:
CCACHE_TEMPDIR: /tmp/.ccache-temp
strategy:
@@ -52,10 +52,10 @@ jobs:
packages: "gperf cmake python3-zmq libdbus-1-dev libharfbuzz-dev"
- name: "Cross-Mac x86_64"
host: "x86_64-apple-darwin"
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev python3-setuptools-git"
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python3-dev python3-setuptools-git"
- name: "Cross-Mac aarch64"
host: "aarch64-apple-darwin"
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev python3-setuptools-git"
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python3-dev python3-setuptools-git"
- name: "x86_64 Freebsd"
host: "x86_64-unknown-freebsd"
packages: "clang-8 gperf cmake python3-zmq libdbus-1-dev libharfbuzz-dev"
@@ -105,7 +105,7 @@ jobs:
${{env.CCACHE_SETTINGS}}
make depends target=${{ matrix.toolchain.host }} -j2
- uses: actions/upload-artifact@v4
if: ${{ matrix.toolchain.host == 'x86_64-w64-mingw32' || matrix.toolchain.host == 'x86_64-apple-darwin' || matrix.toolchain.host == 'aarch64-apple-darwin' || matrix.toolchain.host == 'x86_64-unknown-linux-gnu' }}
if: ${{ matrix.toolchain.host == 'x86_64-w64-mingw32' || matrix.toolchain.host == 'x86_64-apple-darwin' || matrix.toolchain.host == 'aarch64-apple-darwin' || matrix.toolchain.host == 'x86_64-unknown-linux-gnu' || matrix.toolchain.host == 'aarch64-linux-gnu' }}
with:
name: ${{ matrix.toolchain.name }}
path: |
+4 -2
View File
@@ -3,14 +3,16 @@ FROM ubuntu:24.04 AS builder
ENV DEBIAN_FRONTEND=noninteractive
# Install build dependencies
# Install build dependencies including cross-compilers
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential cmake pkg-config git imagemagick libcap-dev librsvg2-bin libz-dev \
g++-mingw-w64-x86-64 clang gcc-arm-none-eabi binutils-x86-64-linux-gnu libtiff-tools \
g++-x86-64-linux-gnu gcc-x86-64-linux-gnu binutils-aarch64-linux-gnu \
g++-aarch64-linux-gnu gcc-aarch64-linux-gnu crossbuild-essential-amd64 \
libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev \
libreadline-dev libexpat1-dev libpgm-dev qttools5-dev-tools libhidapi-dev libusb-1.0-0-dev \
libprotobuf-dev protobuf-compiler libudev-dev libboost-all-dev python3 ccache doxygen graphviz \
ca-certificates curl zip libtool gperf \
ca-certificates curl zip libtool gperf automake autoconf \
&& rm -rf /var/lib/apt/lists/*
# Clone the develop branch
+5 -5
View File
@@ -1,4 +1,4 @@
# Salvium One v1.0.0
# Salvium One v1.0.7
Copyright (c) 2023-2025, Salvium
Portions Copyright (c) 2014-2023, The Monero Project
@@ -172,7 +172,7 @@ invokes cmake commands as needed.
```bash
cd salvium
git checkout v1.0.0
git checkout v1.0.7
make
```
@@ -251,7 +251,7 @@ Tested on a Raspberry Pi Zero with a clean install of minimal Raspbian Stretch (
```bash
git clone https://github.com/salvium/salvium
cd salvium
git checkout v1.0.0
git checkout v1.0.7
```
* Build:
@@ -370,10 +370,10 @@ application.
cd salvium
```
* If you would like a specific [version/tag](https://github.com/salvium/salvium/tags), do a git checkout for that version. eg. 'v1.0.0'. If you don't care about the version and just want binaries from master, skip this step:
* If you would like a specific [version/tag](https://github.com/salvium/salvium/tags), do a git checkout for that version. eg. 'v1.0.7'. If you don't care about the version and just want binaries from master, skip this step:
```bash
git checkout v1.0.0
git checkout v1.0.7
```
* If you are on a 64-bit system, run:
+5
View File
@@ -0,0 +1,5 @@
48417220800f174a3613881a56131d25c49d344228fca124c8331e0b58a8ff06 salvium-v1.0.7-ubuntu22.04-linux-x86_64.zip
52226551a9e1842df46cf068292cfa3c1e05328e0695cf6723365d4401af19a6 salvium-v1.0.7-ubuntu22.04-linux-aarch64.zip
b40c3765479c9d5712e766ddd01314b63e5080472b7639d34388e6b74b36142e salvium-v1.0.7-macos-x86_64.zip
60b05548f69040fe901e336e8fc4189a1028a8d7ded09bad555b3c854a0e8d6e salvium-v1.0.7-macos-aarch64.zip
b2c3e0bb8254ad1f62a78d6670c6e5adba1e42bb823919faad1cbf5796d53910 salvium-v1.0.7-win64.zip
+5
View File
@@ -0,0 +1,5 @@
d0a8b0515ae2ee79849cbc17b0639bb7859e30efcd50e5b058540874cd0919ce salvium-v1.1.0-rc4-ubuntu22.04-linux-x86_64.zip
6528b8b23f09c574fc9383b48b88e87f99609ff5ce1b727872b5554505a69d77 salvium-v1.1.0-rc4-ubuntu22.04-linux-aarch64.zip
00ca183c47f852b8b30e4b87ce3b3536a0e97866e6027bf25d389b4a1bc9c471 salvium-v1.1.0-rc4-macos-x86_64.zip
50480b1043e9b5901576ab45f926b0b51a5d21237c6da549603ad1f13819e6ed salvium-v1.1.0-rc4-macos-aarch64.zip
adf96eee17e16f318fc047721acb5bfa8ae7ed1c0ff8d022da8ab5df64d8ce3f salvium-v1.1.0-rc4-win64.zip
+39 -8
View File
@@ -294,7 +294,22 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
time1 = epee::misc_utils::get_tick_count();
uint64_t num_rct_outs = 0;
oracle::asset_type_counts num_rct_outs_by_asset_type;
oracle::asset_type_counts_v2 num_rct_outs_by_asset_type;
// add newly created tokens
for (const std::pair<transaction, blobdata>& tx : txs)
{
if (tx.first.type == cryptonote::transaction_type::CREATE_TOKEN)
{
uint32_t asset_type_id = cryptonote::asset_id_from_type("sal" + tx.first.token_metadata.asset_type);
if (!num_rct_outs_by_asset_type.add_asset_type(asset_type_id))
throw std::runtime_error("Failed to add asset_type 'sal" + tx.first.token_metadata.asset_type + "'");
}
if (!is_tx_paid_for(tx.first))
throw std::runtime_error("TX is not paid for");
}
blobdata miner_bd = tx_to_blob(blk.miner_tx);
add_transaction(blk_hash, std::make_pair(blk.miner_tx, blobdata_ref(miner_bd)));
blobdata protocol_bd = tx_to_blob(blk.protocol_tx);
@@ -309,12 +324,14 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
std::string asset_type;
if (!get_output_asset_type(vout, asset_type))
throw std::runtime_error("Failed to get output asset type");
num_rct_outs_by_asset_type.add(asset_type, 1);
uint32_t asset_type_id = cryptonote::asset_id_from_type(asset_type);
num_rct_outs_by_asset_type.add_asset_type(asset_type_id);
num_rct_outs_by_asset_type.add(asset_type_id, 1);
}
}
std::map<std::string, int64_t> slippage_counts;
uint64_t audit_total = 0, yield_total = 0;
uint64_t audit_total = 0, token_total = 0, yield_total = 0;
if (blk.protocol_tx.version >= 2)
{
num_rct_outs += blk.protocol_tx.vout.size();
@@ -326,10 +343,12 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
std::string asset_type;
if (!get_output_asset_type(vout, asset_type))
throw std::runtime_error("Failed to get output asset type");
uint32_t asset_type_id = cryptonote::asset_id_from_type(asset_type);
// Update the RCT outs for the asset_type
num_rct_outs_by_asset_type.add_asset_type(asset_type_id);
num_rct_outs_by_asset_type.add(asset_type_id, 1);
// Update the RCT outs
num_rct_outs_by_asset_type.add(asset_type, 1);
// Update the amount tallies by DEDUCTING the minted amount
if (slippage_counts.count(asset_type) == 0)
slippage_counts[asset_type] = 0;
@@ -350,7 +369,9 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
throw std::runtime_error("Failed to get output asset type");
if (vout.amount == 0) {
++num_rct_outs;
num_rct_outs_by_asset_type.add(asset_type, 1);
uint32_t asset_type_id = cryptonote::asset_id_from_type(asset_type);
num_rct_outs_by_asset_type.add_asset_type(asset_type_id);
num_rct_outs_by_asset_type.add(asset_type_id, 1);
}
// Is this a CONVERT TX?
@@ -367,6 +388,16 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
audit_total += tx.first.amount_burnt;
}
// Is this an create_token TX?
if (tx.first.type == cryptonote::transaction_type::CREATE_TOKEN) {
token_total += tx.first.amount_burnt;
/*
uint32_t asset_type_id = cryptonote::asset_id_from_type(tx.first.token_metadata.asset_type);
if (!num_rct_outs_by_asset_type.add_asset_type(asset_type_id))
throw std::runtime_error("Failed to add asset_type '" + tx.first.token_metadata.asset_type + "' to RCT outputs");
*/
}
// Is this a STAKE TX?
if (tx.first.type == cryptonote::transaction_type::STAKE) {
yield_total += tx.first.amount_burnt;
@@ -420,7 +451,7 @@ uint64_t BlockchainDB::add_block( const std::pair<block, blobdata>& blck
// call out to subclass implementation to add the block & metadata
time1 = epee::misc_utils::get_tick_count();
add_block(blk, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, num_rct_outs, num_rct_outs_by_asset_type, blk_hash, slippage_total, yield_total, audit_total, nettype, ybi, abi);
add_block(blk, block_weight, long_term_block_weight, cumulative_difficulty, coins_generated, num_rct_outs, num_rct_outs_by_asset_type, blk_hash, slippage_total, yield_total, audit_total, token_total, nettype, ybi, abi);
TIME_MEASURE_FINISH(time1);
time_add_block1 += time1;
+30 -1
View File
@@ -218,6 +218,18 @@ typedef struct yield_tx_info_carrot {
carrot::encrypted_janus_anchor_t return_anchor_enc;
} yield_tx_info_carrot;
typedef struct token_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;
uint32_t asset_type_id;
} token_tx_info_carrot;
#define DBF_SAFE 1
#define DBF_FAST 2
#define DBF_FASTEST 4
@@ -437,11 +449,12 @@ private:
const difficulty_type& cumulative_difficulty,
const uint64_t& coins_generated,
uint64_t num_rct_outs,
oracle::asset_type_counts& cum_rct_by_asset_type,
oracle::asset_type_counts_v2& cum_rct_by_asset_type,
const crypto::hash& blk_hash,
uint64_t slippage_total,
uint64_t yield_total,
uint64_t audit_total,
uint64_t token_total,
const cryptonote::network_type nettype,
cryptonote::yield_block_info& ybi,
cryptonote::audit_block_info& abi
@@ -1212,6 +1225,20 @@ public:
virtual std::map<std::string, uint64_t> get_circulating_supply() const = 0;
/**
* @brief fetch the tokens from the blockchain
*
* @return the current token values
*/
virtual std::map<std::string, cryptonote::token_metadata_t> get_tokens() const = 0;
/**
* @brief determine if a TX has been paid for
*
* @return the current token values
*/
virtual bool is_tx_paid_for(const cryptonote::transaction& tx) const = 0;
/**
* <!--
* TODO: Rewrite (if necessary) such that all calls to remove_* are
@@ -1937,6 +1964,8 @@ public:
virtual int get_carrot_yield_tx_info(const uint64_t height, std::vector<yield_tx_info_carrot>& yti_container) const = 0;
virtual int get_token_tx_info(const uint64_t height, std::vector<token_tx_info_carrot>& tti_container) const = 0;
/**
* @brief set whether or not to automatically remove logs
+641 -26
View File
@@ -64,7 +64,7 @@ using epee::string_tools::pod_to_hex;
using namespace crypto;
// Increase when the DB structure changes
#define VERSION 2
#define VERSION 3
namespace
{
@@ -222,6 +222,12 @@ namespace
* audit_block_data block height {locked_coins, lc_total}
* audit_tx_data block height {txn hash, locked_coins, return_address}
*
* token_tx_data block height {txn hash, asset_type, supply, return_address}
*
* rct_count_info block height {serialised RCT asset counts}
*
* rollup_tx_info rollup_tag {serialised rollup_tx_data_t struct}
*
* Note: where the data items are of uniform size, DUPFIXED tables have
* been used to save space. In most of these cases, a dummy "zerokval"
* key is used when accessing the table; the Key listed above will be
@@ -300,6 +306,14 @@ 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* const LMDB_TOKEN_TXS = "token_txs";
const char* const LMDB_TOKENS = "tokens";
const char* const LMDB_RCT_COUNT_INFO = "rct_count_info";
const char* const LMDB_ROLLUP_TX_INFO = "rollup_tx_info";
const char zerokey[8] = {0};
const MDB_val zerokval = { sizeof(zerokey), (void *)zerokey };
@@ -357,7 +371,21 @@ typedef struct mdb_block_info_1
oracle::asset_type_counts bi_cum_rct_by_asset_type;
} mdb_block_info_1;
typedef mdb_block_info_1 mdb_block_info;
typedef struct mdb_block_info_2
{
uint64_t bi_height;
uint64_t bi_timestamp;
uint64_t bi_coins;
uint64_t bi_weight; // a size_t really but we need 32-bit compat
uint64_t bi_diff_lo;
uint64_t bi_diff_hi;
crypto::hash bi_hash;
uint64_t bi_cum_rct;
uint64_t bi_long_term_block_weight;
oracle::pricing_record bi_pricing_record;
} mdb_block_info_2;
typedef mdb_block_info_2 mdb_block_info;
typedef struct blk_height {
crypto::hash bh_hash;
@@ -879,6 +907,44 @@ int BlockchainLMDB::get_audit_tx_info(const uint64_t height, std::vector<yield_t
return 0;
}
int BlockchainLMDB::get_token_tx_info(const uint64_t height, std::vector<token_tx_info_carrot>& tti_container) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
// Clear the container
tti_container.clear();
// Query for the (presumably matured) TOKEN_TX_INFO information
TXN_PREFIX_RDONLY();
RCURSOR(token_txs);
MDB_val v;
MDB_val_set(k, height);
MDB_cursor_op op = MDB_SET;
while (1)
{
int ret = mdb_cursor_get(m_cur_token_txs, &k, &v, op);
op = MDB_NEXT_DUP;
if (ret == MDB_NOTFOUND)
break;
if (ret)
throw0(DB_ERROR(lmdb_error("Failed to enumerate audit TX info: ", ret).c_str()));
// Get the data
token_tx_info_carrot *p = (token_tx_info_carrot*)v.mv_data;
// Push result back into the container
tti_container.emplace_back(*p);
// Update the height retrospectively (because the DB stores the count of elements there to handle duplicates, because it's rubbish)
tti_container.back().block_height = height;
}
TXN_POSTFIX_RDONLY();
// Return success to caller
return 0;
}
int BlockchainLMDB::get_yield_block_info(const uint64_t height, yield_block_info& ybi) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
@@ -985,7 +1051,21 @@ int BlockchainLMDB::get_carrot_yield_tx_info(const uint64_t height, std::vector<
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)
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_v2& cum_rct_by_asset_type,
const crypto::hash& blk_hash,
uint64_t slippage_total,
uint64_t yield_total,
uint64_t audit_total,
uint64_t token_total,
const cryptonote::network_type nettype,
cryptonote::yield_block_info& ybi,
cryptonote::audit_block_info& abi)
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
@@ -1020,7 +1100,7 @@ void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t l
abi.block_height = m_height;
abi.locked_coins_this_block = audit_total;
// Put the YBI into the table
// Put the ABI into the table
MDB_val_set(key, m_height);
MDB_val_set(abi_val, abi);
result = mdb_cursor_put(m_cur_audit_blocks, &key, &abi_val, MDB_APPEND);
@@ -1070,6 +1150,7 @@ void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t l
CURSOR(blocks)
CURSOR(block_info)
CURSOR(rct_count_info)
// this call to mdb_cursor_put will change height()
cryptonote::blobdata block_blob(block_to_blob(blk));
@@ -1088,6 +1169,7 @@ void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t l
bi.bi_hash = blk_hash;
bi.bi_cum_rct = num_rct_outs;
bi.bi_pricing_record = blk.pricing_record;
if (m_height > 0)
{
uint64_t last_height = m_height-1;
@@ -1096,21 +1178,40 @@ void BlockchainLMDB::add_block(const block& blk, size_t block_weight, uint64_t l
throw1(BLOCK_DNE(lmdb_error("Failed to get block info: ", result).c_str()));
const mdb_block_info *bi_prev = (const mdb_block_info*)h.mv_data;
bi.bi_cum_rct += bi_prev->bi_cum_rct;
for (auto const& asset_type : oracle::ASSET_TYPES)
cum_rct_by_asset_type.add(asset_type, bi_prev->bi_cum_rct_by_asset_type[asset_type]);
oracle::asset_type_counts_v2 cum_rct_counts_prev;
MDB_val_copy<uint64_t> key_prev_height(last_height);
MDB_val val_prev_counts;
if ((result = mdb_cursor_get(m_cur_rct_count_info, &key_prev_height, &val_prev_counts, MDB_SET)))
throw1(BLOCK_DNE(lmdb_error("Failed to get previous rct count info: ", result).c_str()));
cum_rct_counts_prev.deserialize((uint8_t*)val_prev_counts.mv_data, val_prev_counts.mv_size);
for (auto const& asset_type_id : cum_rct_counts_prev.get_asset_types()) {
if (!cum_rct_by_asset_type.has_asset_type(asset_type_id)) {
cum_rct_by_asset_type.add_asset_type(asset_type_id);
}
cum_rct_by_asset_type.add(asset_type_id, cum_rct_counts_prev[asset_type_id]);
}
}
bi.bi_long_term_block_weight = long_term_block_weight;
bi.bi_cum_rct_by_asset_type = cum_rct_by_asset_type;
MDB_val_set(val, bi);
result = mdb_cursor_put(m_cur_block_info, (MDB_val *)&zerokval, &val, MDB_APPENDDUP);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to add block info to db transaction: ", result).c_str()));
result = mdb_cursor_put(m_cur_block_heights, (MDB_val *)&zerokval, &val_h, 0);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to add block height by hash to db transaction: ", result).c_str()));
// Update the rct_count_info table
std::vector<uint8_t> serialized_counts = cum_rct_by_asset_type.serialize();
MDB_val_copy<uint64_t> key_height(m_height);
MDB_val_sized(val_counts, serialized_counts);
CURSOR(rct_count_info)
result = mdb_cursor_put(m_cur_rct_count_info, &key_height, &val_counts, 0);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to add asset_type_counts to db transaction: ", result).c_str()));
// we use weight as a proxy for size, since we don't have size but weight is >= size
// and often actually equal
m_cum_size += block_weight;
@@ -1135,6 +1236,8 @@ void BlockchainLMDB::remove_block()
CURSOR(circ_supply_tally)
CURSOR(yield_blocks)
CURSOR(audit_blocks)
CURSOR(rct_count_info)
MDB_val_copy<uint64_t> k(m_height - 1);
MDB_val h = k;
if ((result = mdb_cursor_get(m_cur_block_info, (MDB_val *)&zerokval, &h, MDB_GET_BOTH)))
@@ -1157,7 +1260,6 @@ void BlockchainLMDB::remove_block()
throw1(DB_ERROR(lmdb_error("Failed to add removal of block info to db transaction: ", result).c_str()));
MDB_val_copy<uint64_t> k2(m_height - 1);
MDB_val v = k2;
if ((result = mdb_cursor_get(m_cur_yield_blocks, &k2, NULL, MDB_SET)))
throw1(BLOCK_DNE(lmdb_error("Attempting to remove yield block info that's not in the db: ", result).c_str()));
if ((result = mdb_cursor_del(m_cur_yield_blocks, 0)))
@@ -1167,6 +1269,11 @@ void BlockchainLMDB::remove_block()
if ((result = mdb_cursor_get(m_cur_audit_blocks, &k2, NULL, MDB_SET)) == 0)
if ((result = mdb_cursor_del(m_cur_audit_blocks, 0)))
throw1(DB_ERROR(lmdb_error("Failed to add removal of audit block info to db transaction: ", result).c_str()));
if ((result = mdb_cursor_get(m_cur_rct_count_info, &k2, NULL, MDB_SET)))
throw1(BLOCK_DNE(lmdb_error("Attempting to remove rct count info that's not in the db: ", result).c_str()));
if ((result = mdb_cursor_del(m_cur_rct_count_info, 0)))
throw1(DB_ERROR(lmdb_error("Failed to add removal of rct count info to db transaction: ", result).c_str()));
}
boost::multiprecision::int128_t
@@ -1234,6 +1341,32 @@ void write_circulating_supply_data(MDB_cursor *cur_circ_supply_tally, MDB_val id
throw0(DB_ERROR(lmdb_error("Failed to update tally for source circulating supply: ", result).c_str()));
}
inline std::string write_token_metadata(const cryptonote::token_metadata_t& token_metadata)
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
std::stringstream token_blob_str;
binary_archive<true> ba(token_blob_str);
bool r = ::serialization::serialize(ba, const_cast<cryptonote::token_metadata_t&>(token_metadata));
if (!r)
throw0(DB_ERROR("Failed to serialize token_metadata"));
return token_blob_str.str();
}
inline void read_token_metadata(const std::string& token_blob, cryptonote::token_metadata_t& token_metadata)
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
if (token_blob.empty())
throw0(DB_ERROR("Empty token_blob provided to read_token_metadata"));
binary_archive<false> ba{epee::strspan<std::uint8_t>(token_blob)};
bool r = ::serialization::serialize(ba, token_metadata);
if (!r)
throw0(DB_ERROR("Failed to deserialize token_metadata"));
}
uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, const std::pair<transaction, blobdata_ref>& txp, const crypto::hash& tx_hash, const crypto::hash& tx_prunable_hash, const bool miner_tx)
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
@@ -1254,6 +1387,9 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
CURSOR(yield_txs)
CURSOR(audit_txs)
CURSOR(carrot_yield_txs)
CURSOR(token_txs)
CURSOR(tokens)
CURSOR(rollup_tx_info)
MDB_val_set(val_tx_id, tx_id);
MDB_val_set(val_h, tx_hash);
@@ -1364,7 +1500,8 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
tx.type == cryptonote::transaction_type::CONVERT ||
tx.type == cryptonote::transaction_type::STAKE ||
tx.type == cryptonote::transaction_type::AUDIT ||
tx.type == cryptonote::transaction_type::TRANSFER)
tx.type == cryptonote::transaction_type::TRANSFER ||
tx.type == cryptonote::transaction_type::CREATE_TOKEN)
{
// Get the current tally value for the source currency type
@@ -1395,7 +1532,7 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
if (tx.type == cryptonote::transaction_type::PROTOCOL) {
// Iterate over all of the outputs for a PROTOCOL_TX since they're all MINTED
std::map<uint32_t, uint64_t> minted_amounts;
std::map<uint32_t, uint64_t> minted_amounts;
for (const auto& out: tx.vout) {
// Fetch the amount and output_asset_type for this output
@@ -1413,14 +1550,22 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
MDB_val_copy<uint64_t> source_idx(asset.first);
boost::multiprecision::int128_t source_tally = 0;
result = read_circulating_supply_data(m_cur_circ_supply_tally, source_idx, source_tally);
if (result == MDB_NOTFOUND)
throw0(DB_ERROR("minted asset not found"));
if (result == MDB_NOTFOUND){
std::string asset_symbol = cryptonote::asset_type_from_id(asset.first);
if (asset.first == 0x53414C31 /*SAL1*/ || asset.first == 0x53414C00 /*SAL*/) {
throw0(DB_ERROR(("Asset " + asset_symbol + " not found when updating circulating supply for PROTOCOL_TX").c_str()));
}
else{
LOG_PRINT_L1("First time minting asset, ID: " << asset.first << " symbol: " << asset_symbol << " in tx_id: " << tx_id);
}
}
else if (result)
throw0(DB_ERROR(lmdb_error("Failed to get circulating supply tally when adding db transaction: ", result).c_str()));
if (source_tally > source_tally + asset.second)
throw0(DB_ERROR("add_transaction_data() - mint overflow"));
boost::multiprecision::int128_t final_source_tally = source_tally + asset.second;
write_circulating_supply_data(m_cur_circ_supply_tally, source_idx, final_source_tally);
// Log can be changed - not burning here
LOG_PRINT_L1("tx ID " << tx_id << "\n\tAsset Type = " << cryptonote::asset_type_from_id(asset.first) << "\n\tTally before burn =" << source_tally.str() << "\n\tTally after burn =" << final_source_tally.str());
MDB_val_copy<uint64_t> burn_idx(cryptonote::asset_id_from_type("BURN"));
@@ -1428,13 +1573,80 @@ uint64_t BlockchainLMDB::add_transaction_data(const crypto::hash& blk_hash, cons
result = read_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, burn_tally);
if (result && result != MDB_NOTFOUND)
throw0(DB_ERROR(lmdb_error("Failed to get circulating supply tally when adding db transaction: ", result).c_str()));
if (burn_tally < asset.second)
uint64_t burn_amount = 0;
if (asset.first == 0x53414C00 || asset.first == 0x53414C31 || asset.first == 0x4255524E) { //BURN SAL and SAL1 // check
burn_amount = asset.second;
} else {
burn_amount = asset.second / 200;
}
if (burn_tally < burn_amount)
throw0(DB_ERROR("add_transaction_data() - burn underflow"));
boost::multiprecision::int128_t final_burn_tally = burn_tally - asset.second;
boost::multiprecision::int128_t final_burn_tally = burn_tally - burn_amount;
write_circulating_supply_data(m_cur_circ_supply_tally, burn_idx, final_burn_tally);
}
}
// Is there a rollup_tx to add?
if (tx.version >= TRANSACTION_VERSION_ENABLE_TOKENS &&
tx.type == cryptonote::transaction_type::ROLLUP &&
tx.layer2_rollup_data.txs.size())
{
// We already have the necessary values in the `layer2_rollup_data` member of the TX
for (const auto &rollup_tx: tx.layer2_rollup_data.txs) {
MDB_val_set(rtx_key, tx.rollup_binding_tag);
MDB_val_set(rtx_val, rollup_tx);
result = mdb_cursor_put(m_cur_rollup_tx_info, &rtx_key, &rtx_val, MDB_APPENDDUP);
}
}
// Is there token_tx data to add?
if (tx.version >= TRANSACTION_VERSION_ENABLE_TOKENS && tx.type == cryptonote::transaction_type::CREATE_TOKEN)
{
// Create the object we are going to write to the database
token_tx_info_carrot token_data;
token_data.block_height = m_height;
token_data.tx_hash = tx_hash;
token_data.return_pubkey = tx.protocol_tx_data.return_pubkey;
token_data.return_address = tx.protocol_tx_data.return_address;
token_data.return_view_tag = tx.protocol_tx_data.return_view_tag;
token_data.return_anchor_enc = tx.protocol_tx_data.return_anchor_enc;
token_data.locked_coins = tx.amount_burnt;
token_data.asset_type_id = cryptonote::asset_id_from_type("sal" + tx.token_metadata.asset_type);
// 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_token_txs, &val_height, &data, MDB_SET);
if (!result)
{
mdb_size_t num_elems = 0;
result = mdb_cursor_count(m_cur_token_txs, &num_elems);
if (result)
throw0(DB_ERROR(std::string("Failed to get number of token TXs for height: ").append(mdb_strerror(result)).c_str()));
token_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
token_data.block_height = 0;
// Now we know how many there are, write out the data to the DB
MDB_val_set(val_token_tx_data, token_data);
result = mdb_cursor_put(m_cur_token_txs, &val_height, &val_token_tx_data, MDB_APPENDDUP);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to add tx carrot token data to db transaction: ", result).c_str()));
// Now add an entry to the `tokens` table
MDB_val_copy<uint64_t> asset_type_id(token_data.asset_type_id);
std::stringstream token_blob_str;
std::string token_blob = write_token_metadata(tx.token_metadata);
MDB_val_sized(token, token_blob);
int result = mdb_cursor_put(m_cur_tokens, &asset_type_id, &token, 0);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to update token table: ", result).c_str()));
}
// Is there yield_tx data to add?
if (tx.version >= TRANSACTION_VERSION_CARROT && tx.type == cryptonote::transaction_type::STAKE)
{
@@ -1601,6 +1813,9 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
CURSOR(yield_txs)
CURSOR(audit_txs)
CURSOR(carrot_yield_txs)
CURSOR(token_txs)
CURSOR(tokens)
CURSOR(rollup_tx_info)
MDB_val_set(val_h, tx_hash);
@@ -1842,6 +2057,64 @@ void BlockchainLMDB::remove_transaction_data(const crypto::hash& tx_hash, const
}
}
}
// Is there token_tx data to remove?
if (tx.version >= TRANSACTION_VERSION_ENABLE_TOKENS && tx.type == cryptonote::transaction_type::CREATE_TOKEN)
{
// Remove all CREATE_TOKEN entries 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_token_txs, &val_height, &v, op);
if (result == MDB_NOTFOUND) {
throw1(DB_ERROR("Failed to locate token tx for removal from db transaction"));
} else if (result) {
throw1(DB_ERROR(lmdb_error("Failed to locate token_tx data for removal: ", result).c_str()));
}
op = MDB_NEXT_DUP;
const token_tx_info_carrot tti = *(const token_tx_info_carrot*)v.mv_data;
if (tti.tx_hash == tx_hash) {
result = mdb_cursor_del(m_cur_token_txs, 0);
if (result)
throw1(DB_ERROR(lmdb_error("Failed to add removal of token_tx data to db transaction: ", result).c_str()));
break;
}
}
// Now remove an entry from the `tokens` table
MDB_val_copy<uint64_t> asset_type_id(cryptonote::asset_id_from_type("sal" + tx.token_metadata.asset_type));
op = MDB_SET;
result = mdb_cursor_get(m_cur_tokens, &asset_type_id, &v, op);
if (result == MDB_NOTFOUND) {
throw1(DB_ERROR("Failed to locate token for removal from db transaction"));
} else if (result) {
throw1(DB_ERROR(lmdb_error("Failed to locate token data for removal: ", result).c_str()));
}
result = mdb_cursor_del(m_cur_tokens, 0);
if (result)
throw1(DB_ERROR(lmdb_error("Failed to remove token data to db transaction: ", result).c_str()));
}
// Is there a rollup_tx to add?
if (tx.version >= TRANSACTION_VERSION_ENABLE_TOKENS &&
tx.type == cryptonote::transaction_type::ROLLUP &&
tx.layer2_rollup_data.txs.size())
{
for (const auto &rollup_tx: tx.layer2_rollup_data.txs) {
MDB_val_set(rtx_key, tx.rollup_binding_tag);
MDB_val_set(rtx_val, rollup_tx);
result = mdb_cursor_get(m_cur_rollup_tx_info, &rtx_key, &rtx_val, MDB_GET_BOTH);
if (result == MDB_NOTFOUND)
continue;
if (result)
throw1(DB_ERROR(lmdb_error("Failed to locate rollup_tx data for removal: ", result).c_str()));
result = mdb_cursor_del(m_cur_rollup_tx_info, 0);
if (result)
throw1(DB_ERROR(lmdb_error("Error adding removal of tx id to db transaction", result).c_str()));
}
}
// Don't delete the tx_indices entry until the end, after we're done with val_tx_id
if (mdb_cursor_del(m_cur_tx_indices, 0))
@@ -2390,6 +2663,13 @@ void BlockchainLMDB::open(const std::string& filename, const int db_flags)
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");
lmdb_db_open(txn, LMDB_TOKEN_TXS, MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED | MDB_CREATE, m_token_txs, "Failed to open db handle for m_token_txs");
lmdb_db_open(txn, LMDB_TOKENS, MDB_CREATE, m_tokens, "Failed to open db handle for m_tokens");
lmdb_db_open(txn, LMDB_RCT_COUNT_INFO, MDB_INTEGERKEY | MDB_CREATE, m_rct_count_info, "Failed to open db handle for m_rct_count_info");
lmdb_db_open(txn, LMDB_ROLLUP_TX_INFO, MDB_INTEGERKEY | MDB_DUPSORT | MDB_DUPFIXED | MDB_CREATE, m_rollup_tx_info, "Failed to open db handle for m_rollup_tx_info");
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);
@@ -2415,6 +2695,11 @@ 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);
mdb_set_dupsort(txn, m_token_txs, compare_uint64);
mdb_set_compare(txn, m_tokens, compare_uint64);
mdb_set_dupsort(txn, m_rollup_tx_info, compare_uint64);
if (!(mdb_flags & MDB_RDONLY))
{
@@ -2602,6 +2887,12 @@ void BlockchainLMDB::reset()
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()));
if (auto result = mdb_drop(txn, m_token_txs, 0))
throw0(DB_ERROR(lmdb_error("Failed to drop m_token_txs: ", result).c_str()));
if (auto result = mdb_drop(txn, m_rct_count_info, 0))
throw0(DB_ERROR(lmdb_error("Failed to drop m_rct_count_info: ", result).c_str()));
if (auto result = mdb_drop(txn, m_rollup_tx_info, 0))
throw0(DB_ERROR(lmdb_error("Failed to drop m_rollup_tx_info: ", result).c_str()));
// init with current version
MDB_val_str(k, "version");
@@ -3454,8 +3745,13 @@ uint64_t BlockchainLMDB::get_block_timestamp(const uint64_t& height) const
return ret;
}
std::pair<std::vector<uint64_t>, uint64_t> BlockchainLMDB::get_block_cumulative_rct_outputs(const std::vector<uint64_t> &heights, const std::string asset_type) const
std::pair<std::vector<uint64_t>, uint64_t> BlockchainLMDB::get_block_cumulative_rct_outputs_old(const std::vector<uint64_t> &heights, const std::string asset_type) const
{
// Handle tokens separately
if (cryptonote::is_asset_type_token(asset_type)) {
return get_block_cumulative_rct_outputs(heights, asset_type);
}
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
std::vector<uint64_t> res;
@@ -3484,11 +3780,11 @@ std::pair<std::vector<uint64_t>, uint64_t> BlockchainLMDB::get_block_cumulative_
{
if (height >= range_begin && height < range_end)
{
// nohting to do
// nothing to do
}
else
{
if (height == prev_height + 1)
if (false/*height == prev_height + 1*/)
{
MDB_val k2;
result = mdb_cursor_get(m_cur_block_info, &k2, &v, MDB_NEXT_MULTIPLE);
@@ -3508,14 +3804,18 @@ std::pair<std::vector<uint64_t>, uint64_t> BlockchainLMDB::get_block_cumulative_
if (result)
throw0(DB_ERROR(lmdb_error("Error attempting to retrieve rct distribution from the db: ", result).c_str()));
}
const mdb_block_info *bi = ((const mdb_block_info *)v.mv_data) + (height - range_begin);
assert(false);
//const mdb_block_info *bi = ((const mdb_block_info *)v.mv_data) + (height - range_begin);
// if no asset type is provided in the request, an old client is requesting the cumulative outputs,
// and is expecting the global output distribution that isn't bucketed by asset type in response
res.push_back(asset_type.empty() ? bi->bi_cum_rct : bi->bi_cum_rct_by_asset_type[asset_type]);
if (height == heights[heights.size() - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE])
num_spendable_global_outs = bi->bi_cum_rct;
//res.push_back(asset_type.empty() ? bi->bi_cum_rct : bi->bi_cum_rct_by_asset_type[asset_type]);
//if (height == heights[heights.size() - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE])
// num_spendable_global_outs = asset_type.empty() ? bi->bi_cum_rct : bi->bi_cum_rct_by_asset_type[asset_type];
assert(true);
prev_height = height;
}
@@ -3524,6 +3824,64 @@ std::pair<std::vector<uint64_t>, uint64_t> BlockchainLMDB::get_block_cumulative_
return std::make_pair(res, num_spendable_global_outs);
}
std::pair<std::vector<uint64_t>, uint64_t> BlockchainLMDB::get_block_cumulative_rct_outputs(const std::vector<uint64_t> &heights, const std::string asset_type) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
std::vector<uint64_t> res;
uint64_t num_spendable_global_outs = 0;
int result;
// Verify the asset_type matches a token
//if (!cryptonote::is_asset_type_token(asset_type)) {
// throw0(BLOCK_DNE(std::string("Attempt to get token rct distribution failed - '" + asset_type + "' is not a valid token").c_str()));
//}
// Get the token asset_type_id
uint32_t asset_type_id = cryptonote::asset_id_from_type(asset_type);
if (heights.empty())
return {};
res.reserve(heights.size());
TXN_PREFIX_RDONLY();
RCURSOR(rct_count_info);
MDB_stat db_stats;
if ((result = mdb_stat(m_txn, m_blocks, &db_stats)))
throw0(DB_ERROR(lmdb_error("Failed to query m_blocks: ", result).c_str()));
for (size_t i = 0; i < heights.size(); ++i)
if (heights[i] >= db_stats.ms_entries)
throw0(BLOCK_DNE(std::string("Attempt to get rct distribution from height " + std::to_string(heights[i]) + " failed -- block size not in db").c_str()));
uint64_t prev_height = heights[0];
for (uint64_t height: heights)
{
MDB_val v;
MDB_val_set(k, height);
result = mdb_cursor_get(m_cur_rct_count_info, &k, &v, MDB_SET);
if (result == MDB_NOTFOUND) {
throw0(DB_ERROR(lmdb_error("Error attempting to retrieve rct distribution from the db: ", result).c_str()));
}
if (result)
throw0(DB_ERROR(lmdb_error("Failed to enumerate rct distribution info: ", result).c_str()));
oracle::asset_type_counts_v2 rct_count_info;
rct_count_info.deserialize(reinterpret_cast<const uint8_t*>(v.mv_data), v.mv_size);
//if (!rct_count_info.has_asset_type(asset_type_id))
// throw0(BLOCK_DNE(std::string("Attempt to get rct distribution failed - '" + asset_type + "' not found").c_str()));
res.push_back(rct_count_info[asset_type_id]);
if (height == heights[heights.size() - CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE])
num_spendable_global_outs = rct_count_info[asset_type_id];
prev_height = height;
}
TXN_POSTFIX_RDONLY();
return std::make_pair(res, num_spendable_global_outs);
}
uint64_t BlockchainLMDB::get_top_block_timestamp() const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
@@ -3970,6 +4328,102 @@ std::map<std::string,uint64_t> BlockchainLMDB::get_circulating_supply() const
return circulating_supply;
}
std::map<std::string, cryptonote::token_metadata_t> BlockchainLMDB::get_tokens() const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
TXN_PREFIX_RDONLY();
int result;
// Open the tokens table read-only
RCURSOR(tokens)
// Create the map<asset_type, token_data> we are going to return
std::map<std::string, cryptonote::token_metadata_t> token_map;
MDB_val k;
MDB_val v;
MDB_cursor_op op = MDB_FIRST;
while (1)
{
int result = mdb_cursor_get(m_cur_tokens, &k, &v, op);
op = MDB_NEXT;
if (result == MDB_NOTFOUND)
break;
if (result)
throw0(DB_ERROR(lmdb_error("Failed to get tokens: ", result).c_str()));
// Push the data into the tokens return struct
const uint64_t asset_type_id = *(const uint64_t*)k.mv_data;
const std::string asset_type = cryptonote::asset_type_from_id(asset_type_id);
if (!v.mv_size)
throw0(DB_ERROR("zero-length token_blob provided"));
std::string token_blob(reinterpret_cast<const char *>(v.mv_data), v.mv_size);
cryptonote::token_metadata_t token_metadata;
read_token_metadata(token_blob, token_metadata);
if (token_metadata.token.type() == typeid(cryptonote::erc_token_t)) {
// ERC20 token found
erc_token_t token = boost::get<cryptonote::erc_token_t>(token_metadata.token);
LOG_PRINT_L3("erc_token_t version " << token.version);
} else if (token_metadata.token.type() == typeid(cryptonote::sal_token_t)) {
// SAL token found
sal_token_t token = boost::get<cryptonote::sal_token_t>(token_metadata.token);
LOG_PRINT_L3("sal_token_t version " << token.version);
} else {
throw0(DB_ERROR("invalid token_blob provided"));
}
token_map[asset_type] = token_metadata;
}
TXN_POSTFIX_RDONLY();
return token_map;
}
bool BlockchainLMDB::is_tx_paid_for(const cryptonote::transaction& tx) const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
check_open();
TXN_PREFIX_RDONLY();
int result;
// Is it a TRANSFER?
if (tx.type != cryptonote::transaction_type::TRANSFER)
return true;
// Is is a TOKEN?
if (!cryptonote::is_asset_type_token(tx.source_asset_type))
return true;
RCURSOR(rollup_tx_info)
// Calculate the TX prefix hash
crypto::hash tx_prefix_hash;
cryptonote::get_transaction_hash(tx, tx_prefix_hash);
// get the key image from the first input
if (tx.vin.size() == 0)
return false;
const txin_to_key &in_to_key = boost::get < txin_to_key > (tx.vin[0]);
cryptonote::layer2_rollup_tx_t rollup_tx{tx_prefix_hash, in_to_key.k_image, tx.rct_signatures.txnFee};
MDB_val_set(key, tx.rollup_binding_tag);
MDB_val_set(val, rollup_tx);
result = mdb_cursor_get(m_cur_rollup_tx_info, &key, &val, MDB_GET_BOTH);
if (result == MDB_NOTFOUND) {
LOG_PRINT_L2("Transaction ID " << tx_prefix_hash << " has not been paid for");
return false;
}
if (result)
throw1(DB_ERROR(lmdb_error("Failed to locate rollup TX data: ", result).c_str()));
// Can only find a matching record if the ROLLUP is already mined
return true;
}
uint64_t BlockchainLMDB::num_outputs() const
{
LOG_PRINT_L3("BlockchainLMDB::" << __func__);
@@ -5683,8 +6137,169 @@ void BlockchainLMDB::migrate_2_3()
MGINFO_YELLOW("Migrating blockchain from DB version 2 to 3 - this may take a while:");
// Create the missing (and empty) "audit_block_info" records for all blocks
std::map<uint64_t, oracle::asset_type_counts> map_counts;
// Create the updated asset_type_counts_v2 entries
do {
LOG_PRINT_L1("migrating block info:");
result = mdb_txn_begin(m_env, NULL, 0, txn);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
MDB_stat db_stats;
if ((result = mdb_stat(txn, m_blocks, &db_stats)))
throw0(DB_ERROR(lmdb_error("Failed to query m_blocks: ", result).c_str()));
const uint64_t blockchain_height = db_stats.ms_entries;
/* the block_info table name is the same but the old version and new version
* have incompatible data. Create a new table. We want the name to be similar
* to the old name so that it will occupy the same location in the DB.
*/
MDB_dbi o_block_info = m_block_info;
lmdb_db_open(txn, "block_infn", MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, m_block_info, "Failed to open db handle for block_infn");
mdb_set_dupsort(txn, m_block_info, compare_uint64);
MDB_cursor *c_blocks;
result = mdb_cursor_open(txn, m_blocks, &c_blocks);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to open a cursor for blocks: ", result).c_str()));
MDB_cursor *c_old, *c_cur;
i = 0;
while(1) {
if (!(i % 1000)) {
if (i) {
LOGIF(el::Level::Info) {
std::cout << i << " / " << blockchain_height << " \r" << std::flush;
}
txn.commit();
result = mdb_txn_begin(m_env, NULL, 0, txn);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
}
result = mdb_cursor_open(txn, m_block_info, &c_cur);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to open a cursor for block_infn: ", result).c_str()));
result = mdb_cursor_open(txn, o_block_info, &c_old);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to open a cursor for block_info: ", result).c_str()));
if (!i) {
MDB_stat db_stat;
result = mdb_stat(txn, m_block_info, &db_stats);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to query m_block_info: ", result).c_str()));
i = db_stats.ms_entries;
}
}
result = mdb_cursor_get(c_old, &k, &v, MDB_NEXT);
if (result == MDB_NOTFOUND) {
txn.commit();
break;
}
else if (result)
throw0(DB_ERROR(lmdb_error("Failed to get a record from block_info: ", result).c_str()));
const mdb_block_info_1 *bi_old = (const mdb_block_info_1*)v.mv_data;
mdb_block_info_2 bi;
bi.bi_height = bi_old->bi_height;
bi.bi_timestamp = bi_old->bi_timestamp;
bi.bi_coins = bi_old->bi_coins;
bi.bi_weight = bi_old->bi_weight;
bi.bi_diff_lo = bi_old->bi_diff_lo;
bi.bi_diff_hi = bi_old->bi_diff_hi;
bi.bi_hash = bi_old->bi_hash;
bi.bi_cum_rct = bi_old->bi_cum_rct;
bi.bi_long_term_block_weight = bi_old->bi_long_term_block_weight;
bi.bi_pricing_record = bi_old->bi_pricing_record;
map_counts[bi.bi_height] = bi_old->bi_cum_rct_by_asset_type;
MDB_val_set(nv, bi);
result = mdb_cursor_put(c_cur, (MDB_val *)&zerokval, &nv, MDB_APPENDDUP);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to put a record into block_infn: ", result).c_str()));
/* we delete the old records immediately, so the overall DB and mapsize should not grow.
* This is a little slower than just letting mdb_drop() delete it all at the end, but
* it saves a significant amount of disk space.
*/
result = mdb_cursor_del(c_old, 0);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to delete a record from block_info: ", result).c_str()));
i++;
}
result = mdb_txn_begin(m_env, NULL, 0, txn);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
/* Delete the old table */
result = mdb_drop(txn, o_block_info, 1);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to delete old block_info table: ", result).c_str()));
RENAME_DB("block_infn");
mdb_dbi_close(m_env, m_block_info);
lmdb_db_open(txn, "block_info", MDB_INTEGERKEY | MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED, m_block_info, "Failed to open db handle for block_infn");
mdb_set_dupsort(txn, m_block_info, compare_uint64);
txn.commit();
// Now create the new table with our asset_type_counts
LOG_PRINT_L1("migrating RCT asset_type count info:");
result = mdb_txn_begin(m_env, NULL, 0, txn);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
oracle::asset_type_counts_v2 rct_counts;
uint32_t asset_type_id_sal = cryptonote::asset_id_from_type("SAL");
rct_counts.add_asset_type(asset_type_id_sal);
uint32_t asset_type_id_one = cryptonote::asset_id_from_type("SAL1");
rct_counts.add_asset_type(asset_type_id_one);
uint32_t asset_type_id_burn = cryptonote::asset_id_from_type("BURN");
rct_counts.add_asset_type(asset_type_id_burn);
MDB_cursor *c_cur_rct;
i = 0;
uint64_t i_max = map_counts.size();
do {
if (!(i % 1000)) {
if (i) {
LOGIF(el::Level::Info) {
std::cout << i << " / " << blockchain_height << " \r" << std::flush;
}
txn.commit();
result = mdb_txn_begin(m_env, NULL, 0, txn);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
}
result = mdb_cursor_open(txn, m_rct_count_info, &c_cur_rct);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to open a cursor for rct_count_info: ", result).c_str()));
if (!i) {
result = mdb_stat(txn, m_rct_count_info, &db_stats);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to query m_rct_count_info: ", result).c_str()));
i = db_stats.ms_entries;
}
}
// populate the RCT counts
oracle::asset_type_counts old_counts = map_counts.at(i);
rct_counts.asset_counts[asset_type_id_sal] = old_counts["SAL"];
rct_counts.asset_counts[asset_type_id_one] = old_counts["SAL1"];
rct_counts.asset_counts[asset_type_id_burn] = old_counts["BURN"];
std::vector<uint8_t> rct_counts_serialized = rct_counts.serialize();
MDB_val_set(key, i);
MDB_val_sized(value, rct_counts_serialized);
result = mdb_cursor_put(c_cur_rct, &key, &value, MDB_APPEND);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to put a record into : ", result).c_str()));
i++;
} while (i<i_max);
txn.commit();
} while(0);
uint32_t version = VERSION;
@@ -5693,7 +6308,7 @@ void BlockchainLMDB::migrate_2_3()
MDB_val_str(vk, "version");
result = mdb_txn_begin(m_env, NULL, 0, txn);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
throw0(DB_ERROR(lmdb_error("Failed to create a transaction for the db: ", result).c_str()));
result = mdb_put(txn, m_properties, &vk, &v, 0);
if (result)
throw0(DB_ERROR(lmdb_error("Failed to update version for the db: ", result).c_str()));
@@ -5702,8 +6317,8 @@ void BlockchainLMDB::migrate_2_3()
void BlockchainLMDB::migrate(const uint32_t oldversion)
{
//if (oldversion < 3)
// migrate_2_3();
if (oldversion < 3)
migrate_2_3();
}
} // namespace cryptonote
+43 -1
View File
@@ -82,6 +82,14 @@ typedef struct mdb_txn_cursors
MDB_cursor *m_txc_audit_blocks;
MDB_cursor *m_txc_carrot_yield_txs;
MDB_cursor *m_txc_token_txs;
MDB_cursor *m_txc_tokens;
MDB_cursor *m_txc_asset_type_counts;
MDB_cursor *m_txc_rct_count_info;
MDB_cursor *m_txc_rollup_tx_info;
} mdb_txn_cursors;
#define m_cur_blocks m_cursors->m_txc_blocks
@@ -111,6 +119,14 @@ typedef struct mdb_txn_cursors
#define m_cur_audit_blocks m_cursors->m_txc_audit_blocks
#define m_cur_carrot_yield_txs m_cursors->m_txc_carrot_yield_txs
#define m_cur_token_txs m_cursors->m_txc_token_txs
#define m_cur_tokens m_cursors->m_txc_tokens
#define m_cur_asset_type_counts m_cursors->m_txc_asset_type_counts
#define m_cur_rct_count_info m_cursors->m_txc_rct_count_info
#define m_cur_rollup_tx_info m_cursors->m_txc_rollup_tx_info
typedef struct mdb_rflags
{
bool m_rf_txn;
@@ -140,6 +156,15 @@ typedef struct mdb_rflags
bool m_rf_audit_txs;
bool m_rf_audit_blocks;
bool m_rf_carrot_yield_txs;
bool m_rf_token_txs;
bool m_rf_tokens;
bool m_rf_asset_type_counts;
bool m_rf_rct_count_info;
bool m_rf_rollup_tx_info;
} mdb_rflags;
typedef struct mdb_threadinfo
@@ -242,6 +267,8 @@ public:
virtual cryptonote::blobdata get_block_blob_from_height(const uint64_t& height) const;
virtual std::pair<std::vector<uint64_t>, uint64_t> get_block_cumulative_rct_outputs_old(const std::vector<uint64_t> &heights, const std::string asset_type) const;
virtual std::pair<std::vector<uint64_t>, uint64_t> get_block_cumulative_rct_outputs(const std::vector<uint64_t> &heights, const std::string asset_type) const;
virtual uint64_t get_block_timestamp(const uint64_t& height) const;
@@ -277,6 +304,10 @@ public:
virtual uint64_t height() const;
virtual std::map<std::string, uint64_t> get_circulating_supply() const;
virtual std::map<std::string, cryptonote::token_metadata_t> get_tokens() const;
virtual bool is_tx_paid_for(const cryptonote::transaction& tx) const;
virtual bool tx_exists(const crypto::hash& h) const;
virtual bool tx_exists(const crypto::hash& h, uint64_t& tx_index) const;
@@ -406,11 +437,12 @@ private:
const difficulty_type& cumulative_difficulty,
const uint64_t& coins_generated,
uint64_t num_rct_outs,
oracle::asset_type_counts& cum_rct_by_asset_type,
oracle::asset_type_counts_v2& cum_rct_by_asset_type,
const crypto::hash& blk_hash,
uint64_t slippage_total,
uint64_t yield_total,
uint64_t audit_total,
uint64_t token_total,
const cryptonote::network_type nettype,
cryptonote::yield_block_info& ybi,
cryptonote::audit_block_info& abi
@@ -479,6 +511,8 @@ private:
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;
virtual int get_token_tx_info(const uint64_t height, std::vector<token_tx_info_carrot>& tti_container) const;
private:
MDB_env* m_env;
@@ -521,6 +555,14 @@ private:
MDB_dbi m_carrot_yield_txs;
MDB_dbi m_token_txs;
MDB_dbi m_tokens;
MDB_dbi m_asset_type_counts;
MDB_dbi m_rct_count_info;
MDB_dbi m_rollup_tx_info;
mutable uint64_t m_cum_size; // used in batch size estimation
mutable unsigned int m_cum_count;
std::string m_folder;
+6 -1
View File
@@ -156,15 +156,20 @@ public:
const difficulty_type& cumulative_difficulty,
const uint64_t& coins_generated,
uint64_t num_rct_outs,
oracle::asset_type_counts& cum_rct_by_asset_type,
oracle::asset_type_counts_v2& cum_rct_by_asset_type,
const crypto::hash& blk_hash,
uint64_t slippage_total,
uint64_t yield_total,
uint64_t audit_total,
uint64_t token_total,
const cryptonote::network_type nettype,
cryptonote::yield_block_info& ybi,
cryptonote::audit_block_info& abi
) override { }
virtual std::map<std::string, cryptonote::token_metadata_t> get_tokens() const override { return {}; }
virtual bool is_tx_paid_for(const cryptonote::transaction& tx) const override { return true; }
virtual int get_token_tx_info(const uint64_t height, std::vector<token_tx_info_carrot>& tti_container) const override { return 0; }
virtual cryptonote::block get_block_from_height(const uint64_t& height) const override { return cryptonote::block(); }
virtual void set_hard_fork_version(uint64_t height, uint8_t version) override {}
virtual uint8_t get_hard_fork_version(uint64_t height) const override { return 0; }
+10 -13
View File
@@ -214,11 +214,9 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, ''
#define MAX_RINGS 0xffffffff
struct tm prevtm = {0}, currtm;
uint64_t prevsz = 0, currsz = 0;
uint64_t prevtxs = 0, currtxs = 0;
uint64_t currblks = 0;
uint32_t txhr[24] = {0};
unsigned int i;
// uint64_t currsz = 0;
// uint64_t currtxs = 0;
// uint64_t currblks = 0;
const std::map<uint8_t, std::pair<uint64_t, std::pair<std::string, std::string>>> audit_hard_forks = get_config(net_type).AUDIT_HARD_FORKS;
@@ -246,9 +244,7 @@ plot 'stats.csv' index "DATA" using (timecolumn(1,"%Y-%m-%d")):4 with lines, ''
prevtm = currtm;
}
skip:
currsz += bd.size();
uint64_t coinbase_amount;
uint64_t tx_fee_amount = 0;
// currsz += bd.size();
std::set<std::string> used_assets, miner_tx_assets, protocol_tx_assets;
std::map<size_t, std::vector<std::string>> used_tx_versions;
used_assets.insert("SAL");
@@ -325,10 +321,11 @@ skip:
std::cout << timebuf << "" << delimiter << "" << h << "" << delimiter << "" << tx_id << "" << delimiter << "invalid TX detected" << delimiter << std::endl;
continue;
}
currsz += bd.size();
if (db->get_prunable_tx_blob(tx_id, bd))
currsz += bd.size();
currtxs++;
// currsz += bd.size();
// if (db->get_prunable_tx_blob(tx_id, bd))
// currsz += bd.size();
// currtxs++;
db->get_prunable_tx_blob(tx_id, bd);
if (tx.type != cryptonote::transaction_type::TRANSFER &&
tx.type != cryptonote::transaction_type::BURN &&
@@ -374,7 +371,7 @@ skip:
}
}
currblks++;
// currblks++;
if (stop_requested)
break;
@@ -211,8 +211,15 @@ int main(int argc, char* argv[])
throw std::runtime_error("Failed to initialize a database");
}
const std::string filename = (boost::filesystem::path(opt_data_dir) / db->get_db_name()).string();
LOG_PRINT_L0("Loading blockchain from folder " << filename << " ...");
boost::filesystem::path folder(opt_data_dir);
if (opt_stagenet) {
folder /= std::to_string(STAGENET_VERSION);
} else if (opt_testnet) {
folder /= std::to_string(TESTNET_VERSION);
}
folder /= db->get_db_name();
LOG_PRINT_L0("Loading blockchain from folder " << folder << " ...");
const std::string filename = folder.string();
try
{
Binary file not shown.
+73 -9
View File
@@ -282,13 +282,62 @@ crypto::key_image carrot_and_legacy_account::derive_key_image(const crypto::publ
return L;
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::generate_subaddress_map()
crypto::key_image carrot_and_legacy_account::derive_key_image_view_only(const crypto::public_key &address_spend_pubkey,
const crypto::secret_key &sender_extension_g,
const crypto::secret_key &sender_extension_t,
const crypto::public_key &onetime_address) const
{
const auto it = subaddress_map.find(address_spend_pubkey);
CHECK_AND_ASSERT_THROW_MES(it != subaddress_map.cend(),
"carrot and legacy account: derive key image view only: cannot find subaddress");
const bool is_subaddress = it->second.index.is_subaddress();
const uint32_t major_index = it->second.index.major;
const uint32_t minor_index = it->second.index.minor;
const cryptonote::account_keys &keys = get_keys();
crypto::secret_key address_index_generator;
crypto::secret_key subaddress_scalar;
crypto::secret_key subaddress_extension;
crypto::secret_key address_privkey_g;
crypto::secret_key x;
CHECK_AND_ASSERT_THROW_MES(it->second.derive_type == AddressDeriveType::Carrot,
"carrot and legacy account: derive key image view only: not a Carrot address");
// s^j_gen = H_32[s_ga](j_major, j_minor)
make_carrot_index_extension_generator(keys.s_generate_address, major_index, minor_index, address_index_generator);
if (is_subaddress)
{
// k^j_subscal = H_n(K_s, j_major, j_minor, s^j_gen)
make_carrot_subaddress_scalar(keys.m_carrot_account_address.m_spend_public_key, address_index_generator, major_index, minor_index, subaddress_scalar);
}
else
{
// k^j_subscal = 1
sc_1(to_bytes(subaddress_scalar));
}
// k^g_a = k_gi * k^j_subscal
sc_mul(to_bytes(address_privkey_g), to_bytes(keys.k_generate_image), to_bytes(subaddress_scalar));
// x = k^{j,g}_addr + k^g_o
sc_add(to_bytes(x), to_bytes(address_privkey_g), to_bytes(sender_extension_g));
crypto::key_image L;
crypto::generate_key_image(onetime_address, x, L);
return L;
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::generate_subaddress_map(const std::pair<size_t, size_t>& lookahead_size)
{
const std::vector<AddressDeriveType> derive_types{AddressDeriveType::Carrot, AddressDeriveType::PreCarrot};
for (uint32_t major_index = 0; major_index <= MAX_SUBADDRESS_MAJOR_INDEX; ++major_index)
for (uint32_t major_index = 0; major_index <= lookahead_size.first; ++major_index)
{
for (uint32_t minor_index = 0; minor_index <= MAX_SUBADDRESS_MINOR_INDEX; ++minor_index)
for (uint32_t minor_index = 0; minor_index <= lookahead_size.second; ++minor_index)
{
for (const AddressDeriveType derive_type : derive_types)
{
@@ -337,7 +386,7 @@ void carrot_and_legacy_account::set_keys(const cryptonote::account_keys& keys, b
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::create_from_svb_key(const cryptonote::account_public_address& address, const crypto::secret_key& svb_key)
{
{
// top level keys
m_keys.s_master = crypto::null_skey;
make_carrot_provespend_key(m_keys.s_master, m_keys.k_prove_spend);
@@ -359,9 +408,18 @@ void carrot_and_legacy_account::create_from_svb_key(const cryptonote::account_pu
k_view_incoming_dev.view_key_scalar_mult_ed25519(crypto::get_G(),
m_keys.m_carrot_main_address.m_view_public_key
);
// Store fields for Carrot
m_keys.m_spend_secret_key = crypto::null_skey;
m_keys.m_view_secret_key = crypto::null_skey;
m_keys.m_account_address = {crypto::null_pkey, crypto::null_pkey, false};
// Update ALL addresses to be Carrot-only
m_keys.m_carrot_account_address.m_is_carrot = true;
m_keys.m_carrot_main_address.m_is_carrot = true;
// Set the default derive type for addresses
this->default_derive_type = AddressDeriveType::Carrot;
generate_subaddress_map();
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::set_carrot_keys(const AddressDeriveType default_derive_type)
@@ -393,13 +451,19 @@ void carrot_and_legacy_account::set_carrot_keys(const AddressDeriveType default_
m_keys.m_carrot_main_address.m_is_carrot = true;
this->default_derive_type = default_derive_type;
generate_subaddress_map();
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::insert_subaddresses(const std::unordered_map<crypto::public_key, subaddress_index_extended>& subaddress_map_cn)
{
for (const auto &p : subaddress_map_cn)
subaddress_map.insert({p.first, {{p.second.index.major, p.second.index.minor}, p.second.derive_type, p.second.is_return_spend_key}});
for (const auto &p : subaddress_map_cn) {
subaddress_map.insert({p.first, {{p.second.index.major, p.second.index.minor}, p.second.derive_type, p.second.is_return_spend_key}});
if (p.second.derive_type == AddressDeriveType::PreCarrot) {
// Create a matching Carrot address
const subaddress_index_extended subaddr_index{{p.second.index.major, p.second.index.minor}, AddressDeriveType::Carrot, p.second.is_return_spend_key};
const CarrotDestinationV1 addr = subaddress(subaddr_index);
subaddress_map.insert({addr.address_spend_pubkey, subaddr_index});
}
}
}
//----------------------------------------------------------------------------------------------------------------------
void carrot_and_legacy_account::insert_return_output_info(const std::unordered_map<crypto::public_key, return_output_info_t>& roi_map)
+61 -21
View File
@@ -48,12 +48,59 @@ namespace carrot
input_context_t input_context;
crypto::public_key K_o; // output onetime address
crypto::public_key K_change; // change output onetime address
crypto::public_key K_spend_pubkey; // change output spend pubkey
crypto::key_image key_image;
crypto::secret_key sum_g;
crypto::secret_key sender_extension_t;
return_output_info_t() {
// Default constructor for serialization
input_context = input_context_t();
K_o = crypto::public_key();
K_change = crypto::public_key();
K_spend_pubkey = crypto::public_key();
key_image = crypto::key_image();
sum_g = crypto::secret_key();
sender_extension_t = crypto::secret_key();
}
return_output_info_t(
const input_context_t &input_context,
const crypto::public_key &K_o,
const crypto::public_key &K_change,
const crypto::public_key &K_spend_pubkey,
const crypto::key_image &key_image,
const crypto::secret_key &sum_g,
const crypto::secret_key &sender_extension_t):
input_context(input_context),
K_o(K_o),
K_change(K_change),
K_spend_pubkey(K_spend_pubkey),
key_image(key_image),
sum_g(sum_g),
sender_extension_t(sender_extension_t) {}
BEGIN_SERIALIZE_OBJECT()
FIELD(input_context)
FIELD(K_o)
FIELD(K_change)
FIELD(K_spend_pubkey)
FIELD(key_image)
FIELD(sum_g)
FIELD(sender_extension_t)
END_SERIALIZE()
};
// Old return_output_info_t format (for deserializing version 2 wallet caches)
struct return_output_info_retired_t {
input_context_t input_context;
crypto::public_key K_o;
crypto::public_key K_change;
crypto::key_image key_image;
crypto::secret_key x;
crypto::secret_key y;
return_output_info_t() {
// Default constructor for serialization
return_output_info_retired_t() {
input_context = input_context_t();
K_o = crypto::public_key();
K_change = crypto::public_key();
@@ -62,20 +109,6 @@ namespace carrot
y = crypto::secret_key();
}
return_output_info_t(
const input_context_t &input_context,
const crypto::public_key &K_o,
const crypto::public_key &K_change,
const crypto::key_image &key_image,
const crypto::secret_key &x,
const crypto::secret_key &y):
input_context(input_context),
K_o(K_o),
K_change(K_change),
key_image(key_image),
x(x),
y(y) {}
BEGIN_SERIALIZE_OBJECT()
FIELD(input_context)
FIELD(K_o)
@@ -144,7 +177,12 @@ namespace carrot
const crypto::secret_key &sender_extension_t,
const crypto::public_key &onetime_address) const;
void generate_subaddress_map();
crypto::key_image derive_key_image_view_only(const crypto::public_key &address_spend_pubkey,
const crypto::secret_key &sender_extension_g,
const crypto::secret_key &sender_extension_t,
const crypto::public_key &onetime_address) const;
void generate_subaddress_map(const std::pair<size_t, size_t>& lookahead_size);
crypto::secret_key generate(
const crypto::secret_key& recovery_key = crypto::secret_key(),
@@ -183,9 +221,10 @@ namespace boost
x.input_context = carrot::input_context_t();
x.K_o = crypto::public_key();
x.K_change = crypto::public_key();
x.K_spend_pubkey = crypto::public_key();
x.key_image = crypto::key_image();
x.x = crypto::secret_key();
x.y = crypto::secret_key();
x.sum_g = crypto::secret_key();
x.sender_extension_t = crypto::secret_key();
}
template <class Archive>
@@ -194,9 +233,10 @@ namespace boost
a & x.input_context;
a & x.K_o;
a & x.K_change;
a & x.K_spend_pubkey;
a & x.key_image;
a & x.x;
a & x.y;
a & x.sum_g;
a & x.sender_extension_t;
}
}
}
+1
View File
@@ -75,5 +75,6 @@ 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";
static constexpr const unsigned char SPARC_DOMAIN_SEP_RETURN_ADDRESS_SCALAR[] = "SPARC return address scalar";
static constexpr const unsigned char SPARC_DOMAIN_SEP_RETURN_INDEX_SCALAR[] = "SPARC return index scalar";
} //namespace carrot
+5
View File
@@ -122,6 +122,11 @@ bool operator==(const view_tag_t &a, const view_tag_t &b)
return memcmp(&a, &b, sizeof(view_tag_t)) == 0;
}
//-------------------------------------------------------------------------------------------------------------------
bool operator==(const rollup_binding_tag_t &a, const rollup_binding_tag_t &b)
{
return memcmp(&a, &b, sizeof(rollup_binding_tag_t)) == 0;
}
//-------------------------------------------------------------------------------------------------------------------
janus_anchor_t gen_janus_anchor()
{
return crypto::rand<janus_anchor_t>();
+11
View File
@@ -106,6 +106,13 @@ struct encrypted_return_pubkey_t final
unsigned char bytes[ENCRYPTED_RETURN_PUBKEY_BYTES];
};
/// Salvium rollup binding tag
constexpr std::size_t ROLLUP_BINDING_TAG_BYTES{8};
struct rollup_binding_tag_t final
{
unsigned char bytes[ROLLUP_BINDING_TAG_BYTES];
};
/// overloaded operators: address tag
bool operator==(const janus_anchor_t &a, const janus_anchor_t &b);
static inline bool operator!=(const janus_anchor_t &a, const janus_anchor_t &b) { return !(a == b); }
@@ -139,6 +146,10 @@ bool operator==(const encrypted_return_pubkey_t &a, const encrypted_return_pubke
static inline bool operator!=(const encrypted_return_pubkey_t &a, const encrypted_return_pubkey_t &b) { return !(a == b); }
encrypted_return_pubkey_t operator^(const encrypted_return_pubkey_t &a, const encrypted_return_pubkey_t &b);
/// overloaded operators: rollup binding tag
bool operator==(const rollup_binding_tag_t &a, const rollup_binding_tag_t &b);
static inline bool operator!=(const rollup_binding_tag_t &a, const rollup_binding_tag_t &b) { return !(a == b); }
/// generate a random janus anchor
janus_anchor_t gen_janus_anchor();
/// generate a random (non-null) payment ID
+53 -14
View File
@@ -39,6 +39,7 @@ extern "C"
#include "crypto/wallet/crypto.h"
#include "hash_functions.h"
#include "int-util.h"
#include "string_tools.h"
#include "misc_language.h"
#include "ringct/rctOps.h"
#include "transcript_fixed.h"
@@ -211,6 +212,17 @@ void make_sparc_return_privkey(const unsigned char s_sender_receiver_unctx[32],
derive_scalar(transcript.data(), transcript.size(), s_sender_receiver_unctx, &return_privkey_out);
}
//-------------------------------------------------------------------------------------------------------------------
void make_sparc_return_index(const unsigned char s_sender_receiver_unctx[32],
const input_context_t &input_context,
const crypto::public_key &onetime_address,
const uint64_t idx,
crypto::secret_key &return_index_out)
{
// k_idx = H_32(s_sr || input_context || Ko || idx)
const auto transcript = sp::make_fixed_transcript<SPARC_DOMAIN_SEP_RETURN_INDEX_SCALAR>(input_context, onetime_address, idx);
derive_scalar(transcript.data(), transcript.size(), s_sender_receiver_unctx, &return_index_out);
}
//-------------------------------------------------------------------------------------------------------------------
void make_sparc_return_pubkey_encryption_mask(const unsigned char s_sender_receiver_unctx[32],
const input_context_t &input_context,
const crypto::public_key &onetime_address,
@@ -222,22 +234,50 @@ void make_sparc_return_pubkey_encryption_mask(const unsigned char s_sender_recei
}
//-------------------------------------------------------------------------------------------------------------------
void make_sparc_return_pubkey(const unsigned char s_sender_receiver_unctx[32],
const input_context_t &input_context,
const view_balance_secret_device *s_view_balance_dev,
const crypto::public_key &onetime_address,
encrypted_return_pubkey_t &return_pubkey_out)
const input_context_t &input_context,
const view_balance_secret_device *s_view_balance_dev,
const crypto::public_key &onetime_address,
const uint64_t idx,
encrypted_return_pubkey_t &return_pubkey_out)
{
// K_return = k_return G ^ m_return
// compute k_return
crypto::secret_key k_return;
crypto::public_key return_pub;
encrypted_return_pubkey_t K_return;
encrypted_return_pubkey_t m_return;
s_view_balance_dev->make_internal_return_privkey(input_context, onetime_address, k_return);
crypto::secret_key_to_public_key(k_return, return_pub);
static_assert(sizeof(K_return.bytes) == sizeof(return_pub.data), "Size mismatch");
memcpy(K_return.bytes, return_pub.data, sizeof(encrypted_return_pubkey_t));
make_sparc_return_pubkey_encryption_mask(s_sender_receiver_unctx, input_context, onetime_address, m_return);
return_pubkey_out = K_return ^ m_return;
// compute k_idx
crypto::secret_key k_idx;
make_sparc_return_index(s_sender_receiver_unctx, input_context, onetime_address, idx, k_idx);
// compute m_return
encrypted_return_pubkey_t m_return;
make_sparc_return_pubkey_encryption_mask(s_sender_receiver_unctx,
input_context,
onetime_address,
m_return);
#if 1
// compute SPARC K_return = k_return * G
crypto::public_key K_return;
crypto::secret_key_to_public_key(k_return, K_return);
// compute return_enc
encrypted_return_pubkey_t return_pub;
static_assert(sizeof(K_return.data) == sizeof(return_pub.bytes), "Size mismatch");
memcpy(return_pub.bytes, K_return.data, sizeof(encrypted_return_pubkey_t));
#else
// compute SPARC K_return = k_return * G + k_idx * T
rct::key K_return;
rct::addKeys2(K_return,
rct::sk2rct(k_return),
rct::sk2rct(k_idx),
rct::pk2rct(crypto::get_T()));
// compute return_enc
encrypted_return_pubkey_t return_pub;
static_assert(sizeof(K_return.bytes) == sizeof(return_pub.bytes), "Size mismatch");
memcpy(return_pub.bytes, K_return.bytes, sizeof(encrypted_return_pubkey_t));
#endif
return_pubkey_out = return_pub ^ m_return;
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
@@ -470,7 +510,6 @@ bool test_carrot_view_tag(const unsigned char s_sender_receiver_unctx[32],
// vt' = H_3(s_sr || input_context || Ko)
view_tag_t nominal_view_tag;
make_carrot_view_tag(s_sender_receiver_unctx, input_context, onetime_address, nominal_view_tag);
// vt' ?= vt
return nominal_view_tag == view_tag;
}
+19 -4
View File
@@ -138,6 +138,20 @@ void make_sparc_return_privkey(const unsigned char s_sender_receiver_unctx[32],
const crypto::public_key &onetime_address,
crypto::secret_key &return_privkey_out);
/**
* brief: make_sparc_return_index - return index, given non-secret data
* m_return = H_32(s_sr || input_context || Ko || idx)
* param: s_sender_receiver_unctx - s_sr
* param: input_context - input_context
* param: onetime_address - Ko
* param: idx - idx
* outparam: return_index_out - m_return
*/
void make_sparc_return_index(const unsigned char s_sender_receiver_unctx[32],
const input_context_t &input_context,
const crypto::public_key &onetime_address,
const uint64_t idx,
crypto::secret_key &return_index_out);
/**
* brief: make_sparc_return_pubkey_encryption_mask - used for hiding return pubkey
* m_return = H_32(s_sr || input_context || Ko)
* param: s_sender_receiver_unctx - s_sr
@@ -159,10 +173,11 @@ void make_sparc_return_pubkey_encryption_mask(const unsigned char s_sender_recei
* outparam: return_pubkey_mask_out - K_return
*/
void make_sparc_return_pubkey(const unsigned char s_sender_receiver_unctx[32],
const input_context_t &input_context,
const view_balance_secret_device *s_view_balance_dev,
const crypto::public_key &onetime_address,
encrypted_return_pubkey_t &return_pubkey_out);
const input_context_t &input_context,
const view_balance_secret_device *s_view_balance_dev,
const crypto::public_key &onetime_address,
const uint64_t idx,
encrypted_return_pubkey_t &return_pubkey_out);
/**
* brief: make_carrot_input_context_coinbase - input context for a sender-receiver secret (coinbase txs)
* input_context = "C" || IntToBytes256(block_index)
+3 -3
View File
@@ -41,9 +41,9 @@
namespace carrot
{
#define CARROT_DEFINE_SIMPLE_ERROR_TYPE(e, b) class e: b { using b::b; };
#define CARROT_DEFINE_SIMPLE_ERROR_TYPE(e, b) class e: public b { using b::b; };
class carrot_logic_error: std::logic_error { using std::logic_error::logic_error; };
class carrot_logic_error: public std::logic_error { using std::logic_error::logic_error; };
CARROT_DEFINE_SIMPLE_ERROR_TYPE(bad_address_type, carrot_logic_error)
CARROT_DEFINE_SIMPLE_ERROR_TYPE(component_out_of_order, carrot_logic_error)
@@ -55,7 +55,7 @@ CARROT_DEFINE_SIMPLE_ERROR_TYPE(too_few_outputs, carrot_logic_error)
CARROT_DEFINE_SIMPLE_ERROR_TYPE(too_many_outputs, carrot_logic_error)
CARROT_DEFINE_SIMPLE_ERROR_TYPE(invalid_tx_type, carrot_logic_error)
class carrot_runtime_error: std::runtime_error { using std::runtime_error::runtime_error; };
class carrot_runtime_error: public std::runtime_error { using std::runtime_error::runtime_error; };
CARROT_DEFINE_SIMPLE_ERROR_TYPE(crypto_function_failed, carrot_runtime_error)
CARROT_DEFINE_SIMPLE_ERROR_TYPE(not_enough_money, carrot_runtime_error)
+10 -6
View File
@@ -77,7 +77,7 @@ std::optional<AdditionalOutputType> get_additional_output_type(const size_t num_
}
else if (!need_change_output)
{
return AdditionalOutputType::DUMMY;
return AdditionalOutputType::CHANGE_UNIQUE;
}
else // num_selfsend == 1 && need_change_output
{
@@ -174,7 +174,10 @@ 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;
if (tx_type == cryptonote::transaction_type::STAKE || tx_type == cryptonote::transaction_type::BURN) {
if (tx_type == cryptonote::transaction_type::STAKE ||
tx_type == cryptonote::transaction_type::BURN ||
tx_type == cryptonote::transaction_type::CREATE_TOKEN ||
tx_type == cryptonote::transaction_type::ROLLUP) {
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");
@@ -221,11 +224,12 @@ void get_output_enote_proposals(const std::vector<CarrotPaymentProposalV1> &norm
encrypted_payment_id_t encrypted_payment_id;
if (tx_type == cryptonote::transaction_type::RETURN) {
const uint64_t idx = 0;
get_output_proposal_return_v1(normal_payment_proposals[i],
tx_first_key_image,
s_view_balance_dev,
output_entry.first,
encrypted_payment_id);
tx_first_key_image,
s_view_balance_dev,
output_entry.first,
encrypted_payment_id);
} else {
get_output_proposal_normal_v1(normal_payment_proposals[i],
tx_first_key_image,
+95 -7
View File
@@ -36,6 +36,7 @@
#include "misc_language.h"
#include "misc_log_ex.h"
#include "ringct/rctOps.h"
#include "crypto/generators.h"
//third party headers
@@ -208,10 +209,11 @@ static void get_external_output_proposal_parts(const mx25519_pubkey &s_sender_re
// 4. construct the return pubkey
if (s_view_balance_dev != nullptr)
make_sparc_return_pubkey(s_sender_receiver_unctx.data,
input_context,
s_view_balance_dev,
onetime_address_out,
return_pubkey_out);
input_context,
s_view_balance_dev,
onetime_address_out,
0,
return_pubkey_out);
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
@@ -219,6 +221,7 @@ bool operator==(const CarrotPaymentProposalV1 &a, const CarrotPaymentProposalV1
{
return a.destination == b.destination &&
a.amount == b.amount &&
a.asset_type == b.asset_type &&
a.randomness == b.randomness;
}
//-------------------------------------------------------------------------------------------------------------------
@@ -228,6 +231,7 @@ bool operator==(const CarrotPaymentProposalSelfSendV1 &a, const CarrotPaymentPro
a.amount == b.amount &&
a.enote_type == b.enote_type &&
a.internal_message == b.internal_message &&
a.asset_type == b.asset_type &&
0 == memcmp(&a.enote_ephemeral_pubkey, &b.enote_ephemeral_pubkey, sizeof(mx25519_pubkey));
}
//-------------------------------------------------------------------------------------------------------------------
@@ -421,11 +425,95 @@ void get_output_proposal_special_v1(const CarrotPaymentProposalSelfSendV1 &propo
// 8. save the enote ephemeral pubkey, first tx key image, and amount
output_enote_out.enote.enote_ephemeral_pubkey = enote_ephemeral_pubkey;
output_enote_out.enote.tx_first_key_image = tx_first_key_image;
output_enote_out.enote.asset_type = "SAL1";
output_enote_out.enote.asset_type = proposal.asset_type;
output_enote_out.enote.return_enc = crypto::rand<carrot::encrypted_return_pubkey_t>();
output_enote_out.amount = proposal.amount;
}
//-------------------------------------------------------------------------------------------------------------------
void get_output_proposal_paymentchannel_v1(const CarrotPaymentProposalV1 &proposal,
const crypto::key_image &tx_first_key_image,
const view_balance_secret_device *s_view_balance_dev,
const crypto::public_key &K_o,
const uint64_t idx,
RCTOutputEnoteProposal &output_enote_out,
encrypted_payment_id_t &encrypted_payment_id_out)
{
// 1. sanity checks
CARROT_CHECK_AND_THROW(proposal.randomness != null_anchor,
missing_randomness, "invalid randomness for janus anchor (zero).");
// 2. input context: input_context = "R" || KI_1
const input_context_t input_context = make_carrot_input_context(tx_first_key_image);
/*
// K_v = K_return - k_idx * T
CarrotPaymentProposalV1 proposal_with_kv = proposal;
const bool is_sparc = (k_idx != crypto::null_skey) && sc_isnonzero(to_bytes(k_idx)); // check here! for zero its not needed
if (is_sparc)
{
// Calculate K_v = K_return - k_idx * T
const rct::key K_return_rct = rct::pk2rct(proposal.destination.address_view_pubkey);
const rct::key k_idx_T = rct::scalarmultKey(rct::pk2rct(crypto::get_T()), rct::sk2rct(k_idx));
rct::key K_v_rct;
rct::subKeys(K_v_rct, K_return_rct, k_idx_T);
proposal_with_kv.destination.address_view_pubkey = rct::rct2pk(K_v_rct);
}
*/
mx25519_pubkey s_sender_receiver_unctx; auto dhe_wiper = auto_wiper(s_sender_receiver_unctx);
get_normal_proposal_ecdh_parts(proposal,
input_context,
output_enote_out.enote.enote_ephemeral_pubkey,
s_sender_receiver_unctx);
// 4. build the output enote address pieces
crypto::hash s_sender_receiver; auto q_wiper = auto_wiper(s_sender_receiver);
encrypted_return_pubkey_t return_pubkey;
get_external_output_proposal_parts(
s_sender_receiver_unctx,
proposal.destination.address_spend_pubkey,
proposal.destination.payment_id,
proposal.amount,
CarrotEnoteType::PAYMENT,
output_enote_out.enote.enote_ephemeral_pubkey,
input_context,
s_view_balance_dev,
false, // coinbase_amount_commitment
s_sender_receiver,
output_enote_out.amount_blinding_factor,
output_enote_out.enote.amount_commitment,
output_enote_out.enote.onetime_address,
output_enote_out.enote.amount_enc,
encrypted_payment_id_out,
output_enote_out.enote.view_tag,
return_pubkey
);
// Override the onetime address
crypto::secret_key k_idx;
make_sparc_return_index(s_sender_receiver_unctx.data, input_context, K_o, idx, k_idx);
rct::key K_r = rct::addKeys(rct::pk2rct(proposal.destination.address_spend_pubkey),rct::pk2rct(proposal.destination.address_view_pubkey));
rct::key K_idx = rct::scalarmultKey(rct::pk2rct(crypto::get_T()), rct::sk2rct(k_idx));
output_enote_out.enote.onetime_address = rct::rct2pk(rct::addKeys(K_r, K_idx));
// Recalculate the view tag : vt = H_3(s_sr || input_context || Ksra)
make_carrot_view_tag(s_sender_receiver_unctx.data, input_context, output_enote_out.enote.onetime_address, output_enote_out.enote.view_tag);
// Recalculate a_enc = BytesToInt64(a) XOR m_a
output_enote_out.enote.amount_enc = encrypt_carrot_amount(proposal.amount, s_sender_receiver, output_enote_out.enote.onetime_address);
// Recalculate anchor_enc = anchor XOR m_anchor
output_enote_out.enote.anchor_enc = encrypt_carrot_anchor(proposal.randomness, s_sender_receiver, output_enote_out.enote.onetime_address);
// Recalculate the pid_enc = pid XOR m_pid
encrypted_payment_id_out = encrypt_legacy_payment_id(proposal.destination.payment_id, s_sender_receiver, output_enote_out.enote.onetime_address);
// 6. save the amount & first key image & asset type
output_enote_out.amount = proposal.amount;
output_enote_out.enote.asset_type = proposal.asset_type;
output_enote_out.enote.tx_first_key_image = tx_first_key_image;
// disable returning an already returned tx.
output_enote_out.enote.return_enc = crypto::rand<carrot::encrypted_return_pubkey_t>();
}
//-------------------------------------------------------------------------------------------------------------------
void get_output_proposal_return_v1(const CarrotPaymentProposalV1 &proposal,
const crypto::key_image &tx_first_key_image,
const view_balance_secret_device *s_view_balance_dev,
@@ -559,12 +647,12 @@ void get_output_proposal_internal_v1(const CarrotPaymentProposalSelfSendV1 &prop
// 9. save the enote ephemeral pubkey, first tx key image, and amount
output_enote_out.enote.enote_ephemeral_pubkey = enote_ephemeral_pubkey;
output_enote_out.enote.tx_first_key_image = tx_first_key_image;
output_enote_out.enote.asset_type = "SAL1";
output_enote_out.enote.asset_type = proposal.asset_type;
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) {
if (tx_type == cryptonote::transaction_type::STAKE || tx_type == cryptonote::transaction_type::CREATE_TOKEN) {
// 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);
+21 -4
View File
@@ -82,6 +82,8 @@ struct CarrotPaymentProposalSelfSendV1 final
std::optional<mx25519_pubkey> enote_ephemeral_pubkey;
/// anchor: arbitrary, pre-encrypted message for _internal_ selfsends
std::optional<janus_anchor_t> internal_message;
/// asset type
std::string asset_type;
};
struct RCTOutputEnoteProposal
@@ -137,6 +139,21 @@ void get_output_proposal_normal_v1(const CarrotPaymentProposalV1 &proposal,
RCTOutputEnoteProposal &output_enote_out,
encrypted_payment_id_t &encrypted_payment_id_out);
/**
* brief: get_output_proposal_paymentchannel_v1 - convert the carrot proposal to an output proposal
* param: proposal -
* param: tx_first_key_image -
* param: k_view_dev -
* outparam: output_enote_out -
* outparam: encrypted_payment_id_out - pid_enc
*/
void get_output_proposal_paymentchannel_v1(const CarrotPaymentProposalV1 &proposal,
const crypto::key_image &tx_first_key_image,
const view_balance_secret_device *s_view_balance_dev,
const crypto::public_key &K_o,
const uint64_t idx,
RCTOutputEnoteProposal &output_enote_out,
encrypted_payment_id_t &encrypted_payment_id_out);
/**
* brief: get_output_proposal_return_v1 - convert the carrot proposal to an output proposal
* param: proposal -
* param: tx_first_key_image -
@@ -145,10 +162,10 @@ void get_output_proposal_normal_v1(const CarrotPaymentProposalV1 &proposal,
* outparam: encrypted_payment_id_out - pid_enc
*/
void get_output_proposal_return_v1(const CarrotPaymentProposalV1 &proposal,
const crypto::key_image &tx_first_key_image,
const view_balance_secret_device *s_view_balance_dev,
RCTOutputEnoteProposal &output_enote_out,
encrypted_payment_id_t &encrypted_payment_id_out);
const crypto::key_image &tx_first_key_image,
const view_balance_secret_device *s_view_balance_dev,
RCTOutputEnoteProposal &output_enote_out,
encrypted_payment_id_t &encrypted_payment_id_out);
/**
* brief: get_output_proposal_special_v1 - convert the carrot proposal to an output proposal (external selfsend)
* param: proposal -
+19 -19
View File
@@ -387,6 +387,11 @@ bool try_scan_carrot_enote_external_sender(const CarrotEnoteV1 &enote,
CarrotEnoteType &enote_type_out,
const bool check_pid)
{
epee::span<const crypto::public_key> main_address_spend_pubkeys;
if (destination.is_subaddress)
main_address_spend_pubkeys = {};
else
main_address_spend_pubkeys = {&destination.address_spend_pubkey, 1};
crypto::public_key recovered_address_spend_pubkey;
payment_id_t recovered_payment_id;
CarrotEnoteType recovered_enote_type;
@@ -395,7 +400,7 @@ bool try_scan_carrot_enote_external_sender(const CarrotEnoteV1 &enote,
if (!try_scan_carrot_enote_external_normal_checked(enote,
encrypted_payment_id,
s_sender_receiver_unctx,
{&destination.address_spend_pubkey, 1},
main_address_spend_pubkeys,
sender_extension_g_out,
sender_extension_t_out,
recovered_address_spend_pubkey,
@@ -470,6 +475,9 @@ bool try_scan_carrot_enote_internal_receiver(const CarrotEnoteV1 &enote,
crypto::public_key &return_address_out,
bool &is_return_out)
{
// Determine whether this is a full wallet or a watch-only wallet
const cryptonote::account_keys &keys = account.get_keys();
// input_context
const input_context_t input_context = make_carrot_input_context(enote.tx_first_key_image);
@@ -488,7 +496,6 @@ bool try_scan_carrot_enote_internal_receiver(const CarrotEnoteV1 &enote,
input_context,
s_sender_receiver);
bool normal_change_found = true;
if (!try_scan_carrot_enote_internal_burnt(enote,
s_sender_receiver,
sender_extension_g_out,
@@ -518,24 +525,17 @@ bool try_scan_carrot_enote_internal_receiver(const CarrotEnoteV1 &enote,
// calculate the key image for the return output
crypto::secret_key sum_g;
sc_add(to_bytes(sum_g), to_bytes(sender_extension_g_out), to_bytes(k_return));
crypto::key_image key_image = account.derive_key_image(
account.get_keys().m_carrot_account_address.m_spend_public_key,
sum_g,
sender_extension_t_out,
K_r
);
crypto::key_image key_image = account.derive_key_image_view_only(address_spend_pubkey_out,
sum_g,
sender_extension_t_out,
K_r
);
crypto::secret_key x, y;
account.try_searching_for_opening_for_onetime_address(
account.get_keys().m_carrot_account_address.m_spend_public_key,
sum_g,
sender_extension_t_out,
x,
y
);
// save the input context & change output key
account.insert_return_output_info({{K_r, {input_context, output_key, enote.onetime_address, key_image, x, y}}});
// HERE BE DRAGONS!!!
// SRCG: test whether this will even work for return_payment detection
account.insert_return_output_info({{K_r, {input_context, output_key, enote.onetime_address, address_spend_pubkey_out, key_image, sum_g, sender_extension_t_out}}});
//account.insert_return_output_info({{K_r, {input_context, output_key, address_spend_pubkey_out, key_image, sum_g, sender_extension_t_out}}});
// LAND AHOY!!!
}
// janus protection checks are not needed for internal scans
@@ -86,6 +86,12 @@ inline void serialize(Archive &a, carrot::encrypted_return_pubkey_t &x, const bo
}
//---------------------------------------------------
template <class Archive>
inline void serialize(Archive &a, carrot::rollup_binding_tag_t &x, const boost::serialization::version_type ver)
{
a & x.bytes;
}
//---------------------------------------------------
template <class Archive>
inline void serialize(Archive &a, carrot::CarrotDestinationV1 &x, const boost::serialization::version_type ver)
{
a & x.address_spend_pubkey;
@@ -42,3 +42,4 @@ BLOB_SERIALIZER(carrot::view_tag_t);
BLOB_SERIALIZER(carrot::encrypted_janus_anchor_t);
BLOB_SERIALIZER(carrot::encrypted_payment_id_t);
BLOB_SERIALIZER(carrot::encrypted_return_pubkey_t);
BLOB_SERIALIZER(carrot::rollup_binding_tag_t);
+42 -17
View File
@@ -193,31 +193,51 @@ bool try_load_carrot_extra_v1(
}
//-------------------------------------------------------------------------------------------------------------------
cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotEnoteV1> &enotes,
const std::vector<crypto::key_image> &key_images,
const std::vector<cryptonote::tx_source_entry> &sources,
const rct::xmr_amount fee,
const cryptonote::transaction_type tx_type,
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 std::vector<crypto::key_image> &key_images,
const std::vector<cryptonote::tx_source_entry> &sources,
const rct::xmr_amount fee,
const cryptonote::transaction_type tx_type,
const rct::xmr_amount tx_amount_burnt,
const std::vector<uint8_t> &change_masks,
const cryptonote::token_metadata_t &token,
const carrot::RCTOutputEnoteProposal &return_enote,
const encrypted_payment_id_t encrypted_payment_id,
const uint8_t hf_version)
{
const size_t nins = key_images.size();
const size_t nouts = enotes.size();
CHECK_AND_ASSERT_THROW_MES(nins == sources.size(), "invalid inputs/sources size");
CHECK_AND_ASSERT_THROW_MES(change_masks.size() == nouts, "invalid change masks size. Expected: " << nouts - 1 << " got: " << change_masks.size());
// Sanity check asset types - all enotes and sources must be the same
std::string asset_type = "";
for (const auto &enote: enotes) {
if (asset_type == "")
asset_type = enote.asset_type;
else
CHECK_AND_ASSERT_THROW_MES(enote.asset_type == asset_type, "invalid asset_type in enote. Expected: " << asset_type << " got: " << enote.asset_type);
}
for (const auto &source: sources) {
if (asset_type == "")
asset_type = source.asset_type;
else
CHECK_AND_ASSERT_THROW_MES(source.asset_type == asset_type, "invalid asset_type in source. Expected: " << asset_type << " got: " << source.asset_type);
}
cryptonote::transaction tx;
tx.pruned = true;
tx.unlock_time = 0;
tx.source_asset_type = "SAL1";
tx.destination_asset_type = "SAL1";
tx.version = TRANSACTION_VERSION_CARROT;
tx.source_asset_type = asset_type;
tx.destination_asset_type = asset_type;
tx.version = (hf_version >= HF_VERSION_ENABLE_TOKENS) ? TRANSACTION_VERSION_ENABLE_TOKENS : 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.type == cryptonote::transaction_type::STAKE ||
tx.type == cryptonote::transaction_type::BURN ||
tx.type == cryptonote::transaction_type::CREATE_TOKEN ||
tx.type == cryptonote::transaction_type::ROLLUP
) ? tx_amount_burnt : 0;
tx.return_address_change_mask.assign(change_masks.begin(), change_masks.end());
tx.vin.reserve(nins);
tx.vout.reserve(nouts);
@@ -239,7 +259,7 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotE
//L
tx.vin.emplace_back(cryptonote::txin_to_key{ //@TODO: can save 2 bytes by using slim input type
.amount = 0,
.asset_type = "SAL1",
.asset_type = asset_type,
.key_offsets = cryptonote::absolute_output_offsets_to_relative(key_offsets),
.k_image = key_images.at(i)
});
@@ -264,7 +284,7 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotE
tx.rct_signatures.outPk.push_back(rct::ctkey{rct::key{}, enote.amount_commitment});
//K_return
if (tx_type != cryptonote::transaction_type::STAKE) {
if (tx_type != cryptonote::transaction_type::STAKE && tx_type != cryptonote::transaction_type::CREATE_TOKEN) {
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);
@@ -272,7 +292,7 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotE
}
// store the return pubkey for stake txs
if (tx_type == cryptonote::transaction_type::STAKE)
if (tx_type == cryptonote::transaction_type::STAKE || tx_type == cryptonote::transaction_type::CREATE_TOKEN)
{
tx.protocol_tx_data.version = 1;
memcpy(tx.protocol_tx_data.return_address.data, return_enote.enote.onetime_address.data, sizeof(crypto::public_key));
@@ -280,7 +300,12 @@ cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotE
tx.protocol_tx_data.return_view_tag = return_enote.enote.view_tag;
tx.protocol_tx_data.return_anchor_enc = return_enote.enote.anchor_enc;
}
if (tx_type == cryptonote::transaction_type::CREATE_TOKEN) {
tx.token_metadata = token;
} else {
tx.token_metadata = cryptonote::token_metadata_t{};
}
//ephemeral pubkeys: D_e
store_carrot_ephemeral_pubkeys_to_extra(enotes, tx.extra);
+10 -8
View File
@@ -102,14 +102,16 @@ bool try_load_carrot_extra_v1(
* return: a fully populated, pruned, non-coinbase transaction containing given Carrot information
*/
cryptonote::transaction store_carrot_to_transaction_v1(const std::vector<CarrotEnoteV1> &enotes,
const std::vector<crypto::key_image> &key_images,
const std::vector<cryptonote::tx_source_entry> &sources,
const rct::xmr_amount fee,
const cryptonote::transaction_type tx_type,
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);
const std::vector<crypto::key_image> &key_images,
const std::vector<cryptonote::tx_source_entry> &sources,
const rct::xmr_amount fee,
const cryptonote::transaction_type tx_type,
const rct::xmr_amount tx_amount_burnt,
const std::vector<uint8_t> &change_masks,
const cryptonote::token_metadata_t &token,
const RCTOutputEnoteProposal &return_enote,
const encrypted_payment_id_t encrypted_payment_id,
const uint8_t hf_version);
/**
* brief: try_load_carrot_enote_from_transaction_v1 - load one non-coinbase Carrot enote from a cryptonote::transaction
* param: tx -
+18 -5
View File
@@ -88,7 +88,8 @@ static std::pair<std::size_t, boost::multiprecision::uint128_t> input_count_for_
const epee::span<const InputCandidate> input_candidates,
const std::set<std::size_t> &selectable_inputs,
std::size_t max_num_input_count,
const std::map<std::size_t, rct::xmr_amount> &fee_by_input_count)
const std::map<std::size_t, rct::xmr_amount> &fee_by_input_count,
const bool is_token_transfer)
{
// Returns (N, X) where the X is the sum of the amounts of the greatest N <= max_num_input_count
// inputs from selectable_inputs, maximizing X - F(N). F(N) is the fee for this transaction,
@@ -96,6 +97,7 @@ static std::pair<std::size_t, boost::multiprecision::uint128_t> input_count_for_
// the fee, but greater than or equal to the difference of the fee compared to excluding that
// input. If this function returns N == 0, then there aren't enough usable funds, i.e. no N
// exists such that X - F(N) > 0.
// For token transfers, fee is paid separately in SAL1 so we just accumulate all inputs.
if (fee_by_input_count.empty() || selectable_inputs.empty())
return {0, 0};
@@ -125,6 +127,15 @@ static std::pair<std::size_t, boost::multiprecision::uint128_t> input_count_for_
{
const rct::xmr_amount amount = *amount_it;
const rct::xmr_amount current_fee = fee_by_input_count.at(num_ins + 1);
// For token transfers, fee is paid separately in SAL1 - just accumulate inputs
if (is_token_transfer)
{
++num_ins;
cumulative_input_sum += amount;
continue;
}
CARROT_CHECK_AND_THROW(current_fee > last_fee,
carrot_logic_error, "provided fee by input count is not monotonically increasing");
const rct::xmr_amount marginal_fee_diff = current_fee - last_fee;
@@ -372,11 +383,12 @@ select_inputs_func_t make_single_transfer_input_selector(
// 3. Calculate minimum required input money sum for a given input count
const bool subtract_fee = flags & IS_KNOWN_FEE_SUBTRACTABLE;
const bool is_token_transfer = flags & IS_TOKEN_TRANSFER;
std::map<std::size_t, boost::multiprecision::uint128_t> required_money_by_input_count;
for (const auto &fee_and_input_count : fee_by_input_count)
{
required_money_by_input_count[fee_and_input_count.first] =
nominal_output_sum + (subtract_fee ? 0 : fee_and_input_count.second);
nominal_output_sum + ((subtract_fee || is_token_transfer) ? 0 : fee_and_input_count.second);
}
const boost::multiprecision::uint128_t absolute_minimum_required_money
= required_money_by_input_count.cbegin()->second;
@@ -390,9 +402,10 @@ select_inputs_func_t make_single_transfer_input_selector(
"Not enough money in all inputs (" << cryptonote::print_money(total_candidate_money)
<< ") to fund minimum output sum (" << cryptonote::print_money(absolute_minimum_required_money) << ')');
// const bool is_token_transfer = flags & IS_TOKEN_TRANSFER;
std::set<std::size_t> all_idxs; for (std::size_t i = 0; i < input_candidates.size(); ++i) all_idxs.insert(i);
const std::pair<std::size_t, boost::multiprecision::uint128_t> max_usable_money =
input_count_for_max_usable_money(input_candidates, all_idxs, CARROT_MAX_TX_INPUTS, fee_by_input_count);
input_count_for_max_usable_money(input_candidates, all_idxs, CARROT_MAX_TX_INPUTS, fee_by_input_count, is_token_transfer);
CARROT_CHECK_AND_THROW(max_usable_money.second >= absolute_minimum_required_money,
not_enough_usable_money,
"Not enough usable money in top " << max_usable_money.first << " inputs ("
@@ -417,7 +430,7 @@ select_inputs_func_t make_single_transfer_input_selector(
// Skip if not enough money in this selectable set for max number of tx inputs...
const auto max_usable_money = input_count_for_max_usable_money(input_candidates,
input_candidate_subset, CARROT_MAX_TX_INPUTS, fee_by_input_count);
input_candidate_subset, CARROT_MAX_TX_INPUTS, fee_by_input_count, is_token_transfer);
if (!max_usable_money.first)
continue;
else if (max_usable_money.second < required_money_by_input_count.at(max_usable_money.first))
@@ -441,7 +454,7 @@ select_inputs_func_t make_single_transfer_input_selector(
// Skip if not enough money in this selectable set for exact number of inputs...
const auto max_usable_money = input_count_for_max_usable_money(input_candidates,
input_candidate_subset, n_inputs, fee_by_input_count);
input_candidate_subset, n_inputs, fee_by_input_count, is_token_transfer);
if (max_usable_money.first != n_inputs)
continue;
else if (max_usable_money.second < required_money)
+5 -2
View File
@@ -52,14 +52,17 @@ struct InputCandidate
namespace InputSelectionFlags
{
// Quantum forward secrecy (ON = unsafe)
static constexpr std::uint32_t ALLOW_EXTERNAL_INPUTS_IN_NORMAL_TRANSFERS = 1 << 0;
static constexpr std::uint32_t ALLOW_PRE_CARROT_INPUTS_IN_NORMAL_TRANSFERS = 1 << 1;
static constexpr std::uint32_t ALLOW_EXTERNAL_INPUTS_IN_NORMAL_TRANSFERS = 1 << 0; // 000..00000001
static constexpr std::uint32_t ALLOW_PRE_CARROT_INPUTS_IN_NORMAL_TRANSFERS = 1 << 1; // 000..00000010
static constexpr std::uint32_t ALLOW_MIXED_INTERNAL_EXTERNAL = 1 << 2;
static constexpr std::uint32_t ALLOW_MIXED_CARROT_PRE_CARROT = 1 << 3;
// Amount handling
static constexpr std::uint32_t IS_KNOWN_FEE_SUBTRACTABLE = 1 << 4;
static constexpr std::uint32_t ALLOW_DUST = 1 << 5;
// Token transfer (fee is paid separately in SAL1 via rollup tx, so no fee for input selection)
static constexpr std::uint32_t IS_TOKEN_TRANSFER = 1 << 6;
}
/**
+3 -1
View File
@@ -132,7 +132,9 @@ void make_pruned_transaction_from_proposal_v1(const CarrotTransactionProposalV1
0, // tx_amount_burnt
{}, // change_masks
{}, // return_enote
encrypted_payment_id);
{0},
encrypted_payment_id,
/*hf_version=*/10);
// add extra payload and sort
if (!tx_proposal.extra.empty())
+13 -3
View File
@@ -67,8 +67,8 @@ struct CarrotPaymentProposalVerifiableSelfSendV1
*
* For exact details on what goes into the signable transaction hash, see `rct::get_pre_mlsag_hash`.
*/
struct CarrotTransactionProposalV1
{
struct CarrotTransactionProposalV1
{
/// Key images sorted in std::greater order
std::vector<crypto::key_image> key_images_sorted;
// sources in the same order as key_images_sorted.
@@ -89,8 +89,18 @@ struct CarrotTransactionProposalV1
/// how much money tx burns
rct::xmr_amount amount_burnt;
/// used if this is a CREATE_TOKEN transaction
cryptonote::token_metadata_t token;
/// 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
std::vector<std::uint8_t> extra;
};
/// Used if this is a TOKEN_TRANSFER or ROLLUP transaction
carrot::rollup_binding_tag_t rollup_binding_tag;
/// Used if this is a ROLLUP transaction
cryptonote::layer2_rollup_data_t layer2_rollup_data;
};
} //namespace carrot
+43 -7
View File
@@ -209,17 +209,34 @@ void make_carrot_transaction_proposal_v1(const std::vector<CarrotPaymentProposal
input_amount_sum += selected_input.amount;
// callback to balance the outputs with the fee and input sum
carve_fees_and_balance(input_amount_sum, tx_proposal_out.fee, normal_payment_proposals, selfsend_payment_proposals);
std::string asset_type = "SAL1"; // default to SAL1
if (!normal_payment_proposals.empty()) {
asset_type = normal_payment_proposals.at(0).asset_type;
} else if (!selfsend_payment_proposals.empty()) {
asset_type = selfsend_payment_proposals.at(0).proposal.asset_type;
}
uint64_t fee = tx_proposal_out.fee;
if (asset_type != "SAL1") {
if (tx_type != cryptonote::transaction_type::BURN || asset_type != "SAL") {
CARROT_CHECK_AND_THROW(cryptonote::is_valid_custom_asset_type(asset_type),
carrot_logic_error, "make_carrot_transaction_proposal_v1: invalid asset type in payment proposals: " << asset_type);
}
fee = 0; //fee is always in SAL1
}
carve_fees_and_balance(input_amount_sum, fee, normal_payment_proposals, selfsend_payment_proposals);
// sanity check balance
input_amount_sum -= tx_proposal_out.fee;
input_amount_sum -= fee;
for (const CarrotPaymentProposalV1 &normal_payment_proposal : normal_payment_proposals)
input_amount_sum -= normal_payment_proposal.amount;
for (const CarrotPaymentProposalVerifiableSelfSendV1 &selfsend_payment_proposal : selfsend_payment_proposals)
input_amount_sum -= selfsend_payment_proposal.proposal.amount;
if (tx_type != cryptonote::transaction_type::STAKE &&
tx_type != cryptonote::transaction_type::BURN)
tx_type != cryptonote::transaction_type::BURN &&
tx_type != cryptonote::transaction_type::CREATE_TOKEN &&
tx_type != cryptonote::transaction_type::ROLLUP)
{
CHECK_AND_ASSERT_THROW_MES(input_amount_sum == 0,
"make_carrot_transaction_proposal_v1: post-carved transaction does not balance");
@@ -239,7 +256,7 @@ void make_carrot_transaction_proposal_v1(const std::vector<CarrotPaymentProposal
tx_proposal_out.key_images_sorted.end(),
std::greater{}); // consensus rules dictate inputs sorted in *reverse* lexicographical order since v7
// set the transaction type
// set the transaction type & new asset type
tx_proposal_out.tx_type = tx_type;
}
//-------------------------------------------------------------------------------------------------------------------
@@ -255,6 +272,7 @@ void make_carrot_transaction_proposal_v1_transfer(
const subaddress_index_extended &change_address_index,
const std::set<std::size_t> &subtractable_normal_payment_proposals,
const std::set<std::size_t> &subtractable_selfsend_payment_proposals,
const std::string &asset_type,
CarrotTransactionProposalV1 &tx_proposal_out)
{
std::vector<CarrotPaymentProposalVerifiableSelfSendV1> selfsend_payment_proposals = selfsend_payment_proposals_in;
@@ -275,7 +293,8 @@ void make_carrot_transaction_proposal_v1_transfer(
.proposal = CarrotPaymentProposalSelfSendV1{
.destination_address_spend_pubkey = change_address_spend_pubkey,
.amount = 0,
.enote_type = add_payment_type_selfsend ? CarrotEnoteType::PAYMENT : CarrotEnoteType::CHANGE
.enote_type = add_payment_type_selfsend ? CarrotEnoteType::PAYMENT : CarrotEnoteType::CHANGE,
.asset_type = asset_type
},
.subaddr_index = change_address_index
});
@@ -397,10 +416,13 @@ void make_carrot_transaction_proposal_v1_transfer(
// 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)
tx_type == cryptonote::transaction_type::BURN ||
tx_type == cryptonote::transaction_type::CREATE_TOKEN ||
tx_type == cryptonote::transaction_type::ROLLUP)
{
selfsend_payment_proposals.back().proposal.enote_ephemeral_pubkey =
selfsend_payment_proposals.front().proposal.enote_ephemeral_pubkey;
selfsend_payment_proposals.erase(selfsend_payment_proposals.begin());
}
@@ -430,6 +452,7 @@ void make_carrot_transaction_proposal_v1_sweep(
std::vector<CarrotSelectedInput> &&selected_inputs,
const crypto::public_key &change_address_spend_pubkey,
const subaddress_index_extended &change_address_index,
const std::string &asset_type,
CarrotTransactionProposalV1 &tx_proposal_out)
{
// sanity check payment proposals are provided
@@ -452,6 +475,19 @@ void make_carrot_transaction_proposal_v1_sweep(
CHECK_AND_ASSERT_THROW_MES(bool(normal_payment_proposals.size()) ^ bool(selfsend_payment_proposals.size()),
"make carrot transaction proposal v1 sweep: both normal and self-send payment proposals are provided");
std::vector<CarrotPaymentProposalVerifiableSelfSendV1> selfsend_payment_proposals_inout{selfsend_payment_proposals};
//if (tx_type == cryptonote::transaction_type::RETURN) {
selfsend_payment_proposals_inout.push_back(carrot::CarrotPaymentProposalVerifiableSelfSendV1{
.proposal = carrot::CarrotPaymentProposalSelfSendV1{
.destination_address_spend_pubkey = change_address_spend_pubkey,
.amount = 0,
.enote_type = carrot::CarrotEnoteType::CHANGE,
.asset_type = asset_type
},
.subaddr_index = change_address_index
});
// }
const bool is_selfsend_sweep = !selfsend_payment_proposals.empty();
// define input selection callback, which is just a shuttle for `selected_inputs`
@@ -502,7 +538,7 @@ void make_carrot_transaction_proposal_v1_sweep(
// make unsigned transaction with sweep carving callback and selected inputs
make_carrot_transaction_proposal_v1(normal_payment_proposals,
selfsend_payment_proposals,
selfsend_payment_proposals_inout,
fee_per_weight,
fee_quantization_mask,
extra,
+2
View File
@@ -213,6 +213,7 @@ void make_carrot_transaction_proposal_v1_transfer(
const subaddress_index_extended &change_address_index,
const std::set<std::size_t> &subtractable_normal_payment_proposals,
const std::set<std::size_t> &subtractable_selfsend_payment_proposals,
const std::string &asset_type,
CarrotTransactionProposalV1 &tx_proposal_out);
void make_carrot_transaction_proposal_v1_sweep(
@@ -225,6 +226,7 @@ void make_carrot_transaction_proposal_v1_sweep(
std::vector<CarrotSelectedInput> &&selected_inputs,
const crypto::public_key &change_address_spend_pubkey,
const subaddress_index_extended &change_address_index,
const std::string &asset_type,
CarrotTransactionProposalV1 &tx_proposal_out);
} //namespace carrot
+27 -22
View File
@@ -183,28 +183,33 @@ namespace cryptonote
bool checkpoints::init_default_checkpoints(network_type nettype)
{
if (nettype == MAINNET) {
ADD_CHECKPOINT2(1, "b6b45052e7e182ebaeb14ab713db29ad979115e664d766aa0910e325564a27a6", "0x2");
ADD_CHECKPOINT2(10, "82724681cf6bd934eb3253d041de50206a77627ce40ffe418ce6e0fe392ec684", "0x7812a");
ADD_CHECKPOINT2(20, "4dac7b512d876df05bfa4f39b8dbacd75cb1483fbced8bfc5446ebe21b25a04f", "0xba98f");
ADD_CHECKPOINT2(30, "668246360c93ef791a59157cec9cd09722b32a966051feea399082433138f07b", "0xcc235");
ADD_CHECKPOINT2(40, "9a4183bc1d6e9828eac46505c5ef37ae5447ba6c9325dca02be9e1201f939a7d", "0xdf077");
ADD_CHECKPOINT2(50, "5cd8b089d2e77aed9b803b398c6bff07ca652100cb8fa114c91b72509aeeb7e9", "0xf37eb");
ADD_CHECKPOINT2(60, "0e1acf00dd38e0757996dcdc4b69ad54baf7ebe10ae1e8168b192acb1a0ed7f2", "0x10993b");
ADD_CHECKPOINT2(70, "988977507f388221a927e279307b548a0ae0de10ded8c4f22c315e1b483f921a", "0x121537");
ADD_CHECKPOINT2(80, "88ea1c49b20e7596e21ca8137b2a9fa98558df269a15816fe7d7495f1c63ea43", "0x13a290");
ADD_CHECKPOINT2(90, "254800bb6f9794aad95b2226ffc1a1eef0a817472e1877ae08fac6becb55b147", "0x153a55");
ADD_CHECKPOINT2(100, "ba8d75fad878af26ac2504b4868893a7f86c59f013d0f096925cf53271dd04e8", "0x16e91e");
ADD_CHECKPOINT2(110, "dca0779bfe403730b923fa0918645daeec6096b953be2c554f133460c6fcce35", "0x18acb9");
ADD_CHECKPOINT2(120, "5a57287f6b5c105ae264b88050731c5b9ad1313b916143d7585af1d345e70247", "0x1a88f5");
ADD_CHECKPOINT2(130, "4fd292ac0774461e968924f8097e78ec03eee43a2997deaddbc7993e470a61d6", "0x1c6edd");
ADD_CHECKPOINT2(140, "5a3b6ceeef5fd498ea3330acb8a0e87f2c1566c9b0100ad67237e5664d1f053b", "0x1e4991");
ADD_CHECKPOINT2(150, "78f26d08d39f7d5e1a3548277321471e16c95096fa9bcecbe8a420d406ee249b", "0x202406");
ADD_CHECKPOINT2(160, "7acaab1037ccfbadd3126d2612d5dc154020297f980df0b8df462f9c761d3326", "0x22154b");
ADD_CHECKPOINT2(170, "9541ae934e40fa6749ca3453e47cf5fdf38efbac9efcaa2714121e8a21dd2d24", "0x241ce7");
ADD_CHECKPOINT2(180, "e20bc8ac6aabb6b0792f23a29ce42a577c6a57d177a8ac1a51b68fb6de508045", "0x262b40");
ADD_CHECKPOINT2(190, "f69fdad7a15471b63a82668b618ee5b2a384291269d944b11974a723c1604124", "0x2856a3");
ADD_CHECKPOINT2(200, "eba53fa7006dfcdc837a56c0bc8f0e1883cf34861c26934d680252a6878a3f5d", "0x2aa022");
ADD_CHECKPOINT2(90000, "e125b5c1b26521f98e29df6ec88f041c176a2c0a3fcacd5bd0ad2278e9b02fd2", "0xc99801f937888"); // 3546475285149832
ADD_CHECKPOINT2(1, "b6b45052e7e182ebaeb14ab713db29ad979115e664d766aa0910e325564a27a6", "0x2");
ADD_CHECKPOINT2(10, "82724681cf6bd934eb3253d041de50206a77627ce40ffe418ce6e0fe392ec684", "0x7812a");
ADD_CHECKPOINT2(20, "4dac7b512d876df05bfa4f39b8dbacd75cb1483fbced8bfc5446ebe21b25a04f", "0xba98f");
ADD_CHECKPOINT2(30, "668246360c93ef791a59157cec9cd09722b32a966051feea399082433138f07b", "0xcc235");
ADD_CHECKPOINT2(40, "9a4183bc1d6e9828eac46505c5ef37ae5447ba6c9325dca02be9e1201f939a7d", "0xdf077");
ADD_CHECKPOINT2(50, "5cd8b089d2e77aed9b803b398c6bff07ca652100cb8fa114c91b72509aeeb7e9", "0xf37eb");
ADD_CHECKPOINT2(60, "0e1acf00dd38e0757996dcdc4b69ad54baf7ebe10ae1e8168b192acb1a0ed7f2", "0x10993b");
ADD_CHECKPOINT2(70, "988977507f388221a927e279307b548a0ae0de10ded8c4f22c315e1b483f921a", "0x121537");
ADD_CHECKPOINT2(80, "88ea1c49b20e7596e21ca8137b2a9fa98558df269a15816fe7d7495f1c63ea43", "0x13a290");
ADD_CHECKPOINT2(90, "254800bb6f9794aad95b2226ffc1a1eef0a817472e1877ae08fac6becb55b147", "0x153a55");
ADD_CHECKPOINT2(100, "ba8d75fad878af26ac2504b4868893a7f86c59f013d0f096925cf53271dd04e8", "0x16e91e");
ADD_CHECKPOINT2(110, "dca0779bfe403730b923fa0918645daeec6096b953be2c554f133460c6fcce35", "0x18acb9");
ADD_CHECKPOINT2(120, "5a57287f6b5c105ae264b88050731c5b9ad1313b916143d7585af1d345e70247", "0x1a88f5");
ADD_CHECKPOINT2(130, "4fd292ac0774461e968924f8097e78ec03eee43a2997deaddbc7993e470a61d6", "0x1c6edd");
ADD_CHECKPOINT2(140, "5a3b6ceeef5fd498ea3330acb8a0e87f2c1566c9b0100ad67237e5664d1f053b", "0x1e4991");
ADD_CHECKPOINT2(150, "78f26d08d39f7d5e1a3548277321471e16c95096fa9bcecbe8a420d406ee249b", "0x202406");
ADD_CHECKPOINT2(160, "7acaab1037ccfbadd3126d2612d5dc154020297f980df0b8df462f9c761d3326", "0x22154b");
ADD_CHECKPOINT2(170, "9541ae934e40fa6749ca3453e47cf5fdf38efbac9efcaa2714121e8a21dd2d24", "0x241ce7");
ADD_CHECKPOINT2(180, "e20bc8ac6aabb6b0792f23a29ce42a577c6a57d177a8ac1a51b68fb6de508045", "0x262b40");
ADD_CHECKPOINT2(190, "f69fdad7a15471b63a82668b618ee5b2a384291269d944b11974a723c1604124", "0x2856a3");
ADD_CHECKPOINT2(200, "eba53fa7006dfcdc837a56c0bc8f0e1883cf34861c26934d680252a6878a3f5d", "0x2aa022");
ADD_CHECKPOINT2(90000, "e125b5c1b26521f98e29df6ec88f041c176a2c0a3fcacd5bd0ad2278e9b02fd2", "0xc99801f937888"); // 3546475285149832
ADD_CHECKPOINT2(100000, "ff4e8ec805d5bfbcd01f350ac071be1d944ba73e0d27e37d12acb549902b3f3d", "0xDA97F5697F7D8"); // 3845539075979224
ADD_CHECKPOINT2(150000, "c43281eb5b2a41ee77a4465735e4820c6d14d473b568df7987541afc48f18568", "0x12BDA9ED687AAF"); // 5275087110961839
ADD_CHECKPOINT2(225000, "7648405f7cfb24341d9580275b518bb3713c68e970f547faa0b3bcae450d9ec2", "0x18CD5F1B7BA43A"); // 6981207807730746
ADD_CHECKPOINT2(300000, "a18ca65464c1f2d876dd3a00643c9be265655d6bca364eccc5e3d628b9a5cd2c", "0x1F0C2A50FE631C"); // 8739100165038876
ADD_CHECKPOINT2(375000, "07f0c907cc5cb44cef88e5899a411adddd4b0a4419210906e0583efdcafb499f", "0x240C7F9742F265"); // 10146841299710565
}
return true;
}
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#if defined(__x86_64__)
#define DEBUG_BREAK() asm volatile("int $3")
#elif defined(__aarch64__)
#define DEBUG_BREAK() asm volatile("brk #0xf000")
#elif defined(__arm__)
#define DEBUG_BREAK() asm volatile(".inst 0xe7f001f0") // Encoding for bkpt #0xf01, common for ARM32
#else
#error "Unsupported architecture for DEBUG_BREAK"
#endif
+1
View File
@@ -71,6 +71,7 @@ monero_add_library(cncrypto
target_link_libraries(cncrypto
PUBLIC
epee
mx25519_static
randomx
${Boost_SYSTEM_LIBRARY}
${SODIUM_LIBRARY}
+4 -1
View File
@@ -887,4 +887,7 @@ const ge_p3 ge_p3_H = {
{5858699, 5096796, 21321203, -7536921, -5553480, -11439507, -5627669, 15045946, 19977121, 5275251},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{23443568, -5110398, -8776029, -4345135, 6889568, -14710814, 7474843, 3279062, 14550766, -7453428}
};
};
// Precomputed sqrt(-486664) mod p as 32-byte little-endian array
const fe fe_sqrt_m486664 = {12222970, 8312128, 11511410, -9067497, 15300785, 241793, -25456130, -14121551, 12187136, -3972024};
+125 -2
View File
@@ -104,6 +104,21 @@ void fe_1(fe h) {
h[9] = 0;
}
int fe_equal(const fe a, const fe b)
{
fe t;
fe_sub(t, a, b);
return fe_isnonzero(t) == 0;
}
void ge_from_xy(ge_p3 *out, const fe x, const fe y)
{
fe_1(out->Z); // Z = 1
fe_copy(out->X, x); // X = x
fe_copy(out->Y, y); // Y = y
fe_mul(out->T, x, y); // T = x*y
}
/* From fe_add.c */
/*
@@ -365,7 +380,7 @@ int fe_isnegative(const fe f) {
/* From fe_isnonzero.c, modified */
static int fe_isnonzero(const fe f) {
int fe_isnonzero(const fe f) {
unsigned char s[32];
fe_tobytes(s, f);
return (((int) (s[0] | s[1] | s[2] | s[3] | s[4] | s[5] | s[6] | s[7] | s[8] |
@@ -3967,6 +3982,114 @@ int edwards_bytes_to_x25519_vartime(unsigned char *xbytes, const unsigned char *
return 0;
}
// Precomputed sqrt(-1) from Monero (fe_sqrtm1 in crypto-ops-data.c)
extern const fe fe_sqrtm1;
extern const fe fe_sqrt_m486664;
// Function to recover v from u (returns 0 on success, -1 if not on curve)
int fe_sqrt_mont(fe v_out, const fe u_in) {
fe rhs;
fe t0, t1, t2;
fe candidate, c2, check;
// Compute rhs = u^3 + A u^2 + u, A=486662
fe A_fe;
fe_frombytes_vartime(A_fe, (const unsigned char[]){0x06, 0x6D, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}); // Correct little-endian 486662
fe_sq(t0, u_in); // t0 = u^2
fe_mul(t1, t0, u_in); // t1 = u^3
fe_mul(t2, t0, A_fe); // t2 = A u^2
fe_add(rhs, t1, t2); // u^3 + A u^2
fe_add(rhs, rhs, u_in); // + u
// The exponentiation chain for (p+3)/8
fe_1(t1);
fe_sq(t0, t1);
fe_mul(t0, t0, t1);
fe_sq(candidate, t0);
fe_mul(candidate, candidate, t1);
fe_mul(candidate, candidate, rhs);
fe_pow22523(candidate, candidate);
fe_mul(candidate, candidate, t0);
fe_mul(candidate, candidate, rhs);
// Check c^2 == rhs or -rhs
fe_sq(c2, candidate);
fe_sub(check, c2, rhs);
if (fe_isnonzero(check)) {
fe_add(check, c2, rhs);
if (fe_isnonzero(check)) {
return -1; // Not a quadratic residue
}
fe_mul(candidate, candidate, fe_sqrtm1); // Adjust with sqrt(-1)
}
// Output v (principal root; flip to -v if needed for your map)
fe_copy(v_out, candidate);
return 0;
}
void mont_to_ed(fe x_out, fe y_out, const fe u, const fe v) {
fe inv_v, temp;
fe t1, t2, inv_t2;
fe one;
fe_1(one);
fe_invert(inv_v, v); // 1/v
fe_mul(temp, u, inv_v); // u/v
fe_mul(x_out, fe_sqrt_m486664, temp); // sqrt(-486664) * (u/v)
fe_sub(t1, u, one); // u - 1
fe_add(t2, u, one); // u + 1
fe_invert(inv_t2, t2);
fe_mul(y_out, t1, inv_t2); // (u-1)/(u+1)
}
void ed_to_mont(fe u_out, fe v_out, const fe x, const fe y) {
fe t1, t2, inv_t2;
fe one;
fe_1(one);
fe_add(t1, one, y); // 1 + y
fe_sub(t2, one, y); // 1 - y
fe_invert(inv_t2, t2);
fe_mul(u_out, t1, inv_t2); // (1+y)/(1-y)
fe inv_x;
fe_invert(inv_x, x); // 1/x
fe_mul(t1, u_out, inv_x); // u / x
fe_mul(v_out, fe_sqrt_m486664, t1); // sqrt(-486664) * (u/x)
}
// Usage: Add two Montgomery points
void add_mont_points(fe u3, fe v3, const fe u1, const fe v1, const fe u2, const fe v2) {
ge_p3 P_ed, Q_ed, sum_ed;
// Convert to Edwards
fe x1, y1, x2, y2;
mont_to_ed(x1, y1, u1, v1);
mont_to_ed(x2, y2, u2, v2);
// Load into ge_p3 (assume Z=1, T=x*y for affine)
fe_1(P_ed.Z); fe_mul(P_ed.T, x1, y1); fe_copy(P_ed.X, x1); fe_copy(P_ed.Y, y1);
fe_1(Q_ed.Z); fe_mul(Q_ed.T, x2, y2); fe_copy(Q_ed.X, x2); fe_copy(Q_ed.Y, y2);
// Add using ge_
ge_cached Q_cached;
ge_p3_to_cached(&Q_cached, &Q_ed);
ge_p1p1 sum_p1p1;
ge_add(&sum_p1p1, &P_ed, &Q_cached);
ge_p1p1_to_p3(&sum_ed, &sum_p1p1);
// Convert back (normalize to affine: divide by Z)
fe inv_z;
fe_invert(inv_z, sum_ed.Z);
fe x_out, y_out;
fe_mul(x_out, sum_ed.X, inv_z);
fe_mul(y_out, sum_ed.Y, inv_z);
ed_to_mont(u3, v3, x_out, y_out);
}
int ge_p3_is_point_at_infinity_vartime(const ge_p3 *p) {
// https://eprint.iacr.org/2008/522
// X == T == 0 and Y/Z == 1
@@ -4063,4 +4186,4 @@ void fe_dbl(fe h, const fe f)
// Reduce the output for safety to ensure the result can be used as input to
// fe_add or fe_sub without an extra call to fe_reduce
fe_reduce(h, h_res);
}
}
+10 -1
View File
@@ -176,6 +176,11 @@ int sc_isnonzero(const unsigned char *); /* Doesn't normalize */
void ge_p3_to_x25519(unsigned char *xbytes, const ge_p3 *h);
int edwards_bytes_to_x25519_vartime(unsigned char *xbytes, const unsigned char *s);
int fe_sqrt_mont(fe v_out, const fe u_in);
void mont_to_ed(fe x_out, fe y_out, const fe u, const fe v);
void ed_to_mont(fe u_out, fe v_out, const fe x, const fe y);
void add_mont_points(fe u3, fe v3, const fe u1, const fe v1, const fe u2, const fe v2);
// internal
uint64_t load_3(const unsigned char *in);
uint64_t load_4(const unsigned char *in);
@@ -192,10 +197,14 @@ void fe_sq(fe h, const fe f);
void fe_sub(fe h, const fe f, const fe g);
void fe_0(fe h);
void fe_1(fe h);
int fe_equal(const fe a, const fe b);
void ge_from_xy(ge_p3 *out, const fe x, const fe y);
int fe_isnonzero(const fe f);
int ge_p3_is_point_at_infinity_vartime(const ge_p3 *p);
void fe_ed_y_derivatives_to_wei_x(unsigned char *wei_x, const fe inv_one_minus_y, const fe one_plus_y);
void fe_reduce(fe reduced_f, const fe f);
void fe_dbl(fe h, const fe f);
void fe_dbl(fe h, const fe f);
+558 -1
View File
@@ -41,6 +41,7 @@
#include "common/varint.h"
#include "warnings.h"
#include "crypto.h"
#include "mx25519.h"
#include "hash.h"
#include "cryptonote_config.h"
@@ -90,6 +91,16 @@ namespace crypto {
return &reinterpret_cast<const unsigned char &>(scalar);
}
static const mx25519_impl* get_mx25519_impl()
{
static std::once_flag of;
static const mx25519_impl *impl;
std::call_once(of, [&](){ impl = mx25519_select_impl(MX25519_TYPE_AUTO); });
if (impl == nullptr)
throw std::runtime_error("failed to obtain a mx25519 implementation");
return impl;
}
boost::mutex &get_random_lock()
{
static boost::mutex random_lock;
@@ -504,6 +515,391 @@ namespace crypto {
memwipe(&k, sizeof(k));
}
void crypto_ops::generate_carrot_tx_proof(
const hash &prefix_hash,
const public_key &R, // X25519 u-coordinate
const public_key &A, // Ed25519
const boost::optional<public_key> &B, // Ed if present
const public_key &D, // X25519 u-coordinate
const secret_key &r,
const secret_key &a,
signature &sig)
{
// Check if we are sender or receiver
if (r != crypto::null_skey) {
// SENDER
generate_carrot_tx_proof_as_sender(prefix_hash, R, A, B, D, r, a, sig);
return;
}
// RECEIVER
// Load points (A and B and R) into ge_p3
ge_p3 A_p3;
ge_p3 B_p3;
ge_p3 R_p3;
if (ge_frombytes_vartime(&A_p3, &A) != 0)
throw std::runtime_error("recipient view pubkey is invalid");
if (B && ge_frombytes_vartime(&B_p3, &*B) != 0)
throw std::runtime_error("recipient spend pubkey is invalid");
#if !defined(NDEBUG)
{
// Debug check D == a*R
mx25519_pubkey D_x25519;
mx25519_scmul_key(get_mx25519_impl(),
&D_x25519,
reinterpret_cast<const mx25519_privkey*>(&a),
reinterpret_cast<const mx25519_pubkey*>(&R));
public_key dbg_D;
memcpy(&dbg_D, &D_x25519, sizeof(mx25519_pubkey));
assert(D == dbg_D);
}
#endif
//
// 1. Pick random nonce k
//
crypto::secret_key k;
random_scalar(k);
static const public_key zero = {{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
}};
s_comm_2 buf;
buf.msg = prefix_hash;
buf.D = D; // X25519 u-coord
buf.R = R; // X25519 u-coord
buf.A = A; // Ed25519
buf.B = B ? *B : zero;
cn_fast_hash(config::HASH_KEY_TXPROOF_V2,
sizeof(config::HASH_KEY_TXPROOF_V2)-1,
buf.sep);
//
// 2. Compute X = ConvertPointE(k*G or k*B)
//
ge_p3 kB_or_kG_p3;
if (B)
ge_scalarmult_p3(&kB_or_kG_p3, &k, &B_p3);
else
ge_scalarmult_base(&kB_or_kG_p3, &k);
mx25519_pubkey X_x25519;
ge_p3_to_x25519(X_x25519.data, &kB_or_kG_p3);
memcpy(&buf.X, &X_x25519, sizeof(mx25519_pubkey));
//
// 3. Compute Y = k*R
//
mx25519_pubkey Y;
mx25519_scmul_key(get_mx25519_impl(),
&Y,
reinterpret_cast<const mx25519_privkey*>(&k),
reinterpret_cast<const mx25519_pubkey*>(&R));
memcpy(&buf.Y, &Y, sizeof(mx25519_pubkey));
// ---------- Extract and lift R ----------
fe u_R;
fe_frombytes_vartime(u_R, (const unsigned char *)&R);
fe v_R_cand;
if (fe_sqrt_mont(v_R_cand, u_R) != 0)
throw std::runtime_error("R not on curve");
fe x1, y1, x2, y2, v_R_neg;
ge_p3 R_ed1, R_ed2;
// +v (principal)
mont_to_ed(x1, y1, u_R, v_R_cand);
ge_from_xy(&R_ed1, x1, y1);
// -v
fe_neg(v_R_neg, v_R_cand);
mont_to_ed(x2, y2, u_R, v_R_neg);
ge_from_xy(&R_ed2, x2, y2);
// Arbitrarily choose R_sign = true (principal v from fe_sqrt_mont)
bool R_sign = true;
ge_p3 R_ed_correct = R_ed1; // +v
// ---------- Extract and lift D (consistent with chosen R_sign) ----------
fe u_D;
fe_frombytes_vartime(u_D, (const unsigned char *)&D);
fe v_D_cand;
if (fe_sqrt_mont(v_D_cand, u_D) != 0)
throw std::runtime_error("D not on curve");
fe x3, y3, x4, y4, v_D_neg;
// Compute D_ed_true = a * R_ed_correct
ge_p3 D_ed_true;
ge_scalarmult_p3(&D_ed_true, &a, &R_ed_correct);
// Normalize to affine for matching
fe inv_z;
fe_invert(inv_z, D_ed_true.Z);
fe xd_true, yd_true;
fe_mul(xd_true, D_ed_true.X, inv_z);
fe_mul(yd_true, D_ed_true.Y, inv_z);
// +v for D
mont_to_ed(x3, y3, u_D, v_D_cand);
bool D_match1 = fe_equal(x3, xd_true) && fe_equal(y3, yd_true); // Affine match (mont_to_ed gives affine x,y)
// -v for D
fe_neg(v_D_neg, v_D_cand);
mont_to_ed(x4, y4, u_D, v_D_neg);
bool D_match2 = fe_equal(x4, xd_true) && fe_equal(y4, yd_true);
bool D_sign = false;
if (D_match1)
D_sign = true;
else if (D_match2)
D_sign = false;
else
throw std::runtime_error("D lift mismatch with computed D_ed_true");
// Pack signs (MSB is set to [1] for outbound, [0] for inbound
sig.sign_mask =
(R_sign ? 0x01 : 0x00) |
(D_sign ? 0x02 : 0x00);
struct {
s_comm_2 buf;
uint8_t sign_mask;
} challenge_hash;
challenge_hash.buf = buf;
challenge_hash.sign_mask = sig.sign_mask;
//
// 7. Compute challenge c = H(prefix_hash || … || sign_mask)
//
hash_to_scalar(&challenge_hash, sizeof(challenge_hash), sig.c);
//
// 8. Compute response z = k - c*a
//
sc_mulsub(&sig.r, &sig.c, &unwrap(a), &k);
memwipe(&k, sizeof(k));
#if !defined(NDEBUG)
bool ok = check_carrot_tx_proof(prefix_hash, R, A, B, D, sig);
assert(ok);
#endif
}
void crypto_ops::generate_carrot_tx_proof_as_sender(
const hash &prefix_hash,
const public_key &R, // X25519 u-coordinate
const public_key &A, // Ed25519
const boost::optional<public_key> &B, // Ed if present
const public_key &D, // X25519 u-coordinate
const secret_key &r,
const secret_key &a,
signature &sig)
{
// Load only Ed points (A and B) into ge_p3
ge_p3 A_p3;
ge_p3 B_p3;
if (ge_frombytes_vartime(&A_p3, &A) != 0)
throw std::runtime_error("recipient view pubkey is invalid");
if (B && ge_frombytes_vartime(&B_p3, &*B) != 0)
throw std::runtime_error("recipient spend pubkey is invalid");
#if !defined(NDEBUG)
{
assert(sc_check(&r) == 0);
// Debug check R == ConvertPointE(r*G or r*B)
public_key dbg_R;
ge_p3 dbg_R_p3;
if (B)
ge_scalarmult_p3(&dbg_R_p3, &r, &B_p3);
else
ge_scalarmult_base(&dbg_R_p3, &r);
mx25519_pubkey R_x25519;
ge_p3_to_x25519(R_x25519.data, &dbg_R_p3);
memcpy(&dbg_R, &R_x25519, sizeof(mx25519_pubkey));
assert(R == dbg_R);
// Debug check D == ConvertPointE(r*A)
public_key dbg_D;
ge_p3 dbg_D_p3;
ge_scalarmult_p3(&dbg_D_p3, &r, &A_p3);
mx25519_pubkey D_x25519;
ge_p3_to_x25519(D_x25519.data, &dbg_D_p3);
memcpy(&dbg_D, &D_x25519, sizeof(mx25519_pubkey));
assert(D == dbg_D);
}
#endif
//
// 1. Pick random nonce k
//
ec_scalar k;
random_scalar(k);
static const ec_point zero = {{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
}};
s_comm_2 buf;
buf.msg = prefix_hash;
buf.D = D; // X25519 u-coord
buf.R = R; // X25519 u-coord
buf.A = A; // Ed25519
buf.B = B ? *B : zero;
cn_fast_hash(config::HASH_KEY_TXPROOF_V2,
sizeof(config::HASH_KEY_TXPROOF_V2)-1,
buf.sep);
//
// 2. Compute X = ConvertPointE(k*G or k*B)
//
ge_p3 kB_or_kG_p3;
if (B)
ge_scalarmult_p3(&kB_or_kG_p3, &k, &B_p3);
else
ge_scalarmult_base(&kB_or_kG_p3, &k);
mx25519_pubkey X_x25519;
ge_p3_to_x25519(X_x25519.data, &kB_or_kG_p3);
memcpy(&buf.X, &X_x25519, sizeof(mx25519_pubkey));
//
// 3. Compute Y = ConvertPointE(k*A)
//
ge_p3 kA_p3;
ge_scalarmult_p3(&kA_p3, &k, &A_p3);
mx25519_pubkey Y_x25519;
ge_p3_to_x25519(Y_x25519.data, &kA_p3);
memcpy(&buf.Y, &Y_x25519, sizeof(mx25519_pubkey));
//
// 4. Compute true Ed points R_ed_true and D_ed_true
//
ge_p3 R_ed_true, D_ed_true;
if (B)
ge_scalarmult_p3(&R_ed_true, &r, &B_p3);
else
ge_scalarmult_base(&R_ed_true, &r);
ge_scalarmult_p3(&D_ed_true, &r, &A_p3);
//
// 5. Determine sign bits for R and D
//
// ---------- Extract and lift R ----------
fe u_R;
fe_frombytes_vartime(u_R, (const unsigned char *)&R);
fe v_R_cand;
if (fe_sqrt_mont(v_R_cand, u_R) != 0)
throw std::runtime_error("R not on curve");
fe x1, y1, x2, y2, v_R_neg, v_D_neg;
mont_to_ed(x1, y1, u_R, v_R_cand);
fe_neg(v_R_neg, v_R_cand);
mont_to_ed(x2, y2, u_R, v_R_neg);
// Compute affine Edwards coords of R_ed_true
fe inv_z, xr_true, yr_true;
fe_invert(inv_z, R_ed_true.Z);
fe_mul(xr_true, R_ed_true.X, inv_z);
fe_mul(yr_true, R_ed_true.Y, inv_z);
bool R_match1 = fe_equal(xr_true, x1) && fe_equal(yr_true, y1);
bool R_match2 = fe_equal(xr_true, x2) && fe_equal(yr_true, y2);
if (!R_match1 && !R_match2)
throw std::runtime_error("R mapping mismatch");
bool R_sign = R_match1;
// ---------- Extract and lift D ----------
fe u_D;
fe_frombytes_vartime(u_D, (const unsigned char *)&D);
fe v_D_cand;
if (fe_sqrt_mont(v_D_cand, u_D) != 0)
throw std::runtime_error("D not on curve");
mont_to_ed(x1, y1, u_D, v_D_cand);
fe_neg(v_D_neg, v_D_cand);
mont_to_ed(x2, y2, u_D, v_D_neg);
fe_invert(inv_z, D_ed_true.Z);
fe_mul(xr_true, D_ed_true.X, inv_z);
fe_mul(yr_true, D_ed_true.Y, inv_z);
bool D_match1 = fe_equal(xr_true, x1) && fe_equal(yr_true, y1);
bool D_match2 = fe_equal(xr_true, x2) && fe_equal(yr_true, y2);
if (!D_match1 && !D_match2)
throw std::runtime_error("D mapping mismatch");
bool D_sign = D_match1;
//
// 6. Pack sign bits into signature, include in challenge hash
//
sig.sign_mask =
(R_sign ? 0x01 : 0x00) |
(D_sign ? 0x02 : 0x00) |
0x80;
struct {
s_comm_2 buf;
uint8_t sign_mask;
} challenge_hash;
challenge_hash.buf = buf;
challenge_hash.sign_mask = sig.sign_mask;
//
// 7. Compute challenge c = H(prefix_hash || … || sign_mask)
//
hash_to_scalar(&challenge_hash, sizeof(challenge_hash), sig.c);
//
// 8. Compute response z = k - c*r
//
sc_mulsub(&sig.r, &sig.c, &unwrap(r), &k);
memwipe(&k, sizeof(k));
#if !defined(NDEBUG)
bool ok = check_carrot_tx_proof(prefix_hash, R, A, B, D, sig);
assert(ok);
#endif
}
// Verify a proof: either v1 (version == 1) or v2 (version == 2)
bool crypto_ops::check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig, const int version) {
// sanity check
@@ -608,6 +1004,167 @@ namespace crypto {
return sc_isnonzero(&c2) == 0;
}
// R and D are provided in X25519 format (u-coordinate), A and B in Ed25519.
bool crypto_ops::check_carrot_tx_proof(
const hash &prefix_hash,
const public_key &R, // X25519 u
const public_key &A, // Ed25519 viewkey
const boost::optional<public_key> &B, // Ed25519 spendkey if any
const public_key &D, // X25519 u
const signature &sig)
{
ge_p3 A_p3, B_p3;
if (ge_frombytes_vartime(&A_p3, &A) != 0)
return false;
if (B && ge_frombytes_vartime(&B_p3, &*B) != 0)
return false;
if (sc_check(&sig.c) != 0 || sc_check(&sig.r) != 0)
return false;
// Extract sign bits and direction flag
const bool R_sign = (sig.sign_mask & 0x01) != 0;
const bool D_sign = (sig.sign_mask & 0x02) != 0;
const bool outbound = (sig.sign_mask & 0x80) != 0;
//
// 1. Reconstruct R_ed and D_ed from X25519 u-coords + sign bits
//
// ----- R -----
fe u_R, v_R_candidate, v_R;
fe_frombytes_vartime(u_R, (const unsigned char *)&R);
if (fe_sqrt_mont(v_R_candidate, u_R) != 0)
return false;
if (R_sign) fe_copy(v_R, v_R_candidate);
else fe_neg(v_R, v_R_candidate);
fe x_R, y_R;
mont_to_ed(x_R, y_R, u_R, v_R);
ge_p3 R_ed;
ge_from_xy(&R_ed, x_R, y_R); // Z=1, T=X*Y
// ----- D -----
fe u_D, v_D_candidate, v_D;
fe_frombytes_vartime(u_D, (const unsigned char *)&D);
if (fe_sqrt_mont(v_D_candidate, u_D) != 0)
return false;
if (D_sign) fe_copy(v_D, v_D_candidate);
else fe_neg(v_D, v_D_candidate);
fe x_D, y_D;
mont_to_ed(x_D, y_D, u_D, v_D);
ge_p3 D_ed;
ge_from_xy(&D_ed, x_D, y_D);
//
// 2. Compute X'
// If inbound proof, X`= z*G + c*A (or z*B + c*A)
// If outbound proof, X`= z*G + c*R_ed (or z*B + c*R_ed)
//
ge_p3 c_p3;
if (outbound)
ge_scalarmult_p3(&c_p3, &sig.c, &R_ed);
else
ge_scalarmult_p3(&c_p3, &sig.c, &A_p3);
ge_p1p1 X_p1p1;
if (B)
{
// Subaddress: X' = c*A + z*B
ge_p3 rB_p3;
ge_scalarmult_p3(&rB_p3, &sig.r, &B_p3);
ge_cached rB_cached;
ge_p3_to_cached(&rB_cached, &rB_p3);
ge_add(&X_p1p1, &c_p3, &rB_cached);
}
else
{
// Main address: X' = c*R_ed + z*G
ge_p3 rG_p3;
ge_scalarmult_base(&rG_p3, &sig.r);
ge_cached rG_cached;
ge_p3_to_cached(&rG_cached, &rG_p3);
ge_add(&X_p1p1, &c_p3, &rG_cached);
}
ge_p3 X_ed_p3;
ge_p1p1_to_p3(&X_ed_p3, &X_p1p1);
mx25519_pubkey X_x25519;
ge_p3_to_x25519(X_x25519.data, &X_ed_p3);
//
// 3. Compute Y'
// If inbound, Y' = c*D_ed + z*R
// If outbound, Y' = c*D_ed + z*A
//
ge_p3 cD_p3;
ge_scalarmult_p3(&cD_p3, &sig.c, &D_ed);
ge_p3 z_p3;
if (outbound)
ge_scalarmult_p3(&z_p3, &sig.r, &A_p3);
else
ge_scalarmult_p3(&z_p3, &sig.r, &R_ed);
ge_cached z_cached;
ge_p3_to_cached(&z_cached, &z_p3);
ge_p1p1 Y_p1p1;
ge_add(&Y_p1p1, &cD_p3, &z_cached);
ge_p3 Y_ed_p3;
ge_p1p1_to_p3(&Y_ed_p3, &Y_p1p1);
mx25519_pubkey Y_x25519;
ge_p3_to_x25519(Y_x25519.data, &Y_ed_p3);
//
// 4. Rebuild the hash transcript exactly as the prover did
//
static const ec_point zero = {{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
}};
s_comm_2 buf;
buf.msg = prefix_hash;
buf.D = D; // X25519 (same bytes as prover)
buf.R = R; // X25519
buf.A = A; // Ed25519
buf.B = B ? *B : zero;
cn_fast_hash(config::HASH_KEY_TXPROOF_V2,
sizeof(config::HASH_KEY_TXPROOF_V2)-1,
buf.sep);
memcpy(&buf.X, &X_x25519, sizeof(mx25519_pubkey));
memcpy(&buf.Y, &Y_x25519, sizeof(mx25519_pubkey));
struct {
s_comm_2 buf;
uint8_t sign_mask;
} challenge_hash;
challenge_hash.buf = buf;
challenge_hash.sign_mask = sig.sign_mask;
//
// 5. Recompute challenge and compare with sig.c
//
ec_scalar c2;
hash_to_scalar(&challenge_hash, sizeof(challenge_hash), c2);
sc_sub(&c2, &c2, &sig.c);
return sc_isnonzero(&c2) == 0;
}
static void hash_to_ec(const public_key &key, ge_p3 &res) {
hash h;
ge_p2 point;
@@ -796,4 +1353,4 @@ POP_WARNINGS
ki.data[31] ^= 0x80;
}
}
}
}
+28 -3
View File
@@ -85,6 +85,7 @@ namespace crypto {
POD_CLASS signature {
ec_scalar c, r;
uint8_t sign_mask;
friend class crypto_ops;
};
@@ -99,7 +100,7 @@ namespace crypto {
static_assert(sizeof(ec_point) == 32 && sizeof(ec_scalar) == 32 &&
sizeof(public_key) == 32 && sizeof(public_key_memsafe) == 32 && sizeof(secret_key) == 32 &&
sizeof(key_derivation) == 32 && sizeof(key_image) == 32 && sizeof(key_image_y) == 32 &&
sizeof(signature) == 64 && sizeof(view_tag) == 1, "Invalid structure size");
sizeof(signature) == 65 && sizeof(view_tag) == 1, "Invalid structure size");
class crypto_ops {
crypto_ops();
@@ -131,8 +132,14 @@ namespace crypto {
friend void generate_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
static void generate_tx_proof_v1(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
friend void generate_tx_proof_v1(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, signature &);
static void generate_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, const secret_key &, signature &);
friend void generate_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, const secret_key &, signature &);
static void generate_carrot_tx_proof_as_sender(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, const secret_key &, signature &);
friend void generate_carrot_tx_proof_as_sender(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const secret_key &, const secret_key &, signature &);
static bool check_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &, const int);
friend bool check_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &, const int);
static bool check_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &);
friend bool check_carrot_tx_proof(const hash &, const public_key &, const public_key &, const boost::optional<public_key> &, const public_key &, const signature &);
static void derive_key_image_generator(const public_key &, ec_point &);
friend void derive_key_image_generator(const public_key &, ec_point &);
static void generate_key_image(const public_key &, const secret_key &, key_image &);
@@ -260,10 +267,28 @@ namespace crypto {
inline void generate_tx_proof_v1(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, signature &sig) {
crypto_ops::generate_tx_proof_v1(prefix_hash, R, A, B, D, r, sig);
}
/* Generation of a carrot tx proof; for carrot transactions, D is in X25519 domain (D = r*ConvertPointE(A))
* instead of Ed25519 domain (D = r*A). This version applies ConvertPointE transformation.
*/
inline void generate_carrot_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, const secret_key &a, signature &sig) {
crypto_ops::generate_carrot_tx_proof(prefix_hash, R, A, B, D, r, a, sig);
}
inline void generate_carrot_tx_proof_as_sender(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const secret_key &r, const secret_key &a, signature &sig) {
crypto_ops::generate_carrot_tx_proof_as_sender(prefix_hash, R, A, B, D, r, a, sig);
}
inline bool check_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig, const int version) {
return crypto_ops::check_tx_proof(prefix_hash, R, A, B, D, sig, version);
}
/* Verification of a carrot tx proof; R and D should be in Ed25519 domain for verification,
*/
inline bool check_carrot_tx_proof(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig) {
return crypto_ops::check_carrot_tx_proof(prefix_hash, R, A, B, D, sig);
}
/*
inline bool check_carrot_tx_proof_as_sender(const hash &prefix_hash, const public_key &R, const public_key &A, const boost::optional<public_key> &B, const public_key &D, const signature &sig) {
return crypto_ops::check_carrot_tx_proof_as_sender(prefix_hash, R, A, B, D, sig);
}
*/
inline void derive_key_image_generator(const public_key &pub, ec_point &ki_gen) {
crypto_ops::derive_key_image_generator(pub, ki_gen);
}
@@ -378,4 +403,4 @@ CRYPTO_MAKE_HASHABLE_CONSTANT_TIME(public_key_memsafe)
CRYPTO_MAKE_HASHABLE(key_image)
CRYPTO_MAKE_HASHABLE(key_image_y)
CRYPTO_MAKE_COMPARABLE(signature)
CRYPTO_MAKE_COMPARABLE(view_tag)
CRYPTO_MAKE_COMPARABLE(view_tag)
+26 -5
View File
@@ -89,12 +89,24 @@ DISABLE_VS_WARNINGS(4244 4345)
void account_keys::xor_with_key_stream(const crypto::chacha_key &key)
{
// encrypt a large enough byte stream with chacha20
epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * (2 + m_multisig_keys.size()));
epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * (8 + m_multisig_keys.size()));
const char *ptr = key_stream.data();
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
m_spend_secret_key.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
m_view_secret_key.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
s_master.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
k_prove_spend.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
s_view_balance.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
k_view_incoming.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
k_generate_image.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
s_generate_address.data[i] ^= *ptr++;
for (crypto::secret_key &k: m_multisig_keys)
{
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
@@ -116,11 +128,20 @@ DISABLE_VS_WARNINGS(4244 4345)
void account_keys::encrypt_viewkey(const crypto::chacha_key &key)
{
// encrypt a large enough byte stream with chacha20
epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * 2);
epee::wipeable_string key_stream = get_key_stream(key, m_encryption_iv, sizeof(crypto::secret_key) * 8);
const char *ptr = key_stream.data();
ptr += sizeof(crypto::secret_key);
ptr += sizeof(crypto::secret_key); // Skip m_spend_secret_key
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
m_view_secret_key.data[i] ^= *ptr++;
ptr += (2 * sizeof(crypto::secret_key)); // Skip s_master, k_prove_spend
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
s_view_balance.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
k_view_incoming.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
k_generate_image.data[i] ^= *ptr++;
for (size_t i = 0; i < sizeof(crypto::secret_key); ++i)
s_generate_address.data[i] ^= *ptr++;
}
//-----------------------------------------------------------------
void account_keys::decrypt_viewkey(const crypto::chacha_key &key)
@@ -283,9 +304,9 @@ DISABLE_VS_WARNINGS(4244 4345)
std::string account_base::get_carrot_public_address_str(network_type nettype) const
{
// Build the cryptonote::account_public_address
account_public_address addr{m_keys.m_carrot_main_address.m_spend_public_key, m_keys.m_carrot_main_address.m_view_public_key};
account_public_address addr{m_keys.m_carrot_main_address.m_spend_public_key, m_keys.m_carrot_main_address.m_view_public_key, true};
// change this code into base 58
return get_account_address_as_str(nettype, false, addr, true);
return get_account_address_as_str(nettype, false, addr);
}
//-----------------------------------------------------------------
std::string account_base::get_public_integrated_address_str(const crypto::hash8 &payment_id, network_type nettype) const
+5
View File
@@ -75,6 +75,11 @@ namespace cryptonote
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(m_multisig_keys)
const crypto::chacha_iv default_iv{{0, 0, 0, 0, 0, 0, 0, 0}};
KV_SERIALIZE_VAL_POD_AS_BLOB_OPT(m_encryption_iv, default_iv)
if (m_account_address.m_spend_public_key == crypto::null_pkey) {
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(s_view_balance)
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(k_view_incoming)
KV_SERIALIZE(m_carrot_account_address)
}
END_KV_SERIALIZE_MAP()
void encrypt(const crypto::chacha_key &key);
+209 -5
View File
@@ -44,6 +44,7 @@
#include "serialization/debug_archive.h"
#include "serialization/crypto.h"
#include "serialization/keyvalue_serialization.h" // eepe named serialization
#include "serialization/pair.h"
#include "serialization/string.h"
#include "carrot_core/core_types.h"
#include "carrot_impl/carrot_chain_serialization.h"
@@ -193,8 +194,6 @@ namespace cryptonote
VARINT_FIELD(amount)
FIELD(target)
END_SERIALIZE()
};
class protocol_tx_data_t {
@@ -214,6 +213,99 @@ namespace cryptonote
END_SERIALIZE()
};
struct erc_token_t
{
uint8_t version;
std::string contract_address;
std::string lockbox_address;
std::string ticker;
uint64_t erc20_asset_id; // NOTE: this is NOT the Salvium `asset_type_id`!!!
BEGIN_SERIALIZE_OBJECT()
VARINT_FIELD(version)
FIELD(contract_address)
FIELD(lockbox_address)
FIELD(ticker)
VARINT_FIELD(erc20_asset_id)
END_SERIALIZE()
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(contract_address)
KV_SERIALIZE(lockbox_address)
KV_SERIALIZE(ticker)
KV_SERIALIZE(erc20_asset_id)
END_KV_SERIALIZE_MAP()
};
struct sal_token_t
{
uint8_t version;
uint64_t supply;
uint64_t size;
std::string name;
std::string url;
crypto::hash signature;
BEGIN_SERIALIZE_OBJECT()
VARINT_FIELD(version)
VARINT_FIELD(supply)
VARINT_FIELD(size)
FIELD(name)
FIELD(url)
FIELD(signature)
END_SERIALIZE()
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(supply)
KV_SERIALIZE(size)
KV_SERIALIZE(name)
KV_SERIALIZE(url)
END_KV_SERIALIZE_MAP()
};
#define TOKEN_TYPE_UNSET 0
#define TOKEN_TYPE_ERC20 1
#define TOKEN_TYPE_SAL 2
typedef boost::variant<erc_token_t, sal_token_t> token_v;
struct token_metadata_t
{
uint8_t version;
std::string asset_type;
token_v token;
BEGIN_SERIALIZE_OBJECT()
VARINT_FIELD(version)
FIELD(asset_type)
FIELD(token)
END_SERIALIZE()
};
class layer2_rollup_tx_t {
public:
crypto::hash tx_prefix_hash;
crypto::key_image first_key_image;
uint64_t tx_fee;
BEGIN_SERIALIZE_OBJECT()
FIELD(tx_prefix_hash)
FIELD(first_key_image)
VARINT_FIELD(tx_fee)
END_SERIALIZE()
};
class layer2_rollup_data_t {
public:
uint8_t version;
std::vector<layer2_rollup_tx_t> txs;
BEGIN_SERIALIZE_OBJECT()
VARINT_FIELD(version)
FIELD(txs)
END_SERIALIZE()
};
class transaction_prefix
{
@@ -246,6 +338,10 @@ namespace cryptonote
protocol_tx_data_t protocol_tx_data;
carrot::rollup_binding_tag_t rollup_binding_tag;
token_metadata_t token_metadata;
layer2_rollup_data_t layer2_rollup_data;
BEGIN_SERIALIZE()
VARINT_FIELD(version)
if(version == 0 || CURRENT_TRANSACTION_VERSION < version) return false;
@@ -263,9 +359,8 @@ namespace cryptonote
FIELD(return_address_list)
FIELD(return_address_change_mask)
} else {
if (type == cryptonote::transaction_type::STAKE &&
version >= TRANSACTION_VERSION_CARROT)
{
if ((type == cryptonote::transaction_type::STAKE || type == cryptonote::transaction_type::CREATE_TOKEN) &&
(version >= TRANSACTION_VERSION_CARROT)) {
FIELD(protocol_tx_data)
} else {
FIELD(return_address)
@@ -277,6 +372,17 @@ namespace cryptonote
VARINT_FIELD(amount_slippage_limit)
}
}
if (version < TRANSACTION_VERSION_ENABLE_TOKENS) {
return true;
}
if (type == cryptonote::transaction_type::CREATE_TOKEN) {
FIELD(token_metadata)
} else if (type == cryptonote::transaction_type::TRANSFER) {
FIELD(rollup_binding_tag)
} else if (type == cryptonote::transaction_type::ROLLUP) {
FIELD(rollup_binding_tag)
FIELD(layer2_rollup_data)
}
END_SERIALIZE()
public:
@@ -297,10 +403,14 @@ namespace cryptonote
protocol_tx_data.return_pubkey = crypto::null_pkey;
protocol_tx_data.return_view_tag = {};
protocol_tx_data.return_anchor_enc = {};
token_metadata.version = 0;
source_asset_type.clear();
destination_asset_type.clear();
amount_burnt = 0;
amount_slippage_limit = 0;
rollup_binding_tag = {0};
token_metadata = {};
layer2_rollup_data = {};
}
};
@@ -564,6 +674,91 @@ namespace cryptonote
END_SERIALIZE()
};
struct token_block_info {
uint64_t block_height;
std::map<uint32_t, uint64_t> token_info; // Replaces the vector
token_block_info() = default; // default ctor
explicit token_block_info(const uint64_t h)
: block_height(h) {}
void clear() {
block_height = 0;
token_info.clear();
}
std::vector<uint8_t> serialize() const noexcept {
constexpr uint8_t version = 1; // Bump if format changes
uint32_t sz = static_cast<uint32_t>(token_info.size());
size_t total_size = sizeof(uint8_t) + sizeof(uint64_t) + sizeof(uint32_t) + (sz * (sizeof(uint32_t) + sizeof(uint64_t)));
std::vector<uint8_t> buf(total_size);
uint8_t* ptr = buf.data();
std::memcpy(ptr, &version, sizeof(uint8_t));
ptr += sizeof(uint8_t);
std::memcpy(ptr, &block_height, sizeof(uint64_t));
ptr += sizeof(uint64_t);
std::memcpy(ptr, &sz, sizeof(uint32_t));
ptr += sizeof(uint32_t);
for (const auto& p : token_info) { // Iterates in sorted order
std::memcpy(ptr, &p.first, sizeof(uint32_t));
ptr += sizeof(uint32_t);
std::memcpy(ptr, &p.second, sizeof(uint64_t));
ptr += sizeof(uint64_t);
}
return buf;
}
void deserialize(const uint8_t* data, size_t len) {
if (len < sizeof(uint32_t)) {
throw std::runtime_error("Invalid serialized data");
}
uint8_t version;
std::memcpy(&version, data, sizeof(uint8_t));
data += sizeof(uint8_t);
len -= sizeof(uint8_t);
if (version == 0) { // Legacy: No height/version (your current format)
// Handle old blobs: Caller must set block_height externally
block_height = 0; // Or throw if height is mandatory
} else if (version == 1) {
if (len < sizeof(uint64_t) + sizeof(uint32_t)) {
throw std::runtime_error("Invalid serialized data");
}
std::memcpy(&block_height, data, sizeof(uint64_t));
data += sizeof(uint64_t);
len -= sizeof(uint64_t);
} else {
throw std::runtime_error("Unsupported version");
}
uint32_t sz;
std::memcpy(&sz, data, sizeof(uint32_t));
data += sizeof(uint32_t);
len -= sizeof(uint32_t);
if (len != sz * (sizeof(uint32_t) + sizeof(uint64_t))) {
throw std::runtime_error("Invalid serialized data length");
}
token_info.clear();
for (uint32_t i = 0; i < sz; ++i) {
uint32_t k;
uint64_t v;
std::memcpy(&k, data, sizeof(uint32_t));
data += sizeof(uint32_t);
std::memcpy(&v, data, sizeof(uint64_t));
data += sizeof(uint64_t);
token_info[k] = v; // Map handles sorting/uniquness
}
}
};
struct yield_block_info {
uint64_t block_height;
uint64_t slippage_total_this_block;
@@ -784,6 +979,9 @@ 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::token_metadata_t, 0x0);
VARIANT_TAG(binary_archive, cryptonote::erc_token_t, 0x0);
VARIANT_TAG(binary_archive, cryptonote::sal_token_t, 0x1);
VARIANT_TAG(binary_archive, cryptonote::transaction, 0xcc);
VARIANT_TAG(binary_archive, cryptonote::block, 0xbb);
@@ -797,6 +995,9 @@ 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::token_metadata_t, "token_metadata");
VARIANT_TAG(json_archive, cryptonote::erc_token_t, "erc_token");
VARIANT_TAG(json_archive, cryptonote::sal_token_t, "sal_token");
VARIANT_TAG(json_archive, cryptonote::transaction, "tx");
VARIANT_TAG(json_archive, cryptonote::block, "block");
@@ -810,5 +1011,8 @@ 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::token_metadata_t, "token_metadata");
VARIANT_TAG(debug_archive, cryptonote::erc_token_t, "erc_token");
VARIANT_TAG(debug_archive, cryptonote::sal_token_t, "sal_token");
VARIANT_TAG(debug_archive, cryptonote::transaction, "tx");
VARIANT_TAG(debug_archive, cryptonote::block, "block");
@@ -155,10 +155,9 @@ namespace cryptonote {
network_type nettype
, bool subaddress
, account_public_address const & adr
, bool is_carrot
)
{
uint64_t address_prefix = is_carrot
uint64_t address_prefix = adr.m_is_carrot
? (subaddress ? get_config(nettype).CARROT_PUBLIC_SUBADDRESS_BASE58_PREFIX : get_config(nettype).CARROT_PUBLIC_ADDRESS_BASE58_PREFIX)
: (subaddress ? get_config(nettype).CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX : get_config(nettype).CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX);
@@ -169,10 +168,9 @@ namespace cryptonote {
network_type nettype
, account_public_address const & adr
, crypto::hash8 const & payment_id
, bool is_carrot
)
{
uint64_t integrated_address_prefix = is_carrot ? get_config(nettype).CARROT_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : get_config(nettype).CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
uint64_t integrated_address_prefix = adr.m_is_carrot ? get_config(nettype).CARROT_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX : get_config(nettype).CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX;
integrated_address iadr = {
adr, payment_id
@@ -88,14 +88,12 @@ namespace cryptonote {
network_type nettype
, bool subaddress
, const account_public_address& adr
, bool is_carrot = false
);
std::string get_account_integrated_address_as_str(
network_type nettype
, const account_public_address& adr
, const crypto::hash8& payment_id
, bool is_carrot = false
);
bool get_account_address_from_str(
@@ -45,6 +45,7 @@
#include "crypto/crypto.h"
#include "ringct/rctTypes.h"
#include "ringct/rctOps.h"
#include "serialization/containers.h"
namespace boost
{
@@ -181,6 +182,50 @@ namespace boost
a & x.return_anchor_enc;
}
template <class Archive>
inline void serialize(Archive &a, cryptonote::erc_token_t &x, const boost::serialization::version_type ver)
{
a & x.version;
a & x.contract_address;
a & x.lockbox_address;
a & x.ticker;
a & x.erc20_asset_id;
}
template <class Archive>
inline void serialize(Archive &a, cryptonote::sal_token_t &x, const boost::serialization::version_type ver)
{
a & x.version;
a & x.supply;
a & x.size;
a & x.name;
a & x.url;
a & x.signature;
}
template <class Archive>
inline void serialize(Archive &a, cryptonote::token_metadata_t &x, const boost::serialization::version_type ver)
{
a & x.version;
a & x.asset_type;
a & x.token;
}
template <class Archive>
inline void serialize(Archive &a, cryptonote::layer2_rollup_tx_t &x, const boost::serialization::version_type ver)
{
a & x.tx_prefix_hash;
a & x.first_key_image;
a & x.tx_fee;
}
template <class Archive>
inline void serialize(Archive &a, cryptonote::layer2_rollup_data_t &x, const boost::serialization::version_type ver)
{
a & x.version;
a & x.txs;
}
template <class Archive>
inline void serialize(Archive &a, cryptonote::transaction_prefix &x, const boost::serialization::version_type ver)
{
@@ -197,9 +242,8 @@ namespace boost
a & x.return_address_list;
a & x.return_address_change_mask;
} else {
if (x.type == cryptonote::transaction_type::STAKE &&
x.version >= TRANSACTION_VERSION_CARROT)
{
if ((x.type == cryptonote::transaction_type::STAKE || x.type == cryptonote::transaction_type::CREATE_TOKEN) &&
(x.version >= TRANSACTION_VERSION_CARROT)) {
a & x.protocol_tx_data;
} else {
a & x.return_address;
@@ -211,6 +255,16 @@ namespace boost
a & x.amount_slippage_limit;
}
}
if (x.version >= TRANSACTION_VERSION_ENABLE_TOKENS) {
if (x.type == cryptonote::transaction_type::CREATE_TOKEN) {
a & x.token_metadata;
} else if (x.type == cryptonote::transaction_type::TRANSFER) {
a & x.rollup_binding_tag;
} else if (x.type == cryptonote::transaction_type::ROLLUP) {
a & x.rollup_binding_tag;
a & x.layer2_rollup_data;
}
}
}
template <class Archive>
@@ -229,9 +283,8 @@ namespace boost
a & x.return_address_list;
a & x.return_address_change_mask;
} else {
if (x.type == cryptonote::transaction_type::STAKE &&
x.version >= TRANSACTION_VERSION_CARROT)
{
if ((x.type == cryptonote::transaction_type::STAKE || x.type == cryptonote::transaction_type::CREATE_TOKEN) &&
(x.version >= TRANSACTION_VERSION_CARROT)) {
a & x.protocol_tx_data;
} else {
a & x.return_address;
@@ -242,6 +295,16 @@ namespace boost
a & x.destination_asset_type;
a & x.amount_slippage_limit;
}
if (x.version >= TRANSACTION_VERSION_ENABLE_TOKENS) {
if (x.type == cryptonote::transaction_type::CREATE_TOKEN) {
a & x.token_metadata;
} else if (x.type == cryptonote::transaction_type::TRANSFER) {
a & x.rollup_binding_tag;
} else if (x.type == cryptonote::transaction_type::ROLLUP) {
a & x.rollup_binding_tag;
a & x.layer2_rollup_data;
}
}
}
if (x.version == 1)
{
+185 -28
View File
@@ -31,6 +31,7 @@
#include <atomic>
#include <csignal>
#include <map>
#include <boost/algorithm/string.hpp>
#include "wipeable_string.h"
#include "string_tools.h"
@@ -43,6 +44,7 @@
#include "crypto/hash.h"
#include "ringct/rctSigs.h"
#include "oracle/asset_types.h"
#include "common/debugging.h"
using namespace epee;
@@ -1114,7 +1116,37 @@ namespace cryptonote
default:
break;
}
if (asset_type_id)
{
// Check to see if 1st byte is permitted
std::string s, s_prefix;
switch (asset_type_id >> 24) {
case 0x01:
// We have a user-generated token
s_prefix = "sal";
break;
case 0x02:
s_prefix = "erc";
break;
default:
ASSERT_MES_AND_THROW("Invalid asset_type_id: " << asset_type_id);
}
// Break up the remaining 3 bytes into 4 chunks of base36/64
static const char alphabet[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
uint32_t asset_type_id_temp = asset_type_id & 0x00FFFFFF;
for (int i=0; i<4; ++i) {
uint8_t val = asset_type_id_temp & 0x0000003F;
if (val >= 36)
ASSERT_MES_AND_THROW("Invalid asset_type_id: " << asset_type_id);
s.push_back(alphabet[val]);
asset_type_id_temp >>= 6;
}
std::reverse(s.begin(), s.end());
return s_prefix + s;
}
// Should probably throw() here
ASSERT_MES_AND_THROW("Invalid asset_type_id: " << asset_type_id);
return "";
}
//---------------------------------------------------------------
@@ -1129,11 +1161,162 @@ namespace cryptonote
} else if (asset_type == "") {
return 0x00000000;
} else {
// Should probably throw() here
return static_cast<uint32_t>(-1);
if (asset_type.length() != 7) {
LOG_ERROR("Custom asset type '" << asset_type << "' has invalid length.");
return 0x00000000;
}
static const std::string alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// Check the 4-char type
std::string s_type = asset_type.substr(3);
std::transform(s_type.begin(), s_type.end(), s_type.begin(),
[](unsigned char c){ return std::toupper(c); });
uint32_t asset_id = 0x00000000;
for (int i=0; i<s_type.length(); ++i) {
uint8_t idx = alphabet.find(s_type.at(i));
if (idx == std::string::npos || idx >= 36) {
LOG_ERROR("Custom asset type contains invalid char.");
return 0x00000000;
}
asset_id = (asset_id << 6) | (idx & 0x3F);
}
// Check the 3-char prefix
std::string s_prefix = asset_type.substr(0,3);
std::transform(s_prefix.begin(), s_prefix.end(), s_prefix.begin(),
[](unsigned char c){ return std::tolower(c); });
if (s_prefix == "sal") {
asset_id |= 0x01000000;
} else if (s_prefix == "erc") {
asset_id |= 0x02000000;
} else {
LOG_ERROR("Custom asset type has invalid prefix.");
return 0x00000000;
}
return asset_id;
}
}
//---------------------------------------------------------------
bool is_asset_type_token(const std::string& asset_type)
{
// SAL, SAL1, BURN are base asset types
// erc prefix is not included here
if (asset_type.length() >= 3) {
std::string prefix = asset_type.substr(0, 3);
return (prefix == "sal");
}
return false;
}
//---------------------------------------------------------------
bool is_valid_asset_type(const std::string& asset_type) {
// This method does NOT throw()
try {
uint32_t asset_id = asset_id_from_type(asset_type);
std::string asset_type_check = asset_type_from_id(asset_id);
return (asset_type_check == asset_type);
}
catch (std::exception e) {
return false;
}
}
//---------------------------------------------------------------
bool is_valid_custom_asset_type(const std::string& asset_type)
{
if (!is_valid_asset_type(asset_type))
return false;
// double check with reserved IDs
uint32_t id = asset_id_from_type(asset_type);
if (id == 0x53414C00 || id == 0x53414C31 || id == 0x4255524E || id == 0x00000000) {
return false;
}
return true;
}
//---------------------------------------------------------------
uint64_t get_token_creation_price(const std::string& ticker)
{
static const std::map<std::string, uint64_t> premium_tickers = {
// add more
{"USDT", 10000 * COIN},
{"USDC", 10000 * COIN},
{"WBTC", 10000 * COIN},
{"DOGE", 10000 * COIN},
{"SHIB", 10000 * COIN},
{"AVAX", 10000 * COIN},
{"ATOM", 10000 * COIN},
{"NEAR", 10000 * COIN},
{"TRON", 10000 * COIN},
{"HBAR", 10000 * COIN},
{"AAVE", 10000 * COIN},
{"FLOW", 10000 * COIN},
{"EGLD", 10000 * COIN},
{"KLAY", 10000 * COIN},
{"LUNA", 10000 * COIN},
{"DASH", 10000 * COIN},
{"NANO", 10000 * COIN},
{"CORE", 10000 * COIN},
{"BEAM", 10000 * COIN},
{"DYDX", 10000 * COIN},
{"COMP", 10000 * COIN},
{"SAND", 10000 * COIN},
{"MANA", 10000 * COIN},
{"RUNE", 10000 * COIN},
{"PYTH", 10000 * COIN},
{"ARKM", 10000 * COIN},
{"BLUR", 10000 * COIN},
{"STRK", 10000 * COIN},
{"PEPE", 10000 * COIN},
{"BONK", 10000 * COIN},
{"VIUM", 10000 * COIN},
{"GOLD", 10000 * COIN},
{"SILV", 10000 * COIN},
{"CASH", 10000 * COIN},
{"EURO", 10000 * COIN},
{"PESO", 10000 * COIN},
{"BOND", 10000 * COIN},
{"FUND", 10000 * COIN},
{"BANK", 10000 * COIN},
{"SWAP", 10000 * COIN},
{"LEND", 10000 * COIN},
{"LOAN", 10000 * COIN},
{"NOTE", 10000 * COIN},
{"HOLD", 10000 * COIN},
{"BULL", 10000 * COIN},
{"BEAR", 10000 * COIN},
{"TECH", 10000 * COIN},
{"DATA", 10000 * COIN},
{"HASH", 10000 * COIN},
{"NODE", 10000 * COIN},
{"BYTE", 10000 * COIN},
{"GRID", 10000 * COIN},
{"CODE", 10000 * COIN},
{"META", 10000 * COIN},
{"WEB3", 10000 * COIN},
{"NFTS", 10000 * COIN},
{"DEFI", 10000 * COIN},
{"LAND", 10000 * COIN},
{"REAL", 10000 * COIN},
{"RENT", 10000 * COIN},
{"FARM", 10000 * COIN},
{"OILX", 10000 * COIN},
{"ENRG", 10000 * COIN},
{"FUEL", 10000 * COIN},
{"VOTE", 10000 * COIN},
{"PASS", 10000 * COIN},
{"LOCK", 10000 * COIN},
};
// is it a premium ticker
auto it = premium_tickers.find(ticker);
if (it != premium_tickers.end()) {
return it->second;
}
// not premium
return 1000 * COIN;
}
//---------------------------------------------------------------
/**
* The various scenarios that are permitted for Salvium are more extensive than
* they are for Zepyhr / Havan. Specifically, we permit:
@@ -1406,32 +1589,6 @@ namespace cryptonote
<< o.target.type().name() << " and " << tx.vout[0].target.type().name() << ", "
<< "expected matching variant types in transaction");
}
// Verify the asset type
std::string asset_type;
CHECK_AND_ASSERT_MES(cryptonote::get_output_asset_type(o, asset_type), false, "failed to get asset type");
if (hf_version < HF_VERSION_SALVIUM_ONE_PROOFS) {
// Prior to the first audit, ONLY SAL was supported
CHECK_AND_ASSERT_MES(asset_type == "SAL", false, "wrong output asset type:" << asset_type);
} else {
if (tx.type == cryptonote::transaction_type::AUDIT) {
// HERE BE DRAGONS!!!
// SRCG: This will NOT always be the case - when we add an audit for SALx it'll need to support that as well
// The CHANGE for an AUDIT TX must be SAL (and 0 value, and unspendable, and to the origin wallet, and ...)
CHECK_AND_ASSERT_MES(asset_type == "SAL", false, "wrong output asset type:" << asset_type);
// LAND AHOY!!!
} else if (tx.type == cryptonote::transaction_type::PROTOCOL) {
if (hf_version < HF_VERSION_AUDIT1_PAUSE) {
// PROTOCOL TXs are responsible for paying out SAL and SAL1 during the first AUDIT
CHECK_AND_ASSERT_MES(asset_type == "SAL1" || asset_type == "SAL", false, "wrong output asset type:" << asset_type);
} else {
CHECK_AND_ASSERT_MES(asset_type == "SAL1", false, "wrong output asset type:" << asset_type);
}
} else {
// All other TX types must only spend + create SAL1 (MINER, TRANSFER)
CHECK_AND_ASSERT_MES(asset_type == "SAL1", false, "wrong output asset type:" << asset_type);
}
}
}
return true;
}
@@ -135,6 +135,10 @@ namespace cryptonote
uint64_t get_outs_money_amount(const transaction& tx);
std::string asset_type_from_id(const uint32_t asset_type_id);
uint32_t asset_id_from_type(const std::string asset_type);
bool is_valid_custom_asset_type(const std::string& asset_type);
bool is_valid_asset_type(const std::string& asset_type);
bool is_asset_type_token(const std::string& asset_type);
uint64_t get_token_creation_price(const std::string& ticker);
bool get_tx_asset_types(const transaction& tx, const crypto::hash &txid, std::string& source, std::string& destination, const bool is_miner_tx);
bool get_output_public_key(const cryptonote::tx_out& out, crypto::public_key& output_public_key);
boost::optional<crypto::view_tag> get_output_view_tag(const cryptonote::tx_out& out);
+12 -1
View File
@@ -39,6 +39,7 @@
#define TX_EXTRA_NONCE 0x02
#define TX_EXTRA_MERGE_MINING_TAG 0x03
#define TX_EXTRA_TAG_ADDITIONAL_PUBKEYS 0x04
#define TX_EXTRA_TAG_TOKEN 0x80
#define TX_EXTRA_MYSTERIOUS_MINERGATE_TAG 0xDE
#define TX_EXTRA_NONCE_PAYMENT_ID 0x00
@@ -167,6 +168,15 @@ namespace cryptonote
END_SERIALIZE()
};
struct tx_extra_token
{
cryptonote::token_metadata_t token;
BEGIN_SERIALIZE()
FIELD(token)
END_SERIALIZE()
};
struct tx_extra_mysterious_minergate
{
std::string data;
@@ -180,7 +190,7 @@ namespace cryptonote
// varint tag;
// varint size;
// varint data[];
typedef boost::variant<tx_extra_padding, tx_extra_pub_key, tx_extra_nonce, tx_extra_merge_mining_tag, tx_extra_additional_pub_keys, tx_extra_mysterious_minergate> tx_extra_field;
typedef boost::variant<tx_extra_padding, tx_extra_pub_key, tx_extra_nonce, tx_extra_merge_mining_tag, tx_extra_additional_pub_keys, tx_extra_mysterious_minergate, tx_extra_token> tx_extra_field;
}
VARIANT_TAG(binary_archive, cryptonote::tx_extra_padding, TX_EXTRA_TAG_PADDING);
@@ -189,3 +199,4 @@ VARIANT_TAG(binary_archive, cryptonote::tx_extra_nonce, TX_EXTRA_NONCE);
VARIANT_TAG(binary_archive, cryptonote::tx_extra_merge_mining_tag, TX_EXTRA_MERGE_MINING_TAG);
VARIANT_TAG(binary_archive, cryptonote::tx_extra_additional_pub_keys, TX_EXTRA_TAG_ADDITIONAL_PUBKEYS);
VARIANT_TAG(binary_archive, cryptonote::tx_extra_mysterious_minergate, TX_EXTRA_MYSTERIOUS_MINERGATE_TAG);
VARIANT_TAG(binary_archive, cryptonote::tx_extra_token, TX_EXTRA_TAG_TOKEN);
+6 -2
View File
@@ -44,10 +44,11 @@
#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 4
#define CURRENT_TRANSACTION_VERSION 5
#define TRANSACTION_VERSION_2_OUTS 2
#define TRANSACTION_VERSION_N_OUTS 3
#define TRANSACTION_VERSION_CARROT 4
#define TRANSACTION_VERSION_ENABLE_TOKENS 5
#define CURRENT_BLOCK_MAJOR_VERSION 1
#define CURRENT_BLOCK_MINOR_VERSION 1
#define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT 60*60*2
@@ -91,6 +92,8 @@
#define TREASURY_SAL1_MINT_AMOUNT ((uint64_t)130000000000000ull) // 1.3M
#define TREASURY_SAL1_MINT_COUNT 8 // 8 times
#define CREATE_TOKEN_LOCK_PERIOD 10
#define DIFFICULTY_TARGET_V2 120 // seconds
#define DIFFICULTY_TARGET_V1 60 // seconds - before first fork
#define DIFFICULTY_WINDOW_V2 70 // blocks
@@ -244,13 +247,14 @@
#define HF_VERSION_AUDIT2 8
#define HF_VERSION_AUDIT2_PAUSE 9
#define HF_VERSION_CARROT 10
#define HF_VERSION_ENABLE_TOKENS 11
#define HF_VERSION_REQUIRE_VIEW_TAGS 255
#define HF_VERSION_ENABLE_CONVERT 255
#define HF_VERSION_ENABLE_ORACLE 255
#define HF_VERSION_SLIPPAGE_YIELD 255
#define TESTNET_VERSION 15
#define TESTNET_VERSION 16
#define STAGENET_VERSION 1
#define PER_KB_FEE_QUANTIZATION_DECIMALS 8
+312 -43
View File
@@ -38,6 +38,7 @@
#include "include_base_utils.h"
#include "cryptonote_basic/cryptonote_basic_impl.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "tx_pool.h"
#include "blockchain.h"
#include "blockchain_db/locked_txn.h"
@@ -66,6 +67,8 @@
#include "net/http_client.h"
#include "storages/http_abstract_invoke.h"
#include "common/debugging.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "blockchain"
@@ -877,7 +880,7 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
std::vector<uint64_t> timestamps;
std::vector<difficulty_type> difficulties;
uint64_t height;
auto new_top_hash = get_tail_id(height); // get it again now that we have the lock
top_hash = get_tail_id(height); // get it again now that we have the lock
++height;
uint8_t version = get_current_hard_fork_version();
@@ -976,11 +979,11 @@ size_t Blockchain::recalculate_difficulties(boost::optional<uint64_t> start_heig
if (start_height_opt) {
start_height = *start_height_opt;
} else {
bool found = false;
// bool found;
for (size_t i=0; i<num_mainnet_hard_forks; ++i) {
if (version == mainnet_hard_forks[i].version) {
start_height = mainnet_hard_forks[i].height;
found = true;
//found = true;
break;
}
}
@@ -1348,7 +1351,10 @@ 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) {
if (hf_version >= HF_VERSION_ENABLE_TOKENS) {
CHECK_AND_ASSERT_MES(b.miner_tx.version == TRANSACTION_VERSION_ENABLE_TOKENS, false, "miner transaction has wrong version");
CHECK_AND_ASSERT_MES(b.miner_tx.type == cryptonote::transaction_type::MINER, false, "miner transaction has wrong type");
} else 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");
}
@@ -1407,7 +1413,10 @@ bool Blockchain::prevalidate_protocol_transaction(const block& b, uint64_t heigh
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 >= HF_VERSION_ENABLE_TOKENS) {
CHECK_AND_ASSERT_MES(b.protocol_tx.version == TRANSACTION_VERSION_ENABLE_TOKENS, false, "protocol transaction has wrong version");
hf_version_submitted = hf_version;
} else 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 {
@@ -1525,6 +1534,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
case HF_VERSION_AUDIT2:
case HF_VERSION_AUDIT2_PAUSE:
case HF_VERSION_CARROT:
case HF_VERSION_ENABLE_TOKENS:
if (b.miner_tx.amount_burnt > 0) {
CHECK_AND_ASSERT_MES(money_in_use + b.miner_tx.amount_burnt > money_in_use, false, "miner transaction is overflowed by amount_burnt");
money_in_use += b.miner_tx.amount_burnt;
@@ -1557,7 +1567,7 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
}
//------------------------------------------------------------------
// SRCG
bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height, uint8_t hf_version)
bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height, uint8_t hf_version, const std::vector<std::pair<transaction, blobdata>>& txs)
{
LOG_PRINT_L3("Blockchain::" << __func__);
@@ -1633,9 +1643,20 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height,
}
}
// Count CREATE_TOKEN transactions if we're at the right hard fork version
size_t create_token_count = 0;
if (hf_version >= HF_VERSION_ENABLE_TOKENS && !txs.empty()) {
for (const auto& tx_pair : txs) {
const transaction& tx = tx_pair.first;
if (tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
create_token_count++;
}
}
}
// Check we have the correct number of entries
CHECK_AND_ASSERT_MES(
b.protocol_tx.vout.size() == yield_payouts.size() + audit_payouts.size() + carrot_yield_payouts.size(),
b.protocol_tx.vout.size() == yield_payouts.size() + audit_payouts.size() + carrot_yield_payouts.size() + create_token_count,
false, "Invalid number of outputs in protocol_tx - aborting"
);
@@ -1682,6 +1703,71 @@ bool Blockchain::validate_protocol_transaction(const block& b, uint64_t height,
);
}
if (hf_version >= HF_VERSION_ENABLE_TOKENS) {
// Validate create_coin transaction outputs
std::vector<std::pair<protocol_tx_data_t, token_metadata_t>> create_token_data;
for (const auto& tx_pair : txs) {
const transaction& tx = tx_pair.first;
if (tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
create_token_data.push_back(std::make_pair(tx.protocol_tx_data, tx.token_metadata));
}
}
for (const auto& ct_data : create_token_data) {
// 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 == ct_data.first.return_address, false, "Incorrect CREATE_TOKEN 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 CREATE_TOKEN return pubkey detected in protocol_tx");
CHECK_AND_ASSERT_MES(additional_pubkeys[output_idx] == ct_data.first.return_pubkey, false, "Incorrect CREATE_TOKEN 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 == ct_data.first.return_pubkey, false, "Incorrect CREATE_TOKEN return pubkey detected in protocol_tx");
}
// Verify the correct metadata type is provided
CHECK_AND_ASSERT_MES(ct_data.second.token.type() == typeid(cryptonote::sal_token_t), false, "Incorrect CREATE_TOKEN metadata type detected in protocol_tx");
cryptonote::sal_token_t token = boost::get<cryptonote::sal_token_t>(ct_data.second.token);
// Verify the output amount
uint64_t hi, lo;
CHECK_AND_ASSERT_MES(token.supply <= (MONEY_SUPPLY / COIN), false, "Invalid SUPPLY value for CREATE_TOKEN when constructing protocol_tx");
lo = mul128(token.supply, COIN, &hi);
CHECK_AND_ASSERT_MES(hi == 0, false, "Numeric overflow in CREATE_TOKEN supply");
CHECK_AND_ASSERT_MES(b.protocol_tx.vout[output_idx].amount == lo, false, "Incorrect CREATE_TOKEN output amount detected in protocol_tx");
// Verify the output asset type (should be the new asset type from protocol_tx_data)
std::string expected_asset_type = "sal" + ct_data.second.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 == expected_asset_type, false, "Incorrect CREATE_TOKEN output asset_type detected in protocol_tx");
// Validate custom asset type format
CHECK_AND_ASSERT_MES(cryptonote::is_valid_custom_asset_type(out_asset_type), false, "CREATE_TOKEN asset type is invalid");
// Verify the view tag
CHECK_AND_ASSERT_MES(
boost::get<cryptonote::txout_to_carrot_v1>(
b.protocol_tx.vout[output_idx].target
).view_tag == ct_data.first.return_view_tag, false, "Incorrect CREATE_TOKEN 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 == ct_data.first.return_anchor_enc, false, "Incorrect CREATE_TOKEN anchor detected in protocol_tx"
);
output_idx++;
}
}
return true;
}
@@ -1974,19 +2060,46 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
CHECK_AND_ASSERT_MES(diffic, false, "difficulty overhead.");
std::vector<cryptonote::protocol_data_entry> protocol_entries;
size_t txs_weight;
uint64_t fee;
if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, b.major_version))
std::vector<std::pair<protocol_tx_data_t, token_metadata_t>> create_token_entries;
if (!m_tx_pool.fill_block_template(b, median_weight, already_generated_coins, txs_weight, fee, expected_reward, create_token_entries, b.major_version))
{
return false;
}
// create the protocol transaction entries for CREATE_TOKEN TXs
if (b.major_version >= HF_VERSION_ENABLE_TOKENS) {
for (const auto& ct_data_entry: create_token_entries) {
CHECK_AND_ASSERT_MES(ct_data_entry.second.token.type() == typeid(cryptonote::sal_token_t), false, "Incorrect CREATE_TOKEN metadata type detected in protocol_tx");
cryptonote::sal_token_t token = boost::get<cryptonote::sal_token_t>(ct_data_entry.second.token);
cryptonote::protocol_data_entry entry;
uint64_t hi, lo;
CHECK_AND_ASSERT_MES(token.supply <= (MONEY_SUPPLY / COIN), false, "Invalid SUPPLY value for CREATE_TOKEN when constructing protocol_tx");
CHECK_AND_ASSERT_MES(token.supply > 0, false, "Invalid SUPPLY value for CREATE_TOKEN when constructing protocol_tx");
lo = mul128(token.supply, COIN, &hi);
CHECK_AND_ASSERT_MES(hi == 0, false, "Numeric overflow in CREATE_TOKEN supply");
entry.amount_burnt = cryptonote::get_token_creation_price(ct_data_entry.second.asset_type);
entry.amount_minted = lo;
entry.amount_slippage_limit = 0;
entry.source_asset = "SAL1";
entry.destination_asset = "sal" + ct_data_entry.second.asset_type;
entry.type = cryptonote::transaction_type::CREATE_TOKEN;
entry.return_address = ct_data_entry.first.return_address;
entry.return_pubkey = ct_data_entry.first.return_pubkey;
entry.return_view_tag = ct_data_entry.first.return_view_tag;
entry.return_anchor_enc = ct_data_entry.first.return_anchor_enc;
entry.is_carrot = true;
entry.origin_height = height;
protocol_entries.push_back(entry);
}
}
// Check to see if there are any matured YIELD TXs
uint64_t yield_lock_period = get_config(m_nettype).STAKE_LOCK_PERIOD;
uint64_t start_height = (height > yield_lock_period) ? height - yield_lock_period - 1 : 0;
std::vector<cryptonote::protocol_data_entry> protocol_entries;
cryptonote::yield_block_info ybi_matured;
bool ok = get_ybi_entry(start_height, ybi_matured);
if (ok && ybi_matured.locked_coins_this_block > 0) {
@@ -2123,7 +2236,6 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
*/
// Time to construct the protocol_tx
uint64_t protocol_fee = 0;
address_parse_info treasury_address_info;
ok = cryptonote::get_account_address_from_str(treasury_address_info, m_nettype, get_config(m_nettype).TREASURY_ADDRESS);
CHECK_AND_ASSERT_MES(ok, false, "Failed to obtain treasury address info");
@@ -2954,7 +3066,7 @@ bool Blockchain::get_pricing_record(oracle::pricing_record &pr, std::map<std::st
bool r = false;
const uint64_t height = get_current_blockchain_height();
const uint8_t hf_version = m_hardfork->get_current_version();
// const uint8_t hf_version = m_hardfork->get_current_version();
epee::net_utils::http::http_simple_client http_client;
COMMAND_RPC_GET_PRICING_RECORD::request req = AUTO_VAL_INIT(req);
@@ -3728,7 +3840,7 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
if (hf_version >= HF_VERSION_CARROT) {
// 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.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 || tx.type == cryptonote::transaction_type::CREATE_TOKEN || tx.type == cryptonote::transaction_type::ROLLUP) {
if (tx.rct_signatures.type != rct::RCTTypeSalviumOne) {
MERROR_VER("SalviumOne data required after v" + std::to_string(HF_VERSION_CARROT));
tvc.m_invalid_output = true;
@@ -3783,6 +3895,100 @@ bool Blockchain::check_tx_outputs(const transaction& tx, tx_verification_context
return true;
}
//------------------------------------------------------------------
bool Blockchain::check_tx_asset_types(const transaction& tx, tx_verification_context &tvc) const
{
LOG_PRINT_L3("Blockchain::" << __func__);
CRITICAL_REGION_LOCAL(m_blockchain_lock);
const uint8_t hf_version = m_hardfork->get_current_version();
if (hf_version >= HF_VERSION_ENABLE_TOKENS) {
// Get the valid tokens according to the DB
std::map<std::string, cryptonote::token_metadata_t> mapTokens = m_db->get_tokens();
if (tx.type == cryptonote::transaction_type::PROTOCOL || tx.type == cryptonote::transaction_type::MINER) {
return true;
} else if (
tx.type == cryptonote::transaction_type::CREATE_TOKEN ||
tx.type == cryptonote::transaction_type::ROLLUP ||
tx.type == cryptonote::transaction_type::STAKE
) {
if (tx.source_asset_type != "SAL1" || tx.destination_asset_type != "SAL1") {
MERROR_VER("Invalid source/dest asset type for CREATE_TOKEN / ROLLUP / STAKE - provided source asset: " << tx.source_asset_type << ", and destination asset: " << tx.destination_asset_type << ", expected SAL1");
tvc.m_verifivation_failed = true;
return false;
}
} else if (tx.type == cryptonote::transaction_type::BURN) {
if ((tx.source_asset_type != "SAL" && tx.source_asset_type != "SAL1") || tx.destination_asset_type != tx.source_asset_type) {
MERROR_VER("Invalid source/dest asset type for BURN - provided source asset: " << tx.source_asset_type << ", and destination asset: " << tx.destination_asset_type << ", expected (matching) SAL/SAL1");
tvc.m_verifivation_failed = true;
return false;
}
} else if (tx.type == cryptonote::transaction_type::TRANSFER) {
if (tx.source_asset_type != tx.destination_asset_type) {
MERROR_VER("Mismatched asset types for TRANSFER - provided source asset: " << tx.source_asset_type << ", provided destination asset: " << tx.destination_asset_type << ", expected them to match");
tvc.m_verifivation_failed = true;
return false;
} else if (!(tx.source_asset_type == "SAL1" || mapTokens.count(tx.source_asset_type))) {
MERROR_VER("Invalid source asset type for TRANSFER - provided source asset: " << tx.source_asset_type << ", expected valid token type or SAL1");
tvc.m_verifivation_failed = true;
return false;
} else if (tx.destination_asset_type != "SAL1" && !mapTokens.count(tx.destination_asset_type)) {
MERROR_VER("Invalid destination asset type for TRANSFER - provided destination asset: " << tx.destination_asset_type << ", expected valid token type or SAL1");
tvc.m_verifivation_failed = true;
return false;
}
} else if (tx.type == cryptonote::transaction_type::AUDIT || tx.type == cryptonote::transaction_type::CONVERT) {
MERROR_VER("AUDIT and CONVERT transaction types are not allowed in this hardfork version:" << std::to_string(HF_VERSION_ENABLE_TOKENS));
} else {
MERROR_VER("Unknown transaction type: " << tx.type << ".");
tvc.m_verifivation_failed = true;
return false;
}
} else if (hf_version >= HF_VERSION_CARROT) {
if (tx.source_asset_type != "SAL1" || tx.destination_asset_type != "SAL1") {
MERROR_VER("Invalid destination/source asset type - provided destination asset: " << tx.destination_asset_type << ", expected SAL1" << ", provided source asset: " << tx.source_asset_type << ", expected SAL1");
tvc.m_verifivation_failed = true;
return false;
}
// }
} else if (hf_version >= HF_VERSION_SALVIUM_ONE_PROOFS) {
// protocol and miner txs don't have source and destination asset types
if (tx.type == cryptonote::transaction_type::PROTOCOL || tx.type == cryptonote::transaction_type::MINER) {
return true;
} else if (tx.type == cryptonote::transaction_type::AUDIT) {
CHECK_AND_ASSERT_MES(tx.source_asset_type == "SAL" && tx.destination_asset_type == "SAL", false, "wrong source/destination asset type: provided source asset " << tx.source_asset_type << " expected SAL and provided destination asset " << tx.destination_asset_type << " expected SAL");
} else if (tx.type == cryptonote::transaction_type::BURN) {
if (tx.source_asset_type != "SAL1" || tx.destination_asset_type != "BURN") {
MERROR_VER("Invalid source/dest asset type for BURN - provided source asset: " << tx.source_asset_type << ", and destination asset: " << tx.destination_asset_type << ", expected SAL1 and BURN respectively");
tvc.m_verifivation_failed = true;
return false;
}
} else {
CHECK_AND_ASSERT_MES(tx.source_asset_type == "SAL1" && tx.destination_asset_type == "SAL1", false, "wrong source/destination asset type: provided source asset: " << tx.source_asset_type << " expected SAL1 and provided destination asset: " << tx.destination_asset_type << " expected SAL1");
}
} else {
if (tx.type == cryptonote::transaction_type::BURN) {
if (tx.source_asset_type != "SAL" || tx.destination_asset_type != "BURN") {
MERROR_VER("Invalid source/dest asset type for BURN - provided source asset: " << tx.source_asset_type << ", and destination asset: " << tx.destination_asset_type << ", expected SAL and BURN respectively");
tvc.m_verifivation_failed = true;
return false;
}
}
//only SAL existed before hf 6
else{
if (tx.source_asset_type != "SAL" || tx.destination_asset_type != "SAL") {
MERROR_VER("Invalid destination/source asset type - provided destination asset: " << tx.destination_asset_type << ", expected SAL" << ", provided source asset: " << tx.source_asset_type << ", expected SAL");
tvc.m_verifivation_failed = true;
return false;}
}
}
return true;
}
//------------------------------------------------------------------
bool Blockchain::check_tx_type_and_version(const transaction& tx, tx_verification_context &tvc) const
{
@@ -3809,6 +4015,25 @@ bool Blockchain::check_tx_type_and_version(const transaction& tx, tx_verificatio
}
}
// Reverse the order of checking now, because we're using >=
if (hf_version >= HF_VERSION_ENABLE_TOKENS) {
if (tx.type == cryptonote::transaction_type::TRANSFER ||
tx.type == cryptonote::transaction_type::RETURN ||
tx.type == cryptonote::transaction_type::ROLLUP ||
tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
if (tx.version < TRANSACTION_VERSION_ENABLE_TOKENS) {
}
} else {
if (tx.version != TRANSACTION_VERSION_ENABLE_TOKENS) {
}
if (tx.type == cryptonote::transaction_type::PROTOCOL) {
// Allowed multiple asset_types for `vout` entries
} else {
// No mixing of asset_types permitted - verify here
}
}
}
// After v2 allow N-out TXs for TRANSFER ONLY
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) {
@@ -3818,7 +4043,15 @@ bool Blockchain::check_tx_type_and_version(const transaction& tx, tx_verificatio
}
}
if (hf_version >= HF_VERSION_CARROT) {
if (hf_version >= HF_VERSION_ENABLE_TOKENS) {
if (tx.version != TRANSACTION_VERSION_ENABLE_TOKENS) {
MERROR("TX version " + std::to_string(tx.version) + " is not supported, expected " + std::to_string(TRANSACTION_VERSION_ENABLE_TOKENS));
tvc.m_version_mismatch = true;
return false;
}
}
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;
@@ -3835,6 +4068,19 @@ bool Blockchain::check_tx_type_and_version(const transaction& tx, tx_verificatio
}
}
// Make sure CREATE_TOKEN TXs are disabled until we are ready - belt and braces!
if (hf_version < HF_VERSION_ENABLE_TOKENS) {
if (tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
MERROR("CREATE_TOKEN TXs are not permitted prior to v" + std::to_string(HF_VERSION_ENABLE_TOKENS));
tvc.m_version_mismatch = true;
return false;
}
if (tx.type == cryptonote::transaction_type::ROLLUP) {
MERROR("ROLLUP TXs are not permitted prior to v" + std::to_string(HF_VERSION_ENABLE_TOKENS));
tvc.m_version_mismatch = true;
return false;
}
}
if (tx.type == cryptonote::transaction_type::AUDIT) {
// Make sure we are supposed to accept AUDIT txs at this point
@@ -3842,25 +4088,25 @@ bool Blockchain::check_tx_type_and_version(const transaction& tx, tx_verificatio
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);
if (tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
// Check that the specific asset_type being created isn't already in our list of tokens
std::map<std::string, cryptonote::token_metadata_t> mapTokens = m_db->get_tokens();
std::string asset_type = "sal" + tx.token_metadata.asset_type;
if (mapTokens.count(asset_type)) {
MERROR("TX attempting to create '" + asset_type + "', which already exists");
tvc.m_verifivation_failed = true;
return false;
}
// Validate token metadata //! check */
CHECK_AND_ASSERT_MES(tx.token_metadata.token.type() == typeid(cryptonote::sal_token_t), false, "Invalid CREATE_TOKEN metadata type");
cryptonote::sal_token_t token = boost::get<cryptonote::sal_token_t>(tx.token_metadata.token);
CHECK_AND_ASSERT_MES(token.supply > 0 && token.supply <= (MONEY_SUPPLY / COIN), false, "Invalid SUPPLY value for CREATE_TOKEN");
// Validate the amount burnt matches the token creation price
CHECK_AND_ASSERT_MES(tx.amount_burnt == cryptonote::get_token_creation_price(tx.token_metadata.asset_type), false, "Invalid fee paid for CREATE_TOKEN");
}
// 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");
@@ -3895,7 +4141,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 || tx.version == 4, false, "Transaction version is not 2/3/4");
CHECK_AND_ASSERT_MES(tx.version == 2 || tx.version == 3 || tx.version == 4 || tx.version == 5, false, "Transaction version is not 2/3/5");
rct::rctSig &rv = tx.rct_signatures;
@@ -4040,11 +4286,13 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc,
const txin_to_key& in_to_key = boost::get<txin_to_key>(txin);
if (in_to_key.amount == 0)
{
// always consider rct inputs mixable. Even if there's not enough rct
// inputs on the chain to mix with, this is going to be the case for
// just a few blocks right after the fork at most
++n_mixable;
}
// don't always consider rct inputs mixable!
uint64_t n_outputs = m_db->get_num_outputs_of_asset_type(in_to_key.asset_type);
if (n_outputs <= min_mixin)
++n_unmixable;
else
++n_mixable;
}
else
{
uint64_t n_outputs = m_db->get_num_outputs(in_to_key.amount);
@@ -4518,8 +4766,7 @@ void Blockchain::get_dynamic_base_fee_estimate_2021_scaling(uint64_t grace_block
//------------------------------------------------------------------
uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const
{
const uint8_t version = get_current_hard_fork_version();
const uint64_t db_height = m_db->height();
// const uint64_t db_height = m_db->height();
if (grace_blocks >= CRYPTONOTE_REWARD_BLOCKS_WINDOW)
grace_blocks = CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1;
@@ -4670,7 +4917,7 @@ bool Blockchain::calculate_audit_payouts(const uint64_t start_height, std::vecto
// Get the AUDIT TX information for matured staked coins
std::vector<cryptonote::yield_tx_info> audit_entries;
// We get the audit_tx_info from the block where they entered the chain
int audit_tx_result = m_db->get_audit_tx_info(start_height, audit_entries);
m_db->get_audit_tx_info(start_height, audit_entries);
if (!audit_entries.size()) {
// Report error and abort
@@ -4723,7 +4970,7 @@ bool Blockchain::calculate_yield_payouts(const uint64_t start_height, std::vecto
// Get the YIELD TX information for matured staked coins
std::vector<cryptonote::yield_tx_info_carrot> yield_entries;
// We get the yield_tx_info from the block _before_ they started to accrue yield
int yield_tx_result = m_db->get_carrot_yield_tx_info(start_height, yield_entries);
m_db->get_carrot_yield_tx_info(start_height, yield_entries);
if (!yield_entries.size()) {
// Report error and abort
@@ -4789,7 +5036,7 @@ bool Blockchain::calculate_yield_payouts(const uint64_t start_height, std::vecto
// Get the YIELD TX information for matured staked coins
std::vector<cryptonote::yield_tx_info> yield_entries;
// We get the yield_tx_info from the block _before_ they started to accrue yield
int yield_tx_result = m_db->get_yield_tx_info(start_height, yield_entries);
m_db->get_yield_tx_info(start_height, yield_entries);
if (!yield_entries.size()) {
// Report error and abort
@@ -4956,6 +5203,18 @@ bool Blockchain::get_ybi_entry(const uint64_t height, cryptonote::yield_block_in
return true;
}
//------------------------------------------------------------------
bool Blockchain::is_tx_paid_for(const cryptonote::transaction& tx)
{
// Is it a TRANSFER?
if (tx.type != cryptonote::transaction_type::TRANSFER)
return true;
// Is is a TOKEN?
if (!cryptonote::is_asset_type_token(tx.source_asset_type))
return true;
// Has the TRANSFER TOKEN been paid for?
return m_db->is_tx_paid_for(tx);
}
//------------------------------------------------------------------
//TODO: revisit, has changed a bit on upstream
bool Blockchain::check_block_timestamp(std::vector<uint64_t>& timestamps, const block& b, uint64_t& median_ts) const
{
@@ -5330,6 +5589,16 @@ leave:
}
}
#endif
// Check the TX type
tx_verification_context tvc;
if (!check_tx_type_and_version(tx, tvc)) {
MERROR("Block with id: " << id << " failed to pass transaction type and version check for transaction id: " << tx_id);
bvc.m_verifivation_failed = true;
return_tx_to_pool(txs);
goto leave;
}
TIME_MEASURE_FINISH(cc);
t_checktx += cc;
fee_summary += fee;
@@ -5362,7 +5631,7 @@ leave:
TIME_MEASURE_FINISH(vmt);
TIME_MEASURE_START(vpt);
if(!validate_protocol_transaction(bl, blockchain_height, m_hardfork->get_current_version()))
if(!validate_protocol_transaction(bl, blockchain_height, m_hardfork->get_current_version(), txs))
{
MERROR_VER("Block with id: " << id << " has incorrect protocol transaction");
bvc.m_verifivation_failed = true;
@@ -5406,7 +5675,7 @@ leave:
// Update the YBI cache data
uint64_t yield_lock_period = cryptonote::get_config(m_nettype).STAKE_LOCK_PERIOD;
uint64_t ybi_cache_expected_size = std::min(new_height, yield_lock_period);
// uint64_t ybi_cache_expected_size = std::min(new_height, yield_lock_period);
if (new_height > yield_lock_period) {
if (m_yield_block_info_cache.count(new_height - yield_lock_period - 2) != 0) {
m_yield_block_info_cache.erase(new_height - yield_lock_period - 2);
@@ -5524,7 +5793,7 @@ uint64_t Blockchain::get_next_long_term_block_weight(uint64_t block_weight) cons
const uint64_t db_height = m_db->height();
const uint64_t nblocks = std::min<uint64_t>(m_long_term_block_weights_window, db_height);
const uint8_t hf_version = get_current_hard_fork_version();
// const uint8_t hf_version = get_current_hard_fork_version();
if (db_height < CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE)
return block_weight;
@@ -6171,7 +6440,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
} while(0); \
// generate sorted tables for all amounts and absolute offsets
size_t tx_index = 0, block_index = 0;
size_t tx_index = 0; //, block_index = 0;
for (const auto &entry : blocks_entry)
{
if (m_cancel)
@@ -6242,7 +6511,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
}
}
++block_index;
//++block_index;
}
// sort and remove duplicate absolute_offsets in offset_map
@@ -6514,7 +6783,7 @@ void Blockchain::cancel()
}
#if defined(PER_BLOCK_CHECKPOINT)
static const char expected_block_hashes_hash[] = "a2a5a9bc5d606392ac5c14be55b90a92b8577b8ffdac5c63cc6174f41764c753";
static const char expected_block_hashes_hash[] = "7e6c9efd949afb36bce062bbb720605f0dd67b03c0124e5167c527fa59c15ce3";
void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints)
{
if (get_checkpoints == nullptr || !m_fast_sync)
+17 -1
View File
@@ -726,6 +726,19 @@ namespace cryptonote
*/
bool check_tx_outputs(const transaction& tx, tx_verification_context &tvc) const;
/**
* @brief check a transaction's input/output/TX asset types conform to current standards
*
* This function checks, for example at the time of this writing, that
* the asset types are supported by the given TX for the current HF version on-chain.
*
* @param tx the transaction to check the asset types of
* @param tvc returned info about tx verification
*
* @return false if the TX version and/or type is unsupported, otherwise true
*/
bool check_tx_asset_types(const transaction& tx, tx_verification_context &tvc) const;
/**
* @brief check that a transaction's version & type conforms to current standards
*
@@ -1233,6 +1246,8 @@ namespace cryptonote
*/
bool validate_ybi_cache();
bool is_tx_paid_for(const cryptonote::transaction& tx);
#ifndef IN_UNIT_TESTS
private:
#endif
@@ -1564,10 +1579,11 @@ namespace cryptonote
* @param b the block containing the miner transaction to be validated
* @param height the blockchain's weight
* @param version hard fork version for that transaction
* @param txs create_coin transactions in the block
*
* @return false if anything is found wrong with the protocol transaction, otherwise true
*/
bool validate_protocol_transaction(const block& b, uint64_t height, uint8_t hf_version);
bool validate_protocol_transaction(const block& b, uint64_t height, uint8_t hf_version, const std::vector<std::pair<transaction, blobdata>>& txs = std::vector<std::pair<transaction, blobdata>>());
/**
* @brief reverts the blockchain to its previous state following a failed switch
+25 -5
View File
@@ -843,7 +843,8 @@ 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_CARROT ? TRANSACTION_VERSION_CARROT :
const size_t max_tx_version = hf_version >= HF_VERSION_ENABLE_TOKENS ? TRANSACTION_VERSION_ENABLE_TOKENS :
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)
@@ -904,6 +905,7 @@ namespace cryptonote
//-----------------------------------------------------------------------------------------------
bool core::handle_incoming_tx_accumulated_batch(std::vector<tx_verification_batch_info> &tx_info, bool keeped_by_block)
{
const uint8_t hf_version = m_blockchain_storage.get_current_hard_fork_version();
bool ret = true;
if (keeped_by_block && get_blockchain_storage().is_within_compiled_block_hash_area())
{
@@ -925,7 +927,6 @@ namespace cryptonote
if (tx_info[n].tx->version < 2)
continue;
const rct::rctSig &rv = tx_info[n].tx->rct_signatures;
const uint8_t hf_version = m_blockchain_storage.get_current_hard_fork_version();
if (hf_version >= HF_VERSION_CARROT) {
if (rv.type != rct::RCTTypeNull && rv.type != rct::RCTTypeSalviumOne) {
MERROR_VER("Invalid RCT type provided");
@@ -950,7 +951,11 @@ namespace cryptonote
tx_info[n].result = false;
return false;
}
}
}
bool need_rollup = false;
if (hf_version >= HF_VERSION_CARROT) {
need_rollup = (tx_info[n].tx->source_asset_type != "SAL1");
}
switch (rv.type) {
case rct::RCTTypeNull:
// coinbase should not come here, so we reject for all other types
@@ -965,7 +970,8 @@ namespace cryptonote
tx_info[n].tx->type == cryptonote::transaction_type::CONVERT ? tx_info[n].tx->amount_burnt :
tx_info[n].tx->type == cryptonote::transaction_type::STAKE ? tx_info[n].tx->amount_burnt :
tx_info[n].tx->type == cryptonote::transaction_type::AUDIT ? tx_info[n].tx->amount_burnt :
0
0,
need_rollup
))
{
MERROR_VER("rct signature semantics check failed");
@@ -1030,12 +1036,19 @@ namespace cryptonote
continue;
if (tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof && tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproof2 && tx_info[n].tx->rct_signatures.type != rct::RCTTypeCLSAG && tx_info[n].tx->rct_signatures.type != rct::RCTTypeBulletproofPlus && tx_info[n].tx->rct_signatures.type != rct::RCTTypeFullProofs && tx_info[n].tx->rct_signatures.type != rct::RCTTypeSalviumZero && tx_info[n].tx->rct_signatures.type != rct::RCTTypeSalviumOne)
continue;
bool need_rollup = false;
if (hf_version >= HF_VERSION_CARROT) {
need_rollup = (tx_info[n].tx->source_asset_type != "SAL1");
}
if (!rct::verRctSemanticsSimple(tx_info[n].tx->rct_signatures,
tx_info[n].tx->type == cryptonote::transaction_type::BURN ? tx_info[n].tx->amount_burnt :
tx_info[n].tx->type == cryptonote::transaction_type::CONVERT ? tx_info[n].tx->amount_burnt :
tx_info[n].tx->type == cryptonote::transaction_type::STAKE ? tx_info[n].tx->amount_burnt :
tx_info[n].tx->type == cryptonote::transaction_type::AUDIT ? tx_info[n].tx->amount_burnt :
0
tx_info[n].tx->type == cryptonote::transaction_type::CREATE_TOKEN ? tx_info[n].tx->amount_burnt :
tx_info[n].tx->type == cryptonote::transaction_type::ROLLUP ? tx_info[n].tx->amount_burnt :
0,
need_rollup
))
{
set_semantics_failed(tx_info[n].tx_hash);
@@ -1249,6 +1262,13 @@ namespace cryptonote
return false;
}
/*
if (hf_version >= HF_VERSION_ENABLE_TOKENS && !m_blockchain_storage.is_tx_paid_for(tx))
{
MERROR_VER("tx has not been paid for by ROLLUP");
return false;
}
*/
return true;
}
//-----------------------------------------------------------------------------------------------
+8 -5
View File
@@ -346,7 +346,7 @@ namespace cryptonote
// Clear the TX contents
tx.set_null();
tx.version = 2;
tx.version = (hard_fork_version >= HF_VERSION_ENABLE_TOKENS) ? 3 : 2;
bool carrot_found = false;
bool noncarrot_found = false;
tx.type = cryptonote::transaction_type::PROTOCOL;
@@ -356,7 +356,7 @@ namespace cryptonote
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;
@@ -365,7 +365,7 @@ namespace cryptonote
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
@@ -381,7 +381,8 @@ namespace cryptonote
// Build the proposal
carrot::CarrotCoinbaseEnoteV1 e;
e.onetime_address = entry.return_address;
e.amount = entry.amount_burnt;
// amount_minted for CREATE_TOKEN, amount_burnt for STAKE/AUDIT
e.amount = (entry.type == cryptonote::transaction_type::CREATE_TOKEN) ? entry.amount_minted : entry.amount_burnt;
e.asset_type = entry.destination_asset;
e.view_tag = entry.return_view_tag;
e.anchor_enc = entry.return_anchor_enc;
@@ -391,6 +392,7 @@ namespace cryptonote
}
tx = store_carrot_to_coinbase_transaction_v1(enotes, std::string{}, cryptonote::transaction_type::PROTOCOL, height);
tx.amount_burnt = 0;
tx.version = (hard_fork_version >= HF_VERSION_ENABLE_TOKENS) ? TRANSACTION_VERSION_ENABLE_TOKENS : TRANSACTION_VERSION_CARROT;
tx.invalidate_hashes();
}
catch (const std::exception &e)
@@ -539,7 +541,7 @@ namespace cryptonote
}
tx = carrot::store_carrot_to_coinbase_transaction_v1(enotes, extra_nonce, cryptonote::transaction_type::MINER, height);
tx.version = (hard_fork_version >= HF_VERSION_ENABLE_TOKENS) ? TRANSACTION_VERSION_ENABLE_TOKENS : TRANSACTION_VERSION_CARROT;
tx.amount_burnt = stake_reward;
tx.invalidate_hashes();
}
@@ -597,6 +599,7 @@ namespace cryptonote
case HF_VERSION_AUDIT2:
case HF_VERSION_AUDIT2_PAUSE:
case HF_VERSION_CARROT:
case HF_VERSION_ENABLE_TOKENS:
// SRCG: subtract 20% that will be rewarded to staking users
CHECK_AND_ASSERT_MES(tx.amount_burnt == 0, false, "while creating outs: amount_burnt is nonzero");
tx.amount_burnt = amount / 5;
+62 -2
View File
@@ -47,6 +47,7 @@
#include "common/perf_timer.h"
#include "crypto/hash.h"
#include "crypto/duration.h"
#include "common/debugging.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "txpool"
@@ -266,6 +267,20 @@ namespace cryptonote
tvc.m_verifivation_failed = true;
return false;
}
// Check the TX asset types
if (!m_blockchain.check_tx_asset_types(tx, tvc)) {
LOG_PRINT_L1("Transaction with id= "<< id << " has invalid asset type(s)");
tvc.m_verifivation_failed = true;
return false;
}
// HERE BE DRAGONS!!!
// Check that CREATE_TOKEN txs are unique in the pool
if (tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
// TODO: ...scan the existing entries - requires either a registry of CREATE_TOKEN TXs, or to interatively process the pool
}
// LAND AHOY!!!
// assume failure during verification steps until success is certain
tvc.m_verifivation_failed = true;
@@ -1505,6 +1520,10 @@ namespace cryptonote
return false;
}
// Check that `transfer token` TXs have been paid for before accepting them into a block template
if (!m_blockchain.is_tx_paid_for(lazy_tx()))
return false;
//transaction is ok.
return true;
}
@@ -1614,7 +1633,7 @@ namespace cryptonote
//---------------------------------------------------------------------------------
//---------------------------------------------------------------------------------
//TODO: investigate whether boolean return is appropriate
bool tx_memory_pool::fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version)
bool tx_memory_pool::fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, std::vector<std::pair<protocol_tx_data_t, token_metadata_t>> &create_token_entries, uint8_t version)
{
CRITICAL_REGION_LOCAL(m_transactions_lock);
CRITICAL_REGION_LOCAL1(m_blockchain);
@@ -1630,7 +1649,6 @@ namespace cryptonote
return false;
}
size_t max_total_weight_pre_v5 = (130 * median_weight) / 100 - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
size_t max_total_weight_v5 = 2 * median_weight - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE;
size_t max_total_weight = version >= 5 ? max_total_weight_v5 : max_total_weight_pre_v5;
@@ -1638,6 +1656,15 @@ namespace cryptonote
LOG_PRINT_L2("Filling block template, median weight " << median_weight << ", " << m_txs_by_fee_and_receive_time.size() << " txes in the pool");
// Store list of tokens being created
std::set<std::string> tokens;
// Get the list of already-created tokens that are in the DB
std::map<std::string, cryptonote::token_metadata_t> mapTokens = m_blockchain.get_db().get_tokens();
for (const auto &entry: mapTokens) {
tokens.insert(entry.second.asset_type);
}
LockedTXN lock(m_blockchain.get_db());
auto sorted_it = m_txs_by_fee_and_receive_time.begin();
@@ -1760,6 +1787,39 @@ namespace cryptonote
continue;
}
// check for ROLLUP TXs
if (tx.type == cryptonote::transaction_type::ROLLUP)
{
if (version < HF_VERSION_ENABLE_TOKENS) {
LOG_PRINT_L2(" is a ROLLUP transaction before they are permitted - cannot be mined");
continue;
}
}
// check and include create token entries
if (tx.type == transaction_type::CREATE_TOKEN)
{
if (version < HF_VERSION_ENABLE_TOKENS) {
LOG_PRINT_L2(" is a CREATE_TOKEN transaction before they are permitted - cannot be mined");
continue;
}
std::string asset_type = "sal" + tx.token_metadata.asset_type;
if (!cryptonote::is_valid_asset_type(asset_type)) {
LOG_PRINT_L2(" is a CREATE_TOKEN transaction with an invalid asset_type - cannot be mined");
continue;
}
// Check to see if token is already being created
if (tokens.find(tx.token_metadata.asset_type) != tokens.end()) {
LOG_PRINT_L2(" is a CREATE_TOKEN transaction with an asset_type already in the block template - cannot be mined");
continue;
}
tokens.insert(tx.token_metadata.asset_type);
create_token_entries.push_back(std::make_pair(tx.protocol_tx_data, tx.token_metadata));
}
bl.tx_hashes.push_back(sorted_it->second);
total_weight += meta.weight;
fee += meta.fee;
+2 -1
View File
@@ -226,11 +226,12 @@ namespace cryptonote
* @param total_weight return-by-reference the total weight of the new block
* @param fee return-by-reference the total of fees from the included transactions
* @param expected_reward return-by-reference the total reward awarded to the miner finding this block, including transaction fees
* @param create_token_entries list of create coin entries to include in protocol tx
* @param version hard fork version to use for consensus rules
*
* @return true
*/
bool fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, uint8_t version);
bool fill_block_template(block &bl, size_t median_weight, uint64_t already_generated_coins, size_t &total_weight, uint64_t &fee, uint64_t &expected_reward, std::vector<std::pair<protocol_tx_data_t, token_metadata_t>> &create_token_entries, uint8_t version);
/**
* @brief get a list of all transactions in the pool
@@ -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 > 4 || tx.rct_signatures.type > rct::RCTTypeSalviumOne;
const bool untested_tx = tx.version > TRANSACTION_VERSION_ENABLE_TOKENS || 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
+3 -1
View File
@@ -54,6 +54,8 @@ namespace cryptonote
STAKE = 6,
RETURN = 7,
AUDIT = 8,
MAX = 8
CREATE_TOKEN = 9,
ROLLUP = 10,
MAX = 10
};
}
+1 -1
View File
@@ -1355,7 +1355,7 @@ bool t_rpc_command_executor::print_transaction_pool_stats() {
bool t_rpc_command_executor::start_mining(cryptonote::account_public_address address, uint64_t num_threads, cryptonote::network_type nettype, bool do_background_mining, bool ignore_battery, bool is_carrot) {
cryptonote::COMMAND_RPC_START_MINING::request req;
cryptonote::COMMAND_RPC_START_MINING::response res;
req.miner_address = cryptonote::get_account_address_as_str(nettype, false, address, is_carrot);
req.miner_address = cryptonote::get_account_address_as_str(nettype, false, address);
req.threads_count = num_threads;
req.do_background_mining = do_background_mining;
req.ignore_battery = ignore_battery;
+4
View File
@@ -202,6 +202,10 @@ namespace hw {
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
crypto::signature &sig) = 0;
virtual void generate_carrot_tx_proof(const crypto::hash &prefix_hash,
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
const crypto::secret_key &a, crypto::signature &sig) = 0;
virtual bool open_tx(crypto::secret_key &tx_key) = 0;
virtual void get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) = 0;
+6
View File
@@ -282,6 +282,12 @@ namespace hw {
crypto::generate_tx_proof(prefix_hash, R, A, B, D, r, sig);
}
void device_default::generate_carrot_tx_proof(const crypto::hash &prefix_hash,
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
const crypto::secret_key &a, crypto::signature &sig) {
crypto::generate_carrot_tx_proof(prefix_hash, R, A, B, D, r, a, sig);
}
bool device_default::open_tx(crypto::secret_key &tx_key) {
cryptonote::keypair txkey = cryptonote::keypair::generate(*this);
tx_key = txkey.sec;
+4
View File
@@ -112,6 +112,10 @@ namespace hw {
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
crypto::signature &sig) override;
void generate_carrot_tx_proof(const crypto::hash &prefix_hash,
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
const crypto::secret_key &a, crypto::signature &sig) override;
bool open_tx(crypto::secret_key &tx_key) override;
void get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) override;
+8
View File
@@ -1433,6 +1433,14 @@ namespace hw {
#endif
}
void device_ledger::generate_carrot_tx_proof(const crypto::hash &prefix_hash,
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
const crypto::secret_key &a, crypto::signature &sig) {
// to-do: For now, carrot tx proofs are not supported
AUTO_LOCK_CMD();
crypto::generate_carrot_tx_proof(prefix_hash, R, A, B, D, r, a, sig);
}
bool device_ledger::open_tx(crypto::secret_key &tx_key) {
AUTO_LOCK_CMD();
this->lock();
+4
View File
@@ -253,6 +253,10 @@ namespace hw {
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
crypto::signature &sig) override;
void generate_carrot_tx_proof(const crypto::hash &prefix_hash,
const crypto::public_key &R, const crypto::public_key &A, const boost::optional<crypto::public_key> &B, const crypto::public_key &D, const crypto::secret_key &r,
const crypto::secret_key &a, crypto::signature &sig) override;
bool open_tx(crypto::secret_key &tx_key) override;
void get_transaction_prefix_hash(const cryptonote::transaction_prefix& tx, crypto::hash& h) override;
+3
View File
@@ -95,6 +95,9 @@ const hardfork_t testnet_hard_forks[] = {
// version 10 Carrot - including treasury mint - starts from block 1100
{10, 1100, 0, 1739780005 },
// version 11
{11, 1200, 0, 1739880005 },
};
const size_t num_testnet_hard_forks = sizeof(testnet_hard_forks) / sizeof(testnet_hard_forks[0]);
const uint64_t testnet_hard_fork_version_1_till = ((uint64_t)-1);
+120
View File
@@ -26,6 +26,8 @@
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once
#include <cstdint> // For uint8_t, etc.
#include <cstring> // For memcpy
#include <string>
#include <vector>
@@ -74,4 +76,122 @@ namespace oracle {
}
}
};
class asset_type_counts_v2
{
public:
// Fields
std::map<uint32_t, uint64_t> asset_counts;
asset_type_counts_v2() noexcept
: asset_counts{}
{
}
uint64_t operator[](const uint32_t asset_type_id) const noexcept
{
if (asset_counts.count(asset_type_id) == 0)
return 0;
else
return asset_counts.at(asset_type_id);
}
bool add(const uint32_t asset_type_id, const uint64_t amount)
{
if (asset_counts.count(asset_type_id) == 0)
return false;
asset_counts[asset_type_id] += amount;
return true;
}
bool add_asset_type(const uint32_t asset_type_id)
{
if (asset_counts.count(asset_type_id) != 0)
return false;
asset_counts[asset_type_id] = 0;
return true;
}
std::map<uint32_t, uint64_t> get()
{
return asset_counts;
}
std::vector<uint32_t> get_asset_types() const noexcept
{
std::vector<uint32_t> asset_type_ids;
for (const auto& entry: asset_counts) {
asset_type_ids.push_back(entry.first);
}
return asset_type_ids;
}
bool has_asset_type(const uint32_t asset_type_id) const noexcept
{
return asset_counts.count(asset_type_id) == 1;
}
std::vector<uint8_t> serialize() const noexcept {
constexpr uint8_t version = 1; // Bump if format changes
uint32_t sz = static_cast<uint32_t>(asset_counts.size());
size_t total_size = sizeof(uint8_t) + sizeof(uint32_t) + (sz * (sizeof(uint32_t) + sizeof(uint64_t)));
std::vector<uint8_t> buf(total_size);
uint8_t* ptr = buf.data();
std::memcpy(ptr, &version, sizeof(uint8_t));
ptr += sizeof(uint8_t);
std::memcpy(ptr, &sz, sizeof(uint32_t));
ptr += sizeof(uint32_t);
for (const auto& p : asset_counts) { // Iterates in sorted order
std::memcpy(ptr, &p.first, sizeof(uint32_t));
ptr += sizeof(uint32_t);
std::memcpy(ptr, &p.second, sizeof(uint64_t));
ptr += sizeof(uint64_t);
}
return buf;
}
void deserialize(const uint8_t* data, size_t len) {
if (len < sizeof(uint32_t)) {
throw std::runtime_error("Invalid serialized data");
}
uint8_t version;
std::memcpy(&version, data, sizeof(uint8_t));
data += sizeof(uint8_t);
len -= sizeof(uint8_t);
if (version == 1) {
if (len < sizeof(uint32_t)) {
throw std::runtime_error("Invalid serialized data");
}
} else {
throw std::runtime_error("Unsupported version");
}
uint32_t sz;
std::memcpy(&sz, data, sizeof(uint32_t));
data += sizeof(uint32_t);
len -= sizeof(uint32_t);
if (len != sz * (sizeof(uint32_t) + sizeof(uint64_t))) {
throw std::runtime_error("Invalid serialized data length");
}
asset_counts.clear();
for (uint32_t i = 0; i < sz; ++i) {
uint32_t k;
uint64_t v;
std::memcpy(&k, data, sizeof(uint32_t));
data += sizeof(uint32_t);
std::memcpy(&v, data, sizeof(uint64_t));
data += sizeof(uint64_t);
asset_counts[k] = v; // Map handles sorting/uniquness
}
}
};
}
+1 -19
View File
@@ -881,12 +881,6 @@ namespace nodetool
if (m_nettype == cryptonote::MAINNET)
{
return {
"xwvz3ekocr3dkyxfkmgm2hvbpzx2ysqmaxgter7znnqrhoicygkfswid.onion:18083",
"4pixvbejrvihnkxmduo2agsnmc3rrulrqc7s3cbwwrep6h6hrzsibeqd.onion:18083",
"zbjkbsxc5munw3qusl7j2hpcmikhqocdf4pqhnhtpzw5nt5jrmofptid.onion:18083",
"qz43zul2x56jexzoqgkx2trzwcfnr6l3hbtfcfx54g4r3eahy3bssjyd.onion:18083",
"plowsof3t5hogddwabaeiyrno25efmzfxyro2vligremt7sxpsclfaid.onion:18083",
"plowsoffjexmxalw73tkjmf422gq6575fc7vicuu4javzn2ynnte6tyd.onion:18083",
};
}
return {};
@@ -894,10 +888,6 @@ namespace nodetool
if (m_nettype == cryptonote::MAINNET)
{
return {
"s3l6ke4ed3df466khuebb4poienoingwof7oxtbo6j4n56sghe3a.b32.i2p:18080",
"sel36x6fibfzujwvt4hf5gxolz6kd3jpvbjqg6o3ud2xtionyl2q.b32.i2p:18080",
"uqj3aphckqtjsitz7kxx5flqpwjlq5ppr3chazfued7xucv3nheq.b32.i2p:18080",
"vdmnehdjkpkg57nthgnjfuaqgku673r5bpbqg56ix6fyqoywgqrq.b32.i2p:18080",
};
}
return {};
@@ -2054,20 +2044,13 @@ namespace nodetool
return true;
static const std::vector<std::string> dns_urls = {
"blocklist.moneropulse.se"
, "blocklist.moneropulse.org"
, "blocklist.moneropulse.net"
, "blocklist.moneropulse.no"
, "blocklist.moneropulse.fr"
, "blocklist.moneropulse.de"
, "blocklist.moneropulse.ch"
};
std::vector<std::string> records;
if (!tools::dns_utils::load_txt_records_from_dns(records, dns_urls))
return true;
unsigned good = 0, bad = 0;
unsigned good = 0;
for (const auto& record : records)
{
std::vector<std::string> ips;
@@ -2091,7 +2074,6 @@ namespace nodetool
continue;
}
MWARNING("Invalid IP address or subnet from DNS blocklist: " << ip << " - " << parsed_addr.error());
++bad;
}
}
if (good > 0)
+5 -3
View File
@@ -1961,7 +1961,7 @@ namespace rct {
//ver RingCT simple
//assumes only post-rct style inputs (at least for max anonymity)
bool verRctSemanticsSimple(const rctSig & rv, const uint64_t amount_burnt)
bool verRctSemanticsSimple(const rctSig & rv, const uint64_t amount_burnt, const bool fee_paid_for_in_rollup)
{
try
{
@@ -2027,8 +2027,10 @@ namespace rct {
}
key sumOutpks = addKeys(masks);
DP(sumOutpks);
const key txnFeeKey = scalarmultH(d2h(rv.txnFee));
addKeys(sumOutpks, txnFeeKey, sumOutpks);
if (!fee_paid_for_in_rollup) {
const key txnFeeKey = scalarmultH(d2h(rv.txnFee));
addKeys(sumOutpks, txnFeeKey, sumOutpks);
}
const key txnAmountBurntKey = scalarmultH(d2h(amount_burnt));
addKeys(sumOutpks, txnAmountBurntKey, sumOutpks);
+4 -2
View File
@@ -202,9 +202,11 @@ namespace rct {
);
bool verRct(const rctSig & rv, bool semantics);
static inline bool verRct(const rctSig & rv) { return verRct(rv, true) && verRct(rv, false); }
bool verRctSemanticsSimple(const rctSig & rv, const uint64_t amount_burnt=0);
bool verRctSemanticsSimple(const rctSig & rv, const uint64_t amount_burnt=0, const bool fee_paid_for_in_rollup=false);
bool verRctNonSemanticsSimple(const rctSig & rv);
static inline bool verRctSimple(const rctSig & rv, const uint64_t amount_burnt=0) { return verRctSemanticsSimple(rv, amount_burnt) && verRctNonSemanticsSimple(rv); }
static inline bool verRctSimple(const rctSig & rv, const uint64_t amount_burnt=0, const bool fee_paid_for_in_rollup=false) {
return verRctSemanticsSimple(rv, amount_burnt, fee_paid_for_in_rollup) && verRctNonSemanticsSimple(rv);
}
xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev);
xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, hw::device &hwdev);
xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key & mask, hw::device &hwdev);
+68 -5
View File
@@ -28,6 +28,7 @@
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#include <regex>
#include <boost/preprocessor/stringize.hpp>
#include <boost/uuid/nil_generator.hpp>
#include <boost/filesystem.hpp>
@@ -1879,12 +1880,29 @@ namespace cryptonote
}
blobdata block_blob = t_serializable_object_to_blob(b);
crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(b.miner_tx);
const std::vector<crypto::public_key> additional_tx_pub_keys = cryptonote::get_additional_tx_pub_keys_from_extra(b.miner_tx);
if(tx_pub_key == crypto::null_pkey)
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = "Internal error: failed to create block template";
LOG_ERROR("Failed to get tx pub key in coinbase extra");
return false;
// Check for Carrot treasury payout
const uint8_t hf_version = m_core.get_blockchain_storage().get_current_hard_fork_version();
if (hf_version >= HF_VERSION_CARROT && b.miner_tx.vout.size() == 2) {
const auto treasury_payout_data = get_config(nettype()).TREASURY_SAL1_MINT_OUTPUT_DATA;
const bool treasury_payout_exists = (treasury_payout_data.count(height) == 1);
if (!treasury_payout_exists) {
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = "Internal error: failed to create block template (missing treasury payout)";
LOG_ERROR("Failed to get tx pub key in coinbase extra (missing treasury payout)");
return false;
}
tx_pub_key = additional_tx_pub_keys.back();
} else {
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = "Internal error: failed to create block template";
LOG_ERROR("Failed to get tx pub key in coinbase extra");
return false;
}
}
uint64_t next_height;
@@ -3045,6 +3063,52 @@ namespace cryptonote
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_tokens(const COMMAND_RPC_GET_TOKENS::request& req, COMMAND_RPC_GET_TOKENS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
PERF_TIMER(on_get_tokens);
std::map<std::string, cryptonote::token_metadata_t> tokens = m_core.get_blockchain_storage().get_db().get_tokens();
for (const auto &token: tokens) {
if (req.filter == "") {
// Match anything
res.tokens.push_back(token.first);
} else if (token.first.find(req.filter) != std::string::npos) {
// Match ticker
res.tokens.push_back(token.first);
} else if (token.second.token.type() == typeid(cryptonote::sal_token_t)) {
// Check SAL tokens
if (boost::get<cryptonote::sal_token_t>(token.second.token).name.find(req.filter) != std::string::npos) {
// Match name
res.tokens.push_back(token.first);
}
}
}
res.status = CORE_RPC_STATUS_OK;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_token_info(const COMMAND_RPC_GET_TOKEN_INFO::request& req, COMMAND_RPC_GET_TOKEN_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
PERF_TIMER(on_token_info);
std::map<std::string, cryptonote::token_metadata_t> tokens = m_core.get_blockchain_storage().get_db().get_tokens();
if (tokens.count(req.asset_type) == 0) {
res.status = "Token not found";
return true;
}
res.token = tokens[req.asset_type];
if (res.token.token.type() == typeid(cryptonote::erc_token_t)) {
res.token_type = TOKEN_TYPE_ERC20;
res.erc_token = boost::get<cryptonote::erc_token_t>(res.token.token);
} else if (res.token.token.type() == typeid(cryptonote::sal_token_t)) {
res.token_type = TOKEN_TYPE_SAL;
res.sal_token = boost::get<cryptonote::sal_token_t>(res.token.token);
}
res.status = CORE_RPC_STATUS_OK;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool core_rpc_server::on_get_yield_info(const COMMAND_RPC_GET_YIELD_INFO::request& req, COMMAND_RPC_GET_YIELD_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx)
{
CHECK_CORE_READY();
@@ -3108,7 +3172,6 @@ namespace cryptonote
CHECK_PAYMENT(req, res, COST_PER_FEE_ESTIMATE);
const uint8_t version = m_core.get_blockchain_storage().get_current_hard_fork_version();
m_core.get_blockchain_storage().get_dynamic_base_fee_estimate_2021_scaling(req.grace_blocks, res.fees);
res.fee = res.fees[0];
res.quantization_mask = Blockchain::get_fee_quantization_mask();
+4
View File
@@ -175,6 +175,8 @@ namespace cryptonote
MAP_JON_RPC_WE_IF("get_coinbase_tx_sum", on_get_coinbase_tx_sum, COMMAND_RPC_GET_COINBASE_TX_SUM, !m_restricted)
MAP_JON_RPC_WE("get_supply_info", on_get_supply_info, COMMAND_RPC_GET_SUPPLY_INFO)
MAP_JON_RPC_WE("get_yield_info", on_get_yield_info, COMMAND_RPC_GET_YIELD_INFO)
MAP_JON_RPC_WE("get_tokens", on_get_tokens, COMMAND_RPC_GET_TOKENS)
MAP_JON_RPC_WE("get_token_info", on_get_token_info, COMMAND_RPC_GET_TOKEN_INFO)
MAP_JON_RPC_WE("get_fee_estimate", on_get_base_fee_estimate, COMMAND_RPC_GET_BASE_FEE_ESTIMATE)
MAP_JON_RPC_WE_IF("get_alternate_chains",on_get_alternate_chains, COMMAND_RPC_GET_ALTERNATE_CHAINS, !m_restricted)
MAP_JON_RPC_WE_IF("relay_tx", on_relay_tx, COMMAND_RPC_RELAY_TX, !m_restricted)
@@ -254,6 +256,8 @@ namespace cryptonote
bool on_get_coinbase_tx_sum(const COMMAND_RPC_GET_COINBASE_TX_SUM::request& req, COMMAND_RPC_GET_COINBASE_TX_SUM::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_supply_info(const COMMAND_RPC_GET_SUPPLY_INFO::request& req, COMMAND_RPC_GET_SUPPLY_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_yield_info(const COMMAND_RPC_GET_YIELD_INFO::request& req, COMMAND_RPC_GET_YIELD_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_tokens(const COMMAND_RPC_GET_TOKENS::request& req, COMMAND_RPC_GET_TOKENS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_token_info(const COMMAND_RPC_GET_TOKEN_INFO::request& req, COMMAND_RPC_GET_TOKEN_INFO::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_base_fee_estimate(const COMMAND_RPC_GET_BASE_FEE_ESTIMATE::request& req, COMMAND_RPC_GET_BASE_FEE_ESTIMATE::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_get_alternate_chains(const COMMAND_RPC_GET_ALTERNATE_CHAINS::request& req, COMMAND_RPC_GET_ALTERNATE_CHAINS::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
bool on_relay_tx(const COMMAND_RPC_RELAY_TX::request& req, COMMAND_RPC_RELAY_TX::response& res, epee::json_rpc::error& error_resp, const connection_context *ctx = NULL);
+70
View File
@@ -1488,6 +1488,76 @@ namespace cryptonote
typedef epee::misc_utils::struct_init<response_t> response;
};
struct COMMAND_RPC_GET_TOKENS
{
struct request_t
{
std::string filter;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_OPT(filter, (std::string)"")
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
struct response_t
{
std::string status;
std::vector<std::string> tokens;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
KV_SERIALIZE(tokens)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
struct COMMAND_RPC_GET_TOKEN_INFO
{
struct request_t
{
std::string asset_type;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(asset_type)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
struct response_t
{
std::string status;
std::string asset_type;
uint8_t token_type; // erc20 or sal
cryptonote::token_metadata_t token;
cryptonote::erc_token_t erc_token;
cryptonote::sal_token_t sal_token;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
KV_SERIALIZE(token_type)
KV_SERIALIZE(token.version)
KV_SERIALIZE(token.asset_type)
if (token_type == TOKEN_TYPE_ERC20) {
KV_SERIALIZE(erc_token)
token.token = erc_token;
} else {
KV_SERIALIZE(sal_token)
token.token = sal_token;
}
END_KV_SERIALIZE_MAP()
//BEGIN_KV_SERIALIZE_MAP()
//KV_SERIALIZE(status)
//KV_SERIALIZE(token)
//KV_SERIALIZE(asset_type)
//KV_SERIALIZE(sal_token)
//KV_SERIALIZE(erc_token)
//END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
struct peer {
uint64_t id;
std::string host;
+203 -6
View File
@@ -277,9 +277,8 @@ 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 {
if (tx.type == cryptonote::transaction_type::STAKE &&
tx.version >= TRANSACTION_VERSION_CARROT)
{
if ((tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::CREATE_TOKEN) &&
(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);
@@ -290,6 +289,16 @@ void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::t
INSERT_INTO_JSON_OBJECT(dest, destination_asset_type, tx.destination_asset_type);
INSERT_INTO_JSON_OBJECT(dest, amount_slippage_limit, tx.amount_slippage_limit);
}
if (tx.version >= TRANSACTION_VERSION_ENABLE_TOKENS) {
if (tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
INSERT_INTO_JSON_OBJECT(dest, token_metadata, tx.token_metadata);
} else if (tx.type == cryptonote::transaction_type::TRANSFER) {
INSERT_INTO_JSON_OBJECT(dest, rollup_binding_tag, tx.rollup_binding_tag);
} else if (tx.type == cryptonote::transaction_type::ROLLUP) {
INSERT_INTO_JSON_OBJECT(dest, rollup_binding_tag, tx.rollup_binding_tag);
INSERT_INTO_JSON_OBJECT(dest, layer2_rollup_data, tx.layer2_rollup_data);
}
}
}
if (!tx.pruned)
{
@@ -326,9 +335,8 @@ 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 {
if (tx.type == cryptonote::transaction_type::STAKE &&
tx.version >= TRANSACTION_VERSION_CARROT)
{
if ((tx.type == cryptonote::transaction_type::STAKE || tx.type == cryptonote::transaction_type::CREATE_TOKEN) &&
(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);
@@ -339,6 +347,16 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::transaction& tx)
GET_FROM_JSON_OBJECT(val, tx.destination_asset_type, destination_asset_type);
GET_FROM_JSON_OBJECT(val, tx.amount_slippage_limit, amount_slippage_limit);
}
if (tx.version >= TRANSACTION_VERSION_ENABLE_TOKENS) {
if (tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
GET_FROM_JSON_OBJECT(val, tx.token_metadata, token_metadata);
} else if (tx.type == cryptonote::transaction_type::TRANSFER) {
GET_FROM_JSON_OBJECT(val, tx.rollup_binding_tag, rollup_binding_tag);
} else if (tx.type == cryptonote::transaction_type::ROLLUP) {
GET_FROM_JSON_OBJECT(val, tx.rollup_binding_tag, rollup_binding_tag);
GET_FROM_JSON_OBJECT(val, tx.layer2_rollup_data, layer2_rollup_data);
}
}
}
GET_FROM_JSON_OBJECT(val, tx.rct_signatures, ringct);
@@ -518,6 +536,141 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::txin_to_scripthash&
}
}
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::erc_token_t& erc_token)
{
dest.StartObject();
INSERT_INTO_JSON_OBJECT(dest, version, erc_token.version);
INSERT_INTO_JSON_OBJECT(dest, contract_address, erc_token.contract_address);
INSERT_INTO_JSON_OBJECT(dest, lockbox_address, erc_token.lockbox_address);
INSERT_INTO_JSON_OBJECT(dest, ticker, erc_token.ticker);
INSERT_INTO_JSON_OBJECT(dest, erc20_asset_id, erc_token.erc20_asset_id);
dest.EndObject();
}
void fromJsonValue(const rapidjson::Value& val, cryptonote::erc_token_t& erc_token)
{
if (!val.IsObject())
{
throw WRONG_TYPE("json object");
}
GET_FROM_JSON_OBJECT(val, erc_token.version, version);
GET_FROM_JSON_OBJECT(val, erc_token.contract_address, contract_address);
GET_FROM_JSON_OBJECT(val, erc_token.lockbox_address, lockbox_address);
GET_FROM_JSON_OBJECT(val, erc_token.ticker, ticker);
GET_FROM_JSON_OBJECT(val, erc_token.erc20_asset_id, erc20_asset_id);
}
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::sal_token_t& sal_token)
{
dest.StartObject();
INSERT_INTO_JSON_OBJECT(dest, version, sal_token.version);
INSERT_INTO_JSON_OBJECT(dest, supply, sal_token.supply);
INSERT_INTO_JSON_OBJECT(dest, size, sal_token.size);
INSERT_INTO_JSON_OBJECT(dest, name, sal_token.name);
INSERT_INTO_JSON_OBJECT(dest, url, sal_token.url);
INSERT_INTO_JSON_OBJECT(dest, signature, sal_token.signature);
dest.EndObject();
}
void fromJsonValue(const rapidjson::Value& val, cryptonote::sal_token_t& sal_token)
{
if (!val.IsObject())
{
throw WRONG_TYPE("json object");
}
GET_FROM_JSON_OBJECT(val, sal_token.version, version);
GET_FROM_JSON_OBJECT(val, sal_token.supply, supply);
GET_FROM_JSON_OBJECT(val, sal_token.size, size);
GET_FROM_JSON_OBJECT(val, sal_token.name, name);
GET_FROM_JSON_OBJECT(val, sal_token.url, url);
GET_FROM_JSON_OBJECT(val, sal_token.signature, signature);
}
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::token_v& token)
{
dest.StartObject();
struct add_token
{
using result_type = void;
rapidjson::Writer<epee::byte_stream>& dest;
void operator()(cryptonote::erc_token_t const& token) const
{
INSERT_INTO_JSON_OBJECT(dest, erc_token, token);
}
void operator()(cryptonote::sal_token_t const& token) const
{
INSERT_INTO_JSON_OBJECT(dest, sal_token, token);
}
};
boost::apply_visitor(add_token{dest}, token);
dest.EndObject();
}
void fromJsonValue(const rapidjson::Value& val, cryptonote::token_v& token)
{
if (!val.IsObject())
{
throw WRONG_TYPE("json object");
}
if (val.MemberCount() != 1)
{
throw MISSING_KEY("Invalid token object");
}
for (auto const& elem : val.GetObject())
{
if (elem.name == "erc_token")
{
cryptonote::erc_token_t tmpVal;
fromJsonValue(elem.value, tmpVal);
token = std::move(tmpVal);
}
else if (elem.name == "sal_token")
{
cryptonote::sal_token_t tmpVal;
fromJsonValue(elem.value, tmpVal);
token = std::move(tmpVal);
}
}
}
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::token_metadata_t& token_metadata)
{
dest.StartObject();
INSERT_INTO_JSON_OBJECT(dest, version, token_metadata.version);
INSERT_INTO_JSON_OBJECT(dest, asset_type, token_metadata.asset_type);
INSERT_INTO_JSON_OBJECT(dest, token, token_metadata.token);
dest.EndObject();
}
void fromJsonValue(const rapidjson::Value& val, cryptonote::token_metadata_t& token_metadata)
{
if (!val.IsObject())
{
throw WRONG_TYPE("json object");
}
GET_FROM_JSON_OBJECT(val, token_metadata.version, version);
GET_FROM_JSON_OBJECT(val, token_metadata.asset_type, asset_type);
GET_FROM_JSON_OBJECT(val, token_metadata.token, token);
}
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::protocol_tx_data_t& ptd)
{
dest.StartObject();
@@ -545,6 +698,50 @@ void fromJsonValue(const rapidjson::Value& val, cryptonote::protocol_tx_data_t&
GET_FROM_JSON_OBJECT(val, ptd.return_anchor_enc, return_anchor_enc);
}
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::layer2_rollup_tx_t& lrt)
{
dest.StartObject();
INSERT_INTO_JSON_OBJECT(dest, tx_prefix_hash, lrt.tx_prefix_hash);
INSERT_INTO_JSON_OBJECT(dest, first_key_image, lrt.first_key_image);
INSERT_INTO_JSON_OBJECT(dest, tx_fee, lrt.tx_fee);
dest.EndObject();
}
void fromJsonValue(const rapidjson::Value& val, cryptonote::layer2_rollup_tx_t& lrt)
{
if (!val.IsObject())
{
throw WRONG_TYPE("json object");
}
GET_FROM_JSON_OBJECT(val, lrt.tx_prefix_hash, tx_prefix_hash);
GET_FROM_JSON_OBJECT(val, lrt.first_key_image, first_key_image);
GET_FROM_JSON_OBJECT(val, lrt.tx_fee, tx_fee);
}
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::layer2_rollup_data_t& lrd)
{
dest.StartObject();
INSERT_INTO_JSON_OBJECT(dest, version, lrd.version);
INSERT_INTO_JSON_OBJECT(dest, txs, lrd.txs);
dest.EndObject();
}
void fromJsonValue(const rapidjson::Value& val, cryptonote::layer2_rollup_data_t& lrd)
{
if (!val.IsObject())
{
throw WRONG_TYPE("json object");
}
GET_FROM_JSON_OBJECT(val, lrd.version, version);
GET_FROM_JSON_OBJECT(val, lrd.txs, txs);
}
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::txin_to_key& txin)
{
dest.StartObject();
+18
View File
@@ -218,9 +218,27 @@ 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::erc_token_t& token);
void fromJsonValue(const rapidjson::Value& val, cryptonote::erc_token_t& token);
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::sal_token_t& token);
void fromJsonValue(const rapidjson::Value& val, cryptonote::sal_token_t& token);
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::token_v& token);
void fromJsonValue(const rapidjson::Value& val, cryptonote::token_v& token);
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::token_metadata_t& token);
void fromJsonValue(const rapidjson::Value& val, cryptonote::token_metadata_t& token);
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::layer2_rollup_tx_t& lrt);
void fromJsonValue(const rapidjson::Value& val, cryptonote::layer2_rollup_tx_t& lrt);
void toJsonValue(rapidjson::Writer<epee::byte_stream>& dest, const cryptonote::layer2_rollup_data_t& lrd);
void fromJsonValue(const rapidjson::Value& val, cryptonote::layer2_rollup_data_t& lrd);
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);
File diff suppressed because it is too large Load Diff
+5 -2
View File
@@ -194,6 +194,9 @@ namespace cryptonote
bool burn(const std::vector<std::string> &args);
bool convert(const std::vector<std::string> &args);
bool audit(const std::vector<std::string> &args);
bool create_token(const std::vector<std::string> &args);
bool get_tokens(const std::vector<std::string> &args);
bool token_info(const std::vector<std::string> &args);
bool stake(const std::vector<std::string> &args);
bool price_info(const std::vector<std::string> &args);
bool supply_info(const std::vector<std::string> &args);
@@ -205,8 +208,8 @@ namespace cryptonote
std::vector<cryptonote::tx_destination_entry> dsts, size_t num_splits
);
bool account(const std::vector<std::string> &args = std::vector<std::string>());
void print_accounts();
void print_accounts(const std::string& tag);
void print_accounts(bool show_all = false);
void print_accounts(const std::string& tag, bool show_all = false);
bool print_address(const std::vector<std::string> &args = std::vector<std::string>());
bool print_integrated_address(const std::vector<std::string> &args = std::vector<std::string>());
bool address_book(const std::vector<std::string> &args = std::vector<std::string>());
+2 -2
View File
@@ -1,7 +1,7 @@
#define DEF_SALVIUM_VERSION_TAG "@VERSIONTAG@"
#define DEF_SALVIUM_VERSION "1.0.0"
#define DEF_SALVIUM_VERSION "1.1.0-rc4"
#define DEF_MONERO_VERSION_TAG "release"
#define DEF_MONERO_VERSION "0.18.3.4"
#define DEF_MONERO_VERSION "0.18.4.0"
#define DEF_MONERO_RELEASE_NAME "One"
#define DEF_MONERO_VERSION_FULL DEF_SALVIUM_VERSION "-" DEF_SALVIUM_VERSION_TAG ", based on Monero " DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG
#define DEF_MONERO_VERSION_IS_RELEASE @VERSION_IS_RELEASE@
+7
View File
@@ -166,9 +166,16 @@ uint64_t PendingTransactionImpl::amount() const
{
uint64_t result = 0;
for (const auto &ptx : m_pending_tx) {
if (ptx.tx.type == cryptonote::transaction_type::AUDIT ||
ptx.tx.type == cryptonote::transaction_type::BURN ||
ptx.tx.type == cryptonote::transaction_type::STAKE ||
ptx.tx.type == cryptonote::transaction_type::CREATE_TOKEN) {
result += ptx.tx.amount_burnt;
} else {
for (const auto &dest : ptx.dests) {
result += dest.amount;
}
}
}
return result;
}
+5 -5
View File
@@ -44,16 +44,16 @@ SubaddressAccountImpl::SubaddressAccountImpl(WalletImpl *wallet)
void SubaddressAccountImpl::addRow(const std::string &label)
{
m_wallet->m_wallet->add_subaddress_account(label);
refresh();
refresh("SAL1");
}
void SubaddressAccountImpl::setLabel(uint32_t accountIndex, const std::string &label)
{
m_wallet->m_wallet->set_subaddress_label({accountIndex, 0}, label);
refresh();
refresh("SAL1");
}
void SubaddressAccountImpl::refresh()
void SubaddressAccountImpl::refresh(const std::string &asset_type)
{
LOG_PRINT_L2("Refreshing subaddress account");
@@ -64,8 +64,8 @@ void SubaddressAccountImpl::refresh()
i,
m_wallet->m_wallet->get_subaddress_as_str({i,0}),
m_wallet->m_wallet->get_subaddress_label({i,0}),
cryptonote::print_money(m_wallet->m_wallet->balance(i, "SAL", false)),
cryptonote::print_money(m_wallet->m_wallet->unlocked_balance(i, "SAL", false)),
cryptonote::print_money(m_wallet->m_wallet->balance(i, asset_type, false)),
cryptonote::print_money(m_wallet->m_wallet->unlocked_balance(i, asset_type, false)),
cryptonote::print_money(m_wallet->m_wallet->balance(i, "SAL1", false)),
cryptonote::print_money(m_wallet->m_wallet->unlocked_balance(i, "SAL1", false))
));
+1 -1
View File
@@ -40,7 +40,7 @@ public:
~SubaddressAccountImpl();
// Fetches addresses from Wallet2
void refresh();
void refresh(const std::string &asset_type);
std::vector<SubaddressAccountRow*> getAll() const;
void addRow(const std::string &label);
void setLabel(uint32_t accountIndex, const std::string &label);
+19 -5
View File
@@ -175,7 +175,13 @@ void TransactionHistoryImpl::refresh()
uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known
uint64_t fee = pd.m_amount_in - pd.m_amount_out;
uint64_t amount = pd.m_amount_in - change - fee;
if (pd.m_tx.type == cryptonote::transaction_type::AUDIT ||
pd.m_tx.type == cryptonote::transaction_type::BURN ||
pd.m_tx.type == cryptonote::transaction_type::STAKE) {
amount = pd.m_tx.amount_burnt;
if (fee > amount) fee -= amount;
}
std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id);
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
@@ -184,7 +190,7 @@ void TransactionHistoryImpl::refresh()
TransactionInfoImpl * ti = new TransactionInfoImpl();
ti->m_paymentid = payment_id;
ti->m_amount = pd.m_amount_in - change - fee;
ti->m_amount = amount;
ti->m_fee = fee;
ti->m_direction = TransactionInfo::Direction_Out;
ti->m_hash = string_tools::pod_to_hex(hash);
@@ -212,8 +218,16 @@ void TransactionHistoryImpl::refresh()
for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments_out.begin(); i != upayments_out.end(); ++i) {
const tools::wallet2::unconfirmed_transfer_details &pd = i->second;
const crypto::hash &hash = i->first;
uint64_t amount = pd.m_amount_in;
uint64_t fee = amount - pd.m_amount_out;
uint64_t change = pd.m_change == (uint64_t)-1 ? 0 : pd.m_change; // change may not be known
uint64_t fee = pd.m_amount_in - pd.m_amount_out;
uint64_t amount = pd.m_amount_in - change - fee;
if (pd.m_tx.type == cryptonote::transaction_type::AUDIT ||
pd.m_tx.type == cryptonote::transaction_type::BURN ||
pd.m_tx.type == cryptonote::transaction_type::STAKE) {
amount = pd.m_tx.amount_burnt;
if (fee > amount) fee -= amount;
}
std::string payment_id = string_tools::pod_to_hex(i->second.m_payment_id);
if (payment_id.substr(16).find_first_not_of('0') == std::string::npos)
payment_id = payment_id.substr(0,16);
@@ -221,7 +235,7 @@ void TransactionHistoryImpl::refresh()
TransactionInfoImpl * ti = new TransactionInfoImpl();
ti->m_paymentid = payment_id;
ti->m_amount = amount - pd.m_change - fee;
ti->m_amount = amount;
ti->m_fee = fee;
ti->m_direction = TransactionInfo::Direction_Out;
ti->m_failed = is_failed;
+153 -3
View File
@@ -893,24 +893,48 @@ std::string WalletImpl::integratedAddress(const std::string &payment_id, bool ca
std::string WalletImpl::secretViewKey() const
{
uint32_t hf_version = m_wallet->estimate_current_hard_fork();
if (hf_version >= HF_VERSION_CARROT)
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_view_incoming)));
else
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().m_view_secret_key)));
}
std::string WalletImpl::publicViewKey() const
{
uint32_t hf_version = m_wallet->estimate_current_hard_fork();
if (hf_version >= HF_VERSION_CARROT)
return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_carrot_account_address.m_view_public_key);
else
return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_view_public_key);
}
std::string WalletImpl::secretSpendKey() const
{
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().m_spend_secret_key)));
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().m_spend_secret_key)));
}
std::string WalletImpl::publicSpendKey() const
{
uint32_t hf_version = m_wallet->estimate_current_hard_fork();
if (hf_version >= HF_VERSION_CARROT)
return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_carrot_account_address.m_spend_public_key);
else
return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_spend_public_key);
}
std::vector<std::string> WalletImpl::carrotKeys() const
{
return {
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().s_master))),
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_prove_spend))),
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().s_view_balance))),
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_view_incoming))),
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_generate_image))),
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().s_generate_address)))
};
}
std::string WalletImpl::publicMultisigSignerKey() const
{
try {
@@ -921,6 +945,26 @@ std::string WalletImpl::publicMultisigSignerKey() const
}
}
std::string WalletImpl::secretViewBalance() const
{
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().s_view_balance)));
}
std::string WalletImpl::secretProveSpend() const
{
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_prove_spend)));
}
std::string WalletImpl::secretGenerateAddress() const
{
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().s_generate_address)));
}
std::string WalletImpl::secretGenerateImage() const
{
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_generate_image)));
}
std::string WalletImpl::path() const
{
return m_wallet->path();
@@ -1599,6 +1643,107 @@ PendingTransaction *WalletImpl::createStakeTransaction(uint64_t amount, uint32_t
return createTransactionMultDest(Monero::transaction_type::STAKE, std::vector<string> {dst_addr}, payment_id, amount ? (std::vector<uint64_t> {amount}) : (optional<std::vector<uint64_t>>()), mixin_count, asset_type, is_return, priority, subaddr_account, subaddr_indices);
}
PendingTransaction *WalletImpl::createCreateTokenTransaction(const std::string &asset_type, uint64_t supply, const std::string &metadata, const std::string &name, uint32_t size, std::string hash, std::string url, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices){
PendingTransactionImpl * transaction = new PendingTransactionImpl(*this);
std::string token_metadata_hex;
crypto::hash hash_signature{};
if (!hash.empty()) {
if (!epee::string_tools::hex_to_pod(hash, hash_signature)) {
if (metadata.empty()){
setStatusError(tr("Invalid hash format."));
return transaction;
}
}
}
if (metadata.empty()) {
std::string json = (boost::format("{\"name\":\"%s\",\"size\":%d,\"hash\":\"%s\",\"url\":\"%s\"}") % name % size % hash % url).str();
token_metadata_hex = epee::string_tools::buff_to_hex_nodelimer(json);
} else {
token_metadata_hex = metadata;
}
cryptonote::sal_token_t sal_token;
sal_token.version = 1;
sal_token.supply = supply;
// todo: if not provided, use token_metadata_hex
sal_token.size = size;
sal_token.name = name;
sal_token.url = url;
sal_token.signature = hash_signature;
cryptonote::token_metadata_t token_meta;
token_meta.version = 1;
token_meta.asset_type = asset_type;
token_meta.token = sal_token;
clearStatus();
try {
// check if token already exists before attempting to create
std::vector<std::string> existing_tokens;
if (m_wallet->get_tokens(asset_type, existing_tokens) && !existing_tokens.empty()) {
setStatusError(tr("Token with ticker '") + asset_type + tr("' already exists"));
statusWithErrorString(transaction->m_status, transaction->m_errorString);
return transaction;
}
transaction->m_pending_tx = m_wallet->create_token(asset_type, supply, token_metadata_hex, subaddr_account, subaddr_indices
);
pendingTxPostProcess(transaction);
} catch (const tools::error::daemon_busy&) {
setStatusError(tr("daemon is busy. Please try again later."));
} catch (const tools::error::no_connection_to_daemon&) {
setStatusError(tr("no connection to daemon. Please make sure daemon is running."));
} catch (const tools::error::wallet_rpc_error& e) {
setStatusError(tr("RPC error: ") + e.to_string());
} catch (const tools::error::get_outs_error &e) {
setStatusError((boost::format(tr("failed to get outputs to mix: %s")) % e.what()).str());
} catch (const tools::error::not_enough_unlocked_money& e) {
std::ostringstream writer;
writer << boost::format(tr("not enough SAL1 to create token, available only %s, required %s")) %
print_money(e.available()) %
print_money(e.tx_amount());
setStatusError(writer.str());
} catch (const tools::error::not_enough_money& e) {
std::ostringstream writer;
writer << boost::format(tr("not enough SAL1 to create token, overall balance only %s, required %s")) %
print_money(e.available()) %
print_money(e.tx_amount());
setStatusError(writer.str());
} catch (const tools::error::tx_not_possible& e) {
std::ostringstream writer;
writer << boost::format(tr("not enough SAL1 to create token, available only %s, required %s = %s (locked) + %s (fee)")) %
print_money(e.available()) %
print_money(e.tx_amount() + e.fee()) %
print_money(e.tx_amount()) %
print_money(e.fee());
setStatusError(writer.str());
} catch (const tools::error::tx_not_constructed&) {
setStatusError(tr("transaction for token creation was not constructed"));
} catch (const tools::error::tx_rejected& e) {
std::ostringstream writer;
writer << (boost::format(tr("create token transaction %s was rejected by daemon with status: ")) % get_transaction_hash(e.tx())) << e.status();
std::string reason = e.reason();
if (!reason.empty())
writer << tr(". Reason: ") << reason;
setStatusError(writer.str());
} catch (const tools::error::tx_sum_overflow& e) {
setStatusError(e.what());
} catch (const tools::error::tx_too_big& e) {
setStatusError(tr("token creation transaction is too large"));
} catch (const tools::error::transfer_error& e) {
setStatusError(string(tr("token creation error: ")) + e.what());
} catch (const tools::error::wallet_internal_error& e) {
setStatusError(string(tr("internal error: ")) + e.what());
} catch (const std::exception& e) {
setStatusError(string(tr("unexpected error: ")) + e.what());
} catch (...) {
setStatusError(tr("unknown error"));
}
statusWithErrorString(transaction->m_status, transaction->m_errorString);
return transaction;
}
PendingTransaction *WalletImpl::createAuditTransaction(
uint32_t mixin_count,
PendingTransaction::Priority priority,
@@ -1739,8 +1884,8 @@ PendingTransaction *WalletImpl::createTransactionMultDest(const Monero::transact
const auto result = m_wallet->create_transactions_all(0,
converted_tx_type,
asset_type,
m_wallet->get_subaddress({subaddr_account, subaddr_index}),
(subaddr_index > 0),
info.address,
info.is_subaddress,
1,
fake_outs_count,
0 /* unlock_time */,
@@ -2888,4 +3033,9 @@ YieldInfo * WalletImpl::getYieldInfo()
return yi;
}
std::vector<std::string> WalletImpl::getAssetTypes()
{
return m_wallet->list_asset_types();
}
} // namespace
+16
View File
@@ -99,7 +99,12 @@ public:
std::string publicViewKey() const override;
std::string secretSpendKey() const override;
std::string publicSpendKey() const override;
std::vector<std::string> carrotKeys() const override;
std::string publicMultisigSignerKey() const override;
std::string secretViewBalance() const override;
std::string secretProveSpend() const override;
std::string secretGenerateAddress() const override;
std::string secretGenerateImage() const override;
std::string path() const override;
void stop() override;
bool store(const std::string &path) override;
@@ -158,6 +163,15 @@ public:
PendingTransaction::Priority priority = PendingTransaction::Priority_Low,
uint32_t subaddr_account = 0,
std::set<uint32_t> subaddr_indices = {}) override;
PendingTransaction * createCreateTokenTransaction(const std::string &asset_type,
uint64_t supply,
const std::string &metadata,
const std::string &name,
uint32_t size,
std::string hash,
std::string url,
uint32_t subaddr_account,
std::set<uint32_t> subaddr_indices = {}) override;
PendingTransaction * createAuditTransaction(uint32_t mixin_count,
PendingTransaction::Priority priority = PendingTransaction::Priority_Low,
uint32_t subaddr_account = 0,
@@ -247,6 +261,8 @@ public:
YieldInfo * getYieldInfo() override;
virtual std::vector<std::string> getAssetTypes() override;
private:
void clearStatus() const;
void setStatusError(const std::string& message) const;
+68 -2
View File
@@ -59,7 +59,9 @@ enum transaction_type : uint8_t {
STAKE = 6,
RETURN = 7,
AUDIT = 8,
MAX = 8
CREATE_TOKEN = 9,
ROLLUP = 10,
MAX = 10
};
namespace Utils {
@@ -366,7 +368,7 @@ struct SubaddressAccount
virtual std::vector<SubaddressAccountRow*> getAll() const = 0;
virtual void addRow(const std::string &label) = 0;
virtual void setLabel(uint32_t accountIndex, const std::string &label) = 0;
virtual void refresh() = 0;
virtual void refresh(const std::string &asset_type) = 0;
};
struct MultisigState {
@@ -559,12 +561,48 @@ struct Wallet
*/
virtual std::string publicSpendKey() const = 0;
/*!
* \brief allCarrotKeys - returns all Carrot keys
* [0] - s_master
* [1] - k_prove_spend
* [2] - s_view_balance
* [3] - k_view_incoming
* [4] - k_generate_image
* [5] - s_generate_address
* \return - vector of all Carrot keys
*/
virtual std::vector<std::string> carrotKeys() const = 0;
/*!
* \brief publicMultisigSignerKey - returns public signer key
* \return - public multisignature signer key or empty string if wallet is not multisig
*/
virtual std::string publicMultisigSignerKey() const = 0;
/*!
* \brief secretViewBalance - returns Carrot "view balance" secret
* \return - Carrot s_vb
*/
virtual std::string secretViewBalance() const = 0;
/*!
* \brief secretProveSpend - returns Carrot "prove spend" secret
* \return - Carrot secret k_ps
*/
virtual std::string secretProveSpend() const = 0;
/*!
* \brief secretGenerateAddress - returns Carrot "generate address" secret
* \return - Carrot secret s_ga
*/
virtual std::string secretGenerateAddress() const = 0;
/*!
* \brief secretGenerateImage - returns Carrot "generate key image" secret
* \return - Carrot secret k_gi
*/
virtual std::string secretGenerateImage() const = 0;
/*!
* \brief stop - interrupts wallet refresh() loop once (doesn't stop background refresh thread)
*/
@@ -897,6 +935,31 @@ struct Wallet
uint32_t subaddr_account = 0,
std::set<uint32_t> subaddr_indices = {}) = 0;
/*!
* \brief createCreateTokenTransaction creates token creation transaction.
* \param asset_type the type of asset to create
* \param supply token supply amount
* \param metadata
* \param name
* \param size
* \param hash
* \param url
* \param subaddr_account subaddress account from which the input funds are taken
* \param subaddr_indices set of subaddress indices to use for transfer or sweeping. if set empty, all are chosen when sweeping, and one or more are automatically chosen when transferring. after execution, returns the set of actually used indices
* \return PendingTransaction object. caller is responsible to check PendingTransaction::status()
* after object returned
*/
virtual PendingTransaction * createCreateTokenTransaction(const std::string &asset_type,
uint64_t supply,
const std::string &metadata,
const std::string &name,
uint32_t size,
const std::string hash,
const std::string url,
uint32_t subaddr_account = 0,
std::set<uint32_t> subaddr_indices = {}) = 0;
/*!
* \brief createAuditTransaction creates audit transaction.
* \param mixin_count mixin count. if 0 passed, wallet will use default value
@@ -1224,6 +1287,9 @@ struct Wallet
//! get yield information
virtual YieldInfo * getYieldInfo() = 0;
//! get asset types for which the wallet has/had outputs
virtual std::vector<std::string> getAssetTypes() = 0;
};
/**
+8 -1
View File
@@ -973,7 +973,14 @@ std::optional<crypto::key_image> try_derive_enote_key_image(
// x = k_s + k^j_subext + k^g_o
rct::key x;
if (enote_scan_info.is_carrot) {
return acc.derive_key_image(enote_scan_info.address_spend_pubkey,
// if we don't have the s_master key, derive view-only key image
if (acc.get_keys().s_master == crypto::null_skey) {
return acc.derive_key_image_view_only(enote_scan_info.address_spend_pubkey,
enote_scan_info.sender_extension_g,
enote_scan_info.sender_extension_t,
rct::rct2pk(onetime_address));
}
return acc.derive_key_image(enote_scan_info.address_spend_pubkey,
enote_scan_info.sender_extension_g,
enote_scan_info.sender_extension_t,
rct::rct2pk(onetime_address));
+298 -89
View File
@@ -52,6 +52,8 @@
//third party headers
//standard headers
#include <iterator>
#include <set>
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.tx_builder"
@@ -71,11 +73,12 @@ static constexpr T div_ceil(T dividend, T divisor)
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
static bool is_transfer_usable_for_input_selection(const wallet2::transfer_details &td,
const std::uint32_t from_account,
const std::set<std::uint32_t> from_subaddresses,
const rct::xmr_amount ignore_above,
const rct::xmr_amount ignore_below,
const uint64_t top_block_index)
const std::uint32_t from_account,
const std::set<std::uint32_t> from_subaddresses,
const rct::xmr_amount ignore_above,
const rct::xmr_amount ignore_below,
const uint64_t top_block_index,
const std::string asset_type)
{
/**
* This additional check appears to be for fcmp++.
@@ -91,13 +94,13 @@ static bool is_transfer_usable_for_input_selection(const wallet2::transfer_detai
&& td.m_key_image_known
&& !td.m_key_image_partial
&& !td.m_frozen
&& (top_block_index >= td.m_block_height + blocks_locked_for)
&& (top_block_index +1 >= td.m_block_height + blocks_locked_for)
// && last_locked_block_index <= top_block_index
&& td.m_subaddr_index.major == from_account
&& (from_subaddresses.empty() || from_subaddresses.count(td.m_subaddr_index.minor) == 1)
&& td.amount() >= ignore_below
&& td.amount() <= ignore_above
&& td.asset_type == "SAL1"
&& td.asset_type == asset_type
;
}
//-------------------------------------------------------------------------------------------------------------------
@@ -118,7 +121,8 @@ static bool build_payment_proposals(std::vector<carrot::CarrotPaymentProposalV1>
.proposal = carrot::CarrotPaymentProposalSelfSendV1{
.destination_address_spend_pubkey = tx_dest_entry.addr.m_spend_public_key,
.amount = tx_dest_entry.amount,
.enote_type = carrot::CarrotEnoteType::PAYMENT
.enote_type = carrot::CarrotEnoteType::PAYMENT,
.asset_type = tx_dest_entry.asset_type
},
.subaddr_index = {subaddr_index, carrot::AddressDeriveType::Carrot, false},
});
@@ -148,9 +152,10 @@ static cryptonote::tx_destination_entry make_tx_destination_entry(
const carrot::CarrotPaymentProposalV1 &payment_proposal)
{
cryptonote::tx_destination_entry dest = cryptonote::tx_destination_entry(payment_proposal.amount,
{payment_proposal.destination.address_spend_pubkey, payment_proposal.destination.address_view_pubkey},
{payment_proposal.destination.address_spend_pubkey, payment_proposal.destination.address_view_pubkey, /*m_is_carrot*/true},
payment_proposal.destination.is_subaddress);
dest.is_integrated = payment_proposal.destination.payment_id != carrot::null_payment_id;
dest.asset_type = payment_proposal.asset_type;
return dest;
}
//-------------------------------------------------------------------------------------------------------------------
@@ -165,9 +170,11 @@ static cryptonote::tx_destination_entry make_tx_destination_entry(
address_view_pubkey),
"make_tx_destination_entry: view-key multiplication failed");
return cryptonote::tx_destination_entry(payment_proposal.proposal.amount,
{payment_proposal.proposal.destination_address_spend_pubkey, address_view_pubkey},
payment_proposal.subaddr_index.index.is_subaddress());
cryptonote::tx_destination_entry dest = cryptonote::tx_destination_entry(payment_proposal.proposal.amount,
{payment_proposal.proposal.destination_address_spend_pubkey, address_view_pubkey, /*m_is_carrot*/true},
payment_proposal.subaddr_index.index.is_subaddress());
dest.asset_type = payment_proposal.proposal.asset_type;
return dest;
}
//-------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------
@@ -258,6 +265,7 @@ carrot::select_inputs_func_t make_wallet2_single_transfer_input_selector(
const std::uint64_t top_block_index,
const bool allow_carrot_external_inputs_in_normal_transfers,
const bool allow_pre_carrot_inputs_in_normal_transfers,
const std::string &asset_type,
std::set<size_t> &selected_transfer_indices_out)
{
// Collect transfer_container into a `std::vector<carrot::InputCandidate>` for usable inputs
@@ -269,11 +277,12 @@ carrot::select_inputs_func_t make_wallet2_single_transfer_input_selector(
{
const wallet2::transfer_details &td = transfers.at(i);
if (is_transfer_usable_for_input_selection(td,
from_account,
from_subaddresses,
ignore_above,
ignore_below,
top_block_index))
from_account,
from_subaddresses,
ignore_above,
ignore_below,
top_block_index,
asset_type))
{
input_candidates.push_back(carrot::InputCandidate{
.core = carrot::CarrotSelectedInput{
@@ -293,6 +302,7 @@ carrot::select_inputs_func_t make_wallet2_single_transfer_input_selector(
input_candidates_transfer_indices = std::move(input_candidates_transfer_indices),
allow_carrot_external_inputs_in_normal_transfers,
allow_pre_carrot_inputs_in_normal_transfers,
asset_type,
&selected_transfer_indices_out
](
const boost::multiprecision::uint128_t &nominal_output_sum,
@@ -310,6 +320,9 @@ carrot::select_inputs_func_t make_wallet2_single_transfer_input_selector(
flags |= carrot::InputSelectionFlags::ALLOW_EXTERNAL_INPUTS_IN_NORMAL_TRANSFERS;
if (allow_pre_carrot_inputs_in_normal_transfers)
flags |= carrot::InputSelectionFlags::ALLOW_PRE_CARROT_INPUTS_IN_NORMAL_TRANSFERS;
// for token transfers; fee is paid separately in SAL1 via rollup tx
if (cryptonote::is_asset_type_token(asset_type))
flags |= carrot::InputSelectionFlags::IS_TOKEN_TRANSFER;
// Make inner input selection functor
std::set<size_t> selected_input_indices;
@@ -340,13 +353,27 @@ std::vector<cryptonote::tx_source_entry> get_sources(
wallet2 &w
) {
// get decoys
const size_t fake_outputs_count = 15;
size_t fake_outputs_count = (cryptonote::is_asset_type_token(source_asset)) ? 0 : 15;
// check here!
// for tokens, check if there are enough decoys in the chain - if so ring_size= 16
if (cryptonote::is_asset_type_token(source_asset))
{
uint64_t rct_start_height = 0;
std::vector<uint64_t> distribution;
uint64_t num_spendable_global_outs = 0;
if (w.get_rct_distribution(false, source_asset, rct_start_height, distribution, num_spendable_global_outs)
&& num_spendable_global_outs > 15)
{
fake_outputs_count = 15;
}
}
std::vector<std::vector<tools::wallet2::get_outs_entry>> outs;
std::unordered_set<crypto::public_key> valid_public_keys_cache;
w.get_outs(outs, selected_transfers, fake_outputs_count, true, valid_public_keys_cache); // may throw
w.get_outs(outs, transfers, selected_transfers, fake_outputs_count, true, valid_public_keys_cache); // may throw
LOG_PRINT_L2("preparing outputs");
size_t i = 0, out_index = 0;
size_t out_index = 0;
std::vector<cryptonote::tx_source_entry> sources;
for(size_t idx: selected_transfers)
{
@@ -387,7 +414,6 @@ std::vector<cryptonote::tx_source_entry> get_sources(
oe.second.mask = std::get<2>(outs[out_index][n]);
src.outputs.push_back(oe);
}
++i;
//paste real transaction to the random index
auto it_to_replace = std::find_if(src.outputs.begin(), src.outputs.end(), [&](const tx_output_entry& a)
@@ -433,6 +459,134 @@ std::vector<cryptonote::tx_source_entry> get_sources(
return sources;
}
//-------------------------------------------------------------------------------------------------------------------
std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposals_wallet2_createtoken(
wallet2 &w,
const cryptonote::token_metadata_t &token,
const cryptonote::tx_destination_entry &de,
const rct::xmr_amount fee_per_weight,
const rct::xmr_amount fee_quantization_mask,
const uint32_t subaddr_account,
const std::set<uint32_t> &subaddr_indices,
const std::uint64_t top_block_index)
{
wallet2::transfer_container unused_transfers;
w.get_transfers(unused_transfers, "SAL1");
std::vector<carrot::CarrotTransactionProposalV1> tx_proposals;
tx_proposals.reserve(1 / (carrot::CARROT_MAX_TX_OUTPUTS - 1) + 1);
const crypto::public_key change_address_spend_pubkey
= find_change_address_spend_pubkey(w.get_account().get_subaddress_map_ref(), subaddr_account);
std::vector<cryptonote::tx_destination_entry> dsts = {de};
while (!dsts.empty())
{
const std::size_t num_dsts_to_complete = std::min<std::size_t>(dsts.size(), carrot::CARROT_MAX_TX_OUTPUTS - 1);
// build payment proposals and subtractable info from last `num_dsts_to_complete` dsts
std::vector<carrot::CarrotPaymentProposalV1> normal_payment_proposals;
std::vector<carrot::CarrotPaymentProposalVerifiableSelfSendV1> selfsend_payment_proposals;
std::set<std::size_t> subtractable_normal_payment_proposals;
std::set<std::size_t> subtractable_selfsend_payment_proposals;
for (size_t i = 0; i < num_dsts_to_complete && !dsts.empty(); ++i)
{
const cryptonote::tx_destination_entry &dst = dsts.back();
const bool is_selfsend = build_payment_proposals(normal_payment_proposals,
selfsend_payment_proposals,
dst,
w.get_account().get_subaddress_map_cn());
dsts.pop_back();
}
// make input selector
std::set<size_t> selected_transfer_indices;
carrot::select_inputs_func_t select_inputs = make_wallet2_single_transfer_input_selector(
unused_transfers,
subaddr_account,
subaddr_indices,
w.ignore_outputs_above(),
w.ignore_outputs_below(),
top_block_index,
true, //allow_carrot_external_inputs_in_normal_transfers
true, //allow_pre_carrot_inputs_in_normal_transfers
std::string("SAL1"),
selected_transfer_indices);
// make proposal
carrot::CarrotTransactionProposalV1 tx_proposal;
carrot::make_carrot_transaction_proposal_v1_transfer(
normal_payment_proposals,
selfsend_payment_proposals,
fee_per_weight,
fee_quantization_mask,
{},
cryptonote::transaction_type::CREATE_TOKEN,
std::move(select_inputs),
change_address_spend_pubkey,
{{subaddr_account, 0}, carrot::AddressDeriveType::Carrot, false},
subtractable_normal_payment_proposals,
subtractable_selfsend_payment_proposals,
"SAL1",
tx_proposal);
// populate the sources
std::vector<size_t> selected_transfer_indices_sorted;
for (const auto &ki: tx_proposal.key_images_sorted) {
// Scan the transfer_container instead of all of the transfers in the wallet
selected_transfer_indices_sorted.push_back(w.get_transfer_details_from_container(ki, unused_transfers));
}
tx_proposal.sources = get_sources(unused_transfers, selected_transfer_indices_sorted, "SAL1", w);
// update `unused_transfers` for next proposal by removing selected transfers
tools::for_all_in_vector_erase_no_preserve_order_if(unused_transfers,
[&tx_proposal](const wallet2::transfer_details &td) -> bool {
const auto &used_kis = tx_proposal.key_images_sorted;
const auto ki_it = std::find(used_kis.cbegin(), used_kis.cend(), td.m_key_image);
return ki_it != used_kis.cend();
}
);
tx_proposal.token = token;
tx_proposals.push_back(std::move(tx_proposal));
}
return tx_proposals;
}
//-------------------------------------------------------------------------------------------------------------------
std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposals_wallet2_createtoken(
wallet2 &w,
const cryptonote::token_metadata_t &token,
const cryptonote::tx_destination_entry &de,
const std::uint32_t priority,
const std::uint32_t subaddr_account,
const std::set<uint32_t> &subaddr_indices)
{
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_createtoken: not using per-byte base fee");
const rct::xmr_amount fee_per_weight = w.get_base_fee(priority);
MDEBUG("fee_per_weight = " << fee_per_weight << ", from priority = " << priority);
const rct::xmr_amount fee_quantization_mask = w.get_fee_quantization_mask();
MDEBUG("fee_quantization_mask = " << fee_quantization_mask << ", from priority = " << priority);
const std::uint64_t current_chain_height = w.get_blockchain_current_height();
CHECK_AND_ASSERT_THROW_MES(current_chain_height > 0,
"make_carrot_transaction_proposals_wallet2_createtoken: chain height is 0, there is no top block");
const std::uint64_t top_block_index = current_chain_height - 1;
return make_carrot_transaction_proposals_wallet2_createtoken(
w,
token,
de,
fee_per_weight,
fee_quantization_mask,
subaddr_account,
subaddr_indices,
top_block_index);
}
//-------------------------------------------------------------------------------------------------------------------
std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposals_wallet2_transfer(
wallet2 &w,
std::vector<cryptonote::tx_destination_entry> dsts,
@@ -445,8 +599,16 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
wallet2::unique_index_container subtract_fee_from_outputs,
const std::uint64_t top_block_index)
{
// Make sure all destinations have the SAME asset_type
std::string asset_type = dsts[0].asset_type;
if (dsts.size() > 1) {
for (size_t i=0; i<dsts.size(); ++i) {
CHECK_AND_ASSERT_THROW_MES(dsts[i].asset_type == asset_type, "Mixed asset_types in transaction outputs is forbidden");
}
}
wallet2::transfer_container unused_transfers;
w.get_transfers(unused_transfers);
w.get_transfers(unused_transfers, asset_type);
std::vector<carrot::CarrotTransactionProposalV1> tx_proposals;
tx_proposals.reserve(dsts.size() / (carrot::CARROT_MAX_TX_OUTPUTS - 1) + 1);
@@ -491,30 +653,33 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
top_block_index,
/*allow_carrot_external_inputs_in_normal_transfers=*/true,
/*allow_pre_carrot_inputs_in_normal_transfers=*/true,
asset_type,
selected_transfer_indices);
// make proposal
carrot::CarrotTransactionProposalV1 tx_proposal;
carrot::make_carrot_transaction_proposal_v1_transfer(
normal_payment_proposals,
selfsend_payment_proposals,
fee_per_weight,
fee_quantization_mask,
extra,
tx_type,
std::move(select_inputs),
change_address_spend_pubkey,
{{subaddr_account, 0}, carrot::AddressDeriveType::Carrot, false},
subtractable_normal_payment_proposals,
subtractable_selfsend_payment_proposals,
tx_proposal);
normal_payment_proposals,
selfsend_payment_proposals,
fee_per_weight,
fee_quantization_mask,
extra,
tx_type,
std::move(select_inputs),
change_address_spend_pubkey,
{{subaddr_account, 0}, carrot::AddressDeriveType::Carrot, false},
subtractable_normal_payment_proposals,
subtractable_selfsend_payment_proposals,
asset_type,
tx_proposal);
// populate the sources
std::vector<size_t> selected_transfer_indices_sorted;
for (const auto &ki: tx_proposal.key_images_sorted) {
selected_transfer_indices_sorted.push_back(w.get_transfer_details(ki));
// Scan the transfer_container instead of all of the transfers in the wallet
selected_transfer_indices_sorted.push_back(w.get_transfer_details_from_container(ki, unused_transfers));
}
tx_proposal.sources = get_sources(unused_transfers, selected_transfer_indices_sorted, "SAL1", w);
tx_proposal.sources = get_sources(unused_transfers, selected_transfer_indices_sorted, asset_type, w);
// update `unused_transfers` for next proposal by removing selected transfers
tools::for_all_in_vector_erase_no_preserve_order_if(unused_transfers,
@@ -545,8 +710,12 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
CHECK_AND_ASSERT_THROW_MES(use_per_byte_fee,
"make_carrot_transaction_proposals_wallet2_transfer: not using per-byte base fee");
const rct::xmr_amount fee_per_weight = w.get_base_fee(priority);
MDEBUG("fee_per_weight = " << fee_per_weight << ", from priority = " << priority);
// check if this is a token transfer (sal prefix) - tokens have zero fees
const std::string asset_type = dsts.empty() ? "SAL1" : (tx_type == cryptonote::transaction_type::BURN ? "SAL1" : dsts[0].asset_type);
const bool is_token = cryptonote::is_asset_type_token(asset_type);
const rct::xmr_amount fee_per_weight = /*is_token ? 0 :*/ w.get_base_fee(priority);
MDEBUG("fee_per_weight = " << fee_per_weight << ", from priority = " << priority << ", is_token = " << is_token);
const rct::xmr_amount fee_quantization_mask = w.get_fee_quantization_mask();
MDEBUG("fee_quantization_mask = " << fee_quantization_mask << ", from priority = " << priority);
@@ -581,33 +750,44 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
const cryptonote::transaction_type tx_type,
const std::uint64_t top_block_index)
{
wallet2::transfer_container transfers;
w.get_transfers(transfers);
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 <= carrot::CARROT_MAX_TX_OUTPUTS,
carrot::too_many_outputs, "too many sweep destinations per transaction");
// Make sure all destinations have the SAME asset_type
std::string asset_type = "";
for (const crypto::key_image &ki : input_key_images) {
const wallet2::transfer_details &td = w.get_transfer_details(w.get_transfer_details(ki));
if (asset_type == "")
asset_type = td.asset_type;
else
CHECK_AND_ASSERT_THROW_MES(td.asset_type == asset_type, "Mixed asset_types in transaction inputs is forbidden");
}
wallet2::transfer_container unused_transfers;
w.get_transfers(unused_transfers, asset_type);
// Check that the key image is usable and isn't spent, collect amounts, and get subaddress account index
std::vector<rct::xmr_amount> input_amounts;
input_amounts.reserve(input_key_images.size());
std::uint32_t subaddr_account = std::numeric_limits<std::uint32_t>::max();
const auto best_transfers_by_ki = collect_non_burned_transfers_by_key_image(transfers);
const auto best_transfers_by_ki = collect_non_burned_transfers_by_key_image(unused_transfers);
for (const crypto::key_image &ki : input_key_images)
{
const auto ki_it = best_transfers_by_ki.find(ki);
CHECK_AND_ASSERT_THROW_MES(ki_it != best_transfers_by_ki.cend(),
__func__ << ": unknown key image");
const wallet2::transfer_details &td = transfers.at(ki_it->second);
const wallet2::transfer_details &td = unused_transfers.at(ki_it->second);
CHECK_AND_ASSERT_THROW_MES(is_transfer_usable_for_input_selection(td,
td.m_subaddr_index.major,
/*from_subaddresses=*/{},
/*ignore_above=*/MONEY_SUPPLY,
/*ignore_below=*/0,
top_block_index),
__func__ << ": transfer not usable as an input");
td.m_subaddr_index.major,
/*from_subaddresses=*/{},
/*ignore_above=*/MONEY_SUPPLY,
/*ignore_below=*/0,
top_block_index,
asset_type),
__func__ << ": transfer not usable as an input");
input_amounts.push_back(td.amount());
subaddr_account = std::min(subaddr_account, td.m_subaddr_index.major);
}
@@ -624,7 +804,7 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
de.amount = 0;
de.addr = address;
de.is_subaddress = is_subaddress;
de.asset_type = "SAL1";
de.asset_type = asset_type;
const bool is_selfsend_dest = build_payment_proposals(normal_payment_proposals,
selfsend_payment_proposals,
de,
@@ -654,22 +834,23 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
selected_inputs.push_back({input_amounts.at(ki_idx), input_key_images.at(ki_idx)});
carrot::make_carrot_transaction_proposal_v1_sweep(normal_payment_proposals,
selfsend_payment_proposals,
fee_per_weight,
fee_quantization_mask,
extra,
tx_type,
std::move(selected_inputs),
change_address_spend_pubkey,
{{subaddr_account, 0}, carrot::AddressDeriveType::Carrot},
tx_proposal);
selfsend_payment_proposals,
fee_per_weight,
fee_quantization_mask,
extra,
tx_type,
std::move(selected_inputs),
change_address_spend_pubkey,
{{subaddr_account, 0}, carrot::AddressDeriveType::Carrot},
asset_type,
tx_proposal);
// populate the sources
std::vector<size_t> selected_transfer_indices_sorted;
for (const auto &ki: tx_proposal.key_images_sorted) {
selected_transfer_indices_sorted.push_back(w.get_transfer_details(ki));
selected_transfer_indices_sorted.push_back(w.get_transfer_details_from_container(ki, unused_transfers));
}
tx_proposal.sources = get_sources(transfers, selected_transfer_indices_sorted, "SAL1", w);
tx_proposal.sources = get_sources(unused_transfers, selected_transfer_indices_sorted, asset_type, w);
}
CARROT_CHECK_AND_THROW(ki_idx == input_key_images.size(),
@@ -719,12 +900,13 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
const rct::xmr_amount fee_quantization_mask,
const std::vector<uint8_t> &extra,
const cryptonote::transaction_type tx_type,
const std::string &asset_type,
const std::uint32_t subaddr_account,
const std::set<uint32_t> &subaddr_indices,
const std::uint64_t top_block_index)
{
wallet2::transfer_container transfers;
w.get_transfers(transfers);
w.get_transfers(transfers, asset_type);
const std::unordered_map<crypto::key_image, size_t> unburned_transfers_by_key_image =
collect_non_burned_transfers_by_key_image(transfers);
@@ -736,12 +918,13 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
const wallet2::transfer_details &td = transfers.at(transfer_idx);
if (!is_transfer_usable_for_input_selection(td,
subaddr_account,
subaddr_indices,
only_below ? only_below : MONEY_SUPPLY,
1, // ignore_below
top_block_index))
continue;
subaddr_account,
subaddr_indices,
only_below ? only_below : MONEY_SUPPLY,
1, // ignore_below
top_block_index,
asset_type))
continue;
const auto ki_it = unburned_transfers_by_key_image.find(td.m_key_image);
if (ki_it == unburned_transfers_by_key_image.cend())
@@ -776,6 +959,7 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
const std::uint32_t priority,
const std::vector<uint8_t> &extra,
const cryptonote::transaction_type tx_type,
const std::string &asset_type,
const std::uint32_t subaddr_account,
const std::set<uint32_t> &subaddr_indices)
{
@@ -797,6 +981,7 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
fee_quantization_mask,
extra,
tx_type,
asset_type,
subaddr_account,
subaddr_indices,
top_block_index);
@@ -815,8 +1000,14 @@ bool get_address_openings_x_y(
if (return_output_map.find(rct::rct2pk(src.outputs[src.real_output].second.dest)) != return_output_map.end())
{
const auto &return_output = return_output_map.at(rct::rct2pk(src.outputs[src.real_output].second.dest));
x_out = return_output.x;
y_out = return_output.y;
bool r = w.get_account().try_searching_for_opening_for_onetime_address(
return_output.K_spend_pubkey,
return_output.sum_g,
return_output.sender_extension_t,
x_out,
y_out
);
CHECK_AND_ASSERT_THROW_MES(r, "Failed to obtain openings for onetime address (return_payment)");
return true;
}
@@ -1074,28 +1265,37 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
// serialize transaction
cryptonote::transaction tx = carrot::store_carrot_to_transaction_v1(enotes,
tx_proposal.key_images_sorted,
tx_proposal.sources,
tx_proposal.fee,
tx_proposal.tx_type,
tx_proposal.amount_burnt,
change_masks,
return_enote_out,
encrypted_payment_id);
tx_proposal.key_images_sorted,
tx_proposal.sources,
tx_proposal.fee,
tx_proposal.tx_type,
tx_proposal.amount_burnt,
change_masks,
tx_proposal.token,
return_enote_out,
encrypted_payment_id,
const_cast<wallet2&>(w).get_current_hard_fork());
// store the binding tag
tx.rollup_binding_tag = tx_proposal.rollup_binding_tag;
// Is this a ROLLUP TX? If so, store the rollup data
if (tx_proposal.tx_type == cryptonote::transaction_type::ROLLUP)
tx.layer2_rollup_data = tx_proposal.layer2_rollup_data;
// aliases
hw::device &hwdev = acc_keys.get_device();
const auto &sources = tx_proposal.sources;
// inputs
uint64_t amount_in = 0;
// uint64_t amount_in = 0;
rct::carrot_ctkeyV inSk;
inSk.reserve(sources.size());
std::vector<uint64_t> inamounts;
std::vector<unsigned int> index;
for (const auto& src: sources)
{
amount_in += src.amount;
// amount_in += src.amount;
inamounts.push_back(src.amount);
index.push_back(src.real_output);
@@ -1144,7 +1344,7 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
}
// outputs
uint64_t amount_out = 0;
// uint64_t amount_out = 0;
std::vector<uint64_t> outamounts;
rct::keyV destinations;
std::vector<std::string> destination_asset_types;
@@ -1154,7 +1354,7 @@ cryptonote::transaction finalize_all_proofs_from_transfer_details(
destinations.push_back(rct::pk2rct(oep.enote.onetime_address));
destination_asset_types.push_back(oep.enote.asset_type);
outamounts.push_back(oep.amount);
amount_out += oep.amount;
// amount_out += oep.amount;
rct::ctkey key;
key.mask = rct::sk2rct(oep.amount_blinding_factor);
@@ -1255,7 +1455,10 @@ wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionPropos
"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");
if (tx_proposal.tx_type == cryptonote::transaction_type::STAKE || tx_proposal.tx_type == cryptonote::transaction_type::BURN) {
if (tx_proposal.tx_type == cryptonote::transaction_type::STAKE ||
tx_proposal.tx_type == cryptonote::transaction_type::BURN ||
tx_proposal.tx_type == cryptonote::transaction_type::CREATE_TOKEN ||
tx_proposal.tx_type == cryptonote::transaction_type::ROLLUP) {
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");
@@ -1288,7 +1491,8 @@ wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionPropos
carrot::encrypted_payment_id_t encrypted_payment_id;
std::vector<std::pair<bool, std::size_t>> sorted_payment_proposal_indices;
carrot::get_output_enote_proposals_from_proposal_v1(tx_proposal,
/*s_view_balance_dev=*/nullptr,
&account.s_view_balance_dev,
///*s_view_balance_dev=*/nullptr,
&account.k_view_incoming_dev,
output_enote_proposals,
encrypted_payment_id,
@@ -1360,11 +1564,16 @@ wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionPropos
ptx.change_dts = change_dts;
ptx.selected_transfers = std::move(selected_transfers);
ptx.key_images = key_images_string.str();
ptx.tx_key = shared_ephemeral_pubkey ? ephemeral_privkeys.at(0) : crypto::null_skey;
if (shared_ephemeral_pubkey)
ptx.additional_tx_keys = std::move(ephemeral_privkeys);
else
ptx.additional_tx_keys.clear();
if (ephemeral_privkeys.size() == 1) {
ptx.tx_key = ephemeral_privkeys.at(0);
ptx.additional_tx_keys.clear();
} else if (ephemeral_privkeys.size() == 2 && shared_ephemeral_pubkey) {
ptx.tx_key = (ephemeral_privkeys.at(0) == crypto::null_skey) ? ephemeral_privkeys.at(1) : ephemeral_privkeys.at(0);
ptx.additional_tx_keys.clear();
} else {
ptx.tx_key = crypto::null_skey;
ptx.additional_tx_keys = std::move(ephemeral_privkeys);
}
ptx.dests = std::move(dests);
ptx.multisig_sigs = {};
ptx.multisig_tx_key_entropy = {};
+20
View File
@@ -55,8 +55,26 @@ carrot::select_inputs_func_t make_wallet2_single_transfer_input_selector(
const std::uint64_t top_block_index,
const bool allow_carrot_external_inputs_in_normal_transfers,
const bool allow_pre_carrot_inputs_in_normal_transfers,
const std::string &asset_type,
std::set<size_t> &selected_transfer_indices_out);
std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposals_wallet2_createtoken(
wallet2 &w,
const cryptonote::token_metadata_t &token,
const cryptonote::tx_destination_entry &de,
const rct::xmr_amount fee_per_weight,
const rct::xmr_amount fee_quantization_mask,
const uint32_t subaddr_account,
const std::set<uint32_t> &subaddr_indices,
const std::uint64_t top_block_index);
std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposals_wallet2_createtoken(
wallet2 &w,
const cryptonote::token_metadata_t &token,
const cryptonote::tx_destination_entry &de,
const std::uint32_t priority,
const std::uint32_t subaddr_account,
const std::set<uint32_t> &subaddr_indices);
std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposals_wallet2_transfer(
wallet2 &w,
std::vector<cryptonote::tx_destination_entry> dsts,
@@ -109,6 +127,7 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
const rct::xmr_amount fee_quantization_mask,
const std::vector<uint8_t> &extra,
const cryptonote::transaction_type tx_type,
const std::string &asset_type,
const std::uint32_t subaddr_account,
const std::set<uint32_t> &subaddr_indices,
const std::uint64_t top_block_index);
@@ -121,6 +140,7 @@ std::vector<carrot::CarrotTransactionProposalV1> make_carrot_transaction_proposa
const std::uint32_t priority,
const std::vector<uint8_t> &extra,
const cryptonote::transaction_type tx_type,
const std::string &asset_type,
const std::uint32_t subaddr_account,
const std::set<uint32_t> &subaddr_indices);
wallet2::pending_tx make_pending_carrot_tx(const carrot::CarrotTransactionProposalV1 &tx_proposal,
+988 -192
View File
File diff suppressed because it is too large Load Diff
+49 -9
View File
@@ -496,9 +496,10 @@ private:
bool m_coinbase;
cryptonote::subaddress_index m_subaddr_index;
cryptonote::transaction_type m_tx_type;
bool m_is_carrot;
BEGIN_SERIALIZE_OBJECT()
VERSION_FIELD(0)
VERSION_FIELD(1)
FIELD(m_tx_hash)
VARINT_FIELD(m_amount)
FIELD(m_asset_type)
@@ -510,6 +511,11 @@ private:
FIELD(m_coinbase)
FIELD(m_subaddr_index)
VARINT_FIELD(m_tx_type)
if (version < 1) {
m_is_carrot = false;
return true;
}
FIELD(m_is_carrot)
END_SERIALIZE()
};
@@ -1197,6 +1203,7 @@ private:
std::vector<crypto::public_key> get_subaddress_spend_public_keys(uint32_t account, uint32_t begin, uint32_t end) const;
//std::string get_subaddress_as_str(const cryptonote::subaddress_index& index) const;
std::string get_subaddress_as_str(const carrot::subaddress_index_extended& index) const;
std::string get_subaddress_as_str(const carrot::subaddress_index_extended& index, const uint64_t height) const;
std::string get_address_as_str() const { return get_subaddress_as_str({0, 0}); }
std::string get_integrated_address_as_str(const crypto::hash8& payment_id, bool carrot = true) const;
void add_subaddress_account(const std::string& label);
@@ -1287,6 +1294,17 @@ private:
bool parse_unsigned_tx_from_str(const std::string &unsigned_tx_st, unsigned_tx_set &exported_txs) const;
bool load_tx(const std::string &signed_filename, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set&)> accept_func = NULL);
bool parse_tx_from_str(const std::string &signed_tx_st, std::vector<tools::wallet2::pending_tx> &ptx, std::function<bool(const signed_tx_set &)> accept_func);
/**
* Receives a hex-encoded metadata blob that contains the fields necessary to create a `sal_token_t` instance
*/
std::vector<wallet2::pending_tx> create_token(const std::string& token_ticker, const uint64_t token_supply, const std::string& token_metadata_hex, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices);
/**
* Creates a new token using the provided `sal_token_t` struct (verifies the target first)
*/
std::vector<wallet2::pending_tx> create_token(const cryptonote::token_metadata_t& token, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices);
std::vector<wallet2::pending_tx> create_transactions_2(std::vector<cryptonote::tx_destination_entry> dsts, const std::string& source_asset, const std::string& dest_asset, const cryptonote::transaction_type tx_type, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, const unique_index_container& subtract_fee_from_outputs = {}); // pass subaddr_indices by value on purpose
std::vector<wallet2::pending_tx> create_transactions_all(uint64_t below, cryptonote::transaction_type tx_type, const std::string& asset_type, const cryptonote::account_public_address &address, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices);
std::vector<wallet2::pending_tx> create_transactions_single(const crypto::key_image &ki, const cryptonote::account_public_address &address, const cryptonote::transaction_type tx_type, bool is_subaddress, const size_t outputs, const size_t fake_outs_count, const uint64_t unlock_time, uint32_t priority, const std::vector<uint8_t>& extra);
@@ -1308,7 +1326,8 @@ private:
bool check_connection(uint32_t *version = NULL, bool *ssl = NULL, uint32_t timeout = 200000, bool *wallet_is_outdated = NULL, bool *daemon_is_outdated = NULL);
bool check_version(uint32_t *version, bool *wallet_is_outdated, bool *daemon_is_outdated);
bool check_hard_fork_version(cryptonote::network_type nettype, const std::vector<std::pair<uint8_t, uint64_t>> &daemon_hard_forks, const uint64_t height, const uint64_t target_height, bool *wallet_is_outdated, bool *daemon_is_outdated);
void get_transfers(wallet2::transfer_container& incoming_transfers) const;
void get_transfers(wallet2::transfer_container& incoming_transfers, const std::string& asset_type = "") const;
void get_transfers_indices(std::set<size_t>& indices, const std::string& asset_type = "SAL1") const;
void get_payments(const crypto::hash& payment_id, std::list<wallet2::payment_details>& payments, uint64_t min_height = 0, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const;
void get_payments(std::list<std::pair<crypto::hash,wallet2::payment_details>>& payments, uint64_t min_height, uint64_t max_height = (uint64_t)-1, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const;
void get_payments_out(std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>>& confirmed_payments,
@@ -1470,7 +1489,7 @@ private:
BEGIN_SERIALIZE_OBJECT()
MAGIC_FIELD("monero wallet cache")
VERSION_FIELD(2)
VERSION_FIELD(3)
FIELD(m_blockchain)
FIELD(m_transfers)
FIELD(m_transfers_indices)
@@ -1514,6 +1533,14 @@ private:
}
FIELD(m_background_sync_data)
FIELD(m_subaddresses_extended)
if (version == 2)
{
serializable_unordered_map<crypto::public_key, carrot::return_output_info_retired_t> old_roi;
FIELD(old_roi)
m_return_output_info = {};
m_force_rescan = true;
return true;
}
FIELD(m_return_output_info)
END_SERIALIZE()
@@ -1548,6 +1575,8 @@ private:
void set_default_priority(uint32_t p) { m_default_priority = p; }
bool auto_refresh() const { return m_auto_refresh; }
void auto_refresh(bool r) { m_auto_refresh = r; }
bool force_rescan() const { return m_force_rescan; }
void force_rescan(bool r) { m_force_rescan = r; }
AskPasswordType ask_password() const { return m_ask_password; }
void ask_password(AskPasswordType ask) { m_ask_password = ask; }
void set_min_output_count(uint32_t count) { m_min_output_count = count; }
@@ -1661,6 +1690,7 @@ private:
size_t get_num_transfer_details() const { return m_transfers.size(); }
const transfer_details &get_transfer_details(size_t idx) const;
uint8_t estimate_current_hard_fork(const uint64_t height = 0) const;
uint8_t get_current_hard_fork();
void get_hard_fork_info(uint8_t version, uint64_t &earliest_height);
bool use_fork_rules(uint8_t version, int64_t early_blocks = 0);
@@ -1913,10 +1943,13 @@ private:
bool get_pricing_record(oracle::pricing_record& pr, const uint64_t height);
bool get_circulating_supply(std::vector<std::pair<std::string, std::string>> &amounts);
bool get_yield_info(std::vector<cryptonote::yield_block_info>& ybi_data);
bool get_tokens(const std::string& regex, std::vector<std::string>& tokens);
cryptonote::token_metadata_t get_token_info(const std::string& asset_type); // const;
typedef std::tuple<size_t, std::string, std::string, uint64_t, uint64_t> yield_payout_t;
bool get_yield_info(std::vector<cryptonote::yield_block_info>& ybi_data);
bool get_yield_summary_info(uint64_t &total_burnt,
uint64_t &total_supply,
uint64_t &total_locked,
@@ -2065,9 +2098,11 @@ private:
bool is_spent(const transfer_details &td, bool strict = true) const;
bool is_spent(size_t idx, bool strict = true) const;
public:
void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, bool rct, std::unordered_set<crypto::public_key> &valid_public_keys_cache);
void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, std::vector<uint64_t> &rct_offsets, std::unordered_set<crypto::public_key> &valid_public_keys_cache, uint64_t &num_spendable_global_outs, uint64_t &num_outs);
void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const transfer_container &transfers, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, bool rct, std::unordered_set<crypto::public_key> &valid_public_keys_cache);
void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const transfer_container &transfers, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, std::vector<uint64_t> &rct_offsets, std::unordered_set<crypto::public_key> &valid_public_keys_cache, uint64_t &num_spendable_global_outs, uint64_t &num_outs);
size_t get_transfer_details(const crypto::key_image &ki) const;
size_t get_transfer_details_from_container(const crypto::key_image &ki, const transfer_container& container) const;
bool get_rct_distribution(const bool use_global_outs, const std::string &rct_asset_type, uint64_t &start_height, std::vector<uint64_t> &distribution, uint64_t &num_spendable_global_outs);
private:
bool tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& tx_public_key, const rct::key& mask, uint64_t real_index, bool unlocked, std::unordered_set<crypto::public_key> &valid_public_keys_cache) const;
bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const;
@@ -2104,8 +2139,6 @@ private:
void register_devices();
hw::device& lookup_device(const std::string & device_descriptor);
bool get_rct_distribution(const bool use_global_outs, const std::string &rct_asset_type, uint64_t &start_height, std::vector<uint64_t> &distribution, uint64_t &num_spendable_global_outs);
uint64_t get_segregation_fork_height() const;
std::map<std::pair<uint64_t, uint64_t>, size_t> create_output_tracker_cache() const;
@@ -2192,6 +2225,7 @@ private:
uint32_t m_default_priority;
RefreshType m_refresh_type;
bool m_auto_refresh;
bool m_force_rescan = false;
bool m_first_refresh_done;
uint64_t m_refresh_from_block_height;
// If m_refresh_from_block_height is explicitly set to zero we need this to differentiate it from the case that
@@ -2293,7 +2327,7 @@ BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 12)
BOOST_CLASS_VERSION(tools::wallet2::multisig_info, 1)
BOOST_CLASS_VERSION(tools::wallet2::multisig_info::LR, 0)
BOOST_CLASS_VERSION(tools::wallet2::multisig_tx_set, 1)
BOOST_CLASS_VERSION(tools::wallet2::payment_details, 5)
BOOST_CLASS_VERSION(tools::wallet2::payment_details, 6)
BOOST_CLASS_VERSION(tools::wallet2::pool_payment_details, 1)
BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 8)
BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 6)
@@ -2622,6 +2656,12 @@ namespace boost
a & x.m_amounts;
a & x.m_asset_type;
a & x.m_tx_type;
if (ver < 6)
{
x.m_is_carrot = false;
return;
}
a & x.m_is_carrot;
}
template <class Archive>
+1 -1
View File
@@ -152,7 +152,7 @@ bool wallet2::search_for_rpc_payment(uint64_t credits_target, uint32_t n_threads
{
tpool.submit(&waiter, [&, i] {
*(uint32_t*)(hashing_blob.data() + 39) = SWAP32LE(local_nonce-i);
const uint8_t major_version = hashing_blob[0];
// const uint8_t major_version = hashing_blob[0];
crypto::rx_slow_hash(seed_hash.data, hashing_blob.data(), hashing_blob.size(), hash[i].data);
});
}
+376 -33
View File
@@ -439,7 +439,7 @@ namespace tools
entry.subaddr_index = pd.m_subaddr_index;
entry.subaddr_indices.push_back(pd.m_subaddr_index);
//entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index);
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
bool is_carrot = m_wallet->estimate_current_hard_fork(entry.height) >= HF_VERSION_CARROT;
entry.address = m_wallet->get_subaddress_as_str({{pd.m_subaddr_index.major, pd.m_subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward(), pd.m_unlock_time);
}
@@ -473,7 +473,7 @@ namespace tools
for (uint32_t i: pd.m_subaddr_indices)
entry.subaddr_indices.push_back({pd.m_subaddr_account, i});
//entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0});
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
bool is_carrot = m_wallet->estimate_current_hard_fork(entry.height) >= HF_VERSION_CARROT;
entry.address = m_wallet->get_subaddress_as_str({{pd.m_subaddr_account, 0}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward(), pd.m_unlock_time);
}
@@ -508,7 +508,7 @@ namespace tools
for (uint32_t i: pd.m_subaddr_indices)
entry.subaddr_indices.push_back({pd.m_subaddr_account, i});
//entry.address = m_wallet->get_subaddress_as_str({pd.m_subaddr_account, 0});
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
bool is_carrot = m_wallet->estimate_current_hard_fork(entry.height) >= HF_VERSION_CARROT;
entry.address = m_wallet->get_subaddress_as_str({{pd.m_subaddr_account, 0}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward(), pd.m_tx.unlock_time);
}
@@ -533,7 +533,7 @@ namespace tools
entry.subaddr_index = pd.m_subaddr_index;
entry.subaddr_indices.push_back(pd.m_subaddr_index);
//entry.address = m_wallet->get_subaddress_as_str(pd.m_subaddr_index);
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
bool is_carrot = m_wallet->estimate_current_hard_fork(entry.height) >= HF_VERSION_CARROT;
entry.address = m_wallet->get_subaddress_as_str({{pd.m_subaddr_index.major, pd.m_subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
set_confirmations(entry, m_wallet->get_blockchain_current_height(), m_wallet->get_last_block_reward(), pd.m_unlock_time);
}
@@ -542,12 +542,25 @@ namespace tools
{
if (!m_wallet) return not_open(er);
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
bool is_carrot = m_wallet->estimate_current_hard_fork() >= HF_VERSION_CARROT;
std::vector<std::string> assets_in_wallet = m_wallet->list_asset_types();
std::string asset_type = req.asset_type.empty() ? "SAL1" : boost::algorithm::to_upper_copy(req.asset_type);
std::string asset_type = req.asset_type.empty() ? "SAL1" : req.asset_type;
if (asset_type.length() > 4) {
// Assume it is a token and format accordingly
if (boost::algorithm::to_lower_copy(asset_type.substr(0, 3)) == "sal") {
asset_type = "sal" + boost::algorithm::to_upper_copy(asset_type.substr(3));
} else if (boost::algorithm::to_lower_copy(asset_type.substr(0, 3)) == "erc") {
asset_type = "erc" + boost::algorithm::to_upper_copy(asset_type.substr(3));
} else {
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = std::string("Invalid token asset_type '") + asset_type + "' specified";
return false;
}
}
// verify that the asset is in the list of in-wallet assets
if (std::find(assets_in_wallet.begin(), assets_in_wallet.end(), asset_type) == assets_in_wallet.end()) {
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = std::string("Source asset '") + asset_type + "' not found in wallet";
return false;
}
@@ -627,8 +640,6 @@ namespace tools
{
if (!m_wallet) return not_open(er);
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
try
{
THROW_WALLET_EXCEPTION_IF(req.account_index >= m_wallet->get_num_subaddress_accounts(), error::account_index_outofbound);
@@ -651,14 +662,14 @@ namespace tools
res.addresses.resize(res.addresses.size() + 1);
auto& info = res.addresses.back();
const cryptonote::subaddress_index index = {req.account_index, i};
//info.address = m_wallet->get_subaddress_as_str(index);
info.address = m_wallet->get_subaddress_as_str({{req.account_index, i}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
info.address = m_wallet->get_subaddress_as_str({req.account_index, i});
info.address_cn = req.cryptonote ? m_wallet->get_subaddress_as_str({{req.account_index, i}, carrot::AddressDeriveType::PreCarrot}) : "";
info.address_carrot = req.carrot ? m_wallet->get_subaddress_as_str({{req.account_index, i}, carrot::AddressDeriveType::Carrot}) : "";
info.label = m_wallet->get_subaddress_label(index);
info.address_index = index.minor;
info.used = std::find_if(transfers.begin(), transfers.end(), [&](const tools::wallet2::transfer_details& td) { return td.m_subaddr_index == index; }) != transfers.end();
}
//res.address = m_wallet->get_subaddress_as_str({req.account_index, 0});
res.address = m_wallet->get_subaddress_as_str({{req.account_index, 0}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
res.address = m_wallet->get_subaddress_as_str({req.account_index, 0});
}
catch (const std::exception& e)
{
@@ -701,8 +712,6 @@ namespace tools
return false;
}
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
std::vector<std::string> addresses;
std::vector<uint32_t> address_indices;
@@ -713,8 +722,7 @@ namespace tools
m_wallet->add_subaddress(req.account_index, req.label);
uint32_t new_address_index = m_wallet->get_num_subaddresses(req.account_index) - 1;
address_indices.push_back(new_address_index);
//addresses.push_back(m_wallet->get_subaddress_as_str({req.account_index, new_address_index}));
addresses.push_back(m_wallet->get_subaddress_as_str({{req.account_index, new_address_index}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot}));
addresses.push_back(m_wallet->get_subaddress_as_str({req.account_index, new_address_index}));
}
res.address = addresses[0];
@@ -750,8 +758,6 @@ namespace tools
{
if (!m_wallet) return not_open(er);
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
try
{
res.total_balance = 0;
@@ -772,8 +778,7 @@ namespace tools
continue;
wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::subaddress_account_info info;
info.account_index = subaddr_index.major;
//info.base_address = m_wallet->get_subaddress_as_str(subaddr_index);
info.base_address = m_wallet->get_subaddress_as_str({{subaddr_index.major, subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
info.base_address = m_wallet->get_subaddress_as_str({{subaddr_index.major, 0}, carrot::AddressDeriveType::Auto});
//for (const auto& asset: asset_types) {
info.balance = m_wallet->balance(subaddr_index.major, "SAL1", req.strict_balances);
@@ -810,9 +815,7 @@ namespace tools
{
m_wallet->add_subaddress_account(req.label);
res.account_index = m_wallet->get_num_subaddress_accounts() - 1;
//res.address = m_wallet->get_subaddress_as_str({res.account_index, 0});
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
res.address = m_wallet->get_subaddress_as_str({{res.account_index, 0}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
res.address = m_wallet->get_subaddress_as_str({res.account_index, 0});
}
catch (const std::exception& e)
{
@@ -1282,6 +1285,275 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_create_token(const wallet_rpc::COMMAND_RPC_CREATE_TOKEN::request& req, wallet_rpc::COMMAND_RPC_CREATE_TOKEN::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
if (m_wallet->get_current_hard_fork() < HF_VERSION_ENABLE_TOKENS) {
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Create_token is not available yet.";
return false;
}
if (req.ticker.empty()) {
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Empty ticker is not allowed.";
return false;
}
std::string asset_type = req.ticker;
std::transform(asset_type.begin(), asset_type.end(), asset_type.begin(), ::toupper);
if (asset_type.length() != 4) {
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Asset type must be exactly 4 characters long.";
return false;
}
// Validate characters
for (char c : asset_type) {
if (!((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))) {
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Asset type can only contain uppercase letters and digits.";
return false;
}
}
// Check if starts with "SAL"
if (asset_type.substr(0, 3) == "SAL") {
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Asset type cannot start with 'SAL'.";
return false;
}
// todo: update reserved names
if (asset_type == "SAL" || asset_type == "SAL1" || asset_type == "SAL2" || asset_type == "BURN") {
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Asset type is reserved and cannot be used.";
return false;
}
// get tokens
std::vector<std::string> tokens;
if(!m_wallet->get_tokens(asset_type, tokens)) {
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Failed to get token data.";
return false;
}
if (!tokens.empty()) {
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Token already exists.";
return false;
}
// Verify supply amount is acceptable
if (req.supply < 1 || req.supply > MONEY_SUPPLY)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Supply amount must be between 1 and MONEY_SUPPLY.";
return false;
}
// convert req.hash to hash
crypto::hash hash_signature{};
if (!req.hash.empty()) {
if (!epee::string_tools::hex_to_pod(req.hash, hash_signature)) {
//if (metadata.empty()){
er.message = "Invalid hash format.";
return false;
//}
}
}
std::string token_metadata_hex;
if (req.token_metadata_hex.empty()) {
std::string json = (boost::format("{\"name\":\"%s\",\"size\":%d,\"hash\":\"%s\",\"url\":\"%s\"}") % req.name % req.size % req.hash % req.url).str();
token_metadata_hex = epee::string_tools::buff_to_hex_nodelimer(json);
} else {
token_metadata_hex = req.token_metadata_hex;
}
//cryptonote::sal_token_t sal_token{1, req.supply, req.size, req.name, req.url, hash_signature};
//cryptonote::token_metadata_t token{1, asset_type, sal_token};
try
{
// Call the wallet create_token() method
auto ptx_vector = m_wallet->create_token(
asset_type,
req.supply,
token_metadata_hex,
req.account_index,
req.subaddr_indices
);
if (ptx_vector.empty())
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "ptx_vector is empty after create_token.";
return false;
}
if (ptx_vector.size() > 1)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "ptx_vector contains more than one transaction.";
return false;
}
return fill_response(ptx_vector, req.get_tx_keys, res.tx_key_list, res.amount_list, res.amounts_by_dest_list, res.fee_list, res.weight_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, res.spent_key_images_list, er);
}
catch (const std::exception& e)
{
handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_tokens(const wallet_rpc::COMMAND_RPC_GET_TOKENS::request& req, wallet_rpc::COMMAND_RPC_GET_TOKENS::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
LOG_PRINT_L3("on_get_tokens starts");
if (!m_wallet) return not_open(er);
// Get the full list of tokens from the wallet - filtered if necessary
if (!m_wallet->get_tokens(req.regex, res.tokens)) {
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "Failed to get token data from wallet.";
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_return_payment(const wallet_rpc::COMMAND_RPC_RETURN_PAYMENT::request& req, wallet_rpc::COMMAND_RPC_RETURN_PAYMENT::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
LOG_PRINT_L3("on_return_payment starts");
if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
// Get the TX hash we are interested in
crypto::hash txid;
if (!epee::string_tools::hex_to_pod(req.txid, txid))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
er.message = "TX ID has invalid format";
return false;
}
// Get the transfers from the wallet
tools::wallet2::transfer_container transfers;
m_wallet->get_transfers(transfers);
std::vector<size_t> transfers_indices = {};
for (size_t idx=0; idx < transfers.size(); ++idx) {
// Get the TD by reference
tools::wallet2::transfer_details& td = transfers[idx];
// Skip entries we don't care about
if (td.m_txid != txid) continue;
// Found the specified entry - make sure we can return it
if (td.m_tx.type != cryptonote::transaction_type::TRANSFER) {
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = tr("incorrect TX type for txid");
return false;
}
if (td.m_tx.version >= TRANSACTION_VERSION_N_OUTS) {
if (td.m_tx.return_address_list.empty() || td.m_tx.return_address_change_mask.empty()) {
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = tr("invalid return_address_list for txid");
return false;
}
} else {
// Verify we have a valid return_address and tx_pubkey
if (td.m_tx.return_address == crypto::null_pkey || td.m_tx.return_pubkey != crypto::null_pkey) {
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = tr("invalid return_address/return_pubkey for txid");
return false;
}
}
// Check that we have the key image information, and that it is usable
if (!td.m_key_image_known || td.m_key_image_partial || td.m_spent || td.m_frozen) {
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = tr("key image is unavailable (partial / unknown / spent / frozen) for txid");
return false;
}
// Verify that the correct asset type is being returned
if (m_wallet->get_current_hard_fork() >= HF_VERSION_ENABLE_TOKENS) {
if (td.asset_type == "SAL") {
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = tr("SAL may not be returned for txid");
return false;
}
} else if (m_wallet->get_current_hard_fork() >= HF_VERSION_SALVIUM_ONE_PROOFS) {
if (td.asset_type != "SAL1") {
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = tr("Only SAL1 may be returned for txid");
return false;
}
}
// We found the one(s) we were looking for - take a copy of the key_image, etc.
transfers_indices.push_back(idx);
}
// Check we have a valid key_image
if (transfers_indices.empty()) {
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = tr("No matching transfers found in wallet");
return false;
}
try
{
// Call the wallet create_transactions_return() method
auto ptx_vector = m_wallet->create_transactions_return(transfers_indices);
if (ptx_vector.empty())
{
er.code = WALLET_RPC_ERROR_CODE_TX_NOT_POSSIBLE;
er.message = "No transaction created";
return false;
}
if (ptx_vector.size() > 2)
{
er.code = WALLET_RPC_ERROR_CODE_TX_TOO_LARGE;
er.message = "Multiple transactions are created, which is not supposed to happen.";
return false;
}
if (ptx_vector[0].selected_transfers.size() != transfers_indices.size())
{
er.code = WALLET_RPC_ERROR_CODE_TX_NOT_POSSIBLE;
er.message = tr("The transaction uses incorrect number of inputs, which is not supposed to happen");
return false;
}
//return fill_response(ptx_vector, req.get_tx_key, res.tx_key, res.amount, res.amounts_by_dest, res.fee, res.weight, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
// res.tx_hash, req.get_tx_hex, res.tx_blob, req.get_tx_metadata, res.tx_metadata, res.spent_key_images, er);
return fill_response(ptx_vector, req.get_tx_key, res.tx_key_list, res.amount_list, res.amounts_by_dest_list, res.fee_list, res.weight_list, res.multisig_txset, res.unsigned_txset, req.do_not_relay,
res.tx_hash_list, req.get_tx_hex, res.tx_blob_list, req.get_tx_metadata, res.tx_metadata_list, res.spent_key_images_list, er);
}
catch (const std::exception& e)
{
handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR);
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
@@ -2109,8 +2381,6 @@ namespace tools
{
if (!m_wallet) return not_open(er);
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
crypto::hash payment_id;
crypto::hash8 payment_id8;
cryptonote::blobdata payment_id_blob;
@@ -2151,7 +2421,7 @@ namespace tools
rpc_payment.unlock_time = payment.m_unlock_time;
rpc_payment.locked = !m_wallet->is_transfer_unlocked(payment.m_unlock_time, payment.m_block_height);
rpc_payment.subaddr_index = payment.m_subaddr_index;
//rpc_payment.address = m_wallet->get_subaddress_as_str(payment.m_subaddr_index);
bool is_carrot = m_wallet->estimate_current_hard_fork(payment.m_block_height) >= HF_VERSION_CARROT;
rpc_payment.address = m_wallet->get_subaddress_as_str({{payment.m_subaddr_index.major, payment.m_subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
res.payments.push_back(rpc_payment);
}
@@ -2163,8 +2433,6 @@ namespace tools
{
res.payments.clear();
bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
if (!m_wallet) return not_open(er);
/* If the payment ID list is empty, we get payments to any payment ID (or lack thereof) */
@@ -2182,7 +2450,7 @@ namespace tools
rpc_payment.block_height = payment.second.m_block_height;
rpc_payment.unlock_time = payment.second.m_unlock_time;
rpc_payment.subaddr_index = payment.second.m_subaddr_index;
//rpc_payment.address = m_wallet->get_subaddress_as_str(payment.second.m_subaddr_index);
bool is_carrot = m_wallet->estimate_current_hard_fork(payment.second.m_block_height) >= HF_VERSION_CARROT;
rpc_payment.address = m_wallet->get_subaddress_as_str({{payment.second.m_subaddr_index.major, payment.second.m_subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
rpc_payment.locked = !m_wallet->is_transfer_unlocked(payment.second.m_unlock_time, payment.second.m_block_height);
res.payments.push_back(std::move(rpc_payment));
@@ -2238,7 +2506,7 @@ namespace tools
rpc_payment.block_height = payment.m_block_height;
rpc_payment.unlock_time = payment.m_unlock_time;
rpc_payment.subaddr_index = payment.m_subaddr_index;
//rpc_payment.address = m_wallet->get_subaddress_as_str(payment.m_subaddr_index);
bool is_carrot = m_wallet->estimate_current_hard_fork(payment.m_block_height) >= HF_VERSION_CARROT;
rpc_payment.address = m_wallet->get_subaddress_as_str({{payment.m_subaddr_index.major, payment.m_subaddr_index.minor}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
rpc_payment.locked = !m_wallet->is_transfer_unlocked(payment.m_unlock_time, payment.m_block_height);
res.payments.push_back(std::move(rpc_payment));
@@ -2369,6 +2637,55 @@ namespace tools
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().m_spend_secret_key);
res.key = std::string(key.data(), key.size());
}
else if(req.key_type.compare("s_master") == 0)
{
if (m_wallet->watch_only())
{
er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY;
er.message = "The wallet is watch-only. Cannot retrieve s_master key.";
return false;
}
CHECK_IF_BACKGROUND_SYNCING();
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().s_master);
res.key = std::string(key.data(), key.size());
}
else if(req.key_type.compare("k_prove_spend") == 0)
{
if (m_wallet->watch_only())
{
er.code = WALLET_RPC_ERROR_CODE_WATCH_ONLY;
er.message = "The wallet is watch-only. Cannot retrieve k_prove_spend key.";
return false;
}
CHECK_IF_BACKGROUND_SYNCING();
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().k_prove_spend);
res.key = std::string(key.data(), key.size());
}
else if(req.key_type.compare("s_view_balance") == 0)
{
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().s_view_balance);
res.key = std::string(key.data(), key.size());
}
else if(req.key_type.compare("s_view_balance") == 0)
{
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().s_view_balance);
res.key = std::string(key.data(), key.size());
}
else if(req.key_type.compare("k_view_incoming") == 0)
{
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().k_view_incoming);
res.key = std::string(key.data(), key.size());
}
else if(req.key_type.compare("k_generate_image") == 0)
{
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().k_generate_image);
res.key = std::string(key.data(), key.size());
}
else if(req.key_type.compare("s_generate_address") == 0)
{
epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().s_generate_address);
res.key = std::string(key.data(), key.size());
}
else
{
er.message = "key_type " + req.key_type + " not found";
@@ -3319,9 +3636,9 @@ namespace tools
const auto &entry = ab[idx];
std::string address;
if (entry.m_has_payment_id)
address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), entry.m_address, entry.m_payment_id, entry.m_is_carrot);
address = cryptonote::get_account_integrated_address_as_str(m_wallet->nettype(), entry.m_address, entry.m_payment_id);
else
address = get_account_address_as_str(m_wallet->nettype(), entry.m_is_subaddress, entry.m_address, entry.m_is_carrot);
address = get_account_address_as_str(m_wallet->nettype(), entry.m_is_subaddress, entry.m_address);
res.entries.push_back(wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::entry{idx, address, entry.m_description});
}
}
@@ -4945,6 +5262,32 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_get_token_info(const wallet_rpc::COMMAND_RPC_GET_TOKEN_INFO::request& req, wallet_rpc::COMMAND_RPC_GET_TOKEN_INFO::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
// check
if (!m_wallet) return not_open(er);
try {
cryptonote::token_metadata_t token = m_wallet->get_token_info(req.asset_type);
res.status = CORE_RPC_STATUS_OK;
res.version = token.version;
res.asset_type = token.asset_type;
if (token.token.type() == typeid(cryptonote::sal_token_t)) {
res.token_type = TOKEN_TYPE_SAL;
res.sal_token = boost::get<cryptonote::sal_token_t>(token.token);
} else if (token.token.type() == typeid(cryptonote::erc_token_t)) {
res.token_type = TOKEN_TYPE_ERC20;
res.erc_token = boost::get<cryptonote::erc_token_t>(token.token);
}
} catch (const std::exception& e) {
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "Failed to query daemon for token info";
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
}
class t_daemon
+8
View File
@@ -88,6 +88,9 @@ namespace tools
MAP_JON_RPC_WE("thaw", on_thaw, wallet_rpc::COMMAND_RPC_THAW)
MAP_JON_RPC_WE("frozen", on_frozen, wallet_rpc::COMMAND_RPC_FROZEN)
MAP_JON_RPC_WE("audit", on_audit, wallet_rpc::COMMAND_RPC_AUDIT)
MAP_JON_RPC_WE("create_token", on_create_token, wallet_rpc::COMMAND_RPC_CREATE_TOKEN)
MAP_JON_RPC_WE("get_tokens", on_get_tokens, wallet_rpc::COMMAND_RPC_GET_TOKENS)
MAP_JON_RPC_WE("return_payment", on_return_payment, wallet_rpc::COMMAND_RPC_RETURN_PAYMENT)
MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER)
MAP_JON_RPC_WE("transfer_split", on_transfer_split, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT)
MAP_JON_RPC_WE("sign_transfer", on_sign_transfer, wallet_rpc::COMMAND_RPC_SIGN_TRANSFER)
@@ -163,6 +166,7 @@ namespace tools
MAP_JON_RPC_WE("estimate_tx_size_and_weight", on_estimate_tx_size_and_weight, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT)
MAP_JON_RPC_WE("get_default_fee_priority", on_get_default_fee_priority, wallet_rpc::COMMAND_RPC_GET_DEFAULT_FEE_PRIORITY)
MAP_JON_RPC_WE("get_version", on_get_version, wallet_rpc::COMMAND_RPC_GET_VERSION)
MAP_JON_RPC_WE("get_token_info", on_get_token_info, wallet_rpc::COMMAND_RPC_GET_TOKEN_INFO)
MAP_JON_RPC_WE("setup_background_sync", on_setup_background_sync, wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC)
MAP_JON_RPC_WE("start_background_sync", on_start_background_sync, wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC)
MAP_JON_RPC_WE("stop_background_sync", on_stop_background_sync, wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC)
@@ -187,6 +191,9 @@ namespace tools
bool on_thaw(const wallet_rpc::COMMAND_RPC_THAW::request& req, wallet_rpc::COMMAND_RPC_THAW::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_frozen(const wallet_rpc::COMMAND_RPC_FROZEN::request& req, wallet_rpc::COMMAND_RPC_FROZEN::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_audit(const wallet_rpc::COMMAND_RPC_AUDIT::request& req, wallet_rpc::COMMAND_RPC_AUDIT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_create_token(const wallet_rpc::COMMAND_RPC_CREATE_TOKEN::request& req, wallet_rpc::COMMAND_RPC_CREATE_TOKEN::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_get_tokens(const wallet_rpc::COMMAND_RPC_GET_TOKENS::request& req, wallet_rpc::COMMAND_RPC_GET_TOKENS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_return_payment(const wallet_rpc::COMMAND_RPC_RETURN_PAYMENT::request& req, wallet_rpc::COMMAND_RPC_RETURN_PAYMENT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_sign_transfer(const wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_SIGN_TRANSFER::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
@@ -260,6 +267,7 @@ namespace tools
bool on_estimate_tx_size_and_weight(const wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::request& req, wallet_rpc::COMMAND_RPC_ESTIMATE_TX_SIZE_AND_WEIGHT::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_get_default_fee_priority(const wallet_rpc::COMMAND_RPC_GET_DEFAULT_FEE_PRIORITY::request& req, wallet_rpc::COMMAND_RPC_GET_DEFAULT_FEE_PRIORITY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_get_version(const wallet_rpc::COMMAND_RPC_GET_VERSION::request& req, wallet_rpc::COMMAND_RPC_GET_VERSION::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_get_token_info(const wallet_rpc::COMMAND_RPC_GET_TOKEN_INFO::request& req, wallet_rpc::COMMAND_RPC_GET_TOKEN_INFO::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_setup_background_sync(const wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_SETUP_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_start_background_sync(const wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_START_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_stop_background_sync(const wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC::request& req, wallet_rpc::COMMAND_RPC_STOP_BACKGROUND_SYNC::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
+184 -28
View File
@@ -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)
@@ -585,6 +593,82 @@ namespace wallet_rpc
END_KV_SERIALIZE_MAP()
};
struct split_transfer_response
{
std::list<std::string> tx_hash_list;
std::list<std::string> tx_key_list;
std::list<uint64_t> amount_list;
std::list<amounts_list> amounts_by_dest_list;
std::list<uint64_t> fee_list;
std::list<uint64_t> weight_list;
std::list<std::string> tx_blob_list;
std::list<std::string> tx_metadata_list;
std::string multisig_txset;
std::string unsigned_txset;
std::list<key_image_list> spent_key_images_list;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash_list)
KV_SERIALIZE(tx_key_list)
KV_SERIALIZE(amount_list)
KV_SERIALIZE_OPT(amounts_by_dest_list, decltype(amounts_by_dest_list)())
KV_SERIALIZE(fee_list)
KV_SERIALIZE(weight_list)
KV_SERIALIZE(tx_blob_list)
KV_SERIALIZE(tx_metadata_list)
KV_SERIALIZE(multisig_txset)
KV_SERIALIZE(unsigned_txset)
KV_SERIALIZE(spent_key_images_list)
END_KV_SERIALIZE_MAP()
};
struct COMMAND_RPC_RETURN_PAYMENT
{
struct request_t
{
std::string txid;
/* std::list<transfer_destination> destinations;
std::string source_asset;
std::string dest_asset;
uint8_t tx_type;
uint32_t account_index;
std::set<uint32_t> subaddr_indices;
std::set<uint32_t> subtract_fee_from_outputs;
uint32_t priority;
uint64_t ring_size;
uint64_t unlock_time;
std::string payment_id; */
bool get_tx_key;
bool do_not_relay;
bool get_tx_hex;
bool get_tx_metadata;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(txid)
// KV_SERIALIZE(destinations)
// KV_SERIALIZE(source_asset)
// KV_SERIALIZE(dest_asset)
// KV_SERIALIZE(tx_type)
// KV_SERIALIZE(account_index)
// KV_SERIALIZE(subaddr_indices)
// KV_SERIALIZE_OPT(subtract_fee_from_outputs, decltype(subtract_fee_from_outputs)())
// KV_SERIALIZE(priority)
// KV_SERIALIZE_OPT(ring_size, (uint64_t)0)
// KV_SERIALIZE(unlock_time)
// KV_SERIALIZE(payment_id)
KV_SERIALIZE(get_tx_key)
KV_SERIALIZE_OPT(do_not_relay, false)
KV_SERIALIZE_OPT(get_tx_hex, false)
KV_SERIALIZE_OPT(get_tx_metadata, false)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
typedef split_transfer_response response_t;
typedef epee::misc_utils::struct_init<response_t> response;
};
struct COMMAND_RPC_TRANSFER
{
struct request_t
@@ -629,34 +713,6 @@ namespace wallet_rpc
typedef epee::misc_utils::struct_init<response_t> response;
};
struct split_transfer_response
{
std::list<std::string> tx_hash_list;
std::list<std::string> tx_key_list;
std::list<uint64_t> amount_list;
std::list<amounts_list> amounts_by_dest_list;
std::list<uint64_t> fee_list;
std::list<uint64_t> weight_list;
std::list<std::string> tx_blob_list;
std::list<std::string> tx_metadata_list;
std::string multisig_txset;
std::string unsigned_txset;
std::list<key_image_list> spent_key_images_list;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tx_hash_list)
KV_SERIALIZE(tx_key_list)
KV_SERIALIZE(amount_list)
KV_SERIALIZE_OPT(amounts_by_dest_list, decltype(amounts_by_dest_list)())
KV_SERIALIZE(fee_list)
KV_SERIALIZE(weight_list)
KV_SERIALIZE(tx_blob_list)
KV_SERIALIZE(tx_metadata_list)
KV_SERIALIZE(multisig_txset)
KV_SERIALIZE(unsigned_txset)
KV_SERIALIZE(spent_key_images_list)
END_KV_SERIALIZE_MAP()
};
struct COMMAND_RPC_TRANSFER_SPLIT
{
@@ -2882,5 +2938,105 @@ namespace wallet_rpc
};
typedef epee::misc_utils::struct_init<response_t> response;
};
struct COMMAND_RPC_CREATE_TOKEN
{
struct request_t
{
std::string ticker;
uint64_t supply;
uint32_t account_index;
std::set<uint32_t> subaddr_indices;
std::string name;
std::string url;
std::string hash;
uint64_t size;
std::string token_metadata_hex;
bool get_tx_keys;
bool do_not_relay;
bool get_tx_hex;
bool get_tx_metadata;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(ticker)
KV_SERIALIZE(supply)
KV_SERIALIZE(account_index)
KV_SERIALIZE(subaddr_indices)
KV_SERIALIZE_OPT(name, (std::string)"")
KV_SERIALIZE_OPT(url, (std::string)"")
KV_SERIALIZE_OPT(hash, (std::string)"")
KV_SERIALIZE_OPT(size, (uint64_t)0)
KV_SERIALIZE_OPT(token_metadata_hex, (std::string)"")
KV_SERIALIZE_OPT(get_tx_keys, false)
KV_SERIALIZE_OPT(do_not_relay, false)
KV_SERIALIZE_OPT(get_tx_hex, false)
KV_SERIALIZE_OPT(get_tx_metadata, false)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
typedef split_transfer_response response_t;
typedef epee::misc_utils::struct_init<response_t> response;
};
struct COMMAND_RPC_GET_TOKENS
{
struct request_t
{
std::string regex;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE_OPT(regex, (std::string)"")
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
struct response_t
{
std::vector<std::string> tokens;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(tokens)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
struct COMMAND_RPC_GET_TOKEN_INFO
{
struct request_t
{
std::string asset_type;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(asset_type)
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<request_t> request;
struct response_t
{
std::string status;
uint8_t token_type; // erc20 or sal
uint8_t version;
std::string asset_type;
cryptonote::sal_token_t sal_token;
cryptonote::erc_token_t erc_token;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(status)
KV_SERIALIZE(token_type)
KV_SERIALIZE(version)
KV_SERIALIZE(asset_type)
if (token_type == TOKEN_TYPE_ERC20) {
KV_SERIALIZE(erc_token)
} else {
KV_SERIALIZE(sal_token)
}
END_KV_SERIALIZE_MAP()
};
typedef epee::misc_utils::struct_init<response_t> response;
};
}
}
+2 -1
View File
@@ -65,11 +65,12 @@ public:
, const cryptonote::difficulty_type& cumulative_difficulty
, const uint64_t& coins_generated
, uint64_t num_rct_outs
, oracle::asset_type_counts& cum_rct_by_asset_type
, oracle::asset_type_counts_v2& cum_rct_by_asset_type
, const crypto::hash& blk_hash
, uint64_t slippage_total
, uint64_t yield_total
, uint64_t audit_total
, uint64_t token_total
, const cryptonote::network_type nettype
, cryptonote::yield_block_info& ybi
, cryptonote::audit_block_info& abi
+4 -3
View File
@@ -87,11 +87,12 @@ namespace
const difficulty_type& cumulative_difficulty,
const uint64_t& coins_generated,
uint64_t num_rct_outs,
oracle::asset_type_counts& cum_rct_by_asset_type,
oracle::asset_type_counts_v2& cum_rct_by_asset_type,
const crypto::hash& blk_hash,
uint64_t slippage_total,
uint64_t yield_total,
uint64_t audit_total,
uint64_t token_total,
const cryptonote::network_type nettype,
cryptonote::yield_block_info& ybi,
cryptonote::audit_block_info& abi
@@ -178,10 +179,10 @@ static std::unique_ptr<cryptonote::BlockchainAndPool> init_blockchain(const std:
const block *blk = &boost::get<block>(ev);
auto blk_hash = get_block_hash(*blk);
oracle::asset_type_counts num_rct_outs_by_asset_type;
oracle::asset_type_counts_v2 num_rct_outs_by_asset_type;
cryptonote::yield_block_info ybi;
cryptonote::audit_block_info abi;
bdb->add_block(*blk, 1, 1, 1, 0, 0, num_rct_outs_by_asset_type, blk_hash, 0, 0, 0, cryptonote::FAKECHAIN, ybi, abi);
bdb->add_block(*blk, 1, 1, 1, 0, 0, num_rct_outs_by_asset_type, blk_hash, 0, 0, 0, 0, cryptonote::FAKECHAIN, ybi, abi);
}
bool r = bap->blockchain.init(bdb, nettype, true, test_options, 2, nullptr);
Binary file not shown.
Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More