Compare commits

..

152 Commits

Author SHA1 Message Date
Codex Bot 1998d79c3e Keep tx info sync without polyseed API drift
ci/gh-actions/depends / Cross-Mac aarch64 (push) Failing after 7m38s
ci/gh-actions/depends / ARM v7 (push) Has been cancelled
ci/gh-actions/depends / i686 Linux (push) Has been cancelled
ci/gh-actions/depends / i686 Win (push) Has been cancelled
ci/gh-actions/depends / RISCV 64bit (push) Has been cancelled
ci/gh-actions/depends / Cross-Mac x86_64 (push) Has been cancelled
ci/gh-actions/depends / x86_64 Freebsd (push) Has been cancelled
ci/gh-actions/depends / x86_64 Linux (push) Has been cancelled
ci/gh-actions/depends / Win64 (push) Has been cancelled
ci/gh-actions/depends / ARM v8 (push) Has been cancelled
2026-04-06 18:40:08 +02:00
Codex Bot ea4d5a9132 Fix subdirectory linker flag checks
ci/gh-actions/depends / Cross-Mac aarch64 (push) Has been cancelled
ci/gh-actions/depends / ARM v8 (push) Has been cancelled
ci/gh-actions/depends / ARM v7 (push) Has been cancelled
ci/gh-actions/depends / i686 Linux (push) Has been cancelled
ci/gh-actions/depends / i686 Win (push) Has been cancelled
ci/gh-actions/depends / RISCV 64bit (push) Has been cancelled
ci/gh-actions/depends / Cross-Mac x86_64 (push) Has been cancelled
ci/gh-actions/depends / x86_64 Freebsd (push) Has been cancelled
ci/gh-actions/depends / x86_64 Linux (push) Has been cancelled
ci/gh-actions/depends / Win64 (push) Has been cancelled
2026-04-06 18:38:28 +02:00
Codex Bot 585f7b9d05 Add store tx info and return addresses API
ci/gh-actions/depends / Cross-Mac aarch64 (push) Has been cancelled
ci/gh-actions/depends / ARM v8 (push) Has been cancelled
ci/gh-actions/depends / ARM v7 (push) Has been cancelled
ci/gh-actions/depends / i686 Linux (push) Has been cancelled
ci/gh-actions/depends / i686 Win (push) Has been cancelled
ci/gh-actions/depends / RISCV 64bit (push) Has been cancelled
ci/gh-actions/depends / Cross-Mac x86_64 (push) Has been cancelled
ci/gh-actions/depends / x86_64 Freebsd (push) Has been cancelled
ci/gh-actions/depends / x86_64 Linux (push) Has been cancelled
ci/gh-actions/depends / Win64 (push) Has been cancelled
2026-04-06 18:36:27 +02:00
Some Random Crypto Guy b7c707bd4e added hashes file for v1.1.1 2026-04-02 11:02:12 +01:00
Some Random Crypto Guy b57d2c338c bumped version to v1.1.1 2026-04-02 08:48:00 +01:00
Some Random Crypto Guy 28069fc84b Merge branch 'display-rollup-tx-amount-burnt' 2026-04-02 08:44:47 +01:00
Some Random Crypto Guy d7ec62cdbe fixed RPC-based mining issues with block cache template for some miners 2026-04-02 08:43:48 +01:00
auruya 244fc7acaa display amount_burnt for rollup txs 2026-04-01 13:24:10 +03:00
Some Random Crypto Guy 3716330ac7 added hashes file for mainnet release 2026-03-30 14:42:23 +01:00
Some Random Crypto Guy c4e7f6e0f0 implementation of treasury block reward; various fixes for P2Pool and solo-xmrig miners 2026-03-30 12:50:49 +01:00
Some Random Crypto Guy c0862ea66e bumped testnet to (hopefully) final version for Milestone-1 2026-03-27 11:09:25 +00:00
Some Random Crypto Guy 67b41240d2 improved miner_tx security relating to treasury block reward; updated fast-sync checkpoints to block 450000 2026-03-27 11:01:13 +00:00
Some Random Crypto Guy 16110603a1 updated 'fork date set' timestamp to correct value 2026-03-26 08:57:52 +00:00
Some Random Crypto Guy 83934efaad treasury reward payout added for HF11 2026-03-25 19:55:35 +00:00
Some Random Crypto Guy 6fb167308c fixed small asset_type issue for sweep; added HF11 block height 2026-03-23 10:16:26 +00:00
Some Random Crypto Guy d6f95156bc added 1.1.0-rc6 hashes; added consensus block on SAL* tokens 2026-03-19 16:34:50 +00:00
Some Random Crypto Guy 35804afc90 bumped RC version; reset testnet for RC6 2026-03-18 09:30:15 +00:00
Some Random Crypto Guy bc59547714 fix to prevent nefarious AI agents run by Tiamak from paying reduced rollup fees. Thanks for the assist, Tiamak! :-) 2026-03-18 09:27:36 +00:00
Some Random Crypto Guy f2b6068d8b added RC5 hashes file 2026-03-17 14:41:13 +00:00
Some Random Crypto Guy c820967cba fixed issues with ROLLUP circulating supply changes; fixed BURN SAL for HF11+; bumped RC version 2026-03-17 13:34:27 +00:00
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 845d46d7b3 bumped version to v1.0.0 2025-10-01 14:46:23 +01:00
Some Random Crypto Guy e4f8929f2c Merge branch 'develop' 2025-10-01 14:44:09 +01:00
Some Random Crypto Guy c03402d525 updated API methods for 3rd-party wallets 2025-10-01 14:40:47 +01:00
Some Random Crypto Guy d8b18b17f6 bumped RC version 2025-09-30 22:02:11 +01:00
Some Random Crypto Guy 5347266ed9 fixed a couple of wallet cache issues; fixed rescan_bc bug 2025-09-30 22:01:35 +01:00
somerandomcryptoguy 419c26adbf added tx_type setting for payment details - needed for wallet API (#61)
Co-authored-by: Some Random Crypto Guy <somerandomcryptoguy@protonmail.com>
2025-09-12 16:34:02 +01:00
auruya 7727bdb51e Add stake tests (#60)
* update unit tests

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

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

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

* fix is_carrot for stake txs

---------

Co-authored-by: Some Random Crypto Guy <somerandomcryptoguy@protonmail.com>
Co-authored-by: auruya <dream.glorix@gmail.com>
2025-09-10 09:16:14 +01:00
auruya caf8a0a962 fix-prevalidate-protocol-tx-for-carrot (#57) 2025-09-04 20:43:16 +01:00
Some Random Crypto Guy 1b60c08dce fixed issues with STAKE over HF10-HF11 thresholds; bumped RC version 2025-09-04 16:04:29 +01:00
Some Random Crypto Guy 24f9916287 Merge branch 'develop' of https://github.com/salvium/salvium into develop 2025-05-08 09:21:06 +01:00
Some Random Crypto Guy cd22e55296 removed compilation warnings from scanner 2025-03-19 11:26:47 +00:00
144 changed files with 50514 additions and 40527 deletions
+4 -4
View File
@@ -22,7 +22,7 @@ env:
jobs:
build-cross:
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
env:
CCACHE_TEMPDIR: /tmp/.ccache-temp
strategy:
@@ -52,10 +52,10 @@ jobs:
packages: "gperf cmake python3-zmq libdbus-1-dev libharfbuzz-dev"
- name: "Cross-Mac x86_64"
host: "x86_64-apple-darwin"
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev python3-setuptools-git"
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python3-dev python3-setuptools-git"
- name: "Cross-Mac aarch64"
host: "aarch64-apple-darwin"
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev python3-setuptools-git"
packages: "cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python3-dev python3-setuptools-git"
- name: "x86_64 Freebsd"
host: "x86_64-unknown-freebsd"
packages: "clang-8 gperf cmake python3-zmq libdbus-1-dev libharfbuzz-dev"
@@ -105,7 +105,7 @@ jobs:
${{env.CCACHE_SETTINGS}}
make depends target=${{ matrix.toolchain.host }} -j2
- uses: actions/upload-artifact@v4
if: ${{ matrix.toolchain.host == 'x86_64-w64-mingw32' || matrix.toolchain.host == 'x86_64-apple-darwin' || matrix.toolchain.host == 'aarch64-apple-darwin' || matrix.toolchain.host == 'x86_64-unknown-linux-gnu' }}
if: ${{ matrix.toolchain.host == 'x86_64-w64-mingw32' || matrix.toolchain.host == 'x86_64-apple-darwin' || matrix.toolchain.host == 'aarch64-apple-darwin' || matrix.toolchain.host == 'x86_64-unknown-linux-gnu' || matrix.toolchain.host == 'aarch64-linux-gnu' }}
with:
name: ${{ matrix.toolchain.name }}
path: |
+37
View File
@@ -0,0 +1,37 @@
# Use Ubuntu 24.04 as base
FROM ubuntu:24.04 AS builder
ENV DEBIAN_FRONTEND=noninteractive
# Install build dependencies including cross-compilers
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential cmake pkg-config git imagemagick libcap-dev librsvg2-bin libz-dev \
g++-mingw-w64-x86-64 clang gcc-arm-none-eabi binutils-x86-64-linux-gnu libtiff-tools \
g++-x86-64-linux-gnu gcc-x86-64-linux-gnu binutils-aarch64-linux-gnu \
g++-aarch64-linux-gnu gcc-aarch64-linux-gnu crossbuild-essential-amd64 \
libssl-dev libzmq3-dev libunbound-dev libsodium-dev libunwind8-dev liblzma-dev \
libreadline-dev libexpat1-dev libpgm-dev qttools5-dev-tools libhidapi-dev libusb-1.0-0-dev \
libprotobuf-dev protobuf-compiler libudev-dev libboost-all-dev python3 ccache doxygen graphviz \
ca-certificates curl zip libtool gperf automake autoconf \
&& rm -rf /var/lib/apt/lists/*
# Clone the develop branch
WORKDIR /opt
RUN git clone --recursive -b develop https://github.com/salvium/salvium.git
WORKDIR /opt/salvium
# make the script runnable
RUN chmod +x make_releases.sh
# Make sure the output folder exists
RUN mkdir ~/releases
# Expose the releases directory for copying zip files to the host
VOLUME ["~/releases"]
ENTRYPOINT ["/opt/salvium/make_releases.sh"]
# To access the generated zip files on your host, run the container with:
# docker run -v ~/releases:/root/releases <image>
# This will copy the zip files to your host's ~/salvium-releases directory.
+7 -7
View File
@@ -1,6 +1,6 @@
# Salvium One v1.0.0
# Salvium One v1.1.1
Copyright (c) 2023-2024, Salvium
Copyright (c) 2023-2025, Salvium
Portions Copyright (c) 2014-2023, The Monero Project
Portions Copyright (c) 2012-2013 The Cryptonote developers.
@@ -47,7 +47,7 @@ As with many development projects, the repository on GitHub is considered to be
## Supporting the project
Salvium is a 100% community-sponsored endeavor. If you want to join our efforts, the easiest thing you can do is support the project financially. Go to [https://salvium.io/donate/](https://salvium.io/donate/) for more information.
Salvium is a 100% community-sponsored endeavor. If you want to join our efforts, the easiest thing you can do is support the project financially. Go to [https://salvium.io/donate](https://salvium.io/donate) for more information.
## License
@@ -172,7 +172,7 @@ invokes cmake commands as needed.
```bash
cd salvium
git checkout v1.0.0
git checkout v1.1.1
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.1.1
```
* 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.1.1'. 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.1.1
```
* If you are on a 64-bit system, run:
+1 -1
View File
@@ -6,7 +6,7 @@ macro(CHECK_LINKER_FLAG flag VARIABLE)
message(STATUS "Looking for ${flag} linker flag")
endif()
set(_cle_source ${CMAKE_SOURCE_DIR}/cmake/CheckLinkerFlag.c)
set(_cle_source ${CMAKE_CURRENT_SOURCE_DIR}/cmake/CheckLinkerFlag.c)
set(saved_CMAKE_C_FLAGS ${CMAKE_C_FLAGS})
set(CMAKE_C_FLAGS "${flag}")
+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 @@
e032e42ebac862bf90d71f0a231d9f3ddb5583e321ec153ee05144d87629980b salvium-v1.1.0-macos-aarch64.zip
0f31f09cf7be38b50a35510172bc94b7a4fb07c7107c79c4f055754c282c99f5 salvium-v1.1.0-macos-x86_64.zip
4424fd93391daab7eee47c54ab7aad7810a16f4c866209d41ba016d984605ffb salvium-v1.1.0-ubuntu22.04-linux-aarch64.zip
d1a5138f892189dfccc1d51d72ce24147fe6f1a2a5d465d350651426a71282a4 salvium-v1.1.0-ubuntu22.04-linux-x86_64.zip
bb9c9175726b82e061a6a332a27a6845805be4779928df3abbbd5c0f54691c9a salvium-v1.1.0-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
+5
View File
@@ -0,0 +1,5 @@
23a03277e922c3f41ba6ddb0efc7581c3287d9f3faa2ddd19cc2d018a6797701 salvium-v1.1.0-rc5-ubuntu22.04-linux-x86_64.zip
94409b190eae890792b2d04cfffe148828dc23d658cd54a8d7802b12a9f274a4 salvium-v1.1.0-rc5-ubuntu22.04-linux-aarch64.zip
76cc02603cb21cd0729f0e5c9a39bb32310428b15580458ec8d74dcba39c9319 salvium-v1.1.0-rc5-macos-x86_64.zip
dc477718bfb370ecbafebf3e1736df5978200302f13542374f1b1fd43e07ab45 salvium-v1.1.0-rc5-macos-aarch64.zip
0e9b81eeaa32a4709a1cbd4c198721455a3c8cc86c1f1915cfc74af7885f8fee salvium-v1.1.0-rc5-win64.zip
+5
View File
@@ -0,0 +1,5 @@
b712adec8fa6bbcbe461b0748649e4c9e4fb61934fc1adb064779afed28c0758 salvium-v1.1.0-rc6-macos-aarch64.zip
8635955ff784f936a8b8de5be267e5dcdc0b55dfbf0b6f65ba24d098d8b8ab00 salvium-v1.1.0-rc6-macos-x86_64.zip
79b65ba9a074aeba87f03d83a07c3a6c51815d376049b3136a38c1a33260bfac salvium-v1.1.0-rc6-ubuntu22.04-linux-aarch64.zip
9031763ad60d1c8c572c76c5cb51a96b1680b9708c287264388f3d411bda464f salvium-v1.1.0-rc6-ubuntu22.04-linux-x86_64.zip
e3a8e53e092041ed9ed32c98d5d0ecf548f42232402748f862371ba21e1f1f9f salvium-v1.1.0-rc6-win64.zip
+5
View File
@@ -0,0 +1,5 @@
d22bbe19fa5e7eb7ebaf95eee336d73cbd59ded7b5e737213e89a8b84d5791eb salvium-v1.1.1-macos-aarch64.zip
262e2bdffc3c4fee89ec79451a6181175671db5874726d359077c4479754076e salvium-v1.1.1-macos-x86_64.zip
72b37fa30df6b136dba380b88e9ccc4d66804d8286a221a87e944e337f4ba593 salvium-v1.1.1-ubuntu22.04-linux-aarch64.zip
33321419bb426507de0f5de7c1977e436ca34bf4db620fa00eede1fa318b7994 salvium-v1.1.1-ubuntu22.04-linux-x86_64.zip
33e7c1e1bc4e5a1f9c40482eba993a5efb97a8feedfb36dca863bbb5bd9e794f salvium-v1.1.1-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
File diff suppressed because it is too large Load Diff
+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
+44 -7
View File
@@ -209,17 +209,35 @@ 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") {
// check here
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 +257,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 +273,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 +294,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 +417,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 +453,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 +476,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 +539,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)
{
+186 -34
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:
@@ -1381,12 +1564,7 @@ namespace cryptonote
for (const auto &o: tx.vout)
{
if (hf_version >= HF_VERSION_ENFORCE_CARROT)
{
// from v10, require outputs be carrot outputs
CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_carrot_v1), false, "wrong variant type: "
<< o.target.type().name() << ", expected txout_to_carrot_v1 in transaction id=" << get_transaction_hash(tx));
} else if (hf_version >= HF_VERSION_CARROT) {
if (hf_version >= HF_VERSION_CARROT) {
if (tx.type != cryptonote::transaction_type::PROTOCOL) {
CHECK_AND_ASSERT_MES(o.target.type() == typeid(txout_to_carrot_v1), false, "wrong variant type: "
<< o.target.type().name() << ", expected txout_to_carrot_v1 in transaction id=" << get_transaction_hash(tx));
@@ -1411,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);
+2 -1
View File
@@ -175,7 +175,8 @@ namespace cryptonote
uint64_t seed_height;
crypto::hash seed_hash;
if(!m_phandler->get_block_template(bl, m_mine_address, di, height, expected_reward, extra_nonce, seed_height, seed_hash))
crypto::public_key miner_reward_tx_key;
if(!m_phandler->get_block_template(bl, m_mine_address, di, height, expected_reward, extra_nonce, seed_height, seed_hash, miner_reward_tx_key))
{
LOG_ERROR("Failed to get_block_template(), stopping mining");
m_forced_stop = true;
+1 -1
View File
@@ -47,7 +47,7 @@ namespace cryptonote
struct i_miner_handler
{
virtual bool handle_block_found(block& b, block_verification_context &bvc) = 0;
virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash) = 0;
virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key) = 0;
protected:
~i_miner_handler(){};
};
+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);
+38 -39
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
@@ -88,8 +89,14 @@
#define PREMINE_AMOUNT_UPFRONT ((uint64_t)650000000000000ull) // 3.4% of MONEY_SUPPLY
#define PREMINE_AMOUNT_MONTHLY ((uint64_t)65000000000000ull) // 8.6%/24 of MONEY_SUPPLY
#define TREASURY_SAL1_MINT_AMOUNT ((uint64_t)65000000000000ull) // 650K
#define TREASURY_SAL1_MINT_COUNT 16 // 16 times
#define TREASURY_SAL1_MINT_AMOUNT ((uint64_t)130000000000000ull) // 1.3M
#define TREASURY_SAL1_MINT_COUNT 8 // 8 times
#define CREATE_TOKEN_LOCK_PERIOD 10
// HF11 block reward split
#define BLOCK_REWARD_TREASURY_PCT 25 // to treasury, 21600-block unlock
#define BLOCK_REWARD_STAKER_PCT 20 // to stakers via yield
#define DIFFICULTY_TARGET_V2 120 // seconds
#define DIFFICULTY_TARGET_V1 60 // seconds - before first fork
@@ -244,14 +251,14 @@
#define HF_VERSION_AUDIT2 8
#define HF_VERSION_AUDIT2_PAUSE 9
#define HF_VERSION_CARROT 10
#define HF_VERSION_ENFORCE_CARROT 11
#define HF_VERSION_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 18
#define STAGENET_VERSION 1
#define PER_KB_FEE_QUANTIZATION_DECIMALS 8
@@ -316,26 +323,19 @@ namespace config
const uint64_t STAKE_LOCK_PERIOD = 30*24*30;
const uint64_t TREASURY_SAL1_MINT_PERIOD = 30*24*30; // 1 month of blocks
std::string const TREASURY_ADDRESS_CARROT = "SC11ksHLFhy7H1yMk9bUZvADG1Z9ZkR1T5QMknm3RbGBbgdPkyanB2WBb5TER3MsiwJC5BnyoiYs2DBcvAfAm6JQ537iNKdtvm";
std::string const TREASURY_ADDRESS = "SaLvdZR6w1A21sf2Wh6jYEh1wzY4GSbT7RX6FjyPsnLsffWLrzFQeXUXJcmBLRWDzZC2YXeYe5t7qKsnrg9FpmxmEcxPHsEYfqA";
// treasury payout {tx-key, output-key, anchor_enc, vie_tag} tuples
// treasury payout {tx-key, output-key, anchor_enc, view_tag} tuples
const std::map<uint64_t, std::tuple<std::string, std::string, std::string, std::string>> TREASURY_SAL1_MINT_OUTPUT_DATA = {
{1000000ULL, {"a1bdd1da651fbbb845232816e1aa2d4ff29b790f10bbd4f574a012f1199e15a4", "b0733ab6f251b16458efa9ebb3fb99bd54d43173b5768fe9ffc42e0fe46ae3a8", "00", "00"}},
{1020000ULL, {"47996eccbcc078b06d0f6ece37bf3a700c2bd60adfdd898b22096f16a9ad315c", "fd6bcceb4799ee067d59b97a6f66a0f9a70f134220259d3b4d6a2278ba4aca4c", "00", "00"}},
{1040000ULL, {"a3e6754a849b80c21a77e6065fefdae29eeeabf17c407453356244a00545bdb8", "3d395454df1452d715d27190e022b20395871c99af578f7251c3f9752e0274a6", "00", "00"}},
{1060000ULL, {"0d5e97a910e0f9c606ad9c711b6595aaed142d857cde2efa519112b9a29240d5", "56c29e28bdcf4f20b4b45906b93ae7c4bf9ee82e18cd45543cb69a14ce5efb88", "00", "00"}},
{1080000ULL, {"495fa363de88915aa8b74818c4b80715a882a688b4f7127ab7cd3b6885f3567a", "d42dfe0da5579c82e8255eba8c0a17170023f14a6a5030da6abf9f10abb52cbb", "00", "00"}},
{1100000ULL, {"85ea10ec40390e4f406446fb519e974d89536154045c6df28bb3b538b254e20d", "0ce2b7dd3a8ce8b596889dac8081a62f98fd70f1f043944ab4ac592c3c59e77b", "00", "00"}},
{1120000ULL, {"40f201b38a319dda81e7201e57fea7924067a4a332ed71b8e51ec29ac2d67310", "8289aa6963b98d1034e94eae55d8be6b33d0a88f14f174ebcbaec70837986c7c", "00", "00"}},
{1140000ULL, {"c5a648cc7846341357b7b4653a58f9eb4800d88b5de587bceec7a5c28f98d05a", "3f308a203845d88e5e728fcebcdcea1f90e2f424d461617993c672a6138ad2d8", "00", "00"}},
{1160000ULL, {"4c51d6550b8eeb6cc8f0d395cc83a5f90ec2a4d86501b3f68da48d618ccf5711", "53f0bd8cebeefb3a88fffa5d7f6ad43d4712608ded561732467ca499df940454", "00", "00"}},
{1180000ULL, {"ce2f5d82118fed03d5e269e285fc16189a6cd34f38999e5c055a5dea5fce61bc", "f7fc6948b194d9bd6f2df6ecb83f04e6c8d1a2556a63fedb310a4631fe1bfc42", "00", "00"}},
{1200000ULL, {"6248028fd77fb02b5c6ea72dea10b417891a2da7aaf9565aed382e063b4981a3", "63986e1177499bdb23cd49afb519ec18f38cb1b0c386220b376d8ffdc2e37890", "00", "00"}},
{1220000ULL, {"6adcb695aa5d6d01133c68900f29e501e9549816e827ea0c164bbc78f3534dd6", "6a440ccb18f5e703e8000de3865ac40d4c18f081270d32eef377dc831f28d8d0", "00", "00"}},
{1240000ULL, {"b97a4d2259480f34f20e41c489ab5c2e5ae9ee84d8672a7eff8012f2260e121e", "e6eb9147ff40e22209d321d0f1bfbfe20acf5ceb6b9d0bfb13688ad28aa1232e", "00", "00"}},
{1260000ULL, {"4fd214602a36902f22d16927244c456e8cc5a406a9570131f138a028214ffdf0", "34060b8bd96009b9b298280ebd84fa9587fa8c9df6fb5ad0270fb6cd2098885c", "00", "00"}},
{1280000ULL, {"9d60178ec6d6599d7a31298f2559fb9c3111f2c70494b3a1638db877ea55b808", "7985ed03856a929663e954738d0938713407717835f760c7ca4d54844a128c91", "00", "00"}},
{1300000ULL, {"cd65718eab234bf419332e53bd2f48e2ade70af48c5e126ab5080321e1493dfc", "581cb4cca7a0a029ee2cac51dfc00a0c3a657d2eaf67ed3c6ae7bacc11b4f007", "00", "00"}},
{334750, {"1b2cd3ff56aa77c0cbab0473bfb96697ebdd0b25ad230136bfe41d5dc1ef265f","718cf02eabca157fd7ad7f8537db217624bfe1ca99dd09e758357e7000a5e57a","789cca3def51fb879eb7fbca271869b7","79bd0c"}},
{356350, {"b51acbf35265d09f3cfb83dcabde2746991ddf0d30b5a4ecc34043b349a77031","9dc0d2e9534cdccf83494687c55c67c8c1b29834acf97cce53124a08a9549231","588ebc2918d06c009a18a28a8ab76694","ab8c23"}},
{377950, {"771c6865980493846cbb049b34f72b937878cf799ae1126775df35064cf53526","60d340613c7721aeb03f2de1b56e2916d6cb023cc668acaca00e1606e0bb7f64","198bb0e5880c8abf8108158284ca7637","9b93f2"}},
{399550, {"d9159f9bb654230382a69bb7a739c14f9d506c21dba5be9f4cdf66126ed1b24e","c3b34e7f9977f31f659abd9306b650416a674ed7bcfb8c75b257040a1a06b8b9","6f0de181716e0a9e100228d58f11d42c","4c87fd"}},
{421150, {"1e2ab1ae204e7a9375f5b57fed2524e2d1a4702c4bbc6d420eb65d92af44c60c","826ecca0e2837860a6d2be89a410ff6aab22c5f5a66bd0a318de3abe646aa26a","bad663aa8253c40d00de0a2d1dfca60f","4a04d7"}},
{442750, {"18267940a2b37c82850f3fb83d935281ee5cc436b0797ec10ca26c9cc3c0ef01","77bdec7bbc4cabd84cc8cf0666e1fdfe299480b9b2a4cf52cc959d728fa899ab","a8807121e33471cf18aa82af4826c9a7","d35d35"}},
{464350, {"9ab50223bfecec2508e8233540dc13c3fc7e15a8d77ecb90939afee96859724d","675a1b3f2a2cd2bd0e7ec073c426125d95e12ad13e541bf019925c481c2bca15","591d2f2331832c347654ab255ad97e2d","d87c9c"}},
{485950, {"c0d7299fe0b873687e7319f1a0d1f67712ef3ccf6a579d96c5f99743add6f029","dd3d17b5145b56377e023c169ae858cdf48bd6c23e8b2a77e02765ce316e3ca7","6a9e74df01762e2b32db1dc4d69dc3e9","75debe"}}
};
// Hash domain separators
@@ -428,26 +428,20 @@ namespace config
"KWv3Vo1/Gny+1vfaxsXhBQiG1KlHkafNGarzoL0WHW4ocqaaqF5iv8i35A==\n"
"-----END PUBLIC KEY-----\n";
std::string const TREASURY_ADDRESS = "SaLvTyLFta9BiAXeUfFkKvViBkFt4ay5nEUBpWyDKewYggtsoxBbtCUVqaBjtcCDyY1euun8Giv7LLEgvztuurLo5a6Km1zskZn36";
std::string const TREASURY_ADDRESS_CARROT = "SC1TouvX6e4HmkAqsU6AAXLRjgeZnnKHjSpVfMHepTXramNMT2P47AsDmteLH81wdPR2DwMg3cxKvgrhUBeDSUW6MhM3sQb92we";
std::string const TREASURY_ADDRESS = "SaLvTyL2pN2SCAPRxwDQ7qjhdg46VbhZrGZTp2wHKJ5sK434a8ivEH35eWp2FTcmyW6LY6ExfBb9chmQ9xAL1eJyZ5FQjtQGTis3v";
// treasury payout {tx-key, output-key, anchor_enc, vie_tag} tuples
// check address type before future development
const std::map<uint64_t, std::tuple<std::string, std::string, std::string, std::string>> TREASURY_SAL1_MINT_OUTPUT_DATA = {
{1100, {"0d82afb3ae9c5de25a06da49e24e64d37ba05d6144f9f6153cea325c61407106","81440913c1484891d25010986806a62d1fae1b2fe045e3ac1396413638d594f1","40864bbf80397c05516368a1397eb5c3","220bda"}},
{1120, {"a032f2de4d3b5d880d3a636d9786a4a2f586bee360c323bc7521f987a2ada51b","cf8e5eeb5d37996d86c9c4c88fabaabc1ec77bab9cd5ce610a61b49f1e079ce2","9877a989e0216f76a8cdf0a568e57d45","017549"}},
{1140, {"51da7a4c34aa20a800ada615a931fbba5fd1b9f7df78f0f911097ad9d99dc230","dd784a5bf1adf9c01cb44946ca094ed4d38f8d9cccf8edfdab397db3b7631dbf","97314be452f184b54f16a558ee8a7dbe","357ac3"}},
{1160, {"a0cf49118c62cea834fabae83e12f26939ce170f308adadacf7275226e5cdf6d","7ec9cb2dfac8dfa45bab1bab2b1e31a4291a9e3d939e9c193740cc191d1b720d","4e950acc71ffd4f3835ec79202dc46fd","e7da3a"}},
{1180, {"5193e35f6cd3ff8408a442be5eaf4aa1871419ecb0c485546e9d995c4c9c2730","39fb9792b88290b3377036a150a6681334ebe5cb201e2005d2c1d7b212b3df4d","cb458163806a277c7b3513df0d5b0028","1939b5"}},
{1200, {"a2c71a7381bc48576f4cb4313cb6264d2a6e82cd2ddcbbd5460bce1db8d5021e","d0c46f29f4bbdd5538d64a438c0ab93cb0586e67fbfc7d1671f0c9809d6a7fac","af38cc2ddccb9229728ef704e360e15a","8a8dc1"}},
{1220, {"8358105081510e907c98da59636eef777460513d827aba400efbb81d82aead4c","b2b8e2f9bb13e7527d0ee1bc3ebf0ca5057139ee4f9302251455b9f27dca3b16","dc226161830e952b2533fe34b1dd3c48","fb7c67"}},
{1240, {"90b0bc153b97a93eefd59af9b47d26c0c91aadda6203de91a15f2f76fc949074","06e6a11b8742ee78beafe89f894fb9078a514ee6dd4e099182ac982ead37eacc","4fbfd0f3c2b8d514f2c675b37b9f0b6f","3308ad"}},
{1260, {"cd10148b3175804b17e3c68a6a5556364d585dee89953904b32f005a5f6d8f14","d9d6f64119776e9c97fe2fb540cdb5f40bea0c40127fda0f1f2b831cb7fa7d2b","9c21b83d29324e2d91774a623219ae28","1d647c"}},
{1280, {"5963697782de7db5111d12c853f3fec0dd53f597d80cc376c7945f1d4afa224b","cc5e5b1273c10016dc855ec1d9bfa0bcf4736a748125f5341717ad9d080fbba0","19dd1f9a0cac118564e2ccb9f36be53a","7ab2c2"}},
{1300, {"cf1d4d2d21b7b3f1a54889e0688a4654687e28a302a2ac3ad046ac2ad323f97a","5b5926da5e9199f190e86073ebd09ccb867faf1e9887c9e52a47c194915b2575","5775fa47af3c7226f2ec3017470410c9","2b03b3"}},
{1320, {"b565ec8842f3e6c22c7a23eb23f0e5684b462d2261681dff7b2ce71ab02ff170","4f94cf4d811574c9fcc27888cc972ebc6c01ae97146b9882d397b77c9232d1fd","9d142686627c918a26b6c6853b911143","685b18"}},
{1340, {"50d1b1bcacf36ba14b23b3827e36a02a7610dd3199a9f1b9d0a23ebcbd1c2d54","38818e41cf0ce7d54391978801c58087ddd5fe517dadb5e61d840f27a7a81a52","55328f4bff5120d44c5d76fc5a9dfdbd","d17be2"}},
{1360, {"7657bf97a9be9beac5f999a1e2c1226613849c41c12df5d7185d4d6613a27328","63d96a47f29fab0e727d13905cc843df40ccb76b14dc48857d0dd9ec0873e8c5","ab4f7ffa6431452f0fac7eec33e89e3e","5acd14"}},
{1380, {"845e4cbe9b9d2462fae2225da698d6a4e18340f06b312545ee9cfda8127abd41","cc65f1f22f863b31f3c4a53ac3987186c2a699f387a2bf08d8dfed54037912c6","bda3aa3bdd2b42e8183c1013e4350c98","d4a418"}},
{1400, {"4b11413fcba49df61537427ff35fdd47ee12a8467751236ff22681bbea343d2c","32d22bba59559489f422298ae653ef55ec8c87e8705daf4c89da7187c8574cbd","8868e1c1d788ce4560beb4afbe77aa5a","5a35d9"}}
{1100, {"71336a480440ed24a53b2cfd5a5292d1e618c3e843637227883ea2cc42fb346f","a135f59a05ea9e33539e4502b187b4789cc7fba79616c6902a902cc6601f0359","7f1c6970232254a9b13f7f063df2a853","62073c"}},
{1120, {"2cc49b182addc0106b601c9876c01a4b06532c05f9dd9179b2c4f47e5c7c0d74","fd62e59324389f37d0bb628a39f413c11be34d572ec3a40f465e008b9dfd5e0c","fbf8584222e299bb748009eaf2177123","b76a8d"}},
{1140, {"50f1dd5244bb6fdc62b5a18b868a4dc9e9ad15f4a7a54e5a98084a3a5213c846","942de90a3347df3e962d9b0a2745f8aedb265a8caf73aabef9aa713f4ec3e493","933702e29d680bf21ad559a2d3243621","dae32b"}},
{1160, {"114a1fef3a03c81b3b6308fb4e51db42d86945d8f71ee8798749a4aa2d8bfe57","fd77f0289b7e25ae68c86f4cd2c72ac0211a4d0e7a31c84b8d58a33bbd3c511b","d8048bb76b9313768a4c69100cba6d43","46b0a3"}},
{1180, {"cf7fe9d97a7a4cb881bff1d37bb6981bcb0691649c465d660522a43f14b32849","b76908d56108e7111374f60993e9411def7bb2899118f20f49e67a801936a1ad","f203a5acd48f93f1451d32d70716e585","d72097"}},
{1200, {"415e8fa4fb6bed8bc59591883f5e098dde6179e980eccfc4cbb3a64c4168cf47","5426c588d7c5750c76b526ef268b45f7414e2ddea40218ee92d123564c5114c4","fb1926bbef8c70ebd796ae77dffca07d","351edf"}},
{1220, {"1f47cce00e8e1c77cb3ff007491d261dae75de5bbfdbe0f4913d74a8503aaa5e","4d8aac299306989aa169982d89457852a1bab858c668c9b740d96b682bb1961b","d8c0be7926ce704290cfd8d5e0500f33","750003"}},
{1240, {"3afd93c684638d54e68f5d8d417863035f12482251feea5cf7515725a2bb0f4f","7895e6baded473b093b57993ae3497a47b0ea78cf35b37d1387f226f445d9018","76a0c37e6f3d9d508c4bce1d7162514e","09e146"}}
};
}
@@ -480,6 +474,7 @@ namespace config
"KWv3Vo1/Gny+1vfaxsXhBQiG1KlHkafNGarzoL0WHW4ocqaaqF5iv8i35A==\n"
"-----END PUBLIC KEY-----\n";
std::string const TREASURY_ADDRESS_CARROT = ""; // TODO: generate stagenet Carrot treasury address ?
std::string const TREASURY_ADDRESS = "fuLMowH85abK8nz9BBMEem7MAfUbQu4aSHHUV9j5Z86o6Go9Lv2U5ZQiJCWPY9R9HA8p5idburazjAhCqDngLo7fYPCD9ciM9ee1A";
// treasury payout {tx-key, output-key, anchor_enc, view_tag} tuples
@@ -534,6 +529,7 @@ namespace cryptonote
uint64_t TREASURY_SAL1_MINT_PERIOD;
std::map<uint8_t, std::pair<uint64_t, std::pair<std::string, std::string>>> const AUDIT_HARD_FORKS;
std::string TREASURY_ADDRESS;
std::string TREASURY_ADDRESS_CARROT;
std::map<uint64_t, std::tuple<std::string, std::string, std::string, std::string>> TREASURY_SAL1_MINT_OUTPUT_DATA;
};
inline const config_t& get_config(network_type nettype)
@@ -557,6 +553,7 @@ namespace cryptonote
::config::TREASURY_SAL1_MINT_PERIOD,
::config::AUDIT_HARD_FORKS,
::config::TREASURY_ADDRESS,
::config::TREASURY_ADDRESS_CARROT,
::config::TREASURY_SAL1_MINT_OUTPUT_DATA
};
static const config_t testnet = {
@@ -578,6 +575,7 @@ namespace cryptonote
::config::testnet::TREASURY_SAL1_MINT_PERIOD,
::config::testnet::AUDIT_HARD_FORKS,
::config::testnet::TREASURY_ADDRESS,
::config::testnet::TREASURY_ADDRESS_CARROT,
::config::testnet::TREASURY_SAL1_MINT_OUTPUT_DATA
};
static const config_t stagenet = {
@@ -599,6 +597,7 @@ namespace cryptonote
::config::stagenet::TREASURY_SAL1_MINT_PERIOD,
::config::stagenet::AUDIT_HARD_FORKS,
::config::stagenet::TREASURY_ADDRESS,
::config::stagenet::TREASURY_ADDRESS_CARROT,
::config::stagenet::TREASURY_SAL1_MINT_OUTPUT_DATA
};
switch (nettype)
+521 -87
View File
@@ -31,6 +31,7 @@
#include <algorithm>
#include <cstdio>
#include <limits>
#include <boost/asio/dispatch.hpp>
#include <boost/filesystem.hpp>
#include <boost/range/adaptor/reversed.hpp>
@@ -38,6 +39,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"
@@ -56,6 +58,8 @@
#include "crypto/hash.h"
#include "cryptonote_core.h"
#include "ringct/rctSigs.h"
#include "carrot_core/payment_proposal.h"
#include "carrot_impl/format_utils.h"
#include "common/perf_timer.h"
#include "common/notify.h"
#include "common/varint.h"
@@ -66,6 +70,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 +883,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 +982,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 +1354,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");
}
@@ -1403,14 +1412,22 @@ bool Blockchain::prevalidate_protocol_transaction(const block& b, uint64_t heigh
CHECK_AND_ASSERT_MES(b.protocol_tx.vin[0].type() == typeid(txin_gen), false, "coinbase protocol transaction in the block has the wrong type");
CHECK_AND_ASSERT_MES(b.protocol_tx.version > 1, false, "Invalid coinbase protocol transaction version");
if (hf_version >= HF_VERSION_CARROT) {
CHECK_AND_ASSERT_MES(b.protocol_tx.version == TRANSACTION_VERSION_CARROT || 2, false, "protocol transaction has wrong version");
CHECK_AND_ASSERT_MES(b.protocol_tx.type == cryptonote::transaction_type::PROTOCOL, false, "protocol transaction has wrong type");
}
// Work out what the HF version _was_ when the STAKE outputs were created
uint64_t stake_lock_period = get_config(m_nettype).STAKE_LOCK_PERIOD;
uint8_t hf_version_submitted = get_ideal_hard_fork_version(height - stake_lock_period - 1);
if (hf_version >= HF_VERSION_ENFORCE_CARROT) {
CHECK_AND_ASSERT_MES(b.protocol_tx.version == TRANSACTION_VERSION_CARROT, false, "protocol transaction has wrong version");
if (hf_version >= HF_VERSION_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 {
CHECK_AND_ASSERT_MES(b.protocol_tx.version == 2, false, "protocol transaction has wrong version");
}
CHECK_AND_ASSERT_MES(b.protocol_tx.type == cryptonote::transaction_type::PROTOCOL, false, "protocol transaction has wrong type");
} else {
hf_version_submitted = hf_version;
}
// for v2 txes (ringct), we only accept empty rct signatures for protocol transactions,
@@ -1433,8 +1450,7 @@ bool Blockchain::prevalidate_protocol_transaction(const block& b, uint64_t heigh
return false;
}
CHECK_AND_ASSERT_MES(check_output_types(b.protocol_tx, hf_version), false, "protocol transaction has invalid output type(s) in block " << get_block_hash(b));
CHECK_AND_ASSERT_MES(check_output_types(b.protocol_tx, hf_version_submitted), false, "protocol transaction has invalid output type(s) in block " << get_block_hash(b));
return true;
}
//------------------------------------------------------------------
@@ -1475,7 +1491,53 @@ std::tuple<bool, size_t> Blockchain::validate_treasury_payout(const transaction&
return {false, 0};
}
return {true, output - tx.vout.begin()};
// Burning-bug guards: verify view_tag, anchor_enc, and D_e from the hardcoded config
// so a miner cannot tamper with those fields while keeping K_o correct.
// Check view_tag
carrot::view_tag_t expected_view_tag{};
if (!epee::string_tools::hex_to_pod(viewtag, expected_view_tag)) {
MERROR_VER("treasury payout: failed to deserialize expected view_tag");
return {false, 0};
}
if (target.view_tag != expected_view_tag) {
MERROR_VER("treasury payout view_tag mismatch (burning bug: K_o correct but view_tag tampered)");
return {false, 0};
}
// Check anchor_enc
carrot::encrypted_janus_anchor_t expected_anchor_enc{};
if (!epee::string_tools::hex_to_pod(anchor_enc, expected_anchor_enc)) {
MERROR_VER("treasury payout: failed to deserialize expected anchor_enc");
return {false, 0};
}
if (0 != memcmp(&target.encrypted_janus_anchor, &expected_anchor_enc, sizeof(expected_anchor_enc))) {
MERROR_VER("treasury payout anchor_enc mismatch (burning bug: K_o correct but anchor tampered)");
return {false, 0};
}
// Check D_e (enote ephemeral pubkey)
mx25519_pubkey expected_tx_key_raw{};
if (!epee::string_tools::hex_to_pod(tx_key, expected_tx_key_raw)) {
MERROR_VER("treasury payout: failed to deserialize expected tx_key");
return {false, 0};
}
const crypto::public_key expected_De = carrot::raw_byte_convert<crypto::public_key>(expected_tx_key_raw);
const size_t output_idx = output - tx.vout.begin();
const crypto::public_key single_tx_pubkey = cryptonote::get_tx_pub_key_from_extra(tx);
const std::vector<crypto::public_key> additional_tx_pubkeys = cryptonote::get_additional_tx_pub_keys_from_extra(tx);
crypto::public_key actual_De{};
if (!additional_tx_pubkeys.empty() && output_idx < additional_tx_pubkeys.size()) {
actual_De = additional_tx_pubkeys[output_idx];
} else if (single_tx_pubkey != crypto::null_pkey) {
actual_De = single_tx_pubkey;
}
if (actual_De != expected_De) {
MERROR_VER("treasury payout D_e mismatch (burning bug: K_o correct but ephemeral key tampered)");
return {false, 0};
}
return {true, output_idx};
}
//------------------------------------------------------------------
// This function validates the miner transaction reward
@@ -1498,42 +1560,23 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
treasury_index_in_tx_outputs = index_in_tx_outputs;
}
//validate reward
// Calculate reward being issued
uint64_t money_in_use = 0;
CHECK_AND_ASSERT_MES(b.miner_tx.amount_burnt > 0 || height == 0, false, "invalid tx.amount_burnt for miner_tx");
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;
for(size_t i = 0; i < b.miner_tx.vout.size(); i++)
{
// skip the treasury output
if (treasury_payout_exists && (i == treasury_index_in_tx_outputs)) {
continue;
}
CHECK_AND_ASSERT_MES(money_in_use + b.miner_tx.vout[i].amount >= money_in_use, false, "miner transaction is overflowed by output amount");
money_in_use += b.miner_tx.vout[i].amount;
}
partial_block_reward = false;
switch (version) {
case HF_VERSION_BULLETPROOF_PLUS:
case HF_VERSION_ENABLE_N_OUTS:
case HF_VERSION_FULL_PROOFS:
case HF_VERSION_ENFORCE_FULL_PROOFS:
case HF_VERSION_SHUTDOWN_USER_TXS:
case HF_VERSION_SALVIUM_ONE_PROOFS:
case HF_VERSION_AUDIT1_PAUSE:
case HF_VERSION_AUDIT2:
case HF_VERSION_AUDIT2_PAUSE:
case HF_VERSION_CARROT:
case HF_VERSION_ENFORCE_CARROT:
if (b.miner_tx.amount_burnt > 0) {
CHECK_AND_ASSERT_MES(money_in_use + b.miner_tx.amount_burnt > money_in_use, false, "miner transaction is overflowed by amount_burnt");
money_in_use += b.miner_tx.amount_burnt;
}
if (already_generated_coins != 0)
CHECK_AND_ASSERT_MES(money_in_use / 5 == b.miner_tx.amount_burnt, false, "miner_transaction has incorrect amount_burnt amount");
break;
default:
assert(false);
break;
}
// Make sure the TOTAL REWARD is correct
uint64_t median_weight = m_current_block_cumul_weight_median;
if (!get_block_reward(median_weight, cumulative_block_weight, already_generated_coins, base_reward, version))
{
@@ -1550,11 +1593,119 @@ bool Blockchain::validate_miner_transaction(const block& b, size_t cumulative_bl
MDEBUG("coinbase transaction doesn't use full amount of block reward: spent: " << money_in_use << ", block reward " << base_reward + fee << "(" << base_reward << "+" << fee << ")");
return false;
}
// HF-specific additional checks
switch (version) {
case HF_VERSION_BULLETPROOF_PLUS:
case HF_VERSION_ENABLE_N_OUTS:
case HF_VERSION_FULL_PROOFS:
case HF_VERSION_ENFORCE_FULL_PROOFS:
case HF_VERSION_SHUTDOWN_USER_TXS:
case HF_VERSION_SALVIUM_ONE_PROOFS:
case HF_VERSION_AUDIT1_PAUSE:
case HF_VERSION_AUDIT2:
case HF_VERSION_AUDIT2_PAUSE:
case HF_VERSION_CARROT:
if (already_generated_coins != 0) {
// HF1-10: block reward split is 80% miner + 20% staker (amount_burnt)
CHECK_AND_ASSERT_MES(money_in_use / 5 == b.miner_tx.amount_burnt, false, "miner_transaction has incorrect amount_burnt amount");
}
break;
case HF_VERSION_ENABLE_TOKENS:
// HF11: block reward split is 60% miner + 25% treasury + 15% staker (amount_burnt)
if (already_generated_coins != 0) {
// Validate treasury share: one output must equal block_reward * 25 / 100
uint64_t expected_treasury_block_reward = money_in_use * BLOCK_REWARD_TREASURY_PCT / 100;
// Validate staker share: amount_burnt == block_reward * 15 / 100
uint64_t expected_staker_block_reward = (money_in_use - expected_treasury_block_reward) * BLOCK_REWARD_STAKER_PCT / 100;
CHECK_AND_ASSERT_MES(expected_staker_block_reward == b.miner_tx.amount_burnt, false,
"miner_transaction has incorrect amount_burnt for HF11 (expected " << expected_staker_block_reward << ", got " << b.miner_tx.amount_burnt << ")");
uint64_t expected_miner_block_reward = money_in_use - b.miner_tx.amount_burnt - expected_treasury_block_reward;
// treasury_destination
address_parse_info treasury_addr_info;
bool addr_ok = cryptonote::get_account_address_from_str(treasury_addr_info, m_nettype, get_config(m_nettype).TREASURY_ADDRESS_CARROT);
CHECK_AND_ASSERT_MES(addr_ok, false, "Failed to parse treasury address for validation");
carrot::CarrotDestinationV1 treasury_destination;
carrot::make_carrot_main_address_v1(treasury_addr_info.address.m_spend_public_key,
treasury_addr_info.address.m_view_public_key,
treasury_destination);
// deterministic janus anchor
const carrot::janus_anchor_t treasury_anchor = get_deterministic_treasury_anchor_from_height(height);
const carrot::CarrotPaymentProposalV1 treasury_proposal{
.destination = treasury_destination,
.amount = expected_treasury_block_reward,
.asset_type = "SAL1",
.randomness = treasury_anchor
};
carrot::CarrotCoinbaseEnoteV1 expected_enote;
carrot::get_coinbase_output_proposal_v1(treasury_proposal, height, expected_enote);
// Get the ephemeral pubkeys from the TX
crypto::public_key tx_pubkey = cryptonote::get_tx_pub_key_from_extra(b.miner_tx.extra);
bool has_single = (tx_pubkey != crypto::null_pkey);
std::vector<crypto::public_key> additional_tx_pubkeys = cryptonote::get_additional_tx_pub_keys_from_extra(b.miner_tx.extra);
bool found_treasury_block_reward = false;
for (size_t i = 0; i < b.miner_tx.vout.size(); i++) {
// Get the output
CHECK_AND_ASSERT_MES(b.miner_tx.vout[i].target.type() == typeid(txout_to_carrot_v1), false, "Output of miner_tx is not txout_to_carrot_V1");
const auto &output = boost::get<txout_to_carrot_v1>(b.miner_tx.vout[i].target);
// Check the output type is SAL1
CHECK_AND_ASSERT_MES(output.asset_type == "SAL1", false, "Output of miner_tx is not SAL1");
// Skip the premine remint
if (treasury_payout_exists && (i == treasury_index_in_tx_outputs)) continue;
// Could this be the treasury block reward?
if (b.miner_tx.vout[i].amount == expected_treasury_block_reward) {
/// Check Ko
if (output.key != expected_enote.onetime_address) continue;
// Check view_tag
CHECK_AND_ASSERT_MES(output.view_tag == expected_enote.view_tag, false,
"treasury output view_tag mismatch (burning bug: K_o correct but view_tag tampered)");
// Check anchor_enc
CHECK_AND_ASSERT_MES(0 == memcmp(&output.encrypted_janus_anchor, &expected_enote.anchor_enc, sizeof(expected_enote.anchor_enc)), false,
"treasury output anchor_enc mismatch (burning bug: K_o correct but anchor tampered)");
// Check D_e
const crypto::public_key expected_De = carrot::raw_byte_convert<crypto::public_key>(expected_enote.enote_ephemeral_pubkey);
crypto::public_key actual_De{};
if (!additional_tx_pubkeys.empty() && i < additional_tx_pubkeys.size()) {
actual_De = additional_tx_pubkeys[i];
} else if (has_single) {
actual_De = tx_pubkey;
}
CHECK_AND_ASSERT_MES(actual_De == expected_De, false,
"treasury output D_e mismatch"); //important
// Passed all checks
found_treasury_block_reward = true;
continue;
}
}
CHECK_AND_ASSERT_MES(found_treasury_block_reward, false, "miner_tx missing treasury output with expected amount " << expected_treasury_block_reward);
}
break;
default:
CHECK_AND_ASSERT_MES(false, false, "invalid HF detected in miner_tx : " << version);
break;
}
return true;
}
//------------------------------------------------------------------
// 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__);
@@ -1630,9 +1781,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"
);
@@ -1679,6 +1841,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;
}
@@ -1804,7 +2031,7 @@ uint64_t Blockchain::get_current_cumulative_block_weight_median() const
// in a lot of places. That flag is not referenced in any of the code
// nor any of the makefiles, howeve. Need to look into whether or not it's
// necessary at all.
bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
bool Blockchain::create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key)
{
LOG_PRINT_L3("Blockchain::" << __func__);
size_t median_weight;
@@ -1833,6 +2060,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
expected_reward = m_btc_expected_reward;
seed_height = m_btc_seed_height;
seed_hash = m_btc_seed_hash;
miner_reward_tx_key = m_btc_miner_reward_tx_key;
return true;
}
MDEBUG("Not using cached template: address " << (!memcmp(&miner_address, &m_btc_address, sizeof(cryptonote::account_public_address))) << ", nonce " << (m_btc_nonce == ex_nonce) << ", cookie " << (m_btc_pool_cookie == m_tx_pool.cookie()) << ", from_block " << (!!from_block));
@@ -1971,19 +2199,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) {
@@ -2093,6 +2348,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
entry.P_change = audit_entry.first.P_change;
entry.return_pubkey = audit_entry.first.return_pubkey;
entry.origin_height = matured_audit_height;
entry.is_carrot = false;
protocol_entries.push_back(entry);
}
}
@@ -2119,10 +2375,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");
ok = construct_protocol_tx(height, b.protocol_tx, protocol_entries, b.major_version);
CHECK_AND_ASSERT_MES(ok, false, "Failed to construct protocol tx");
@@ -2186,8 +2438,9 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
*/
//make blocks coin-base tx looks close to real coinbase tx to get truthful blob weight
uint8_t hf_version = b.major_version;
size_t max_outs = hf_version >= 4 ? 1 : 11;
bool r = construct_miner_tx(height, median_weight, already_generated_coins, txs_weight, fee, miner_address, b.miner_tx, m_nettype, m_hardfork->get_hardforks(), ex_nonce, max_outs, hf_version);
size_t max_outs = hf_version >= HF_VERSION_ENABLE_TOKENS ? 3 : (hf_version >= 4 ? 1 : 11);
bool r = construct_miner_tx(height, median_weight, already_generated_coins, txs_weight, fee, miner_address, miner_reward_tx_key, b.miner_tx, m_nettype, m_hardfork->get_hardforks(), ex_nonce, max_outs, hf_version);
CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, first chance");
size_t cumulative_weight = txs_weight + get_transaction_weight(b.miner_tx);
#if defined(DEBUG_CREATE_BLOCK_TEMPLATE)
@@ -2196,7 +2449,7 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
#endif
for (size_t try_count = 0; try_count != 10; ++try_count)
{
r = construct_miner_tx(height, median_weight, already_generated_coins, cumulative_weight, fee, miner_address, b.miner_tx, m_nettype, m_hardfork->get_hardforks(), ex_nonce, max_outs, hf_version);
r = construct_miner_tx(height, median_weight, already_generated_coins, cumulative_weight, fee, miner_address, miner_reward_tx_key, b.miner_tx, m_nettype, m_hardfork->get_hardforks(), ex_nonce, max_outs, hf_version);
CHECK_AND_ASSERT_MES(r, false, "Failed to construct miner tx, second chance");
size_t coinbase_weight = get_transaction_weight(b.miner_tx);
@@ -2241,16 +2494,16 @@ bool Blockchain::create_block_template(block& b, const crypto::hash *from_block,
#endif
if (!from_block)
cache_block_template(b, miner_address, ex_nonce, diffic, height, expected_reward, seed_height, seed_hash, pool_cookie);
cache_block_template(b, miner_address, ex_nonce, diffic, height, expected_reward, seed_height, seed_hash, pool_cookie, miner_reward_tx_key);
return true;
}
LOG_ERROR("Failed to create_block_template with " << 10 << " tries");
return false;
}
//------------------------------------------------------------------
bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
bool Blockchain::create_block_template(block& b, const account_public_address& miner_address, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key)
{
return create_block_template(b, NULL, miner_address, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash);
return create_block_template(b, NULL, miner_address, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash, miner_reward_tx_key);
}
//------------------------------------------------------------------
bool Blockchain::get_miner_data(uint8_t& major_version, uint64_t& height, crypto::hash& prev_id, crypto::hash& seed_hash, difficulty_type& difficulty, uint64_t& median_weight, uint64_t& already_generated_coins, std::vector<tx_block_template_backlog_entry>& tx_backlog)
@@ -2950,7 +3203,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);
@@ -3724,7 +3977,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;
@@ -3779,6 +4032,107 @@ 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.source_asset_type != tx.destination_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 SAL/SAL1 for both");
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.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 destination asset: " << tx.destination_asset_type << ", expected BURN" << ", provided source asset: " << tx.source_asset_type << ", expected SAL1");
tvc.m_verifivation_failed = true;
return false;
}
} else {
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 != "SAL" && 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 SAL/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
{
@@ -3805,6 +4159,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) {
@@ -3814,7 +4187,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;
@@ -3831,6 +4212,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
@@ -3838,24 +4232,40 @@ 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 ticker doesn't begin with the reserved chars `SAL`
CHECK_AND_ASSERT_MES(tx.token_metadata.asset_type.substr(0,3) != "SAL", false, "Invalid CREATE_TOKEN ticker - SAL* is reserved");
// 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");
}
if (tx.type == cryptonote::transaction_type::ROLLUP) {
CHECK_AND_ASSERT_MES(tx.layer2_rollup_data.version == 1, false, "Invalid ROLLUP data version");
CHECK_AND_ASSERT_MES(!tx.layer2_rollup_data.txs.empty(), false, "ROLLUP must include at least one paid TX entry");
uint64_t expected_amount_burnt = 0;
for (const auto &rollup_tx : tx.layer2_rollup_data.txs) {
CHECK_AND_ASSERT_MES(rollup_tx.tx_fee > 0, false, "ROLLUP contains a paid TX entry with zero fee");
CHECK_AND_ASSERT_MES(expected_amount_burnt <= std::numeric_limits<uint64_t>::max() - rollup_tx.tx_fee, false, "Numeric overflow in ROLLUP fee total");
expected_amount_burnt += rollup_tx.tx_fee;
}
CHECK_AND_ASSERT_MES(tx.amount_burnt == expected_amount_burnt, false, "Invalid amount_burnt for ROLLUP");
}
// Check for invalid TX types
if (tx.type == cryptonote::transaction_type::UNSET || tx.type > cryptonote::transaction_type::MAX) {
@@ -3891,7 +4301,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;
@@ -4036,11 +4446,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);
@@ -4514,8 +4926,7 @@ void Blockchain::get_dynamic_base_fee_estimate_2021_scaling(uint64_t grace_block
//------------------------------------------------------------------
uint64_t Blockchain::get_dynamic_base_fee_estimate(uint64_t grace_blocks) const
{
const uint8_t version = get_current_hard_fork_version();
const uint64_t db_height = m_db->height();
// const uint64_t db_height = m_db->height();
if (grace_blocks >= CRYPTONOTE_REWARD_BLOCKS_WINDOW)
grace_blocks = CRYPTONOTE_REWARD_BLOCKS_WINDOW - 1;
@@ -4666,7 +5077,7 @@ bool Blockchain::calculate_audit_payouts(const uint64_t start_height, std::vecto
// Get the AUDIT TX information for matured staked coins
std::vector<cryptonote::yield_tx_info> audit_entries;
// We get the audit_tx_info from the block where they entered the chain
int audit_tx_result = m_db->get_audit_tx_info(start_height, audit_entries);
m_db->get_audit_tx_info(start_height, audit_entries);
if (!audit_entries.size()) {
// Report error and abort
@@ -4719,7 +5130,7 @@ bool Blockchain::calculate_yield_payouts(const uint64_t start_height, std::vecto
// Get the YIELD TX information for matured staked coins
std::vector<cryptonote::yield_tx_info_carrot> yield_entries;
// We get the yield_tx_info from the block _before_ they started to accrue yield
int yield_tx_result = m_db->get_carrot_yield_tx_info(start_height, yield_entries);
m_db->get_carrot_yield_tx_info(start_height, yield_entries);
if (!yield_entries.size()) {
// Report error and abort
@@ -4785,7 +5196,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
@@ -4952,6 +5363,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
{
@@ -5326,6 +5749,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;
@@ -5358,7 +5791,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;
@@ -5402,7 +5835,7 @@ leave:
// Update the YBI cache data
uint64_t yield_lock_period = cryptonote::get_config(m_nettype).STAKE_LOCK_PERIOD;
uint64_t ybi_cache_expected_size = std::min(new_height, yield_lock_period);
// uint64_t ybi_cache_expected_size = std::min(new_height, yield_lock_period);
if (new_height > yield_lock_period) {
if (m_yield_block_info_cache.count(new_height - yield_lock_period - 2) != 0) {
m_yield_block_info_cache.erase(new_height - yield_lock_period - 2);
@@ -5520,7 +5953,7 @@ uint64_t Blockchain::get_next_long_term_block_weight(uint64_t block_weight) cons
const uint64_t db_height = m_db->height();
const uint64_t nblocks = std::min<uint64_t>(m_long_term_block_weights_window, db_height);
const uint8_t hf_version = get_current_hard_fork_version();
// const uint8_t hf_version = get_current_hard_fork_version();
if (db_height < CRYPTONOTE_LONG_TERM_BLOCK_WEIGHT_WINDOW_SIZE)
return block_weight;
@@ -6167,7 +6600,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
} while(0); \
// generate sorted tables for all amounts and absolute offsets
size_t tx_index = 0, block_index = 0;
size_t tx_index = 0; //, block_index = 0;
for (const auto &entry : blocks_entry)
{
if (m_cancel)
@@ -6238,7 +6671,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
}
}
++block_index;
//++block_index;
}
// sort and remove duplicate absolute_offsets in offset_map
@@ -6510,7 +6943,7 @@ void Blockchain::cancel()
}
#if defined(PER_BLOCK_CHECKPOINT)
static const char expected_block_hashes_hash[] = "a2a5a9bc5d606392ac5c14be55b90a92b8577b8ffdac5c63cc6174f41764c753";
static const char expected_block_hashes_hash[] = "f3bf12451890c9cbeb9f90b2762e4ee2756aaa726958e280e27b51ed9edf84b3";
void Blockchain::load_compiled_in_block_hashes(const GetCheckpointsCallback& get_checkpoints)
{
if (get_checkpoints == nullptr || !m_fast_sync)
@@ -6652,7 +7085,7 @@ void Blockchain::invalidate_block_template_cache()
m_btc_valid = false;
}
void Blockchain::cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t height, uint64_t expected_reward, uint64_t seed_height, const crypto::hash &seed_hash, uint64_t pool_cookie)
void Blockchain::cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t height, uint64_t expected_reward, uint64_t seed_height, const crypto::hash &seed_hash, uint64_t pool_cookie, crypto::public_key &miner_reward_tx_key)
{
MDEBUG("Setting block template cache");
m_btc = b;
@@ -6664,6 +7097,7 @@ void Blockchain::cache_block_template(const block &b, const cryptonote::account_
m_btc_seed_hash = seed_hash;
m_btc_seed_height = seed_height;
m_btc_pool_cookie = pool_cookie;
m_btc_miner_reward_tx_key = miner_reward_tx_key;
m_btc_valid = true;
}
+21 -5
View File
@@ -394,8 +394,8 @@ namespace cryptonote
*
* @return true if block template filled in successfully, else false
*/
bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash);
bool create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash);
bool create_block_template(block& b, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key);
bool create_block_template(block& b, const crypto::hash *from_block, const account_public_address& miner_address, difficulty_type& di, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key);
/**
* @brief gets data required to create a block template and start mining on it
@@ -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
@@ -1318,7 +1333,7 @@ namespace cryptonote
crypto::hash m_btc_seed_hash;
uint64_t m_btc_seed_height;
bool m_btc_valid;
crypto::public_key m_btc_miner_reward_tx_key;
bool m_batch_success;
@@ -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
@@ -1743,7 +1759,7 @@ namespace cryptonote
*
* At some point, may be used to push an update to miners
*/
void cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t height, uint64_t expected_reward, uint64_t seed_height, const crypto::hash &seed_hash, uint64_t pool_cookie);
void cache_block_template(const block &b, const cryptonote::account_public_address &address, const blobdata &nonce, const difficulty_type &diff, uint64_t height, uint64_t expected_reward, uint64_t seed_height, const crypto::hash &seed_hash, uint64_t pool_cookie, crypto::public_key &miner_reward_tx_key);
/**
* @brief sends new block notifications to ZMQ `miner_data` subscribers
+29 -9
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;
}
//-----------------------------------------------------------------------------------------------
@@ -1522,14 +1542,14 @@ namespace cryptonote
notify_txpool_event(tx_blobs, epee::to_span(tx_hashes), epee::to_span(txs), just_broadcasted);
}
//-----------------------------------------------------------------------------------------------
bool core::get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
bool core::get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key)
{
return m_blockchain_storage.create_block_template(b, adr, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash);
return m_blockchain_storage.create_block_template(b, adr, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash, miner_reward_tx_key);
}
//-----------------------------------------------------------------------------------------------
bool core::get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash)
bool core::get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key)
{
return m_blockchain_storage.create_block_template(b, prev_block, adr, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash);
return m_blockchain_storage.create_block_template(b, prev_block, adr, diffic, height, expected_reward, ex_nonce, seed_height, seed_hash, miner_reward_tx_key);
}
//-----------------------------------------------------------------------------------------------
bool core::get_miner_data(uint8_t& major_version, uint64_t& height, crypto::hash& prev_id, crypto::hash& seed_hash, difficulty_type& difficulty, uint64_t& median_weight, uint64_t& already_generated_coins, std::vector<tx_block_template_backlog_entry>& tx_backlog)
+2 -2
View File
@@ -231,8 +231,8 @@ namespace cryptonote
*
* @note see Blockchain::create_block_template
*/
virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash) override;
virtual bool get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash);
virtual bool get_block_template(block& b, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key) override;
virtual bool get_block_template(block& b, const crypto::hash *prev_block, const account_public_address& adr, difficulty_type& diffic, uint64_t& height, uint64_t& expected_reward, const blobdata& ex_nonce, uint64_t &seed_height, crypto::hash &seed_hash, crypto::public_key &miner_reward_tx_key);
/**
* @copydoc Blockchain::get_miner_data
+98 -44
View File
@@ -346,14 +346,30 @@ 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;
const bool force_carrot = hard_fork_version >= HF_VERSION_ENFORCE_CARROT;
if (force_carrot)
// Scan the protocol_data to make sure all or none are Carrot
for (auto const& entry: protocol_data) {
if (entry.is_carrot) carrot_found = true;
else noncarrot_found = true;
}
if (carrot_found && noncarrot_found) {
LOG_ERROR("Cannot mix Carrot and non-Carrot outputs in the same protocol transaction");
return false;
}
if (carrot_found && hard_fork_version < HF_VERSION_CARROT) {
LOG_ERROR("Carrot outputs found in CryptoNote protocol transaction");
return false;
}
if (carrot_found || (!noncarrot_found && hard_fork_version >= HF_VERSION_CARROT))
{
// Ensure the TX version is correct
tx.version = TRANSACTION_VERSION_CARROT;
try
{
// Create a vector of enotes
@@ -365,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;
@@ -373,9 +390,9 @@ namespace cryptonote
memcpy(e.enote_ephemeral_pubkey.data, entry.return_pubkey.data, sizeof(crypto::public_key));
enotes.push_back(e);
}
carrot_found = true;
tx = store_carrot_to_coinbase_transaction_v1(enotes, std::string{}, cryptonote::transaction_type::PROTOCOL, height);
tx.amount_burnt = 0;
tx.version = (hard_fork_version >= HF_VERSION_ENABLE_TOKENS) ? TRANSACTION_VERSION_ENABLE_TOKENS : TRANSACTION_VERSION_CARROT;
tx.invalidate_hashes();
}
catch (const std::exception &e)
@@ -412,14 +429,12 @@ namespace cryptonote
additional_tx_public_keys.push_back(entry.return_pubkey);
tx.vout.push_back(out);
carrot_found = true;
} else {
// Create the TX output for this refund
tx_out out;
cryptonote::set_tx_out(entry.amount_burnt, entry.destination_asset, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, entry.return_address, false, crypto::view_tag{}, out);
additional_tx_public_keys.push_back(entry.return_pubkey);
tx.vout.push_back(out);
noncarrot_found = true;
}
} else if (entry.type == cryptonote::transaction_type::AUDIT) {
// PAYOUT
@@ -430,19 +445,9 @@ namespace cryptonote
cryptonote::set_tx_out(entry.amount_burnt, entry.destination_asset, CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW, entry.return_address, false, crypto::view_tag{}, out);
additional_tx_public_keys.push_back(entry.return_pubkey);
tx.vout.push_back(out);
noncarrot_found = true;
}
}
if (carrot_found && noncarrot_found) {
LOG_ERROR("Cannot mix Carrot and non-Carrot outputs in the same protocol transaction");
return false;
}
if (carrot_found) {
// Ensure the TX version is correct
tx.version = TRANSACTION_VERSION_CARROT;
}
// Add in all of the additional TX pubkeys we need to process the payments
add_additional_tx_pub_keys_to_extra(tx.extra, additional_tx_public_keys);
@@ -454,7 +459,17 @@ namespace cryptonote
return true;
}
//---------------------------------------------------------------
bool construct_miner_tx(size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, transaction& tx, network_type nettype, const std::vector<hardfork_t>& hardforks, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) {
carrot::janus_anchor_t get_deterministic_treasury_anchor_from_height(uint64_t height)
{
carrot::janus_anchor_t treasury_anchor{};
for (int i = 0; i < 8; ++i)
treasury_anchor.bytes[i] = (height >> (8 * i)) & 0xff;
for (int i = 8; i < 16; ++i)
treasury_anchor.bytes[i] = 0;
return treasury_anchor;
}
//---------------------------------------------------------------
bool construct_miner_tx(size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, crypto::public_key& miner_reward_tx_key, transaction& tx, network_type nettype, const std::vector<hardfork_t>& hardforks, const blobdata& extra_nonce, size_t max_outs, uint8_t hard_fork_version) {
// Clear the TX contents
tx.set_null();
@@ -482,33 +497,70 @@ namespace cryptonote
{
try
{
// Build the miner payout
carrot::CarrotDestinationV1 destination;
// miner destination
carrot::CarrotDestinationV1 miner_destination;
carrot::make_carrot_main_address_v1(miner_address.m_spend_public_key,
miner_address.m_view_public_key,
destination);
miner_destination);
CHECK_AND_ASSERT_THROW_MES(!miner_destination.is_subaddress,
"construct_miner_tx: subaddresses are not allowed in miner transactions");
CHECK_AND_ASSERT_THROW_MES(miner_destination.payment_id == carrot::null_payment_id,
"construct_miner_tx: integrated addresses are not allowed in miner transactions");
CHECK_AND_ASSERT_THROW_MES(!destination.is_subaddress,
"make_single_enote_carrot_coinbase_transaction_v1: subaddress are not allowed in miner transactions");
CHECK_AND_ASSERT_THROW_MES(destination.payment_id == carrot::null_payment_id,
"make_single_enote_carrot_coinbase_transaction_v1: integrated addresses are not allowed in miner transactions");
const bool do_new_split = (hard_fork_version >= HF_VERSION_ENABLE_TOKENS);
uint64_t treasury_reward = do_new_split ? (block_reward * BLOCK_REWARD_TREASURY_PCT / 100) : 0;
uint64_t stake_reward = (block_reward - treasury_reward) * BLOCK_REWARD_STAKER_PCT / 100;
uint64_t miner_reward = (block_reward - treasury_reward - stake_reward);
uint64_t stake_reward = block_reward / 5;
const carrot::CarrotPaymentProposalV1 payment_proposal{
.destination = destination,
.amount = block_reward - stake_reward,
//miner enote
const carrot::CarrotPaymentProposalV1 miner_proposal{
.destination = miner_destination,
.amount = miner_reward,
.asset_type = "SAL1",
.randomness = carrot::gen_janus_anchor()
};
std::vector<carrot::CarrotCoinbaseEnoteV1> enotes(treasury_payout_exists ? 2 : 1);
carrot::get_coinbase_output_proposal_v1(payment_proposal, height, enotes.front());
// Determine number of enotes: miner + optional treasury_reward + optional treasury_mint
size_t num_enotes = 1;
if (do_new_split) num_enotes++; // treasury reward output
if (treasury_payout_exists) num_enotes++;
std::vector<carrot::CarrotCoinbaseEnoteV1> enotes(num_enotes);
carrot::get_coinbase_output_proposal_v1(miner_proposal, height, enotes[0]);
// Check to see if there needs to be a treasury payout
// STORE THE MINER TX_PUB_KEY NOW
miner_reward_tx_key = carrot::raw_byte_convert<crypto::public_key>(enotes[0].enote_ephemeral_pubkey);
size_t enote_idx = 1;
// Add the treasury reward enote (25% of block reward) for HF11+
if (do_new_split) {
//treasury address for HF11+ block reward split
address_parse_info treasury_addr_info;
bool treasury_ok = cryptonote::get_account_address_from_str(treasury_addr_info, nettype, get_config(nettype).TREASURY_ADDRESS_CARROT);
CHECK_AND_ASSERT_MES(treasury_ok, false, "Failed to parse treasury address for block reward split"); // maybe more check can be added here, but it's enough for now (bcs validation)
carrot::CarrotDestinationV1 treasury_destination;
carrot::make_carrot_main_address_v1(treasury_addr_info.address.m_spend_public_key,
treasury_addr_info.address.m_view_public_key,
treasury_destination);
// Derive a deterministic janus anchor from height so validators can independently recompute the expected treasury K_o
const carrot::janus_anchor_t treasury_anchor = get_deterministic_treasury_anchor_from_height(height);
const carrot::CarrotPaymentProposalV1 treasury_proposal{
.destination = treasury_destination,
.amount = treasury_reward,
.asset_type = "SAL1",
.randomness = treasury_anchor
};
carrot::get_coinbase_output_proposal_v1(treasury_proposal, height, enotes[enote_idx]);
enote_idx++;
}
//
if (treasury_payout_exists) {
// Convert the strings into meaningful data
const auto [tx_public_key_str, onetime_address_str, anchor_enc_str, view_tag_str] = treasury_payout_data.at(height);
mx25519_pubkey tx_public_key;
CHECK_AND_ASSERT_THROW_MES(epee::string_tools::hex_to_pod(tx_public_key_str, tx_public_key), "fail to deserialize treasury tx public key");
@@ -519,8 +571,8 @@ namespace cryptonote
carrot::view_tag_t view_tag;
CHECK_AND_ASSERT_THROW_MES(epee::string_tools::hex_to_pod(view_tag_str, view_tag), "fail to deserialize treasury tx view_tag");
// Manually produce an enote for the treasury payout using the hardcoded keys
carrot::CarrotCoinbaseEnoteV1 &treasury_enote = enotes.back();
//
carrot::CarrotCoinbaseEnoteV1 &treasury_enote = enotes[enote_idx];
treasury_enote.onetime_address = onetime_address;
treasury_enote.amount = TREASURY_SAL1_MINT_AMOUNT;
treasury_enote.asset_type = "SAL1";
@@ -528,15 +580,15 @@ namespace cryptonote
treasury_enote.view_tag = view_tag;
treasury_enote.enote_ephemeral_pubkey = tx_public_key;
treasury_enote.block_index = height;
// sort enotes by K_o
if (enotes[0].onetime_address > enotes[1].onetime_address) {
std::swap(enotes[0], enotes[1]);
}
}
tx = carrot::store_carrot_to_coinbase_transaction_v1(enotes, extra_nonce, cryptonote::transaction_type::MINER, height);
// Sort enotes by K_o
std::sort(enotes.begin(), enotes.end(), [](const carrot::CarrotCoinbaseEnoteV1 &a, const carrot::CarrotCoinbaseEnoteV1 &b) {
return a.onetime_address < b.onetime_address;
});
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();
}
@@ -551,6 +603,8 @@ namespace cryptonote
keypair txkey = keypair::generate(hw::get_device("default"));
add_tx_pub_key_to_extra(tx, txkey.pub);
// STORE THE MINER TX_PUB_KEY NOW
miner_reward_tx_key = txkey.pub;
if(!extra_nonce.empty())
if(!add_extra_nonce_to_tx_extra(tx.extra, extra_nonce))
return false;
@@ -594,7 +648,7 @@ namespace cryptonote
case HF_VERSION_AUDIT2:
case HF_VERSION_AUDIT2_PAUSE:
case HF_VERSION_CARROT:
case HF_VERSION_ENFORCE_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;
+3 -1
View File
@@ -77,7 +77,9 @@ namespace cryptonote
//---------------------------------------------------------------
bool construct_protocol_tx(const size_t height, transaction& tx, std::vector<protocol_data_entry>& protocol_data, const uint8_t hf_version);
//---------------------------------------------------------------
bool construct_miner_tx(size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, transaction& tx, network_type nettype = network_type::FAKECHAIN, const std::vector<hardfork_t>& hardforks = {}, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1);
carrot::janus_anchor_t get_deterministic_treasury_anchor_from_height(uint64_t height);
//---------------------------------------------------------------
bool construct_miner_tx(size_t height, size_t median_weight, uint64_t already_generated_coins, size_t current_block_weight, uint64_t fee, const account_public_address &miner_address, crypto::public_key& miner_reward_tx_key, transaction& tx, network_type nettype = network_type::FAKECHAIN, const std::vector<hardfork_t>& hardforks = {}, const blobdata& extra_nonce = blobdata(), size_t max_outs = 999, uint8_t hard_fork_version = 1);
struct tx_source_entry
{
+72 -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();
@@ -1749,6 +1776,49 @@ namespace cryptonote
LOG_PRINT_L2(" key images already seen");
continue;
}
if (version < HF_VERSION_CARROT && tx.version >= TRANSACTION_VERSION_CARROT)
{
LOG_PRINT_L2(" is a Carrot transaction - cannot be mined");
continue;
}
if (version >= HF_VERSION_CARROT && tx.version < TRANSACTION_VERSION_CARROT)
{
LOG_PRINT_L2(" is not a Carrot transaction - cannot be mined");
continue;
}
// 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;
+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
};
}
+2 -1
View File
@@ -149,7 +149,8 @@ void print_genesis_tx_hex(const cryptonote::network_type nettype) {
//Prepare genesis_tx
cryptonote::transaction tx_genesis;
cryptonote::construct_miner_tx(0, 0, 0, 10, 0, miner_acc1.get_keys().m_account_address, tx_genesis, (network_type)nettype, {}, blobdata(), 999, 1);
crypto::public_key miner_reward_tx_key = crypto::null_pkey;
cryptonote::construct_miner_tx(0, 0, 0, 10, 0, miner_acc1.get_keys().m_account_address, miner_reward_tx_key, tx_genesis, (network_type)nettype, {}, blobdata(), 999, 1);
std::cout << "Object:" << std::endl;
std::cout << obj_to_json_str(tx_genesis) << std::endl << std::endl;
+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;
+9 -3
View File
@@ -58,6 +58,12 @@ const hardfork_t mainnet_hard_forks[] = {
// version 9 starts from block 179200, which is on or around the 10th of March, 2025. Fork time finalised on 2025-02-24. No fork voting occurs for the v9 fork.
{ 9, 179200, 0, 1740393800 },
// version 10 Carrot - including treasury mint - starts from block 334750, which is on or around the 13th of October, 2025. Fork time finalised on 2025-09-29. No fork voting occurs for the v10 fork.
{10, 334750, 0, 1759142500 },
// version 11 Two Milestone 1 - starts from block 465000, which is on or around the 13th of April, 2026. Fork time finalised on 2026-03-20. No fork voting occurs for the v11 fork.
{11, 465000, 0, 1774000000 },
};
const size_t num_mainnet_hard_forks = sizeof(mainnet_hard_forks) / sizeof(mainnet_hard_forks[0]);
const uint64_t mainnet_hard_fork_version_1_till = ((uint64_t)-1);
@@ -92,9 +98,9 @@ const hardfork_t testnet_hard_forks[] = {
// version 10 Carrot - including treasury mint - starts from block 1100
{10, 1100, 0, 1739780005 },
// version 11 Carrot and CryptoNote starts from block 1200
{11, 1200, 0, 1756811153 },
// 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);
+50 -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>
@@ -1870,15 +1871,14 @@ namespace cryptonote
bool core_rpc_server::get_block_template(const account_public_address &address, const crypto::hash *prev_block, const cryptonote::blobdata &extra_nonce, size_t &reserved_offset, cryptonote::difficulty_type &difficulty, uint64_t &height, uint64_t &expected_reward, block &b, uint64_t &seed_height, crypto::hash &seed_hash, crypto::hash &next_seed_hash, epee::json_rpc::error &error_resp)
{
b = boost::value_initialized<cryptonote::block>();
if(!m_core.get_block_template(b, prev_block, address, difficulty, height, expected_reward, extra_nonce, seed_height, seed_hash))
crypto::public_key tx_pub_key = crypto::null_pkey;
if(!m_core.get_block_template(b, prev_block, address, difficulty, height, expected_reward, extra_nonce, seed_height, seed_hash, tx_pub_key))
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
error_resp.message = "Internal error: failed to create block template";
LOG_ERROR("Failed to create block template");
return false;
}
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);
if(tx_pub_key == crypto::null_pkey)
{
error_resp.code = CORE_RPC_ERROR_CODE_INTERNAL_ERROR;
@@ -1886,7 +1886,7 @@ namespace cryptonote
LOG_ERROR("Failed to get tx pub key in coinbase extra");
return false;
}
blobdata block_blob = t_serializable_object_to_blob(b);
uint64_t next_height;
crypto::rx_seedheights(height, &seed_height, &next_height);
if (next_height != seed_height)
@@ -3045,6 +3045,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 +3154,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-rc3"
#define DEF_SALVIUM_VERSION "1.1.1"
#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);
+83 -6
View File
@@ -34,16 +34,60 @@
#include "wallet.h"
#include "crypto/hash.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "wallet/wallet2.h"
#include <string>
#include <list>
#include <unordered_map>
#include <unordered_set>
using namespace epee;
namespace Monero {
namespace {
std::string to_hex_or_empty(const crypto::public_key &key)
{
if (key == crypto::null_pkey)
return {};
return string_tools::pod_to_hex(key);
}
std::vector<std::string> extract_return_addresses(const cryptonote::transaction_prefix &tx)
{
std::vector<std::string> addresses;
if (tx.type == cryptonote::transaction_type::STAKE)
{
auto hex = to_hex_or_empty(tx.protocol_tx_data.return_address);
if (hex.empty())
hex = to_hex_or_empty(tx.return_address);
if (!hex.empty())
addresses.push_back(hex);
return addresses;
}
if (tx.type != cryptonote::transaction_type::PROTOCOL)
return addresses;
std::unordered_set<std::string> seen;
for (const auto &out : tx.vout)
{
crypto::public_key output_key = crypto::null_pkey;
if (!cryptonote::get_output_public_key(out, output_key))
continue;
auto hex = to_hex_or_empty(output_key);
if (hex.empty())
continue;
if (seen.emplace(hex).second)
addresses.push_back(hex);
}
return addresses;
}
} // namespace
TransactionHistory::~TransactionHistory() {}
@@ -130,6 +174,7 @@ void TransactionHistoryImpl::refresh()
std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> in_payments;
m_wallet->m_wallet->get_payments(in_payments, min_height, max_height);
std::unordered_map<crypto::hash, TransactionInfoImpl*> protocol_by_hash;
for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = in_payments.begin(); i != in_payments.end(); ++i) {
const tools::wallet2::payment_details &pd = i->second;
std::string payment_id = string_tools::pod_to_hex(i->first);
@@ -152,6 +197,8 @@ void TransactionHistoryImpl::refresh()
ti->m_unlock_time = pd.m_unlock_time;
ti->m_type = static_cast<Monero::transaction_type>(static_cast<uint8_t>(pd.m_tx_type));
ti->m_asset = pd.m_asset_type;
if (pd.m_tx_type == cryptonote::transaction_type::PROTOCOL)
protocol_by_hash.emplace(pd.m_tx_hash, ti);
m_history.push_back(ti);
}
@@ -175,7 +222,14 @@ 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 ||
pd.m_tx.type == cryptonote::transaction_type::ROLLUP) {
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 +238,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);
@@ -197,6 +251,7 @@ void TransactionHistoryImpl::refresh()
ti->m_confirmations = (wallet_height > pd.m_block_height) ? wallet_height - pd.m_block_height : 0;
ti->m_type = static_cast<Monero::transaction_type>(static_cast<uint8_t>(pd.m_tx.type));
ti->m_asset = pd.m_tx.source_asset_type;
ti->m_return_addresses = extract_return_addresses(pd.m_tx);
// single output transaction might contain multiple transfers
for (const auto &d: pd.m_dests) {
@@ -212,8 +267,17 @@ 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 ||
pd.m_tx.type == cryptonote::transaction_type::ROLLUP) {
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 +285,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;
@@ -235,6 +299,7 @@ void TransactionHistoryImpl::refresh()
ti->m_confirmations = 0;
ti->m_type = static_cast<Monero::transaction_type>(static_cast<uint8_t>(pd.m_tx.type));
ti->m_asset = pd.m_tx.source_asset_type;
ti->m_return_addresses = extract_return_addresses(pd.m_tx);
for (const auto &d : pd.m_dests)
{
ti->m_transfers.push_back({d.amount, d.address(m_wallet->m_wallet->nettype(), pd.m_payment_id), d.asset_type});
@@ -266,11 +331,23 @@ void TransactionHistoryImpl::refresh()
ti->m_confirmations = 0;
ti->m_type = static_cast<Monero::transaction_type>(static_cast<uint8_t>(pd.m_tx_type));
ti->m_asset = pd.m_asset_type;
if (pd.m_tx_type == cryptonote::transaction_type::PROTOCOL)
protocol_by_hash.emplace(pd.m_tx_hash, ti);
m_history.push_back(ti);
LOG_PRINT_L1(__FUNCTION__ << ": Unconfirmed payment found " << pd.m_amount);
}
std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>> confirmed_protocol_payments;
m_wallet->m_wallet->get_payments_out(confirmed_protocol_payments, min_height, max_height);
for (const auto &entry : confirmed_protocol_payments)
{
const auto it = protocol_by_hash.find(entry.first);
if (it == protocol_by_hash.end())
continue;
it->second->m_return_addresses = extract_return_addresses(entry.second.m_tx);
}
}
} // namespace
+5
View File
@@ -134,6 +134,11 @@ string TransactionInfoImpl::paymentId() const
return m_paymentid;
}
const std::vector<std::string> &TransactionInfoImpl::returnAddresses() const
{
return m_return_addresses;
}
const std::vector<TransactionInfo::Transfer> &TransactionInfoImpl::transfers() const
{
return m_transfers;
+2
View File
@@ -60,6 +60,7 @@ public:
virtual std::string hash() const override;
virtual std::time_t timestamp() const override;
virtual std::string paymentId() const override;
virtual const std::vector<std::string> &returnAddresses() const override;
virtual const std::vector<Transfer> &transfers() const override;
virtual uint64_t confirmations() const override;
virtual uint64_t unlockTime() const override;
@@ -81,6 +82,7 @@ private:
std::string m_hash;
std::time_t m_timestamp;
std::string m_paymentid;
std::vector<std::string> m_return_addresses;
std::vector<Transfer> m_transfers;
uint64_t m_confirmations;
uint64_t m_unlock_time;
+171 -9
View File
@@ -806,6 +806,16 @@ std::string WalletImpl::seed(const std::string& seed_offset) const
return std::string(seed.data(), seed.size()); // TODO
}
void WalletImpl::setStoreTxInfo(bool store)
{
m_wallet->store_tx_info(store);
}
bool WalletImpl::storeTxInfo() const
{
return m_wallet->store_tx_info();
}
std::string WalletImpl::getSeedLanguage() const
{
return m_wallet->get_seed_language();
@@ -877,40 +887,64 @@ bool WalletImpl::setDevicePassphrase(const std::string &passphrase)
return status() == Status_Ok;
}
std::string WalletImpl::address(uint32_t accountIndex, uint32_t addressIndex) const
std::string WalletImpl::address(uint32_t accountIndex, uint32_t addressIndex, bool carrot) const
{
return m_wallet->get_subaddress_as_str({accountIndex, addressIndex});
return m_wallet->get_subaddress_as_str({{accountIndex, addressIndex}, carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});
}
std::string WalletImpl::integratedAddress(const std::string &payment_id) const
std::string WalletImpl::integratedAddress(const std::string &payment_id, bool carrot) const
{
crypto::hash8 pid;
if (!tools::wallet2::parse_short_payment_id(payment_id, pid)) {
return "";
}
return m_wallet->get_integrated_address_as_str(pid);
return m_wallet->get_integrated_address_as_str(pid, carrot);
}
std::string WalletImpl::secretViewKey() const
{
uint32_t hf_version = m_wallet->estimate_current_hard_fork();
if (hf_version >= HF_VERSION_CARROT)
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_view_incoming)));
else
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().m_view_secret_key)));
}
std::string WalletImpl::publicViewKey() const
{
uint32_t hf_version = m_wallet->estimate_current_hard_fork();
if (hf_version >= HF_VERSION_CARROT)
return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_carrot_account_address.m_view_public_key);
else
return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_view_public_key);
}
std::string WalletImpl::secretSpendKey() const
{
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().m_spend_secret_key)));
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().m_spend_secret_key)));
}
std::string WalletImpl::publicSpendKey() const
{
uint32_t hf_version = m_wallet->estimate_current_hard_fork();
if (hf_version >= HF_VERSION_CARROT)
return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_carrot_account_address.m_spend_public_key);
else
return epee::string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_account_address.m_spend_public_key);
}
std::vector<std::string> WalletImpl::carrotKeys() const
{
return {
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().s_master))),
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_prove_spend))),
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().s_view_balance))),
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_view_incoming))),
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_generate_image))),
epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().s_generate_address)))
};
}
std::string WalletImpl::publicMultisigSignerKey() const
{
try {
@@ -921,6 +955,26 @@ std::string WalletImpl::publicMultisigSignerKey() const
}
}
std::string WalletImpl::secretViewBalance() const
{
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().s_view_balance)));
}
std::string WalletImpl::secretProveSpend() const
{
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_prove_spend)));
}
std::string WalletImpl::secretGenerateAddress() const
{
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().s_generate_address)));
}
std::string WalletImpl::secretGenerateImage() const
{
return epee::string_tools::pod_to_hex(unwrap(unwrap(m_wallet->get_account().get_keys().k_generate_image)));
}
std::string WalletImpl::path() const
{
return m_wallet->path();
@@ -1588,7 +1642,8 @@ PendingTransaction* WalletImpl::restoreMultisigTransaction(const string& signDat
PendingTransaction *WalletImpl::createStakeTransaction(uint64_t amount, uint32_t mixin_count, PendingTransaction::Priority priority, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices)
{
// Need to populate {dst_entr, payment_id, asset_type, is_return}
const string dst_addr = m_wallet->get_subaddress_as_str({subaddr_account, 0});//MY LOCAL (SUB)ADDRESS
const bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
const string dst_addr = m_wallet->get_subaddress_as_str({{subaddr_account, 0}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});//MY LOCAL (SUB)ADDRESS
const string payment_id = "";
const string asset_type = "SAL1";
const bool is_return = false;
@@ -1598,6 +1653,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,
@@ -1605,7 +1761,8 @@ PendingTransaction *WalletImpl::createAuditTransaction(
std::set<uint32_t> subaddr_indices
) {
// Need to populate {dst_entr, payment_id, asset_type, is_return}
const string dst_addr = m_wallet->get_subaddress_as_str({subaddr_account, 0});//MY LOCAL (SUB)ADDRESS
const bool is_carrot = m_wallet->get_current_hard_fork() >= HF_VERSION_CARROT;
const string dst_addr = m_wallet->get_subaddress_as_str({{subaddr_account, 0}, is_carrot ? carrot::AddressDeriveType::Carrot : carrot::AddressDeriveType::PreCarrot});//MY LOCAL (SUB)ADDRESS
const string payment_id = "";
const string asset_type = "SAL";
const bool is_return = false;
@@ -1737,8 +1894,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 */,
@@ -2886,4 +3043,9 @@ YieldInfo * WalletImpl::getYieldInfo()
return yi;
}
std::vector<std::string> WalletImpl::getAssetTypes()
{
return m_wallet->list_asset_types();
}
} // namespace
+20 -2
View File
@@ -83,6 +83,8 @@ public:
Device getDeviceType() const override;
bool close(bool store = true);
std::string seed(const std::string& seed_offset = "") const override;
void setStoreTxInfo(bool store) override;
bool storeTxInfo() const override;
std::string getSeedLanguage() const override;
void setSeedLanguage(const std::string &arg) override;
// void setListener(Listener *) {}
@@ -93,13 +95,18 @@ public:
const std::string& getPassword() const override;
bool setDevicePin(const std::string &password) override;
bool setDevicePassphrase(const std::string &password) override;
std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const override;
std::string integratedAddress(const std::string &payment_id) const override;
std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0, bool carrot = true) const override;
std::string integratedAddress(const std::string &payment_id, bool carrot = true) const override;
std::string secretViewKey() const override;
std::string publicViewKey() const override;
std::string secretSpendKey() const override;
std::string publicSpendKey() const override;
std::vector<std::string> carrotKeys() const override;
std::string publicMultisigSignerKey() const override;
std::string secretViewBalance() const override;
std::string secretProveSpend() const override;
std::string secretGenerateAddress() const override;
std::string secretGenerateImage() const override;
std::string path() const override;
void stop() override;
bool store(const std::string &path) override;
@@ -158,6 +165,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 +263,8 @@ public:
YieldInfo * getYieldInfo() override;
virtual std::vector<std::string> getAssetTypes() override;
private:
void clearStatus() const;
void setStatusError(const std::string& message) const;
+74 -5
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 {
@@ -234,6 +236,7 @@ struct TransactionInfo
virtual std::string hash() const = 0;
virtual std::time_t timestamp() const = 0;
virtual std::string paymentId() const = 0;
virtual const std::vector<std::string> & returnAddresses() const = 0;
//! only applicable for output transactions
virtual const std::vector<Transfer> & transfers() const = 0;
virtual Monero::transaction_type type() const = 0;
@@ -366,7 +369,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 {
@@ -501,6 +504,8 @@ struct Wallet
virtual ~Wallet() = 0;
virtual std::string seed(const std::string& seed_offset = "") const = 0;
virtual void setStoreTxInfo(bool store) = 0;
virtual bool storeTxInfo() const = 0;
virtual std::string getSeedLanguage() const = 0;
virtual void setSeedLanguage(const std::string &arg) = 0;
//! returns wallet status (Status_Ok | Status_Error)
@@ -513,8 +518,8 @@ struct Wallet
virtual const std::string& getPassword() const = 0;
virtual bool setDevicePin(const std::string &pin) { (void)pin; return false; };
virtual bool setDevicePassphrase(const std::string &passphrase) { (void)passphrase; return false; };
virtual std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const = 0;
std::string mainAddress() const { return address(0, 0); }
virtual std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0, bool carrot = true) const = 0;
std::string mainAddress(bool carrot = true) const { return address(0, 0, carrot); }
virtual std::string path() const = 0;
virtual NetworkType nettype() const = 0;
bool mainnet() const { return nettype() == MAINNET; }
@@ -533,7 +538,7 @@ struct Wallet
* generated
* \return - 106 characters string representing integrated address
*/
virtual std::string integratedAddress(const std::string &payment_id) const = 0;
virtual std::string integratedAddress(const std::string &payment_id, bool carrot = true) const = 0;
/*!
* \brief secretViewKey - returns secret view key
@@ -559,12 +564,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 +938,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 +1290,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));

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